@jiangliffey/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 (81) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc +55 -0
  3. package/CLAUDE.md +81 -0
  4. package/README.md +200 -0
  5. package/app/controller/base.js +38 -0
  6. package/app/controller/project.js +74 -0
  7. package/app/controller/view.js +22 -0
  8. package/app/extend/logger.js +35 -0
  9. package/app/middleware/api-params-verify.js +81 -0
  10. package/app/middleware/api-sign-verify.js +35 -0
  11. package/app/middleware/error-handler.js +33 -0
  12. package/app/middleware/project-handler.js +27 -0
  13. package/app/middleware.js +37 -0
  14. package/app/pages/asserts/custom.css +12 -0
  15. package/app/pages/boot.js +50 -0
  16. package/app/pages/common/curl.js +89 -0
  17. package/app/pages/common/utils.js +2 -0
  18. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +21 -0
  19. package/app/pages/dashboard/complex-view/header-view/header-view.vue +123 -0
  20. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +43 -0
  21. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +40 -0
  22. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +124 -0
  23. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +23 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +87 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +100 -0
  26. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +118 -0
  27. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +124 -0
  28. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +80 -0
  29. package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +21 -0
  30. package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +135 -0
  31. package/app/pages/dashboard/dashboard.vue +96 -0
  32. package/app/pages/dashboard/entry.dashboard.js +45 -0
  33. package/app/pages/dashboard/todo/todo.vue +11 -0
  34. package/app/pages/store/index.js +4 -0
  35. package/app/pages/store/menu.js +58 -0
  36. package/app/pages/store/project.js +14 -0
  37. package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
  38. package/app/pages/widgets/header-container/asserts/logo.png +0 -0
  39. package/app/pages/widgets/header-container/header-container.vue +106 -0
  40. package/app/pages/widgets/schema-form/complex-view/input/input.vue +134 -0
  41. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +136 -0
  42. package/app/pages/widgets/schema-form/complex-view/select/select.vue +116 -0
  43. package/app/pages/widgets/schema-form/form-item-config.js +23 -0
  44. package/app/pages/widgets/schema-form/schema-form.vue +135 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +50 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +67 -0
  47. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +44 -0
  48. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +51 -0
  49. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +129 -0
  50. package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
  51. package/app/pages/widgets/schema-table/schema-table.vue +235 -0
  52. package/app/pages/widgets/sider-container/sider-container.vue +31 -0
  53. package/app/public/static/logo.png +0 -0
  54. package/app/public/static/normalize.css +239 -0
  55. package/app/router/project.js +6 -0
  56. package/app/router/view.js +8 -0
  57. package/app/router-schema/project.js +30 -0
  58. package/app/service/base.js +11 -0
  59. package/app/service/project.js +56 -0
  60. package/app/view/entry.tpl +26 -0
  61. package/app/webpack/config/webpack.base.js +203 -0
  62. package/app/webpack/config/webpack.dev.js +59 -0
  63. package/app/webpack/config/webpack.prod.js +107 -0
  64. package/app/webpack/dev.js +53 -0
  65. package/app/webpack/libs/blank.js +3 -0
  66. package/app/webpack/prod.js +17 -0
  67. package/config/config.default.js +3 -0
  68. package/docs/dashboard-model.js +153 -0
  69. package/elpis-core/env.js +23 -0
  70. package/elpis-core/index.js +96 -0
  71. package/elpis-core/loader/config.js +50 -0
  72. package/elpis-core/loader/controller.js +54 -0
  73. package/elpis-core/loader/extend.js +49 -0
  74. package/elpis-core/loader/middleware.js +53 -0
  75. package/elpis-core/loader/router-schema.js +41 -0
  76. package/elpis-core/loader/router.js +45 -0
  77. package/elpis-core/loader/service.js +54 -0
  78. package/index.js +40 -0
  79. package/model/index.js +99 -0
  80. package/package.json +92 -0
  81. package/test/controller/project.test.js +225 -0
@@ -0,0 +1,96 @@
1
+ const Koa = require('koa');
2
+ const path = require('path');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ const env = require('./env');
6
+
7
+ const middlewareLoader = require('./loader/middleware');
8
+ const routerSchemaLoader = require('./loader/router-schema');
9
+ const routerLoader = require('./loader/router');
10
+ const controllerLoader = require('./loader/controller');
11
+ const serviceLoader = require('./loader/service');
12
+ const configLoader = require('./loader/config');
13
+ const extendLoader = require('./loader/extend');
14
+
15
+
16
+ module.exports = {
17
+ /**
18
+ * 启动项目
19
+ * @param options 项目配置
20
+ * options = {
21
+ * name: '项目名称',
22
+ * homPage: '项目主页',
23
+ * }
24
+ */
25
+ start(options = {}) {
26
+ // koa 实例
27
+ const app = new Koa();
28
+
29
+ // 应用配置
30
+ app.options = options;
31
+
32
+ // 基础路径
33
+ app.baseDir = process.cwd();
34
+
35
+ // 业务文件路径
36
+ app.businessPath = path.resolve(app.baseDir, `.${sep}app`);
37
+
38
+ // 初始化环境配置
39
+ app.env = env(app);
40
+ console.log(`-- [start] env: ${app.env.getEnv()} --`);
41
+
42
+ // 加载 middleware
43
+ middlewareLoader(app);
44
+ console.log(`-- [start] middleware loaded --`);
45
+
46
+ // 加载 routerSchema
47
+ routerSchemaLoader(app);
48
+ console.log(`-- [start] routerSchema loaded --`);
49
+
50
+ // 加载 controller
51
+ controllerLoader(app);
52
+ console.log(`-- [start] controller loaded --`);
53
+
54
+ // 加载 service
55
+ serviceLoader(app);
56
+ console.log(`-- [start] service loaded --`);
57
+
58
+ // 加载 config
59
+ configLoader(app);
60
+ console.log(`-- [start] config loaded --`);
61
+
62
+ // 加载 extend
63
+ extendLoader(app);
64
+ console.log(`-- [start] extend loaded --`);
65
+
66
+ // 注册elpis全局中间件
67
+ const elpisMiddlewarePath = path.resolve(__dirname, `..${sep}app${sep}middleware.js`);
68
+ const elpisMiddleware = require(elpisMiddlewarePath);
69
+ elpisMiddleware(app);
70
+ console.log(`-- [start] global elpis middleware loaded --`);
71
+
72
+ // 注册业务全局中间件
73
+ try {
74
+ require(`${app.businessPath}${sep}middleware.js`)(app);
75
+ console.log(`-- [start] global business middleware loaded --`);
76
+ } catch (e) {
77
+ console.error('未找到业务全局中间件文件');
78
+ }
79
+
80
+ // 注册路由
81
+ routerLoader(app);
82
+ console.log(`-- [start] router loaded --`);
83
+
84
+ // 启动服务
85
+ try {
86
+ const port = process.env.PORT || 8080;
87
+ const host = process.env.IP || '0.0.0.0';
88
+ app.listen(port, host);
89
+ console.log(`Server running at ${port}`);
90
+ } catch (e) {
91
+ console.error(e);
92
+ }
93
+
94
+ return app;
95
+ }
96
+ }
@@ -0,0 +1,50 @@
1
+ const path = require('path');
2
+ const { env } = require('process');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ /**
6
+ * config loader
7
+ * @param {object} app Koa实例
8
+ * 配置区分 本地/测试/生产环境,通过 env 环境读取不同的配置文件 env.config
9
+ * 通过 env.config 覆盖默认的 default.config 配置,加载到 app.config 上
10
+ *
11
+ * 目录下对应的config配置
12
+ * 默认配置 config/config.default.js
13
+ * 本地配置 config/config.local.js
14
+ * 测试配置 config/config.beta.js
15
+ * 生产配置 config/config.prod.js
16
+ */
17
+
18
+ module.exports = (app) => {
19
+ // elpis config 目录及相关文件
20
+ const elpisConfigPath = path.resolve(__dirname, `..${sep}..${sep}config`);
21
+ let defaultConfig = require(path.resolve(elpisConfigPath, `.${sep}config.default.js`));
22
+
23
+ // 获取 业务 config 目录及相关文件
24
+ const businessConfigPath = path.resolve(process.cwd(), `.${sep}config`);
25
+ try {
26
+ defaultConfig = {
27
+ ...defaultConfig,
28
+ ...require(path.resolve(businessConfigPath, `.${sep}config.default.js`))
29
+ }
30
+ } catch (e) {
31
+ console.warn('未找到默认配置文件 config.default.js');
32
+ }
33
+
34
+ // 获取 env.config
35
+ let envConfig = {};
36
+ try {
37
+ if(app.env.isLocal()) { // 本地环境
38
+ envConfig = require(path.resolve(businessConfigPath, `.${sep}config.local.js`));
39
+ } else if(app.env.isBeta()) { // 测试环境
40
+ envConfig = require(path.resolve(businessConfigPath, `.${sep}config.beta.js`));
41
+ } else if(app.env.isProduction()) { // 生产环境
42
+ envConfig = require(path.resolve(businessConfigPath, `.${sep}config.prod.js`));
43
+ }
44
+ } catch (e) {
45
+ console.warn('未找到环境配置文件 env.config.js');
46
+ }
47
+
48
+ // 覆盖并加载 config 配置
49
+ app.config = Object.assign({}, defaultConfig, envConfig);
50
+ }
@@ -0,0 +1,54 @@
1
+ const glob = require('glob');
2
+ const path = require('path');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ /**
6
+ * controller loader
7
+ * @param {object} app Koa实例
8
+ * 加载 所有controller,可通过 'app.controller.${目录}.${文件名}' 访问
9
+ * 例如:app.controller.auth.login 可访问 controller/auth/login.js 中的中间件
10
+ * 注意:中间件文件必须导出一个函数,且函数接受一个参数 app
11
+ */
12
+
13
+ module.exports = (app) => {
14
+ const controller = {};
15
+
16
+ // 读取 elpis/app/controller/xxx/xxx.js下的所有文件,加载中间件
17
+ const elpisControllerPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}controller`);
18
+ const elpisFileList = glob.sync(path.resolve(elpisControllerPath, `.${sep}**${sep}**.js`));
19
+ elpisFileList.forEach(file => handleFile(file));
20
+
21
+ // 读取 业务根目录/app/controller/xxx/xxx.js下的所有文件,加载中间件
22
+ const businessControllerPath = path.resolve(app.businessPath, `.${sep}controller`);
23
+ const businessFileList = glob.sync(path.resolve(businessControllerPath, `.${sep}**${sep}**.js`));
24
+ businessFileList.forEach(file => handleFile(file));
25
+
26
+ // 遍历所有文件目录,把内容加载到 app.controller 上
27
+ function handleFile(file) {
28
+ // 提取文件名称
29
+ let name = path.resolve(file)
30
+
31
+ // 截取文件路径中 'app/controller/' 后面的部分,去掉 '.js' 后缀
32
+ name = name.substring(name.lastIndexOf(`controller${sep}`) + `controller${sep}`.length, name.lastIndexOf('.js'));
33
+
34
+ // 把 '-' 替换为驼峰命名
35
+ name = name.replace(/[_-]([a-z])/ig, (s) => s.substring(1).toUpperCase());
36
+
37
+ // 挂载 controller 到内存 app 对象中
38
+ let tempController = controller;
39
+ const names = name.split(sep);
40
+ for (let i = 0, len = names.length; i < len; i++) {
41
+ if(i === len - 1) {
42
+ const controllerModule = require(path.resolve(file))(app);
43
+ tempController[names[i]] = new controllerModule();
44
+ } else {
45
+ if(!tempController[names[i]]) {
46
+ tempController[names[i]] = {};
47
+ }
48
+ tempController = tempController[names[i]];
49
+ }
50
+ }
51
+ }
52
+
53
+ app.controller = controller;
54
+ }
@@ -0,0 +1,49 @@
1
+ const glob = require('glob');
2
+ const path = require('path');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ /**
6
+ * extend loader
7
+ * @param {object} app Koa实例
8
+ * 加载 所有extend,可通过 'app.extend.${文件名}' 访问
9
+ * 例如:
10
+ * app/extend
11
+ * ├── custom-extend.js
12
+ * ==> app.extend.customExtend
13
+ *
14
+ */
15
+
16
+ module.exports = (app) => {
17
+ // 读取 elpis/app/extend/xxx.js下的所有文件,加载中间件
18
+ const elpisExtendPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}extend`);
19
+ const elpisFileList = glob.sync(path.resolve(elpisExtendPath, `.${sep}**${sep}**.js`));
20
+ elpisFileList.forEach(file => handleFile(file));
21
+
22
+ // 读取 业务/app/extend/xxx.js下的所有文件,加载中间件
23
+ const businessExtendPath = path.resolve(app.businessPath, `.${sep}extend`);
24
+ const businessFileList = glob.sync(path.resolve(businessExtendPath, `.${sep}**${sep}**.js`));
25
+ businessFileList.forEach(file => handleFile(file));
26
+
27
+ // 把内容加载到 app.extend 上
28
+ function handleFile(file) {
29
+ // 提取文件名称
30
+ let name = path.resolve(file)
31
+
32
+ // 截取文件路径中 'app/extend/' 后面的部分,去掉 '.js' 后缀
33
+ name = name.substring(name.lastIndexOf(`extend${sep}`) + `extend${sep}`.length, name.lastIndexOf('.js'));
34
+
35
+ // 把 '-' 替换为驼峰命名
36
+ name = name.replace(/[_-]([a-z])/ig, (s) => s.substring(1).toUpperCase());
37
+
38
+ // 过滤app已经存在的key
39
+ for (const key in app) {
40
+ if(key === name) {
41
+ console.warn(`app对象已存在${name}属性,extend文件${file}加载失败`);
42
+ return;
43
+ }
44
+ }
45
+
46
+ // 挂载 extend 到内存 app 对象中
47
+ app[name] = require(path.resolve(file))(app);
48
+ }
49
+ }
@@ -0,0 +1,53 @@
1
+ const glob = require('glob');
2
+ const path = require('path');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ /**
6
+ * middleware loader
7
+ * @param {object} app Koa实例
8
+ * 加载 所有middleware,可通过 'app.middlewares.${目录}.${文件名}' 访问
9
+ * 例如:app.middlewares.auth.login 可访问 middleware/auth/login.js 中的中间件
10
+ * 注意:中间件文件必须导出一个函数,且函数接受一个参数 app
11
+ */
12
+
13
+ module.exports = (app) => {
14
+ const middlewares = {};
15
+
16
+ // 读取 elpis/app/middleware/xxx/xxx.js下的所有文件,加载中间件
17
+ const elpisMiddlewarePath = path.resolve(__dirname, `..${sep}..${sep}app${sep}middleware`);
18
+ const elpisFileList = glob.sync(path.resolve(elpisMiddlewarePath, `.${sep}**${sep}**.js`));
19
+ elpisFileList.forEach(file => handleFile(file));
20
+
21
+ // 读取 业务根目录/app/middleware/xxx/xxx.js下的所有文件,加载中间件
22
+ const businessMiddlewarePath = path.resolve(app.businessPath, `.${sep}middleware`);
23
+ const businessFileList = glob.sync(path.resolve(businessMiddlewarePath, `.${sep}**${sep}**.js`));
24
+ businessFileList.forEach(file => handleFile(file));
25
+
26
+ // 遍历所有文件目录,把内容加载到 app.middleware 上
27
+ function handleFile(file) {
28
+ // 提取文件名称
29
+ let name = path.resolve(file)
30
+
31
+ // 截取文件路径中 'app/middleware/' 后面的部分,去掉 '.js' 后缀
32
+ name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf('.js'));
33
+
34
+ // 把 '-' 替换为驼峰命名
35
+ name = name.replace(/[_-]([a-z])/ig, (s) => s.substring(1).toUpperCase());
36
+
37
+ // 挂载 middleware 到内存 app 对象中
38
+ let tempMiddleware = middlewares;
39
+ const names = name.split(sep);
40
+ for (let i = 0, len = names.length; i < len; i++) {
41
+ if(i === len - 1) {
42
+ tempMiddleware[names[i]] = require(path.resolve(file))(app);
43
+ } else {
44
+ if(!tempMiddleware[names[i]]) {
45
+ tempMiddleware[names[i]] = {};
46
+ }
47
+ tempMiddleware = tempMiddleware[names[i]];
48
+ }
49
+ }
50
+ }
51
+
52
+ app.middlewares = middlewares;
53
+ }
@@ -0,0 +1,41 @@
1
+ const glob = require('glob');
2
+ const path = require('path');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ /**
6
+ * router-schema loader
7
+ * @param {object} app Koa实例
8
+ * 通过 'json-schema & ajv' 对API规则进行约束,配合 api-params-verify 中间件使用
9
+ * app/router-schema/xxx/xxx.js
10
+ 输出:
11
+ app.routerSchema = {
12
+ '${api1}': ${jsonSchema},
13
+ '${api2}': ${jsonSchema},
14
+ '${api3}': ${jsonSchema},
15
+ ...
16
+ }
17
+ */
18
+
19
+ module.exports = (app) => {
20
+ let routerSchema = {};
21
+
22
+ // 读取 elpis/app/router-schema/xxx/xxx.js下的所有文件
23
+ const elpisRouterSchemaPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}router-schema`);
24
+ const elpisFileList = glob.sync(path.resolve(elpisRouterSchemaPath, `.${sep}**${sep}**.js`));
25
+ elpisFileList.forEach(file => handleFile(file));
26
+
27
+ // 读取 业务/router-schema/xxx/xxx.js下的所有文件
28
+ const businessRouterSchemaPath = path.resolve(app.businessPath, `.${sep}router-schema`);
29
+ const businessFileList = glob.sync(path.resolve(businessRouterSchemaPath, `.${sep}**${sep}**.js`));
30
+ businessFileList.forEach(file => handleFile(file));
31
+
32
+ // 注册所有 routerSchema 到 app.routerSchema 上(使得可以 'app.routerSchema' 这样访问)
33
+ function handleFile(file) {
34
+ routerSchema = {
35
+ ...routerSchema,
36
+ ...require(path.resolve(file))
37
+ }
38
+ }
39
+
40
+ app.routerSchema = routerSchema;
41
+ }
@@ -0,0 +1,45 @@
1
+ const KoaRouter = require('koa-router');
2
+ const glob = require('glob');
3
+ const path = require('path');
4
+ const { sep } = path; // 兼容不同操作系统上的斜杠
5
+
6
+ /**
7
+ * router loader
8
+ * @param {object} app Koa实例
9
+ * 解析所有 app/router/ 下的所有js文件,注册到 KoaRouter 上
10
+ */
11
+
12
+ module.exports = (app) => {
13
+ // 实例化 KoaRouter
14
+ const router = new KoaRouter();
15
+
16
+ // 找到 elpis 路由文件路径
17
+ const elpisRouterPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}router`);
18
+ // 注册所有 elpis 路由
19
+ const elpisFileList = glob.sync(path.resolve(elpisRouterPath, `.${sep}**${sep}**.js`))
20
+ elpisFileList.forEach(file => {
21
+ require(path.resolve(file))(app, router);
22
+ });
23
+
24
+ // 找到业务路由文件路径
25
+ const businessRouterPath = path.resolve(app.businessPath, `.${sep}router`);
26
+ // 注册所有业务路由
27
+ const businessFileList = glob.sync(path.resolve(businessRouterPath, `.${sep}**${sep}**.js`))
28
+ businessFileList.forEach(file => {
29
+ // module.exports = (app,router) => {
30
+ // router.get('xxx/xxx/xxx/xxx', xxxController)
31
+ // }
32
+
33
+ require(path.resolve(file))(app, router);
34
+ });
35
+
36
+ // 路由兜底(健壮性)
37
+ router.get('*', async (ctx, next) => {
38
+ ctx.status = 404; //临时重定向
39
+ ctx.redirect(`${app?.options?.homePage ?? '/'}`);
40
+ });
41
+
42
+ // 路由注册到app上
43
+ app.use(router.routes());
44
+ app.use(router.allowedMethods());
45
+ }
@@ -0,0 +1,54 @@
1
+ const glob = require('glob');
2
+ const path = require('path');
3
+ const { sep } = path; // 兼容不同操作系统上的斜杠
4
+
5
+ /**
6
+ * service loader
7
+ * @param {object} app Koa实例
8
+ * 加载 所有service,可通过 'app.service.${目录}.${文件名}' 访问
9
+ * 例如:app.service.auth.login 可访问 service/auth/login.js 中的中间件
10
+ * 注意:中间件文件必须导出一个函数,且函数接受一个参数 app
11
+ */
12
+
13
+ module.exports = (app) => {
14
+ const service = {};
15
+
16
+ // 读取 elpis/app/service/xxx/xxx.js下的所有文件,加载中间件
17
+ const elpisServicePath = path.resolve(__dirname, `..${sep}..${sep}app${sep}service`);
18
+ const elpisFileList = glob.sync(path.resolve(elpisServicePath, `.${sep}**${sep}**.js`));
19
+ elpisFileList.forEach(file => handleFile(file));
20
+
21
+ // 读取 业务根目录/app/service/xxx/xxx.js下的所有文件,加载中间件
22
+ const businessServicePath = path.resolve(app.businessPath, `.${sep}service`);
23
+ const businessFileList = glob.sync(path.resolve(businessServicePath, `.${sep}**${sep}**.js`));
24
+ businessFileList.forEach(file => handleFile(file));
25
+
26
+ // 遍历所有文件目录,把内容加载到 app.service 上
27
+ function handleFile(file) {
28
+ // 提取文件名称
29
+ let name = path.resolve(file)
30
+
31
+ // 截取文件路径中 'app/service/' 后面的部分,去掉 '.js' 后缀
32
+ name = name.substring(name.lastIndexOf(`service${sep}`) + `service${sep}`.length, name.lastIndexOf('.js'));
33
+
34
+ // 把 '-' 替换为驼峰命名
35
+ name = name.replace(/[_-]([a-z])/ig, (s) => s.substring(1).toUpperCase());
36
+
37
+ // 挂载 service 到内存 app 对象中
38
+ let tempService = service;
39
+ const names = name.split(sep);
40
+ for (let i = 0, len = names.length; i < len; i++) {
41
+ if(i === len - 1) {
42
+ const serviceModule = require(path.resolve(file))(app);
43
+ tempService[names[i]] = new serviceModule();
44
+ } else {
45
+ if(!tempService[names[i]]) {
46
+ tempService[names[i]] = {};
47
+ }
48
+ tempService = tempService[names[i]];
49
+ }
50
+ }
51
+ }
52
+
53
+ app.service = service;
54
+ }
package/index.js ADDED
@@ -0,0 +1,40 @@
1
+ // 引入elpis-core
2
+ const ElpisCore = require('./elpis-core');
3
+ // 引入 前端工程化构建方法
4
+ const FEBuildDev = require('./app/webpack/dev.js');
5
+ const FEBuildProd = require('./app/webpack/prod.js');
6
+
7
+ module.exports = {
8
+ /**
9
+ * 服务端基础配置,供 elpis-core 使用
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
+ * @param env 环境变量 local/prod
21
+ */
22
+ fontendBuild(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
+ */
34
+ serverStart(options = {}) {
35
+ const app = ElpisCore.start(options);
36
+ return app;
37
+ }
38
+ }
39
+
40
+
package/model/index.js ADDED
@@ -0,0 +1,99 @@
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
+
13
+ // 因为 project 继承 model,所以需要处理修改和新增内容的情况
14
+ // project有的键值,model也有 => 修改(重载)
15
+ // project有的键值,model没有 => 新增(拓展)
16
+ // model有的键值,project没有 => 保留(继承)
17
+
18
+ // 处理修改和保留
19
+ for(let i = 0; i < modelValue.length; i++) {
20
+ let modelItem = modelValue[i];
21
+ const projItem = projValue.find(projItem => projItem.key === modelItem.key);
22
+ // project 有的键值,model也有,则递归调用 projectExtendModel 方法覆盖修改
23
+ result.push(projItem ? projectExtendModel(modelItem, projItem) : modelItem);
24
+ }
25
+
26
+ // 处理新增
27
+ for(let i = 0; i < projValue.length; i++) {
28
+ const projItem = projValue[i];
29
+ const modelItem = modelValue.find(modelItem => modelItem.key === projItem.key);
30
+ if(!modelItem) {
31
+ result.push(projItem);
32
+ }
33
+ }
34
+
35
+ return result;
36
+ }
37
+ })
38
+ }
39
+
40
+ /**
41
+ * 解析 model 配置,并返回组织且继承后的数据结构
42
+ * [{
43
+ * model: ${model}
44
+ * project: {
45
+ * proj1Key: ${proj1},
46
+ * proj2Key: ${proj2}
47
+ * }
48
+ * }, ...]
49
+ */
50
+ module.exports = (app) => {
51
+ const modelList = [];
52
+
53
+ // 遍历当前文件夹,构造模型数据结构,挂载到 modelList 上
54
+ const modelPath = path.resolve(process.cwd(), `.${sep}model`);
55
+ const fileList = glob.sync(path.resolve(modelPath, `.${sep}**${sep}**.js`));
56
+ fileList.forEach(file => {
57
+ if(path.basename(file) === 'index.js') { return; }
58
+
59
+ // 区分配置类型(model / project)
60
+ const type = file.indexOf('/project/') > -1 ? 'project' : 'model';
61
+
62
+ if(type === 'project') {
63
+ const modelKey = file.match(/\/model\/(.*?)\/project/)?.[1];
64
+ const projKey = file.match(/\/project\/(.*?)\.js/)?.[1];
65
+ let modelItem = modelList.find(item => item.model?.key === modelKey);
66
+ if(!modelItem) { //初始化 model 数据结构
67
+ modelItem = {};
68
+ modelList.push(modelItem);
69
+ }
70
+ if(!modelItem.project) { //初始化 project 数据结构
71
+ modelItem.project = {};
72
+ }
73
+ modelItem.project[projKey] = require(path.resolve(file));
74
+ modelItem.project[projKey].key = projKey; //注入 projectKey
75
+ modelItem.project[projKey].modelKey = modelKey; //注入 modelKey
76
+ };
77
+
78
+ if(type === 'model') {
79
+ const modelKey = file.match(/\/model\/(.*?)\/model\.js/)?.[1];
80
+ let modelItem = modelList.find(item => item.model?.key === modelKey);
81
+ if(!modelItem) {
82
+ modelItem = {};
83
+ modelList.push(modelItem);
84
+ }
85
+ modelItem.model = require(path.resolve(file));
86
+ modelItem.model.key = modelKey; //注入 modelKey
87
+ };
88
+ });
89
+
90
+ // 数据进一步整理:project => 继承 model
91
+ modelList.forEach(item => {
92
+ const { model, project } =item;
93
+ for(const key in project) {
94
+ project[key] = projectExtendModel(model,project[key]);
95
+ }
96
+ })
97
+
98
+ return modelList;
99
+ }
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@jiangliffey/elpis",
3
+ "version": "1.0.0",
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
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://git.code.tencent.com/jiangliffey/elpis.git"
13
+ },
14
+ "author": "jiangliffey",
15
+ "license": "ISC",
16
+ "dependencies": {
17
+ "@babel/core": "^7.24.0",
18
+ "@babel/runtime": "^7.29.2",
19
+ "@element-plus/icons-vue": "^2.3.2",
20
+ "ajv": "^6.10.2",
21
+ "axios": "^0.19.2",
22
+ "echarts": "^5.5.0",
23
+ "element-plus": "^2.3.7",
24
+ "generate-password": "^1.7.1",
25
+ "glob": "^7.1.4",
26
+ "jsonwebtoken": "^9.0.2",
27
+ "knex": "^0.19.0",
28
+ "koa": "2.7.0",
29
+ "koa-bodyparser": "^4.2.1",
30
+ "koa-nunjucks-2": "^3.0.2",
31
+ "koa-router": "^7.4.0",
32
+ "koa-static": "^5.0.0",
33
+ "koa-useragent": "2.0.0",
34
+ "koa2-cors": "^2.0.6",
35
+ "less": "^3.8.1",
36
+ "lodash": "^4.17.21",
37
+ "log4js": "^6.9.1",
38
+ "md5": "^2.2.1",
39
+ "moment": "^2.29.4",
40
+ "mysql": "^2.18.1",
41
+ "node-schedule": "^2.1.1",
42
+ "nodemon": "^1.19.2",
43
+ "path": "^0.12.7",
44
+ "pinia": "^2.1.6",
45
+ "superagent": "^8.1.2",
46
+ "vue": "^3.3.4",
47
+ "vue-json-viewer": "^3.0.4",
48
+ "vue-router": "^4.2.4",
49
+ "vuex": "^4.1.0",
50
+ "@babel/plugin-transform-runtime": "^7.1.0",
51
+ "@babel/preset-env": "^7.4.5",
52
+ "babel-loader": "^8.0.4",
53
+ "clean-webpack-plugin": "^0.1.19",
54
+ "consoler": "^0.2.0",
55
+ "css-loader": "^0.23.1",
56
+ "css-minimizer-webpack-plugin": "^5.0.1",
57
+ "directory-named-webpack-plugin": "^4.0.1",
58
+ "express": "^4.18.2",
59
+ "file-loader": "^6.2.0",
60
+ "happypack": "^5.0.1",
61
+ "html-webpack-inject-attributes-plugin": "^1.0.1",
62
+ "html-webpack-plugin": "^5.5.3",
63
+ "less-loader": "^11.1.3",
64
+ "mini-css-extract-plugin": "^2.7.6",
65
+ "style-loader": "^0.14.1",
66
+ "terser-webpack-plugin": "^5.4.0",
67
+ "url-loader": "^4.1.1",
68
+ "vue-loader": "^17.2.2",
69
+ "vue-style-loader": "^4.1.2",
70
+ "webpack": "^5.88.1",
71
+ "webpack-dev-middleware": "^6.1.1",
72
+ "webpack-hot-middleware": "^2.25.4",
73
+ "webpack-merge": "^5.10.0"
74
+ },
75
+ "devDependencies": {
76
+ "assert": "^2.0.0",
77
+ "babel-eslint": "^10.0.2",
78
+ "eslint": "^7.32.0",
79
+ "eslint-plugin-import": "^2.28.1",
80
+ "eslint-plugin-vue": "^9.17.0",
81
+ "ghooks": "~1.0.3",
82
+ "mocha": "^6.1.4",
83
+ "supertest": "^4.0.2",
84
+ "validate-commit-msg": "~2.14.0"
85
+ },
86
+ "config": {
87
+ "ghooks": {
88
+ "commit-msg": "validate-commit-msg",
89
+ "pre-commit": "npm run lint"
90
+ }
91
+ }
92
+ }