@mangjuxiong/elpis 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.
Files changed (81) hide show
  1. package/.babelrc +5 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc +66 -0
  4. package/README.md +214 -0
  5. package/app/controller/base.js +39 -0
  6. package/app/controller/project.js +75 -0
  7. package/app/controller/view.js +19 -0
  8. package/app/extend/database.js +5 -0
  9. package/app/extend/logger.js +38 -0
  10. package/app/middleware/api-params-verify.js +74 -0
  11. package/app/middleware/api-sign-verify.js +36 -0
  12. package/app/middleware/error-handle.js +32 -0
  13. package/app/middleware/project-handler.js +26 -0
  14. package/app/middleware.js +39 -0
  15. package/app/pages/asserts/custom.css +14 -0
  16. package/app/pages/boot.js +48 -0
  17. package/app/pages/common/curl.js +80 -0
  18. package/app/pages/common/utils.js +34 -0
  19. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +24 -0
  20. package/app/pages/dashboard/complex-view/header-view/head-view.vue +113 -0
  21. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +43 -0
  22. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +35 -0
  23. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +118 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/components-config.js +23 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +85 -0
  26. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +103 -0
  27. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +112 -0
  28. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +124 -0
  29. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +90 -0
  30. package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +22 -0
  31. package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +113 -0
  32. package/app/pages/dashboard/dashboard.vue +85 -0
  33. package/app/pages/dashboard/entry.dashboard.js +46 -0
  34. package/app/pages/store/index.js +4 -0
  35. package/app/pages/store/menu.js +54 -0
  36. package/app/pages/store/project.js +13 -0
  37. package/app/pages/widgets/head-container/asserts/logo.png +0 -0
  38. package/app/pages/widgets/head-container/head-container.vue +77 -0
  39. package/app/pages/widgets/schema-form/complex-view/input/input.vue +134 -0
  40. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +135 -0
  41. package/app/pages/widgets/schema-form/complex-view/select/select.vue +120 -0
  42. package/app/pages/widgets/schema-form/form-item-config.js +23 -0
  43. package/app/pages/widgets/schema-form/schema-form.vue +130 -0
  44. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +51 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +55 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +38 -0
  47. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +40 -0
  48. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +100 -0
  49. package/app/pages/widgets/schema-search-bar/search-item-config.js +31 -0
  50. package/app/pages/widgets/schema-table/schema-table.vue +225 -0
  51. package/app/pages/widgets/sider-container/sider-container.vue +27 -0
  52. package/app/public/output/entry.page1.tpl +42 -0
  53. package/app/public/static/logo.jpg +0 -0
  54. package/app/public/static/normalize.css +267 -0
  55. package/app/router/project.js +6 -0
  56. package/app/router/view.js +7 -0
  57. package/app/router-schema/project.js +30 -0
  58. package/app/service/base.js +13 -0
  59. package/app/service/project.js +42 -0
  60. package/app/view/entry.tpl +25 -0
  61. package/app/webpack/config/babel.config.js +4 -0
  62. package/app/webpack/config/webpack.base.js +229 -0
  63. package/app/webpack/config/webpack.dev.js +57 -0
  64. package/app/webpack/config/webpack.prod.js +120 -0
  65. package/app/webpack/libs/blank.js +1 -0
  66. package/app/webpack/libs/dev.js +53 -0
  67. package/app/webpack/libs/prod.js +22 -0
  68. package/config/config.default.js +4 -0
  69. package/elpis-core/env.js +20 -0
  70. package/elpis-core/index.js +81 -0
  71. package/elpis-core/loader/config.js +52 -0
  72. package/elpis-core/loader/controller.js +59 -0
  73. package/elpis-core/loader/extend.js +47 -0
  74. package/elpis-core/loader/middleware.js +57 -0
  75. package/elpis-core/loader/router-schema.js +41 -0
  76. package/elpis-core/loader/router.js +42 -0
  77. package/elpis-core/loader/service.js +59 -0
  78. package/index.js +39 -0
  79. package/model/index.js +107 -0
  80. package/package.json +90 -0
  81. package/test/controller/project.test.js +194 -0
package/index.js ADDED
@@ -0,0 +1,39 @@
1
+ // 引入 elpis-core
2
+ const ElpisCore = require('./elpis-core');
3
+ // 引入前端工程化构建
4
+ const FEBuildDev = require('./app/webpack/libs/dev.js');
5
+ const FEBuildProd = require('./app/webpack/libs/prod.js');
6
+
7
+ module.exports = {
8
+ /**
9
+ * 服务端基础
10
+ */
11
+ Controller: {
12
+ Base: require('./app/controller/base.js'),
13
+ },
14
+ Service: {
15
+ Base: require('./app/service/base.js'),
16
+ },
17
+
18
+ /**
19
+ * 编译构建前端工程
20
+ * @params env 环境变量 local/production
21
+ */
22
+ frontendBuild: (env) => {
23
+ if (env === 'local') {
24
+ FEBuildDev();
25
+ } else if (env === 'production') {
26
+ FEBuildProd();
27
+ }
28
+ },
29
+
30
+ /**
31
+ * 启动 elpis
32
+ * @param options 项目配置,透传到 elpis-core
33
+ * @returns
34
+ */
35
+ serverStart: (options = {}) => {
36
+ const app = ElpisCore.start(options);
37
+ return app;
38
+ },
39
+ };
package/model/index.js ADDED
@@ -0,0 +1,107 @@
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(Array.isArray(modelValue) && Array.isArray(projValue)){
11
+ let result = [];
12
+ // 因为project继承model,所以需要处理的情况有
13
+ // project有的键值,model也有 => 修改(重载)
14
+ // project有的键值,model没有 => 新增 (拓展)
15
+ // model 有的,project没有 => 保留(继承)
16
+
17
+ // 处理修改和保留
18
+ for(let i = 0; i < modelValue.length; i++){
19
+ let modelItem = modelValue[i];
20
+ const projItem = projValue.find(projItem=>projItem.key === modelItem.key);
21
+ // project有的键值,model也有,则递归调用projectExtendModel 方法覆盖
22
+ result.push(projItem?projectExtendModel(modelItem,projItem):modelItem);
23
+ }
24
+
25
+ // 处理新增
26
+ for(let i = 0; i < projValue.length; i++){
27
+ const projItem = projValue[i];
28
+ const modelItem = modelValue.find(modelItem=>modelItem.key === projItem.key);
29
+ if(!modelItem){
30
+ result.push(projItem);
31
+ }
32
+ }
33
+
34
+ return result;
35
+ }
36
+ });
37
+ };
38
+
39
+ /**
40
+ * 解析 model 配置,并返回组织且继承后的数据结构
41
+ * [{
42
+ * model: ${model},
43
+ * project:{
44
+ * projKey1: ${project1},
45
+ * projKey2: ${project2},
46
+ * }
47
+ * },...]
48
+ *
49
+ */
50
+ module.exports = (app)=>{
51
+ const modelList = [];
52
+
53
+ // 遍历当前文件夹,构造模型数据结构,挂载到modelList上
54
+ const modelPath = path.resolve(process.cwd(), `.${sep}model`);
55
+ const fileLst = glob.sync(path.resolve(modelPath, `.${sep}**${sep}**.js`));
56
+ fileLst.forEach((file)=>{
57
+ // 排除掉index.js文件
58
+ if(file.indexOf('index.js')>-1) return;
59
+
60
+ // 区分配置类型 (model / project)
61
+ const type = file.indexOf(`/project/`) > -1 ? 'project' : 'model';
62
+ if(type === 'model'){
63
+ const modelKey = file.match(/\/model\/(.*?)\/model\.js/)?.[1];
64
+ let modelItem = modelList.find (item=>item.model?.key === modelKey);
65
+
66
+ if(!modelItem){
67
+ // 初始化model数据结构
68
+ modelItem = {};
69
+ modelList.push(modelItem);
70
+ }
71
+ modelItem.model = require(path.resolve(file));
72
+ // 注入model key
73
+ modelItem.model.key = modelKey;
74
+ }
75
+
76
+ if(type === 'project'){
77
+ const modelKey = file.match(/\/model\/(.*?)\/project/)?.[1];
78
+ const projKey = file.match(/\/project\/(.*?)\.js/)?.[1];
79
+ let modelItem = modelList.find (item=>item.model?.key === modelKey);
80
+
81
+ if(!modelItem){
82
+ // 初始化model数据结构
83
+ modelItem = {};
84
+ modelList.push(modelItem);
85
+ }
86
+ if(!modelItem.project){
87
+ // 初始化project数据结构
88
+ modelItem.project = {};
89
+ }
90
+ modelItem.project[projKey] = require(path.resolve(file));
91
+ // 注入project key
92
+ modelItem.project[projKey].key = projKey;
93
+ // 注入model key
94
+ modelItem.project[projKey].modelKey = modelKey;
95
+ }
96
+ });
97
+
98
+ // 数据进一步整理: project => 继承 model
99
+ modelList.forEach((item)=>{
100
+ const { model, project } = item;
101
+ for(const key in project){
102
+ project[key] = projectExtendModel(model,project[key]);
103
+ }
104
+ });
105
+
106
+ return modelList;
107
+ };
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@mangjuxiong/elpis",
3
+ "version": "1.0.2",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "lint": "eslint --quiet --ext js,vue .",
8
+ "test": "set _ENV='local'&& mocha test/**/*.js"
9
+ },
10
+ "author": "mangjuxiong",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "@babel/core": "^7.24.0",
14
+ "@babel/runtime": "^7.27.6",
15
+ "@element-plus/icons-vue": "^2.3.1",
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
+ "path": "^0.12.7",
40
+ "pinia": "^2.1.6",
41
+ "superagent": "^8.1.2",
42
+ "vue": "^3.3.4",
43
+ "vue-json-viewer": "^3.0.4",
44
+ "vue-router": "^4.2.4",
45
+ "vuex": "^4.1.0",
46
+ "@babel/plugin-transform-runtime": "^7.1.0",
47
+ "@babel/preset-env": "^7.4.5",
48
+ "babel-loader": "^8.0.4",
49
+ "clean-webpack-plugin": "^0.1.19",
50
+ "consoler": "^0.2.0",
51
+ "css-loader": "^0.23.1",
52
+ "css-minimizer-webpack-plugin": "^5.0.1",
53
+ "directory-named-webpack-plugin": "^4.0.1",
54
+ "eslint-config-airbnb-base": "^15.0.0",
55
+ "express": "^4.18.2",
56
+ "file-loader": "^6.2.0",
57
+ "happypack": "^5.0.1",
58
+ "html-webpack-inject-attributes-plugin": "^1.0.1",
59
+ "html-webpack-plugin": "^5.6.3",
60
+ "less-loader": "^11.1.3",
61
+ "mini-css-extract-plugin": "^2.7.6",
62
+ "style-loader": "^0.14.1",
63
+ "terser-webpack-plugin": "^5.3.12",
64
+ "url-loader": "^4.1.1",
65
+ "vue-loader": "^17.2.2",
66
+ "vue-style-loader": "^4.1.2",
67
+ "webpack": "^5.88.1",
68
+ "webpack-dev-middleware": "^6.1.1",
69
+ "webpack-hot-middleware": "^2.25.4",
70
+ "webpack-merge": "^4.2.1"
71
+ },
72
+ "devDependencies": {
73
+ "assert": "^2.0.0",
74
+ "babel-eslint": "^10.1.0",
75
+ "@babel/eslint-parser": "^7.26.5",
76
+ "eslint": "^8.57.1",
77
+ "eslint-plugin-import": "^2.31.0",
78
+ "eslint-plugin-vue": "^9.32.0",
79
+ "ghooks": "~1.0.3",
80
+ "mocha": "^6.1.4",
81
+ "supertest": "^4.0.2",
82
+ "validate-commit-msg": "~2.14.0"
83
+ },
84
+ "config": {
85
+ "ghooks": {
86
+ "commit-msg": "validate-commit-msg",
87
+ "pre-commit": "npm run lint"
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,194 @@
1
+ const assert = require('assert');
2
+ const supertest = require('supertest');
3
+ const md5 = require('md5');
4
+ const eplisCore = require('../../elpis-core');
5
+
6
+ const signKey = 'mshsiksjksnnmsma';
7
+ const st = Date.now();
8
+
9
+ describe('测试project相关接口', function () {
10
+ this.timeout(60000);
11
+ let request;
12
+
13
+ let modelList;
14
+ const projectList = [];
15
+
16
+ it('启动服务', async () => {
17
+ const app = await eplisCore.start();
18
+ modelList = require('../../model/index.js')(app);
19
+ modelList.forEach((modelItem) => {
20
+ const { project } = modelItem;
21
+ for (const pKey in project) {
22
+ projectList.push(project[pKey]);
23
+ }
24
+ });
25
+ request = supertest(app.listen());
26
+ });
27
+
28
+ // 获取当前projKey 对应模型下的项目列表,无projKey则返回所有项目
29
+ it('GET /api/project/list without proj_key', async () => {
30
+ let tmpRequest = request.get('/api/project/list');
31
+ tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`));
32
+ tmpRequest = tmpRequest.set('s_t', st);
33
+ const res = await tmpRequest;
34
+ assert(res.body.success === true);
35
+
36
+ const resData = res.body.data;
37
+ assert(resData.length === projectList.length);
38
+ for (let i = 0; i < resData.length; i++) {
39
+ const item = resData[i];
40
+ assert(item.modelKey);
41
+ assert(item.key);
42
+ assert(item.name);
43
+ assert(item.desc !== undefined);
44
+ assert(item.homePage !== undefined);
45
+ }
46
+ });
47
+ // 获取当前projKey 对应模型下的项目列表
48
+ it('GET /api/project/list with proj_key', async () => {
49
+ const project = projectList[Math.floor(Math.random() * projectList.length)];
50
+ const { modelKey } = projectList.find((item) => item.key === project.key);
51
+
52
+ let tmpRequest = request.get('/api/project/list');
53
+ tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`));
54
+ tmpRequest = tmpRequest.set('s_t', st);
55
+ tmpRequest = tmpRequest.query({
56
+ proj_key: project.key,
57
+ });
58
+ const res = await tmpRequest;
59
+ assert(res.body.success === true);
60
+
61
+ const resData = res.body.data;
62
+ assert(resData.length === projectList.filter((item) => item.modelKey === modelKey).length);
63
+
64
+ for (let i = 0; i < resData.length; i++) {
65
+ const item = resData[i];
66
+ assert(item.modelKey);
67
+ assert(item.key);
68
+ assert(item.name);
69
+ assert(item.desc !== undefined);
70
+ assert(item.homePage !== undefined);
71
+ }
72
+ });
73
+
74
+ // 获取项目和模型的数据结构
75
+ it('GET /api/project/model_list', async () => {
76
+ let tmpRequest = request.get('/api/project/model_list');
77
+ tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`));
78
+ tmpRequest = tmpRequest.set('s_t', st);
79
+ const res = await tmpRequest;
80
+ assert(res.body.success === true);
81
+ const resData = res.body.data;
82
+ assert(resData.length > 0);
83
+ for (let i = 0; i < resData.length; i++) {
84
+ const item = resData[i];
85
+ assert(item.model);
86
+ assert(item.model.key);
87
+ assert(item.model.name);
88
+ assert(item.project);
89
+ for (const projKey in item.project) {
90
+ const proj = item.project[projKey];
91
+ assert(proj.key);
92
+ assert(proj.name);
93
+ }
94
+ }
95
+ });
96
+
97
+ // 根据proj_key获取项目配置,没有传proj_key则报错
98
+ it('GET /api/project without proj_key', async () => {
99
+ let tmpRequest = request.get('/api/project');
100
+ tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`));
101
+ tmpRequest = tmpRequest.set('s_t', st);
102
+ const res = await tmpRequest;
103
+ assert(res.body.success === false);
104
+
105
+ const resData = res.body;
106
+ assert(resData.code === 442);
107
+ assert(resData.message.indexOf("request validate fail: data should have required property 'proj_key'") > -1);
108
+ });
109
+ // 获取当前projKey 对应模型下的项目列表,无projKey则返回所有项目
110
+ it('GET /api/project fail', async () => {
111
+ let tmpRequest = request.get('/api/project');
112
+ tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`));
113
+ tmpRequest = tmpRequest.set('s_t', st);
114
+ tmpRequest = tmpRequest.query({
115
+ proj_key: 'xxxxxxxx',
116
+ });
117
+ const res = await tmpRequest;
118
+ assert(res.body.success === false);
119
+
120
+ const resData = res.body;
121
+ assert(resData.code === 50000);
122
+ assert(resData.message === '项目不存在');
123
+ });
124
+ // 获取当前projKey 对应模型下的项目列表,无projKey则返回所有项目
125
+ it('GET /api/project with proj_key', async () => {
126
+ for (let i = 0; i < projectList.length; i++) {
127
+ const project = projectList[i];
128
+ console.log('----- GET /api/project with proj_key:', project.key);
129
+ let tmpRequest = request.get('/api/project');
130
+ tmpRequest = tmpRequest.set('s_sign', md5(`${signKey}_${st}`));
131
+ tmpRequest = tmpRequest.set('s_t', st);
132
+ tmpRequest = tmpRequest.query({
133
+ proj_key: project.key,
134
+ });
135
+ const res = await tmpRequest;
136
+ assert(res.body.success === true);
137
+
138
+ const resData = res.body.data;
139
+ assert(resData.key === project.key);
140
+ assert(resData.modelKey);
141
+ assert(resData.name);
142
+ assert(resData.desc !== undefined);
143
+ assert(resData.homePage !== undefined);
144
+
145
+ const { menu } = resData;
146
+ menu.forEach((menuItem) => {
147
+ checkMenuItem(menuItem);
148
+ });
149
+ }
150
+
151
+ // 校验menu菜单
152
+ function checkMenuItem(menuItem) {
153
+ console.log('----- GET /api/project with proj_key checkMenuItem ~ menuItem:', menuItem.key);
154
+ assert(menuItem.key);
155
+ assert(menuItem.name);
156
+ assert(menuItem.menuType);
157
+
158
+ if (menuItem.menuType === 'module') {
159
+ checkMoudle(menuItem);
160
+ }
161
+ if (menuItem.menuType === 'group') {
162
+ assert(menuItem.subMenu !== undefined);
163
+ menuItem.subMenu.forEach((subMenuItem) => {
164
+ checkMenuItem(subMenuItem);
165
+ });
166
+ }
167
+ }
168
+
169
+ // 检查moudle菜单配置
170
+ function checkMoudle(menuItem) {
171
+ const { moduleType } = menuItem;
172
+ assert(moduleType);
173
+ if (moduleType === 'iframe') {
174
+ assert(menuItem.iframeConfig);
175
+ assert(menuItem.iframeConfig.path !== undefined);
176
+ } else if (moduleType === 'custom') {
177
+ assert(menuItem.customConfig);
178
+ assert(menuItem.customConfig.path !== undefined);
179
+ } else if (moduleType === 'sider') {
180
+ const { siderConfig } = menuItem;
181
+ assert(siderConfig);
182
+ assert(siderConfig.menu);
183
+ siderConfig.menu.forEach((subMenuItem) => {
184
+ checkMenuItem(subMenuItem);
185
+ });
186
+ } else if (moduleType === 'schema') {
187
+ const { schemaConfig } = menuItem;
188
+ assert(schemaConfig);
189
+ assert(schemaConfig.api !== undefined);
190
+ assert(schemaConfig.schema);
191
+ }
192
+ }
193
+ });
194
+ });