@james-zhang/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/.tmp-check.js +47 -0
- package/README.md +287 -0
- package/app/controller/base.js +37 -0
- package/app/controller/project.js +76 -0
- package/app/controller/view.js +17 -0
- package/app/extend/logger.js +36 -0
- package/app/middleware/api-params-verify.js +78 -0
- package/app/middleware/api-sign-verify.js +31 -0
- package/app/middleware/error-handler.js +36 -0
- package/app/middleware/project-handler.js +26 -0
- package/app/middleware.js +69 -0
- package/app/pages/asserts/custom.css +10 -0
- package/app/pages/boot.js +44 -0
- package/app/pages/common/curl.js +76 -0
- package/app/pages/common/utils.js +2 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +19 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +131 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +29 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-pannel/search-pannel.vue +36 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-pannel/table-pannel.vue +125 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +19 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +119 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +131 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +149 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +126 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +111 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +28 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +138 -0
- package/app/pages/dashboard/dashboard.vue +121 -0
- package/app/pages/dashboard/entry.dashboard.js +52 -0
- package/app/pages/dashboard/router.js +3 -0
- package/app/pages/dashboard/todo/todo.vue +10 -0
- package/app/pages/store/index.js +3 -0
- package/app/pages/store/menu.js +71 -0
- package/app/pages/store/project.js +15 -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 +118 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +165 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +164 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +138 -0
- package/app/pages/widgets/schema-form/form-item-config.js +20 -0
- package/app/pages/widgets/schema-form/schema-form.vue +154 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +49 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +65 -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 +50 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +134 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +277 -0
- package/app/pages/widgets/sider-container/sider-container.vue +42 -0
- package/app/public/static/logo.png +0 -0
- package/app/public/static/normalize.css +239 -0
- package/app/router/project.js +7 -0
- package/app/router/view.js +7 -0
- package/app/router-schema/project.js +34 -0
- package/app/service/base.js +10 -0
- package/app/service/project.js +43 -0
- package/app/view/entry.tpl +31 -0
- package/app/webpack/config/webpack.base.js +224 -0
- package/app/webpack/config/webpack.dev.js +53 -0
- package/app/webpack/config/webpack.prod.js +104 -0
- package/app/webpack/dev.js +54 -0
- package/app/webpack/libs/blank.js +1 -0
- package/app/webpack/prod.js +17 -0
- package/config/config.default.js +3 -0
- package/docs/dashboard-model.js +150 -0
- package/elpis-core/env.js +21 -0
- package/elpis-core/index.js +82 -0
- package/elpis-core/loader/config.js +58 -0
- package/elpis-core/loader/controller.js +55 -0
- package/elpis-core/loader/extend.js +42 -0
- package/elpis-core/loader/middleware.js +50 -0
- package/elpis-core/loader/router-schema.js +28 -0
- package/elpis-core/loader/router.js +42 -0
- package/elpis-core/loader/service.js +51 -0
- package/index.js +37 -0
- package/logs/application.log +50 -0
- package/logs/application.log.2026-06-13 +159 -0
- package/model/index.js +119 -0
- package/package.json +88 -0
- package/scripts/generate-cover.py +93 -0
- package/scripts/generate-diagrams.js +80 -0
- package/test/controller/project.test.js +218 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行时异常错误处理,兜底所有异常
|
|
3
|
+
* @param {object} app koa 应用实例
|
|
4
|
+
* @returns 中间件函数
|
|
5
|
+
*/
|
|
6
|
+
module.exports=(app)=>{
|
|
7
|
+
return async (ctx,next)=>{
|
|
8
|
+
try{
|
|
9
|
+
await next()
|
|
10
|
+
}catch(err){
|
|
11
|
+
//异常处理
|
|
12
|
+
const {status,message,detail}=err
|
|
13
|
+
|
|
14
|
+
app.logger.info(JSON.stringify(err))
|
|
15
|
+
app.logger.error('[--exception--]:',err) //[--exception--]方便日志检索
|
|
16
|
+
app.logger.error('[--exception--]:',status,message,detail)
|
|
17
|
+
|
|
18
|
+
//用户访问一个目前不存在的页面
|
|
19
|
+
if(message && message.indexOf('template not found')>-1){
|
|
20
|
+
//页面重定向
|
|
21
|
+
ctx.status=302 //临时重定向,不用301因为用户可能会因为缓存即使页面上线依然重定向
|
|
22
|
+
ctx.redirect(`${app.options?.homePage || '/'}`)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const resBody={
|
|
27
|
+
code:50000,
|
|
28
|
+
message:"网络异常 请稍后重试",
|
|
29
|
+
success:false,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ctx.status=200
|
|
33
|
+
ctx.body=resBody
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 处理项目相关内容中间件
|
|
3
|
+
*/
|
|
4
|
+
module.exports=(app)=>{
|
|
5
|
+
return async (ctx,next)=>{
|
|
6
|
+
//只对业务API进行proj_key处理
|
|
7
|
+
if (!ctx.path.startsWith('/api/proj/')){
|
|
8
|
+
return await next();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//获取proj_key
|
|
12
|
+
const {proj_key:projKey} = ctx.request.headers;
|
|
13
|
+
if (!projKey){
|
|
14
|
+
ctx.status=200
|
|
15
|
+
ctx.body={
|
|
16
|
+
success:false,
|
|
17
|
+
code:446,
|
|
18
|
+
msg:'proj_key不能为空'
|
|
19
|
+
}
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ctx.projKey=projKey;
|
|
24
|
+
await next();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const path=require('path')
|
|
2
|
+
const http=require('http')
|
|
3
|
+
|
|
4
|
+
module.exports=(app)=>{
|
|
5
|
+
//开发环境代理
|
|
6
|
+
console.log('[dev-proxy] isLocal:',app.env.isLocal(),'_ENV:',JSON.stringify(process.env._ENV),'fn:',app.env.isLocal.toString())
|
|
7
|
+
if(app.env.isLocal()){
|
|
8
|
+
//主应用中添加代理中间件,将 /public/dist/dev/ 请求转发到 webpack dev server(端口9002)
|
|
9
|
+
app.use(async(ctx,next)=>{
|
|
10
|
+
if(ctx.path.startsWith('/public/dist/dev/')){
|
|
11
|
+
ctx.body=await new Promise((resolve,reject)=>{
|
|
12
|
+
const req=http.request({
|
|
13
|
+
hostname:'127.0.0.1',
|
|
14
|
+
port:9002,
|
|
15
|
+
path:ctx.path,
|
|
16
|
+
method:ctx.method,
|
|
17
|
+
headers:ctx.headers,
|
|
18
|
+
},(res)=>{
|
|
19
|
+
ctx.status=res.statusCode
|
|
20
|
+
Object.keys(res.headers).forEach(key=>{
|
|
21
|
+
ctx.set(key,res.headers[key])
|
|
22
|
+
})
|
|
23
|
+
let data=[]
|
|
24
|
+
res.on('data',chunk=>data.push(chunk))
|
|
25
|
+
res.on('end',()=>resolve(Buffer.concat(data)))
|
|
26
|
+
})
|
|
27
|
+
req.on('error',reject)
|
|
28
|
+
req.end()
|
|
29
|
+
})
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
await next()
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//静态文件服务
|
|
37
|
+
const koaStatic=require('koa-static')
|
|
38
|
+
app.use(koaStatic(path.resolve(process.cwd(),'./app/public')))
|
|
39
|
+
|
|
40
|
+
//模版渲染引擎
|
|
41
|
+
const koaNunjucks=require('koa-nunjucks-2')
|
|
42
|
+
app.use(koaNunjucks({
|
|
43
|
+
ext:'tpl',
|
|
44
|
+
path:path.resolve(process.cwd(),'./app/public'),
|
|
45
|
+
nunjucksConfig:{
|
|
46
|
+
trimBlocks:true,
|
|
47
|
+
noCache:app.env.isLocal(),//本地开发禁用模板缓存,确保模板改动即时生效
|
|
48
|
+
}
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
//引入ctx.body解析中间件
|
|
52
|
+
const bodyParser = require('koa-bodyparser')
|
|
53
|
+
app.use(bodyParser({
|
|
54
|
+
formLimit:'1000mb',
|
|
55
|
+
enableTypes:['json','form','text'],
|
|
56
|
+
methods:['POST','PUT','PATCH','DELETE'],
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
//引入(异常捕获)错误处理中间件
|
|
60
|
+
app.use(app.middlewares.errorHandler)
|
|
61
|
+
|
|
62
|
+
//引入API签名验证中间件
|
|
63
|
+
app.use(app.middlewares.apiSignVerify)
|
|
64
|
+
//引入API参数验证中间件
|
|
65
|
+
app.use(app.middlewares.apiParamsVerify)
|
|
66
|
+
//引入项目处理中间件
|
|
67
|
+
app.use(app.middlewares.projectHandler)
|
|
68
|
+
}
|
|
69
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createApp } from "vue";
|
|
2
|
+
|
|
3
|
+
//引入element-plus组件库
|
|
4
|
+
import ElementPlus from 'element-plus'
|
|
5
|
+
import 'element-plus/theme-chalk/index.css'
|
|
6
|
+
|
|
7
|
+
import './asserts/custom.css'
|
|
8
|
+
import pinia from '$elpisStore'
|
|
9
|
+
import {createRouter,createWebHistory} from 'vue-router'
|
|
10
|
+
/*
|
|
11
|
+
* Vue 页面主入口,用于启动vue
|
|
12
|
+
* @param pageComponent vue入口组件
|
|
13
|
+
* @param routes 路由列表
|
|
14
|
+
* @param libs 页面依赖的第三方包
|
|
15
|
+
*/
|
|
16
|
+
export default (pageComponent, options = {})=>{
|
|
17
|
+
const {routes, libs}=options
|
|
18
|
+
const app=createApp(pageComponent)
|
|
19
|
+
app.use(ElementPlus)
|
|
20
|
+
app.use(pinia)
|
|
21
|
+
|
|
22
|
+
//引入页面依赖的第三方包
|
|
23
|
+
if(libs && libs.length){
|
|
24
|
+
for(let i=0;i<libs.length;++i){
|
|
25
|
+
app.use(libs[i])
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if(routes && routes.length){
|
|
30
|
+
//页面路由
|
|
31
|
+
const router=createRouter({
|
|
32
|
+
history:createWebHistory(), //采用history模式
|
|
33
|
+
routes,
|
|
34
|
+
})
|
|
35
|
+
app.use(router)
|
|
36
|
+
router.isReady().then(()=>{
|
|
37
|
+
app.mount('#root')
|
|
38
|
+
})
|
|
39
|
+
} else {
|
|
40
|
+
//没有路由时直接挂载
|
|
41
|
+
app.mount('#root')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import Axios from "axios";
|
|
2
|
+
import md5 from "md5";
|
|
3
|
+
import { ElMessage } from "element-plus";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 前端封装curl命令
|
|
7
|
+
* @param options 请求参数
|
|
8
|
+
*/
|
|
9
|
+
const curl=({
|
|
10
|
+
url,
|
|
11
|
+
method = 'post',
|
|
12
|
+
headers = {},
|
|
13
|
+
query = {},
|
|
14
|
+
data = {},
|
|
15
|
+
responseType = 'json',
|
|
16
|
+
timeout = 60000,
|
|
17
|
+
errorMessage="网络异常",
|
|
18
|
+
})=>{
|
|
19
|
+
//接口签名处理(根据实际情况添加)
|
|
20
|
+
const signKey ='fai3ihihfi43go53gjo53g53'
|
|
21
|
+
const st = Date.now()
|
|
22
|
+
|
|
23
|
+
const dtoHeaders = {
|
|
24
|
+
...headers,
|
|
25
|
+
s_t:st,
|
|
26
|
+
s_sign: md5(`${signKey}_${st}`)
|
|
27
|
+
}
|
|
28
|
+
if(url.indexOf('/api/proj/')>-1 && window.projKey){
|
|
29
|
+
dtoHeaders.proj_key = window.projKey
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//构造请求参数(把参数转换为 axios 参数)
|
|
33
|
+
const ajaxSetting = {
|
|
34
|
+
url,
|
|
35
|
+
method,
|
|
36
|
+
params:query,
|
|
37
|
+
data,
|
|
38
|
+
responseType,
|
|
39
|
+
timeout,
|
|
40
|
+
headers:dtoHeaders
|
|
41
|
+
}
|
|
42
|
+
return Axios.request(ajaxSetting).then(res=>{
|
|
43
|
+
const resData = res.data
|
|
44
|
+
|
|
45
|
+
//后端API返回格式
|
|
46
|
+
const {success,message,code} = resData
|
|
47
|
+
if(!success){
|
|
48
|
+
if(code===442){
|
|
49
|
+
ElMessage.error("请求参数错误")
|
|
50
|
+
}else if(code===445){
|
|
51
|
+
ElMessage.error("请求不合法")
|
|
52
|
+
}else if(code===446){
|
|
53
|
+
ElMessage.error("缺少项目必要参数")
|
|
54
|
+
}else if(code === 50000){
|
|
55
|
+
ElMessage.error(message)
|
|
56
|
+
}else{
|
|
57
|
+
ElMessage.error(errorMessage)
|
|
58
|
+
}
|
|
59
|
+
return Promise.resolve({success,message,code})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//成功
|
|
63
|
+
const {data,metadata} = resData
|
|
64
|
+
return Promise.resolve({success,data,metaData:metadata})
|
|
65
|
+
}).catch(err=>{
|
|
66
|
+
const {message} = err
|
|
67
|
+
if(message.match(/timeout/)){
|
|
68
|
+
return Promise.resolve({
|
|
69
|
+
message:'请求超时',
|
|
70
|
+
code:504,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
return Promise.resolve (errorMessage)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
export default curl
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-sub-menu :index="menuItem.key">
|
|
3
|
+
<template #title>
|
|
4
|
+
{{ menuItem.name }}
|
|
5
|
+
</template>
|
|
6
|
+
<div v-for="item in menuItem.subMenu" :key="item.key">
|
|
7
|
+
<sub-menu v-if="item.subMenu && item.subMenu.length > 0" :menuItem="item" />
|
|
8
|
+
<el-menu-item v-else :index="item.key">{{ item.name }}</el-menu-item>
|
|
9
|
+
</div>
|
|
10
|
+
</el-sub-menu>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
const { menuItem } = defineProps(['menuItem'])
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<style lang="less" scoped>
|
|
18
|
+
|
|
19
|
+
</style>
|
|
@@ -0,0 +1,131 @@
|
|
|
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" :key="'sub-menu-'+item.key" />
|
|
13
|
+
<el-menu-item v-else :index="item.key" :key="'menu-item-'+item.key">{{ item.name }}</el-menu-item>
|
|
14
|
+
</template>
|
|
15
|
+
</el-menu>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<template #setting-content>
|
|
19
|
+
<!-- 根据projectStore.projectList渲染项目列表 -->
|
|
20
|
+
<el-dropdown @command="handleProjectCommand">
|
|
21
|
+
<span class="project-list">
|
|
22
|
+
{{ projName }}
|
|
23
|
+
<el-icon v-if="projectStore.projectList.length>1" class="el-icon-right">
|
|
24
|
+
<ArrowDown />
|
|
25
|
+
</el-icon>
|
|
26
|
+
</span>
|
|
27
|
+
<template v-if="projectStore.projectList.length > 1" #dropdown>
|
|
28
|
+
<el-dropdown-menu>
|
|
29
|
+
<el-dropdown-item
|
|
30
|
+
v-for="item in projectStore.projectList"
|
|
31
|
+
:key="item.key"
|
|
32
|
+
:command="item.key"
|
|
33
|
+
:disabled="item.name === projName"
|
|
34
|
+
>
|
|
35
|
+
{{ item.name }}
|
|
36
|
+
</el-dropdown-item>
|
|
37
|
+
</el-dropdown-menu>
|
|
38
|
+
</template>
|
|
39
|
+
</el-dropdown>
|
|
40
|
+
</template>
|
|
41
|
+
<template #main-content>
|
|
42
|
+
<slot name="main-content" ></slot>
|
|
43
|
+
</template>
|
|
44
|
+
</header-container>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup>
|
|
48
|
+
import { ref,watch,onMounted } from 'vue';
|
|
49
|
+
import { useRoute } from 'vue-router';
|
|
50
|
+
import { ArrowDown } from '@element-plus/icons-vue';
|
|
51
|
+
import HeaderContainer from '$elpisWidgets/header-container/header-container.vue';
|
|
52
|
+
import SubMenu from '$elpisPages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue';
|
|
53
|
+
import { useMenuStore } from '$elpisStore/menu';
|
|
54
|
+
import { useProjectStore } from '$elpisStore/project';
|
|
55
|
+
|
|
56
|
+
const route = useRoute();
|
|
57
|
+
const projectStore = useProjectStore();
|
|
58
|
+
const menuStore = useMenuStore();
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
const emit = defineEmits(['menu-select'])
|
|
62
|
+
|
|
63
|
+
defineProps({
|
|
64
|
+
projName: String
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const activeKey = ref('');
|
|
68
|
+
|
|
69
|
+
const setActiveKey=function(){
|
|
70
|
+
const menuItem=menuStore.findMenuItem({
|
|
71
|
+
key:'key',
|
|
72
|
+
value:route.query.key
|
|
73
|
+
})
|
|
74
|
+
activeKey.value=menuItem?.key
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
watch(()=>route.query.key,()=>{
|
|
78
|
+
setActiveKey()
|
|
79
|
+
})
|
|
80
|
+
watch(()=>menuStore.menuList,()=>{
|
|
81
|
+
setActiveKey()
|
|
82
|
+
},{deep:true})
|
|
83
|
+
onMounted(()=>{
|
|
84
|
+
setActiveKey()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
//菜单点击事件
|
|
90
|
+
const onMenuSelect=function(menuKey) {
|
|
91
|
+
const menuItem=menuStore.findMenuItem({
|
|
92
|
+
key:'key',
|
|
93
|
+
value:menuKey
|
|
94
|
+
})
|
|
95
|
+
if(!menuItem){
|
|
96
|
+
console.warn('未找到菜单项', menuKey)
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
emit('menu-select',menuItem)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const handleProjectCommand=function(event) {
|
|
103
|
+
const projectItem=projectStore.projectList.find(item=>{
|
|
104
|
+
return item.key === event
|
|
105
|
+
})
|
|
106
|
+
if(!projectItem || !projectItem.homePage){
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
const {host} = window.location
|
|
110
|
+
window.location.replace(`http://${host}/view/dashboard${projectItem.homePage}`)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<style lang="less" scoped>
|
|
119
|
+
.project-list {
|
|
120
|
+
margin-right: 20px;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
color:var(--el-color-primary);
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
outline: none;
|
|
126
|
+
}
|
|
127
|
+
:deep(.el-menu-horizontal .el-menu) {
|
|
128
|
+
border-bottom: 0;
|
|
129
|
+
}
|
|
130
|
+
</style>
|
|
131
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<iframe :src="path" class="iframe"></iframe>
|
|
3
|
+
</template>
|
|
4
|
+
<script setup>
|
|
5
|
+
import { ref, watchEffect } from 'vue';
|
|
6
|
+
import { useRoute } from 'vue-router';
|
|
7
|
+
import { useMenuStore } from '$elpisStore/menu';
|
|
8
|
+
|
|
9
|
+
const route = useRoute();
|
|
10
|
+
const menuStore = useMenuStore();
|
|
11
|
+
const path = ref('');
|
|
12
|
+
|
|
13
|
+
watchEffect(() => {
|
|
14
|
+
const { key, sider_key: siderKey } = route.query;
|
|
15
|
+
const menuItem = menuStore.findMenuItem({
|
|
16
|
+
key: 'key',
|
|
17
|
+
value: key ?? siderKey,
|
|
18
|
+
});
|
|
19
|
+
path.value = menuItem?.iframeConfig?.path || '';
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<style lang="less" scoped>
|
|
24
|
+
.iframe{
|
|
25
|
+
border:0;
|
|
26
|
+
width: 100%;
|
|
27
|
+
height: 100%;
|
|
28
|
+
}
|
|
29
|
+
</style>
|
package/app/pages/dashboard/complex-view/schema-view/complex-view/search-pannel/search-pannel.vue
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-card class="search-panel">
|
|
3
|
+
<SchemaSearchBar
|
|
4
|
+
:schema="SearchSchema"
|
|
5
|
+
@search="onSearch"
|
|
6
|
+
@reset="onReset"
|
|
7
|
+
@load="onLoad"
|
|
8
|
+
/>
|
|
9
|
+
</el-card>
|
|
10
|
+
</template>
|
|
11
|
+
<script setup>
|
|
12
|
+
import {inject,ref,computed} from 'vue'
|
|
13
|
+
import SchemaSearchBar from '$elpisWidgets/schema-search-bar/schema-search-bar.vue'
|
|
14
|
+
const schemaViewData=inject('schemaViewData',{})
|
|
15
|
+
const SearchSchema = computed(() => schemaViewData.SearchSchema?.value ?? {})
|
|
16
|
+
|
|
17
|
+
const emit=defineEmits(['search','reset','load'])
|
|
18
|
+
|
|
19
|
+
const onSearch=(SearchValObj)=>{
|
|
20
|
+
emit('search',SearchValObj)
|
|
21
|
+
}
|
|
22
|
+
const onReset=()=>{
|
|
23
|
+
emit('reset')
|
|
24
|
+
}
|
|
25
|
+
const onLoad=(SearchValObj)=>{
|
|
26
|
+
emit('load',SearchValObj)
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
<style lang="less" scoped>
|
|
30
|
+
.search-panel{
|
|
31
|
+
margin:10px;
|
|
32
|
+
}
|
|
33
|
+
:deep(.el-card__body){
|
|
34
|
+
padding-bottom: 2px;
|
|
35
|
+
}
|
|
36
|
+
</style>
|
package/app/pages/dashboard/complex-view/schema-view/complex-view/table-pannel/table-pannel.vue
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
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
|
+
:key="item.eventKey"
|
|
13
|
+
@click="operationHandler({btnConfig:item})"
|
|
14
|
+
>
|
|
15
|
+
{{item.label}}
|
|
16
|
+
</el-button>
|
|
17
|
+
</el-row>
|
|
18
|
+
|
|
19
|
+
<!-- schema-table(组件widget) -->
|
|
20
|
+
<schema-table
|
|
21
|
+
ref="schemaTableRef"
|
|
22
|
+
:schema="tableSchema"
|
|
23
|
+
:api="api"
|
|
24
|
+
:apiParams="apiParamsRef"
|
|
25
|
+
:buttons="tableConfig?.rowButtons ?? []"
|
|
26
|
+
@operate="operationHandler"
|
|
27
|
+
/>
|
|
28
|
+
</el-card>
|
|
29
|
+
</template>
|
|
30
|
+
<script setup>
|
|
31
|
+
import { ref, onMounted, watch,inject,computed } from 'vue'
|
|
32
|
+
import {ElMessageBox,ElNotification} from 'element-plus'
|
|
33
|
+
import $curl from '$elpisCommon/curl.js'
|
|
34
|
+
import SchemaTable from '$elpisWidgets/schema-table/schema-table.vue'
|
|
35
|
+
|
|
36
|
+
const emit = defineEmits(['operate'])
|
|
37
|
+
|
|
38
|
+
const schemaViewData=inject('schemaViewData',{})
|
|
39
|
+
|
|
40
|
+
const api = computed(() => schemaViewData.api?.value ?? '')
|
|
41
|
+
const apiParamsRef = schemaViewData.apiParams ?? ref({})
|
|
42
|
+
const tableSchema = computed(() => schemaViewData.tableSchema?.value ?? {})
|
|
43
|
+
const tableConfig = computed(() => schemaViewData.tableConfig?.value)
|
|
44
|
+
|
|
45
|
+
const schemaTableRef = ref(null)
|
|
46
|
+
|
|
47
|
+
const EventHandlerMap={
|
|
48
|
+
remove:removeData,
|
|
49
|
+
}
|
|
50
|
+
const operationHandler=({btnConfig,rowData})=>{
|
|
51
|
+
const {eventKey}=btnConfig
|
|
52
|
+
if(EventHandlerMap[eventKey]){
|
|
53
|
+
EventHandlerMap[eventKey]({btnConfig,rowData})
|
|
54
|
+
}else{
|
|
55
|
+
emit('operate',{btnConfig,rowData})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function removeData({btnConfig,rowData}){
|
|
59
|
+
const {eventOption}=btnConfig
|
|
60
|
+
if(!eventOption?.params){
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
const {params}=eventOption
|
|
64
|
+
const removeKey=Object.keys(params)[0]
|
|
65
|
+
|
|
66
|
+
let removeValue
|
|
67
|
+
const removeValueList = params[removeKey].split('::')
|
|
68
|
+
if(removeValueList[0]==='schema' && removeValueList[1]){
|
|
69
|
+
removeValue=rowData[removeValueList[1]]
|
|
70
|
+
}
|
|
71
|
+
ElMessageBox.confirm(`确认删除${removeKey} 为:${removeValue} 数据吗?`, '提示', {
|
|
72
|
+
confirmButtonText: '确定',
|
|
73
|
+
cancelButtonText: '取消',
|
|
74
|
+
type: 'warning',
|
|
75
|
+
}).then(async () => {
|
|
76
|
+
schemaTableRef.value.showLoading()
|
|
77
|
+
const res=await $curl({
|
|
78
|
+
url:api.value,
|
|
79
|
+
method:'delete',
|
|
80
|
+
data:{
|
|
81
|
+
[removeKey]:removeValue,
|
|
82
|
+
},
|
|
83
|
+
errorMessages:'删除失败'
|
|
84
|
+
})
|
|
85
|
+
schemaTableRef.value.hideLoading()
|
|
86
|
+
|
|
87
|
+
if(!res || !res.data || !res.success){
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
ElNotification({
|
|
91
|
+
title:'删除成功',
|
|
92
|
+
message:'删除成功',
|
|
93
|
+
type:'success',
|
|
94
|
+
})
|
|
95
|
+
await initTableData()
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const initTableData=async ()=>{
|
|
101
|
+
await schemaTableRef.value.initData()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const loadTableData=async ()=>{
|
|
105
|
+
await schemaTableRef.value.loadTableData()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
defineExpose({
|
|
109
|
+
loadTableData,
|
|
110
|
+
})
|
|
111
|
+
</script>
|
|
112
|
+
<style lang="less" scoped>
|
|
113
|
+
.table-panel{
|
|
114
|
+
height: 100%;
|
|
115
|
+
width: 100%;
|
|
116
|
+
.operation-panel{
|
|
117
|
+
margin-bottom: 10px;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
:deep(.el-card__body){
|
|
121
|
+
height: 98%;
|
|
122
|
+
display: flex;
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
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 '$businessSchemaViewComponentConfig'
|
|
7
|
+
|
|
8
|
+
const ComponentConfig={
|
|
9
|
+
createForm:{
|
|
10
|
+
component:CreateForm
|
|
11
|
+
},
|
|
12
|
+
editForm:{
|
|
13
|
+
component:EditForm
|
|
14
|
+
},
|
|
15
|
+
detailPanel:{
|
|
16
|
+
component:DetailPanel
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export default {...ComponentConfig,...BusinessComponentConfig}
|