@dmqweb/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 +59 -0
- package/.vscode/settings.json +15 -0
- package/README.md +198 -0
- package/app/controller/base.js +41 -0
- package/app/controller/project.js +98 -0
- package/app/controller/view.js +24 -0
- package/app/extend/logger.js +43 -0
- package/app/middleware/api-params-verify.js +89 -0
- package/app/middleware/api-sign-verify.js +47 -0
- package/app/middleware/error-handler.js +41 -0
- package/app/middleware/project-handler.js +27 -0
- package/app/middleware.js +40 -0
- package/app/pages/asserts/custom.css +12 -0
- package/app/pages/boot.js +59 -0
- package/app/pages/common/curl.js +88 -0
- package/app/pages/common/util.js +3 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +40 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +141 -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 +39 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +146 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +24 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +118 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +177 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +157 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +150 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +113 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +35 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +134 -0
- package/app/pages/dashboard/dashboard.vue +127 -0
- package/app/pages/dashboard/entry.dashboard.js +46 -0
- package/app/pages/store/index.js +5 -0
- package/app/pages/store/menu.js +61 -0
- package/app/pages/store/project.js +13 -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 +144 -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 +166 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +144 -0
- package/app/pages/widgets/schema-form/form-item.config.js +24 -0
- package/app/pages/widgets/schema-form/schema-form.vue +144 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +57 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +77 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +51 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +58 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +138 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +254 -0
- package/app/pages/widgets/sider-container/sider-container.vue +32 -0
- package/app/router/business.js +15 -0
- package/app/router/project.js +10 -0
- package/app/router/view.js +11 -0
- package/app/router-schema/business.js +82 -0
- package/app/router-schema/project.js +40 -0
- package/app/service/base.js +13 -0
- package/app/service/project.js +55 -0
- package/app/view/entry.tpl +27 -0
- package/app/webpack/config/blank.js +3 -0
- package/app/webpack/config/webpack.base.js +269 -0
- package/app/webpack/config/webpack.dev.js +61 -0
- package/app/webpack/config/webpack.prod.js +149 -0
- package/app/webpack/dev.js +58 -0
- package/app/webpack/prod.js +21 -0
- package/config/config.default.js +3 -0
- package/elpis-core/env.js +22 -0
- package/elpis-core/index.js +99 -0
- package/elpis-core/loader/config.js +51 -0
- package/elpis-core/loader/controller.js +75 -0
- package/elpis-core/loader/extend.js +54 -0
- package/elpis-core/loader/middleware.js +69 -0
- package/elpis-core/loader/router-schema.js +50 -0
- package/elpis-core/loader/router.js +52 -0
- package/elpis-core/loader/service.js +74 -0
- package/index.js +29 -0
- package/jsconfig.json +16 -0
- package/logs/applocation.log +3 -0
- package/model/index.js +119 -0
- package/package.json +93 -0
- package/test/controller/project.test.js +216 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-drawer
|
|
3
|
+
v-model="isShow"
|
|
4
|
+
directory="rtl"
|
|
5
|
+
size="550"
|
|
6
|
+
destroy-on-close
|
|
7
|
+
>
|
|
8
|
+
<template #header>
|
|
9
|
+
<h2>
|
|
10
|
+
{{ title }}
|
|
11
|
+
</h2>
|
|
12
|
+
</template>
|
|
13
|
+
<template #default>
|
|
14
|
+
<schema-form
|
|
15
|
+
ref="schemaFormRef"
|
|
16
|
+
v-loading="loading"
|
|
17
|
+
:schema="currentSchema"
|
|
18
|
+
/>
|
|
19
|
+
</template>
|
|
20
|
+
<template #footer>
|
|
21
|
+
<el-button
|
|
22
|
+
type="primary"
|
|
23
|
+
@click="save"
|
|
24
|
+
>
|
|
25
|
+
{{ saveBtnText }}
|
|
26
|
+
</el-button>
|
|
27
|
+
</template>
|
|
28
|
+
</el-drawer>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import { inject, ref, computed } from 'vue';
|
|
33
|
+
import schemaForm from '$elpisWidgets/schema-form/schema-form.vue';
|
|
34
|
+
import $curl from '$elpisCommon/curl.js'
|
|
35
|
+
import { ElNotification } from 'element-plus';
|
|
36
|
+
const {
|
|
37
|
+
api,
|
|
38
|
+
components
|
|
39
|
+
} = inject('schemaViewData')
|
|
40
|
+
const emit = defineEmits(['command'])
|
|
41
|
+
const title = ref('')
|
|
42
|
+
const saveBtnText = ref('')
|
|
43
|
+
// 用来外部识别组件名称
|
|
44
|
+
const name = ref('createForm')
|
|
45
|
+
|
|
46
|
+
// 使用 computed 确保响应式更新
|
|
47
|
+
const currentSchema = computed(() => {
|
|
48
|
+
return components.value?.[name.value]?.schema || {}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const schemaFormRef = ref(null)
|
|
52
|
+
const loading = ref(false)
|
|
53
|
+
|
|
54
|
+
// 抽屉窗口是否显示
|
|
55
|
+
const isShow = ref(false)
|
|
56
|
+
// 显示表单
|
|
57
|
+
const show = () => {
|
|
58
|
+
const { config } = components.value[name.value]
|
|
59
|
+
|
|
60
|
+
title.value = config.title
|
|
61
|
+
saveBtnText.value = config.saveBtnText
|
|
62
|
+
|
|
63
|
+
isShow.value = true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 关闭表单
|
|
67
|
+
const close = () => {
|
|
68
|
+
isShow.value = false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 表单点击
|
|
72
|
+
const save = async () => {
|
|
73
|
+
// 防止重复提交
|
|
74
|
+
if(loading.value) return
|
|
75
|
+
|
|
76
|
+
// 校验表单
|
|
77
|
+
if(!schemaFormRef.value.validate()) {
|
|
78
|
+
console.log('表单校验失败')
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
loading.value = true
|
|
83
|
+
const res = await $curl({
|
|
84
|
+
method: 'post',
|
|
85
|
+
url: api.value,
|
|
86
|
+
data: {
|
|
87
|
+
...schemaFormRef.value.getValue()
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
loading.value = false
|
|
91
|
+
if(!res || !res.success) {
|
|
92
|
+
ElNotification({
|
|
93
|
+
title: '保存失败',
|
|
94
|
+
message: '保存失败',
|
|
95
|
+
type: 'error'
|
|
96
|
+
})
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
ElNotification({
|
|
100
|
+
title: '创建成功',
|
|
101
|
+
message: '创建成功',
|
|
102
|
+
type: 'success'
|
|
103
|
+
})
|
|
104
|
+
close()
|
|
105
|
+
emit('command', {
|
|
106
|
+
event: 'loadTableData'
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
defineExpose({
|
|
111
|
+
name,
|
|
112
|
+
show
|
|
113
|
+
})
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<style lang="less" scoped>
|
|
117
|
+
|
|
118
|
+
</style>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-drawer
|
|
3
|
+
v-model="isShow"
|
|
4
|
+
directory="rtl"
|
|
5
|
+
size="550"
|
|
6
|
+
destroy-on-close
|
|
7
|
+
>
|
|
8
|
+
<!-- -->
|
|
9
|
+
<template #header>
|
|
10
|
+
<h2>
|
|
11
|
+
{{ title }}
|
|
12
|
+
</h2>
|
|
13
|
+
</template>
|
|
14
|
+
<template #default>
|
|
15
|
+
<el-card
|
|
16
|
+
v-loading="loading"
|
|
17
|
+
shadow="always"
|
|
18
|
+
class="detail-panel"
|
|
19
|
+
>
|
|
20
|
+
<el-row
|
|
21
|
+
v-for="(item, key) in components[name]?.schema?.properties"
|
|
22
|
+
:key="key"
|
|
23
|
+
type="flex"
|
|
24
|
+
align="middle"
|
|
25
|
+
class="row-item"
|
|
26
|
+
>
|
|
27
|
+
<el-row class="item-label">
|
|
28
|
+
{{ item.label }}:
|
|
29
|
+
<br>
|
|
30
|
+
</el-row>
|
|
31
|
+
<el-row class="item-value">
|
|
32
|
+
{{ dotModel[key] }}
|
|
33
|
+
</el-row>
|
|
34
|
+
</el-row>
|
|
35
|
+
</el-card>
|
|
36
|
+
</template>
|
|
37
|
+
</el-drawer>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup>
|
|
41
|
+
import { ref, inject } from 'vue';
|
|
42
|
+
import $curl from '$elpisCommon/curl.js'
|
|
43
|
+
import { ElNotification } from 'element-plus';
|
|
44
|
+
|
|
45
|
+
const isShow = ref(false)
|
|
46
|
+
const loading = ref(false)
|
|
47
|
+
const name = ref('detailPanel')
|
|
48
|
+
const title = ref('')
|
|
49
|
+
const mainKey = ref('')
|
|
50
|
+
const mainValue = ref('')
|
|
51
|
+
const dotModel = ref({})
|
|
52
|
+
|
|
53
|
+
const {
|
|
54
|
+
api,
|
|
55
|
+
components
|
|
56
|
+
} = inject('schemaViewData')
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
// 显示表单
|
|
60
|
+
const show = (rowData) => {
|
|
61
|
+
const { config } = components.value[name.value]
|
|
62
|
+
|
|
63
|
+
title.value = config.title
|
|
64
|
+
mainKey.value = config.mainKey // 表单的主键
|
|
65
|
+
mainValue.value = rowData[mainKey.value] // 主键的值
|
|
66
|
+
dotModel.value = {}
|
|
67
|
+
|
|
68
|
+
isShow.value = true
|
|
69
|
+
|
|
70
|
+
fetchFormData()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
// 获取表单数据
|
|
75
|
+
const fetchFormData = async () => {
|
|
76
|
+
// 防止重复提交
|
|
77
|
+
if(loading.value) return
|
|
78
|
+
|
|
79
|
+
loading.value = true
|
|
80
|
+
const res = await $curl({
|
|
81
|
+
method: 'get',
|
|
82
|
+
url: api.value,
|
|
83
|
+
params: {
|
|
84
|
+
[mainKey.value]: mainValue.value
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
loading.value = false
|
|
88
|
+
|
|
89
|
+
if(!res || !res.success || !res.data) {
|
|
90
|
+
ElNotification({
|
|
91
|
+
title: '提示',
|
|
92
|
+
message: '获取数据失败',
|
|
93
|
+
type: 'error'
|
|
94
|
+
})
|
|
95
|
+
return // 添加return防止继续执行
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 确保res.data是对象类型,如果是数字则包装成对象
|
|
99
|
+
if (typeof res.data === 'object' && res.data !== null) {
|
|
100
|
+
dotModel.value = res.data
|
|
101
|
+
} else {
|
|
102
|
+
// 如果res.data不是对象,可能是直接返回的值,需要包装
|
|
103
|
+
console.warn('API返回的数据格式不正确,期望对象但收到:', typeof res.data, res.data)
|
|
104
|
+
dotModel.value = {}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
defineExpose({
|
|
109
|
+
show,
|
|
110
|
+
name
|
|
111
|
+
})
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<style lang="less" scoped>
|
|
115
|
+
.detail-panel {
|
|
116
|
+
background: linear-gradient(135deg, #f5f7fa 0%, #e4edf9 100%);
|
|
117
|
+
border-radius: 12px;
|
|
118
|
+
padding: 30px;
|
|
119
|
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
|
|
120
|
+
border: none;
|
|
121
|
+
|
|
122
|
+
.row-item {
|
|
123
|
+
height: auto;
|
|
124
|
+
min-height: 50px;
|
|
125
|
+
line-height: 1.6;
|
|
126
|
+
padding: 15px 0;
|
|
127
|
+
border-bottom: 1px solid #eaeef5;
|
|
128
|
+
|
|
129
|
+
&:last-child {
|
|
130
|
+
border-bottom: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.item-label {
|
|
134
|
+
margin-right: 20px;
|
|
135
|
+
width: 130px;
|
|
136
|
+
font-weight: 600;
|
|
137
|
+
color: #2d3748;
|
|
138
|
+
font-size: 15px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.item-value {
|
|
142
|
+
color: #4a5568;
|
|
143
|
+
font-size: 15px;
|
|
144
|
+
flex: 1;
|
|
145
|
+
word-break: break-word;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 响应式设计,适配不同屏幕尺寸
|
|
151
|
+
@media (max-width: 768px) {
|
|
152
|
+
.detail-panel {
|
|
153
|
+
padding: 20px;
|
|
154
|
+
|
|
155
|
+
.row-item {
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
align-items: flex-start;
|
|
158
|
+
|
|
159
|
+
.item-label {
|
|
160
|
+
width: 100%;
|
|
161
|
+
margin-right: 0;
|
|
162
|
+
margin-bottom: 5px;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 暗色主题适配
|
|
169
|
+
:deep(.el-card) {
|
|
170
|
+
background: transparent;
|
|
171
|
+
border: none;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
:deep(.el-drawer__body) {
|
|
175
|
+
padding: 20px;
|
|
176
|
+
}
|
|
177
|
+
</style>
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-drawer
|
|
3
|
+
v-model="isShow"
|
|
4
|
+
directory="rtl"
|
|
5
|
+
size="550"
|
|
6
|
+
destroy-on-close
|
|
7
|
+
>
|
|
8
|
+
<template #header>
|
|
9
|
+
<h2>
|
|
10
|
+
{{ title }}
|
|
11
|
+
</h2>
|
|
12
|
+
</template>
|
|
13
|
+
<template #default>
|
|
14
|
+
<schema-form
|
|
15
|
+
ref="schemaFormRef"
|
|
16
|
+
v-loading="loading"
|
|
17
|
+
:schema="currentSchema"
|
|
18
|
+
:model="dotModel"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
21
|
+
<template #footer>
|
|
22
|
+
<el-button
|
|
23
|
+
type="primary"
|
|
24
|
+
@click="save"
|
|
25
|
+
>
|
|
26
|
+
{{ saveBtnText }}
|
|
27
|
+
</el-button>
|
|
28
|
+
</template>
|
|
29
|
+
</el-drawer>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script setup>
|
|
33
|
+
import { ref, inject, computed } from 'vue';
|
|
34
|
+
import schemaForm from '$elpisWidgets/schema-form/schema-form.vue';
|
|
35
|
+
import $curl from '$elpisCommon/curl.js'
|
|
36
|
+
import { ElNotification } from 'element-plus';
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
api,
|
|
40
|
+
components
|
|
41
|
+
} = inject('schemaViewData')
|
|
42
|
+
|
|
43
|
+
const emit = defineEmits(['command'])
|
|
44
|
+
|
|
45
|
+
const name = ref('editForm')
|
|
46
|
+
|
|
47
|
+
const schemaFormRef = ref(null)
|
|
48
|
+
const isShow = ref(false)
|
|
49
|
+
const loading = ref(false)
|
|
50
|
+
const title = ref('')
|
|
51
|
+
const saveBtnText = ref('')
|
|
52
|
+
const mainKey = ref('')
|
|
53
|
+
const mainValue = ref('')
|
|
54
|
+
const dotModel = ref({})
|
|
55
|
+
|
|
56
|
+
// 使用 computed 确保响应式更新
|
|
57
|
+
const currentSchema = computed(() => {
|
|
58
|
+
return components.value?.[name.value]?.schema || {}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const show = (rowData) => {
|
|
62
|
+
const { config } = components.value[name.value]
|
|
63
|
+
|
|
64
|
+
title.value = config.title
|
|
65
|
+
saveBtnText.value = config.saveBtnText
|
|
66
|
+
mainKey.value = config.mainKey // 表单的主键
|
|
67
|
+
mainValue.value = rowData[mainKey.value] // 主键的值
|
|
68
|
+
dotModel.value = {}
|
|
69
|
+
|
|
70
|
+
isShow.value = true
|
|
71
|
+
|
|
72
|
+
fetchFormData()
|
|
73
|
+
}
|
|
74
|
+
const close = () => {
|
|
75
|
+
isShow.value = false
|
|
76
|
+
}
|
|
77
|
+
// 获取表单数据
|
|
78
|
+
const fetchFormData = async () => {
|
|
79
|
+
// 防止重复提交
|
|
80
|
+
if(loading.value) return
|
|
81
|
+
|
|
82
|
+
loading.value = true
|
|
83
|
+
const res = await $curl({
|
|
84
|
+
method: 'get',
|
|
85
|
+
url: api.value,
|
|
86
|
+
params: {
|
|
87
|
+
[mainKey.value]: mainValue.value
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
loading.value = false
|
|
91
|
+
|
|
92
|
+
if(!res || !res.success || !res.data) {
|
|
93
|
+
ElNotification({
|
|
94
|
+
title: '提示',
|
|
95
|
+
message: '获取数据失败',
|
|
96
|
+
type: 'error'
|
|
97
|
+
})
|
|
98
|
+
return // 添加return防止继续执行
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 确保res.data是对象类型,如果是数字则包装成对象
|
|
102
|
+
if (typeof res.data === 'object' && res.data !== null) {
|
|
103
|
+
dotModel.value = res.data
|
|
104
|
+
} else {
|
|
105
|
+
// 如果res.data不是对象,可能是直接返回的值,需要包装
|
|
106
|
+
console.warn('API返回的数据格式不正确,期望对象但收到:', typeof res.data, res.data)
|
|
107
|
+
dotModel.value = {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 表单点击
|
|
112
|
+
const save = async () => {
|
|
113
|
+
if(loading.value) return
|
|
114
|
+
// 校验表单
|
|
115
|
+
if(!schemaFormRef.value.validate()) {
|
|
116
|
+
console.log('表单校验失败')
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
loading.value = true
|
|
121
|
+
const res = await $curl({
|
|
122
|
+
method: 'put',
|
|
123
|
+
url: api.value,
|
|
124
|
+
data: {
|
|
125
|
+
[mainKey.value]: mainValue.value,
|
|
126
|
+
...schemaFormRef.value.getValue()
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
loading.value = false
|
|
130
|
+
if(!res || !res.success) {
|
|
131
|
+
ElNotification({
|
|
132
|
+
title: '提示',
|
|
133
|
+
message: '修改失败',
|
|
134
|
+
type: 'error'
|
|
135
|
+
})
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
ElNotification({
|
|
139
|
+
title: '提示',
|
|
140
|
+
message: '修改成功',
|
|
141
|
+
type: 'success'
|
|
142
|
+
})
|
|
143
|
+
close()
|
|
144
|
+
emit('command', {
|
|
145
|
+
event: 'loadTableData'
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
defineExpose({
|
|
150
|
+
name,
|
|
151
|
+
show
|
|
152
|
+
})
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<style lang="less" scoped>
|
|
156
|
+
|
|
157
|
+
</style>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ref, watch } from 'vue'
|
|
2
|
+
import { useRoute } from 'vue-router'
|
|
3
|
+
import { useMenuStore } from '$elpisStore/menu'
|
|
4
|
+
import { cloneDeep } from 'lodash';
|
|
5
|
+
export const useSchema = function() {
|
|
6
|
+
const route = useRoute()
|
|
7
|
+
const menuStore = useMenuStore()
|
|
8
|
+
|
|
9
|
+
const api = ref('')
|
|
10
|
+
const tableConfig = ref()
|
|
11
|
+
const tableSchema = ref({})
|
|
12
|
+
const searchConfig = ref()
|
|
13
|
+
const searchSchema = ref({})
|
|
14
|
+
const components = ref()
|
|
15
|
+
|
|
16
|
+
// 构造 schemaConfig 相关配置, 输送给 schemaView 解释
|
|
17
|
+
const buildData = function() {
|
|
18
|
+
const { key, sider_key } = route.query
|
|
19
|
+
|
|
20
|
+
// 添加菜单项存在性检查
|
|
21
|
+
if (!key && !sider_key) {
|
|
22
|
+
resetSchemaData()
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
if (!menuStore.menuList || menuStore.menuList.length === 0) {
|
|
26
|
+
// 初始化早期静默返回,等 watch 触发
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const mItem = menuStore.findMenuItem({
|
|
31
|
+
key: 'key',
|
|
32
|
+
value: key ?? sider_key
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// 不是 schema 模块时,清空并退出(不警告)
|
|
36
|
+
if (!mItem || mItem.moduleType !== 'schema') {
|
|
37
|
+
resetSchemaData()
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sConfig = mItem.schemaConfig
|
|
42
|
+
if (!sConfig) {
|
|
43
|
+
// 理论上继承后会有;异常也静默,避免刷屏
|
|
44
|
+
resetSchemaData()
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const configSchema = cloneDeep((sConfig.schema ?? {}))
|
|
49
|
+
api.value = sConfig.api ?? ''
|
|
50
|
+
tableConfig.value = undefined
|
|
51
|
+
tableSchema.value = {}
|
|
52
|
+
searchConfig.value = undefined
|
|
53
|
+
searchSchema.value = {}
|
|
54
|
+
components.value = {}
|
|
55
|
+
|
|
56
|
+
// 构造 tableSchema 和 tableConfig
|
|
57
|
+
tableSchema.value = buildDtoSchema(configSchema, 'table')
|
|
58
|
+
tableConfig.value = sConfig.tableConfig ?? {}
|
|
59
|
+
|
|
60
|
+
// 构造 searchSchema 和 searchConfig
|
|
61
|
+
const dtoSearchSchema = buildDtoSchema(configSchema, 'search')
|
|
62
|
+
// 循环遍历searchSchema的属性,将路由参数的值赋给searchSchema的属性的option的default属性
|
|
63
|
+
for(const key in dtoSearchSchema.properties) {
|
|
64
|
+
if(route.query[key] !== undefined) {
|
|
65
|
+
dtoSearchSchema.properties[key].option.default = route.query[key]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
searchSchema.value = dtoSearchSchema
|
|
69
|
+
searchConfig.value = sConfig.searchConfig ?? {}
|
|
70
|
+
|
|
71
|
+
// 构造 components = { comKey: { schema:{} config: {} }
|
|
72
|
+
const { componentConfig } = sConfig
|
|
73
|
+
if(componentConfig && Object.keys(componentConfig).length > 0){
|
|
74
|
+
const dtoComponents = {}
|
|
75
|
+
|
|
76
|
+
for(const comKey in componentConfig) {
|
|
77
|
+
dtoComponents[comKey] = {
|
|
78
|
+
schema: buildDtoSchema(configSchema, comKey),
|
|
79
|
+
config: componentConfig[comKey]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
components.value = dtoComponents
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 重置所有schema相关数据为默认值
|
|
87
|
+
const resetSchemaData = function() {
|
|
88
|
+
tableSchema.value = {}
|
|
89
|
+
tableConfig.value = {}
|
|
90
|
+
searchConfig.value = {}
|
|
91
|
+
searchSchema.value = {}
|
|
92
|
+
api.value = ''
|
|
93
|
+
components.value = {}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 通用构建 schema 方法 (清除掉无效字段)
|
|
97
|
+
const buildDtoSchema = (_schema, comName) => {
|
|
98
|
+
if (!_schema?.properties) return {}
|
|
99
|
+
|
|
100
|
+
const dtoSchema = {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 提取有效 schema 字段信息
|
|
106
|
+
// 循环遍历每个properties字段属性
|
|
107
|
+
for (const key in _schema.properties) {
|
|
108
|
+
// 拿取properties下的每个字段key
|
|
109
|
+
const props = _schema.properties[key]
|
|
110
|
+
// 筛选出需要显示的字段
|
|
111
|
+
if (props[`${comName}Option`]) {
|
|
112
|
+
let dtoProps = {}
|
|
113
|
+
// 循环遍历key下的每个属性
|
|
114
|
+
for (const pKey in props) {
|
|
115
|
+
// 过滤掉非option的属性, 筛选出存储基本属性先存放到dtoProps中,
|
|
116
|
+
if (pKey.indexOf('Option') < 0) {
|
|
117
|
+
dtoProps[pKey] = props[pKey]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// 将指定的comName的option属性存放到dtoProps中
|
|
121
|
+
dtoProps = Object.assign({},dtoProps, {option: props[`${comName}Option`]})
|
|
122
|
+
|
|
123
|
+
const { required } = _schema
|
|
124
|
+
if(required && required.find(item => item === key)){
|
|
125
|
+
dtoProps.option.required = true
|
|
126
|
+
}
|
|
127
|
+
// 将处理好的字段存放到dtoSchema的properties中
|
|
128
|
+
dtoSchema.properties[key] = dtoProps
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return dtoSchema
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
watch([
|
|
135
|
+
() => route.query.key,
|
|
136
|
+
() => route.query.sider_key,
|
|
137
|
+
() => menuStore.menuList
|
|
138
|
+
], () => {
|
|
139
|
+
buildData()
|
|
140
|
+
}, { deep: true, immediate: true})
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
api,
|
|
144
|
+
tableConfig,
|
|
145
|
+
tableSchema,
|
|
146
|
+
searchConfig,
|
|
147
|
+
searchSchema,
|
|
148
|
+
components
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-row
|
|
3
|
+
v-loading="loading"
|
|
4
|
+
class="schema-view"
|
|
5
|
+
>
|
|
6
|
+
<search-panel
|
|
7
|
+
v-if="searchSchema?.properties && Object.keys(searchSchema.properties).length > 0"
|
|
8
|
+
@search="onSearch"
|
|
9
|
+
/>
|
|
10
|
+
<table-panel
|
|
11
|
+
ref="tablePanelRef"
|
|
12
|
+
@operate="onTableOperate"
|
|
13
|
+
/>
|
|
14
|
+
<component
|
|
15
|
+
:is="ComponentConfig[key]?.component"
|
|
16
|
+
v-for="(component, key) in components"
|
|
17
|
+
:key="key"
|
|
18
|
+
ref="comListRef"
|
|
19
|
+
@command="handleComCommand"
|
|
20
|
+
/>
|
|
21
|
+
</el-row>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup>
|
|
25
|
+
import { ref, provide, computed } from 'vue';
|
|
26
|
+
import SearchPanel from './complex-view/search-panel/search-panel.vue'
|
|
27
|
+
import TablePanel from './complex-view/table-panel/table-panel.vue'
|
|
28
|
+
import ComponentConfig from './components/component-config';
|
|
29
|
+
import { useSchema } from './hook/schema';
|
|
30
|
+
|
|
31
|
+
const apiParams = ref({});
|
|
32
|
+
// 从menulist中获取各种配置
|
|
33
|
+
const {
|
|
34
|
+
api,
|
|
35
|
+
tableSchema,
|
|
36
|
+
tableConfig,
|
|
37
|
+
searchSchema,
|
|
38
|
+
searchConfig,
|
|
39
|
+
components
|
|
40
|
+
} = useSchema();
|
|
41
|
+
|
|
42
|
+
const loading = computed(() => {
|
|
43
|
+
return !tableSchema.value || Object.keys(tableSchema.value).length === 0
|
|
44
|
+
})
|
|
45
|
+
// 跨层级传递数据
|
|
46
|
+
provide('schemaViewData', {
|
|
47
|
+
api,
|
|
48
|
+
apiParams,
|
|
49
|
+
tableSchema,
|
|
50
|
+
tableConfig,
|
|
51
|
+
searchSchema,
|
|
52
|
+
searchConfig,
|
|
53
|
+
components
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const comListRef = ref([]);
|
|
57
|
+
const tablePanelRef = ref(null);
|
|
58
|
+
|
|
59
|
+
const onSearch = (searchValObj) => {
|
|
60
|
+
apiParams.value = searchValObj;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const eventHandlerMap = {
|
|
64
|
+
showComponent: showComponent
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 表格操作
|
|
68
|
+
const onTableOperate = ({ btnConfig, rowData }) => {
|
|
69
|
+
const { eventKey } = btnConfig;
|
|
70
|
+
if(eventHandlerMap[eventKey]){
|
|
71
|
+
eventHandlerMap[eventKey]({ btnConfig, rowData })
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 显示组件
|
|
76
|
+
function showComponent({ btnConfig, rowData }) {
|
|
77
|
+
const { comName } = btnConfig.eventOption;
|
|
78
|
+
if(!comName) {
|
|
79
|
+
console.error('请配置组件名称');
|
|
80
|
+
return
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const comRef = comListRef.value.find(item => item.name === comName);
|
|
84
|
+
if(!comRef && typeof comRef.show !== 'function') {
|
|
85
|
+
console.error('配置不正确');
|
|
86
|
+
return
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
comRef.show(rowData);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 处理来自子组件的命令
|
|
93
|
+
const handleComCommand = (data) => {
|
|
94
|
+
const { event } = data;
|
|
95
|
+
if(event === 'loadTableData'){
|
|
96
|
+
tablePanelRef.value.loadTableData();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<style lang="less" scoped>
|
|
102
|
+
.schema-view {
|
|
103
|
+
width: 100%;
|
|
104
|
+
height: 100%;
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
}
|
|
108
|
+
.loading-container {
|
|
109
|
+
flex: 1;
|
|
110
|
+
position: relative;
|
|
111
|
+
min-height: 400px;
|
|
112
|
+
}
|
|
113
|
+
</style>
|