vue2-client 1.18.23 → 1.18.25

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.
Files changed (92) hide show
  1. package/.eslintrc.js +90 -90
  2. package/Components.md +60 -60
  3. package/docs/index.md +30 -30
  4. package/index.js +31 -31
  5. package/jest-transform-stub.js +8 -8
  6. package/jest.setup.js +7 -7
  7. package/package.json +1 -1
  8. package/src/assets/img/querySlotDemo.svg +15 -15
  9. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  10. package/src/base-client/components/common/CitySelect/index.js +3 -3
  11. package/src/base-client/components/common/CitySelect/index.md +109 -109
  12. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  13. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  14. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  15. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  16. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  17. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  18. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  19. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  20. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  21. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  22. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  23. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  24. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  25. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  26. package/src/base-client/components/common/Tree/index.js +2 -2
  27. package/src/base-client/components/common/Upload/index.js +3 -3
  28. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  29. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  30. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  31. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  32. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  33. package/src/base-client/components/common/XDescriptions/XDescriptionsGroup.vue +358 -137
  34. package/src/base-client/components/common/XDescriptions/demo.vue +104 -17
  35. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  36. package/src/base-client/components/common/XDescriptions/index.md +322 -83
  37. package/src/base-client/components/common/XForm/index.md +178 -178
  38. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  39. package/src/base-client/components/common/XStepView/index.js +3 -3
  40. package/src/base-client/components/common/XStepView/index.md +31 -31
  41. package/src/base-client/components/common/XTable/index.md +255 -255
  42. package/src/base-client/components/his/HChart/HChart.vue +2 -0
  43. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  44. package/src/base-client/plugins/Config.js +19 -19
  45. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  46. package/src/components/Charts/Bar.vue +62 -62
  47. package/src/components/Charts/ChartCard.vue +134 -134
  48. package/src/components/Charts/Liquid.vue +67 -67
  49. package/src/components/Charts/MiniArea.vue +39 -39
  50. package/src/components/Charts/MiniBar.vue +39 -39
  51. package/src/components/Charts/MiniProgress.vue +75 -75
  52. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  53. package/src/components/Charts/Radar.vue +68 -68
  54. package/src/components/Charts/RankList.vue +77 -77
  55. package/src/components/Charts/TagCloud.vue +113 -113
  56. package/src/components/Charts/TransferBar.vue +64 -64
  57. package/src/components/Charts/Trend.vue +82 -82
  58. package/src/components/Charts/chart.less +12 -12
  59. package/src/components/Charts/smooth.area.less +13 -13
  60. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  61. package/src/components/NumberInfo/index.js +3 -3
  62. package/src/components/NumberInfo/index.less +54 -54
  63. package/src/components/NumberInfo/index.md +43 -43
  64. package/src/components/card/ChartCard.vue +79 -79
  65. package/src/components/chart/Bar.vue +60 -60
  66. package/src/components/chart/MiniArea.vue +67 -67
  67. package/src/components/chart/MiniBar.vue +59 -59
  68. package/src/components/chart/MiniProgress.vue +57 -57
  69. package/src/components/chart/Radar.vue +80 -80
  70. package/src/components/chart/RankingList.vue +60 -60
  71. package/src/components/chart/Trend.vue +79 -79
  72. package/src/components/chart/index.less +9 -9
  73. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  74. package/src/components/input/IInput.vue +66 -66
  75. package/src/components/menu/SideMenu.vue +75 -75
  76. package/src/components/menu/menu.js +273 -273
  77. package/src/components/tool/AStepItem.vue +60 -60
  78. package/src/layouts/CommonLayout.vue +56 -56
  79. package/src/layouts/header/HeaderNotice.vue +177 -177
  80. package/src/lib.js +1 -1
  81. package/src/mock/extend/index.js +84 -84
  82. package/src/mock/goods/index.js +108 -108
  83. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  84. package/src/pages/system/dictionary/index.vue +44 -44
  85. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  86. package/src/pages/system/monitor/operLog/index.vue +37 -37
  87. package/src/router/async/router.map.js +2 -1
  88. package/src/services/api/cas.js +79 -79
  89. package/src/store/modules/setting.js +119 -119
  90. package/src/utils/authority-utils.js +85 -85
  91. package/src/utils/errorCode.js +6 -6
  92. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
@@ -1,63 +1,177 @@
1
1
  <template>
2
2
  <a-row id="XDescriptionGroup" :gutter="24">
3
- <a-col :span="4" v-if="showLeftTab && realData.length">
4
- <a-tabs tab-position="left" v-model="activeTab" @change="scrollToGroup">
5
- <template v-for="(item,index) in realData">
6
- <a-tab-pane
7
- :tab="item.title"
8
- :key="index"
9
- v-if="item.title">
10
- </a-tab-pane>
11
- </template>
12
- </a-tabs>
13
- </a-col>
14
- <a-col :span="showLeftTab ? 20 : 24" ref="descriptionsGroupContext" class="descriptionsGroupContext">
15
- <div
16
- class="descriptions-item"
17
- :ref="`descriptions-item-${realDataIndex}`"
18
- :key="realDataIndex"
19
- v-for="(realDataItem, realDataIndex) in realData">
20
- <template v-if="!loadError">
21
- <!-- 带有子的详情 -->
22
- <template
23
- v-if="realDataItem.title && groups[realDataIndex]?.type ==='array'"
24
- >
25
- <div class="ant-descriptions-title">{{ realDataItem.title }}</div>
26
- <div class="descriptions-array-item">
27
- <a-descriptions
28
- v-for="(arrayItem,arrayIndex) in realDataItem.column"
29
- :column="isMobile ? 1 : column"
30
- size="small"
31
- :key="arrayIndex"
32
- :title="arrayItem.title">
33
- {{ arrayItem }}
34
- <template v-for="(item, index) in arrayItem.column">
35
- <!-- 大多数情况 循环下 空值省略不展示,todo 后期可能加配置处理 -->
3
+ <!-- left 模式:左侧导航 + 右侧内容 -->
4
+ <template v-if="tabMode === 'left'">
5
+ <a-col :span="4" v-if="realData.length">
6
+ <a-tabs tab-position="left" v-model="activeTab" @change="scrollToGroup">
7
+ <template v-for="(item,index) in realData">
8
+ <a-tab-pane
9
+ :tab="item.title"
10
+ :key="index"
11
+ v-if="item.title">
12
+ </a-tab-pane>
13
+ </template>
14
+ </a-tabs>
15
+ </a-col>
16
+ <a-col :span="realData.length ? 20 : 24" ref="descriptionsGroupContext" class="descriptionsGroupContext">
17
+ <div
18
+ class="descriptions-item"
19
+ :ref="`descriptions-item-${realDataIndex}`"
20
+ :key="realDataIndex"
21
+ v-for="(realDataItem, realDataIndex) in realData">
22
+ <!-- 渲染所有分组内容 -->
23
+ <template v-if="!loadError">
24
+ <!-- 带有子的详情 -->
25
+ <template
26
+ v-if="realDataItem.title && groups[realDataIndex]?.type ==='array'"
27
+ >
28
+ <div class="ant-descriptions-title">{{ realDataItem.title }}</div>
29
+ <div class="descriptions-array-item">
30
+ <a-descriptions
31
+ v-for="(arrayItem,arrayIndex) in realDataItem.column"
32
+ :column="isMobile ? 1 : column"
33
+ size="small"
34
+ :key="arrayIndex"
35
+ :title="arrayItem.title">
36
+ <template v-for="(item, index) in arrayItem.column">
37
+ <!-- 大多数情况 循环下 空值省略不展示,todo 后期可能加配置处理 -->
38
+ <a-descriptions-item
39
+ :key="index"
40
+ v-if="item.value"
41
+ :label="item.key">
42
+ {{ formatText(item.value) }}
43
+ </a-descriptions-item>
44
+ </template>
45
+ </a-descriptions>
46
+ </div>
47
+ </template>
48
+ <a-descriptions
49
+ v-else-if="realDataItem.title"
50
+ :column="isMobile ? 1 : column"
51
+ :title="realDataItem.title">
52
+ <a-descriptions-item
53
+ v-for="(item, index) in realDataItem.column"
54
+ :key="index"
55
+ :span="item.span || 1"
56
+ v-if="shouldShowField(item, data)">
57
+ <template #label>
58
+ {{ item.key }}
59
+ <span v-if="isFieldRequired(item, data)" style="color: red;"> *</span>
60
+ </template>
61
+ <span :style="getFieldStyle(item, data)">{{ formatFieldValue(item.value, item, data) || '--' }}</span>
62
+ </a-descriptions-item>
63
+ </a-descriptions>
64
+ </template>
65
+ </div>
66
+ </a-col>
67
+ </template>
68
+
69
+ <!-- top 模式:顶部横向 tab -->
70
+ <template v-else-if="tabMode === 'top'">
71
+ <a-col :span="24" v-if="realData.length">
72
+ <a-tabs v-model="activeTab" size="small" :tabBarStyle="{ margin: 0, padding: '0 8px' }">
73
+ <a-tab-pane v-for="(item,index) in realData" :key="index" :tab="item.title">
74
+ <div class="tab-content-scroll">
75
+ <!-- 只渲染当前激活的分组内容 -->
76
+ <template v-if="!loadError && realData[activeTab]">
77
+ <!-- 带有子的详情 -->
78
+ <template
79
+ v-if="realData[activeTab].title && groups[activeTab]?.type ==='array'"
80
+ >
81
+ <div class="ant-descriptions-title" v-if="tabMode !== 'top'">{{ realData[activeTab].title }}</div>
82
+ <div class="descriptions-array-item">
83
+ <a-descriptions
84
+ v-for="(arrayItem,arrayIndex) in realData[activeTab].column"
85
+ :column="isMobile ? 1 : column"
86
+ size="small"
87
+ :key="arrayIndex"
88
+ :title="arrayItem.title">
89
+ <template v-for="(fieldItem, fieldIndex) in arrayItem.column">
90
+ <a-descriptions-item
91
+ :key="fieldIndex"
92
+ v-if="fieldItem.value"
93
+ :label="fieldItem.key">
94
+ {{ formatText(fieldItem.value) }}
95
+ </a-descriptions-item>
96
+ </template>
97
+ </a-descriptions>
98
+ </div>
99
+ </template>
100
+ <a-descriptions
101
+ v-else-if="realData[activeTab].title"
102
+ :column="isMobile ? 1 : column"
103
+ :title="tabMode === 'top' ? undefined : realData[activeTab].title">
36
104
  <a-descriptions-item
37
- :key="index"
38
- v-if="item.value"
39
- :label="item.key">
40
- {{ formatText(item.value) }}
105
+ v-for="(fieldItem, fieldIndex) in realData[activeTab].column"
106
+ :key="fieldIndex"
107
+ :span="fieldItem.span || 1"
108
+ v-if="shouldShowField(fieldItem, data)">
109
+ <template #label>
110
+ {{ fieldItem.key }}
111
+ <span v-if="isFieldRequired(fieldItem, data)" style="color: red;"> *</span>
112
+ </template>
113
+ <span :style="getFieldStyle(fieldItem, fieldItem.value, data)">{{ formatFieldValue(fieldItem.value, fieldItem, data) || '--' }}</span>
41
114
  </a-descriptions-item>
42
- </template>
43
- </a-descriptions>
115
+ </a-descriptions>
116
+ </template>
44
117
  </div>
118
+ </a-tab-pane>
119
+ </a-tabs>
120
+ </a-col>
121
+ </template>
122
+
123
+ <!-- none 模式:无 tab,直接展示所有内容 -->
124
+ <template v-else>
125
+ <a-col :span="24" class="descriptionsGroupContext">
126
+ <div
127
+ class="descriptions-item"
128
+ :key="realDataIndex"
129
+ v-for="(realDataItem, realDataIndex) in realData">
130
+ <!-- 渲染所有分组内容 -->
131
+ <template v-if="!loadError">
132
+ <!-- 带有子的详情 -->
133
+ <template
134
+ v-if="realDataItem.title && groups[realDataIndex]?.type ==='array'"
135
+ >
136
+ <div class="ant-descriptions-title">{{ realDataItem.title }}</div>
137
+ <div class="descriptions-array-item">
138
+ <a-descriptions
139
+ v-for="(arrayItem,arrayIndex) in realDataItem.column"
140
+ :column="isMobile ? 1 : column"
141
+ size="small"
142
+ :key="arrayIndex"
143
+ :title="arrayItem.title">
144
+ <template v-for="(item, index) in arrayItem.column">
145
+ <a-descriptions-item
146
+ :key="index"
147
+ v-if="item.value"
148
+ :label="item.key">
149
+ {{ formatText(item.value) }}
150
+ </a-descriptions-item>
151
+ </template>
152
+ </a-descriptions>
153
+ </div>
154
+ </template>
155
+ <a-descriptions
156
+ v-else-if="realDataItem.title"
157
+ :column="isMobile ? 1 : column"
158
+ :title="realDataItem.title">
159
+ <a-descriptions-item
160
+ v-for="(item, index) in realDataItem.column"
161
+ :key="index"
162
+ :span="item.span || 1"
163
+ v-if="shouldShowField(item, data)">
164
+ <template #label>
165
+ {{ item.key }}
166
+ <span v-if="isFieldRequired(item, data)" style="color: red;"> *</span>
167
+ </template>
168
+ <span :style="getFieldStyle(item, item.value, data)">{{ formatFieldValue(item.value, item, data) || '--' }}</span>
169
+ </a-descriptions-item>
170
+ </a-descriptions>
45
171
  </template>
46
- <a-descriptions
47
- v-else-if="realDataItem.title"
48
- :column="isMobile ? 1 : column"
49
- :title="realDataItem.title">
50
- <a-descriptions-item
51
- v-for="(item, index) in realDataItem.column"
52
- :key="index"
53
- :span="item.span || 1"
54
- :label="item.key">
55
- <nobr>{{ item.value || '--' }}</nobr>
56
- </a-descriptions-item>
57
- </a-descriptions>
58
- </template>
59
- </div>
60
- </a-col>
172
+ </div>
173
+ </a-col>
174
+ </template>
61
175
  </a-row>
62
176
  </template>
63
177
  <script>
@@ -65,6 +179,7 @@
65
179
  import { mapState } from 'vuex'
66
180
  import { getRealKeyData } from '@vue2-client/utils/formatter'
67
181
  import { getConfigByName } from '@vue2-client/services/api/common'
182
+ import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
68
183
  import XAddNativeForm from '@vue2-client/base-client/components/common/XAddNativeForm/XAddNativeForm.vue'
69
184
 
70
185
  export default {
@@ -79,40 +194,6 @@ export default {
79
194
  required: true,
80
195
  default: undefined
81
196
  },
82
- // 展示列配置 格式 {title:[{key,name}]} {描述列表标题:[{字段名,字段中文名}]}
83
- // {
84
- // "groups": {
85
- // "表具信息": {
86
- // "f_gasbrand": "气表品牌"
87
- // },
88
- // "客户信息": {
89
- // "f_userinfo_code": "用户编号"
90
- // },
91
- // "设备信息":{
92
- // "type": "array",
93
- // "value": {
94
- // "f_devices_num": "设备数量",
95
- // "f_devices_type": "设备类型",
96
- // },
97
- // "key": "devices"
98
- // }
99
- // }
100
- // }
101
- // 数据模式结构
102
- // "groups": [{
103
- // "name": "客户信息",
104
- // "fields": [
105
- // {
106
- // "name": "备注",
107
- // "key": "f_comments",
108
- // "span": 3
109
- // },
110
- // {
111
- // "name": "地址",
112
- // "key": "f_address",
113
- // "span": 3
114
- // }]
115
- // }]
116
197
  configName: {
117
198
  type: String,
118
199
  required: true,
@@ -123,22 +204,7 @@ export default {
123
204
  type: String,
124
205
  default: process.env.VUE_APP_SYSTEM_NAME
125
206
  },
126
- // 每列显示数量
127
- column: {
128
- type: Number,
129
- default: 3
130
- },
131
- // 处理 key tuf_f_userinfo_id -> f_userinfo_id
132
- getRealData: {
133
- type: Boolean,
134
- default: false
135
- },
136
- // 是否展示左侧tab
137
- showLeftTab: {
138
- type: Boolean,
139
- default: false
140
- }
141
- },
207
+ },
142
208
  mounted () {
143
209
  },
144
210
  beforeDestroy () {
@@ -158,7 +224,11 @@ export default {
158
224
  realData: [],
159
225
  // 获取到的配置组
160
226
  groups: {},
161
- activeTab: 0
227
+ activeTab: 0,
228
+ // 从配置中获取的值
229
+ column: 3, // 默认值
230
+ getRealData: false, // 默认值
231
+ tabMode: 'left', // 默认值
162
232
  }
163
233
  },
164
234
  computed: {
@@ -169,12 +239,15 @@ export default {
169
239
  this.loading = true
170
240
  if (this.configName) {
171
241
  this.getConfig()
172
- this.$nextTick(() => {
173
- const formGroupContext = this.$refs.descriptionsGroupContext?.$el
174
- if (formGroupContext && formGroupContext.addEventListener) {
175
- formGroupContext.addEventListener('scroll', this.onScroll)
176
- }
177
- })
242
+ // 只在 left 模式启用滚动监听
243
+ if (this.tabMode === 'left') {
244
+ this.$nextTick(() => {
245
+ const formGroupContext = this.$refs.descriptionsGroupContext?.$el
246
+ if (formGroupContext && formGroupContext.addEventListener) {
247
+ formGroupContext.addEventListener('scroll', this.onScroll)
248
+ }
249
+ })
250
+ }
178
251
  } else {
179
252
  this.loading = false
180
253
  this.loadError = true
@@ -189,32 +262,13 @@ export default {
189
262
  getConfig () {
190
263
  getConfigByName(this.configName, this.serviceName, (res) => {
191
264
  if (res.groups) {
192
- const groups = Array.isArray(res.groups)
193
- ? res.groups
194
- : Object.entries(res.groups).map(([groupName, groupConfig]) => {
195
- if (groupConfig.type === 'array') {
196
- return {
197
- name: groupName,
198
- type: 'array',
199
- key: groupConfig.key,
200
- fields: Object.entries(groupConfig.value || {}).map(([k, v]) => ({
201
- key: k,
202
- name: v
203
- }))
204
- }
205
- }
206
-
207
- // 处理普通旧格式配置
208
- return {
209
- name: groupName,
210
- fields: Object.entries(groupConfig).map(([fieldKey, fieldValue]) => ({
211
- key: fieldKey,
212
- name: typeof fieldValue === 'string' ? fieldValue : fieldValue.name,
213
- ...(typeof fieldValue === 'object' ? fieldValue : {})
214
- }))
215
- }
216
- })
265
+ // 从配置中获取 column、getRealData 和 tabMode
266
+ this.column = res.column !== undefined ? res.column : 3
267
+ this.getRealData = res.getRealData !== undefined ? res.getRealData : false
268
+ this.tabMode = res.tabMode !== undefined ? res.tabMode : 'left'
217
269
 
270
+ // 解析分组配置
271
+ const groups = this.parseGroupsConfig(res.groups)
218
272
  this.groups = groups
219
273
  this.realData = groups.map(group => {
220
274
  const dataItem = { title: group.name }
@@ -233,7 +287,12 @@ export default {
233
287
  dataItem.column = group.fields.map(field => ({
234
288
  key: field.name,
235
289
  value: this.getRealKeyData(this.data, field.key),
236
- span: field.span
290
+ span: field.span,
291
+ // 存储字段函数配置
292
+ showIf: field.showIf,
293
+ styleFunc: field.styleFunc,
294
+ formatFunc: field.formatFunc,
295
+ requireFunc: field.requireFunc
237
296
  }))
238
297
  }
239
298
 
@@ -242,10 +301,142 @@ export default {
242
301
  }
243
302
  })
244
303
  },
304
+ /**
305
+ * 解析分组配置数据,支持新旧两种配置格式
306
+ * @param {Object|Array} groupsConfig - 分组配置对象或数组
307
+ * @returns {Array} 解析后的分组数组
308
+ *
309
+ * 配置格式说明:
310
+ *
311
+ * 旧模式 - 对象格式 (Object):
312
+ * {
313
+ * "表具信息": {
314
+ * "f_gasbrand": "气表品牌", // 简单字段配置
315
+ * "f_meter_type": "表具类型"
316
+ * },
317
+ * "设备信息": {
318
+ * "type": "array", // 数组类型标识
319
+ * "key": "devices", // type为array时必填:数据源key
320
+ * "value": { // type为array时的字段配置
321
+ * "f_devices_num": "设备数量",
322
+ * "f_devices_type": "设备类型"
323
+ * }
324
+ * },
325
+ * "客户信息": {
326
+ * "f_user_code": { // 字段可配置为对象
327
+ * "name": "用户编号",
328
+ * "span": 2 // 可选:列跨度
329
+ * },
330
+ * "f_user_name": "用户姓名" // 简单字段配置
331
+ * }
332
+ * }
333
+ *
334
+ * 新模式 - 数组格式 (Array):
335
+ * [{
336
+ * "name": "客户信息",
337
+ * "fields": [
338
+ * {
339
+ * "name": "用户编号",
340
+ * "key": "f_user_code",
341
+ * "span": 3
342
+ * }
343
+ * ]
344
+ * }, {
345
+ * "name": "设备信息",
346
+ * "type": "array", // 数组类型标识
347
+ * "key": "devices", // type为array时必填:数据源key
348
+ * "fields": [
349
+ * {
350
+ * "name": "设备数量",
351
+ * "key": "f_devices_num"
352
+ * },
353
+ * {
354
+ * "name": "设备类型",
355
+ * "key": "f_devices_type"
356
+ * }
357
+ * ]
358
+ * }]
359
+ */
360
+ parseGroupsConfig (groupsConfig) {
361
+ // 如果已经是数组格式,直接返回
362
+ if (Array.isArray(groupsConfig)) {
363
+ return groupsConfig
364
+ }
365
+
366
+ // 解析对象格式配置
367
+ return Object.entries(groupsConfig).map(([groupName, groupConfig]) => {
368
+ // 处理数组类型的配置
369
+ if (groupConfig.type === 'array') {
370
+ return {
371
+ name: groupName,
372
+ type: 'array',
373
+ key: groupConfig.key,
374
+ fields: Object.entries(groupConfig.value || {}).map(([k, v]) => ({
375
+ key: k,
376
+ name: v
377
+ }))
378
+ }
379
+ }
380
+
381
+ // 处理普通配置(支持新旧格式)
382
+ return {
383
+ name: groupName,
384
+ fields: Object.entries(groupConfig).map(([fieldKey, fieldValue]) => ({
385
+ key: fieldKey,
386
+ name: typeof fieldValue === 'string' ? fieldValue : fieldValue.name,
387
+ showIf: typeof fieldValue === 'object' ? fieldValue.showIf : undefined,
388
+ styleFunc: typeof fieldValue === 'object' ? fieldValue.styleFunc : undefined,
389
+ formatFunc: typeof fieldValue === 'object' ? fieldValue.formatFunc : undefined,
390
+ requireFunc: typeof fieldValue === 'object' ? fieldValue.requireFunc : undefined,
391
+ ...(typeof fieldValue === 'object' ? fieldValue : {})
392
+ }))
393
+ }
394
+ })
395
+ },
245
396
  // 文字格式化
246
397
  formatText (value) {
247
398
  return value ?? '--'
248
399
  },
400
+ // 执行字段展示函数
401
+ shouldShowField (field, data) {
402
+ if (!field.showIf) return true
403
+ try {
404
+ return executeStrFunctionByContext(this, field.showIf, [data, field.key])
405
+ } catch (error) {
406
+ console.warn('showIf 函数执行错误:', error)
407
+ return true
408
+ }
409
+ },
410
+ // 获取字段样式
411
+ getFieldStyle (field, data) {
412
+ if (!field.styleFunc) return {}
413
+ try {
414
+ return executeStrFunctionByContext(this, field.styleFunc, [data, field.key])
415
+ } catch (error) {
416
+ console.warn('styleFunc 函数执行错误:', error)
417
+ return {}
418
+ }
419
+ },
420
+ // 格式化字段值
421
+ formatFieldValue (value, field, data) {
422
+ if (!field.formatFunc) return value
423
+ try {
424
+ return executeStrFunctionByContext(this, field.formatFunc, [data, field.key])
425
+ } catch (error) {
426
+ console.warn('formatFunc 函数执行错误:', error)
427
+ return value
428
+ }
429
+ },
430
+ // 判断字段是否必填(显示红色星号)
431
+ isFieldRequired (field, data) {
432
+ if (!field.requireFunc) return false
433
+ try {
434
+ return executeStrFunctionByContext(this, field.requireFunc, [data, field.key])
435
+ } catch (error) {
436
+ console.warn('requireFunc 函数执行错误:', error)
437
+ return false
438
+ }
439
+ },
249
440
  getRealKeyData (data, key) {
250
441
  if (this.getRealData) {
251
442
  return getRealKeyData(data)[key] || ''
@@ -254,6 +445,9 @@ export default {
254
445
  }
255
446
  },
256
447
  onScroll () {
448
+ // 只在 left 模式启用滚动联动
449
+ if (this.tabMode !== 'left') return
450
+
257
451
  const formGroupContext = this.$refs.descriptionsGroupContext.$el
258
452
  let groupItems = []
259
453
  if (formGroupContext && formGroupContext.querySelectorAll) {
@@ -298,6 +492,16 @@ export default {
298
492
  color: @primary-color;
299
493
  }
300
494
 
495
+ :deep(.ant-descriptions-item-label) {
496
+ color: #737373; // 统一标签颜色
497
+ font-weight: 500;
498
+ }
499
+
500
+ :deep(.ant-descriptions-item-content) {
501
+ color: #262626; // 统一内容颜色
502
+ font-weight: 400;
503
+ }
504
+
301
505
  .descriptions-item {
302
506
  margin-bottom: 8px;
303
507
  }
@@ -310,5 +514,22 @@ export default {
310
514
  height: 100%;
311
515
  overflow-y: scroll;
312
516
  }
517
+
518
+ // top 模式样式
519
+ .tab-content-scroll {
520
+ overflow-y: auto;
521
+ padding: 8px 10px;
522
+
523
+ :deep(.ant-descriptions-item) {
524
+ padding-bottom: 12px;
525
+ }
526
+ }
527
+
528
+ // none 模式:移除分组间距
529
+ &:has(.tabMode-none) {
530
+ .descriptions-item {
531
+ margin-bottom: 4px;
532
+ }
533
+ }
313
534
  }
314
535
  </style>