vue2-client 1.16.37 → 1.16.39

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 (24) hide show
  1. package/package.json +112 -112
  2. package/src/assets/img/paymentMethod/icon1.png +0 -0
  3. package/src/assets/img/paymentMethod/icon2.png +0 -0
  4. package/src/assets/img/paymentMethod/icon3.png +0 -0
  5. package/src/assets/img/paymentMethod/icon4.png +0 -0
  6. package/src/assets/img/paymentMethod/icon5.png +0 -0
  7. package/src/assets/img/paymentMethod/icon6.png +0 -0
  8. package/src/base-client/components/common/HIS/HButtons/HButtons.vue +364 -366
  9. package/src/base-client/components/common/HIS/HForm/HForm.vue +1 -1
  10. package/src/base-client/components/common/HIS/HFormGroup/HFormGroup.vue +120 -120
  11. package/src/base-client/components/common/HIS/HFormGroup/index.js +3 -3
  12. package/src/base-client/components/common/HIS/demo.vue +61 -61
  13. package/src/base-client/components/common/XReport/XReport.vue +18 -9
  14. package/src/base-client/components/common/XReport/XReportHospitalizationDemo.vue +45 -0
  15. package/src/base-client/components/common/XReportGrid/XReportTrGroup.vue +824 -824
  16. package/src/base-client/components/common/XTable/XTable.vue +4 -0
  17. package/src/base-client/components/his/XCharge/testConfig.js +149 -0
  18. package/src/base-client/components/his/XHDescriptions/XHDescriptions.vue +61 -19
  19. package/src/base-client/components/his/XHisEditor/XHisEditor.vue +705 -705
  20. package/src/base-client/components/his/XList/XList.vue +117 -5
  21. package/src/base-client/components/his/threeTestOrders/editor.vue +113 -113
  22. package/src/pages/userInfoDetailManage/ExceptionRecordQuery/index.vue +45 -45
  23. package/src/router/async/router.map.js +132 -129
  24. package/src/services/api/common.js +1 -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>