vue2-client 1.14.97 → 1.15.1
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/.env.message +19 -19
- package/package.json +109 -109
- package/src/base-client/components/common/XFormTable/XFormTable.vue +2 -2
- package/src/base-client/components/common/XFormTable/demo.vue +86 -86
- package/src/base-client/components/common/XTable/XTable.vue +1587 -1571
- package/src/base-client/components/common/XUploadFilesView/index.vue +485 -485
- package/src/base-client/components/his/XQuestionnaire/XQuestionnaire.vue +10 -14
- package/src/base-client/components/his/XQuestionnaire/XQuestionnaireItem.vue +23 -47
- package/src/base-client/components/layout/XPageView/RenderRow.vue +88 -88
- package/src/base-client/components/layout/XPageView/XPageView.vue +223 -223
- package/src/base-client/components/layout/XPageView/XTab/XTab.vue +96 -96
- package/src/base-client/components/layout/XPageView/componentTypes.js +22 -22
- package/src/pages/WorkflowDetail/WorkFlowDemo2.vue +70 -1
- package/src/pages/WorkflowDetail/WorkflowPageDetail/LeaveMessage.vue +388 -388
- package/src/pages/XPageViewExample/index.vue +149 -149
- package/src/router/async/router.map.js +127 -127
- package/vue.config.js +220 -220
@@ -1,223 +1,223 @@
|
|
1
|
-
<script setup>
|
2
|
-
import { getConfigByName } from '@vue2-client/services/api/common'
|
3
|
-
import { reactive, ref, provide } from 'vue'
|
4
|
-
import RenderRow from './RenderRow'
|
5
|
-
import XErrorView from '@vue2-client/base-client/components/layout/XPageView/XErrorView.vue'
|
6
|
-
|
7
|
-
// 页面布局配置
|
8
|
-
const layout = ref(null)
|
9
|
-
|
10
|
-
// 加载状态:0-加载中,1-已加载,2-出现错误
|
11
|
-
const loadingStatus = {
|
12
|
-
LOADING: 0,
|
13
|
-
LOADED: 1,
|
14
|
-
ERROR: 2
|
15
|
-
}
|
16
|
-
const loaded = ref(loadingStatus.LOADING)
|
17
|
-
|
18
|
-
// 组件计数
|
19
|
-
const componentTotal = ref(0)
|
20
|
-
const registerComponentTotal = ref(0)
|
21
|
-
|
22
|
-
// 组件注册集合
|
23
|
-
const componentRefMap = reactive({})
|
24
|
-
|
25
|
-
// 数据上下文
|
26
|
-
const dataContext = reactive({
|
27
|
-
comps: componentRefMap,
|
28
|
-
func: {
|
29
|
-
getConfigByName
|
30
|
-
},
|
31
|
-
data: {}
|
32
|
-
})
|
33
|
-
|
34
|
-
/**
|
35
|
-
* 初始化构建页面组件
|
36
|
-
*/
|
37
|
-
const init = (params) => {
|
38
|
-
const { configName, configValue, serviceName } = params
|
39
|
-
|
40
|
-
// 重置状态
|
41
|
-
loaded.value = loadingStatus.LOADING
|
42
|
-
componentTotal.value = 0
|
43
|
-
registerComponentTotal.value = 0
|
44
|
-
Object.keys(componentRefMap).forEach(key => delete componentRefMap[key])
|
45
|
-
|
46
|
-
const processConfig = (config) => {
|
47
|
-
if (!config) {
|
48
|
-
loaded.value = loadingStatus.ERROR
|
49
|
-
return
|
50
|
-
}
|
51
|
-
|
52
|
-
layout.value = config
|
53
|
-
setComponentTotal(layout.value.children)
|
54
|
-
loaded.value = loadingStatus.LOADED
|
55
|
-
}
|
56
|
-
|
57
|
-
if (configName) {
|
58
|
-
getConfigByName(configName, serviceName, processConfig)
|
59
|
-
} else if (configValue) {
|
60
|
-
try {
|
61
|
-
const config = typeof configValue === 'string' ? JSON.parse(configValue) : configValue
|
62
|
-
processConfig(config)
|
63
|
-
} catch (error) {
|
64
|
-
console.error('配置解析错误:', error)
|
65
|
-
loaded.value = loadingStatus.ERROR
|
66
|
-
}
|
67
|
-
} else {
|
68
|
-
loaded.value = loadingStatus.ERROR
|
69
|
-
}
|
70
|
-
}
|
71
|
-
|
72
|
-
/**
|
73
|
-
* 注册组件
|
74
|
-
*/
|
75
|
-
const registerComponent = (name, vm) => {
|
76
|
-
if (!vm) return
|
77
|
-
|
78
|
-
componentRefMap[name] = vm
|
79
|
-
registerComponentTotal.value++
|
80
|
-
console.debug(`总组件数量:${componentTotal.value},已注册数量:${registerComponentTotal.value}`)
|
81
|
-
|
82
|
-
// 所有组件都已注册完成
|
83
|
-
if (registerComponentTotal.value >= componentTotal.value) {
|
84
|
-
registerEvents(layout.value.children)
|
85
|
-
// 初始化页面
|
86
|
-
initPage()
|
87
|
-
}
|
88
|
-
}
|
89
|
-
provide('registerComponent', registerComponent)
|
90
|
-
// 提供数据上下文给子组件
|
91
|
-
provide('dataContext', dataContext)
|
92
|
-
|
93
|
-
/**
|
94
|
-
* 初始化页面
|
95
|
-
*/
|
96
|
-
const initPage = () => {
|
97
|
-
if (!layout.value?.onMounted) return
|
98
|
-
|
99
|
-
console.info('开始初始化页面')
|
100
|
-
try {
|
101
|
-
// 使用 Function 构造函数替代 eval
|
102
|
-
// eslint-disable-next-line no-new-func
|
103
|
-
const onMountedFun = new Function('data', `return (${layout.value.onMounted})(data)`)
|
104
|
-
onMountedFun(dataContext)
|
105
|
-
} catch (error) {
|
106
|
-
console.error('页面初始化错误:', error)
|
107
|
-
}
|
108
|
-
}
|
109
|
-
|
110
|
-
/**
|
111
|
-
* 设置需要注册的组件总数
|
112
|
-
*/
|
113
|
-
const setComponentTotal = (children) => {
|
114
|
-
if (!children?.length) return
|
115
|
-
|
116
|
-
children.forEach((child) => {
|
117
|
-
// 如果不是row和text类型,追加组件数量
|
118
|
-
if (child.type !== 'row' && child.type !== 'text') {
|
119
|
-
componentTotal.value++
|
120
|
-
}
|
121
|
-
|
122
|
-
// 递归追加子组件数量
|
123
|
-
if (child.children?.length) {
|
124
|
-
setComponentTotal(child.children)
|
125
|
-
}
|
126
|
-
})
|
127
|
-
}
|
128
|
-
|
129
|
-
/**
|
130
|
-
* 注册组件事件
|
131
|
-
*/
|
132
|
-
const registerEvents = (children) => {
|
133
|
-
if (!children?.length) return
|
134
|
-
|
135
|
-
children.forEach((child) => {
|
136
|
-
// 如果有事件,注册它们
|
137
|
-
if (child.event) {
|
138
|
-
Object.entries(child.event).forEach(([eventName, handler]) => {
|
139
|
-
try {
|
140
|
-
// 使用 Function 构造函数替代 eval
|
141
|
-
// eslint-disable-next-line no-new-func
|
142
|
-
const eventHandler = new Function('...args', `return (${handler})(...args)`)
|
143
|
-
const componentInstance = componentRefMap[child.id]
|
144
|
-
|
145
|
-
if (componentInstance) {
|
146
|
-
componentInstance.$on(eventName, (...args) => {
|
147
|
-
eventHandler.call(componentInstance, ...args, dataContext)
|
148
|
-
})
|
149
|
-
}
|
150
|
-
} catch (error) {
|
151
|
-
console.error(`注册事件 ${eventName} 错误:`, error)
|
152
|
-
}
|
153
|
-
})
|
154
|
-
}
|
155
|
-
|
156
|
-
// 递归注册子组件的事件
|
157
|
-
if (child.children?.length) {
|
158
|
-
registerEvents(child.children)
|
159
|
-
}
|
160
|
-
})
|
161
|
-
}
|
162
|
-
|
163
|
-
// 导出组件接口
|
164
|
-
defineExpose({ init })
|
165
|
-
</script>
|
166
|
-
|
167
|
-
<template>
|
168
|
-
<a-row
|
169
|
-
type="flex"
|
170
|
-
:gutter="layout?.gutter || 0"
|
171
|
-
:align="layout?.align || 'top'"
|
172
|
-
:justify="layout?.justify || 'start'"
|
173
|
-
class="liuli-page">
|
174
|
-
<template v-if="loaded === loadingStatus.LOADED">
|
175
|
-
<template v-if="layout.children?.length">
|
176
|
-
<a-col v-for="row in layout.children" :key="`page-col-${row.id}`" :span="24">
|
177
|
-
<render-row :row="row"/>
|
178
|
-
</a-col>
|
179
|
-
</template>
|
180
|
-
<template v-else>
|
181
|
-
<div class="liuli-page__empty">
|
182
|
-
<a-empty description="无页面内容" />
|
183
|
-
</div>
|
184
|
-
</template>
|
185
|
-
</template>
|
186
|
-
|
187
|
-
<template v-else-if="loaded === loadingStatus.LOADING">
|
188
|
-
<div class="liuli-page__loading">
|
189
|
-
<a-spin tip="页面加载中..." />
|
190
|
-
</div>
|
191
|
-
</template>
|
192
|
-
|
193
|
-
<template v-else>
|
194
|
-
<div class="liuli-page__error">
|
195
|
-
<XErrorView />
|
196
|
-
</div>
|
197
|
-
</template>
|
198
|
-
</a-row>
|
199
|
-
</template>
|
200
|
-
|
201
|
-
<style scoped lang="less">
|
202
|
-
.liuli-page {
|
203
|
-
position: relative;
|
204
|
-
width: 100%;
|
205
|
-
height: 100%;
|
206
|
-
|
207
|
-
&__content {
|
208
|
-
min-height: 100px;
|
209
|
-
}
|
210
|
-
|
211
|
-
&__loading, &__error, &__empty {
|
212
|
-
display: flex;
|
213
|
-
width: 100%;
|
214
|
-
justify-content: center;
|
215
|
-
align-items: center;
|
216
|
-
min-height: 200px;
|
217
|
-
}
|
218
|
-
|
219
|
-
&__error {
|
220
|
-
width: 100%;
|
221
|
-
}
|
222
|
-
}
|
223
|
-
</style>
|
1
|
+
<script setup>
|
2
|
+
import { getConfigByName } from '@vue2-client/services/api/common'
|
3
|
+
import { reactive, ref, provide } from 'vue'
|
4
|
+
import RenderRow from './RenderRow'
|
5
|
+
import XErrorView from '@vue2-client/base-client/components/layout/XPageView/XErrorView.vue'
|
6
|
+
|
7
|
+
// 页面布局配置
|
8
|
+
const layout = ref(null)
|
9
|
+
|
10
|
+
// 加载状态:0-加载中,1-已加载,2-出现错误
|
11
|
+
const loadingStatus = {
|
12
|
+
LOADING: 0,
|
13
|
+
LOADED: 1,
|
14
|
+
ERROR: 2
|
15
|
+
}
|
16
|
+
const loaded = ref(loadingStatus.LOADING)
|
17
|
+
|
18
|
+
// 组件计数
|
19
|
+
const componentTotal = ref(0)
|
20
|
+
const registerComponentTotal = ref(0)
|
21
|
+
|
22
|
+
// 组件注册集合
|
23
|
+
const componentRefMap = reactive({})
|
24
|
+
|
25
|
+
// 数据上下文
|
26
|
+
const dataContext = reactive({
|
27
|
+
comps: componentRefMap,
|
28
|
+
func: {
|
29
|
+
getConfigByName
|
30
|
+
},
|
31
|
+
data: {}
|
32
|
+
})
|
33
|
+
|
34
|
+
/**
|
35
|
+
* 初始化构建页面组件
|
36
|
+
*/
|
37
|
+
const init = (params) => {
|
38
|
+
const { configName, configValue, serviceName } = params
|
39
|
+
|
40
|
+
// 重置状态
|
41
|
+
loaded.value = loadingStatus.LOADING
|
42
|
+
componentTotal.value = 0
|
43
|
+
registerComponentTotal.value = 0
|
44
|
+
Object.keys(componentRefMap).forEach(key => delete componentRefMap[key])
|
45
|
+
|
46
|
+
const processConfig = (config) => {
|
47
|
+
if (!config) {
|
48
|
+
loaded.value = loadingStatus.ERROR
|
49
|
+
return
|
50
|
+
}
|
51
|
+
|
52
|
+
layout.value = config
|
53
|
+
setComponentTotal(layout.value.children)
|
54
|
+
loaded.value = loadingStatus.LOADED
|
55
|
+
}
|
56
|
+
|
57
|
+
if (configName) {
|
58
|
+
getConfigByName(configName, serviceName, processConfig)
|
59
|
+
} else if (configValue) {
|
60
|
+
try {
|
61
|
+
const config = typeof configValue === 'string' ? JSON.parse(configValue) : configValue
|
62
|
+
processConfig(config)
|
63
|
+
} catch (error) {
|
64
|
+
console.error('配置解析错误:', error)
|
65
|
+
loaded.value = loadingStatus.ERROR
|
66
|
+
}
|
67
|
+
} else {
|
68
|
+
loaded.value = loadingStatus.ERROR
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* 注册组件
|
74
|
+
*/
|
75
|
+
const registerComponent = (name, vm) => {
|
76
|
+
if (!vm) return
|
77
|
+
|
78
|
+
componentRefMap[name] = vm
|
79
|
+
registerComponentTotal.value++
|
80
|
+
console.debug(`总组件数量:${componentTotal.value},已注册数量:${registerComponentTotal.value}`)
|
81
|
+
|
82
|
+
// 所有组件都已注册完成
|
83
|
+
if (registerComponentTotal.value >= componentTotal.value) {
|
84
|
+
registerEvents(layout.value.children)
|
85
|
+
// 初始化页面
|
86
|
+
initPage()
|
87
|
+
}
|
88
|
+
}
|
89
|
+
provide('registerComponent', registerComponent)
|
90
|
+
// 提供数据上下文给子组件
|
91
|
+
provide('dataContext', dataContext)
|
92
|
+
|
93
|
+
/**
|
94
|
+
* 初始化页面
|
95
|
+
*/
|
96
|
+
const initPage = () => {
|
97
|
+
if (!layout.value?.onMounted) return
|
98
|
+
|
99
|
+
console.info('开始初始化页面')
|
100
|
+
try {
|
101
|
+
// 使用 Function 构造函数替代 eval
|
102
|
+
// eslint-disable-next-line no-new-func
|
103
|
+
const onMountedFun = new Function('data', `return (${layout.value.onMounted})(data)`)
|
104
|
+
onMountedFun(dataContext)
|
105
|
+
} catch (error) {
|
106
|
+
console.error('页面初始化错误:', error)
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* 设置需要注册的组件总数
|
112
|
+
*/
|
113
|
+
const setComponentTotal = (children) => {
|
114
|
+
if (!children?.length) return
|
115
|
+
|
116
|
+
children.forEach((child) => {
|
117
|
+
// 如果不是row和text类型,追加组件数量
|
118
|
+
if (child.type !== 'row' && child.type !== 'text') {
|
119
|
+
componentTotal.value++
|
120
|
+
}
|
121
|
+
|
122
|
+
// 递归追加子组件数量
|
123
|
+
if (child.children?.length) {
|
124
|
+
setComponentTotal(child.children)
|
125
|
+
}
|
126
|
+
})
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* 注册组件事件
|
131
|
+
*/
|
132
|
+
const registerEvents = (children) => {
|
133
|
+
if (!children?.length) return
|
134
|
+
|
135
|
+
children.forEach((child) => {
|
136
|
+
// 如果有事件,注册它们
|
137
|
+
if (child.event) {
|
138
|
+
Object.entries(child.event).forEach(([eventName, handler]) => {
|
139
|
+
try {
|
140
|
+
// 使用 Function 构造函数替代 eval
|
141
|
+
// eslint-disable-next-line no-new-func
|
142
|
+
const eventHandler = new Function('...args', `return (${handler})(...args)`)
|
143
|
+
const componentInstance = componentRefMap[child.id]
|
144
|
+
|
145
|
+
if (componentInstance) {
|
146
|
+
componentInstance.$on(eventName, (...args) => {
|
147
|
+
eventHandler.call(componentInstance, ...args, dataContext)
|
148
|
+
})
|
149
|
+
}
|
150
|
+
} catch (error) {
|
151
|
+
console.error(`注册事件 ${eventName} 错误:`, error)
|
152
|
+
}
|
153
|
+
})
|
154
|
+
}
|
155
|
+
|
156
|
+
// 递归注册子组件的事件
|
157
|
+
if (child.children?.length) {
|
158
|
+
registerEvents(child.children)
|
159
|
+
}
|
160
|
+
})
|
161
|
+
}
|
162
|
+
|
163
|
+
// 导出组件接口
|
164
|
+
defineExpose({ init })
|
165
|
+
</script>
|
166
|
+
|
167
|
+
<template>
|
168
|
+
<a-row
|
169
|
+
type="flex"
|
170
|
+
:gutter="layout?.gutter || 0"
|
171
|
+
:align="layout?.align || 'top'"
|
172
|
+
:justify="layout?.justify || 'start'"
|
173
|
+
class="liuli-page">
|
174
|
+
<template v-if="loaded === loadingStatus.LOADED">
|
175
|
+
<template v-if="layout.children?.length">
|
176
|
+
<a-col v-for="row in layout.children" :key="`page-col-${row.id}`" :span="24">
|
177
|
+
<render-row :row="row"/>
|
178
|
+
</a-col>
|
179
|
+
</template>
|
180
|
+
<template v-else>
|
181
|
+
<div class="liuli-page__empty">
|
182
|
+
<a-empty description="无页面内容" />
|
183
|
+
</div>
|
184
|
+
</template>
|
185
|
+
</template>
|
186
|
+
|
187
|
+
<template v-else-if="loaded === loadingStatus.LOADING">
|
188
|
+
<div class="liuli-page__loading">
|
189
|
+
<a-spin tip="页面加载中..." />
|
190
|
+
</div>
|
191
|
+
</template>
|
192
|
+
|
193
|
+
<template v-else>
|
194
|
+
<div class="liuli-page__error">
|
195
|
+
<XErrorView />
|
196
|
+
</div>
|
197
|
+
</template>
|
198
|
+
</a-row>
|
199
|
+
</template>
|
200
|
+
|
201
|
+
<style scoped lang="less">
|
202
|
+
.liuli-page {
|
203
|
+
position: relative;
|
204
|
+
width: 100%;
|
205
|
+
height: 100%;
|
206
|
+
|
207
|
+
&__content {
|
208
|
+
min-height: 100px;
|
209
|
+
}
|
210
|
+
|
211
|
+
&__loading, &__error, &__empty {
|
212
|
+
display: flex;
|
213
|
+
width: 100%;
|
214
|
+
justify-content: center;
|
215
|
+
align-items: center;
|
216
|
+
min-height: 200px;
|
217
|
+
}
|
218
|
+
|
219
|
+
&__error {
|
220
|
+
width: 100%;
|
221
|
+
}
|
222
|
+
}
|
223
|
+
</style>
|
@@ -1,96 +1,96 @@
|
|
1
|
-
<template>
|
2
|
-
<a-card :bordered="false" :body-style="bodyStyle">
|
3
|
-
<a-tabs
|
4
|
-
:tabBarGutter="tabBarGutter"
|
5
|
-
:activeKey="activeKey"
|
6
|
-
@change="tabPaneChange"
|
7
|
-
:hideAdd="true"
|
8
|
-
:tabBarStyle="{ display: showTabBar ? 'block' : 'none' }"
|
9
|
-
>
|
10
|
-
<slot name="extraBeforeTabs"></slot>
|
11
|
-
<a-tab-pane
|
12
|
-
:forceRender="true"
|
13
|
-
v-for="(tab, index) in data"
|
14
|
-
:key="index"
|
15
|
-
:tab="tab.title"
|
16
|
-
>
|
17
|
-
<component
|
18
|
-
:is="resolveComponentType(tab.type)"
|
19
|
-
:key="`xTabPaneComp${index}`"
|
20
|
-
:ref="`tab_comp_${index}`"
|
21
|
-
v-bind="tab.props || {}"
|
22
|
-
/>
|
23
|
-
</a-tab-pane>
|
24
|
-
</a-tabs>
|
25
|
-
</a-card>
|
26
|
-
</template>
|
27
|
-
|
28
|
-
<script setup>
|
29
|
-
import { ref, onMounted } from 'vue'
|
30
|
-
import { resolveComponentType } from '../componentTypes'
|
31
|
-
|
32
|
-
const props = defineProps({
|
33
|
-
// 标签页数据
|
34
|
-
data: {
|
35
|
-
type: Array,
|
36
|
-
required: true
|
37
|
-
},
|
38
|
-
// 是否显示标签栏
|
39
|
-
showTabBar: {
|
40
|
-
type: Boolean,
|
41
|
-
default: true
|
42
|
-
},
|
43
|
-
// 标签页切换时的回调函数
|
44
|
-
onChange: {
|
45
|
-
type: [String, Function],
|
46
|
-
default: null
|
47
|
-
},
|
48
|
-
// Tab间距
|
49
|
-
tabBarGutter: {
|
50
|
-
type: Number,
|
51
|
-
default: 10
|
52
|
-
},
|
53
|
-
// 卡片样式
|
54
|
-
bodyStyle: {
|
55
|
-
type: Object,
|
56
|
-
default: () => ({})
|
57
|
-
},
|
58
|
-
// 默认激活的标签页
|
59
|
-
defaultActiveKey: {
|
60
|
-
type: [String, Number],
|
61
|
-
default: 0
|
62
|
-
}
|
63
|
-
})
|
64
|
-
|
65
|
-
// 激活的标签页
|
66
|
-
const activeKey = ref(0)
|
67
|
-
|
68
|
-
// 切换标签页
|
69
|
-
const tabPaneChange = (newKey) => {
|
70
|
-
if (activeKey.value === newKey) return
|
71
|
-
|
72
|
-
const oldKey = activeKey.value
|
73
|
-
activeKey.value = newKey
|
74
|
-
|
75
|
-
// 触发标签页切换事件
|
76
|
-
if (props.onChange) {
|
77
|
-
try {
|
78
|
-
if (props.onChange instanceof Function) {
|
79
|
-
props.onChange(oldKey, newKey, props.data[oldKey], props.data[newKey])
|
80
|
-
} else {
|
81
|
-
// 创建一个安全的函数执行环境
|
82
|
-
// eslint-disable-next-line no-new-func
|
83
|
-
const onChange = new Function('oldKey', 'newKey', 'oldTab', 'newTab', `return (${props.onChange})(oldKey, newKey, oldTab, newTab)`)
|
84
|
-
onChange(oldKey, newKey, props.data[oldKey], props.data[newKey])
|
85
|
-
}
|
86
|
-
} catch (error) {
|
87
|
-
console.error('执行标签页切换回调错误:', error)
|
88
|
-
}
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
// 生命周期
|
93
|
-
onMounted(() => {
|
94
|
-
activeKey.value = props.defaultActiveKey
|
95
|
-
})
|
96
|
-
</script>
|
1
|
+
<template>
|
2
|
+
<a-card :bordered="false" :body-style="bodyStyle">
|
3
|
+
<a-tabs
|
4
|
+
:tabBarGutter="tabBarGutter"
|
5
|
+
:activeKey="activeKey"
|
6
|
+
@change="tabPaneChange"
|
7
|
+
:hideAdd="true"
|
8
|
+
:tabBarStyle="{ display: showTabBar ? 'block' : 'none' }"
|
9
|
+
>
|
10
|
+
<slot name="extraBeforeTabs"></slot>
|
11
|
+
<a-tab-pane
|
12
|
+
:forceRender="true"
|
13
|
+
v-for="(tab, index) in data"
|
14
|
+
:key="index"
|
15
|
+
:tab="tab.title"
|
16
|
+
>
|
17
|
+
<component
|
18
|
+
:is="resolveComponentType(tab.type)"
|
19
|
+
:key="`xTabPaneComp${index}`"
|
20
|
+
:ref="`tab_comp_${index}`"
|
21
|
+
v-bind="tab.props || {}"
|
22
|
+
/>
|
23
|
+
</a-tab-pane>
|
24
|
+
</a-tabs>
|
25
|
+
</a-card>
|
26
|
+
</template>
|
27
|
+
|
28
|
+
<script setup>
|
29
|
+
import { ref, onMounted } from 'vue'
|
30
|
+
import { resolveComponentType } from '../componentTypes'
|
31
|
+
|
32
|
+
const props = defineProps({
|
33
|
+
// 标签页数据
|
34
|
+
data: {
|
35
|
+
type: Array,
|
36
|
+
required: true
|
37
|
+
},
|
38
|
+
// 是否显示标签栏
|
39
|
+
showTabBar: {
|
40
|
+
type: Boolean,
|
41
|
+
default: true
|
42
|
+
},
|
43
|
+
// 标签页切换时的回调函数
|
44
|
+
onChange: {
|
45
|
+
type: [String, Function],
|
46
|
+
default: null
|
47
|
+
},
|
48
|
+
// Tab间距
|
49
|
+
tabBarGutter: {
|
50
|
+
type: Number,
|
51
|
+
default: 10
|
52
|
+
},
|
53
|
+
// 卡片样式
|
54
|
+
bodyStyle: {
|
55
|
+
type: Object,
|
56
|
+
default: () => ({})
|
57
|
+
},
|
58
|
+
// 默认激活的标签页
|
59
|
+
defaultActiveKey: {
|
60
|
+
type: [String, Number],
|
61
|
+
default: 0
|
62
|
+
}
|
63
|
+
})
|
64
|
+
|
65
|
+
// 激活的标签页
|
66
|
+
const activeKey = ref(0)
|
67
|
+
|
68
|
+
// 切换标签页
|
69
|
+
const tabPaneChange = (newKey) => {
|
70
|
+
if (activeKey.value === newKey) return
|
71
|
+
|
72
|
+
const oldKey = activeKey.value
|
73
|
+
activeKey.value = newKey
|
74
|
+
|
75
|
+
// 触发标签页切换事件
|
76
|
+
if (props.onChange) {
|
77
|
+
try {
|
78
|
+
if (props.onChange instanceof Function) {
|
79
|
+
props.onChange(oldKey, newKey, props.data[oldKey], props.data[newKey])
|
80
|
+
} else {
|
81
|
+
// 创建一个安全的函数执行环境
|
82
|
+
// eslint-disable-next-line no-new-func
|
83
|
+
const onChange = new Function('oldKey', 'newKey', 'oldTab', 'newTab', `return (${props.onChange})(oldKey, newKey, oldTab, newTab)`)
|
84
|
+
onChange(oldKey, newKey, props.data[oldKey], props.data[newKey])
|
85
|
+
}
|
86
|
+
} catch (error) {
|
87
|
+
console.error('执行标签页切换回调错误:', error)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
// 生命周期
|
93
|
+
onMounted(() => {
|
94
|
+
activeKey.value = props.defaultActiveKey
|
95
|
+
})
|
96
|
+
</script>
|
@@ -1,22 +1,22 @@
|
|
1
|
-
// 支持的组件类型映射
|
2
|
-
export const components = {
|
3
|
-
XTab: () => import('@vue2-client/base-client/components/layout/XPageView/XTab'),
|
4
|
-
XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable'),
|
5
|
-
XAddNativeForm: () => import('@vue2-client/base-client/components/common/XAddNativeForm'),
|
6
|
-
XReportGrid: () => import('@vue2-client/base-client/components/common/XReportGrid/XReport.vue'),
|
7
|
-
XErrorView: () => import('@vue2-client/base-client/components/layout/XPageView/XErrorView'),
|
8
|
-
// 全局组件不需要导入,直接返回null,让Vue使用全局注册的组件
|
9
|
-
}
|
10
|
-
|
11
|
-
// 组件类型解析函数
|
12
|
-
export const resolveComponentType = (type) => {
|
13
|
-
if (!type) return components.XErrorView
|
14
|
-
|
15
|
-
// 如果是Ant Design Vue组件(以'a-'开头),直接返回原名称
|
16
|
-
if (type.startsWith('a-')) {
|
17
|
-
return type
|
18
|
-
}
|
19
|
-
|
20
|
-
// 否则从已注册的组件集合中查找
|
21
|
-
return components[type] || components.XErrorView
|
22
|
-
}
|
1
|
+
// 支持的组件类型映射
|
2
|
+
export const components = {
|
3
|
+
XTab: () => import('@vue2-client/base-client/components/layout/XPageView/XTab'),
|
4
|
+
XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable'),
|
5
|
+
XAddNativeForm: () => import('@vue2-client/base-client/components/common/XAddNativeForm'),
|
6
|
+
XReportGrid: () => import('@vue2-client/base-client/components/common/XReportGrid/XReport.vue'),
|
7
|
+
XErrorView: () => import('@vue2-client/base-client/components/layout/XPageView/XErrorView'),
|
8
|
+
// 全局组件不需要导入,直接返回null,让Vue使用全局注册的组件
|
9
|
+
}
|
10
|
+
|
11
|
+
// 组件类型解析函数
|
12
|
+
export const resolveComponentType = (type) => {
|
13
|
+
if (!type) return components.XErrorView
|
14
|
+
|
15
|
+
// 如果是Ant Design Vue组件(以'a-'开头),直接返回原名称
|
16
|
+
if (type.startsWith('a-')) {
|
17
|
+
return type
|
18
|
+
}
|
19
|
+
|
20
|
+
// 否则从已注册的组件集合中查找
|
21
|
+
return components[type] || components.XErrorView
|
22
|
+
}
|