@slice_cy/mint 1.2.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 (98) hide show
  1. package/README.md +697 -0
  2. package/app/common/base-response.js +10 -0
  3. package/app/common/business-error.js +10 -0
  4. package/app/common/error-code.js +22 -0
  5. package/app/common/return-utils.js +40 -0
  6. package/app/common/router-schema-validator.js +25 -0
  7. package/app/controller/base.js +24 -0
  8. package/app/controller/project.js +90 -0
  9. package/app/controller/view.js +13 -0
  10. package/app/docs/dsl-doc.js +148 -0
  11. package/app/extend/logger.js +30 -0
  12. package/app/extend/params-valid.js +6 -0
  13. package/app/extend/return-utils.js +9 -0
  14. package/app/middleware/api-sign-verify.js +25 -0
  15. package/app/middleware/err-handler.js +18 -0
  16. package/app/middleware/project-handler.js +25 -0
  17. package/app/pages/boot.js +34 -0
  18. package/app/pages/dashboard/complex-view/header-view/index.vue +108 -0
  19. package/app/pages/dashboard/complex-view/iframe-view/index.vue +37 -0
  20. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/index.vue +36 -0
  21. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/index.vue +123 -0
  22. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +14 -0
  23. package/app/pages/dashboard/complex-view/schema-view/components/createForm/index.vue +72 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/detailPanel/index.vue +52 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/editForm/index.vue +87 -0
  26. package/app/pages/dashboard/complex-view/schema-view/hook/use-schema.js +113 -0
  27. package/app/pages/dashboard/complex-view/schema-view/index.vue +114 -0
  28. package/app/pages/dashboard/complex-view/sider-view/index.vue +116 -0
  29. package/app/pages/dashboard/entry.dashboard.js +44 -0
  30. package/app/pages/dashboard/index.vue +60 -0
  31. package/app/pages/service/index.js +70 -0
  32. package/app/pages/store/index.js +4 -0
  33. package/app/pages/store/menu.js +86 -0
  34. package/app/pages/store/project.js +9 -0
  35. package/app/pages/utils/index.js +26 -0
  36. package/app/pages/widgets/header-container/assets/avatar.png +0 -0
  37. package/app/pages/widgets/header-container/assets/icon.png +0 -0
  38. package/app/pages/widgets/header-container/index.vue +108 -0
  39. package/app/pages/widgets/schema-form/components/input/index.vue +155 -0
  40. package/app/pages/widgets/schema-form/components/input-number/index.vue +148 -0
  41. package/app/pages/widgets/schema-form/components/select/index.vue +132 -0
  42. package/app/pages/widgets/schema-form/form-item-cofnig.js +18 -0
  43. package/app/pages/widgets/schema-form/index.vue +137 -0
  44. package/app/pages/widgets/schema-search-bar/component/date-range-picker/index.vue +55 -0
  45. package/app/pages/widgets/schema-search-bar/component/dynamic-select/index.vue +70 -0
  46. package/app/pages/widgets/schema-search-bar/component/input/index.vue +54 -0
  47. package/app/pages/widgets/schema-search-bar/component/select/index.vue +65 -0
  48. package/app/pages/widgets/schema-search-bar/index.vue +143 -0
  49. package/app/pages/widgets/schema-search-bar/search-item-config.js +20 -0
  50. package/app/pages/widgets/schema-table/index.vue +198 -0
  51. package/app/pages/widgets/sider-container/index.vue +30 -0
  52. package/app/post-router.js +9 -0
  53. package/app/pre-middleware.js +37 -0
  54. package/app/public/dist/entry.dashboard.tpl +2 -0
  55. package/app/public/dist/entry.project-list.tpl +2 -0
  56. package/app/public/dist/prod/css/async.609.bcbe5779.css +1 -0
  57. package/app/public/dist/prod/css/async.952.9bc7c180.css +1 -0
  58. package/app/public/dist/prod/css/common.1a98227b.css +1 -0
  59. package/app/public/dist/prod/css/entry.project-list.14f64dd9.css +1 -0
  60. package/app/public/dist/prod/js/142.56646c73.bundle.js +1 -0
  61. package/app/public/dist/prod/js/609.8270b20d.bundle.js +1 -0
  62. package/app/public/dist/prod/js/68.2813fafe.bundle.js +1 -0
  63. package/app/public/dist/prod/js/952.c76c04fb.bundle.js +1 -0
  64. package/app/public/dist/prod/js/common.9985f827.bundle.js +1 -0
  65. package/app/public/dist/prod/js/entry.dashboard.cd7591e4.bundle.js +1 -0
  66. package/app/public/dist/prod/js/entry.project-list.beb0fa9d.bundle.js +1 -0
  67. package/app/public/dist/prod/js/runtime.5602b906.bundle.js +1 -0
  68. package/app/public/dist/prod/js/vendor.709fbf32.bundle.js +53 -0
  69. package/app/public/dist/prod/static/images/avatar.78e68eaa.png +0 -0
  70. package/app/public/dist/static/logo.png +0 -0
  71. package/app/public/dist/static/normalize.css +1 -0
  72. package/app/public/static/logo.png +0 -0
  73. package/app/public/static/normalize.css +267 -0
  74. package/app/router/project.js +6 -0
  75. package/app/router/view.js +5 -0
  76. package/app/router-schema/project.js +14 -0
  77. package/app/service/base.js +22 -0
  78. package/app/service/project.js +49 -0
  79. package/app/view/entry.tpl +18 -0
  80. package/app/webpack/config/webpack.base.js +288 -0
  81. package/app/webpack/config/webpack.dev.js +53 -0
  82. package/app/webpack/config/webpack.prod.js +106 -0
  83. package/app/webpack/dev.js +34 -0
  84. package/app/webpack/libs/block.js +1 -0
  85. package/app/webpack/prod.js +21 -0
  86. package/config/config.default.js +5 -0
  87. package/index.js +37 -0
  88. package/mint-core/env.js +23 -0
  89. package/mint-core/index.js +101 -0
  90. package/mint-core/loader/config.js +59 -0
  91. package/mint-core/loader/controller.js +59 -0
  92. package/mint-core/loader/extend.js +42 -0
  93. package/mint-core/loader/middleware.js +54 -0
  94. package/mint-core/loader/router-schema.js +39 -0
  95. package/mint-core/loader/router.js +36 -0
  96. package/mint-core/loader/service.js +57 -0
  97. package/model/index.js +94 -0
  98. package/package.json +93 -0
@@ -0,0 +1,22 @@
1
+ module.exports = {
2
+ SUCCESS: {
3
+ code: 0,
4
+ message: "成功",
5
+ },
6
+ NOT_FOUND: {
7
+ code: 40400,
8
+ message: "资源不存在",
9
+ },
10
+ SYSTEM_ERROR: {
11
+ code: 50000,
12
+ message: "系统异常,请联系管理员",
13
+ },
14
+ PARAMS_VALIDATE_ERROR: {
15
+ code: 40000,
16
+ message: "参数校验失败",
17
+ },
18
+ SIGNATURE_ERROR: {
19
+ code: 44500,
20
+ message: "签名错误",
21
+ },
22
+ };
@@ -0,0 +1,40 @@
1
+ const BaseResponse = require("./base-response");
2
+ const BusinessError = require("./business-error");
3
+ const ERROR_CODE = require("./error-code");
4
+
5
+ const success = (data = {}, metaData = {}) => {
6
+ return new BaseResponse({
7
+ code: ERROR_CODE.SUCCESS.code,
8
+ message: ERROR_CODE.SUCCESS.message,
9
+ data,
10
+ metaData,
11
+ });
12
+ };
13
+
14
+ const error = (code, message, data = null) => {
15
+ return new BaseResponse({
16
+ code,
17
+ message,
18
+ data,
19
+ });
20
+ };
21
+
22
+ const businessError = (businessCode, options = {}) => {
23
+ const { code, message } = businessCode;
24
+ const { data = null, message: customMessage } = options;
25
+
26
+ return error(code, customMessage || message, data);
27
+ };
28
+
29
+ const throwIf = (bool, businessCode, message) => {
30
+ if (bool) {
31
+ throw new BusinessError(businessCode.code, message || businessCode.message);
32
+ }
33
+ };
34
+
35
+ module.exports = {
36
+ success,
37
+ error,
38
+ businessError,
39
+ throwIf,
40
+ };
@@ -0,0 +1,25 @@
1
+ const ERROR_CODE = require("./error-code");
2
+ const { throwIf } = require("./return-utils");
3
+
4
+ const formatZodIssues = (issues) => {
5
+ return (issues || [])
6
+ .map((issue) => {
7
+ const fieldPath = issue.path?.length ? issue.path.join(".") : "value";
8
+ return `${fieldPath}: ${issue.message}`;
9
+ })
10
+ .join("; ");
11
+ };
12
+
13
+ const validate = (schema, data) => {
14
+ const result = schema.safeParse(data);
15
+ throwIf(
16
+ !result.success,
17
+ ERROR_CODE.PARAMS_VALIDATE_ERROR,
18
+ formatZodIssues(result?.error?.issues),
19
+ );
20
+ return result.data;
21
+ };
22
+
23
+ module.exports = {
24
+ validate,
25
+ };
@@ -0,0 +1,24 @@
1
+ module.exports = (app) => {
2
+ /**
3
+ * controller基类
4
+ * 统一收拢controller的通用方法
5
+ */
6
+ return class BaseController {
7
+ constructor() {
8
+ this.routerSchema = app.routerSchema;
9
+ }
10
+ /**
11
+ * API处理成功时统一返回结构
12
+ * @param {object} ctx 上下文
13
+ * @param {object} data 响应数据
14
+ */
15
+ success(ctx, data = {}, metaData = {}) {
16
+ ctx.status = 200;
17
+ ctx.body = app.returnUtils.success(data, metaData);
18
+ }
19
+
20
+ validateParams(schema, data) {
21
+ return app.paramsValid.validate(schema, data);
22
+ }
23
+ };
24
+ };
@@ -0,0 +1,90 @@
1
+ module.exports = (app) => {
2
+ const BaseController = require("./base")(app);
3
+ return class ProjectController extends BaseController {
4
+ /**
5
+ * 获取模型列表
6
+ * @returns
7
+ */
8
+ async getModelList(ctx) {
9
+ const { project: ProjectService } = app.service;
10
+ const result = await ProjectService.getModelList();
11
+ const modelList = result.reduce((pre, cur) => {
12
+ const { model, project } = cur;
13
+ const { key, name, desc } = model;
14
+ const item = {
15
+ key,
16
+ name,
17
+ desc,
18
+ };
19
+ item.project = Object.keys(project).reduce((preProj, proj) => {
20
+ const { name, desc, homePage, icon, key } = project[proj];
21
+ return {
22
+ ...preProj,
23
+ [proj]: {
24
+ name,
25
+ desc,
26
+ homePage,
27
+ icon,
28
+ key,
29
+ },
30
+ };
31
+ }, {});
32
+ return [...pre, item];
33
+ }, []);
34
+ return this.success(ctx, modelList);
35
+ }
36
+
37
+ /**
38
+ * 获取项目列表
39
+ * @param {string} project_key
40
+ */
41
+ async getProjectList(ctx) {
42
+ const { project: ProjectService } = app.service;
43
+ // 参数校验
44
+ const params = this.validateParams(
45
+ this.routerSchema.projectListQueryDTO.get,
46
+ ctx.request.query,
47
+ );
48
+ // 调用服务
49
+ const projectList = await ProjectService.getProjectList(params.project_key);
50
+
51
+ const projectListVo = projectList.map((i) => {
52
+ const { icon, homePage, name, desc, key, modelKey } = i;
53
+ return {
54
+ icon,
55
+ homePage,
56
+ name,
57
+ desc,
58
+ key,
59
+ modelKey,
60
+ };
61
+ });
62
+
63
+ // 返回数据
64
+ return this.success(ctx, projectListVo);
65
+ }
66
+
67
+ /**
68
+ * 获取项目详情
69
+ * @param {string} project_key
70
+ */
71
+ async getProject(ctx) {
72
+ const { project: ProjectService } = app.service;
73
+ // 参数校验
74
+ const params = this.validateParams(this.routerSchema.projectQueryDTO.get, ctx.request.query);
75
+ // 调用服务
76
+ const projectInfo = await ProjectService.getProject(params.project_key);
77
+ const { desc, key, modelKey, name, icon, homePage, menu } = projectInfo;
78
+ const projectVo = {
79
+ desc,
80
+ key,
81
+ modelKey,
82
+ name,
83
+ icon,
84
+ homePage,
85
+ menu,
86
+ };
87
+ return this.success(ctx, projectVo);
88
+ }
89
+ };
90
+ };
@@ -0,0 +1,13 @@
1
+ module.exports = (app) => {
2
+ return class ViewController {
3
+ /**
4
+ * 返回模版页面
5
+ * @param {object} ctx 上下文
6
+ */
7
+ async render(ctx) {
8
+ await ctx.render(`dist/entry.${ctx.params.page}`, {
9
+ project_key: ctx.request.query.project_key,
10
+ });
11
+ }
12
+ };
13
+ };
@@ -0,0 +1,148 @@
1
+ /**
2
+ * 个人对DSL的理解
3
+ * visible字段的必要性:对于通用的模块,比如user,大概率会在领域模型中定义,项目模型继承领域模型定义的用户模块的schema。
4
+ * 但是项目模型对于同业务模块下的字段显示隐藏的需求也许会不一致,因此visible可以作为一个渲染的子开关由项目模型自行覆盖,是有必要定义的
5
+ * 整份DSL的设计:
6
+ * 1. 由数据驱动菜单,菜单驱动业务板块
7
+ * 2. 业务板块核心的本质都是处理数据源,因此要从数据维度出发可以得出:obj的value的顶层是对数据的本质描述(约束),
8
+ * 拓展其他xxxOption作为一个数据字段在任意场景中的描述,从而得出schema的设计
9
+ * 3. 业务板块的数据来源,通过api字段定义好一个业务模块的前缀实现
10
+ * 4. 业务板块的UI渲染展示,就可以从页面子版块维度出发,分为search,table 以及 component动态组件
11
+ * 总结:要从站点维度,业务板块维度以及单个板块的操作板块维度,数据维度进行分析
12
+ */
13
+
14
+ /**
15
+ * 领域模型开发过程中的反思:
16
+ * 组件封装思路:核心考虑接受的参数,暴露的api、抛出什么事件
17
+ * 开发前端功能时,要考虑对后端资源的合理利用,避免滥用
18
+ * 通用组件的封装尽量保持纯粹性,如果要与业务强相关需要新增一层组件桥梁进行数据转发通信
19
+ * 目录规范,组件引入规范,模版编写规范
20
+ * 对复合数据需要考虑纯粹性和去噪音提取
21
+ * 组件需要拿到众多子组件的数据时,要考虑load问题
22
+ * 核心重复使用的方法要落地到pina等共享状态管理中
23
+ * 熟悉了递归的思路写法
24
+ * 尽可能使用reduce / Object + reduce实现数据高内聚低耦合的写法,避免数据处理散乱在多行中
25
+ * 从安全性考虑,SSR注入,数据隔离的思想
26
+ */
27
+
28
+ /**
29
+ * 动态组件的思路:
30
+ * 业务板块下,无论在哪里交互触发,最终抛出的事件都会直达schema版块,由schema版块进行事件处理,完成调度各种动态组件实现各种联动拓展
31
+ * 动态组件处理好抛出事件,再由schema板块进行分发到具体的子组件行为
32
+ */
33
+ module.exports = {
34
+ model: "", // 不同DSL模版标识,用于匹配对应页面渲染模版
35
+ name: "", // 站点标题
36
+ desc: "", // 站点描述
37
+ icon: "", // 站点图标
38
+ homePage: "", // 站点首页
39
+ // 菜单数组,承载分组/功能模块菜单
40
+ menu: [
41
+ {
42
+ key: "", // 菜单唯一标识,全局不可重复
43
+ name: "", // 当前菜单展示名称
44
+ menuType: "", // 菜单类型枚举:group(分组菜单) | module(功能模块菜单)
45
+ moduleType: "", // 模块渲染类型枚举:custom(自定义页面) | schema(标准CRUD页面) | iframe(内嵌页面) | sider(侧边子菜单容器)
46
+ subMenu: [], // 如果menuType === group时必填;分组下嵌套子菜单数组,结构同当前menu
47
+ // menuType === module && moduleType === custom 时必传
48
+ customConfig: {
49
+ path: "", // 自定义页面前端路由地址
50
+ },
51
+ // menuType === module && moduleType === iframe 时必传
52
+ iframeConfig: {
53
+ path: "", // iframe跳转外部页面完整URL地址
54
+ },
55
+ // menuType === module && moduleType === sider 时必传
56
+ siderConfig: {
57
+ menu: [], // 侧边栏子菜单数组,结构同外层menu单项;子菜单禁止再次配置moduleType: sider
58
+ },
59
+ // menuType === module && moduleType === schema 时必传
60
+ schemeConfig: {
61
+ /**
62
+ * 遵循RESTful API规范,接口约定:
63
+ * 【GET】 /xxx/list 列表查询
64
+ * 【GET】 /xxx/:id 单条详情
65
+ * 【POST】 /xxx 新增数据
66
+ * 【PUT】 /xxx/:id 编辑修改
67
+ * 【DELETE】 /xxx/:id 删除数据
68
+ */
69
+ api: "", // CRUD接口基础路由前缀
70
+ // 表单/表格/搜索通用数据字段描述schema
71
+ schema: {
72
+ type: "object",
73
+ properties: {
74
+ ["key"]: {
75
+ type: "", // 字段类型
76
+ label: "", // 字段名称
77
+ // ...标准schema配置
78
+
79
+ // 字段在表格中的表现,不配置tableOption代表字段不在表格中渲染
80
+ tableOption: {
81
+ uiTableColumnConfig: {}, // ui组件的表格列props配置
82
+ visible: true, // 字段是否在表格中渲染显示;仅显式配置false时隐藏(定义该开关的意义是考虑到DSL继承覆盖的问题)
83
+ toFixed: 2, // 数字字段小数保留位数,仅数字类型生效
84
+ },
85
+ // 搜索栏表单配置,缺失该节点则此字段不渲染到搜索区
86
+ searchOption: {
87
+ uiComponentConfig: {}, // 表单控件配置
88
+ comType: "", // 搜索组件类型枚举:input | select | dynamicSelect | dateRangePicker
89
+ enumList: [], // 如果comType === select时,需要传enumList
90
+ visible: true, // 字段是否在表格中渲染显示;仅显式配置false时隐藏(定义该开关的意义是考虑到DSL集成覆盖的问题)
91
+ default: "", // 默认值
92
+ api: "", // comType === dynamicSelect时必填,下拉选项数据请求接口
93
+ },
94
+ "[comName]Option": {
95
+ uiComponentConfig: {}, // 表单控件配置
96
+ comType: "", // 搜索组件类型枚举:input | select | dynamicSelect | dateRangePicker
97
+ enumList: [], // 如果comType === select时,需要传enumList
98
+ visible: true, // 字段是否在表格中渲染显示;仅显式配置false时隐藏(定义该开关的意义是考虑到DSL集成覆盖的问题)
99
+ default: "", // 默认值
100
+ disabled: true, // 是否禁用
101
+ api: "", // comType === dynamicSelect时必填,下拉选项数据请求接口
102
+ },
103
+ ...{}, // schema扩展自定义字段属性,按需拓展
104
+ },
105
+ ...{},
106
+ },
107
+ required: [], // 会注入到多option中: required: true/false
108
+ },
109
+ // 表格区域整体UI渲染配置
110
+ tableConfig: {
111
+ headerButtons: [
112
+ // 表格顶部操作按钮组
113
+ {
114
+ label: "", // 按钮展示文字
115
+ eventKey: "", // 按钮绑定事件唯一标识,前端匹配对应事件逻辑
116
+ eventOption: {
117
+ // 当eventKey === remove时,必传
118
+ params: {
119
+ paramsKey: "固定值 | schema::[tableKey]", // 参数取值规则,固定格式schema::字段名,代表取当前行schema字段值
120
+ },
121
+ // 当eventKey === showComponent时,需要传
122
+ comName: "",
123
+ },
124
+ uiButtonConfig: {}, // 透传Button组件属性
125
+ },
126
+ ...{},
127
+ ],
128
+ rowButtons: [
129
+ // 表格每行右侧操作按钮组
130
+ {
131
+ label: "", // 按钮展示文字
132
+ eventKey: "", // 按钮绑定事件唯一标识
133
+ eventOption: {}, // 行按钮事件参数配置,格式同headerButtons.eventOption
134
+ uiButtonConfig: {}, // 透传Button组件属性
135
+ },
136
+ ...{},
137
+ ],
138
+ },
139
+ searchConfig: {}, // 搜索栏全局拓展配置,可自定义搜索按钮、布局、默认查询参数等
140
+ componentConfig: {
141
+ ["comName"]: {
142
+ // 组件的配置项,支持用户自定义拓展
143
+ },
144
+ }, // 动态组件配置
145
+ },
146
+ },
147
+ ],
148
+ };
@@ -0,0 +1,30 @@
1
+ const log4js = require("log4js");
2
+ module.exports = (app) => {
3
+ let logger;
4
+ const { isDevelopment } = app.$env;
5
+ if (isDevelopment()) {
6
+ logger = console;
7
+ } else {
8
+ log4js.configure({
9
+ appenders: {
10
+ console: {
11
+ type: "console",
12
+ },
13
+ dateFile: {
14
+ type: "dateFile",
15
+ filename: "logs/application.log",
16
+ pattern: ".yyyy-MM-dd",
17
+ alwaysIncludePattern: true,
18
+ },
19
+ },
20
+ categories: {
21
+ default: {
22
+ appenders: ["console", "dateFile"],
23
+ level: "info",
24
+ },
25
+ },
26
+ });
27
+ logger = log4js.getLogger();
28
+ }
29
+ return logger;
30
+ };
@@ -0,0 +1,6 @@
1
+ const { validate } = require("../common/router-schema-validator");
2
+ module.exports = (app) => {
3
+ return {
4
+ validate,
5
+ };
6
+ };
@@ -0,0 +1,9 @@
1
+ const { success, error, businessError, throwIf } = require("../common/return-utils");
2
+ module.exports = (app) => {
3
+ return {
4
+ success,
5
+ error,
6
+ businessError,
7
+ throwIf,
8
+ };
9
+ };
@@ -0,0 +1,25 @@
1
+ const md5 = require("md5");
2
+ const ERROR_CODE = require("../common/error-code");
3
+
4
+ const API_EXPIRE = 1000 * 60 * 5;
5
+
6
+ module.exports = (app) => {
7
+ return async (ctx, next) => {
8
+ const { method, path } = ctx;
9
+ // 只对api请求做验签
10
+ if (!/^\/api/g.test(ctx.path)) {
11
+ return await next();
12
+ }
13
+
14
+ const { s_sign: sSign, s_t: st } = ctx.request.headers;
15
+ const signKey = "mint";
16
+ const signature = md5(`${signKey}_${st}`);
17
+ app.logger.info(`[${method}]${path} ${signature}`);
18
+ if (!sSign || !st || signature !== sSign || Date.now() - st > API_EXPIRE) {
19
+ ctx.status = 200;
20
+ ctx.body = app.returnUtils.businessError(ERROR_CODE.SIGNATURE_ERROR);
21
+ return;
22
+ }
23
+ await next();
24
+ };
25
+ };
@@ -0,0 +1,18 @@
1
+ const BusinessError = require("../common/business-error");
2
+ const ERROR_CODE = require("../common/error-code");
3
+
4
+ module.exports = (app) => {
5
+ return async (ctx, next) => {
6
+ try {
7
+ await next();
8
+ } catch (err) {
9
+ console.log("-----------err-----------", err);
10
+ app.logger.error(err.message);
11
+ ctx.status = 200;
12
+ ctx.body =
13
+ err instanceof BusinessError
14
+ ? app.returnUtils.error(err.code, err.message)
15
+ : app.returnUtils.businessError(ERROR_CODE.SYSTEM_ERROR);
16
+ }
17
+ };
18
+ };
@@ -0,0 +1,25 @@
1
+ const { SYSTEM_ERROR } = require("../common/error-code");
2
+ /**
3
+ * 项目维度的中间件
4
+ * @param {*} app
5
+ * @returns
6
+ */
7
+ module.exports = (app) => {
8
+ return async function (ctx, next) {
9
+ // 只对api请求做验签
10
+ if (!/^\/api\/proj\//i.test(ctx.url)) {
11
+ await next();
12
+ return;
13
+ }
14
+
15
+ const { project_key } = ctx.request.headers;
16
+ if (!project_key) {
17
+ ctx.status = 200;
18
+ ctx.body = app.returnUtils.businessError(SYSTEM_ERROR, { message: "获取项目异常" });
19
+ return;
20
+ }
21
+ ctx.projectKey = project_key;
22
+
23
+ await next();
24
+ };
25
+ };
@@ -0,0 +1,34 @@
1
+ import { createApp } from "vue";
2
+ import { createRouter, createWebHistory } from "vue-router";
3
+ import store from "$mintStore/index";
4
+ import "ant-design-vue/dist/reset.css";
5
+
6
+ /**
7
+ * 启动器
8
+ * @param {*} App 根组件
9
+ * @param {object} options 配置项
10
+ * @param {array} options.routes 路由配置项
11
+ * @param {function(app)} callback 可通用callback的vue实例app自行挂载
12
+ */
13
+ export default (App, options = {}, callback) => {
14
+ const { routes = [] } = options;
15
+ const app = createApp(App);
16
+ // 挂载store
17
+ app.use(store);
18
+ // 对外提供自定义挂载拓展
19
+ callback && callback(app);
20
+ // 挂载路由
21
+ if (routes && routes.length) {
22
+ const router = createRouter({
23
+ history: createWebHistory(),
24
+ routes,
25
+ });
26
+ app.use(router);
27
+ router.isReady().then(() => {
28
+ // 挂载完成
29
+ app.mount("#app");
30
+ });
31
+ } else {
32
+ app.mount("#app");
33
+ }
34
+ };
@@ -0,0 +1,108 @@
1
+ <template>
2
+ <header-container v-bind="$props">
3
+ <template #menu-content>
4
+ <a-menu
5
+ v-model:selected-keys="avtiveKey"
6
+ mode="horizontal"
7
+ :items="menuList"
8
+ @click="onMenuSelect"
9
+ />
10
+ </template>
11
+ <template #setting-content>
12
+ <a-dropdown>
13
+ <a
14
+ class="ant-dropdown-link"
15
+ @click.prevent
16
+ >
17
+ 切换项目
18
+ <DownOutlined />
19
+ </a>
20
+ <template #overlay>
21
+ <a-menu
22
+ :items="proejectOption"
23
+ @click="handleProjectSelect"
24
+ ></a-menu>
25
+ </template>
26
+ </a-dropdown>
27
+ </template>
28
+ <template #main-content>
29
+ <slot name="main-content"></slot>
30
+ </template>
31
+ </header-container>
32
+ </template>
33
+
34
+ <script setup>
35
+ import { computed, ref, onMounted, watch } from "vue";
36
+ import { useRoute } from "vue-router";
37
+ import HeaderContainer from "$mintWidgets/header-container/index.vue";
38
+ import { useProjectStore } from "$mintStore/project";
39
+ import { useMenuStore } from "$mintStore/menu";
40
+
41
+ const emit = defineEmits(["menu-select"]);
42
+
43
+ const route = useRoute();
44
+
45
+ const projectStore = useProjectStore();
46
+ const menuStore = useMenuStore();
47
+
48
+ const menuList = ref([]);
49
+
50
+ const proejectOption = computed(() => {
51
+ return projectStore.projectList.map((i) => ({
52
+ key: i.key,
53
+ label: i.name,
54
+ disabled: i.key === route.query.project_key,
55
+ }));
56
+ });
57
+
58
+ const avtiveKey = ref([]);
59
+ const setActiveKey = () => {
60
+ const key = route.query.key;
61
+ if (!key) {
62
+ return;
63
+ }
64
+ const item = menuStore.findMenuItem({
65
+ key: "key",
66
+ value: route.query.key,
67
+ });
68
+
69
+ avtiveKey.value = item?.key ? [item.key] : [];
70
+ };
71
+
72
+ const setMenuList = () => {
73
+ menuList.value = menuStore.transfomMenuList();
74
+ };
75
+
76
+ watch(
77
+ [() => menuStore.menuList, () => route.query.key],
78
+ () => {
79
+ setActiveKey();
80
+ setMenuList();
81
+ },
82
+ { deep: true },
83
+ );
84
+
85
+ const onMenuSelect = ({ key }) => {
86
+ const menuItem = menuStore.findMenuItem({
87
+ key: "key",
88
+ value: key,
89
+ });
90
+ if (menuItem) {
91
+ emit("menu-select", menuItem);
92
+ }
93
+ };
94
+
95
+ const handleProjectSelect = ({ key }) => {
96
+ const project = projectStore.projectList.find((i) => i.key === key);
97
+ if (project && project.homePage) {
98
+ window.location.replace(`${location.origin}/view/dashboard${project.homePage}`);
99
+ }
100
+ };
101
+
102
+ onMounted(() => {
103
+ setActiveKey();
104
+ setMenuList();
105
+ });
106
+ </script>
107
+
108
+ <style lang="less" scoped></style>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <iframe
3
+ width="100%"
4
+ height="100%"
5
+ :src="path"
6
+ ></iframe>
7
+ </template>
8
+
9
+ <script setup>
10
+ import { ref, watch, onMounted } from "vue";
11
+ import { useMenuStore } from "$mintStore/menu";
12
+ import { useRoute } from "vue-router";
13
+ const route = useRoute();
14
+ const menuStore = useMenuStore();
15
+
16
+ const path = ref("");
17
+ const setPath = () => {
18
+ const menuItem = menuStore.findMenuItem({
19
+ key: "key",
20
+ value: route.query.sider_key || route.query.key,
21
+ });
22
+ if (!menuItem) return;
23
+ path.value = menuItem?.iframeConfig?.path ?? "";
24
+ };
25
+
26
+ watch(
27
+ [() => route.query.key, () => route.query.sider_key, () => menuStore.menuList],
28
+ () => {
29
+ setPath();
30
+ },
31
+ { deep: true },
32
+ );
33
+
34
+ onMounted(() => {
35
+ setPath();
36
+ });
37
+ </script>