vue2-client 1.16.47 → 1.16.49

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 (44) hide show
  1. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145434.vue +641 -0
  2. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145453.vue +641 -0
  3. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145610.vue +647 -0
  4. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145629.vue +647 -0
  5. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145901.vue +645 -0
  6. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145907.vue +651 -0
  7. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926145920.vue +651 -0
  8. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926150047.vue +651 -0
  9. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926151820.vue +646 -0
  10. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926151827.vue +646 -0
  11. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152115.vue +646 -0
  12. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152212.vue +653 -0
  13. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152215.vue +653 -0
  14. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152337.vue +657 -0
  15. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152341.vue +657 -0
  16. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152826.vue +657 -0
  17. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926152828.vue +646 -0
  18. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153121.vue +654 -0
  19. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153242.vue +654 -0
  20. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153318.vue +646 -0
  21. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153415.vue +646 -0
  22. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153435.vue +655 -0
  23. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153606.vue +655 -0
  24. package/.history/src/base-client/components/common/XDataCard/XDataCard_20250926153653.vue +655 -0
  25. package/package.json +112 -112
  26. package/src/base-client/components/common/HIS/HButtons/HButtons.vue +47 -45
  27. package/src/base-client/components/common/HIS/HFormGroup/HFormGroup.vue +120 -120
  28. package/src/base-client/components/common/HIS/HFormGroup/index.js +3 -3
  29. package/src/base-client/components/common/HIS/HFormTable/HFormTable.vue +257 -256
  30. package/src/base-client/components/common/HIS/demo.vue +61 -61
  31. package/src/base-client/components/common/XCollapse/XCollapse.vue +461 -461
  32. package/src/base-client/components/common/XDataCard/XDataCard.vue +44 -18
  33. package/src/base-client/components/common/XDataCard/test.vue +367 -0
  34. package/src/base-client/components/common/XInput/XInput.vue +147 -147
  35. package/src/base-client/components/common/XReportGrid/XReportTrGroup.vue +824 -824
  36. package/src/base-client/components/common/XTable/XTable.vue +1610 -1610
  37. package/src/base-client/components/common/XTimeline/XTimeline.vue +454 -454
  38. package/src/base-client/components/his/XHDescriptions/XHDescriptions.vue +6 -1
  39. package/src/base-client/components/his/XHisEditor/XHisEditor.vue +705 -705
  40. package/src/base-client/components/his/threeTestOrders/editor.vue +113 -113
  41. package/src/pages/WorkflowDetail/WorkFlowDemo3.vue +225 -203
  42. package/src/pages/userInfoDetailManage/ExceptionRecordQuery/index.vue +45 -45
  43. package/src/router/async/router.map.js +129 -129
  44. package/src/services/api/common.js +2 -0
@@ -1,824 +1,824 @@
1
- <template>
2
- <a-row id="has_row" type="flex" :gutter="gutter" :style="isWidget ? {margin: '0px'} : {'margin-bottom': '.5rem'}">
3
- <template v-for="(cell, cellIndex) in columns">
4
- <a-col
5
- name="trGroup"
6
- v-if="Array.isArray(cell) || !cell.dontShowRow"
7
- :key="cellIndex"
8
- :ref="`trGroup_${ cell.slotRef || cellIndex}`"
9
- @hook:mounted="(h)=>applyAllStyles(cell,cellIndex)"
10
- :span="calculateColSpan(cell)">
11
- <div id="report_widget" v-if="isWidget">
12
- <!-- 插槽渲染 -->
13
- <template v-if="Array.isArray(cell)">
14
- <!-- 处理 cell 是数组的情况 -->
15
- <div v-for="(item, index) in cell" :key="index">
16
- <x-report-tr-group
17
- :env="env"
18
- :key="index"
19
- :columns="recalculateItem(item)"
20
- :config-data="configData"
21
- :config="config"
22
- :display="true">
23
- </x-report-tr-group>
24
- </div>
25
- </template>
26
- <template v-else-if="cell.type === 'slot'">
27
- <template
28
- v-if="[
29
- 'x-form-table',
30
- 'h-form-table',
31
- 'x-add-native-form',
32
- 'h-add-native-form',
33
- 'x-tree-pro',
34
- 'x-his-editor',
35
- 'h-form',
36
- 'x-tab',
37
- 'h-tab',
38
- 'x-form-group',
39
- 'h-form-group',
40
- 'x-report',
41
- 'x-buttons',
42
- 'h-buttons',
43
- 'x-label-select',
44
- 'x-conversation',
45
- 'x-check-list',
46
- 'x-cardSet',
47
- 'x-collapse',
48
- 'x-h-descriptions',
49
- 'x-sidebar',
50
- 'x-list',
51
- 'x-input',
52
- 'x-time-line',
53
- 'x-radio',
54
- 'x-calendar',
55
- 'x-time-select',
56
- 'x-checkbox',
57
- 'x-title',
58
- 'x-select',
59
- 'x-tree-rows',
60
- 'x-three-test-orders',
61
- 'x-shift-schedule',
62
- 'x-charge',
63
- 'x-questionnaire',
64
- 'x-import-excel-button',
65
- 'x-chart'
66
- ].includes(cell.slotType)">
67
- <component
68
- :is="getComponentName(cell.slotConfig, cell.serviceName, cell.slotType)"
69
- :key="cellIndex"
70
- :ref="`dynamicComponent_${ cell.slotRef || cellIndex}`"
71
- v-bind="cell.attrs"
72
- :serviceName="cell.serviceName"
73
- :serverName="cell.serviceName"
74
- v-on="getEventHandlers(cell)"
75
- @hook:mounted="(h)=>onComponentMounted(h,cell,cellIndex)"
76
- @beforeDataChange="beforeDataChange"
77
- @rowClick="handleRowClick"
78
- @onExpand="onExpand"
79
- :queryParamsName="cell.slotConfig"
80
- :configName="cell.slotConfig"
81
- :countVisible="false"
82
- :env="env"
83
- />
84
- </template>
85
- </template>
86
- </div>
87
- <a-card v-else class="flexItem" :bordered="false">
88
- <!-- 插槽渲染 -->
89
- <template v-if="Array.isArray(cell)">
90
- <!-- 处理 cell 是数组的情况 -->
91
- <div v-for="(item, index) in cell" :key="index">
92
- <x-report-tr-group
93
- :server-name="serverName"
94
- :env="env"
95
- :key="index"
96
- :columns="recalculateItem(item)"
97
- :config-data="configData"
98
- :config="config"
99
- :display="true">
100
- </x-report-tr-group>
101
- </div>
102
- </template>
103
- <template v-else-if="cell.type === 'slot'">
104
- <template
105
- v-if="[
106
- 'x-form-table',
107
- 'h-form-table',
108
- 'x-add-native-form',
109
- 'h-add-native-form',
110
- 'h-form',
111
- 'x-tree-pro',
112
- 'x-his-editor',
113
- 'x-tab',
114
- 'h-tab',
115
- 'x-form-group',
116
- 'h-form-group',
117
- 'x-report',
118
- 'x-buttons',
119
- 'h-buttons',
120
- 'x-label-select',
121
- 'x-conversation',
122
- 'x-check-list',
123
- 'x-cardSet',
124
- 'x-collapse',
125
- 'x-h-descriptions',
126
- 'x-sidebar',
127
- 'x-list',
128
- 'x-input',
129
- 'x-time-line',
130
- 'x-radio',
131
- 'x-calendar',
132
- 'x-time-select',
133
- 'x-checkbox',
134
- 'x-title',
135
- 'x-select',
136
- 'x-tree-rows',
137
- 'x-three-test-orders',
138
- 'x-shift-schedule',
139
- 'x-charge',
140
- 'x-questionnaire',
141
- 'x-import-excel-button',
142
- 'x-chart'
143
- ].includes(cell.slotType)">
144
- <component
145
- :is="getComponentName(cell.slotConfig, cell.serviceName, cell.slotType)"
146
- :key="cellIndex"
147
- :ref="`dynamicComponent_${ cell.slotRef || cellIndex}`"
148
- v-bind="cell.attrs"
149
- :serviceName="cell.serviceName"
150
- :serverName="cell.serviceName"
151
- v-on="getEventHandlers(cell)"
152
- @hook:mounted="(h)=>onComponentMounted(h,cell,cellIndex)"
153
- @beforeDataChange="beforeDataChange"
154
- @rowClick="handleRowClick"
155
- @onExpand="onExpand"
156
- :queryParamsName="cell.slotConfig"
157
- :configName="cell.slotConfig"
158
- :countVisible="false"
159
- :env="env"
160
- />
161
- </template>
162
- </template>
163
- </a-card>
164
- </a-col>
165
- </template>
166
- </a-row>
167
- </template>
168
-
169
- <script>
170
- import Upload from '@vue2-client/base-client/components/common/Upload'
171
- import { getRealKeyData } from '@vue2-client/utils/util'
172
- import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
173
- import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
174
- import { getMicroData, getWindow, isMicroAppEnv, microDispatch } from '@vue2-client/utils/microAppUtils'
175
-
176
- export default {
177
- name: 'XReportTrGroup',
178
- components: {
179
- Upload,
180
- XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable/XFormTable.vue'),
181
- HFormTable: () => import('@vue2-client/base-client/components/common/HIS/HFormTable/HFormTable.vue'),
182
- HForm: () => import('@vue2-client/base-client/components/common/HIS/HForm/HForm.vue'),
183
- XAddNativeForm: () => import('@vue2-client/base-client/components/common/XAddNativeForm/XAddNativeForm.vue'),
184
- HAddNativeForm: () => import('@vue2-client/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm.vue'),
185
- XFormGroup: () => import('@vue2-client/base-client/components/common/XFormGroup/XFormGroup.vue'),
186
- HFormGroup: () => import('@vue2-client/base-client/components/common/HIS/HFormGroup/HFormGroup.vue'),
187
- XTreePro: () => import('@vue2-client/base-client/components/common/XTree/XTreePro.vue'),
188
- XHisEditor: () => import('@vue2-client/base-client/components/his/XHisEditor/XHisEditor.vue'),
189
- XTab: () => import('@vue2-client/base-client/components/common/XTab/XTab.vue'),
190
- HTab: () => import('@vue2-client/base-client/components/common/HIS/HTab/HTab.vue'),
191
- XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
192
- XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
193
- HButtons: () => import('@vue2-client/base-client/components/common/HIS/HButtons/HButtons.vue'),
194
- XLabelSelect: () => import('@vue2-client/base-client/components/common/XLabelSelect/XLabelSelect.vue'),
195
- XConversation: () => import('@vue2-client/base-client/components/common/XConversation/XConversation.vue'),
196
- XCheckList: () => import('@vue2-client/base-client/components/common/XCheckList/XCheckList.vue'),
197
- XCardSet: () => import('@vue2-client/base-client/components/common/XCardSet/XCardSet.vue'),
198
- XCollapse: () => import('@vue2-client/base-client/components/common/XCollapse/XCollapse.vue'),
199
- XHDescriptions: () => import('@vue2-client/base-client/components/his/XHDescriptions/XHDescriptions.vue'),
200
- XSidebar: () => import('@vue2-client/base-client/components/his/XSidebar/XSidebar.vue'),
201
- XList: () => import('@vue2-client/base-client/components/his/XList/XList.vue'),
202
- XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
203
- XTimeLine: () => import('@vue2-client/base-client/components/common/XTimeline/XTimeline.vue'),
204
- XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
205
- XCalendar: () => import('@vue2-client/base-client/components/common/XCalendar/XCalendar.vue'),
206
- XTimeSelect: () => import('@vue2-client/base-client/components/his/XTimeSelect/XTimeSelect.vue'),
207
- XCheckbox: () => import('@vue2-client/base-client/components/his/XCheckbox/XCheckbox.vue'),
208
- XTitle: () => import('@vue2-client/base-client/components/his/XTitle/XTitle.vue'),
209
- XSelect: () => import('@vue2-client/base-client/components/his/XSelect/XSelect.vue'),
210
- XTreeRows: () => import('@vue2-client/base-client/components/his/XTreeRows/XTreeRows.vue'),
211
- XThreeTestOrders: () => import('@vue2-client/base-client/components/his/threeTestOrders/threeTestOrders.vue'),
212
- XShiftSchedule: () => import('@vue2-client/base-client/components/his/XShiftSchedule/XShiftSchedule.vue'),
213
- XCharge: () => import('@vue2-client/base-client/components/his/XCharge/XCharge.vue'),
214
- XQuestionnaire: () => import('@vue2-client/base-client/components/his/XQuestionnaire/XQuestionnaire.vue'),
215
- XImportExcelButton: () => import('@vue2-client/base-client/components/his/XImportExcelButton/XImportExcelButton.vue'),
216
- XChart: () => import('@vue2-client/base-client/components/his/XChart/XChart.vue')
217
- },
218
- props: {
219
- // 每一行的配置
220
- columns: {
221
- type: Array,
222
- required: true
223
- },
224
- showImgInCell: {
225
- type: Boolean,
226
- default: false
227
- },
228
- config: {
229
- type: Object,
230
- default: function () {
231
- return {}
232
- }
233
- },
234
- // 命名空间
235
- serverName: {
236
- type: String,
237
- default: 'af-system'
238
- },
239
- // 环境
240
- env: {
241
- type: String,
242
- default: 'prod'
243
- },
244
- // 原始配置
245
- configData: {
246
- type: Object,
247
- required: true
248
- },
249
- // 是否为展示行
250
- display: {
251
- type: Boolean,
252
- default: false
253
- },
254
- },
255
- computed: {
256
- allSlotSum () {
257
- // 计算总共有多少个Slot
258
- let sum = 0
259
- this.columns.forEach((item) => {
260
- if (Array.isArray(item)) {
261
- item.forEach((cell) => {
262
- if (cell.type === 'slot') {
263
- sum++
264
- }
265
- })
266
- } else if (item.type && item.type === 'slot') {
267
- sum++
268
- }
269
- })
270
- return sum
271
- }
272
- },
273
- data () {
274
- return {
275
- gutter: [8, { xs: 8, sm: 16, md: 24, lg: 32 }], // 设置水槽大小
276
- maxColSpan: 12,
277
- uploadParams: {
278
- type: 'image',
279
- accept: ['*'],
280
- resUploadStock: 1,
281
- pathKey: 'cs'
282
- },
283
- mixinData: {},
284
- flexItemBodyState: {},
285
- // 已经渲染得插槽得数量
286
- slotRendered: 0,
287
- // tableConfig: {}
288
- }
289
- },
290
- watch: {
291
- columns: {
292
- deep: true,
293
- immediate: true,
294
- handler (newVal) {
295
- // 使用nextTick确保DOM更新
296
- this.$nextTick(() => {
297
- this.$forceUpdate()
298
- })
299
- }
300
- }
301
- },
302
- inject: {
303
- openDialog: { default: false },
304
- emitEvent: { default: false },
305
- registerComponent: { default: false },
306
- setColSpanByName: { default: false },
307
- setGlobalData: { default: false },
308
- getGlobalData: { default: false },
309
- getComponentByName: { default: false },
310
- runLogic: { default: false },
311
- getMixinData: { default: false },
312
- getSelectedId: { default: false },
313
- isInAModal: { default: false },
314
- getConfigByName: { default: false },
315
- getSelectedData: { default: false },
316
- getOutEnv: { default: false },
317
- currUser: { default: false },
318
- isWidget: { default: false },
319
- findComponentByName: { default: false },
320
- closeAddReport: { default: false }
321
- },
322
- methods: {
323
- getWindow,
324
- isMicroAppEnv,
325
- microDispatch,
326
- getMicroData,
327
- getRealKeyData,
328
- handleRowClick (record) {
329
- this.$emit('rowClick', record)
330
- },
331
- beforeDataChange (record) {
332
- this.$emit('beforeDataChange', record)
333
- },
334
- onExpand (expanded, record) {
335
- this.$emit('expand', expanded, record)
336
- },
337
- listClick (data) {
338
- this.$emit('listClick', data)
339
- },
340
- calculateColSpan (cell) {
341
- return Array.isArray(cell)
342
- ? cell[0][0]?.colSpan * 2
343
- : (cell?.colSpan ?? cell?.def?.colSpan ?? 1) * 2
344
- },
345
- applyAllStyles (cell, cellIndex) {
346
- // 应用组件样式
347
- const component = this.$refs[`trGroup_${ cell.slotRef || cellIndex}`][0]
348
- // 确保组件已经完全挂载
349
- this.$nextTick(() => {
350
- this.applyComponentStyles(component, cell, cellIndex)
351
- })
352
- },
353
- onComponentMounted (h, cell, cellIndex) {
354
- this.slotRendered += 1
355
- if (this.slotRendered >= this.allSlotSum) {
356
- this.$emit('slotRendered')
357
- }
358
- if (this.getMixinData && this.getMixinData()) {
359
- this.mixinData = this.getMixinData()
360
- }
361
- if (cell.slotRef) {
362
- this.registerComponent(cell.slotRef, this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0])
363
- }
364
- // 传递给祖先组件
365
- const shouldInit = cell.shouldInit == null ? true : cell.shouldInit
366
- if (shouldInit) {
367
- if (cell.slotType === 'x-add-native-form' || cell.slotType === 'h-form') {
368
- // 简易表单需要主动调用初始化方法
369
- getConfigByName(cell.slotConfig, cell.serviceName, async (res) => {
370
- // 如果配置了 表单初始化logic
371
- // 调用 logic 获取参数
372
- let param = { ...this.mixinData }
373
- let selectedId
374
- if (res.paramLogicName) {
375
- if (!!this.getSelectedId) {
376
- selectedId = this.getSelectedId()
377
- if (typeof selectedId !== 'object') {
378
- selectedId = { selectedId: selectedId }
379
- }
380
- }
381
- param = Object.assign(param, await runLogic(res.paramLogicName, selectedId, cell.serviceName))
382
- }
383
- this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
384
- serviceName: cell.serviceName,
385
- configName: cell.slotConfig,
386
- formItems: res.formJson,
387
- showSubmitBtn: !this.isInAModal,
388
- businessType: param.businessType || '新增',
389
- layout: res.xAddFormLayout,
390
- primaryKey: res.primaryKey,
391
- ...res,
392
- fixedAddForm: param,
393
- modifyModelData: {
394
- files: param.files,
395
- images: param.images
396
- }
397
- })
398
- }, this.env === 'dev')
399
- } else if (cell.slotType === 'x-form-group') {
400
- // 简易表单需要主动调用初始化方法
401
- getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
402
- // 如果配置了 表单初始化logic
403
- // 调用 logic 获取参数
404
- const param = { ...this.mixinData }
405
- this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
406
- ...res,
407
- serviceName: cell.serviceName,
408
- showSubmitBtn: !this.isInAModal,
409
- businessType: param.businessType || '新增',
410
- modifyModelData: param,
411
- showLeftTab: true,
412
- })
413
- }, this.env === 'dev')
414
- } else if (cell.slotType === 'x-label-select') {
415
- // 按钮组需要主动调用初始化方法
416
- getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
417
- this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
418
- ...res,
419
- serviceName: cell.serviceName,
420
- })
421
- }, this.env === 'dev')
422
- }
423
- }
424
- if (cell.slotType === 'x-report') {
425
- const param = { ...this.mixinData }
426
- this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
427
- configName: cell.slotConfig,
428
- configData: param
429
- })
430
- }
431
- if (cell.slotType === 'x-conversation') {
432
- getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
433
- this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
434
- serviceName: cell.serviceName,
435
- ...res,
436
- })
437
- }, this.env === 'dev')
438
- }
439
- if (cell.slotType === 'x-check-list') {
440
- getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
441
- this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
442
- serviceName: cell.serviceName,
443
- ...res,
444
- })
445
- }, this.env === 'dev')
446
- }
447
- },
448
- recalculateItem (item) {
449
- const totalColSpan = item.reduce((sum, cell) => {
450
- // 保留手动设置的colSpan
451
- return sum + (cell._isManualColSpan ? 0 : (cell.colSpan || 1))
452
- }, 0)
453
- return item.map(cell => {
454
- // 跳过已手动设置的单元格
455
- if (cell._isManualColSpan) return cell
456
- const newColSpan = Math.round((cell.colSpan || 1) / totalColSpan * 12)
457
- return {
458
- ...cell,
459
- colSpan: newColSpan,
460
- // 标记自动计算的单元格
461
- _isAutoCalculated: true
462
- }
463
- })
464
- },
465
-
466
- getEventHandlers (cell) {
467
- const handlers = {}
468
- if (!cell?.events || cell?.events?.length === 0) {
469
- return handlers
470
- }
471
- cell.events.forEach(event => {
472
- handlers[event.type] = async (...args) => {
473
- let func = event.customFunction
474
- if (func && func.startsWith('function')) {
475
- func = func.replace('function', 'async function')
476
- }
477
- const result = await executeStrFunctionByContext(this, func, args)
478
- if (result instanceof Promise) {
479
- result.then((res) => {
480
- if (!res) return
481
- let messageType = 'success'
482
- // 如果传递了组件名字 自动调用刷新
483
- if (res?.name) {
484
- const waitRefreshRef = this.getComponentByName(res.name)
485
- if (waitRefreshRef) {
486
- waitRefreshRef.refresh()
487
- } else {
488
- console.warn(`未找到组件${res.name}无法刷新`)
489
- }
490
- }
491
- // 如果传递消息类型 自动调用消息
492
- if (res?.messageType) {
493
- messageType = res.messageType
494
- }
495
- // 如果传递了提示信息自动调用提示
496
- if (res?.message) {
497
- this.$message[messageType](res?.message)
498
- }
499
- })
500
- }
501
- }
502
- })
503
- return handlers
504
- },
505
- getComponentName (queryParamsName, serviceName, componentName) {
506
- return componentName
507
- },
508
- // 判断单元格样式
509
- determineCellStyle (cell, color = '#000', borderWidth = '1px') {
510
- // 如果声明了borderColor
511
- if (this.config.style.borderColor) {
512
- color = this.config.style.borderColor
513
- }
514
- let result = {}
515
- // 如果表格也声明了样式,用表格样式将样式覆盖
516
- if (cell.style) {
517
- if (cell.noBorder) {
518
- result = { ...cell.style }
519
- } else {
520
- if (this.noTopBorder) {
521
- result = { ...cell.style }
522
- } else {
523
- result = { ...cell.style }
524
- }
525
- }
526
- return result
527
- }
528
- return result
529
- },
530
- // 把用户定义的组件,传递到整个杉格中,方便调用
531
- passComponentNamesToAncestor (refs) {
532
- // 遍历所有 refs
533
- Object.entries(refs).forEach(([refKey, refValue]) => {
534
- // 检查 ref 是否以 dynamicComponent_ 开头
535
- if (refKey.startsWith('dynamicComponent_')) {
536
- const componentRef = refValue[0]
537
- if (componentRef) {
538
- // 去掉前缀并获取组件名字
539
- const index = refKey.replace('dynamicComponent_', '') // 去掉前缀
540
- // 传递给祖先组件
541
- this.registerComponent(index, componentRef)
542
- }
543
- }
544
- })
545
- },
546
- // 获取组件样式配置
547
- async getComponentStyleConfig (componentType) {
548
- try {
549
- // 从配置中获取样式定义
550
- const publicStyleConfig = this.$appdata.getStylesByKey('public') || {}
551
- const styleConfig = this.$appdata.getStylesByKey(componentType) || {}
552
- Object.assign(publicStyleConfig, styleConfig)
553
- return publicStyleConfig
554
- } catch (error) {
555
- console.error('获取组件样式配置失败:', error)
556
- return {}
557
- }
558
- },
559
-
560
- // 解析组件样式配置
561
- async parseComponentStyles (cell) {
562
- if (!cell.class) return { rootStyles: {}, childStyles: {} }
563
-
564
- const styleConfig = await this.getComponentStyleConfig(cell.slotType)
565
- if (!styleConfig) return { rootStyles: {}, childStyles: {} }
566
-
567
- const rootStyles = {}
568
- const childStyles = new Map()
569
-
570
- // 处理每个class配置
571
- cell.class.split(' ').forEach(className => {
572
- const classConfig = styleConfig[className]
573
- if (!classConfig) return
574
-
575
- // 处理根节点样式
576
- Object.entries(classConfig).forEach(([key, value]) => {
577
- if (!key.startsWith('*') && typeof key !== 'object') {
578
- rootStyles[key] = value
579
- }
580
- })
581
-
582
- // 处理子节点样式
583
- this.parseNestedStyles(classConfig, childStyles)
584
- })
585
- return {
586
- rootStyles,
587
- childStyles
588
- }
589
- },
590
-
591
- // 递归解析嵌套的样式配置
592
- parseNestedStyles (config, styleMap, parentKey = '') {
593
- Object.entries(config).forEach(([key, value]) => {
594
- if (!key.startsWith('*')) return
595
-
596
- const className = key.replace('*', '.')
597
-
598
- // 如果值是对象,检查是否包含样式和子节点
599
- if (typeof value === 'object') {
600
- const { style = {}, children = {} } = this.separateStyleAndChildren(value)
601
-
602
- // 创建或获取当前节点的样式配置
603
- if (!styleMap.has(className)) {
604
- styleMap.set(className, {
605
- styles: {},
606
- children: new Map()
607
- })
608
- }
609
-
610
- const nodeData = styleMap.get(className)
611
-
612
- // 合并样式
613
- Object.assign(nodeData.styles, style)
614
-
615
- // 递归处理子节点
616
- this.parseNestedStyles(children, nodeData.children, className)
617
- }
618
- })
619
- },
620
-
621
- // 分离样式属性和子节点配置
622
- separateStyleAndChildren (obj) {
623
- const style = {}
624
- const children = {}
625
-
626
- Object.entries(obj).forEach(([key, value]) => {
627
- if (key.startsWith('*')) {
628
- // 子节点配置
629
- children[key] = value
630
- } else {
631
- // 样式属性
632
- style[key] = value
633
- }
634
- })
635
-
636
- return { style, children }
637
- },
638
-
639
- // 应用组件样式
640
- async applyComponentStyles (component, cell, cellIndex) {
641
- if (!component || !component.$el) return
642
-
643
- const { rootStyles, childStyles } = await this.parseComponentStyles(cell)
644
-
645
- // 应用根节点样式
646
- if (Object.keys(rootStyles).length > 0) {
647
- Object.entries(rootStyles).forEach(([property, value]) => {
648
- component.$el.style.setProperty(property, value, 'important')
649
- })
650
- }
651
-
652
- // 如果没有子节点样式,直接返回
653
- if (childStyles.size === 0) return
654
-
655
- let retryCount = 0
656
- const maxRetries = 5
657
- const retryInterval = 100 // 100ms
658
-
659
- const applyStyles = () => {
660
- this.applyChildStylesOptimized(component.$el, childStyles)
661
- }
662
-
663
- // 首次应用样式
664
- applyStyles()
665
-
666
- // 创建重试机制
667
- const retryApplyStyles = () => {
668
- if (retryCount >= maxRetries) return
669
-
670
- setTimeout(() => {
671
- applyStyles()
672
- retryCount++
673
- retryApplyStyles()
674
- }, retryInterval)
675
- }
676
-
677
- // 开始重试
678
- retryApplyStyles()
679
-
680
- // 创建 MutationObserver 用于动态内容
681
- const observer = new MutationObserver((mutations) => {
682
- // 检查是否有新增节点
683
- const hasNewNodes = mutations.some(mutation =>
684
- mutation.type === 'childList' && mutation.addedNodes.length > 0
685
- )
686
-
687
- if (hasNewNodes) {
688
- applyStyles()
689
- }
690
- })
691
-
692
- // 配置 observer
693
- observer.observe(component.$el, {
694
- childList: true,
695
- subtree: true,
696
- attributes: false
697
- })
698
-
699
- // 3秒后停止观察
700
- // setTimeout(() => {
701
- // observer.disconnect()
702
- // }, 3000)
703
-
704
- // 组件销毁时清理
705
- this.$once('hook:beforeDestroy', () => {
706
- observer.disconnect()
707
- })
708
- },
709
-
710
- // 优化后的子节点样式应用方法
711
- applyChildStylesOptimized (rootElement, styleMap, parentSelector = '') {
712
- if (!rootElement) return
713
-
714
- // 处理样式映射
715
- try {
716
- for (const [selector, data] of styleMap.entries()) {
717
- const currentSelector = parentSelector ? `${parentSelector} ${selector}` : selector
718
-
719
- try {
720
- // 查找匹配的元素
721
- const elements = Array.from(rootElement.querySelectorAll(currentSelector))
722
-
723
- if (!elements.length) continue
724
-
725
- // 应用当前层级样式
726
- if (data.styles) {
727
- elements.forEach(element => {
728
- if (!element) return
729
-
730
- // 应用每个样式属性
731
- Object.entries(data.styles).forEach(([property, value]) => {
732
- try {
733
- element.style.setProperty(property, value, 'important')
734
- } catch (err) {
735
- console.warn(`设置样式失败: ${property}=${value}`, err)
736
- }
737
- })
738
- })
739
- }
740
-
741
- // 处理子层级
742
- if (data.children && data.children.size > 0) {
743
- elements.forEach(element => {
744
- if (element) {
745
- this.applyChildStylesOptimized(element, data.children, currentSelector)
746
- }
747
- })
748
- }
749
- } catch (err) {
750
- console.warn(`处理选择器失败: ${currentSelector}`, err)
751
- continue
752
- }
753
- }
754
- } catch (err) {}
755
- }
756
- },
757
- beforeMount () {
758
- if (this.useOssForImg) {
759
- this.uploadParams.resUploadMode = 'oss'
760
- }
761
- },
762
- mounted () {
763
- },
764
- }
765
-
766
- </script>
767
-
768
- <style scoped lang="less">
769
- .inputsDiv {
770
- display: flex;
771
- justify-content: space-between;
772
-
773
- .inputsDivItem {
774
- display: flex;
775
- align-items: center;
776
- padding: 0 4px;
777
- white-space: nowrap;
778
-
779
- .inputsDivItemLabel {
780
- padding: 0 4px;
781
- }
782
- }
783
- }
784
-
785
- .tdNoBorder {
786
- border-left: 1px solid #000;
787
- border-right: 1px solid #000;
788
- padding: 8px;
789
- }
790
-
791
- .tdWithBorder {
792
- border: 1px solid #000;
793
- padding: 8px;
794
- }
795
-
796
- .tdWithNoTopBorder {
797
- border-top-style: none;
798
- border-left: 1px solid #000;
799
- border-right: 1px solid #000;
800
- border-bottom: 1px solid #000;
801
- padding: 8px;
802
- }
803
-
804
- .grid-content {
805
- border-radius: 4px;
806
- min-height: 36px;
807
- text-align: center;
808
- color: #fff;
809
- background-color: #606266;
810
- }
811
-
812
- .bg-purple {
813
- background: #9254de;
814
- }
815
-
816
- .bg-purple-light {
817
- background: #b37feb;
818
- }
819
-
820
- .flexItem {
821
- border-radius: 8px;
822
- height: 100%;
823
- }
824
- </style>
1
+ <template>
2
+ <a-row id="has_row" type="flex" :gutter="gutter" :style="isWidget ? {margin: '0px'} : {'margin-bottom': '.5rem'}">
3
+ <template v-for="(cell, cellIndex) in columns">
4
+ <a-col
5
+ name="trGroup"
6
+ v-if="Array.isArray(cell) || !cell.dontShowRow"
7
+ :key="cellIndex"
8
+ :ref="`trGroup_${ cell.slotRef || cellIndex}`"
9
+ @hook:mounted="(h)=>applyAllStyles(cell,cellIndex)"
10
+ :span="calculateColSpan(cell)">
11
+ <div id="report_widget" v-if="isWidget">
12
+ <!-- 插槽渲染 -->
13
+ <template v-if="Array.isArray(cell)">
14
+ <!-- 处理 cell 是数组的情况 -->
15
+ <div v-for="(item, index) in cell" :key="index">
16
+ <x-report-tr-group
17
+ :env="env"
18
+ :key="index"
19
+ :columns="recalculateItem(item)"
20
+ :config-data="configData"
21
+ :config="config"
22
+ :display="true">
23
+ </x-report-tr-group>
24
+ </div>
25
+ </template>
26
+ <template v-else-if="cell.type === 'slot'">
27
+ <template
28
+ v-if="[
29
+ 'x-form-table',
30
+ 'h-form-table',
31
+ 'x-add-native-form',
32
+ 'h-add-native-form',
33
+ 'x-tree-pro',
34
+ 'x-his-editor',
35
+ 'h-form',
36
+ 'x-tab',
37
+ 'h-tab',
38
+ 'x-form-group',
39
+ 'h-form-group',
40
+ 'x-report',
41
+ 'x-buttons',
42
+ 'h-buttons',
43
+ 'x-label-select',
44
+ 'x-conversation',
45
+ 'x-check-list',
46
+ 'x-cardSet',
47
+ 'x-collapse',
48
+ 'x-h-descriptions',
49
+ 'x-sidebar',
50
+ 'x-list',
51
+ 'x-input',
52
+ 'x-time-line',
53
+ 'x-radio',
54
+ 'x-calendar',
55
+ 'x-time-select',
56
+ 'x-checkbox',
57
+ 'x-title',
58
+ 'x-select',
59
+ 'x-tree-rows',
60
+ 'x-three-test-orders',
61
+ 'x-shift-schedule',
62
+ 'x-charge',
63
+ 'x-questionnaire',
64
+ 'x-import-excel-button',
65
+ 'x-chart'
66
+ ].includes(cell.slotType)">
67
+ <component
68
+ :is="getComponentName(cell.slotConfig, cell.serviceName, cell.slotType)"
69
+ :key="cellIndex"
70
+ :ref="`dynamicComponent_${ cell.slotRef || cellIndex}`"
71
+ v-bind="cell.attrs"
72
+ :serviceName="cell.serviceName"
73
+ :serverName="cell.serviceName"
74
+ v-on="getEventHandlers(cell)"
75
+ @hook:mounted="(h)=>onComponentMounted(h,cell,cellIndex)"
76
+ @beforeDataChange="beforeDataChange"
77
+ @rowClick="handleRowClick"
78
+ @onExpand="onExpand"
79
+ :queryParamsName="cell.slotConfig"
80
+ :configName="cell.slotConfig"
81
+ :countVisible="false"
82
+ :env="env"
83
+ />
84
+ </template>
85
+ </template>
86
+ </div>
87
+ <a-card v-else class="flexItem" :bordered="false">
88
+ <!-- 插槽渲染 -->
89
+ <template v-if="Array.isArray(cell)">
90
+ <!-- 处理 cell 是数组的情况 -->
91
+ <div v-for="(item, index) in cell" :key="index">
92
+ <x-report-tr-group
93
+ :server-name="serverName"
94
+ :env="env"
95
+ :key="index"
96
+ :columns="recalculateItem(item)"
97
+ :config-data="configData"
98
+ :config="config"
99
+ :display="true">
100
+ </x-report-tr-group>
101
+ </div>
102
+ </template>
103
+ <template v-else-if="cell.type === 'slot'">
104
+ <template
105
+ v-if="[
106
+ 'x-form-table',
107
+ 'h-form-table',
108
+ 'x-add-native-form',
109
+ 'h-add-native-form',
110
+ 'h-form',
111
+ 'x-tree-pro',
112
+ 'x-his-editor',
113
+ 'x-tab',
114
+ 'h-tab',
115
+ 'x-form-group',
116
+ 'h-form-group',
117
+ 'x-report',
118
+ 'x-buttons',
119
+ 'h-buttons',
120
+ 'x-label-select',
121
+ 'x-conversation',
122
+ 'x-check-list',
123
+ 'x-cardSet',
124
+ 'x-collapse',
125
+ 'x-h-descriptions',
126
+ 'x-sidebar',
127
+ 'x-list',
128
+ 'x-input',
129
+ 'x-time-line',
130
+ 'x-radio',
131
+ 'x-calendar',
132
+ 'x-time-select',
133
+ 'x-checkbox',
134
+ 'x-title',
135
+ 'x-select',
136
+ 'x-tree-rows',
137
+ 'x-three-test-orders',
138
+ 'x-shift-schedule',
139
+ 'x-charge',
140
+ 'x-questionnaire',
141
+ 'x-import-excel-button',
142
+ 'x-chart'
143
+ ].includes(cell.slotType)">
144
+ <component
145
+ :is="getComponentName(cell.slotConfig, cell.serviceName, cell.slotType)"
146
+ :key="cellIndex"
147
+ :ref="`dynamicComponent_${ cell.slotRef || cellIndex}`"
148
+ v-bind="cell.attrs"
149
+ :serviceName="cell.serviceName"
150
+ :serverName="cell.serviceName"
151
+ v-on="getEventHandlers(cell)"
152
+ @hook:mounted="(h)=>onComponentMounted(h,cell,cellIndex)"
153
+ @beforeDataChange="beforeDataChange"
154
+ @rowClick="handleRowClick"
155
+ @onExpand="onExpand"
156
+ :queryParamsName="cell.slotConfig"
157
+ :configName="cell.slotConfig"
158
+ :countVisible="false"
159
+ :env="env"
160
+ />
161
+ </template>
162
+ </template>
163
+ </a-card>
164
+ </a-col>
165
+ </template>
166
+ </a-row>
167
+ </template>
168
+
169
+ <script>
170
+ import Upload from '@vue2-client/base-client/components/common/Upload'
171
+ import { getRealKeyData } from '@vue2-client/utils/util'
172
+ import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
173
+ import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
174
+ import { getMicroData, getWindow, isMicroAppEnv, microDispatch } from '@vue2-client/utils/microAppUtils'
175
+
176
+ export default {
177
+ name: 'XReportTrGroup',
178
+ components: {
179
+ Upload,
180
+ XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable/XFormTable.vue'),
181
+ HFormTable: () => import('@vue2-client/base-client/components/common/HIS/HFormTable/HFormTable.vue'),
182
+ HForm: () => import('@vue2-client/base-client/components/common/HIS/HForm/HForm.vue'),
183
+ XAddNativeForm: () => import('@vue2-client/base-client/components/common/XAddNativeForm/XAddNativeForm.vue'),
184
+ HAddNativeForm: () => import('@vue2-client/base-client/components/common/HIS/HAddNativeForm/HAddNativeForm.vue'),
185
+ XFormGroup: () => import('@vue2-client/base-client/components/common/XFormGroup/XFormGroup.vue'),
186
+ HFormGroup: () => import('@vue2-client/base-client/components/common/HIS/HFormGroup/HFormGroup.vue'),
187
+ XTreePro: () => import('@vue2-client/base-client/components/common/XTree/XTreePro.vue'),
188
+ XHisEditor: () => import('@vue2-client/base-client/components/his/XHisEditor/XHisEditor.vue'),
189
+ XTab: () => import('@vue2-client/base-client/components/common/XTab/XTab.vue'),
190
+ HTab: () => import('@vue2-client/base-client/components/common/HIS/HTab/HTab.vue'),
191
+ XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
192
+ XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
193
+ HButtons: () => import('@vue2-client/base-client/components/common/HIS/HButtons/HButtons.vue'),
194
+ XLabelSelect: () => import('@vue2-client/base-client/components/common/XLabelSelect/XLabelSelect.vue'),
195
+ XConversation: () => import('@vue2-client/base-client/components/common/XConversation/XConversation.vue'),
196
+ XCheckList: () => import('@vue2-client/base-client/components/common/XCheckList/XCheckList.vue'),
197
+ XCardSet: () => import('@vue2-client/base-client/components/common/XCardSet/XCardSet.vue'),
198
+ XCollapse: () => import('@vue2-client/base-client/components/common/XCollapse/XCollapse.vue'),
199
+ XHDescriptions: () => import('@vue2-client/base-client/components/his/XHDescriptions/XHDescriptions.vue'),
200
+ XSidebar: () => import('@vue2-client/base-client/components/his/XSidebar/XSidebar.vue'),
201
+ XList: () => import('@vue2-client/base-client/components/his/XList/XList.vue'),
202
+ XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
203
+ XTimeLine: () => import('@vue2-client/base-client/components/common/XTimeline/XTimeline.vue'),
204
+ XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
205
+ XCalendar: () => import('@vue2-client/base-client/components/common/XCalendar/XCalendar.vue'),
206
+ XTimeSelect: () => import('@vue2-client/base-client/components/his/XTimeSelect/XTimeSelect.vue'),
207
+ XCheckbox: () => import('@vue2-client/base-client/components/his/XCheckbox/XCheckbox.vue'),
208
+ XTitle: () => import('@vue2-client/base-client/components/his/XTitle/XTitle.vue'),
209
+ XSelect: () => import('@vue2-client/base-client/components/his/XSelect/XSelect.vue'),
210
+ XTreeRows: () => import('@vue2-client/base-client/components/his/XTreeRows/XTreeRows.vue'),
211
+ XThreeTestOrders: () => import('@vue2-client/base-client/components/his/threeTestOrders/threeTestOrders.vue'),
212
+ XShiftSchedule: () => import('@vue2-client/base-client/components/his/XShiftSchedule/XShiftSchedule.vue'),
213
+ XCharge: () => import('@vue2-client/base-client/components/his/XCharge/XCharge.vue'),
214
+ XQuestionnaire: () => import('@vue2-client/base-client/components/his/XQuestionnaire/XQuestionnaire.vue'),
215
+ XImportExcelButton: () => import('@vue2-client/base-client/components/his/XImportExcelButton/XImportExcelButton.vue'),
216
+ XChart: () => import('@vue2-client/base-client/components/his/XChart/XChart.vue')
217
+ },
218
+ props: {
219
+ // 每一行的配置
220
+ columns: {
221
+ type: Array,
222
+ required: true
223
+ },
224
+ showImgInCell: {
225
+ type: Boolean,
226
+ default: false
227
+ },
228
+ config: {
229
+ type: Object,
230
+ default: function () {
231
+ return {}
232
+ }
233
+ },
234
+ // 命名空间
235
+ serverName: {
236
+ type: String,
237
+ default: 'af-system'
238
+ },
239
+ // 环境
240
+ env: {
241
+ type: String,
242
+ default: 'prod'
243
+ },
244
+ // 原始配置
245
+ configData: {
246
+ type: Object,
247
+ required: true
248
+ },
249
+ // 是否为展示行
250
+ display: {
251
+ type: Boolean,
252
+ default: false
253
+ },
254
+ },
255
+ computed: {
256
+ allSlotSum () {
257
+ // 计算总共有多少个Slot
258
+ let sum = 0
259
+ this.columns.forEach((item) => {
260
+ if (Array.isArray(item)) {
261
+ item.forEach((cell) => {
262
+ if (cell.type === 'slot') {
263
+ sum++
264
+ }
265
+ })
266
+ } else if (item.type && item.type === 'slot') {
267
+ sum++
268
+ }
269
+ })
270
+ return sum
271
+ }
272
+ },
273
+ data () {
274
+ return {
275
+ gutter: [8, { xs: 8, sm: 16, md: 24, lg: 32 }], // 设置水槽大小
276
+ maxColSpan: 12,
277
+ uploadParams: {
278
+ type: 'image',
279
+ accept: ['*'],
280
+ resUploadStock: 1,
281
+ pathKey: 'cs'
282
+ },
283
+ mixinData: {},
284
+ flexItemBodyState: {},
285
+ // 已经渲染得插槽得数量
286
+ slotRendered: 0,
287
+ // tableConfig: {}
288
+ }
289
+ },
290
+ watch: {
291
+ columns: {
292
+ deep: true,
293
+ immediate: true,
294
+ handler (newVal) {
295
+ // 使用nextTick确保DOM更新
296
+ this.$nextTick(() => {
297
+ this.$forceUpdate()
298
+ })
299
+ }
300
+ }
301
+ },
302
+ inject: {
303
+ openDialog: { default: false },
304
+ emitEvent: { default: false },
305
+ registerComponent: { default: false },
306
+ setColSpanByName: { default: false },
307
+ setGlobalData: { default: false },
308
+ getGlobalData: { default: false },
309
+ getComponentByName: { default: false },
310
+ runLogic: { default: false },
311
+ getMixinData: { default: false },
312
+ getSelectedId: { default: false },
313
+ isInAModal: { default: false },
314
+ getConfigByName: { default: false },
315
+ getSelectedData: { default: false },
316
+ getOutEnv: { default: false },
317
+ currUser: { default: false },
318
+ isWidget: { default: false },
319
+ findComponentByName: { default: false },
320
+ closeAddReport: { default: false }
321
+ },
322
+ methods: {
323
+ getWindow,
324
+ isMicroAppEnv,
325
+ microDispatch,
326
+ getMicroData,
327
+ getRealKeyData,
328
+ handleRowClick (record) {
329
+ this.$emit('rowClick', record)
330
+ },
331
+ beforeDataChange (record) {
332
+ this.$emit('beforeDataChange', record)
333
+ },
334
+ onExpand (expanded, record) {
335
+ this.$emit('expand', expanded, record)
336
+ },
337
+ listClick (data) {
338
+ this.$emit('listClick', data)
339
+ },
340
+ calculateColSpan (cell) {
341
+ return Array.isArray(cell)
342
+ ? cell[0][0]?.colSpan * 2
343
+ : (cell?.colSpan ?? cell?.def?.colSpan ?? 1) * 2
344
+ },
345
+ applyAllStyles (cell, cellIndex) {
346
+ // 应用组件样式
347
+ const component = this.$refs[`trGroup_${ cell.slotRef || cellIndex}`][0]
348
+ // 确保组件已经完全挂载
349
+ this.$nextTick(() => {
350
+ this.applyComponentStyles(component, cell, cellIndex)
351
+ })
352
+ },
353
+ onComponentMounted (h, cell, cellIndex) {
354
+ this.slotRendered += 1
355
+ if (this.slotRendered >= this.allSlotSum) {
356
+ this.$emit('slotRendered')
357
+ }
358
+ if (this.getMixinData && this.getMixinData()) {
359
+ this.mixinData = this.getMixinData()
360
+ }
361
+ if (cell.slotRef) {
362
+ this.registerComponent(cell.slotRef, this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0])
363
+ }
364
+ // 传递给祖先组件
365
+ const shouldInit = cell.shouldInit == null ? true : cell.shouldInit
366
+ if (shouldInit) {
367
+ if (cell.slotType === 'x-add-native-form' || cell.slotType === 'h-form') {
368
+ // 简易表单需要主动调用初始化方法
369
+ getConfigByName(cell.slotConfig, cell.serviceName, async (res) => {
370
+ // 如果配置了 表单初始化logic
371
+ // 调用 logic 获取参数
372
+ let param = { ...this.mixinData }
373
+ let selectedId
374
+ if (res.paramLogicName) {
375
+ if (!!this.getSelectedId) {
376
+ selectedId = this.getSelectedId()
377
+ if (typeof selectedId !== 'object') {
378
+ selectedId = { selectedId: selectedId }
379
+ }
380
+ }
381
+ param = Object.assign(param, await runLogic(res.paramLogicName, selectedId, cell.serviceName))
382
+ }
383
+ this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
384
+ serviceName: cell.serviceName,
385
+ configName: cell.slotConfig,
386
+ formItems: res.formJson,
387
+ showSubmitBtn: !this.isInAModal,
388
+ businessType: param.businessType || '新增',
389
+ layout: res.xAddFormLayout,
390
+ primaryKey: res.primaryKey,
391
+ ...res,
392
+ fixedAddForm: param,
393
+ modifyModelData: {
394
+ files: param.files,
395
+ images: param.images
396
+ }
397
+ })
398
+ }, this.env === 'dev')
399
+ } else if (cell.slotType === 'x-form-group') {
400
+ // 简易表单需要主动调用初始化方法
401
+ getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
402
+ // 如果配置了 表单初始化logic
403
+ // 调用 logic 获取参数
404
+ const param = { ...this.mixinData }
405
+ this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
406
+ ...res,
407
+ serviceName: cell.serviceName,
408
+ showSubmitBtn: !this.isInAModal,
409
+ businessType: param.businessType || '新增',
410
+ modifyModelData: param,
411
+ showLeftTab: true,
412
+ })
413
+ }, this.env === 'dev')
414
+ } else if (cell.slotType === 'x-label-select') {
415
+ // 按钮组需要主动调用初始化方法
416
+ getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
417
+ this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
418
+ ...res,
419
+ serviceName: cell.serviceName,
420
+ })
421
+ }, this.env === 'dev')
422
+ }
423
+ }
424
+ if (cell.slotType === 'x-report') {
425
+ const param = { ...this.mixinData }
426
+ this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
427
+ configName: cell.slotConfig,
428
+ configData: param
429
+ })
430
+ }
431
+ if (cell.slotType === 'x-conversation') {
432
+ getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
433
+ this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
434
+ serviceName: cell.serviceName,
435
+ ...res,
436
+ })
437
+ }, this.env === 'dev')
438
+ }
439
+ if (cell.slotType === 'x-check-list') {
440
+ getConfigByName(cell.slotConfig, cell.serviceName, (res) => {
441
+ this.$refs[`dynamicComponent_${cell.slotRef || cellIndex}`][0].init({
442
+ serviceName: cell.serviceName,
443
+ ...res,
444
+ })
445
+ }, this.env === 'dev')
446
+ }
447
+ },
448
+ recalculateItem (item) {
449
+ const totalColSpan = item.reduce((sum, cell) => {
450
+ // 保留手动设置的colSpan
451
+ return sum + (cell._isManualColSpan ? 0 : (cell.colSpan || 1))
452
+ }, 0)
453
+ return item.map(cell => {
454
+ // 跳过已手动设置的单元格
455
+ if (cell._isManualColSpan) return cell
456
+ const newColSpan = Math.round((cell.colSpan || 1) / totalColSpan * 12)
457
+ return {
458
+ ...cell,
459
+ colSpan: newColSpan,
460
+ // 标记自动计算的单元格
461
+ _isAutoCalculated: true
462
+ }
463
+ })
464
+ },
465
+
466
+ getEventHandlers (cell) {
467
+ const handlers = {}
468
+ if (!cell?.events || cell?.events?.length === 0) {
469
+ return handlers
470
+ }
471
+ cell.events.forEach(event => {
472
+ handlers[event.type] = async (...args) => {
473
+ let func = event.customFunction
474
+ if (func && func.startsWith('function')) {
475
+ func = func.replace('function', 'async function')
476
+ }
477
+ const result = await executeStrFunctionByContext(this, func, args)
478
+ if (result instanceof Promise) {
479
+ result.then((res) => {
480
+ if (!res) return
481
+ let messageType = 'success'
482
+ // 如果传递了组件名字 自动调用刷新
483
+ if (res?.name) {
484
+ const waitRefreshRef = this.getComponentByName(res.name)
485
+ if (waitRefreshRef) {
486
+ waitRefreshRef.refresh()
487
+ } else {
488
+ console.warn(`未找到组件${res.name}无法刷新`)
489
+ }
490
+ }
491
+ // 如果传递消息类型 自动调用消息
492
+ if (res?.messageType) {
493
+ messageType = res.messageType
494
+ }
495
+ // 如果传递了提示信息自动调用提示
496
+ if (res?.message) {
497
+ this.$message[messageType](res?.message)
498
+ }
499
+ })
500
+ }
501
+ }
502
+ })
503
+ return handlers
504
+ },
505
+ getComponentName (queryParamsName, serviceName, componentName) {
506
+ return componentName
507
+ },
508
+ // 判断单元格样式
509
+ determineCellStyle (cell, color = '#000', borderWidth = '1px') {
510
+ // 如果声明了borderColor
511
+ if (this.config.style.borderColor) {
512
+ color = this.config.style.borderColor
513
+ }
514
+ let result = {}
515
+ // 如果表格也声明了样式,用表格样式将样式覆盖
516
+ if (cell.style) {
517
+ if (cell.noBorder) {
518
+ result = { ...cell.style }
519
+ } else {
520
+ if (this.noTopBorder) {
521
+ result = { ...cell.style }
522
+ } else {
523
+ result = { ...cell.style }
524
+ }
525
+ }
526
+ return result
527
+ }
528
+ return result
529
+ },
530
+ // 把用户定义的组件,传递到整个杉格中,方便调用
531
+ passComponentNamesToAncestor (refs) {
532
+ // 遍历所有 refs
533
+ Object.entries(refs).forEach(([refKey, refValue]) => {
534
+ // 检查 ref 是否以 dynamicComponent_ 开头
535
+ if (refKey.startsWith('dynamicComponent_')) {
536
+ const componentRef = refValue[0]
537
+ if (componentRef) {
538
+ // 去掉前缀并获取组件名字
539
+ const index = refKey.replace('dynamicComponent_', '') // 去掉前缀
540
+ // 传递给祖先组件
541
+ this.registerComponent(index, componentRef)
542
+ }
543
+ }
544
+ })
545
+ },
546
+ // 获取组件样式配置
547
+ async getComponentStyleConfig (componentType) {
548
+ try {
549
+ // 从配置中获取样式定义
550
+ const publicStyleConfig = this.$appdata.getStylesByKey('public') || {}
551
+ const styleConfig = this.$appdata.getStylesByKey(componentType) || {}
552
+ Object.assign(publicStyleConfig, styleConfig)
553
+ return publicStyleConfig
554
+ } catch (error) {
555
+ console.error('获取组件样式配置失败:', error)
556
+ return {}
557
+ }
558
+ },
559
+
560
+ // 解析组件样式配置
561
+ async parseComponentStyles (cell) {
562
+ if (!cell.class) return { rootStyles: {}, childStyles: {} }
563
+
564
+ const styleConfig = await this.getComponentStyleConfig(cell.slotType)
565
+ if (!styleConfig) return { rootStyles: {}, childStyles: {} }
566
+
567
+ const rootStyles = {}
568
+ const childStyles = new Map()
569
+
570
+ // 处理每个class配置
571
+ cell.class.split(' ').forEach(className => {
572
+ const classConfig = styleConfig[className]
573
+ if (!classConfig) return
574
+
575
+ // 处理根节点样式
576
+ Object.entries(classConfig).forEach(([key, value]) => {
577
+ if (!key.startsWith('*') && typeof key !== 'object') {
578
+ rootStyles[key] = value
579
+ }
580
+ })
581
+
582
+ // 处理子节点样式
583
+ this.parseNestedStyles(classConfig, childStyles)
584
+ })
585
+ return {
586
+ rootStyles,
587
+ childStyles
588
+ }
589
+ },
590
+
591
+ // 递归解析嵌套的样式配置
592
+ parseNestedStyles (config, styleMap, parentKey = '') {
593
+ Object.entries(config).forEach(([key, value]) => {
594
+ if (!key.startsWith('*')) return
595
+
596
+ const className = key.replace('*', '.')
597
+
598
+ // 如果值是对象,检查是否包含样式和子节点
599
+ if (typeof value === 'object') {
600
+ const { style = {}, children = {} } = this.separateStyleAndChildren(value)
601
+
602
+ // 创建或获取当前节点的样式配置
603
+ if (!styleMap.has(className)) {
604
+ styleMap.set(className, {
605
+ styles: {},
606
+ children: new Map()
607
+ })
608
+ }
609
+
610
+ const nodeData = styleMap.get(className)
611
+
612
+ // 合并样式
613
+ Object.assign(nodeData.styles, style)
614
+
615
+ // 递归处理子节点
616
+ this.parseNestedStyles(children, nodeData.children, className)
617
+ }
618
+ })
619
+ },
620
+
621
+ // 分离样式属性和子节点配置
622
+ separateStyleAndChildren (obj) {
623
+ const style = {}
624
+ const children = {}
625
+
626
+ Object.entries(obj).forEach(([key, value]) => {
627
+ if (key.startsWith('*')) {
628
+ // 子节点配置
629
+ children[key] = value
630
+ } else {
631
+ // 样式属性
632
+ style[key] = value
633
+ }
634
+ })
635
+
636
+ return { style, children }
637
+ },
638
+
639
+ // 应用组件样式
640
+ async applyComponentStyles (component, cell, cellIndex) {
641
+ if (!component || !component.$el) return
642
+
643
+ const { rootStyles, childStyles } = await this.parseComponentStyles(cell)
644
+
645
+ // 应用根节点样式
646
+ if (Object.keys(rootStyles).length > 0) {
647
+ Object.entries(rootStyles).forEach(([property, value]) => {
648
+ component.$el.style.setProperty(property, value, 'important')
649
+ })
650
+ }
651
+
652
+ // 如果没有子节点样式,直接返回
653
+ if (childStyles.size === 0) return
654
+
655
+ let retryCount = 0
656
+ const maxRetries = 5
657
+ const retryInterval = 100 // 100ms
658
+
659
+ const applyStyles = () => {
660
+ this.applyChildStylesOptimized(component.$el, childStyles)
661
+ }
662
+
663
+ // 首次应用样式
664
+ applyStyles()
665
+
666
+ // 创建重试机制
667
+ const retryApplyStyles = () => {
668
+ if (retryCount >= maxRetries) return
669
+
670
+ setTimeout(() => {
671
+ applyStyles()
672
+ retryCount++
673
+ retryApplyStyles()
674
+ }, retryInterval)
675
+ }
676
+
677
+ // 开始重试
678
+ retryApplyStyles()
679
+
680
+ // 创建 MutationObserver 用于动态内容
681
+ const observer = new MutationObserver((mutations) => {
682
+ // 检查是否有新增节点
683
+ const hasNewNodes = mutations.some(mutation =>
684
+ mutation.type === 'childList' && mutation.addedNodes.length > 0
685
+ )
686
+
687
+ if (hasNewNodes) {
688
+ applyStyles()
689
+ }
690
+ })
691
+
692
+ // 配置 observer
693
+ observer.observe(component.$el, {
694
+ childList: true,
695
+ subtree: true,
696
+ attributes: false
697
+ })
698
+
699
+ // 3秒后停止观察
700
+ // setTimeout(() => {
701
+ // observer.disconnect()
702
+ // }, 3000)
703
+
704
+ // 组件销毁时清理
705
+ this.$once('hook:beforeDestroy', () => {
706
+ observer.disconnect()
707
+ })
708
+ },
709
+
710
+ // 优化后的子节点样式应用方法
711
+ applyChildStylesOptimized (rootElement, styleMap, parentSelector = '') {
712
+ if (!rootElement) return
713
+
714
+ // 处理样式映射
715
+ try {
716
+ for (const [selector, data] of styleMap.entries()) {
717
+ const currentSelector = parentSelector ? `${parentSelector} ${selector}` : selector
718
+
719
+ try {
720
+ // 查找匹配的元素
721
+ const elements = Array.from(rootElement.querySelectorAll(currentSelector))
722
+
723
+ if (!elements.length) continue
724
+
725
+ // 应用当前层级样式
726
+ if (data.styles) {
727
+ elements.forEach(element => {
728
+ if (!element) return
729
+
730
+ // 应用每个样式属性
731
+ Object.entries(data.styles).forEach(([property, value]) => {
732
+ try {
733
+ element.style.setProperty(property, value, 'important')
734
+ } catch (err) {
735
+ console.warn(`设置样式失败: ${property}=${value}`, err)
736
+ }
737
+ })
738
+ })
739
+ }
740
+
741
+ // 处理子层级
742
+ if (data.children && data.children.size > 0) {
743
+ elements.forEach(element => {
744
+ if (element) {
745
+ this.applyChildStylesOptimized(element, data.children, currentSelector)
746
+ }
747
+ })
748
+ }
749
+ } catch (err) {
750
+ console.warn(`处理选择器失败: ${currentSelector}`, err)
751
+ continue
752
+ }
753
+ }
754
+ } catch (err) {}
755
+ }
756
+ },
757
+ beforeMount () {
758
+ if (this.useOssForImg) {
759
+ this.uploadParams.resUploadMode = 'oss'
760
+ }
761
+ },
762
+ mounted () {
763
+ },
764
+ }
765
+
766
+ </script>
767
+
768
+ <style scoped lang="less">
769
+ .inputsDiv {
770
+ display: flex;
771
+ justify-content: space-between;
772
+
773
+ .inputsDivItem {
774
+ display: flex;
775
+ align-items: center;
776
+ padding: 0 4px;
777
+ white-space: nowrap;
778
+
779
+ .inputsDivItemLabel {
780
+ padding: 0 4px;
781
+ }
782
+ }
783
+ }
784
+
785
+ .tdNoBorder {
786
+ border-left: 1px solid #000;
787
+ border-right: 1px solid #000;
788
+ padding: 8px;
789
+ }
790
+
791
+ .tdWithBorder {
792
+ border: 1px solid #000;
793
+ padding: 8px;
794
+ }
795
+
796
+ .tdWithNoTopBorder {
797
+ border-top-style: none;
798
+ border-left: 1px solid #000;
799
+ border-right: 1px solid #000;
800
+ border-bottom: 1px solid #000;
801
+ padding: 8px;
802
+ }
803
+
804
+ .grid-content {
805
+ border-radius: 4px;
806
+ min-height: 36px;
807
+ text-align: center;
808
+ color: #fff;
809
+ background-color: #606266;
810
+ }
811
+
812
+ .bg-purple {
813
+ background: #9254de;
814
+ }
815
+
816
+ .bg-purple-light {
817
+ background: #b37feb;
818
+ }
819
+
820
+ .flexItem {
821
+ border-radius: 8px;
822
+ height: 100%;
823
+ }
824
+ </style>