@tushi11/elpis 1.0.1 → 1.0.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/.eslintrc.js +71 -0
- package/README.md +269 -40
- package/app/controller/base.js +3 -4
- package/app/controller/project.js +58 -5
- package/app/controller/view.js +1 -0
- package/app/extend/moment.js +3 -0
- package/app/middleware/error-handler.js +1 -1
- package/app/middleware/verify/api-only-params-verify.js +47 -0
- package/app/middleware/{api-params-verify.js → verify/api-params-verify.js} +1 -1
- package/app/middleware/{api-sign-verify.js → verify/api-sign-verify.js} +7 -8
- package/app/middleware.js +30 -15
- package/app/pages/boot.js +9 -2
- package/app/pages/common/curl.js +2 -2
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +25 -2
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +9 -0
- package/app/pages/dashboard/complex-view/schema-view/components/components-config.js +3 -9
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +30 -1
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +2 -1
- package/app/pages/store/menu.js +31 -2
- package/app/pages/widgets/header-container/header-container.vue +4 -4
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +1 -1
- package/app/pages/widgets/schema-form/form-item-config.js +3 -9
- package/app/pages/widgets/schema-form/schema-form.vue +5 -4
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +2 -1
- package/app/pages/widgets/schema-search-bar/search-item-config.js +4 -12
- package/app/pages/widgets/schema-table/schema-table.vue +2 -4
- package/app/router/project.js +1 -0
- package/app/router/view.js +3 -2
- package/app/service/base.js +12 -11
- package/app/service/project.js +13 -8
- package/app/view/entry.tpl +1 -1
- package/app/webpack/config/webpack.base.js +104 -77
- package/app/webpack/config/webpack.dev.js +1 -1
- package/app/webpack/config/webpack.prod.js +2 -9
- package/app/webpack/dev.js +11 -4
- package/app/webpack/prod.js +4 -3
- package/config/config.default.js +12 -0
- package/config/ui-config.js +9 -0
- package/elpis-core/index.js +11 -11
- package/elpis-core/loader/config.js +4 -5
- package/elpis-core/loader/controller.js +0 -1
- package/elpis-core/loader/extend.js +3 -3
- package/elpis-core/loader/middleware.js +2 -2
- package/elpis-core/loader/router-schema.js +1 -1
- package/elpis-core/loader/router.js +1 -4
- package/model/index.js +9 -2
- package/package.json +24 -23
- package/test/controller/project.test.js +19 -19
- package/.eslintrc +0 -55
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
// 继承的规则集:从已定义的规则集中扩展基础配置
|
|
3
|
+
extends: [
|
|
4
|
+
// 'plugin:vue/base', // Vue 基础规则集(适用于所有 Vue 版本),包含最核心的语法校验(如模板语法错误、组件基础规范),无版本特定规则
|
|
5
|
+
// 'plugin:vue/recommended', // Vue 2 推荐规则集(基于 base 扩展),包含 Vue 2 最佳实践(如组件命名、Props 规范等),不兼容 Vue 3 语法
|
|
6
|
+
// 'plugin:vue/vue3-essential', // Vue 3 核心基础规则集,包含 Vue 3 必备语法校验(如 <template v-for> 支持、Composition API 规范),确保代码可运行
|
|
7
|
+
'plugin:vue/vue3-recommended', // Vue 3 推荐规则集(基于 vue3-essential 扩展),添加 Vue 3 最佳实践(如避免 XSS 风险、单向数据流规范),适合生产环境
|
|
8
|
+
],
|
|
9
|
+
|
|
10
|
+
// 启用的 ESLint 插件:这里使用 'vue' 插件(eslint-plugin-vue),提供 Vue 语法校验能力
|
|
11
|
+
plugins: ['vue'],
|
|
12
|
+
|
|
13
|
+
// 运行环境:指定代码运行的环境,ESLint 会识别对应环境的全局变量(如 window、process)
|
|
14
|
+
env: {
|
|
15
|
+
browser: true, // 浏览器环境(识别 window、document 等全局变量)
|
|
16
|
+
node: true, // Node.js 环境(识别 process、require 等全局变量)
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
// 解析器:指定用于解析 Vue 单文件组件(.vue)的专用解析器
|
|
20
|
+
parser: 'vue-eslint-parser',
|
|
21
|
+
// 解析器选项:配置解析器的行为
|
|
22
|
+
parserOptions: {
|
|
23
|
+
parser: 'babel-eslint', // 指定用于解析 JavaScript 部分的解析器(注:该包已废弃,建议替换为 @babel/eslint-parser)
|
|
24
|
+
ecmaVersion: 2017, // 支持的 ECMAScript 版本(2017 及以下语法)
|
|
25
|
+
sourceType: 'module', // 代码模块类型(使用 ES 模块语法:import/export)
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// 自定义规则:覆盖或补充继承的规则,值含义:"off"(关闭)、"warn"(警告)、"error"(报错)// 详细规则说明:https://eslint.org/docs/rules/
|
|
29
|
+
rules: {
|
|
30
|
+
// JavaScript 核心规则
|
|
31
|
+
'no-unused-vars': [2, { args: 'none' }], // 严格检查未使用的变量(报错级别),忽略未使用的函数参数
|
|
32
|
+
strict: 'off', // 关闭严格模式检查(不强制使用 'use strict')
|
|
33
|
+
'valid-jsdoc': 'off', // 关闭 JSDoc 语法有效性检查
|
|
34
|
+
'jsdoc/require-param-description': 'off', // 关闭 JSDoc 参数描述必填校验
|
|
35
|
+
'jsdoc/require-param-type': 'off', // 关闭 JSDoc 参数类型必填校验
|
|
36
|
+
'jsdoc/check-param-names': 'off', // 关闭 JSDoc 参数名一致性检查
|
|
37
|
+
'jsdoc/require-param': 'off', // 关闭 JSDoc 强制参数说明校验
|
|
38
|
+
'jsdoc/check-tag-names': 'off', // 关闭 JSDoc 标签名合法性检查
|
|
39
|
+
'linebreak-style': 'off', // 关闭换行符风格检查(不强制 LF/CRLF)
|
|
40
|
+
'array-bracket-spacing': 'off', // 关闭数组括号间距检查(如 [1, 2] 与 [ 1, 2 ])
|
|
41
|
+
'prefer-promise-reject-errors': 'off', // 关闭 Promise.reject() 必须传入 Error 实例的检查
|
|
42
|
+
'comma-dangle': 'off', // 关闭对象/数组末尾逗号检查(如 {a:1} 与 {a:1,})
|
|
43
|
+
'newline-per-chained-call': 'off', // 关闭链式调用强制换行检查(如 a.b().c())
|
|
44
|
+
'no-loop-func': 'off', // 允许在循环中定义函数(可能导致闭包问题,按需开启)
|
|
45
|
+
'no-empty': 'off', // 允许空代码块(如 if () {})
|
|
46
|
+
'no-else-return': 'off', // 允许 else 块中使用 return(不强制提前 return)
|
|
47
|
+
'no-unneeded-ternary': 'off', // 允许不必要的三元表达式(如 condition ? true : false)
|
|
48
|
+
'no-eval': 'off', // 允许使用 eval()(存在安全风险,不建议在生产环境关闭)
|
|
49
|
+
'prefer-destructuring': 'off', // 不强制使用解构赋值(如 const {a} = obj)
|
|
50
|
+
'no-param-reassign': 'off', // 允许修改函数参数(可能导致副作用,按需开启)
|
|
51
|
+
'max-len': 'off', // 关闭单行代码长度限制
|
|
52
|
+
'no-restricted-syntax': 'off', // 不限制使用特定语法(如 for...in 循环)
|
|
53
|
+
'no-plusplus': 'off', // 允许使用 ++/-- 运算符
|
|
54
|
+
'no-useless-escape': 'off', // 允许不必要的转义字符(如 \' 在双引号字符串中)
|
|
55
|
+
'no-nested-ternary': 'off', // 允许嵌套三元表达式
|
|
56
|
+
radix: 'off', // 关闭 parseInt 必须指定基数的检查
|
|
57
|
+
'arrow-body-style': 'off', // 不强制箭头函数体风格(如 (a) => { return a } 与 (a) => a)
|
|
58
|
+
'arrow-parens': 'off', // 不强制箭头函数参数是否带括号(如 a => a 与 (a) => a)
|
|
59
|
+
// Vue 相关规则
|
|
60
|
+
'vue/multi-word-component-names': 'off', // 允许组件使用单单词命名(默认要求多单词避免与 HTML 标签冲突)
|
|
61
|
+
'vue/valid-v-for': 'off', // 关闭 v-for 语法合法性检查(使用自定义校验逻辑)
|
|
62
|
+
'vue/no-multiple-template-root': 'off', // 允许多根模板(Vue 3 原生支持,默认开启)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// 全局变量声明:指定代码中可直接使用的全局变量,无需显式声明
|
|
66
|
+
globals: {
|
|
67
|
+
$: true, // 允许使用全局 jQuery 变量(如 $.ajax())
|
|
68
|
+
axios: true, // 允许使用全局 axios 变量(如 axios.get())
|
|
69
|
+
Vue: true, // 允许使用全局 Vue 变量(如 Vue.createApp())
|
|
70
|
+
},
|
|
71
|
+
};
|
package/README.md
CHANGED
|
@@ -2,47 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
## 一个企业级应用框架,通过全栈实现。
|
|
4
4
|
|
|
5
|
-
### model配置
|
|
5
|
+
### model 配置
|
|
6
|
+
|
|
6
7
|
```javascript
|
|
7
8
|
{
|
|
8
9
|
mode: 'dashboard', // 模板类型,不同模板类型对应不一样的模板数据结构
|
|
9
|
-
name: '', //
|
|
10
|
-
desc: '', //
|
|
11
|
-
icon: '', //
|
|
12
|
-
homePage: '', //
|
|
10
|
+
name: '', // 模板名称
|
|
11
|
+
desc: '', // 模板描述
|
|
12
|
+
icon: '', // 模板图标
|
|
13
|
+
homePage: '/schema?proj_key=pdd&key=product', // 模板首页(项目配置) // proj_key=项目的文件名 || key=头部菜单栏的默认选中项 || sider_key=左侧菜单栏的默认选中项
|
|
13
14
|
// 头部菜单
|
|
14
15
|
menu: [
|
|
15
16
|
{
|
|
16
17
|
key: '', // 菜单唯一描述
|
|
17
18
|
name: '', // 菜单名称
|
|
18
|
-
menuType: '', //
|
|
19
|
+
menuType: '', // 菜单类型(菜单目录、菜单项) group | module
|
|
19
20
|
|
|
20
21
|
// 当menuType == group时,可填
|
|
21
22
|
subMenu: [
|
|
22
23
|
{
|
|
23
|
-
//
|
|
24
|
+
// 可递归的菜单项 menuItem
|
|
24
25
|
},
|
|
26
|
+
// ...
|
|
25
27
|
],
|
|
26
28
|
|
|
27
29
|
// 当menuType == module时,可填
|
|
28
|
-
moduleType: '', //
|
|
30
|
+
moduleType: '', // 模块类型: sider | iframe | custom | schema
|
|
29
31
|
|
|
30
32
|
// 当moduleType == sider 时
|
|
31
33
|
siderConfig: {
|
|
32
34
|
menu: [
|
|
33
35
|
{
|
|
34
|
-
//
|
|
36
|
+
// 可递归的菜单项 menuItem (除 moduleType === sider)
|
|
35
37
|
},
|
|
38
|
+
// ...
|
|
36
39
|
],
|
|
37
40
|
},
|
|
41
|
+
|
|
38
42
|
// 当moduleType == iframe 时
|
|
39
43
|
iframeConfig: {
|
|
40
|
-
path: '', // iframe
|
|
44
|
+
path: '', // iframe 模块路径
|
|
41
45
|
},
|
|
46
|
+
|
|
42
47
|
// 当moduleType == custom 时
|
|
43
48
|
customConfig: {
|
|
44
49
|
path: '', // 自定义路由路径
|
|
45
50
|
},
|
|
51
|
+
|
|
46
52
|
// 当moduleType == schema 时
|
|
47
53
|
schemaConfig: {
|
|
48
54
|
api: '', // 数据源API(遵循 RESTFUL 规范)
|
|
@@ -50,14 +56,14 @@
|
|
|
50
56
|
// 板块数据结构
|
|
51
57
|
type: 'object',
|
|
52
58
|
properties: {
|
|
53
|
-
|
|
54
|
-
...schema, // 标准 schema 配置
|
|
59
|
+
key1: {
|
|
60
|
+
...schema, // 标准 schema 配置 ???
|
|
55
61
|
type: '', // 字段类型
|
|
56
62
|
label: '', // 字段的中文名
|
|
57
63
|
// 字段在 table 中的相关配置
|
|
58
64
|
tableOption: {
|
|
59
65
|
...elTableColumnConfig, // 标准 el-table-column 配置
|
|
60
|
-
toFixed: 0, //
|
|
66
|
+
toFixed: 0, // 保留几位小数点(数字字段)
|
|
61
67
|
visible: true, // 默认为 true(为 false 时,表示不在 table 中显示)
|
|
62
68
|
},
|
|
63
69
|
// 字段在 search-bar 中的相关配置
|
|
@@ -76,7 +82,7 @@
|
|
|
76
82
|
// 字段在 createForm 中相关配置
|
|
77
83
|
createFormOption: {
|
|
78
84
|
...elComponentConfig, // 标准 el-component 配置
|
|
79
|
-
comType: '', //
|
|
85
|
+
comType: '', // 控件类型(如 input/select/...)
|
|
80
86
|
visible: true, // 是否展示(true/false),默认为 true
|
|
81
87
|
disabled: false, // 是否禁用(true/false),默认为 false
|
|
82
88
|
default: '', // 默认值
|
|
@@ -104,36 +110,46 @@
|
|
|
104
110
|
},
|
|
105
111
|
// table 相关配置
|
|
106
112
|
tableConfig: {
|
|
113
|
+
// 表格头部按钮
|
|
107
114
|
headerButtons: [
|
|
108
115
|
{
|
|
109
116
|
label: '', // 按钮名称
|
|
110
117
|
eventKey: '', // 按钮事件名称
|
|
111
118
|
// 按钮事件具体配置
|
|
112
119
|
eventOption: {
|
|
113
|
-
// 当 eventKey === 'showComponent'
|
|
120
|
+
// 当 eventKey === 'showComponent'时,启用 comName,决定调用哪个组件
|
|
114
121
|
comName: '', // 组件名称
|
|
115
122
|
},
|
|
116
123
|
...elButtonConfig, // 标准 el-button 配置
|
|
117
124
|
},
|
|
118
|
-
|
|
125
|
+
// ...
|
|
126
|
+
],
|
|
127
|
+
// 表格行内按钮
|
|
119
128
|
rowButtons: [
|
|
129
|
+
// 按钮事件具体配置
|
|
120
130
|
{
|
|
121
131
|
label: '', // 按钮名称
|
|
122
132
|
eventKey: '', // 按钮事件名称
|
|
123
133
|
eventOption: {
|
|
124
|
-
// 当 eventKey === 'showComponent'
|
|
134
|
+
// 当 eventKey === 'showComponent'时,启用 comName,决定调用哪个组件
|
|
125
135
|
comName: '', // 组件名称
|
|
126
136
|
|
|
137
|
+
// 当 eventKey === 'useApi'
|
|
138
|
+
apiOption: {
|
|
139
|
+
api: '', // 接口地址
|
|
140
|
+
},
|
|
141
|
+
|
|
127
142
|
// 当 eventKey === 'remove'时,可填
|
|
128
143
|
params: {
|
|
129
144
|
// paramsKey 等于 参数的键值
|
|
130
145
|
// rowValueKey 等于 参数值,格式为 schema::tableKey,到 table 中找相应的字段
|
|
131
|
-
paramsKey: rowValueKey,
|
|
146
|
+
paramsKey: rowValueKey, // 【 例如 product_id: 'schema::product_id' 】
|
|
132
147
|
},
|
|
133
|
-
},
|
|
148
|
+
},
|
|
134
149
|
...elButtonConfig, // 标准 el-button 配置
|
|
135
150
|
},
|
|
136
|
-
|
|
151
|
+
// ...
|
|
152
|
+
],
|
|
137
153
|
},
|
|
138
154
|
// search-bar 相关配置
|
|
139
155
|
searchConfig: {},
|
|
@@ -163,77 +179,290 @@
|
|
|
163
179
|
}
|
|
164
180
|
```
|
|
165
181
|
|
|
166
|
-
|
|
167
|
-
|
|
168
182
|
### 服务端启动
|
|
183
|
+
|
|
169
184
|
```javascript
|
|
170
185
|
const { serverStart } = require('@tushi11/elpis');
|
|
171
186
|
|
|
172
187
|
// 启动 elpis 服务
|
|
173
|
-
const app = serverStart({
|
|
188
|
+
const app = serverStart({
|
|
189
|
+
title: 'elpis应用',
|
|
190
|
+
// 其他配置项
|
|
191
|
+
});
|
|
174
192
|
```
|
|
175
193
|
|
|
176
|
-
|
|
177
|
-
|
|
178
194
|
### 自定义服务端
|
|
195
|
+
|
|
179
196
|
- router-schema
|
|
197
|
+
|
|
198
|
+
- app/router-schema/\*\*.js (一定是放在一级目录下的,每份都是配置)
|
|
199
|
+
|
|
200
|
+
- ```js
|
|
201
|
+
module.exports = {
|
|
202
|
+
'${api1}': ${jsonSchema},
|
|
203
|
+
'${api2}': ${jsonSchema},
|
|
204
|
+
'${api3}': ${jsonSchema},
|
|
205
|
+
'${api4}': ${jsonSchema},
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
180
209
|
- router
|
|
210
|
+
|
|
181
211
|
- controller
|
|
212
|
+
|
|
213
|
+
- 通过这样的方式去访问 ==> app.controller.customModule.customController
|
|
214
|
+
|
|
182
215
|
- service
|
|
216
|
+
|
|
183
217
|
- extend
|
|
218
|
+
|
|
184
219
|
- config
|
|
185
220
|
|
|
221
|
+
- 目录下对应的 config 配置 (app/config/config.xxxx.js)
|
|
222
|
+
|
|
223
|
+
* 默认配置 config/config.default.js
|
|
224
|
+
* 本地配置 config/config.local.js
|
|
225
|
+
* 测试配置 config/config.beta.js
|
|
226
|
+
* 生产配置 config/config.prod.js
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
// config.prod.js
|
|
230
|
+
module.exports = {
|
|
231
|
+
name: 'demo-prod'
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
1. 在 router-schema 定义接口的入参出参的json-schema,实现接口的入参出参校验能力,这一能力的提供者是elpis的中间件```api-params-verify```。
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
module.exports = {
|
|
243
|
+
'/api/demo': {
|
|
244
|
+
get: {
|
|
245
|
+
query: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
key: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
2. 在router中定义接口路由。
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
module.exports = (app, router) => {
|
|
264
|
+
const { demo: demoController } = app.controller;
|
|
265
|
+
router.get('/api/demo', demoController.get.bind(demoController));
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
3. 在controller中定义接口处理器。
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
module.exports = (app) => {
|
|
275
|
+
return class DemoController {
|
|
276
|
+
get(ctx) {
|
|
277
|
+
this.success(ctx, [{a: 1, b: 'demo'}], {
|
|
278
|
+
total: 3,
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
4. 在service中定义接口处理服务。
|
|
288
|
+
|
|
289
|
+
```js
|
|
290
|
+
module.exports = (app) => class DemoService {
|
|
291
|
+
// sevice 能力
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
5. 在extend中定义其他扩展能力。elpis内置了日志功能,基于log4js。
|
|
296
|
+
|
|
297
|
+
6. 在根目录的config中定义项目的变量。
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
// config.dev.js
|
|
301
|
+
module.exports = {
|
|
302
|
+
name: 'demo-default'
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
// config.prod.js
|
|
308
|
+
module.exports = {
|
|
309
|
+
name: 'demo-prod'
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
186
315
|
|
|
187
316
|
|
|
188
317
|
### 前端构建
|
|
318
|
+
|
|
189
319
|
```javascript
|
|
320
|
+
// build.js
|
|
190
321
|
const { frontendBuild } = require('@tushi11/elpis');
|
|
191
322
|
|
|
192
|
-
// 编译构建前端工程
|
|
323
|
+
// 编译构建前端工程 - 这里会根据环境变量自动选择启动开发环境还是打包生产环境
|
|
193
324
|
frontendBuild(process.env._ENV);
|
|
194
325
|
```
|
|
195
326
|
|
|
196
327
|
|
|
197
328
|
|
|
198
|
-
###
|
|
199
|
-
|
|
329
|
+
### 拓展开发
|
|
330
|
+
|
|
331
|
+
#### 自定义页面拓展
|
|
332
|
+
|
|
333
|
+
- 在 `app/pages/` 目录下写入口 entry.xxx.js
|
|
334
|
+
|
|
335
|
+
定义一个demo页面
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
// app/pages/demo/entry.demo.js
|
|
339
|
+
import elpisBoot from '@elpisBoot';
|
|
340
|
+
import DemoVue from './demo.vue'
|
|
341
|
+
elpisBoot(DemoVue);
|
|
200
342
|
|
|
201
|
-
|
|
202
|
-
|
|
343
|
+
// demo.vue
|
|
344
|
+
<template>
|
|
345
|
+
<div style="color: red;">
|
|
346
|
+
demo
|
|
347
|
+
<el-input v-model="value" />
|
|
348
|
+
</div>
|
|
349
|
+
</template>
|
|
350
|
+
|
|
351
|
+
<script setup>
|
|
352
|
+
import { ref, watch } from 'vue'
|
|
353
|
+
const value = ref('');
|
|
354
|
+
|
|
355
|
+
watch(value, (newvalue, oldvalue) => {
|
|
356
|
+
console.log(oldvalue, newvalue);
|
|
357
|
+
})
|
|
358
|
+
</script>
|
|
359
|
+
|
|
360
|
+
<style scoped lang="less"></style>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
#### dashboard / custom-view 自定义页面扩展
|
|
366
|
+
|
|
367
|
+
- 在 `app/pages/dashboard/xxx` 下写页面
|
|
368
|
+
|
|
369
|
+
#### dashboard / schema-view / components 动态组件扩展
|
|
203
370
|
|
|
204
|
-
### dashboard / schema-view / components 动态组件扩展
|
|
205
371
|
1. 在 `app/pages/dashboard/complex-view/schema-view/components` 下写组件
|
|
206
372
|
2. 配置到 `app/pages/dashboard/complex-view/schema-view/components/components-config.js`
|
|
207
373
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
374
|
+
#### schema-form 控件扩展
|
|
375
|
+
|
|
376
|
+
1. 在 `app/pages/widgets/schema-form/complex-view` 下写控件
|
|
377
|
+
2. 配置到 `app/pages/widgets/schema-form/form-item-config.js`
|
|
378
|
+
|
|
379
|
+
#### schema-search-bar 控件扩展
|
|
380
|
+
|
|
381
|
+
1. 在 `app/pages/widgets/schema-search-bar/complex-view` 下写控件
|
|
382
|
+
2. 配置到 `app/pages/widgets/schema-search-bar/search-item-config.js`
|
|
383
|
+
|
|
384
|
+
#### header-container 控件扩展
|
|
211
385
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
2. 配置到 `app/widgets/schema-search-bar/search-item-config.js`
|
|
386
|
+
1. 在 `app/pages/widgets/header-container/complex-view` 下写控件
|
|
387
|
+
2. 配置到 `app/pages/widgets/header-container/header-config.js`
|
|
215
388
|
|
|
216
389
|
|
|
217
390
|
|
|
218
|
-
## 规范
|
|
391
|
+
## 规范(约定优于配置)
|
|
219
392
|
|
|
220
393
|
### 引入组件
|
|
394
|
+
|
|
221
395
|
1. 在 JS 中,用 大写开头的驼峰
|
|
396
|
+
|
|
222
397
|
```
|
|
223
398
|
import HeaderContainer from '$elpisWidgets/header-container/header-container.vue';
|
|
224
399
|
```
|
|
225
400
|
|
|
226
|
-
2. 在template中,用【小写 + '-'】连接(传参数,命名都用这个规则)
|
|
401
|
+
2. 在 template 中,用【小写 + '-'】连接(传参数,命名都用这个规则)
|
|
227
402
|
```
|
|
228
403
|
<header-container :title="'项目列表'"></header-container>
|
|
229
404
|
```
|
|
230
405
|
|
|
231
406
|
### 组件传参
|
|
407
|
+
|
|
232
408
|
1. 组件传参用【'-'】,不用驼峰。
|
|
233
409
|
```
|
|
234
410
|
<sub-menu :menu-item="item"></sub-menu>
|
|
235
411
|
```
|
|
236
412
|
2. 组件接收参数可以用驼峰
|
|
237
|
-
```
|
|
413
|
+
```js
|
|
238
414
|
const { menuItem } = defineProps(['menuItem']);
|
|
239
415
|
```
|
|
416
|
+
|
|
417
|
+
### 方法命名
|
|
418
|
+
|
|
419
|
+
onMenuSelect:用户点击的事件方法
|
|
420
|
+
|
|
421
|
+
- 比如做埋点的时候,不是用户点击而是逻辑中调用的,那么记录就有问题了
|
|
422
|
+
|
|
423
|
+
handleMenuSelect:逻辑中调用方法
|
|
424
|
+
|
|
425
|
+
### pages的目录结构
|
|
426
|
+
|
|
427
|
+
- pages里所有entry开头的js文件都是多页面。
|
|
428
|
+
- 多页面进去以后每一个页面渲染后都是用vue启动的vue实例,所以进来以后又是一个spa的单页面应用。
|
|
429
|
+
- 比如进入到dashboard页面,又因为dashboard是用boot方法来启动的(用了vue3的createApp构建出来的),所以它又是一个单页面应用
|
|
430
|
+
|
|
431
|
+
- 子模块/子组件,统一放到complex-view下面
|
|
432
|
+
|
|
433
|
+
- 子模块的内容与该页面是强相关,所以放在同一个页面下。全局使用的组件放在widgets下。
|
|
434
|
+
|
|
435
|
+
```
|
|
436
|
+
|--pages
|
|
437
|
+
|--dashboard
|
|
438
|
+
|--complex-view
|
|
439
|
+
|--header-view // 子模块父文件夹
|
|
440
|
+
|--asserts // 里面放只应用在该子模块的资源
|
|
441
|
+
|--xxx.png
|
|
442
|
+
|--header-view.vue // 必须用一个父文件夹包裹。因为把资源单独放在了通用的资源文件夹里,容易在删除整个模块时漏删了。
|
|
443
|
+
|--dashboard.vue
|
|
444
|
+
|--entry.dashboard.js
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
### 后端定义路由
|
|
452
|
+
|
|
453
|
+
- 渲染页面的用 `/view/` 开头: router.get('/view/:page', viewController.renderPage.bind(viewController));
|
|
454
|
+
- 接口用 `/api/` 开头:router.post('/api/auth/login', authController.login.bind(authController));
|
|
455
|
+
- 如果携带 proj_key 的业务api要用`/api/proj/`开头: router.get('/api/proj/user', userController.get.bind(userController));
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
### model模型配置的目录结构
|
|
459
|
+
|
|
460
|
+
```js
|
|
461
|
+
// modelList => projectList => menuList
|
|
462
|
+
|--app
|
|
463
|
+
|--model
|
|
464
|
+
|-- xxx(自定义的模型名称,如buiness/people)
|
|
465
|
+
|-- model.js (基类。需要放在自定义的model模型目录下)
|
|
466
|
+
|-- project
|
|
467
|
+
|-- xxx.js (子类。如jd.js/pdd.js。项目文件必须放在project目录下)
|
|
468
|
+
```
|
package/app/controller/base.js
CHANGED
|
@@ -5,9 +5,9 @@ module.exports = (app) =>
|
|
|
5
5
|
* 统一收拢 controller 相关的公共方法
|
|
6
6
|
*/
|
|
7
7
|
constructor() {
|
|
8
|
-
this.app = app;
|
|
9
|
-
this.config = app.config;
|
|
10
|
-
this.service = app.service;
|
|
8
|
+
// this.app = app;
|
|
9
|
+
// this.config = app.config;
|
|
10
|
+
// this.service = app.service;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -32,7 +32,6 @@ module.exports = (app) =>
|
|
|
32
32
|
* @param {object} code 错误码
|
|
33
33
|
*/
|
|
34
34
|
fail(ctx, message, code) {
|
|
35
|
-
// ctx.status = 200;
|
|
36
35
|
ctx.body = {
|
|
37
36
|
success: false,
|
|
38
37
|
message,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
1
2
|
module.exports = (app) => {
|
|
2
3
|
const BaseController = require('./base')(app);
|
|
3
4
|
return class ProjectController extends BaseController {
|
|
@@ -14,6 +15,10 @@ module.exports = (app) => {
|
|
|
14
15
|
this.fail(ctx, '获取项目异常', 50000);
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
18
|
+
if (!projConfig.homePage) {
|
|
19
|
+
this.fail(ctx, `${projConfig.name} - 未找到homePage`, 50000);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
this.success(ctx, projConfig);
|
|
19
24
|
}
|
|
@@ -23,17 +28,24 @@ module.exports = (app) => {
|
|
|
23
28
|
*/
|
|
24
29
|
getList(ctx) {
|
|
25
30
|
const { proj_key: projKey } = ctx.request.query;
|
|
26
|
-
|
|
27
31
|
const { project: projectService } = app.service;
|
|
28
32
|
const projectList = projectService.getList({ projKey });
|
|
33
|
+
let errorMsg = null;
|
|
29
34
|
|
|
30
35
|
// 构造关键数据 list
|
|
31
36
|
const dtoProjectList = projectList.map((item) => {
|
|
32
37
|
const { modelKey, key, name, desc, homePage } = item;
|
|
38
|
+
if (!homePage) {
|
|
39
|
+
return (errorMsg = item);
|
|
40
|
+
}
|
|
33
41
|
return { modelKey, key, name, desc, homePage };
|
|
34
42
|
});
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
if (errorMsg) {
|
|
45
|
+
return this.fail(ctx, `${errorMsg.name} - 未找到homePage`, 50000);
|
|
46
|
+
} else {
|
|
47
|
+
this.success(ctx, dtoProjectList);
|
|
48
|
+
}
|
|
37
49
|
}
|
|
38
50
|
|
|
39
51
|
/**
|
|
@@ -42,6 +54,7 @@ module.exports = (app) => {
|
|
|
42
54
|
async getModelList(ctx) {
|
|
43
55
|
const { project: projectService } = app.service;
|
|
44
56
|
const modelList = await projectService.getModelList();
|
|
57
|
+
let errorMsg = null;
|
|
45
58
|
|
|
46
59
|
// 构造返回结果,只返回关键数据
|
|
47
60
|
const dtoModelList = modelList.reduce((preList, item) => {
|
|
@@ -53,8 +66,11 @@ module.exports = (app) => {
|
|
|
53
66
|
|
|
54
67
|
// 构造 project 关键数据
|
|
55
68
|
const dtoProject = Object.keys(project).reduce((preObj, projKey) => {
|
|
56
|
-
const { key, name, desc, homePage } = project[projKey];
|
|
57
|
-
|
|
69
|
+
const { key, name, desc, homePage, modelKey } = project[projKey];
|
|
70
|
+
if (!homePage) {
|
|
71
|
+
return (errorMsg = project[projKey]);
|
|
72
|
+
}
|
|
73
|
+
preObj[projKey] = { key, name, desc, homePage, modelKey };
|
|
58
74
|
return preObj;
|
|
59
75
|
}, {});
|
|
60
76
|
// 另一种写法
|
|
@@ -72,7 +88,44 @@ module.exports = (app) => {
|
|
|
72
88
|
|
|
73
89
|
return preList;
|
|
74
90
|
}, []);
|
|
75
|
-
|
|
91
|
+
|
|
92
|
+
if (errorMsg) {
|
|
93
|
+
this.fail(ctx, `${errorMsg.name} - 未找到homePage`, 50000);
|
|
94
|
+
} else {
|
|
95
|
+
this.success(ctx, dtoModelList);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 获取城市天气信息
|
|
101
|
+
* @param {string} city 城市名称
|
|
102
|
+
* @example https://api.bugpk.com/api/citywid?city=朝阳
|
|
103
|
+
*/
|
|
104
|
+
async getCityWeather(ctx) {
|
|
105
|
+
const { city } = ctx.request.query;
|
|
106
|
+
|
|
107
|
+
const res = await axios({
|
|
108
|
+
method: 'get',
|
|
109
|
+
url: 'https://api.bugpk.com/api/citywid',
|
|
110
|
+
params: {
|
|
111
|
+
city: city || '朝阳',
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
if (res.status !== 200) return this.fail(ctx, '获取天气信息失败', 50000);
|
|
115
|
+
|
|
116
|
+
const dtoRes = JSON.parse(JSON.stringify(res.data));
|
|
117
|
+
|
|
118
|
+
if (dtoRes.success === true) {
|
|
119
|
+
return this.success(ctx, dtoRes.results, { total: dtoRes.count });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const firstJsonStr = dtoRes.split('}')[0] + '}'; // 拼接回完整的第一个JSON
|
|
124
|
+
const firstJson = JSON.parse(firstJsonStr);
|
|
125
|
+
return this.fail(ctx, firstJson.error, 50000);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return this.fail(ctx, '获取天气信息失败', 50000);
|
|
128
|
+
}
|
|
76
129
|
}
|
|
77
130
|
};
|
|
78
131
|
};
|
package/app/controller/view.js
CHANGED
|
@@ -9,6 +9,7 @@ module.exports = (app) => {
|
|
|
9
9
|
app.logger.info(`[ViewController] query: ${JSON.stringify(query)}`);
|
|
10
10
|
app.logger.info(`[ViewController] params: ${JSON.stringify(params)}`);
|
|
11
11
|
await ctx.render(`dist/entry.${ctx.params.page}`, {
|
|
12
|
+
// 利用 SSR 服务端渲染,将参数注入到前端模板中。
|
|
12
13
|
projKey: ctx.query?.proj_key,
|
|
13
14
|
name: app.options?.name,
|
|
14
15
|
env: app.env.get(),
|
|
@@ -9,7 +9,7 @@ module.exports = (app) => {
|
|
|
9
9
|
} catch (err) {
|
|
10
10
|
// 异常处理
|
|
11
11
|
const { status, message, detail } = err;
|
|
12
|
-
app.logger.info(JSON.stringify(err));
|
|
12
|
+
app.logger.info('[-- exception --]:', JSON.stringify(err));
|
|
13
13
|
app.logger.error('[-- exception --]:', err);
|
|
14
14
|
app.logger.error('[-- exception --]:', status, message, detail);
|
|
15
15
|
|