@lixianwei/elpis 1.0.0

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 (86) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc +55 -0
  3. package/.idea/dictionaries/project.xml +8 -0
  4. package/.idea/elpis.iml +8 -0
  5. package/.idea/git_toolbox_prj.xml +15 -0
  6. package/.idea/inspectionProfiles/Project_Default.xml +329 -0
  7. package/.idea/modules.xml +8 -0
  8. package/.idea/vcs.xml +6 -0
  9. package/README.md +201 -0
  10. package/app/controller/base.js +77 -0
  11. package/app/controller/project.js +69 -0
  12. package/app/controller/view.js +20 -0
  13. package/app/extend/logger.js +35 -0
  14. package/app/middleware/api-params-verify.js +74 -0
  15. package/app/middleware/api-sign-verify.js +33 -0
  16. package/app/middleware/error-hander.js +32 -0
  17. package/app/middleware/project-handler.js +25 -0
  18. package/app/middleware.js +37 -0
  19. package/app/pages/asserts/custom.css +11 -0
  20. package/app/pages/boot.js +43 -0
  21. package/app/pages/common/curl.js +83 -0
  22. package/app/pages/common/index.js +2 -0
  23. package/app/pages/common/utils.js +0 -0
  24. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +23 -0
  25. package/app/pages/dashboard/complex-view/header-view/header-view.vue +117 -0
  26. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +40 -0
  27. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +45 -0
  28. package/app/pages/dashboard/complex-view/schema-view/complex-view/tabel-panel/tabel-panel.vue +134 -0
  29. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +20 -0
  30. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +89 -0
  31. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +101 -0
  32. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +128 -0
  33. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +119 -0
  34. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +96 -0
  35. package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +21 -0
  36. package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +117 -0
  37. package/app/pages/dashboard/dashboard.vue +88 -0
  38. package/app/pages/dashboard/entry.dashboard.js +41 -0
  39. package/app/pages/router/index.js +11 -0
  40. package/app/pages/store/index.js +4 -0
  41. package/app/pages/store/menu.js +65 -0
  42. package/app/pages/store/project.js +16 -0
  43. package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
  44. package/app/pages/widgets/header-container/asserts/logo.png +0 -0
  45. package/app/pages/widgets/header-container/header-container.vue +103 -0
  46. package/app/pages/widgets/schema-form/complex-view/input/input.vue +142 -0
  47. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +143 -0
  48. package/app/pages/widgets/schema-form/complex-view/select/select.vue +130 -0
  49. package/app/pages/widgets/schema-form/form-item-config.js +20 -0
  50. package/app/pages/widgets/schema-form/schema-form.vue +132 -0
  51. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +48 -0
  52. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +67 -0
  53. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +40 -0
  54. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +50 -0
  55. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +142 -0
  56. package/app/pages/widgets/schema-search-bar/search-item-config.js +24 -0
  57. package/app/pages/widgets/schema-table/schema-table.vue +238 -0
  58. package/app/pages/widgets/sider-container/sider-container.vue +31 -0
  59. package/app/public/static/logo.png +0 -0
  60. package/app/public/static/normalize.css +239 -0
  61. package/app/router/project.js +9 -0
  62. package/app/router/view.js +7 -0
  63. package/app/router-schema/project.js +32 -0
  64. package/app/service/base.js +12 -0
  65. package/app/service/project.js +42 -0
  66. package/app/view/entry.tpl +26 -0
  67. package/app/webpack/config/webpack.base.js +219 -0
  68. package/app/webpack/config/webpack.dev.js +55 -0
  69. package/app/webpack/config/webpack.prod.js +118 -0
  70. package/app/webpack/dev.js +52 -0
  71. package/app/webpack/libs/blank.js +2 -0
  72. package/app/webpack/prod.js +18 -0
  73. package/config/config.default.js +3 -0
  74. package/elpis-core/env.js +20 -0
  75. package/elpis-core/index.js +92 -0
  76. package/elpis-core/loader/config.js +54 -0
  77. package/elpis-core/loader/controller.js +61 -0
  78. package/elpis-core/loader/extend.js +55 -0
  79. package/elpis-core/loader/middleware.js +61 -0
  80. package/elpis-core/loader/router-schema.js +39 -0
  81. package/elpis-core/loader/router.js +42 -0
  82. package/elpis-core/loader/service.js +64 -0
  83. package/index.js +41 -0
  84. package/model/index.js +105 -0
  85. package/package.json +94 -0
  86. package/test/controller/project.test.js +194 -0
@@ -0,0 +1,64 @@
1
+ const glob = require('glob');
2
+ const path = require('path');
3
+ const {sep} = path
4
+ /*
5
+ * service loader
6
+ * @param{object} app Koa 实例
7
+ * 加载所有service,可通过 'app.service.${目录}.${文件}' 访问
8
+ * 例子
9
+ * app/service
10
+ * |
11
+ * | --custom-module
12
+ * |
13
+ * | --custom-service.js
14
+ * => app.service.customModule.customService
15
+ * */
16
+
17
+ module.exports = (app) => {
18
+
19
+ const service = {}
20
+ // 读取elpis/app/service/**/**.js 下所有文件
21
+ const elpisServicePath = path.resolve(__dirname,`..${sep}..${sep}app${sep}service`);
22
+ const elpisFileList = glob.sync(path.resolve(elpisServicePath, `.${sep}**${sep}**.js`));
23
+ elpisFileList.forEach(file => {
24
+ handelFile(file)
25
+ })
26
+
27
+ // 读取业务/app/service/**/**.js 下所有文件
28
+ const businessServicePath = path.resolve(app.businessPath,`.${sep}service`);
29
+ const businessFileList = glob.sync(path.resolve(businessServicePath, `.${sep}**${sep}**.js`));
30
+ businessFileList.forEach(file => {
31
+ handelFile(file)
32
+ })
33
+ //把内容加载到app.service 下
34
+
35
+ function handelFile(file){
36
+ //提取文件名称
37
+ let name = path.resolve(file)
38
+
39
+ // 截取路径 app/service/custom-module/custom-service.js =>custom-module/custom-service
40
+ name = name.substring(name.lastIndexOf(`service${sep}`)+`service${sep}`.length,name.lastIndexOf('.'));
41
+
42
+ //把'-' 统一改成驼峰命名
43
+ name = name.replace(/[_-][a-z]/ig,(s)=>s.substring(1).toUpperCase());
44
+
45
+ //挂载sevice到内存app对象中
46
+ let temService = service;
47
+ const names = name.split(sep) //[customModule(目录),customService(文件)]
48
+ for(let i =0,len = names.length;i<len; ++i ){
49
+ if(i===len - 1){
50
+ const ServiceMoule = require(path.resolve(file))(app)
51
+ temService[names[i]] = new ServiceMoule();
52
+ }else{
53
+ if(!temService[names[i]]){
54
+ temService[names[i]] = {};
55
+ }
56
+ temService = temService[names[i]];
57
+ }
58
+ }
59
+ }
60
+
61
+ // console.log(service,'service')
62
+ app.service = service;
63
+ };
64
+
package/index.js ADDED
@@ -0,0 +1,41 @@
1
+ const ElpisCore = require('./elpis-core');
2
+ // 引入 前端工程化构建方法
3
+ const FEBuildDev = require('./app/webpack/dev.js');
4
+ const FEBuildProd = require('./app/webpack/prod.js');
5
+
6
+ module.exports = {
7
+
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
+ * 启动elpis
31
+ * @params options 项目配置 透传到elpis-core
32
+ * */
33
+ serverStart(options = {}) {
34
+ // 启动项目
35
+ const app = ElpisCore.start(options)
36
+ return app
37
+ }
38
+ };
39
+
40
+
41
+
package/model/index.js ADDED
@@ -0,0 +1,105 @@
1
+ const _ = require('lodash')
2
+ const glob = require('glob');
3
+ const path = require('path');
4
+ const {sep} = path;
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
+ for (let i= 0; i<projValue.length; ++i){
26
+ const projItem = projValue[i];
27
+ const modelItem = modelValue.find(modelItem=>modelItem.key === projItem.key);
28
+ if(!modelItem){
29
+ result.push(projItem);
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+
35
+ });
36
+ }
37
+ /*
38
+ * 解析model配置,并返回组织且继承后的数据结构
39
+ * [{
40
+ * model:${model}
41
+ * project:{
42
+ * proj1:${proj1},
43
+ * proj2:${proj2}
44
+ * }
45
+ * },...]
46
+ * */
47
+
48
+ module.exports=(app)=>{
49
+ const modelList = [];
50
+ // 遍历当前文件夹,构造模型数据结构,挂载到modelList上
51
+ const modelPath = path.resolve(app.baseDir,`.${sep}/model`)
52
+ const fileList = glob.sync(path.resolve(modelPath,`.${sep}**${sep}**.js`));
53
+ fileList.forEach(file=>{
54
+ if(file.indexOf('index.js')>-1) {
55
+ return;
56
+ }
57
+
58
+ // 区分配置类型(model/project)
59
+ // windows系统需要在这个地方转换一下 不然windows系统识别sep 为 \
60
+ const normalized = path.resolve(file);
61
+ const type = normalized.indexOf(`${sep}project${sep}`) > -1 ? 'project' : 'model';
62
+
63
+ if(type === 'project'){
64
+ // 正则匹配找到 model/buiness/project/**.js 中的buiness 这个文件标识
65
+ const modelKey = file.match(/\/model\/(.*?)\/project/)?.[1]
66
+ // 正则匹配找到 model/buiness/project/**.js 中的project 这个文件标识
67
+ const projectKey = file.match(/\/project\/(.*?)\.js/)?.[1]
68
+ let modelItem = modelList.find(item=>item.model?.key === modelKey);
69
+ if(!modelItem){ //初始话model数据结构
70
+ modelItem={}
71
+ modelList.push(modelItem);
72
+ }
73
+ if(!modelItem.project){ //初始化project 数据结构
74
+ modelItem.project={}
75
+ }
76
+ modelItem.project[projectKey]= require(path.resolve(file))
77
+ modelItem.project[projectKey].key = projectKey; //注入projectKey
78
+ modelItem.project[projectKey].modelKey = modelKey; //注入modelKey
79
+ }
80
+
81
+ if(type === 'model'){
82
+ // 正则匹配找到 model/buiness/model.js 中的buiness 这个文件标识
83
+ const modelKey = file.match(/\/model\/(.*?)\/model\.js/)?.[1]
84
+ let modelItem = modelList.find(item=>item.model?.key === modelKey)
85
+ if(!modelItem){
86
+ modelItem = {}
87
+ modelList.push(modelItem)
88
+ }
89
+ modelItem.model = require(path.resolve(file))
90
+ modelItem.model.key = modelKey //注入modelKey
91
+
92
+ }
93
+
94
+ })
95
+
96
+ // 数据进一步整理 project=>继承model
97
+ modelList.forEach(item=>{
98
+ const {model,project} = item;
99
+ for(const key in project){
100
+ project[key] = projectExtendModel(model,project[key]);
101
+ }
102
+
103
+ })
104
+ return modelList
105
+ }
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@lixianwei/elpis",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "cross-env _ENV=local mocha test/**/*.js",
8
+ "lint": "eslint --quiet --ext js,vue ."
9
+
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://git.code.tencent.com/li1995/elpis.git"
14
+ },
15
+ "author": "lixianwei",
16
+ "license": "ISC",
17
+ "dependencies": {
18
+ "@babel/core": "^7.24.0",
19
+ "@babel/runtime": "^7.24.0",
20
+ "@element-plus/icons-vue": "^2.3.2",
21
+ "ajv": "^6.10.2",
22
+ "axios": "^0.19.2",
23
+ "echarts": "^5.5.0",
24
+ "element-plus": "^2.3.7",
25
+ "generate-password": "^1.7.1",
26
+ "glob": "^7.1.4",
27
+ "jsonwebtoken": "^9.0.2",
28
+ "knex": "^0.19.0",
29
+ "koa": "2.7.0",
30
+ "koa-bodyparser": "^4.2.1",
31
+ "koa-nunjucks-2": "^3.0.2",
32
+ "koa-router": "^7.4.0",
33
+ "koa-static": "^5.0.0",
34
+ "koa-useragent": "2.0.0",
35
+ "koa2-cors": "^2.0.6",
36
+ "less": "^3.8.1",
37
+ "lodash": "^4.17.21",
38
+ "log4js": "^6.9.1",
39
+ "md5": "^2.2.1",
40
+ "moment": "^2.29.4",
41
+ "mysql": "^2.18.1",
42
+ "node-schedule": "^2.1.1",
43
+ "nodemon": "^1.19.2",
44
+ "path": "^0.12.7",
45
+ "pinia": "^2.1.6",
46
+ "superagent": "^8.1.2",
47
+ "vue": "^3.3.4",
48
+ "vue-json-viewer": "^3.0.4",
49
+ "vue-router": "^4.2.4",
50
+ "vuex": "^4.1.0",
51
+ "@babel/plugin-transform-runtime": "^7.1.0",
52
+ "@babel/preset-env": "^7.4.5",
53
+ "babel-loader": "^8.0.4",
54
+ "clean-webpack-plugin": "^0.1.19",
55
+ "consoler": "^0.2.0",
56
+ "cross-env": "^10.1.0",
57
+ "css-loader": "^0.23.1",
58
+ "css-minimizer-webpack-plugin": "^5.0.1",
59
+ "directory-named-webpack-plugin": "^4.0.1",
60
+ "express": "^4.18.2",
61
+ "file-loader": "^6.2.0",
62
+ "happypack": "^5.0.1",
63
+ "html-webpack-inject-attributes-plugin": "^1.0.1",
64
+ "html-webpack-plugin": "^5.5.3",
65
+ "less-loader": "^11.1.3",
66
+ "mini-css-extract-plugin": "^2.7.6",
67
+ "style-loader": "^0.14.1",
68
+ "terser-webpack-plugin": "^5.6.1",
69
+ "url-loader": "^4.1.1",
70
+ "vue-loader": "^17.2.2",
71
+ "vue-style-loader": "^4.1.2",
72
+ "webpack": "^5.88.1",
73
+ "webpack-dev-middleware": "^6.1.1",
74
+ "webpack-hot-middleware": "^2.25.4",
75
+ "webpack-merge": "^4.2.1"
76
+ },
77
+ "devDependencies": {
78
+ "assert": "^2.0.0",
79
+ "babel-eslint": "^10.0.2",
80
+ "eslint": "^7.32.0",
81
+ "eslint-plugin-import": "^2.28.1",
82
+ "eslint-plugin-vue": "^9.17.0",
83
+ "ghooks": "~1.0.3",
84
+ "mocha": "^6.1.4",
85
+ "supertest": "^4.0.2",
86
+ "validate-commit-msg": "~2.14.0"
87
+ },
88
+ "config": {
89
+ "ghooks": {
90
+ "commit-msg": "validate-commit-msg",
91
+ "pre-commit": "npm run lint"
92
+ }
93
+ }
94
+ }
@@ -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 = 'klx05hb3n1c9ujp8uhxbs2ikkiowp212';
7
+
8
+ const st = Date.now();
9
+ describe('测试project 相关接口',function(){
10
+ this.timeout(60000);
11
+ let request;
12
+ let modelList ;
13
+ const projectList =[];
14
+ it('启动服务',async ()=>{
15
+ const app = await eplisCore.start();
16
+ modelList = require('../../model/index')(app)
17
+ modelList.forEach(async (modelItem)=>{
18
+ const {project} = modelItem
19
+ for(const pKey in project){
20
+ projectList.push(project[pKey])
21
+ }
22
+ })
23
+ request = supertest(app.listen());
24
+ })
25
+
26
+
27
+ it('GET /api/project without proj_key', async ()=>{
28
+ let tmpRequest = request.get('/api/project');
29
+ tmpRequest = tmpRequest.set('s_t',st)
30
+ tmpRequest = tmpRequest.set('s_sign',md5(`${signKey}_${st}`));
31
+ const res = await tmpRequest
32
+ assert(res.body.success === false)
33
+ const resBody = res.body;
34
+ assert(resBody.code === 442)
35
+ assert(resBody.message.indexOf(`request validate fail: data should have required property 'proj_key'`) > -1)
36
+ })
37
+
38
+ it('GET /api/project fail', async ()=>{
39
+ let tmpRequest = request.get('/api/project');
40
+ tmpRequest = tmpRequest.set('s_t',st)
41
+ tmpRequest = tmpRequest.set('s_sign',md5(`${signKey}_${st}`));
42
+ tmpRequest = tmpRequest.query({
43
+ proj_key:'xxxxxxxxxx'
44
+ });
45
+ const res = await tmpRequest;
46
+ assert(res.body.success === false)
47
+ const resBody = res.body;
48
+ assert(resBody.code === 50000)
49
+ assert(resBody.message === '获取项目异常')
50
+
51
+ })
52
+
53
+ it('GET /api/project with proj_key', async ()=>{
54
+ for(let i =0;i<projectList.length;i++){
55
+ const {key:projKey} = projectList[i];
56
+ let tmpRequest = request.get('/api/project');
57
+ tmpRequest = tmpRequest.set('s_t',st)
58
+ tmpRequest = tmpRequest.set('s_sign',md5(`${signKey}_${st}`));
59
+ tmpRequest = tmpRequest.query({
60
+ proj_key:projKey
61
+ });
62
+ const res = await tmpRequest;
63
+ assert(res.body.success === true)
64
+ const resData = res.body.data;
65
+ assert(resData.key === projKey)
66
+ assert(resData.name)
67
+ assert(resData.modelKey)
68
+ assert(resData.desc !== undefined)
69
+ assert(resData.homePage !== undefined)
70
+ const {menu} = resData;
71
+ menu.forEach(async (menuItem)=>{
72
+ checkMenuItem(menuItem)
73
+ })
74
+ }
75
+ // 校验menu菜单
76
+ function checkMenuItem(menuItem){
77
+ console.log('-----',menuItem.key)
78
+ assert(menuItem.key)
79
+ assert(menuItem.name)
80
+ assert(menuItem.menuType)
81
+ if(menuItem.menuType === 'group'){
82
+ assert(menuItem.subMenu !== undefined);
83
+ menuItem.subMenu.forEach(async (subMenuItem)=>{
84
+ checkMenuItem(subMenuItem)
85
+ })
86
+
87
+
88
+ }
89
+ if(menuItem.menuType === 'module'){
90
+ checkModule(menuItem)
91
+ }
92
+ }
93
+ // 检查module 菜单配置
94
+ function checkModule(menuItem){
95
+ const {moduleType} = menuItem
96
+ assert(moduleType)
97
+ if(moduleType === 'sider'){
98
+ const {siderConfig} = menuItem
99
+ assert(siderConfig)
100
+ assert(siderConfig.menu)
101
+ siderConfig.menu.forEach(async (siderMenuItem)=>{
102
+ checkMenuItem(siderMenuItem)
103
+
104
+ })
105
+ }
106
+ if(moduleType === 'iframe'){
107
+ const {iframeConfig} = menuItem
108
+ assert(iframeConfig)
109
+ assert(iframeConfig.path)
110
+ }
111
+ if(moduleType === 'custom'){
112
+ const {customConfig} = menuItem
113
+ assert(customConfig)
114
+ assert(customConfig.path)
115
+ }
116
+ if(moduleType === 'schema'){
117
+ const {schemaConfig} = menuItem
118
+ assert(schemaConfig)
119
+ assert(schemaConfig.api !== undefined)
120
+ assert(schemaConfig.schema)
121
+ }
122
+ }
123
+
124
+ })
125
+
126
+
127
+
128
+
129
+ it('GET /api/project/list without proj_key',async ()=>{
130
+ let tmpRequest = request.get('/api/project/list');
131
+ tmpRequest = tmpRequest.set('s_t',st)
132
+ tmpRequest = tmpRequest.set('s_sign',md5(`${signKey}_${st}`));
133
+ const res = await tmpRequest;
134
+ assert(res.body.success === true);
135
+ const resData = res.body.data;
136
+ assert(resData.length === projectList.length);
137
+ for(let i=0; i<resData.length; ++i) {
138
+ const item = resData[i];
139
+ assert(item.modelKey)
140
+ assert(item.key)
141
+ assert(item.name)
142
+ assert(item.desc !== undefined)
143
+ assert(item.homePage !== undefined)
144
+ }
145
+ })
146
+
147
+ it('GET /api/project/list with proj_key',async ()=>{
148
+ const {key:projKey} = projectList[Math.floor(Math.random()*projectList.length)];
149
+ const {modelKey} = await projectList.find(item => item.key === projKey);
150
+ let tmpRequest = request.get('/api/project/list');
151
+ tmpRequest = tmpRequest.set('s_t',st)
152
+ tmpRequest = tmpRequest.set('s_sign',md5(`${signKey}_${st}`));
153
+ tmpRequest = tmpRequest.query({
154
+ proj_key: projKey,
155
+ })
156
+ const res = await tmpRequest;
157
+ assert(res.body.success === true);
158
+ const resData = res.body.data;
159
+ assert(projectList.filter(item=>item.modelKey===modelKey).length === resData.length);
160
+ for(let i=0; i<resData.length; ++i) {
161
+ const item = resData[i];
162
+ assert(item.modelKey)
163
+ assert(item.key)
164
+ assert(item.name)
165
+ assert(item.desc !== undefined)
166
+ assert(item.homePage !== undefined)
167
+ }
168
+
169
+ })
170
+
171
+ it('GET /api/project/model_list', async ()=>{
172
+ let tmpRequest = request.get('/api/project/model_list');
173
+ tmpRequest = tmpRequest.set('s_t',st)
174
+ tmpRequest = tmpRequest.set('s_sign',md5(`${signKey}_${st}`));
175
+ const res = await tmpRequest;
176
+
177
+ assert(res.body.success===true)
178
+ const resData = res.body.data;
179
+ assert(resData.length>0)
180
+ for(let i = 0;i<resData.length;++i){
181
+ const item = resData[i];
182
+ assert(item.model)
183
+ assert(item.model.key)
184
+ assert(item.model.name)
185
+ assert(item.project)
186
+ for(const projKey in item.project){
187
+ assert(item.project[projKey])
188
+ assert(item.project[projKey].name)
189
+ assert(item.project[projKey].key)
190
+ }
191
+ }
192
+ })
193
+
194
+ })