@xujingquan/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.
- package/.browserslistrc +3 -0
- package/.eslintignore +4 -0
- package/.eslintrc +63 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +16 -0
- package/.prettierrc +10 -0
- package/README.md +248 -0
- package/app/controller/base.js +41 -0
- package/app/controller/project.js +75 -0
- package/app/controller/view.js +28 -0
- package/app/extend/logger.js +39 -0
- package/app/middleware/api-params-verify.js +90 -0
- package/app/middleware/api-sign-veriyf.js +47 -0
- package/app/middleware/error-handler.js +33 -0
- package/app/middleware/project-handler.js +30 -0
- package/app/middleware.js +45 -0
- package/app/pages/asserts/custom.css +13 -0
- package/app/pages/boot.js +56 -0
- package/app/pages/common/api/business-api.js +19 -0
- package/app/pages/common/api/project-api.js +27 -0
- package/app/pages/common/request.js +119 -0
- package/app/pages/common/utils.js +2 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +20 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +116 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +44 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +37 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +122 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +23 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +86 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +82 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +115 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +135 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +93 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +21 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +115 -0
- package/app/pages/dashboard/dashboard.vue +93 -0
- package/app/pages/dashboard/entry.dashboard.js +45 -0
- package/app/pages/store/index.js +4 -0
- package/app/pages/store/menu.js +61 -0
- package/app/pages/store/project.js +17 -0
- package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
- package/app/pages/widgets/header-container/asserts/logo.png +0 -0
- package/app/pages/widgets/header-container/header-container.vue +111 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +141 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +142 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +119 -0
- package/app/pages/widgets/schema-form/form-item-config.js +23 -0
- package/app/pages/widgets/schema-form/schema-form.vue +130 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +50 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +62 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +40 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +48 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +121 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +243 -0
- package/app/pages/widgets/sider-container/sider-container.vue +26 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/md5.js +950 -0
- package/app/public/static/normalize.css +267 -0
- package/app/router/project.js +11 -0
- package/app/router/view.js +13 -0
- package/app/router-schema/project.js +33 -0
- package/app/service/base.js +14 -0
- package/app/service/project.js +43 -0
- package/app/view/entry.tpl +27 -0
- package/app/webpack/config/utils.js +49 -0
- package/app/webpack/config/webpack-dev.js +55 -0
- package/app/webpack/config/webpack-prod.js +192 -0
- package/app/webpack/config/webpack.base.js +273 -0
- package/app/webpack/dev.js +60 -0
- package/app/webpack/libs/blank.js +1 -0
- package/app/webpack/prod.js +27 -0
- package/babel.config.js +15 -0
- package/commitlint.config.js +3 -0
- package/config/config.default.js +4 -0
- package/elpis-core/env.js +20 -0
- package/elpis-core/index.js +86 -0
- package/elpis-core/loader/config.js +54 -0
- package/elpis-core/loader/controller.js +69 -0
- package/elpis-core/loader/extend.js +57 -0
- package/elpis-core/loader/middleware.js +66 -0
- package/elpis-core/loader/router-schema.js +49 -0
- package/elpis-core/loader/router.js +50 -0
- package/elpis-core/loader/service.js +69 -0
- package/index.js +47 -0
- package/jsconfig.json +19 -0
- package/model/index.js +103 -0
- package/package.json +105 -0
- package/test/controller/project.test.js +200 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行时异常错误处理,兜底所有异常
|
|
3
|
+
* @param {[object]} app koa 实例
|
|
4
|
+
*/
|
|
5
|
+
module.exports = (app) => {
|
|
6
|
+
return async (ctx, next) => {
|
|
7
|
+
try {
|
|
8
|
+
await next(); // 没有异常继续执行
|
|
9
|
+
} catch (err) {
|
|
10
|
+
// 异常处理
|
|
11
|
+
const { status, message, detail } = err;
|
|
12
|
+
|
|
13
|
+
app.logger.info(JSON.stringify(err));
|
|
14
|
+
app.logger.error('[-- exception --]:', err);
|
|
15
|
+
app.logger.error('[-- exception --]:', status, message, detail);
|
|
16
|
+
|
|
17
|
+
if (message && message.indexOf('template not found') > -1) {
|
|
18
|
+
// 页面重定向
|
|
19
|
+
ctx.status = 302; // 302临时重定向 301永久吃东西
|
|
20
|
+
ctx.redirect(`${app?.options?.homePage ?? '/'}`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const resBody = {
|
|
25
|
+
success: false,
|
|
26
|
+
code: 50000,
|
|
27
|
+
message: '网络异常,请稍后重试',
|
|
28
|
+
};
|
|
29
|
+
ctx.status = 200;
|
|
30
|
+
ctx.body = resBody;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* proejctHandler 相关项目处理内容
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = (app) => {
|
|
6
|
+
return async (ctx, next) => {
|
|
7
|
+
// 只对 业务 API 进行 proj_key 处理
|
|
8
|
+
if (ctx.path.indexOf('/api/proj/') < 0) {
|
|
9
|
+
return await next();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 获取 projKey
|
|
13
|
+
const { proj_key: projKey } = ctx.request.headers;
|
|
14
|
+
|
|
15
|
+
if (!projKey) {
|
|
16
|
+
ctx.status = 200;
|
|
17
|
+
ctx.body = {
|
|
18
|
+
success: false,
|
|
19
|
+
message: 'proj_key is not found',
|
|
20
|
+
code: 446,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ctx.projKey = projKey;
|
|
27
|
+
|
|
28
|
+
await next();
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
/**
|
|
3
|
+
* 用户自定义的全局 middleware
|
|
4
|
+
* */
|
|
5
|
+
module.exports = (app) => {
|
|
6
|
+
// 资源目录
|
|
7
|
+
const publicPath = path.resolve(process.cwd(), './app/public');
|
|
8
|
+
// 配置静态根目录
|
|
9
|
+
const KoaStatic = require('koa-static');
|
|
10
|
+
app.use(KoaStatic(publicPath, {}));
|
|
11
|
+
|
|
12
|
+
// 模版渲染引擎
|
|
13
|
+
const koaNunjucks = require('koa-nunjucks-2');
|
|
14
|
+
app.use(
|
|
15
|
+
koaNunjucks({
|
|
16
|
+
ext: 'tpl', // 后续需要变量注入,所以使用 tpl
|
|
17
|
+
path: publicPath,
|
|
18
|
+
nunjucksConfig: {
|
|
19
|
+
noCache: true,
|
|
20
|
+
trimBlocks: true, // 去掉多余行
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// 引入 ctx.body 解析中间件
|
|
26
|
+
const bodyParser = require('koa-bodyparser');
|
|
27
|
+
app.use(
|
|
28
|
+
bodyParser({
|
|
29
|
+
formLimit: '1000mb',
|
|
30
|
+
enableTypes: ['form', 'json', 'text'],
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// 引入异常捕获中间件
|
|
35
|
+
app.use(app.middlewares.errorHandler);
|
|
36
|
+
|
|
37
|
+
// 引入签名合法性校验
|
|
38
|
+
app.use(app.middlewares.apiSignVeriyf);
|
|
39
|
+
|
|
40
|
+
// 引入 API 参数校验
|
|
41
|
+
app.use(app.middlewares.apiParamsVerify);
|
|
42
|
+
|
|
43
|
+
// 引入项目处理中间件
|
|
44
|
+
app.use(app.middlewares.projectHandler);
|
|
45
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createApp } from 'vue';
|
|
2
|
+
|
|
3
|
+
import ElementPlus from 'element-plus';
|
|
4
|
+
import 'element-plus/theme-chalk/index.css';
|
|
5
|
+
import 'element-plus/theme-chalk/dark/css-vars.css';
|
|
6
|
+
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
|
7
|
+
|
|
8
|
+
import './asserts/custom.css';
|
|
9
|
+
|
|
10
|
+
import { createWebHistory, createRouter } from 'vue-router';
|
|
11
|
+
|
|
12
|
+
import pinia from '$elpisStore/index';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* vue 页面主入口,用于启动 vue
|
|
16
|
+
* @param pageComponent vue 入口组件
|
|
17
|
+
* @param routes 路由列表
|
|
18
|
+
* @param libs 页面依赖的第三方包
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export default (pageComponent, { routes = [], libs = [] } = {}) => {
|
|
22
|
+
// 创建应用实例
|
|
23
|
+
const app = createApp(pageComponent);
|
|
24
|
+
|
|
25
|
+
// 应用element-ui
|
|
26
|
+
app.use(ElementPlus, {
|
|
27
|
+
locale: zhCn,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 引入 pinia
|
|
31
|
+
app.use(pinia);
|
|
32
|
+
|
|
33
|
+
// 引入第三方包
|
|
34
|
+
if (libs && libs.length) {
|
|
35
|
+
for (let i = 0; i < libs.length; i++) {
|
|
36
|
+
app.use(libs[i]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (routes && routes.length) {
|
|
41
|
+
// 页面路由
|
|
42
|
+
const router = createRouter({
|
|
43
|
+
history: createWebHistory(),
|
|
44
|
+
routes,
|
|
45
|
+
});
|
|
46
|
+
app.use(router);
|
|
47
|
+
|
|
48
|
+
// 等待路由
|
|
49
|
+
router.isReady().then(() => {
|
|
50
|
+
// 挂载应用(需要放在最后)
|
|
51
|
+
app.mount('#root');
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
app.mount('#root');
|
|
55
|
+
}
|
|
56
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { request } from '../request';
|
|
2
|
+
|
|
3
|
+
export function tableDataPort(options) {
|
|
4
|
+
const {
|
|
5
|
+
method = 'get',
|
|
6
|
+
url = '',
|
|
7
|
+
params = undefined,
|
|
8
|
+
data = undefined,
|
|
9
|
+
errorMessage = '',
|
|
10
|
+
} = options;
|
|
11
|
+
|
|
12
|
+
return request({
|
|
13
|
+
method,
|
|
14
|
+
url,
|
|
15
|
+
params,
|
|
16
|
+
data,
|
|
17
|
+
errorMessage,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { request } from '../request';
|
|
2
|
+
|
|
3
|
+
export function projectModelList() {
|
|
4
|
+
return request({
|
|
5
|
+
method: 'get',
|
|
6
|
+
url: '/project/model_list',
|
|
7
|
+
errorMessage: '获取项目列表失败',
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function projectList(params) {
|
|
12
|
+
return request({
|
|
13
|
+
method: 'get',
|
|
14
|
+
url: '/project/list',
|
|
15
|
+
params,
|
|
16
|
+
errorMessage: '获取项目列表失败',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function project(params) {
|
|
21
|
+
return request({
|
|
22
|
+
method: 'get',
|
|
23
|
+
url: '/project',
|
|
24
|
+
params,
|
|
25
|
+
errorMessage: '获取项目列表失败',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ElMessage } from 'element-plus';
|
|
2
|
+
const md5 = require('md5');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 前端封装的 curl 方法
|
|
6
|
+
* @param {object} config 请求参数
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
let baseURL = '';
|
|
10
|
+
if (process.env.NODE_ENV === 'development') {
|
|
11
|
+
// 测服
|
|
12
|
+
baseURL = '/api';
|
|
13
|
+
} else {
|
|
14
|
+
// 正式服
|
|
15
|
+
baseURL = '/api';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function request(config) {
|
|
19
|
+
const {
|
|
20
|
+
url = '', // 请求地址
|
|
21
|
+
method = 'post', // 请求方法
|
|
22
|
+
headers = {}, // 请求头
|
|
23
|
+
params = {}, // url query
|
|
24
|
+
data = {}, // post body
|
|
25
|
+
responseType = 'json', // response data type
|
|
26
|
+
timeout = 6000, // timeout
|
|
27
|
+
errorMessage = '网络异常',
|
|
28
|
+
} = config;
|
|
29
|
+
|
|
30
|
+
// 构造请求参数(把参数转换为 axios 参数)
|
|
31
|
+
const ajaxString = {
|
|
32
|
+
baseURL,
|
|
33
|
+
url,
|
|
34
|
+
method,
|
|
35
|
+
params,
|
|
36
|
+
data,
|
|
37
|
+
responseType,
|
|
38
|
+
timeout,
|
|
39
|
+
headers: {
|
|
40
|
+
...headers,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 1.创建axios的实例
|
|
45
|
+
const instance = axios.create(ajaxString);
|
|
46
|
+
|
|
47
|
+
// 2.1.请求拦截
|
|
48
|
+
instance.interceptors.request.use(
|
|
49
|
+
(config) => {
|
|
50
|
+
// 设置签名
|
|
51
|
+
const signKey = 'da98fc11-295c-a64f-5b31-d6dc4055dd06';
|
|
52
|
+
const st = Date.now();
|
|
53
|
+
const signature = md5(`${signKey}_${st}`);
|
|
54
|
+
config.headers['s_t'] = st;
|
|
55
|
+
config.headers['s_sign'] = signature;
|
|
56
|
+
|
|
57
|
+
// 只对路由存在 /api/proj 做处理
|
|
58
|
+
if (url.indexOf('/proj/') > -1 && window.projKey) {
|
|
59
|
+
// HTML 模版注入的参数
|
|
60
|
+
config.headers['proj_key'] = window.projKey;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return config;
|
|
64
|
+
},
|
|
65
|
+
(err) => {
|
|
66
|
+
return Promise.reject(err.message);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// 2.2.响应拦截
|
|
71
|
+
instance.interceptors.response.use(
|
|
72
|
+
(res) => {
|
|
73
|
+
const resData = res.data || {};
|
|
74
|
+
// 后端 API 返回格式
|
|
75
|
+
const { success } = resData;
|
|
76
|
+
|
|
77
|
+
// 失败
|
|
78
|
+
if (!success) {
|
|
79
|
+
const { message, code } = resData;
|
|
80
|
+
switch (code) {
|
|
81
|
+
case 442:
|
|
82
|
+
ElMessage.error('请求参数异常');
|
|
83
|
+
break;
|
|
84
|
+
case 445:
|
|
85
|
+
ElMessage.error('请求不合法');
|
|
86
|
+
break;
|
|
87
|
+
case 446:
|
|
88
|
+
ElMessage.error('缺少项目必要参数');
|
|
89
|
+
break;
|
|
90
|
+
case 50000:
|
|
91
|
+
ElMessage.error(message);
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
ElMessage.error(errorMessage);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
console.error(message, '[API] message');
|
|
98
|
+
return Promise.reject({ success, code, message });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 成功
|
|
102
|
+
const { data, metadata } = resData;
|
|
103
|
+
return Promise.resolve({ success, data, metadata });
|
|
104
|
+
},
|
|
105
|
+
(err) => {
|
|
106
|
+
const { message } = err;
|
|
107
|
+
if (message.match(/timeout/)) {
|
|
108
|
+
return Promise.reject({
|
|
109
|
+
message: 'Request Timeout',
|
|
110
|
+
code: 504,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return Promise.reject(err);
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
// 3.发送真正的网络请求
|
|
118
|
+
return instance(config);
|
|
119
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-sub-menu :index="menuItem.key">
|
|
3
|
+
<template #title>{{ menuItem.name }}</template>
|
|
4
|
+
<div v-for="item in menuItem.subMenu" :key="item.key">
|
|
5
|
+
<!-- 回调,调用自己 -->
|
|
6
|
+
<sub-menu v-if="item.subMenu && item.subMenu.length > 0" :menuItem="item" />
|
|
7
|
+
<el-menu-item v-else :index="item.key">
|
|
8
|
+
{{ item.name }}
|
|
9
|
+
</el-menu-item>
|
|
10
|
+
</div>
|
|
11
|
+
</el-sub-menu>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
defineProps({
|
|
16
|
+
menuItem: Object,
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<style lang="less" scoped></style>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header-container :title="projName">
|
|
3
|
+
<template #menu-content>
|
|
4
|
+
<!-- 根据 menuStore.menuList 渲染 -->
|
|
5
|
+
<el-menu
|
|
6
|
+
:default-active="activeKey"
|
|
7
|
+
:ellipsis="false"
|
|
8
|
+
mode="horizontal"
|
|
9
|
+
@select="onMenuSelect"
|
|
10
|
+
>
|
|
11
|
+
<template v-for="item in menuStore.menuList">
|
|
12
|
+
<sub-menu v-if="item.subMenu && item.subMenu.length > 0" :menu-item="item" />
|
|
13
|
+
<el-menu-item v-else :index="item.key">{{ item.name }}</el-menu-item>
|
|
14
|
+
</template>
|
|
15
|
+
</el-menu>
|
|
16
|
+
</template>
|
|
17
|
+
<template #setting-content>
|
|
18
|
+
<!-- 根据 projStore.projectList 渲染 -->
|
|
19
|
+
<el-dropdown @command="handleProjectCommand">
|
|
20
|
+
<span class="project-list">
|
|
21
|
+
{{ projName }}
|
|
22
|
+
<el-icon v-if="projectStore.projectList.length > 1" class="el-icon--right">
|
|
23
|
+
<ArrowDown />
|
|
24
|
+
</el-icon>
|
|
25
|
+
</span>
|
|
26
|
+
<template v-if="projectStore.projectList.length > 1" #dropdown>
|
|
27
|
+
<el-dropdown-menu>
|
|
28
|
+
<el-dropdown-item
|
|
29
|
+
v-for="item in projectStore.projectList"
|
|
30
|
+
:key="item.key"
|
|
31
|
+
:command="item.key"
|
|
32
|
+
:disabled="item.name === projName"
|
|
33
|
+
>
|
|
34
|
+
{{ item.name }}
|
|
35
|
+
</el-dropdown-item>
|
|
36
|
+
</el-dropdown-menu>
|
|
37
|
+
</template>
|
|
38
|
+
</el-dropdown>
|
|
39
|
+
</template>
|
|
40
|
+
<template #main-content>
|
|
41
|
+
<slot name="main-content"></slot>
|
|
42
|
+
</template>
|
|
43
|
+
</header-container>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup>
|
|
47
|
+
import { ArrowDown } from '@element-plus/icons-vue';
|
|
48
|
+
import HeaderContainer from '$elpisWidgets/header-container/header-container.vue';
|
|
49
|
+
import SubMenu from './complex-view/sub-menu/sub-menu.vue';
|
|
50
|
+
|
|
51
|
+
import { ref, watch, onMounted } from 'vue';
|
|
52
|
+
import { useRoute } from 'vue-router';
|
|
53
|
+
import { useMenuStore } from '$elpisStore/menu';
|
|
54
|
+
import { useProjectStore } from '$elpisStore/project';
|
|
55
|
+
|
|
56
|
+
const emit = defineEmits(['menu-select']);
|
|
57
|
+
defineProps({
|
|
58
|
+
projName: String,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const route = useRoute();
|
|
62
|
+
const menuStore = useMenuStore();
|
|
63
|
+
const projectStore = useProjectStore();
|
|
64
|
+
|
|
65
|
+
const activeKey = ref('');
|
|
66
|
+
watch(
|
|
67
|
+
[() => route.query.key, () => menuStore.menuList],
|
|
68
|
+
() => {
|
|
69
|
+
setActiveKey();
|
|
70
|
+
},
|
|
71
|
+
{ deep: true }
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
onMounted(() => {
|
|
75
|
+
setActiveKey();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const setActiveKey = function () {
|
|
79
|
+
const menuItem = menuStore.findMenuItem({
|
|
80
|
+
key: 'key',
|
|
81
|
+
value: route.query.key,
|
|
82
|
+
});
|
|
83
|
+
activeKey.value = menuItem?.key;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const onMenuSelect = function (menuKey) {
|
|
87
|
+
const menuItem = menuStore.findMenuItem({
|
|
88
|
+
key: 'key',
|
|
89
|
+
value: menuKey,
|
|
90
|
+
});
|
|
91
|
+
emit('menu-select', menuItem);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleProjectCommand = function (event) {
|
|
95
|
+
const projItem = projectStore.projectList?.find((item) => item.key === event);
|
|
96
|
+
if (!projItem || !projItem.homePage) return;
|
|
97
|
+
const { host } = window.location;
|
|
98
|
+
|
|
99
|
+
window.location.replace(`http://${host}/view/dashboard${projItem.homePage}`);
|
|
100
|
+
};
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<style lang="less" scoped>
|
|
104
|
+
.project-list {
|
|
105
|
+
display: flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
margin-right: 20px;
|
|
108
|
+
color: var(--el-color-primary);
|
|
109
|
+
outline: none;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
:deep(.el-menu--horizontal.el-menu) {
|
|
114
|
+
border-bottom: 1px solid #e8e8e8;
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<iframe :src="path" class="iframe" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { ref, watch, onMounted } from 'vue';
|
|
7
|
+
import { useRoute } from 'vue-router';
|
|
8
|
+
import { useMenuStore } from '$elpisStore/menu';
|
|
9
|
+
|
|
10
|
+
const route = useRoute();
|
|
11
|
+
const menuStore = useMenuStore();
|
|
12
|
+
|
|
13
|
+
const path = ref('');
|
|
14
|
+
const setPath = function () {
|
|
15
|
+
const { key, sider_key: siderKey } = route.query;
|
|
16
|
+
|
|
17
|
+
const menuItem = menuStore.findMenuItem({
|
|
18
|
+
key: 'key',
|
|
19
|
+
value: siderKey ?? key,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
path.value = menuItem?.iframeConfig?.path ?? '';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
watch(
|
|
26
|
+
[() => route.query.key, () => route.query.sider_key, () => route.query.menuList],
|
|
27
|
+
() => {
|
|
28
|
+
setPath();
|
|
29
|
+
},
|
|
30
|
+
{ deep: true }
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
onMounted(() => {
|
|
34
|
+
setPath();
|
|
35
|
+
});
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<style lang="less" scoped>
|
|
39
|
+
.iframe {
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 100%;
|
|
42
|
+
border: 0;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-card class="search-panel">
|
|
3
|
+
<schema-search-bar :schema="searchSchema" @load="load" @search="search" @reset="reset" />
|
|
4
|
+
</el-card>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
import SchemaSearchBar from '$elpisWidgets/schema-search-bar/schema-search-bar.vue';
|
|
9
|
+
|
|
10
|
+
import { inject } from 'vue';
|
|
11
|
+
|
|
12
|
+
const { searchSchema } = inject('schemaViewData');
|
|
13
|
+
|
|
14
|
+
const emits = defineEmits(['search']);
|
|
15
|
+
|
|
16
|
+
const load = function (searchValObj) {
|
|
17
|
+
emits('search', searchValObj);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const search = function (searchValObj) {
|
|
21
|
+
emits('search', searchValObj);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const reset = function () {
|
|
25
|
+
emits('search', {});
|
|
26
|
+
};
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<style lang="less" scoped>
|
|
30
|
+
.search-panel {
|
|
31
|
+
margin: 10px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:deep(el-card__body) {
|
|
35
|
+
padding-bottom: 2px;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-card class="table-panel">
|
|
3
|
+
<!-- operation-panel -->
|
|
4
|
+
<el-row v-if="tableConfig?.headerButtons?.length > 0" justify="end" class="operation-panel">
|
|
5
|
+
<el-button
|
|
6
|
+
v-for="item in tableConfig.headerButtons"
|
|
7
|
+
v-bind="item"
|
|
8
|
+
@click="operationHandler({ btnConfig: item })"
|
|
9
|
+
>{{ item.label }}</el-button
|
|
10
|
+
>
|
|
11
|
+
</el-row>
|
|
12
|
+
<!-- schema-table (组件 widget) -->
|
|
13
|
+
<schema-table
|
|
14
|
+
ref="schemaTableRef"
|
|
15
|
+
:schema="tableSchema"
|
|
16
|
+
:api="api"
|
|
17
|
+
:apiParams="apiParams"
|
|
18
|
+
:buttons="tableConfig?.rowButtons ?? []"
|
|
19
|
+
@operate="operationHandler"
|
|
20
|
+
/>
|
|
21
|
+
</el-card>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup>
|
|
25
|
+
import SchemaTable from '$elpisWidgets/schema-table/schema-table.vue';
|
|
26
|
+
|
|
27
|
+
import { ref, inject } from 'vue';
|
|
28
|
+
import { tableDataPort } from '$elpisCommon/api/business-api';
|
|
29
|
+
import { ElMessageBox, ElNotification } from 'element-plus';
|
|
30
|
+
|
|
31
|
+
const emits = defineEmits(['operate']);
|
|
32
|
+
|
|
33
|
+
const { api, apiParams, tableSchema, tableConfig } = inject('schemaViewData');
|
|
34
|
+
const schemaTableRef = ref(null);
|
|
35
|
+
|
|
36
|
+
const removeData = async function ({ btnConfig, rowData }) {
|
|
37
|
+
const { eventOption } = btnConfig;
|
|
38
|
+
if (!eventOption?.params) return;
|
|
39
|
+
|
|
40
|
+
const { params } = eventOption;
|
|
41
|
+
const removeKey = Object.keys(params)[0];
|
|
42
|
+
let removeValue;
|
|
43
|
+
const removeValueList = params[removeKey].split('::');
|
|
44
|
+
|
|
45
|
+
if (removeValueList[0] === 'schema' && removeValueList[1]) {
|
|
46
|
+
removeValue = rowData[removeValueList[1]];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ElMessageBox.confirm(`确认删除 ${removeKey} 为:${removeValue} 的数据吗?`, 'Warning', {
|
|
50
|
+
confirmButtonText: '确认',
|
|
51
|
+
cancelButtonText: '取消',
|
|
52
|
+
type: 'warning',
|
|
53
|
+
})
|
|
54
|
+
.then(async () => {
|
|
55
|
+
schemaTableRef.value.showLoading();
|
|
56
|
+
|
|
57
|
+
const res = await tableDataPort({
|
|
58
|
+
method: 'delete',
|
|
59
|
+
url: api.value,
|
|
60
|
+
data: {
|
|
61
|
+
[removeKey]: removeValue,
|
|
62
|
+
},
|
|
63
|
+
errorMessage: '删除失败',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!res || !res.success || !res.data) return;
|
|
67
|
+
|
|
68
|
+
ElNotification({
|
|
69
|
+
title: '删除成功',
|
|
70
|
+
message: '删除成功',
|
|
71
|
+
type: 'success',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await schemaTableRef.value.loadTableData();
|
|
75
|
+
})
|
|
76
|
+
.finally(() => {
|
|
77
|
+
schemaTableRef.value.hideLoading();
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const eventHandlerMap = {
|
|
82
|
+
remove: removeData,
|
|
83
|
+
};
|
|
84
|
+
const operationHandler = function ({ btnConfig, rowData }) {
|
|
85
|
+
const { eventKey } = btnConfig;
|
|
86
|
+
if (eventHandlerMap[eventKey]) {
|
|
87
|
+
eventHandlerMap[eventKey]({ btnConfig, rowData });
|
|
88
|
+
} else {
|
|
89
|
+
emits('operate', { btnConfig, rowData });
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const initTableData = async function () {
|
|
94
|
+
await schemaTableRef.value.initData();
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const loadTableData = async function () {
|
|
98
|
+
await schemaTableRef.value.loadTableData();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
defineExpose({
|
|
102
|
+
initTableData,
|
|
103
|
+
loadTableData,
|
|
104
|
+
});
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<style lang="less" scoped>
|
|
108
|
+
.table-panel {
|
|
109
|
+
flex: 1;
|
|
110
|
+
margin: 10px;
|
|
111
|
+
|
|
112
|
+
.operation-panel {
|
|
113
|
+
margin-bottom: 10px;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
:deep(.el-card__body) {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
height: 98%;
|
|
121
|
+
}
|
|
122
|
+
</style>
|