@tushi11/elpis 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -577
- package/app/pages/common/feedback.js +6 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,118 +1,48 @@
|
|
|
1
|
-
# elpis
|
|
1
|
+
# elpis
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
- elpis 的设计初衷是为了解决重复性的增删改查工作,将常规的增删改查沉淀到模型中,通过模型解析器解析模型配置,加上模型数据渲染组件将数据渲染到页面,并且支持常规交互
|
|
5
|
-
- 最终打造一个全栈,全流程去开发一个支持多网站建设的系统平台(应用框架)。能通过配置化去沉淀 80%的重复性需求,并且提供各种各样的定制化能力,可灵活支持剩余 20%的定制化需求开发。
|
|
6
|
-
|
|
7
|
-
## 问题
|
|
8
|
-
|
|
9
|
-
传统开发中,80%的时间消耗在重复性业务逻辑上:
|
|
10
|
-
|
|
11
|
-
1. 基础 CRUD 接口的重复开发
|
|
12
|
-
2. 表单 / 表格 / 搜索框等组件的重复编写
|
|
13
|
-
3. 验证逻辑、交互逻辑的重复实现
|
|
14
|
-
|
|
15
|
-
## 解决方案
|
|
16
|
-
|
|
17
|
-
Elpis 通过 DSL 领域模型的开发范式重构开发流程:
|
|
18
|
-
|
|
19
|
-
1. 统一建模层
|
|
20
|
-
|
|
21
|
-
- 模型使用继承的理念,一个领域模型可以衍生出若干个项目
|
|
22
|
-
|
|
23
|
-
2. 智能解析引擎
|
|
24
|
-
|
|
25
|
-
- 模型 → 页面:自动生成 CRUD 标准页面
|
|
26
|
-
|
|
27
|
-
- 模型 → 组件:动态渲染表单/表格/搜索框/详情面板/...
|
|
28
|
-
|
|
29
|
-
- 模型 → 菜单:自动化配置前端路由
|
|
30
|
-
|
|
31
|
-
- 一个领域模型可以衍生出若干个项目,**领域模型**和**项目**的关系是**对象继承关系**,项目(子类)**继承于**领域模型(基类),领域模型可以沉淀各个项目中**重复**的**功能/页面,实现复用。**
|
|
32
|
-
|
|
33
|
-
## 项目技术栈
|
|
34
|
-
|
|
35
|
-
| **后端技术栈** | 前端技术栈 |
|
|
36
|
-
| ------------------------------- | --------------------------------- |
|
|
37
|
-
| **框架**: Koa2 | **框架**: Vue 3 (Composition API) |
|
|
38
|
-
| **模板引擎**: Nunjucks | **UI 组件库**: Element Plus |
|
|
39
|
-
| **测试框架**: Mocha + Supertest | **状态管理**: Vuex 4 |
|
|
40
|
-
| **工具库**: Lodash, MD5, Glob | **路由**: Vue Router 4 |
|
|
41
|
-
| | **构建工具**: Webpack 5 |
|
|
42
|
-
| | **样式预处理**: Less |
|
|
43
|
-
|
|
44
|
-
## 项目架构设计
|
|
45
|
-
|
|
46
|
-
- **展示层**:Vue3 + element-plus + webpack5
|
|
47
|
-
|
|
48
|
-
- 融合了**多页面 SSR-MPA**和**单页面 CSR-SPA**两种模式。在配置多个单页面入口的基础上,赋予每个单页面通过前端路由跳转子页面的能力。
|
|
49
|
-
- 通过服务端渲染生成**多页面(SSR)入口**,里面渲染的不同 页面/路由 都是一个独立的**单页面 CSR-SPA 应用**,是通过**前端路由 router**实现子页面间的无缝切换(即首屏是服务端渲染,次屏是客户端渲染的)
|
|
50
|
-
- 整个方案利用起首屏里 SEO 的好处,次屏 SPA 的好处
|
|
51
|
-
|
|
52
|
-
- **BFF 层**:Node.js18(Koa2)
|
|
53
|
-
|
|
54
|
-
- 细分为接入层、业务层和服务层。
|
|
55
|
-
- 接入层主要负责路由分发,通过分析页面请求来判断是进行渲染操作还是调用 BFF 接口;
|
|
56
|
-
- 业务层聚焦于前端页面所需的各类业务处理,为页面功能实现提供业务逻辑支撑;
|
|
57
|
-
- 服务层则主要承担获取数据的处理工作,保障数据的有效获取与传递。
|
|
58
|
-
|
|
59
|
-
- **数据层**:MySQL + log4js 日志
|
|
60
|
-
|
|
61
|
-
- 包括了数据库,日志,后端服务接口以及其他各类数据来源。
|
|
62
|
-
|
|
63
|
-
- (这个系统用的是 nodejs 来作为运行时页面服务器,同时它也是 API 的 BFF 层)
|
|
64
|
-
|
|
65
|
-
## model 配置(核心)
|
|
66
|
-
|
|
67
|
-
model 的配置是使用 elpis 的重中之重,根据下面给到的 model 示例配置,进行项目 model 模型配置。
|
|
68
|
-
|
|
69
|
-
Model 是 Elpis 的**核心配置单元**,通过定义领域模型的结构、交互和接口,驱动全栈功能生成。
|
|
3
|
+
## 一个企业级应用框架,通过全栈实现。
|
|
70
4
|
|
|
5
|
+
### model配置
|
|
71
6
|
```javascript
|
|
72
7
|
{
|
|
73
8
|
mode: 'dashboard', // 模板类型,不同模板类型对应不一样的模板数据结构
|
|
74
|
-
name: '', //
|
|
75
|
-
desc: '', //
|
|
76
|
-
icon: '', //
|
|
77
|
-
homePage: '
|
|
9
|
+
name: '', // 名称
|
|
10
|
+
desc: '', // 描述
|
|
11
|
+
icon: '', // 图标
|
|
12
|
+
homePage: '', // 首页(项目配置)
|
|
78
13
|
// 头部菜单
|
|
79
14
|
menu: [
|
|
80
15
|
{
|
|
81
16
|
key: '', // 菜单唯一描述
|
|
82
17
|
name: '', // 菜单名称
|
|
83
|
-
menuType: '', //
|
|
18
|
+
menuType: '', // 枚举值, group / module
|
|
84
19
|
|
|
85
20
|
// 当menuType == group时,可填
|
|
86
21
|
subMenu: [
|
|
87
22
|
{
|
|
88
|
-
//
|
|
23
|
+
// 可递归 menuItem
|
|
89
24
|
},
|
|
90
|
-
// ...
|
|
91
25
|
],
|
|
92
26
|
|
|
93
27
|
// 当menuType == module时,可填
|
|
94
|
-
moduleType: '', //
|
|
28
|
+
moduleType: '', // 枚举值: sider/iframe/custom/schema
|
|
95
29
|
|
|
96
30
|
// 当moduleType == sider 时
|
|
97
31
|
siderConfig: {
|
|
98
32
|
menu: [
|
|
99
33
|
{
|
|
100
|
-
//
|
|
34
|
+
// 可递归 menuItem(除 moduleType === sider)
|
|
101
35
|
},
|
|
102
|
-
// ...
|
|
103
36
|
],
|
|
104
37
|
},
|
|
105
|
-
|
|
106
38
|
// 当moduleType == iframe 时
|
|
107
39
|
iframeConfig: {
|
|
108
|
-
path: '', // iframe
|
|
40
|
+
path: '', // iframe 路径
|
|
109
41
|
},
|
|
110
|
-
|
|
111
42
|
// 当moduleType == custom 时
|
|
112
43
|
customConfig: {
|
|
113
44
|
path: '', // 自定义路由路径
|
|
114
45
|
},
|
|
115
|
-
|
|
116
46
|
// 当moduleType == schema 时
|
|
117
47
|
schemaConfig: {
|
|
118
48
|
api: '', // 数据源API(遵循 RESTFUL 规范)
|
|
@@ -120,38 +50,19 @@ Model 是 Elpis 的**核心配置单元**,通过定义领域模型的结构、
|
|
|
120
50
|
// 板块数据结构
|
|
121
51
|
type: 'object',
|
|
122
52
|
properties: {
|
|
123
|
-
|
|
124
|
-
...schema, // 标准 schema 配置
|
|
125
|
-
type: '', // 字段类型
|
|
53
|
+
key: {
|
|
54
|
+
...schema, // 标准 schema 配置
|
|
55
|
+
type: '', // 字段类型
|
|
126
56
|
label: '', // 字段的中文名
|
|
127
57
|
// 字段在 table 中的相关配置
|
|
128
58
|
tableOption: {
|
|
129
59
|
...elTableColumnConfig, // 标准 el-table-column 配置
|
|
60
|
+
toFixed: 0, // 保留小数点后几位
|
|
130
61
|
visible: true, // 默认为 true(为 false 时,表示不在 table 中显示)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// 当前字段的格式化配置
|
|
134
|
-
formatConfig:{
|
|
135
|
-
color: 'red', // 单元格颜色(支持十六进制/rgb/颜色名)
|
|
136
|
-
// 1. 自定义格式化函数(优先级最高)// value为当前单元格的值,row为当前行的数据
|
|
137
|
-
formatFn: (value, row) => `${value}元`,
|
|
138
|
-
|
|
139
|
-
// 以下为预设格式化类型配置。(可选,'date'/'enum'/'number') // formatType 和 formatOptions 需同时配置。
|
|
140
|
-
// 1. 日期格式化
|
|
141
|
-
formatType: 'date', // 预设日期类型
|
|
142
|
-
formatOptions: 'YYYY-MM-DD HH:mm:ss', // 日期格式(可选,默认YYYY-MM-DD HH:mm:ss)
|
|
143
|
-
// 2. 枚举格式化(如性别、状态)
|
|
144
|
-
formatType: 'enum', // 预设枚举类型
|
|
145
|
-
formatOptions: { 1: '男', 2: '女', 3: '未知' },
|
|
146
|
-
enumColor: { 1: 'green', 2: '#e6a23c', 3: 'orange' } // 枚举颜色映射(支持颜色名/十六进制/rgb)
|
|
147
|
-
// 3. 数字格式化(如金额、百分比)
|
|
148
|
-
formatType: 'number', // 预设数字类型
|
|
149
|
-
formatOptions: { toFixed: 2, unit: '元' }, // 保留2位小数+添加单位
|
|
150
|
-
}
|
|
151
62
|
},
|
|
152
63
|
// 字段在 search-bar 中的相关配置
|
|
153
64
|
searchOption: {
|
|
154
|
-
...
|
|
65
|
+
...eleComponentConfig, // 标准 el-component-column 配置
|
|
155
66
|
comType: '', // 配置组件类型(如 input/select/...)
|
|
156
67
|
default: '', // 默认值
|
|
157
68
|
|
|
@@ -165,45 +76,24 @@ Model 是 Elpis 的**核心配置单元**,通过定义领域模型的结构、
|
|
|
165
76
|
// 字段在 createForm 中相关配置
|
|
166
77
|
createFormOption: {
|
|
167
78
|
...elComponentConfig, // 标准 el-component 配置
|
|
168
|
-
comType: '', //
|
|
79
|
+
comType: '', // 控件型(如 input/select/...)
|
|
169
80
|
visible: true, // 是否展示(true/false),默认为 true
|
|
170
81
|
disabled: false, // 是否禁用(true/false),默认为 false
|
|
171
|
-
default: '
|
|
172
|
-
|
|
173
|
-
// 当 comType === 'select'|'radio'|'checkbox' 时生效 // 枚举列表
|
|
174
|
-
enumList: [
|
|
175
|
-
{ value: 1, label: 'option1' },
|
|
176
|
-
{ value: 2, label: 'option2' },
|
|
177
|
-
],
|
|
178
|
-
|
|
179
|
-
// 当 comType === 'select'时并使用了api时生效。适配接口返回字段名不一致的情况(如接口返回 name/code,可通过配置映射)
|
|
180
|
-
labelKey:'name',
|
|
181
|
-
valueKey: 'code',
|
|
82
|
+
default: '', // 默认值
|
|
182
83
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
apiParams: '', // 传递接口参数
|
|
186
|
-
|
|
187
|
-
// 当 comType === 'switch' 时生效
|
|
188
|
-
switchModelType:'', // switch 类型,可选值:'boolean'、'string',默认:'boolean'
|
|
189
|
-
|
|
190
|
-
// 当 comType === 'dateTimePicker' 时。配置 shortcuts 快捷选项时,value 需直接传递 Date 对象。禁止直接使用函数(() => {})作为 value,否则会导致组件无法解析赋值
|
|
191
|
-
shortcuts: [
|
|
192
|
-
{ text: '今天', value: new Date() },
|
|
193
|
-
{
|
|
194
|
-
text: '昨天',
|
|
195
|
-
// value 不能是函数类型。必须使用立即执行函数提前计算并返回Date对象
|
|
196
|
-
value: (() => {
|
|
197
|
-
return new Date().setDate(new Date().getDate() - 1);
|
|
198
|
-
})(),
|
|
199
|
-
},
|
|
200
|
-
],
|
|
84
|
+
// 当 comType === 'select' 时生效
|
|
85
|
+
enumList: [], // 枚举列表
|
|
201
86
|
},
|
|
202
87
|
// 字段在 editForm 表单中的相关配置
|
|
203
88
|
editFormOption: {
|
|
204
89
|
...elComponentConfig, // 标准 el-component 配置
|
|
205
90
|
comType: '', // 控件型(如 input/select/...)
|
|
206
|
-
//
|
|
91
|
+
visible: true, // 是否展示(true/false),默认为 true
|
|
92
|
+
disabled: false, // 是否禁用(true/false),默认为 false
|
|
93
|
+
default: '', // 默认值
|
|
94
|
+
|
|
95
|
+
// 当 comType === 'select' 时生效
|
|
96
|
+
enumList: [], // 枚举列表
|
|
207
97
|
},
|
|
208
98
|
detailPanelOption: {
|
|
209
99
|
...elComponentConfig, // 标准 el-component 配置
|
|
@@ -214,55 +104,36 @@ Model 是 Elpis 的**核心配置单元**,通过定义领域模型的结构、
|
|
|
214
104
|
},
|
|
215
105
|
// table 相关配置
|
|
216
106
|
tableConfig: {
|
|
217
|
-
// 表格头部按钮
|
|
218
107
|
headerButtons: [
|
|
219
108
|
{
|
|
220
109
|
label: '', // 按钮名称
|
|
221
110
|
eventKey: '', // 按钮事件名称
|
|
222
111
|
// 按钮事件具体配置
|
|
223
112
|
eventOption: {
|
|
224
|
-
// 当 eventKey === 'showComponent'
|
|
113
|
+
// 当 eventKey === 'showComponent'
|
|
225
114
|
comName: '', // 组件名称
|
|
226
115
|
},
|
|
227
116
|
...elButtonConfig, // 标准 el-button 配置
|
|
228
117
|
},
|
|
229
|
-
|
|
230
|
-
],
|
|
231
|
-
// 表格行内按钮
|
|
118
|
+
], // 表头按钮
|
|
232
119
|
rowButtons: [
|
|
233
|
-
// 按钮事件具体配置
|
|
234
120
|
{
|
|
235
121
|
label: '', // 按钮名称
|
|
236
122
|
eventKey: '', // 按钮事件名称
|
|
237
123
|
eventOption: {
|
|
238
|
-
// 当 eventKey === 'showComponent'
|
|
124
|
+
// 当 eventKey === 'showComponent'
|
|
239
125
|
comName: '', // 组件名称
|
|
240
126
|
|
|
241
|
-
// 当 eventKey === 'useApi'
|
|
242
|
-
apiOption: {
|
|
243
|
-
api: '', // 接口地址
|
|
244
|
-
},
|
|
245
|
-
|
|
246
127
|
// 当 eventKey === 'remove'时,可填
|
|
247
128
|
params: {
|
|
248
129
|
// paramsKey 等于 参数的键值
|
|
249
|
-
// rowValueKey 等于
|
|
250
|
-
paramsKey: rowValueKey,
|
|
130
|
+
// rowValueKey 等于 参数值,格式为 schema::tableKey,到 table 中找相应的字段
|
|
131
|
+
paramsKey: rowValueKey,
|
|
251
132
|
},
|
|
252
|
-
},
|
|
133
|
+
}, // 按钮事件具体配置
|
|
253
134
|
...elButtonConfig, // 标准 el-button 配置
|
|
254
135
|
},
|
|
255
|
-
|
|
256
|
-
showSelection: 'false' // 默认不显示多选列
|
|
257
|
-
// Table所有原生属性(Element Plus TableProps类型)
|
|
258
|
-
tableProps: {
|
|
259
|
-
rowKey: 'id', // 表格的唯一标识字段。默认是id
|
|
260
|
-
......,
|
|
261
|
-
},
|
|
262
|
-
// 分页所有原生属性(Element Plus PaginationProps类型)
|
|
263
|
-
paginationProps: {...}
|
|
264
|
-
// ...
|
|
265
|
-
],
|
|
136
|
+
], // 行按钮
|
|
266
137
|
},
|
|
267
138
|
// search-bar 相关配置
|
|
268
139
|
searchConfig: {},
|
|
@@ -272,25 +143,17 @@ Model 是 Elpis 的**核心配置单元**,通过定义领域模型的结构、
|
|
|
272
143
|
createForm: {
|
|
273
144
|
title: '', // 表单标题
|
|
274
145
|
saveBtnText: '', // 保存按钮文案
|
|
275
|
-
type: '', // 弹窗样式,可选值:'drawer'、'dialog',默认:'dialog'
|
|
276
|
-
dialogWidth: '800px', // 弹窗宽度,默认:'500px'
|
|
277
|
-
formLabelWidth: '70px', // 表单项标签宽度,默认:'70px'
|
|
278
146
|
},
|
|
279
147
|
// edit-form 表单相关配置
|
|
280
148
|
editForm: {
|
|
281
149
|
mainKey: '', // 表单主键,用于唯一标识要修改的数据对象
|
|
282
150
|
title: '', // 表单标题
|
|
283
151
|
saveBtnText: '', // 保存按钮文案
|
|
284
|
-
type: '', // 弹窗样式,可选值:'drawer'、'dialog',默认:'dialog'
|
|
285
|
-
dialogWidth: '800px', // 弹窗宽度,默认:'500px'
|
|
286
|
-
formLabelWidth: '70px', // 表单项标签宽度,默认:'70px'
|
|
287
152
|
},
|
|
288
153
|
// detail-panel 相关配置
|
|
289
154
|
detailPanel: {
|
|
290
155
|
mainKey: '', // 表单主键,用于唯一标识要修改的数据对象
|
|
291
156
|
title: '', // 详情面板标题
|
|
292
|
-
type: '', // 弹窗样式,可选值:'drawer'、'dialog',默认:'dialog'
|
|
293
|
-
dialogWidth: '800px', // 弹窗宽度,默认:'500px'
|
|
294
157
|
},
|
|
295
158
|
// ...支持用户动态扩展
|
|
296
159
|
},
|
|
@@ -300,448 +163,77 @@ Model 是 Elpis 的**核心配置单元**,通过定义领域模型的结构、
|
|
|
300
163
|
}
|
|
301
164
|
```
|
|
302
165
|
|
|
303
|
-
## node 服务端启动
|
|
304
|
-
|
|
305
|
-
### 启动服务端
|
|
306
166
|
|
|
307
|
-
引入 elpis,并启动。其中,startServer 可以传入配置项 options,它会挂载在 koa-app 上,通过`app.options`访问。除此之外,options 还会传递给客户端,因此,你可以传递服务端获取到的数据,以实现数据在服务端渲染时服务端和客户端之间的同步。
|
|
308
167
|
|
|
168
|
+
### 服务端启动
|
|
309
169
|
```javascript
|
|
310
|
-
// 使用框架内置服务
|
|
311
170
|
const { serverStart } = require('@tushi11/elpis');
|
|
312
171
|
|
|
313
172
|
// 启动 elpis 服务
|
|
314
|
-
const app = serverStart({
|
|
315
|
-
name: 'ElpisDemo',
|
|
316
|
-
// 其他配置项
|
|
317
|
-
icon: '/static/logo.png',
|
|
318
|
-
homePage: '/view/project-list',
|
|
319
|
-
});
|
|
173
|
+
const app = serverStart({});
|
|
320
174
|
```
|
|
321
175
|
|
|
322
|
-
### 自定义(拓展)服务端
|
|
323
176
|
|
|
324
|
-
Elpis 支持基于 **elpis-core** 的自定义服务端开发,目录结构遵循约定:
|
|
325
177
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
└── config # 环境配置(多环境支持)
|
|
334
|
-
```
|
|
178
|
+
### 自定义服务端
|
|
179
|
+
- router-schema
|
|
180
|
+
- router
|
|
181
|
+
- controller
|
|
182
|
+
- service
|
|
183
|
+
- extend
|
|
184
|
+
- config
|
|
335
185
|
|
|
336
|
-
- router-schema 是用来定义接口入参出参的 json-schema。实现接口的入参出参校验能力,是 elpis 的中间件`api-params-verify`。
|
|
337
186
|
|
|
338
|
-
```js
|
|
339
|
-
// app/router-schema/**.js (一定是放在一级目录下的,每份都是配置)
|
|
340
|
-
module.exports = {
|
|
341
|
-
'/api/proj/product/list': {
|
|
342
|
-
get: {
|
|
343
|
-
query: {
|
|
344
|
-
type: 'object',
|
|
345
|
-
properties: {
|
|
346
|
-
page: {
|
|
347
|
-
type: 'string',
|
|
348
|
-
},
|
|
349
|
-
size: {
|
|
350
|
-
type: 'string',
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
required: ['page', 'size'],
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
},
|
|
357
|
-
};
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
- router 定义后端接口路由
|
|
361
|
-
|
|
362
|
-
```js
|
|
363
|
-
module.exports = (app, router) => {
|
|
364
|
-
const { demo: demoController } = app.controller;
|
|
365
|
-
router.get('/api/demo', demoController.get.bind(demoController));
|
|
366
|
-
};
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
- controller 定义接口处理器(面向业务 )
|
|
370
|
-
|
|
371
|
-
- app 可以通过这样的方式去访问 ==> `app.controller.customModule.customController`
|
|
372
|
-
|
|
373
|
-
```js
|
|
374
|
-
module.exports = (app) => {
|
|
375
|
-
const BaseController = require('@tushi11/elpis').Controller.Base(app);
|
|
376
|
-
return class customController extends BaseController {
|
|
377
|
-
async create(ctx) {
|
|
378
|
-
this.success(ctx, { user_id: 11 });
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
};
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
- service 定义接口处理服务(增删查改数据库)
|
|
385
|
-
|
|
386
|
-
```js
|
|
387
|
-
module.exports = (app) => {
|
|
388
|
-
return class UserService extends BaseService {
|
|
389
|
-
async get(userId) {
|
|
390
|
-
const result = await database('t_user').select('*').where({
|
|
391
|
-
user_id: userId,
|
|
392
|
-
status: app.status.NORMAL,
|
|
393
|
-
});
|
|
394
|
-
return result[0] ?? {};
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
};
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
- extend 定义其他扩展能力。
|
|
401
|
-
|
|
402
|
-
```js
|
|
403
|
-
module.exports = (app) => {
|
|
404
|
-
return require('moment');
|
|
405
|
-
};
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
- config 定义项目的变量(app/config/config.xxxx.js)
|
|
409
|
-
|
|
410
|
-
```js
|
|
411
|
-
/*
|
|
412
|
-
* 默认配置 config/config.default.js
|
|
413
|
-
* 本地配置 config/config.local.js
|
|
414
|
-
* 测试配置 config/config.beta.js
|
|
415
|
-
* 生产配置 config/config.prod.js
|
|
416
|
-
*/
|
|
417
|
-
// config.prod.js
|
|
418
|
-
module.exports = {
|
|
419
|
-
name: 'demo-prod',
|
|
420
|
-
};
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
## 前端开发
|
|
424
|
-
|
|
425
|
-
### 构建与启动
|
|
426
187
|
|
|
188
|
+
### 前端构建
|
|
427
189
|
```javascript
|
|
428
|
-
// build.js
|
|
429
190
|
const { frontendBuild } = require('@tushi11/elpis');
|
|
430
191
|
|
|
431
|
-
// 编译构建前端工程
|
|
192
|
+
// 编译构建前端工程
|
|
432
193
|
frontendBuild(process.env._ENV);
|
|
433
194
|
```
|
|
434
195
|
|
|
435
|
-
### 多页面拓展(自定义页面)
|
|
436
196
|
|
|
437
|
-
- **约定:**在 `app/pages/` 目录下创建多页面入口( 以`entry.xxx.js`命名)
|
|
438
197
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
import elpisBoot from '@elpisBoot'; // 调用 elpisBoot 中的方法创建vue示例
|
|
442
|
-
import DemoVue from './demo.vue'; // 自定义 vue 组件
|
|
198
|
+
### 自定义页面拓展
|
|
199
|
+
* 在 `app/pages/` 目录下写入口 entry.xxx.js
|
|
443
200
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
{
|
|
447
|
-
path: '/view/auth/login',
|
|
448
|
-
component: () => import('./complex-view/login/login.vue'),
|
|
449
|
-
},
|
|
450
|
-
];
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* elpisBoot 是一个 Vue3 应用的通用启动器函数,用于Vue 实例初始化,实现独立的SPA应用。支持通过配置项实现不同页面的个性化需求。
|
|
454
|
-
* vue 页面主入口,用于启动 vue
|
|
455
|
-
* @params pageComponent vue 入口组件
|
|
456
|
-
* @param {Object} options 配置项
|
|
457
|
-
* @param {Array} options.routes 路由列表
|
|
458
|
-
* @param {Array} options.libs 页面依赖的第三方包
|
|
459
|
-
*/
|
|
460
|
-
elpisBoot(DemoVue, { routes }); // 创建vue示例
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
- 在`app/pages/` 定义 vue 页面
|
|
464
|
-
|
|
465
|
-
```js
|
|
466
|
-
// demo.vue
|
|
467
|
-
<template>
|
|
468
|
-
<div style="color: red;">
|
|
469
|
-
demo
|
|
470
|
-
<el-input v-model="value" />
|
|
471
|
-
</div>
|
|
472
|
-
<router-view></router-view>
|
|
473
|
-
</template>
|
|
474
|
-
|
|
475
|
-
<script setup>
|
|
476
|
-
import { ref, watch } from 'vue'
|
|
477
|
-
const value = ref('');
|
|
478
|
-
|
|
479
|
-
watch(value, (newvalue, oldvalue) => {
|
|
480
|
-
console.log(oldvalue, newvalue);
|
|
481
|
-
})
|
|
482
|
-
</script>
|
|
483
|
-
|
|
484
|
-
<style scoped lang="scss"></style>
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
#### dashboard 页面扩展
|
|
488
|
-
|
|
489
|
-
##### dashboard / custom-view 自定义页面扩展
|
|
490
|
-
|
|
491
|
-
- 在 `app/pages/dashboard/xxx` 下写页面
|
|
492
|
-
|
|
493
|
-
```js
|
|
494
|
-
// app/pages/dashboard/todo/todo.vue
|
|
495
|
-
<template>
|
|
496
|
-
<h1>待开发</h1>
|
|
497
|
-
</template>
|
|
498
|
-
|
|
499
|
-
<script setup></script>
|
|
500
|
-
|
|
501
|
-
<style lang="scss" scoped></style>
|
|
502
|
-
|
|
503
|
-
<script setup>
|
|
504
|
-
import { ref, watch } from 'vue'
|
|
505
|
-
const value = ref('');
|
|
506
|
-
|
|
507
|
-
watch(value, (newvalue, oldvalue) => {
|
|
508
|
-
console.log(oldvalue, newvalue);
|
|
509
|
-
})
|
|
510
|
-
</script>
|
|
511
|
-
|
|
512
|
-
<style scoped lang="scss"></style>
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
##### dashboard 路由配置
|
|
516
|
-
|
|
517
|
-
- 在`app/pages/dashboard/router.js`中进行配置
|
|
518
|
-
|
|
519
|
-
```js
|
|
520
|
-
module.exports = ({ routes, siderRoutes }) => {
|
|
521
|
-
// 头部路由
|
|
522
|
-
routes.push({
|
|
523
|
-
path: '/view/dashboard/todo',
|
|
524
|
-
component: () => import('./todo/todo.vue'),
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
// 侧边路由
|
|
528
|
-
siderRoutes.push({
|
|
529
|
-
path: 'todo',
|
|
530
|
-
component: () => import('./todo/todo.vue'),
|
|
531
|
-
});
|
|
532
|
-
};
|
|
533
|
-
```
|
|
201
|
+
### dashboard / custom-view 自定义页面扩展
|
|
202
|
+
* 在 `app/pages/dashboard/xxx` 下写页面
|
|
534
203
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
| 扩展控制 | 目录位置 | 配置文件 |
|
|
540
|
-
| :----------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
541
|
-
| dashboard / schema-view / components | `app/pages/dashboard/complex-view/schema-view/components` | `app/pages/dashboard/complex-view/schema-view/components/components-config.js` |
|
|
542
|
-
| schema-form | `app/pages/widgets/schema-form/complex-view` | `app/pages/widgets/schema-form/form-item-config.js` |
|
|
543
|
-
| schema-search-bar | `app/pages/widgets/schema-search-bar/complex-view` | `app/pages/widgets/schema-search-bar/search-item-config.js` |
|
|
544
|
-
| header-container | `app/pages/widgets/header-container/complex-view` | `app/pages/widgets/header-container/header-config.js` |
|
|
545
|
-
| | | |
|
|
546
|
-
|
|
547
|
-
```js
|
|
548
|
-
// components-config.js 示例:
|
|
549
|
-
import createForm from './create-form/create-form.vue';
|
|
550
|
-
const ComponentConfig = {
|
|
551
|
-
createForm
|
|
552
|
-
};
|
|
553
|
-
export default ComponentConfig;
|
|
554
|
-
|
|
555
|
-
// form-item-config.js 示例:
|
|
556
|
-
import textarea from './complex-view/textarea/textarea.vue';
|
|
557
|
-
const FormItemConfig = {
|
|
558
|
-
textarea,
|
|
559
|
-
};
|
|
560
|
-
export default FormItemConfig;
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
- **schemaView** 页面的实现是项目的核心,是在菜单 menu 中配置一份基于 **json-schema** 规范的 **schema** 配置,结合各个解析器,实现页面渲染
|
|
564
|
-
|
|
565
|
-
### 配置文件拓展
|
|
566
|
-
|
|
567
|
-
- 在`app/pages/configs/configs.js`进行前端内容配置
|
|
568
|
-
|
|
569
|
-
```js
|
|
570
|
-
module.exports = {
|
|
571
|
-
// 主题 / 布局设置
|
|
572
|
-
themeSetting: {
|
|
573
|
-
menuMode: 'horizontal', // 菜单类型,可选值:'horizontal'、'vertical',默认:'horizontal'
|
|
574
|
-
dialogMode: 'dialog', // 弹窗样式,可选值:'drawer'、'dialog',默认:'dialog'
|
|
575
|
-
useI18n: false, // 是否使用国际化
|
|
576
|
-
/**1. useI18n 为true 时,全局翻译翻译: <div>{{ t('user.name') }}</div>
|
|
577
|
-
*2. 带 t:: 前缀,强制翻译(即使全局 useI18n 为 false):<div>{{ t('t::user.name') }}</div>
|
|
578
|
-
*/
|
|
579
|
-
|
|
580
|
-
// 主题设置组件的默认配置
|
|
581
|
-
language: 'zh-cn',
|
|
582
|
-
showCrumb: true,
|
|
583
|
-
showProject: true,
|
|
584
|
-
showLogo: true,
|
|
585
|
-
isUniqueOpened: false,
|
|
586
|
-
sideWidth: 200,
|
|
587
|
-
sideTheme: 'light',
|
|
588
|
-
sideDarkColor: '#1d2124',
|
|
589
|
-
openMultipleTabs: true,
|
|
590
|
-
theme: '#4A5DFF',
|
|
591
|
-
successTheme: '#67c23a',
|
|
592
|
-
warningTheme: '#e6a23c',
|
|
593
|
-
dangerTheme: '#f56c6c',
|
|
594
|
-
errorTheme: '#f56c6c',
|
|
595
|
-
infoTheme: '#909399', //信息主题色
|
|
596
|
-
},
|
|
204
|
+
### dashboard / schema-view / components 动态组件扩展
|
|
205
|
+
1. 在 `app/pages/dashboard/complex-view/schema-view/components` 下写组件
|
|
206
|
+
2. 配置到 `app/pages/dashboard/complex-view/schema-view/components/components-config.js`
|
|
597
207
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
version: '1.0.0', // 携带项目版本号(便于后端兼容处理)
|
|
602
|
-
},
|
|
603
|
-
|
|
604
|
-
// 请求配置
|
|
605
|
-
requestSetting: {
|
|
606
|
-
// 只有一个代理则使用proxy:
|
|
607
|
-
timeout: 10 * 1000, // 请求超时时长
|
|
608
|
-
// baseURL: 'https://java-admin.likeadmin.cn',
|
|
609
|
-
// proxyIndex: 0, // 默认不配置。配置后,根据下标获取proxyOption配置
|
|
610
|
-
// 反向代理配置 (匹配的 match 如: '/public2', '/public' 要注意顺序)
|
|
611
|
-
proxyOption: [
|
|
612
|
-
{
|
|
613
|
-
host: 'https://java-admin.likeadmin.cn',
|
|
614
|
-
match: '/adminapi',
|
|
615
|
-
},
|
|
616
|
-
// {
|
|
617
|
-
// host: 'http://localhost:3001',
|
|
618
|
-
// match: '/public2',
|
|
619
|
-
// map: (path) => path.replace('/public2', ''),
|
|
620
|
-
// },
|
|
621
|
-
// {
|
|
622
|
-
// host: 'http://localhost:3000', // 目标服务器地址
|
|
623
|
-
// match: '/public', // match: /^\/public/, // 匹配需要代理的请求路径
|
|
624
|
-
// map: (path) => path.replace('/public', ''), // 重写真实请求地址。路径映射,去除 /api 前缀
|
|
625
|
-
// // jar: true, // 启用 cookie 支持
|
|
626
|
-
// },
|
|
627
|
-
],
|
|
628
|
-
},
|
|
629
|
-
};
|
|
630
|
-
```
|
|
208
|
+
### schema-form 控件扩展
|
|
209
|
+
1. 在 `app/widgets/schema-form/complex-view` 下写控件
|
|
210
|
+
2. 配置到 `app/widgets/schema-form/form-item-config.js`
|
|
631
211
|
|
|
632
|
-
|
|
212
|
+
### schema-search-bar 控件扩展
|
|
213
|
+
1. 在 `app/widgets/schema-search-bar/complex-view` 下写控件
|
|
214
|
+
2. 配置到 `app/widgets/schema-search-bar/search-item-config.js`
|
|
633
215
|
|
|
634
|
-
### 组件引入与使用
|
|
635
216
|
|
|
636
|
-
- 在 JS 中,用 大写开头的驼峰
|
|
637
217
|
|
|
638
|
-
|
|
639
|
-
import HeaderContainer from '$elpisWidgets/header-container/header-container.vue';
|
|
640
|
-
```
|
|
218
|
+
## 规范
|
|
641
219
|
|
|
642
|
-
|
|
220
|
+
### 引入组件
|
|
221
|
+
1. 在 JS 中,用 大写开头的驼峰
|
|
222
|
+
```
|
|
223
|
+
import HeaderContainer from '$elpisWidgets/header-container/header-container.vue';
|
|
224
|
+
```
|
|
643
225
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
226
|
+
2. 在template中,用【小写 + '-'】连接(传参数,命名都用这个规则)
|
|
227
|
+
```
|
|
228
|
+
<header-container :title="'项目列表'"></header-container>
|
|
229
|
+
```
|
|
647
230
|
|
|
648
231
|
### 组件传参
|
|
649
|
-
|
|
650
232
|
1. 组件传参用【'-'】,不用驼峰。
|
|
651
|
-
|
|
652
233
|
```
|
|
653
234
|
<sub-menu :menu-item="item"></sub-menu>
|
|
654
235
|
```
|
|
655
|
-
|
|
656
236
|
2. 组件接收参数可以用驼峰
|
|
657
|
-
|
|
658
|
-
```js
|
|
237
|
+
```
|
|
659
238
|
const { menuItem } = defineProps(['menuItem']);
|
|
660
239
|
```
|
|
661
|
-
|
|
662
|
-
### 方法命名规范
|
|
663
|
-
|
|
664
|
-
- `onXxx`:用户点击的事件方法(如:`onMenuSelect`)
|
|
665
|
-
|
|
666
|
-
- 比如做埋点的时候,不是用户点击而是逻辑中调用的,那么记录就有问题了
|
|
667
|
-
|
|
668
|
-
- `handleXxx`:逻辑中调用方法(如:`handleMenuSelect`)
|
|
669
|
-
|
|
670
|
-
### 后端定义路由
|
|
671
|
-
|
|
672
|
-
- 渲染页面的用 `/view/` 开头: router.get('/view/:page', viewController.renderPage.bind(viewController));
|
|
673
|
-
- 接口用 `/api/` 开头:router.post('/api/auth/login', authController.login.bind(authController));
|
|
674
|
-
- 如果携带 proj_key 的业务 api 要用`/api/proj/`开头: router.get('/api/proj/user', userController.get.bind(userController));
|
|
675
|
-
|
|
676
|
-
### 前端构建部分的目录规范如下:
|
|
677
|
-
|
|
678
|
-
- 自定义扩展页需在`app/pages/`目录下创建页面入口文件;
|
|
679
|
-
- 其他组件相关的扩展开发完成后,只需在对应目录的配置文件中添加组件配置,即可被 elpis 前端构建流程一并处理;
|
|
680
|
-
- 模板配置则统一放置于`model/`目录下。
|
|
681
|
-
|
|
682
|
-
### pages 的目录结构
|
|
683
|
-
|
|
684
|
-
- pages 里所有 entry 开头的 js 文件都是多页面。
|
|
685
|
-
|
|
686
|
-
- 多页面进去以后每一个页面渲染后都是用 vue 启动的 vue 实例,所以进来以后又是一个 spa 的单页面应用。
|
|
687
|
-
|
|
688
|
-
- 比如进入到 dashboard 页面,又因为 dashboard 是用 boot 方法来启动的(用了 vue3 的 createApp 构建出来的),所以它又是一个单页面应用
|
|
689
|
-
|
|
690
|
-
- 子模块/子组件,统一放到 complex-view 下面
|
|
691
|
-
|
|
692
|
-
- 子模块的内容与该页面是强相关,所以放在同一个页面下。全局使用的组件放在 widgets 下。
|
|
693
|
-
|
|
694
|
-
```
|
|
695
|
-
|--pages
|
|
696
|
-
|--dashboard
|
|
697
|
-
|--complex-view
|
|
698
|
-
|--header-view // 子模块父文件夹
|
|
699
|
-
|--asserts // 里面放只应用在该子模块的资源
|
|
700
|
-
|--xxx.png
|
|
701
|
-
|--header-view.vue // 必须用一个父文件夹包裹。因为把资源单独放在了通用的资源文件夹里,容易在删除整个模块时漏删了。
|
|
702
|
-
|--dashboard.vue
|
|
703
|
-
|--entry.dashboard.js
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### model 模型配置的目录结构
|
|
707
|
-
|
|
708
|
-
```js
|
|
709
|
-
// modelList => projectList => menuList
|
|
710
|
-
|--app
|
|
711
|
-
|--model
|
|
712
|
-
|-- xxx(自定义的模型名称,如buiness/people)
|
|
713
|
-
|-- model.js (基类。需要放在自定义的model模型目录下)
|
|
714
|
-
|-- project
|
|
715
|
-
|-- xxx.js (子类。如jd.js/pdd.js。项目文件必须放在project目录下)
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
### 提交规范
|
|
719
|
-
|
|
720
|
-
```
|
|
721
|
-
<type>(<scope>): <subject>
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
1. `feat`:新功能(Feature)的添加。
|
|
725
|
-
2. `fix`:Bug 修复
|
|
726
|
-
3. `docs`: 文档 (documentation)的修改
|
|
727
|
-
4. `style`:不影响代码逻辑的样式(如空格、格式、标点等)修改。
|
|
728
|
-
5. `refactor`:代码重构,既不添加新功能也不修复 Bug。
|
|
729
|
-
6. `perf`:性能优化。
|
|
730
|
-
7. `test`:添加测试用例或修改现有测试。
|
|
731
|
-
8. `build`:与构建系统或外部依赖项相关的更改。
|
|
732
|
-
9. `ci`:持续集成(Continuous Integration)配置或脚本的更改。
|
|
733
|
-
10. `chore`:其他不归属上述类型的杂项事务,例如更新构建工具的版本。
|
|
734
|
-
|
|
735
|
-
## 文章参考:
|
|
736
|
-
|
|
737
|
-
[elpis - 前端全栈框架(befool)](https://juejin.cn/post/7534628703218286634?searchId=20250814201022CE9BAC83B06A9D621265)
|
|
738
|
-
|
|
739
|
-
[@yd779821/elpis](https://www.npmjs.com/package/@yd779821/elpis)
|
|
740
|
-
|
|
741
|
-
[Elpis - 基于 Koa + Vue3 的企业级全栈应用框架(allen)](https://juejin.cn/post/7523038523676196910) [@allen-zx/elpis](https://www.npmjs.com/package/@allen-z-x/elpis)
|
|
742
|
-
|
|
743
|
-
[全栈领域模型框架 elpis(fivaclo)](https://juejin.cn/post/7479443523205496832) [@fivaclo/elpis](https://www.npmjs.com/package/@fivaclo/elpis)
|
|
744
|
-
|
|
745
|
-
[@xinight/elpis](https://www.npmjs.com/package/@xinight/elpis)
|
|
746
|
-
|
|
747
|
-
[@tangfsin/elpis](https://www.npmjs.com/package/@tangfsin/elpis?activeTab=readme)
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
// import {
|
|
2
|
+
// ElLoading,
|
|
3
|
+
// ElMessage,
|
|
4
|
+
// ElMessageBox,
|
|
5
|
+
// ElNotification,
|
|
6
|
+
// } from 'element-plus';
|
|
2
7
|
export class Feedback {
|
|
3
8
|
loadingInstance = null;
|
|
4
9
|
static instance = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tushi11/elpis",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
"nprogress": "^0.2.0",
|
|
71
71
|
"path": "^0.12.7",
|
|
72
72
|
"pinia": "^2.1.6",
|
|
73
|
-
"postcss": "^8.4.41",
|
|
74
73
|
"postcss-loader": "^8.1.1",
|
|
75
74
|
"postcss-pxtorem": "^6.1.0",
|
|
76
75
|
"sass": "^1.77.8",
|
|
@@ -107,6 +106,7 @@
|
|
|
107
106
|
"eslint-plugin-vue": "^9.17.0",
|
|
108
107
|
"ghooks": "~1.0.3",
|
|
109
108
|
"mocha": "^6.1.4",
|
|
109
|
+
"postcss": "^8.4.41",
|
|
110
110
|
"supertest": "^4.0.2",
|
|
111
111
|
"tailwindcss": "^3.4.10",
|
|
112
112
|
"validate-commit-msg": "~2.14.0"
|