@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.
- package/.eslintignore +3 -0
- package/.eslintrc +55 -0
- package/CLAUDE.md +81 -0
- package/README.md +200 -0
- package/app/controller/base.js +38 -0
- package/app/controller/project.js +74 -0
- package/app/controller/view.js +22 -0
- package/app/extend/logger.js +35 -0
- package/app/middleware/api-params-verify.js +81 -0
- package/app/middleware/api-sign-verify.js +35 -0
- package/app/middleware/error-handler.js +33 -0
- package/app/middleware/project-handler.js +27 -0
- package/app/middleware.js +37 -0
- package/app/pages/asserts/custom.css +12 -0
- package/app/pages/boot.js +50 -0
- package/app/pages/common/curl.js +89 -0
- package/app/pages/common/utils.js +2 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +21 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +123 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +43 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +40 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +124 -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 +87 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +100 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +118 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +124 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +80 -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 +135 -0
- package/app/pages/dashboard/dashboard.vue +96 -0
- package/app/pages/dashboard/entry.dashboard.js +45 -0
- package/app/pages/dashboard/todo/todo.vue +11 -0
- package/app/pages/store/index.js +4 -0
- package/app/pages/store/menu.js +58 -0
- package/app/pages/store/project.js +14 -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 +106 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +134 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +136 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +116 -0
- package/app/pages/widgets/schema-form/form-item-config.js +23 -0
- package/app/pages/widgets/schema-form/schema-form.vue +135 -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 +67 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +44 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +51 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +129 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +235 -0
- package/app/pages/widgets/sider-container/sider-container.vue +31 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/normalize.css +239 -0
- package/app/router/project.js +6 -0
- package/app/router/view.js +8 -0
- package/app/router-schema/project.js +30 -0
- package/app/service/base.js +11 -0
- package/app/service/project.js +56 -0
- package/app/view/entry.tpl +26 -0
- package/app/webpack/config/webpack.base.js +203 -0
- package/app/webpack/config/webpack.dev.js +59 -0
- package/app/webpack/config/webpack.prod.js +107 -0
- package/app/webpack/dev.js +53 -0
- package/app/webpack/libs/blank.js +3 -0
- package/app/webpack/prod.js +17 -0
- package/config/config.default.js +3 -0
- package/docs/dashboard-model.js +153 -0
- package/elpis-core/env.js +23 -0
- package/elpis-core/index.js +96 -0
- package/elpis-core/loader/config.js +50 -0
- package/elpis-core/loader/controller.js +54 -0
- package/elpis-core/loader/extend.js +49 -0
- package/elpis-core/loader/middleware.js +53 -0
- package/elpis-core/loader/router-schema.js +41 -0
- package/elpis-core/loader/router.js +45 -0
- package/elpis-core/loader/service.js +54 -0
- package/index.js +40 -0
- package/model/index.js +99 -0
- package/package.json +92 -0
- package/test/controller/project.test.js +225 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* projectHandler 相关项目处理内容
|
|
3
|
+
*/
|
|
4
|
+
module.exports = (app) => {
|
|
5
|
+
return async (ctx, next) => {
|
|
6
|
+
// 只对业务API进行 proj_key 处理
|
|
7
|
+
if(ctx.path.indexOf('/api/proj/') < 0) {
|
|
8
|
+
return await next();
|
|
9
|
+
}
|
|
10
|
+
// 获取 projKey
|
|
11
|
+
const { proj_key: projKey } = ctx.request.headers;
|
|
12
|
+
|
|
13
|
+
if(!projKey) {
|
|
14
|
+
ctx.status = 200;
|
|
15
|
+
ctx.body = {
|
|
16
|
+
success: false,
|
|
17
|
+
message: 'proj_key not found',
|
|
18
|
+
code: 446
|
|
19
|
+
};
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ctx.projKey = projKey;
|
|
24
|
+
|
|
25
|
+
await next();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = (app) => {
|
|
4
|
+
//配置静态根目录
|
|
5
|
+
const koaStatic = require('koa-static');
|
|
6
|
+
app.use(koaStatic(path.resolve(process.cwd(), './app/public')));
|
|
7
|
+
|
|
8
|
+
// 模板渲染引擎
|
|
9
|
+
const koaNunjucks = require('koa-nunjucks-2');
|
|
10
|
+
app.use(koaNunjucks({
|
|
11
|
+
ext: 'tpl',
|
|
12
|
+
path: path.resolve(process.cwd(), './app/public'),
|
|
13
|
+
nunjucksConfig: {
|
|
14
|
+
noCache: true,
|
|
15
|
+
trimBlocks: true
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
// 引入 ctx.body 解析中间件
|
|
20
|
+
const bodyParser = require('koa-bodyparser');
|
|
21
|
+
app.use(bodyParser({
|
|
22
|
+
formLimit: '1000mb',
|
|
23
|
+
enableTypes: ['json', 'form', 'text']
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// 引入异常捕获中间件
|
|
27
|
+
app.use(app.middlewares.errorHandler);
|
|
28
|
+
|
|
29
|
+
// 签名合法性校验
|
|
30
|
+
app.use(app.middlewares.apiSignVerify);
|
|
31
|
+
|
|
32
|
+
// 引入 API 参数校验
|
|
33
|
+
app.use(app.middlewares.apiParamsVerify);
|
|
34
|
+
|
|
35
|
+
// 引入 项目处理中间件
|
|
36
|
+
app.use(app.middlewares.projectHandler);
|
|
37
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createApp } from "vue";
|
|
2
|
+
|
|
3
|
+
// 引入elementUI
|
|
4
|
+
import ElementPlus from 'element-plus';
|
|
5
|
+
import 'element-plus/theme-chalk/index.css';
|
|
6
|
+
import 'element-plus/theme-chalk/dark/css-vars.css'
|
|
7
|
+
|
|
8
|
+
import './asserts/custom.css';
|
|
9
|
+
|
|
10
|
+
import pinia from '$elpisStore';
|
|
11
|
+
|
|
12
|
+
import { createRouter, createWebHistory } from "vue-router";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* vue页面主入口,用于启动vue
|
|
17
|
+
* @params pageComponent vue 入口组件
|
|
18
|
+
* @params routes 路由列表
|
|
19
|
+
* @params libs 页面依赖的第三包
|
|
20
|
+
*/
|
|
21
|
+
export default (pageComponent, { routes, libs } = {}) => {
|
|
22
|
+
const app = createApp(pageComponent);
|
|
23
|
+
|
|
24
|
+
// 应用elementUI
|
|
25
|
+
app.use(ElementPlus); //顺序不能反:先注册 再挂载
|
|
26
|
+
|
|
27
|
+
// 引入 pinia
|
|
28
|
+
app.use(pinia);
|
|
29
|
+
|
|
30
|
+
// 引入 第三方包
|
|
31
|
+
if(libs && libs.length) {
|
|
32
|
+
for (let i = 0; i < libs.length; i++) {
|
|
33
|
+
app.use(libs[i]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 页面路由
|
|
38
|
+
if(routes && routes.length) {
|
|
39
|
+
const router = createRouter({
|
|
40
|
+
history: createWebHistory('/view/dashboard'), // 采用history模式
|
|
41
|
+
routes,
|
|
42
|
+
})
|
|
43
|
+
app.use(router);
|
|
44
|
+
router.isReady().then(() => {
|
|
45
|
+
app.mount('#root');
|
|
46
|
+
})
|
|
47
|
+
} else {
|
|
48
|
+
app.mount('#root');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const md5 = require('md5');
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { ElMessage } from 'element-plus'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 前端封装的 curl 方法
|
|
7
|
+
* @params options 请求参数
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const curl = ({
|
|
11
|
+
url, // 请求地址
|
|
12
|
+
method = 'post', // 请求方法
|
|
13
|
+
headers = {}, // 请求头
|
|
14
|
+
query = {}, // 请求参数:query
|
|
15
|
+
data = {}, // post body
|
|
16
|
+
responseType = 'json', // response data type
|
|
17
|
+
timeout = 6000,
|
|
18
|
+
errorMessage = '网络异常'
|
|
19
|
+
}) => {
|
|
20
|
+
|
|
21
|
+
// 接口签名处理(让接口便动态)
|
|
22
|
+
const signKey = 'dftgyh84adsuh462wef46ef5s6d4fiop';
|
|
23
|
+
const st = Date.now();
|
|
24
|
+
|
|
25
|
+
const dtoHeaders = {
|
|
26
|
+
...headers,
|
|
27
|
+
s_t: st,
|
|
28
|
+
s_sign: md5(`${signKey}_${st}`)
|
|
29
|
+
}
|
|
30
|
+
if(url.indexOf('/api/proj/') > -1 && window.projKey) {
|
|
31
|
+
dtoHeaders.proj_key = window.projKey;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 构造请求参数(把参数转换为axios参数)
|
|
35
|
+
const ajaxStting = {
|
|
36
|
+
url,
|
|
37
|
+
method,
|
|
38
|
+
params: query,
|
|
39
|
+
data,
|
|
40
|
+
responseType,
|
|
41
|
+
timeout,
|
|
42
|
+
headers: dtoHeaders
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return axios.request(ajaxStting).then((response) => {
|
|
46
|
+
const resData = response.data || {};
|
|
47
|
+
|
|
48
|
+
// 后端API返回格式
|
|
49
|
+
const { success } = resData;
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// 失败
|
|
53
|
+
if(!success) {
|
|
54
|
+
const { message, code } = resData;
|
|
55
|
+
if(code === 442) {
|
|
56
|
+
ElMessage.error('请求参数异常');
|
|
57
|
+
} else if(code === 445) {
|
|
58
|
+
ElMessage.error('请求不合法');
|
|
59
|
+
} else if(code === 446) {
|
|
60
|
+
ElMessage.error('缺少项目必要参数');
|
|
61
|
+
} else if(code === 50000) {
|
|
62
|
+
ElMessage.error(message);
|
|
63
|
+
} else {
|
|
64
|
+
ElMessage.error(errorMessage);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.error(message);
|
|
68
|
+
|
|
69
|
+
return Promise.resolve({ success, code, message });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 成功
|
|
73
|
+
const { data, metadata } = resData;
|
|
74
|
+
return Promise.resolve({ success, data, metadata });
|
|
75
|
+
|
|
76
|
+
}).catch((error) => {
|
|
77
|
+
const { message } = error;
|
|
78
|
+
if(message.match(/timeout/)) {
|
|
79
|
+
return Promise.resolve({
|
|
80
|
+
message: 'Request Timeout',
|
|
81
|
+
code: 504,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return Promise.resolve(error);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default curl;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
<sub-menu
|
|
6
|
+
v-if="item.subMenu && item.subMenu.length > 0"
|
|
7
|
+
:menu-item="item"
|
|
8
|
+
>
|
|
9
|
+
</sub-menu>
|
|
10
|
+
<el-menu-item v-else :index="item.key">{{ item.name }}</el-menu-item>
|
|
11
|
+
</div>
|
|
12
|
+
</el-sub-menu>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup>
|
|
16
|
+
const { menuItem } = defineProps([ 'menuItem' ]);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<style lang="less" scoped>
|
|
20
|
+
|
|
21
|
+
</style>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header-container :title="projName">
|
|
3
|
+
<template #menu-content>
|
|
4
|
+
<!-- 根据menujStore.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" :key="item.key">
|
|
12
|
+
<sub-menu
|
|
13
|
+
v-if="item.subMenu && item.subMenu.length > 0" :menu-item="item"
|
|
14
|
+
></sub-menu>
|
|
15
|
+
<el-menu-item v-else :index="item.key">{{ item.name }}</el-menu-item>
|
|
16
|
+
</template>
|
|
17
|
+
</el-menu>
|
|
18
|
+
</template>
|
|
19
|
+
<template #setting-content>
|
|
20
|
+
<!-- 根据projStore.projectList 渲染 -->
|
|
21
|
+
<el-dropdown @command="handleProjectCommand">
|
|
22
|
+
<span class="project-list">
|
|
23
|
+
{{ projName }}
|
|
24
|
+
<el-icon v-if="projectStore.projectList.length > 1" class="el-icon--right"><ArrowDown /></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
|
+
>{{ item.name }}</el-dropdown-item>
|
|
34
|
+
</el-dropdown-menu>
|
|
35
|
+
</template>
|
|
36
|
+
</el-dropdown>
|
|
37
|
+
</template>
|
|
38
|
+
<template #main-content>
|
|
39
|
+
<slot name="main-content"></slot>
|
|
40
|
+
</template>
|
|
41
|
+
</header-container>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script setup>
|
|
45
|
+
import { ref, onMounted, watch } from 'vue';
|
|
46
|
+
import { useRoute } from 'vue-router';
|
|
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
|
+
import { useMenuStore } from '$elpisStore/menu.js';
|
|
51
|
+
import { useProjectStore } from '$elpisStore/project.js';
|
|
52
|
+
|
|
53
|
+
const route = useRoute();
|
|
54
|
+
const menuStore = useMenuStore();
|
|
55
|
+
const projectStore = useProjectStore();
|
|
56
|
+
|
|
57
|
+
defineProps({
|
|
58
|
+
projName: String
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const emit = defineEmits([ 'menu-select' ]);
|
|
62
|
+
|
|
63
|
+
const activeKey = ref('');
|
|
64
|
+
const setActiveKey = () => {
|
|
65
|
+
const menuItem = menuStore.findMenuItem({
|
|
66
|
+
key: 'key',
|
|
67
|
+
value: route.query.key
|
|
68
|
+
});
|
|
69
|
+
activeKey.value = menuItem?.key;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// watch(() => route.query.key, () => {
|
|
73
|
+
// setActiveKey();
|
|
74
|
+
// })
|
|
75
|
+
// watch(() => menuStore.menuList, () => {
|
|
76
|
+
// setActiveKey();
|
|
77
|
+
// },{ deep: true })
|
|
78
|
+
// onMounted(() => {
|
|
79
|
+
// setActiveKey();
|
|
80
|
+
// })
|
|
81
|
+
|
|
82
|
+
watch(
|
|
83
|
+
[() => route.query.key, () => menuStore.menuList],
|
|
84
|
+
() => {
|
|
85
|
+
setActiveKey();
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
immediate: true,
|
|
89
|
+
deep: true
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const onMenuSelect = (menuKey) => {
|
|
94
|
+
const menuItem = menuStore.findMenuItem({
|
|
95
|
+
key: 'key',
|
|
96
|
+
value: menuKey
|
|
97
|
+
});
|
|
98
|
+
emit('menu-select', menuItem);
|
|
99
|
+
}
|
|
100
|
+
const handleProjectCommand = (event) => {
|
|
101
|
+
const projectItem = projectStore.projectList.find(item => item.key === event);
|
|
102
|
+
if(!projectItem || !projectItem.homePage) { return; }
|
|
103
|
+
const { origin } = window.location;
|
|
104
|
+
window.location.replace(`${origin}/view/dashboard${projectItem.homePage}`);
|
|
105
|
+
};
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<style lang="less" scoped>
|
|
109
|
+
:deep(.el-menu--horizontal.el-menu) {
|
|
110
|
+
border-bottom: 0;
|
|
111
|
+
}
|
|
112
|
+
:deep(.el-menu) {
|
|
113
|
+
background-color: unset;
|
|
114
|
+
}
|
|
115
|
+
.project-list {
|
|
116
|
+
margin-right: 20px;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
color: var(--el-color-primary);
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
outline: none;
|
|
122
|
+
}
|
|
123
|
+
</style>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<iframe :src="path" class="iframe"></iframe>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { ref, watch } from 'vue';
|
|
7
|
+
import { useRoute } from 'vue-router';
|
|
8
|
+
import { useMenuStore } from '$elpisStore/menu.js';
|
|
9
|
+
|
|
10
|
+
const route = useRoute();
|
|
11
|
+
const menuStore = useMenuStore();
|
|
12
|
+
|
|
13
|
+
const path = ref('');
|
|
14
|
+
const setPath = () => {
|
|
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, () => menuStore.menuList],
|
|
27
|
+
() => {
|
|
28
|
+
setPath();
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
immediate: true,
|
|
32
|
+
deep: true
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<style lang="less" scoped>
|
|
38
|
+
.iframe {
|
|
39
|
+
border: 0;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: 100%;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-card class="search-panel">
|
|
3
|
+
<schema-search-bar
|
|
4
|
+
:schema="searchSchema"
|
|
5
|
+
@load="onLoad"
|
|
6
|
+
@search="onSearch"
|
|
7
|
+
@reset="onReset"
|
|
8
|
+
></schema-search-bar>
|
|
9
|
+
</el-card>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
import { ref, inject } from 'vue';
|
|
14
|
+
import SchemaSearchBar from '$elpisWidgets/schema-search-bar/schema-search-bar.vue';
|
|
15
|
+
|
|
16
|
+
const { searchSchema } = inject('schemaViewData');
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits([ 'search' ]);
|
|
19
|
+
|
|
20
|
+
const onLoad = (searchValObj) => {
|
|
21
|
+
emit('search', searchValObj);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const onSearch = (searchValObj) => {
|
|
25
|
+
emit('search', searchValObj);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const onReset = (searchValObj) => {
|
|
29
|
+
emit('search', searchValObj);
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<style lang="less" scoped>
|
|
34
|
+
.search-panel {
|
|
35
|
+
margin: 10px 10px 0 10px;
|
|
36
|
+
}
|
|
37
|
+
:deep(.el-card__body) {
|
|
38
|
+
padding-bottom: 2px;
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-card class="table-panel">
|
|
3
|
+
<!-- operation-panel -->
|
|
4
|
+
<el-row
|
|
5
|
+
v-if="tableConfig?.headerButtons?.length > 0"
|
|
6
|
+
justify="end"
|
|
7
|
+
class="operation-panel"
|
|
8
|
+
>
|
|
9
|
+
<el-button
|
|
10
|
+
v-for="item in tableConfig?.headerButtons"
|
|
11
|
+
v-bind="item"
|
|
12
|
+
@click="operationHandler({ btnConfig: item })"
|
|
13
|
+
>{{ item.label }}</el-button>
|
|
14
|
+
</el-row>
|
|
15
|
+
<!-- schema-table(组件 widget) -->
|
|
16
|
+
<schema-table
|
|
17
|
+
ref="schemaTableRef"
|
|
18
|
+
:schema="tableSchema"
|
|
19
|
+
:api="api"
|
|
20
|
+
:apiParams="apiParams"
|
|
21
|
+
:buttons="tableConfig?.rowButtons ?? []"
|
|
22
|
+
@operate="operationHandler"
|
|
23
|
+
>
|
|
24
|
+
</schema-table>
|
|
25
|
+
</el-card>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup>
|
|
29
|
+
import { ref, inject } from 'vue';
|
|
30
|
+
import { ElMessageBox, ElNotification } from 'element-plus';
|
|
31
|
+
import $curl from '$elpisCommon/curl.js';
|
|
32
|
+
import SchemaTable from '$elpisWidgets/schema-table/schema-table.vue';
|
|
33
|
+
|
|
34
|
+
const emit = defineEmits([ 'operate' ]);
|
|
35
|
+
|
|
36
|
+
const { api, apiParams, tableSchema, tableConfig } = inject('schemaViewData')
|
|
37
|
+
|
|
38
|
+
const schemaTableRef = ref(null);
|
|
39
|
+
|
|
40
|
+
const EventHandlerMap = {
|
|
41
|
+
remove: removeData
|
|
42
|
+
}
|
|
43
|
+
const operationHandler = ({ btnConfig, rowData }) => {
|
|
44
|
+
const { eventKey } = btnConfig;
|
|
45
|
+
if(EventHandlerMap[eventKey]) {
|
|
46
|
+
EventHandlerMap[eventKey]({ btnConfig, rowData });
|
|
47
|
+
} else {
|
|
48
|
+
emit('operate', { btnConfig, rowData })
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function removeData ({ btnConfig, rowData }) {
|
|
52
|
+
const { eventOption } = btnConfig;
|
|
53
|
+
if(!eventOption?.params) { return; }
|
|
54
|
+
|
|
55
|
+
const { params } = eventOption;
|
|
56
|
+
|
|
57
|
+
const removeKey = Object.keys(params)[0];
|
|
58
|
+
|
|
59
|
+
let removeValue;
|
|
60
|
+
const removeValueList = params[removeKey].split('::');
|
|
61
|
+
if(removeValueList[0] === 'schema' && removeValueList[1]) {
|
|
62
|
+
removeValue = rowData[removeValueList[1]];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ElMessageBox.confirm(
|
|
66
|
+
`确认删除 ${removeKey} 为:${removeValue} 数据?`,
|
|
67
|
+
'Warning',
|
|
68
|
+
{
|
|
69
|
+
confirmButtonText: '确认',
|
|
70
|
+
cancelButtonText: '取消',
|
|
71
|
+
type: 'warning'
|
|
72
|
+
}
|
|
73
|
+
).then(async () => {
|
|
74
|
+
schemaTableRef.value.showLoading();
|
|
75
|
+
const res = await $curl({
|
|
76
|
+
method: 'delete',
|
|
77
|
+
url: api.value,
|
|
78
|
+
data: {
|
|
79
|
+
[removeKey]: removeValue
|
|
80
|
+
},
|
|
81
|
+
errorMessages: '删除失败'
|
|
82
|
+
});
|
|
83
|
+
schemaTableRef.value.hideLoading();
|
|
84
|
+
|
|
85
|
+
if(!res || !res.success || !res.data) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
ElNotification({
|
|
90
|
+
title: '删除成功',
|
|
91
|
+
message: '删除成功',
|
|
92
|
+
type: 'success'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await initTableData();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const initTableData = async() => {
|
|
100
|
+
await schemaTableRef.value.initData();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const loadTableData = async() => {
|
|
104
|
+
await schemaTableRef.value.loadTableData();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
defineExpose({ loadTableData })
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style lang="less" scoped>
|
|
111
|
+
.table-panel {
|
|
112
|
+
flex: 1;
|
|
113
|
+
margin: 10px;
|
|
114
|
+
|
|
115
|
+
.operation-panel {
|
|
116
|
+
margin-bottom: 10px;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
:deep(.el-card__body) {
|
|
120
|
+
height: 98%;
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
}
|
|
124
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import createForm from "./create-form/create-form.vue";
|
|
2
|
+
import editForm from "./edit-form/edit-form.vue";
|
|
3
|
+
import detailPanel from "./detail-panel/detail-panel.vue";
|
|
4
|
+
|
|
5
|
+
// 业务拓展 Component 配置
|
|
6
|
+
import BusinessComponentConfig from '$businessComponentConfig';
|
|
7
|
+
|
|
8
|
+
const ComponentConfig = {
|
|
9
|
+
createForm: {
|
|
10
|
+
component: createForm
|
|
11
|
+
},
|
|
12
|
+
editForm: {
|
|
13
|
+
component: editForm
|
|
14
|
+
},
|
|
15
|
+
detailPanel: {
|
|
16
|
+
component: detailPanel
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
...ComponentConfig,
|
|
22
|
+
...BusinessComponentConfig
|
|
23
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-drawer
|
|
3
|
+
v-model="isShow"
|
|
4
|
+
direction="rtl"
|
|
5
|
+
:destroy-on-close="true"
|
|
6
|
+
:size="550"
|
|
7
|
+
>
|
|
8
|
+
<template #header>
|
|
9
|
+
<h3>{{ title }}</h3>
|
|
10
|
+
</template>
|
|
11
|
+
<template #default>
|
|
12
|
+
<schema-form ref="schemaFormRef" v-loading="loading" :schema="components[name]?.schema"></schema-form>
|
|
13
|
+
</template>
|
|
14
|
+
<template #footer>
|
|
15
|
+
<el-button type="primary" @click="save">{{ saveBtnText }}</el-button>
|
|
16
|
+
</template>
|
|
17
|
+
</el-drawer>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup>
|
|
21
|
+
import { ref, inject } from 'vue';
|
|
22
|
+
import $curl from '$elpisCommon/curl.js';
|
|
23
|
+
import { ElNotification } from 'element-plus';
|
|
24
|
+
import SchemaForm from '$elpisWidgets/schema-form/schema-form.vue';
|
|
25
|
+
|
|
26
|
+
const { api, components } = inject('schemaViewData');
|
|
27
|
+
|
|
28
|
+
const emit = defineEmits([ 'command' ])
|
|
29
|
+
|
|
30
|
+
const name = ref('createForm');
|
|
31
|
+
|
|
32
|
+
const schemaFormRef = ref(null);
|
|
33
|
+
const isShow = ref(false);
|
|
34
|
+
const loading = ref(false);
|
|
35
|
+
const title = ref('');
|
|
36
|
+
const saveBtnText = ref('');
|
|
37
|
+
|
|
38
|
+
const show = () => {
|
|
39
|
+
const { config } = components.value[name.value];
|
|
40
|
+
|
|
41
|
+
title.value = config.title;
|
|
42
|
+
saveBtnText.value = config.saveBtnText;
|
|
43
|
+
|
|
44
|
+
isShow.value = true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const close = () => {
|
|
48
|
+
isShow.value = false;
|
|
49
|
+
}
|
|
50
|
+
const save = async() => {
|
|
51
|
+
if(loading.value) { return; }
|
|
52
|
+
|
|
53
|
+
// 校验表单
|
|
54
|
+
if(!schemaFormRef.value.validate()) { return; }
|
|
55
|
+
|
|
56
|
+
loading.value = true;
|
|
57
|
+
|
|
58
|
+
const res = await $curl({
|
|
59
|
+
method: 'post',
|
|
60
|
+
url: api.value,
|
|
61
|
+
data: {
|
|
62
|
+
...schemaFormRef.value.getValue(),
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
loading.value = false;
|
|
66
|
+
if(res?.success) {
|
|
67
|
+
ElNotification.success({
|
|
68
|
+
title: '创建成功',
|
|
69
|
+
message: '创建成功'
|
|
70
|
+
});
|
|
71
|
+
isShow.value = false;
|
|
72
|
+
} else {
|
|
73
|
+
ElNotification.error({
|
|
74
|
+
title: '创建失败',
|
|
75
|
+
message: '创建失败'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
close();
|
|
79
|
+
emit('command', {event: 'loadTableData'});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
defineExpose({ name, show })
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<style lang="less" scoped>
|
|
86
|
+
|
|
87
|
+
</style>
|