@juge0218/elpis 1.0.1

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.
Files changed (79) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc +65 -0
  3. package/README.md +213 -0
  4. package/app/controller/base.js +38 -0
  5. package/app/controller/project.js +70 -0
  6. package/app/controller/view.js +19 -0
  7. package/app/extend/logger.js +29 -0
  8. package/app/middleware/api-params-verify.js +72 -0
  9. package/app/middleware/api-sign-verify.js +30 -0
  10. package/app/middleware/error-handler.js +34 -0
  11. package/app/middleware/project-handler.js +23 -0
  12. package/app/middleware.js +38 -0
  13. package/app/pages/assert/custom.css +8 -0
  14. package/app/pages/boot.js +45 -0
  15. package/app/pages/common/curl.js +82 -0
  16. package/app/pages/common/utils.js +4 -0
  17. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +17 -0
  18. package/app/pages/dashboard/complex-view/header-view/header-view.vue +111 -0
  19. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +47 -0
  20. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +38 -0
  21. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +105 -0
  22. package/app/pages/dashboard/complex-view/schema-view/components/componet-config.js +19 -0
  23. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +79 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +95 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +105 -0
  26. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +118 -0
  27. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +71 -0
  28. package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +17 -0
  29. package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +116 -0
  30. package/app/pages/dashboard/dashboard.vue +96 -0
  31. package/app/pages/dashboard/entry.dashboard.js +53 -0
  32. package/app/pages/dashboard/todo/todo.vue +11 -0
  33. package/app/pages/store/index.js +4 -0
  34. package/app/pages/store/menu.js +55 -0
  35. package/app/pages/store/project.js +14 -0
  36. package/app/pages/widgets/header-container/assets/avatar.jpg +0 -0
  37. package/app/pages/widgets/header-container/assets/logo.png +0 -0
  38. package/app/pages/widgets/header-container/header-container.vue +113 -0
  39. package/app/pages/widgets/schema-form/complex-view/input/input.vue +133 -0
  40. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +131 -0
  41. package/app/pages/widgets/schema-form/complex-view/select/select.vue +111 -0
  42. package/app/pages/widgets/schema-form/form-item-config.js +20 -0
  43. package/app/pages/widgets/schema-form/schema-form.vue +134 -0
  44. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +49 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +61 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +44 -0
  47. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +46 -0
  48. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +112 -0
  49. package/app/pages/widgets/schema-search-bar/search-item-config.js +24 -0
  50. package/app/pages/widgets/schema-table/schema-table.vue +215 -0
  51. package/app/pages/widgets/sider-container/sider-container.vue +32 -0
  52. package/app/public/static/logo.png +0 -0
  53. package/app/public/static/normalize.css +240 -0
  54. package/app/router/project.js +6 -0
  55. package/app/router/view.js +8 -0
  56. package/app/router-schema/project.js +30 -0
  57. package/app/service/base.js +12 -0
  58. package/app/service/project.js +48 -0
  59. package/app/view/entry.tpl +28 -0
  60. package/app/webpack/config/webpack.base.js +249 -0
  61. package/app/webpack/config/webpack.dev.js +53 -0
  62. package/app/webpack/config/webpack.prod.js +80 -0
  63. package/app/webpack/dev.js +55 -0
  64. package/app/webpack/libs/blank.js +0 -0
  65. package/app/webpack/prod.js +21 -0
  66. package/config/config.default.js +3 -0
  67. package/elpis-core/env.js +20 -0
  68. package/elpis-core/index.js +97 -0
  69. package/elpis-core/loader/config.js +46 -0
  70. package/elpis-core/loader/controller.js +67 -0
  71. package/elpis-core/loader/extend.js +52 -0
  72. package/elpis-core/loader/middleware.js +64 -0
  73. package/elpis-core/loader/router-schema.js +47 -0
  74. package/elpis-core/loader/router.js +44 -0
  75. package/elpis-core/loader/service.js +65 -0
  76. package/index.js +38 -0
  77. package/model/index.js +111 -0
  78. package/package.json +88 -0
  79. package/test/controller/project.test.js +208 -0
package/index.js ADDED
@@ -0,0 +1,38 @@
1
+
2
+ // 引入elpis-core
3
+ const ElpisCore = require("./elpis-core")
4
+ // 引入 前端工程化构建方法
5
+ const FEBuildDev = require("./app/webpack/dev.js")
6
+ const FEBuildProd = require("./app/webpack/prod.js")
7
+
8
+ module.exports = {
9
+ /**
10
+ * 服务端基础
11
+ */
12
+ Controller: {
13
+ Base: require('./app/controller/base.js')
14
+ },
15
+ Service: {
16
+ Base: require('./app/service/base.js')
17
+ },
18
+ /**
19
+ * 编译构建前端工程
20
+ * @param env 环境变量 local/production
21
+ */
22
+ frontendBuild (env) {
23
+ if (env === 'local') {
24
+ FEBuildDev()
25
+ } else if (env === 'production') {
26
+ FEBuildProd()
27
+ }
28
+ },
29
+ /**
30
+ * 启动 elpis
31
+ * @param {object} options 项目配置,透传 elpis-core
32
+ */
33
+ serverStart () {
34
+ const app = ElpisCore.start()
35
+ return app
36
+ },
37
+
38
+ }
package/model/index.js ADDED
@@ -0,0 +1,111 @@
1
+ const glob = require('glob')
2
+ const path = require('path')
3
+ const { sep } = path
4
+ const _ = require('lodash')
5
+
6
+ //project 继承 model 方法
7
+ const projectExtendModel = (model, project) => {
8
+ return _.mergeWith({}, model, project, (modelValue, projValue) => {
9
+ //处理数组合并的特殊情况
10
+ if (_.isArray(modelValue) && _.isArray(projValue)) {
11
+ let result = []
12
+
13
+ // 因为 project 继承 model ,所以需要处理修改和新增的内容
14
+ // project有的键值,model也有=>修改
15
+ // project有的键值,model没有=>新增
16
+ // model有的键值,project没有=>保留(继承)
17
+
18
+ // 处理修改
19
+ for (let i = 0; i < modelValue.length; i++) {
20
+ const modelItem = modelValue[i]
21
+ const projItem = projValue.find(item => item.key === modelItem.key)
22
+ // project有的键值,model也有,则递归调用 projectExtendModel 方法覆盖修改
23
+ result.push(projItem ? projectExtendModel(modelItem, projItem) : modelItem)
24
+ }
25
+ // 处理新增
26
+ for (let i = 0; i < projValue.length; i++) {
27
+ const projItem = projValue[i]
28
+ const modelItem = modelValue.find(item => item.key === projItem.key)
29
+ if (!modelItem) {
30
+ result.push(projItem)
31
+ }
32
+ }
33
+
34
+ return result
35
+ }
36
+ })
37
+ }
38
+ /**
39
+ * 解析 model 配置 ,并返回组织且继承后的数据结构
40
+ * [{
41
+ * model:${model}
42
+ * project:{
43
+ * proj1Key:${proj1},
44
+ * proj2Key:${proj2},
45
+ * }...]
46
+ */
47
+ module.exports = (app) => {
48
+ const modelList = []
49
+
50
+ // 遍历当前文件夹,构造模型数据结构,加入到 modelList
51
+ const modelPath = path.resolve(process.cwd(), `.${sep}model`)
52
+ const fileList = glob.sync(path.resolve(modelPath, `.${sep}**${sep}**.js`))
53
+
54
+ fileList.forEach(file => {
55
+ if (file.indexOf('index.js') > -1) return
56
+
57
+ // 区分配置类型 (model / project)
58
+ // 将路径标准化为统一的正斜杠,避免 Windows 下的反斜杠影响匹配
59
+ // 说明:部分环境中 glob 已返回正斜杠,此处再次标准化以提高稳健性
60
+ const unixFile = file.replace(/\\/g, "/");
61
+
62
+ // 仅匹配以 /model/<key>/project/<name>.js 结尾,避免 "D:/project" 被误判为项目目录
63
+ const isProject = /\/model\/[^/]+\/project\/[^/]+\.js$/i.test(unixFile);
64
+
65
+ // 仅匹配以 /model/<key>/model.js 结尾
66
+ const isModel = /\/model\/[^/]+\/model\.js$/i.test(unixFile);
67
+ const type = isProject ? "project" : isModel ? "model" : "unknown";
68
+
69
+ // 非上述两类文件不在解析范围内,忽略
70
+ if (type === "unknown") return
71
+
72
+ if (type === 'project') {
73
+ // console.log(file, 'project');
74
+ const modelKey = file.match(/\/model\/(.*?)\/project/)?.[1]
75
+ const projKey = file.match(/\/project\/(.*?)\.js$/)?.[1]
76
+ let modelItem = modelList.find(item => item.model?.key === modelKey)
77
+ if (!modelItem) {//初始化 model 数据结构
78
+ modelItem = {}
79
+ modelList.push(modelItem)
80
+ }
81
+ if (!modelItem.project) {//初始化 model.project 数据结构
82
+ modelItem.project = {}
83
+ }
84
+ modelItem.project[projKey] = require(path.resolve(file))
85
+ modelItem.project[projKey].key = projKey//注入
86
+ modelItem.project[projKey].modelKey = modelKey
87
+ }
88
+
89
+ if (type === 'model') {
90
+ // console.log(file, 'model');
91
+ const modelKey = file.match(/\/model\/(.*?)\/model\.js$/)?.[1]
92
+ let modelItem = modelList.find(item => item.model?.key === modelKey)
93
+ if (!modelItem) {
94
+ modelItem = {}
95
+ modelList.push(modelItem)
96
+ }
97
+ modelItem.model = require(path.resolve(file))
98
+ modelItem.model.key = modelKey
99
+ }
100
+ })
101
+
102
+ // 数据进一步整理 :project=>继承model
103
+ modelList.forEach(item => {
104
+ const { model, project } = item
105
+ for (const key in project) {
106
+ project[key] = projectExtendModel(model, project[key])
107
+ }
108
+ })
109
+
110
+ return modelList
111
+ }
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@juge0218/elpis",
3
+ "version": "1.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "lint": "eslint --quiet --ext js,vue .",
8
+ "test": "cross-env _ENV=local mocha 'test/**/*.js'"
9
+ },
10
+ "author": "居葛",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "@babel/core": "^7.24.0",
14
+ "@babel/runtime": "^7.24.0",
15
+ "@element-plus/icons-vue": "^2.3.2",
16
+ "ajv": "^6.10.2",
17
+ "axios": "^0.19.2",
18
+ "echarts": "^5.5.0",
19
+ "element-plus": "^2.3.7",
20
+ "generate-password": "^1.7.1",
21
+ "glob": "^7.1.4",
22
+ "jsonwebtoken": "^9.0.2",
23
+ "knex": "^0.19.0",
24
+ "koa": "2.7.0",
25
+ "koa-bodyparser": "^4.2.1",
26
+ "koa-nunjucks-2": "^3.0.2",
27
+ "koa-router": "^7.4.0",
28
+ "koa-static": "^5.0.0",
29
+ "koa-useragent": "2.0.0",
30
+ "koa2-cors": "^2.0.6",
31
+ "less": "^3.8.1",
32
+ "lodash": "^4.17.21",
33
+ "log4js": "^6.9.1",
34
+ "md5": "^2.2.1",
35
+ "moment": "^2.29.4",
36
+ "mysql": "^2.18.1",
37
+ "node-schedule": "^2.1.1",
38
+ "nodemon": "^1.19.2",
39
+ "pinia": "^2.1.6",
40
+ "superagent": "^8.1.2",
41
+ "vue": "^3.3.4",
42
+ "vue-json-viewer": "^3.0.4",
43
+ "vue-router": "^4.2.4",
44
+ "vuex": "^4.1.0",
45
+ "@babel/plugin-transform-runtime": "^7.1.0",
46
+ "@babel/preset-env": "^7.4.5",
47
+ "babel-loader": "^8.0.4",
48
+ "clean-webpack-plugin": "^0.1.19",
49
+ "consoler": "^0.2.0",
50
+ "cross-env": "^7.0.3",
51
+ "css-loader": "^0.23.1",
52
+ "css-minimizer-webpack-plugin": "^5.0.1",
53
+ "directory-named-webpack-plugin": "^4.0.1",
54
+ "express": "^4.18.2",
55
+ "file-loader": "^6.2.0",
56
+ "html-webpack-inject-attributes-plugin": "^1.0.1",
57
+ "html-webpack-plugin": "^5.5.3",
58
+ "less-loader": "^11.1.3",
59
+ "mini-css-extract-plugin": "^2.7.6",
60
+ "style-loader": "^0.14.1",
61
+ "terser-webpack-plugin": "^5.3.10",
62
+ "thread-loader": "^4.0.4",
63
+ "url-loader": "^4.1.1",
64
+ "vue-loader": "^17.2.2",
65
+ "vue-style-loader": "^4.1.2",
66
+ "webpack": "^5.88.1",
67
+ "webpack-dev-middleware": "^6.1.1",
68
+ "webpack-hot-middleware": "^2.25.4",
69
+ "webpack-merge": "^4.2.1"
70
+ },
71
+ "devDependencies": {
72
+ "assert": "^2.0.0",
73
+ "babel-eslint": "^10.0.2",
74
+ "eslint": "^7.32.0",
75
+ "eslint-plugin-import": "^2.28.1",
76
+ "eslint-plugin-vue": "^9.17.0",
77
+ "mocha": "^6.1.4",
78
+ "ghooks": "~1.0.3",
79
+ "supertest": "^4.0.2",
80
+ "validate-commit-msg": "~2.14.0"
81
+ },
82
+ "config": {
83
+ "ghooks": {
84
+ "pre-commit": "npm run lint",
85
+ "commit-msg": "validate-commit-msg"
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,208 @@
1
+ const assert = require('assert');
2
+ const supertest = require('supertest');
3
+ const md5 = require('md5');
4
+ const elpisCore = require('../../elpis-core');
5
+
6
+ const signKey = 'klx05hb3n1c9ujp8uhxbs2ikkiowp212'
7
+ const st = Date.now();
8
+
9
+ describe('测试 project 相关接口', function () {
10
+ this.timeout(60000);
11
+
12
+ let modelList
13
+
14
+ let projectList = []
15
+
16
+ let request
17
+
18
+ it('启动服务', async () => {
19
+ const app = await elpisCore.start()
20
+ modelList = require('../../model/index.js')(app)
21
+ modelList.forEach(modelItem => {
22
+ const { project } = modelItem
23
+ for (const pKey in project) {
24
+ projectList.push(project[pKey])
25
+ }
26
+ })
27
+ request = supertest(app.listen())
28
+ })
29
+
30
+ it('GET /api/project without proj_key', async () => {
31
+ let tmpRequest = request.get('/api/project')
32
+ tmpRequest.set('s_t', st)
33
+ tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
34
+ const res = await tmpRequest
35
+
36
+ assert(res.body.success === false)
37
+
38
+ const resBody = res.body
39
+ assert(resBody.code === 442)
40
+ assert(resBody.message.indexOf(`request validate fail: data should have required property 'proj_key'`) > -1)
41
+ })
42
+
43
+ it('GET /api/project fail', async () => {
44
+ let tmpRequest = request.get('/api/project')
45
+ tmpRequest.set('s_t', st)
46
+ tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
47
+ tmpRequest.query({
48
+ proj_key: 'xxxxxxxx'
49
+ })
50
+ const res = await tmpRequest
51
+
52
+ assert(res.body.success === false)
53
+
54
+ const resBody = res.body
55
+ assert(resBody.code === 50000)
56
+ assert(resBody.message === `获取项目异常`)
57
+ })
58
+
59
+ it('GET /api/project with proj_key', async () => {
60
+ for (let i = 0; i < projectList.length; i++) {
61
+ const projItem = projectList[i]
62
+ const { key: projKey } = projItem
63
+ console.log(`GET /api/project with proj_key: ${projKey}`);
64
+ let tmpRequest = request.get('/api/project')
65
+ tmpRequest.set('s_t', st)
66
+ tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
67
+ tmpRequest.query({
68
+ proj_key: projKey
69
+ })
70
+ const res = await tmpRequest
71
+
72
+ assert(res.body.success === true)
73
+
74
+ const resData = res.body.data
75
+ assert(resData.key === projKey)
76
+ assert(resData.modelKey)
77
+ assert(resData.name)
78
+ assert(resData.desc !== undefined)
79
+ assert(resData.homePage !== undefined)
80
+
81
+ const { menu } = resData
82
+ menu.forEach(menuItem => {
83
+ checkMenuItem(menuItem)
84
+ })
85
+ }
86
+
87
+ // 检查 menu 菜单
88
+ function checkMenuItem (menuItem) {
89
+ console.log(`-------'GET /api/project with proj_key - menuKey: ${menuItem.key}`);
90
+ assert(menuItem.key)
91
+ assert(menuItem.name)
92
+ assert(menuItem.menuType)
93
+ if (menuItem.menuType === 'group') {
94
+ assert(menuItem.subMenu !== undefined)
95
+ menuItem.subMenu.forEach(subMenuItem => {
96
+ checkMenuItem(subMenuItem)
97
+ })
98
+ }
99
+ if (menuItem.menuType === 'module') {
100
+ checkModule(menuItem)
101
+ }
102
+ }
103
+ // 检查 module 菜单配置
104
+ function checkModule (menuItem) {
105
+ const { moduleType } = menuItem
106
+ assert(moduleType)
107
+ if (moduleType === 'sider') {
108
+ const { siderConfig } = menuItem
109
+ assert(siderConfig)
110
+ assert(siderConfig.menu)
111
+ siderConfig.menu.forEach(siderMenuItem => {
112
+ checkMenuItem(siderMenuItem)
113
+ })
114
+ }
115
+ if (moduleType === 'iframe') {
116
+ const { iframeConfig } = menuItem
117
+ assert(iframeConfig)
118
+ assert(iframeConfig.path !== undefined)
119
+ }
120
+ if (moduleType === 'custom') {
121
+ const { customConfig } = menuItem
122
+ assert(customConfig)
123
+ assert(customConfig.path !== undefined)
124
+ }
125
+ if (moduleType === 'schema') {
126
+ const { schemaConfig } = menuItem
127
+ assert(schemaConfig)
128
+ assert(schemaConfig.api !== undefined)
129
+ assert(schemaConfig.schema)
130
+ }
131
+ }
132
+ })
133
+
134
+ it('GET /api/project/list with proj_key', async () => {
135
+ let tmpRequest = request.get('/api/project/list')
136
+ tmpRequest.set('s_t', st)
137
+ tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
138
+ const res = await tmpRequest
139
+
140
+ assert(res.body.success === true)
141
+
142
+ const resData = res.body.data
143
+ assert(resData.length === projectList.length)
144
+
145
+ for (let i = 0; i < resData.length; i++) {
146
+ const item = resData[i]
147
+ assert(item.modelKey)
148
+ assert(item.key)
149
+ assert(item.name)
150
+ assert(item.desc !== undefined)
151
+ assert(item.homePage !== undefined)
152
+ }
153
+ })
154
+
155
+ it('GET /api/project/list with proj_key', async () => {
156
+
157
+ const { key: projKey } = projectList[Math.floor(Math.random() * projectList.length)]
158
+
159
+ let tmpRequest = request.get('/api/project/list')
160
+ tmpRequest.set('s_t', st)
161
+ tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
162
+ tmpRequest.query({
163
+ proj_key: projKey
164
+ })
165
+ const res = await tmpRequest
166
+
167
+ assert(res.body.success === true)
168
+
169
+ const resData = res.body.data
170
+
171
+ const { modelKey } = projectList.find(item => item.key === projKey)
172
+
173
+ assert(projectList.filter(item => item.modelKey === modelKey).length === resData.length)
174
+
175
+ for (let i = 0; i < resData.length; i++) {
176
+ const item = resData[i]
177
+ assert(item.modelKey)
178
+ assert(item.key)
179
+ assert(item.name)
180
+ assert(item.desc !== undefined)
181
+ assert(item.homePage !== undefined)
182
+ }
183
+ })
184
+
185
+ it('GET /api/project/model_list', async () => {
186
+ let tmpRequest = request.get('/api/project/model_list')
187
+ tmpRequest.set('s_t', st)
188
+ tmpRequest.set('s_sign', md5(`${signKey}_${st}`))
189
+ const res = await tmpRequest
190
+
191
+ assert(res.body.success === true)
192
+
193
+ const resData = res.body.data
194
+ assert(resData.length > 0)
195
+
196
+ for (let i = 0; i < resData.length; i++) {
197
+ const item = resData[i]
198
+ assert(item.model)
199
+ assert(item.model.key)
200
+ assert(item.model.name)
201
+ assert(item.project)
202
+ for (const projKey in item.project) {
203
+ assert(item.project[projKey].key)
204
+ assert(item.project[projKey].name)
205
+ }
206
+ }
207
+ })
208
+ })