vue2-client 1.16.59 → 1.16.61

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 (26) hide show
  1. package/package.json +112 -112
  2. package/src/assets/svg/female.svg +1 -1
  3. package/src/assets/svg/male.svg +1 -1
  4. package/src/base-client/components/common/HIS/HButtons/HButtons.vue +6 -17
  5. package/src/base-client/components/common/HIS/HFormGroup/HFormGroup.vue +22 -4
  6. package/src/base-client/components/common/HIS/HFormGroup/index.js +3 -3
  7. package/src/base-client/components/common/HIS/HFormTable/HFormTable.vue +379 -379
  8. package/src/base-client/components/common/HIS/HTab/HTab.vue +9 -15
  9. package/src/base-client/components/common/HIS/demo.vue +61 -61
  10. package/src/base-client/components/common/XCollapse/XCollapse.vue +708 -461
  11. package/src/base-client/components/common/XFormGroup/XFormGroup.vue +11 -2
  12. package/src/base-client/components/common/XFormTable/demo.vue +46 -4
  13. package/src/base-client/components/common/XInput/XInput.vue +147 -147
  14. package/src/base-client/components/common/XReport/print.js +186 -186
  15. package/src/base-client/components/common/XTable/XTable.vue +1610 -1610
  16. package/src/base-client/components/common/XTimeline/XTimeline.vue +454 -454
  17. package/src/base-client/components/his/XHDescriptions/XHDescriptions.vue +95 -1
  18. package/src/base-client/components/his/XHisEditor/XHisEditor.vue +705 -705
  19. package/src/base-client/components/his/XList/XList.vue +829 -829
  20. package/src/base-client/components/his/XSidebar/XSidebar.vue +22 -31
  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 +129 -129
  24. package/src/utils/map-utils.js +47 -47
  25. package/src-base-client/components/common/XCollapse/XCollapse.vue +0 -0
  26. package/vue.config.js +0 -10
@@ -1,461 +1,708 @@
1
- <template>
2
- <div class="x-collapse-wrapper" :class="wrapperClassObject">
3
- <a-collapse
4
- :activeKey="activeKey"
5
- @change="handleChange"
6
- :bordered="config.bordered || true"
7
- :expand-icon-position="config.expandIconPosition || 'right'"
8
- :style="config.style || ''"
9
- >
10
- <a-collapse-panel
11
- v-for="(panel, panelIndex) in config.showData"
12
- :key="panelIndex.toString()"
13
- :show-arrow="config.showArrow || false"
14
- :disabled="config.collapsible">
15
- <template #header>
16
- <div class="header-content">
17
- <!-- 新增蓝色圆点图标,根据配置显示 -->
18
- <div
19
- v-if="config.showCircleIcon"
20
- class="blue-circle-icon"
21
- :style="config.circleIconStyle || {}"></div>
22
- <span
23
- class="header-text"
24
- :style="config.titleStyle">
25
- {{ panel.title }}
26
- </span>
27
- <!-- 当有 title2 数据时显示信息项 -->
28
- <template v-if="panel.title2 && panel.title2.length">
29
- <span
30
- v-for="(item, headerIndex) in panel.title2"
31
- :key="headerIndex"
32
- class="info-item"
33
- :style="config.title2Style">
34
- <!-- 根据showTitle是否显示键名 -->
35
- <span v-if="item.showTitle">{{ item.key }}:</span>
36
- <span>{{ item.value }}</span>
37
- </span>
38
- </template>
39
- <!-- 当有 title3 数据时显示时间项(与 title2 一致:支持数组/单项) -->
40
- <template v-if="panel.title3 && Array.isArray(panel.title3) && panel.title3.length">
41
- <span
42
- v-for="(item, t3Index) in panel.title3"
43
- :key="t3Index"
44
- :class="['time-item', { 'time-first': t3Index === 0 }]"
45
- :style="config.title3Style">
46
- <span v-if="item.showTitle">{{ item.key }}:</span>
47
- <span>{{ item.value }}</span>
48
- </span>
49
- </template>
50
- <span
51
- v-else-if="panel.title3"
52
- class="time-item time-first"
53
- :style="config.title3Style">
54
- {{ panel.title3 }}
55
- </span>
56
- <!-- 修改搜索框的显示条件 -->
57
- <a-input-search
58
- v-if="panel.search"
59
- v-model="searchText[panelIndex]"
60
- :placeholder="panel.searchPlace"
61
- class="search-input"
62
- @search="(value) => onSearch(value, panelIndex)"
63
- @click.stop/>
64
- </div>
65
- </template>
66
-
67
- <!-- 新增设置图标,根据配置显示 -->
68
- <template #extra v-if="config.showSettingIcon">
69
- <a-icon
70
- v-if="activeKey.includes(panelIndex.toString())"
71
- :type="config.settingIconType || 'setting'"
72
- class="setting-icon"
73
- @click.stop="handleSettingClick(panel, panelIndex)"/>
74
- </template>
75
-
76
- <!-- 根据类型显示不同内容 -->
77
- <template v-if="panel.type === 'picture'">
78
- <img :src="panel.configName" alt="图片" style="width: 100%; max-width: 500px;"/>
79
- </template>
80
- <template v-else-if="['x-image-report','x-form-table','x-simple-table','x-add-native-form','x-tree-pro', 'x-his-editor', 'x-tab', 'x-form-group', 'x-report', 'x-buttons', 'x-label-select', 'x-conversation', 'x-check-list', 'x-cardSet', 'x-collapse','x-h-descriptions', 'x-sidebar', 'x-list','x-input','x-time-line', 'x-radio', 'x-text-card','x-tree-rows'].includes(panel.type)">
81
- <component
82
- :is="getComponentName(panel.type)"
83
- :ref="`dynamicComponent_${ panel.type }_${ panelIndex }`"
84
- :serverName="panel.serverName || 'af-his'"
85
- :queryParamsName="panel.configName"
86
- :parameter="panel.parameter"
87
- :countVisible="false"
88
- :env="env"
89
- :style="config.componentStyle || ''"
90
- v-bind="panel.attrs || {}"
91
- :class="panel.className"
92
- :ipanelIndex="panelIndex"
93
- @deleteData="deleteData"
94
- @add="add"
95
- @listClick="listClick"
96
- @click="click"
97
- @component-mounted="handleMounted"
98
- @search-complete="handleSearchComplete" />
99
- </template>
100
- </a-collapse-panel>
101
- </a-collapse>
102
- </div>
103
- </template>
104
-
105
- <script>
106
- import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
107
-
108
- export default {
109
- name: 'XCollapse',
110
- components: {
111
- XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable/XFormTable.vue'),
112
- XAddNativeForm: () => import('@vue2-client/base-client/components/common/XAddNativeForm/XAddNativeForm.vue'),
113
- XFormGroup: () => import('@vue2-client/base-client/components/common/XFormGroup/XFormGroup.vue'),
114
- XTreePro: () => import('@vue2-client/base-client/components/common/XTree/XTreePro.vue'),
115
- XHisEditor: () => import('@vue2-client/base-client/components/his/XHisEditor/XHisEditor.vue'),
116
- XTab: () => import('@vue2-client/base-client/components/common/XTab/XTab.vue'),
117
- XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
118
- XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
119
- XLabelSelect: () => import('@vue2-client/base-client/components/common/XLabelSelect/XLabelSelect.vue'),
120
- XConversation: () => import('@vue2-client/base-client/components/common/XConversation/XConversation.vue'),
121
- XCheckList: () => import('@vue2-client/base-client/components/common/XCheckList/XCheckList.vue'),
122
- XCardSet: () => import('@vue2-client/base-client/components/common/XCardSet/XCardSet.vue'),
123
- XCollapse: () => import('@vue2-client/base-client/components/common/XCollapse/XCollapse.vue'),
124
- XHDescriptions: () => import('@vue2-client/base-client/components/his/XHDescriptions/XHDescriptions.vue'),
125
- XImageReport: () => import('@vue2-client/base-client/components/his/XImageReport/XImageReport.vue'),
126
- XSidebar: () => import('@vue2-client/base-client/components/his/XSidebar/XSidebar.vue'),
127
- XList: () => import('@vue2-client/base-client/components/his/XList/XList.vue'),
128
- XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
129
- XTimeLine: () => import('@vue2-client/base-client/components/common/XTimeline/XTimeline.vue'),
130
- XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
131
- XTextCard: () => import('@vue2-client/base-client/components/his/XTextCard/XTextCard.vue'),
132
- XTreeRows: () => import('@vue2-client/base-client/components/his/XTreeRows/XTreeRows.vue'),
133
- XSimpleTable: () => import('@vue2-client/base-client/components/his/XSimpleTable/XSimpleTable.vue')
134
- },
135
- data () {
136
- return {
137
- activeKey: [],
138
- config: {},
139
- configName: '',
140
- searchText: {},
141
- sonInstances: [] // 存储子组件实例
142
- }
143
- },
144
- props: {
145
- // 环境
146
- env: {
147
- type: String,
148
- default: 'prod'
149
- },
150
- // json名
151
- queryParamsName: {
152
- type: Object,
153
- default: null
154
- },
155
- parameter: {
156
- type: Object,
157
- default: () => {
158
- return {}
159
- }
160
- }
161
- },
162
- created () {
163
- this.getData(this.queryParamsName, this.parameter)
164
- },
165
- beforeDestroy () {},
166
- computed: {
167
- // 基于 $attrs 的样式类开关(与 XHDescriptions.vue 思路一致)
168
- wrapperClassObject () {
169
- const attrs = this.$attrs || {}
170
- const classes = {}
171
- const booleanStyleKeys = [
172
- 'style1'
173
- ]
174
- booleanStyleKeys.forEach(key => {
175
- const val = attrs[key]
176
- const truthy = val === true || val === '' || val === 'true'
177
- if (truthy) classes[`xcollapse-${key}`] = true
178
- })
179
- const size = attrs.size
180
- if (size && typeof size === 'string') classes[`xcollapse-size-${size}`] = true
181
- return classes
182
- }
183
- },
184
- methods: {
185
- handleMounted (instance) {
186
- this.sonInstances.push(instance)
187
- },
188
- handleSearchComplete ({ hasMatch, panelIndex }) {
189
- if (hasMatch && !this.activeKey.includes(panelIndex.toString())) {
190
- // 只展开包含搜索结果的面板
191
- this.activeKey = [panelIndex.toString()]
192
- }
193
- },
194
- getComponentName (componentName) {
195
- return componentName
196
- },
197
- add (data) {
198
- this.$emit('add', data)
199
- },
200
- deleteData (data) {
201
- this.$emit('deleteData', data)
202
- },
203
- listClick (data) {
204
- this.$emit('listClick', data)
205
- },
206
- click (data) {
207
- this.$emit('click', data)
208
- },
209
- getConfigByName (componentName) {
210
- const refKey = `dynamicComponent_${componentName}`
211
- return this.$refs[refKey]
212
- },
213
- // xTreeRow 组件搜素功能
214
- searchTreeRows (title) {
215
- this.$nextTick(() => {
216
- const instances = this.sonInstances
217
- instances.forEach(comp => {
218
- try {
219
- comp.searchTitleToggle?.(title)
220
- } catch (e) {
221
- console.error('调用失败:', e)
222
- }
223
- })
224
- })
225
- },
226
- async getData (config, parameter) {
227
- this.configName = config
228
- getConfigByName(config, 'af-his', res => {
229
- this.config = res
230
- console.warn(this.config)
231
- runLogic(res.mainLogic, parameter, 'af-his').then(result => {
232
- this.config.showData = result
233
- console.log('result', result)
234
- // 更具timeType更改时间类型
235
- if (this.config.timeType && this.config.timeType === '.') {
236
- this.config.showData.forEach(panel => {
237
- // if (panel.title3) {
238
- // panel.title3 = this.convertToCustomFormat(panel.title3)
239
- // }
240
- })
241
- this.$forceUpdate()
242
- }
243
- const shouldCollapseAll = this.config.collapseAllByDefault || false
244
- // 初始化展开状态
245
- this.activeKey = this.config.showData.map((_, i) => i.toString())
246
- // 初始化关闭所有折叠面板
247
- if (shouldCollapseAll) {
248
- setTimeout(() => {
249
- this.activeKey = [] // 关闭所有面板
250
- }, 0.00001)
251
- }
252
- })
253
- })
254
- },
255
- refreshXCollapse () {
256
- this.getData(this.queryParamsName, this.parameter)
257
- },
258
- convertToCustomFormat (dateString) {
259
- // 创建一个新的 Date 对象
260
- const date = new Date(dateString)
261
- // 获取年、月、日
262
- const year = date.getFullYear()
263
- const month = date.getMonth() + 1 // 月份从0开始,所以需要加1
264
- const day = date.getDate()
265
- // 返回格式化后的字符串
266
- return `${year}.${month}.${day}`
267
- },
268
- handleChange (keys) {
269
- this.activeKey = keys
270
- // console.log(this.activeKey)
271
- },
272
- onSearch (value, panelIndex) {
273
- this.$emit('searchChange', { value: value, panelIndex: panelIndex })
274
- },
275
- handleSettingClick (panel, panelIndex) {
276
- this.$emit('settingClick', { panel, panelIndex })
277
- }
278
- },
279
- watch: {
280
- queryParamsName: {
281
- handler (newValue) {
282
- this.getData(newValue, this.parameter)
283
- },
284
- deep: true
285
- }
286
- }
287
- }
288
- </script>
289
-
290
- <style scoped lang="less">
291
- .header-content {
292
- display: flex;
293
- align-items: center;
294
- gap: 24px;
295
- white-space: nowrap;
296
- overflow: hidden;
297
- }
298
-
299
- .header-text {
300
- margin-right: 16px;
301
- font-size: 16px;
302
- font-weight: 800;
303
- flex-shrink: 0;
304
- }
305
-
306
- .info-item {
307
- display: inline-flex;
308
- align-items: center;
309
- gap: 4px;
310
- font-size: 12px;
311
- color: #888888;
312
- flex-shrink: 0;
313
- }
314
-
315
- .time-item {
316
- margin-left: auto;
317
- text-align: right;
318
- flex-shrink: 0;
319
- }
320
-
321
- :deep(.ant-collapse-header) {
322
- position: relative;
323
- border-bottom: v-bind('config.showLine ? "1px solid #000000" : "none"');
324
- align-items: center !important;
325
- background-color: #ffffff;
326
- }
327
-
328
- :deep(.ant-collapse-header-text) {
329
- flex: 1;
330
- }
331
-
332
- :deep(.ant-collapse-content > .ant-collapse-content-box) {
333
- padding: 0;
334
- }
335
-
336
- :deep(.ant-card-body) {
337
- padding: 8px;
338
- }
339
- .search-input {
340
- margin-left: auto;
341
- width: 100%;
342
- }
343
- :deep(.ant-collapse-item-disabled > .ant-collapse-header) {
344
- cursor: default !important;
345
- }
346
-
347
- /* 新增样式 */
348
- .blue-circle-icon {
349
- width: 12px;
350
- height: 12px;
351
- border-radius: 6px;
352
- background: #3362da;
353
- margin-right: 8px;
354
- flex-shrink: 0;
355
- }
356
-
357
- .setting-icon {
358
- font-size: 16px;
359
- cursor: pointer;
360
- display: inline-flex;
361
- align-items: center;
362
- justify-content: center;
363
- height: 100%;
364
- line-height: 1;
365
- vertical-align: middle;
366
- margin-top: -20px; /* 微调偏移量(根据实际情况调整) */
367
- }
368
-
369
- :deep(.ant-collapse-extra) {
370
- display: flex !important;
371
- align-items: center;
372
- }
373
-
374
- .configurable-area {
375
- padding: 16px;
376
- min-height: 100px;
377
- border: 1px dashed #d9d9d9;
378
- border-radius: 4px;
379
- background-color: #fafafa;
380
- }
381
-
382
- .empty-hint {
383
- color: #999;
384
- text-align: center;
385
- margin: 20px 0;
386
- }
387
-
388
- // 基于根容器类进行样式整合:x-collapse-wrapper.xcollapse-style1
389
- .x-collapse-wrapper {
390
- &.xcollapse-style1 {
391
- height: 1185px;
392
- .header-content {
393
- gap: 6px; // 圆点与title1更紧凑的基础间距
394
- }
395
- .blue-circle-icon {
396
- margin-right: 0px !important; // 图标与标题更紧凑
397
- }
398
- .header-text {
399
- font-family: "Source Han Sans";
400
- font-size: 18px;
401
- font-weight: 700;
402
- line-height: normal;
403
- letter-spacing: 0em;
404
- color: #313131;
405
- // 额外拉开与 title2 的距离(在小gap基础上单独增大)
406
- margin-right: 25px !important;
407
- }
408
-
409
- .info-item {
410
- font-family: "Source Han Sans";
411
- font-size: 18px;
412
- font-weight: 700;
413
- line-height: normal;
414
- text-align: right;
415
- color: #313131;
416
- letter-spacing: 0em
417
- }
418
-
419
- .time-item {
420
- font-family: "Source Han Sans";
421
- font-size: 18px;
422
- font-weight: 400;
423
- line-height: normal;
424
- text-align: right;
425
- color: #313131;
426
- letter-spacing: 0em;
427
- }
428
-
429
- // 让每个面板成为独立卡片:去掉 antd 默认的分隔线,增加间距与圆角
430
- :deep(.ant-collapse) {
431
- background: transparent;
432
- border: 0;
433
- }
434
-
435
- :deep(.ant-collapse > .ant-collapse-item) {
436
- width: 100%;
437
- height: 410px;
438
- margin: 16px 0; // 面板之间留白
439
- border-radius: 6px; // 分离的圆角
440
- overflow: hidden; // 保持圆角
441
- background: #FFFFFF; // 独立白底
442
- box-sizing: border-box;
443
- border: 1px solid #E5E9F0; // 每个面板自身边框
444
- }
445
-
446
- :deep(.ant-collapse > .ant-collapse-item:first-child) { margin-top: 0; }
447
- :deep(.ant-collapse > .ant-collapse-item:last-child) { margin-bottom: 0; }
448
-
449
- :deep(.ant-collapse-content > .ant-collapse-content-box) { background: #FFFFFF; }
450
-
451
- // 表头:恢复合适的上下内边距,去掉顶部额外空白
452
- :deep(.ant-collapse-header) {
453
- background: #FFFFFF;
454
- padding: 12px 16px; // 恢复上下内边距
455
- align-items: center;
456
- color: #262626;
457
- border-bottom: none !important; // 移除标题处下边线,避免与外边框连为一体
458
- }
459
- }
460
- }
461
- </style>
1
+ <template>
2
+ <div class="x-collapse-wrapper" :class="wrapperClassObject">
3
+ <div class="collapse-content-wrapper" :class="{ 'with-pagination': shouldShowPagination }">
4
+ <a-collapse
5
+ :activeKey="activeKey"
6
+ @change="handleChange"
7
+ :bordered="config.bordered || true"
8
+ :expand-icon-position="config.expandIconPosition || 'right'"
9
+ :style="config.style || ''"
10
+ >
11
+ <a-collapse-panel
12
+ v-for="(panel, panelIndex) in pagedPanels"
13
+ :key="panelIndex.toString()"
14
+ :show-arrow="config.showArrow || false"
15
+ :disabled="config.collapsible">
16
+ <template #header>
17
+ <div class="header-content">
18
+ <!-- 新增蓝色圆点图标,根据配置显示 -->
19
+ <div
20
+ v-if="config.showCircleIcon"
21
+ class="blue-circle-icon"
22
+ :style="config.circleIconStyle || {}"></div>
23
+ <span
24
+ class="header-text"
25
+ :style="config.titleStyle">
26
+ {{ getPanelTitle(panel) }}
27
+ </span>
28
+ <!-- 当有 title2 数据时显示信息项 -->
29
+ <template v-if="panel.title2 && panel.title2.length">
30
+ <span
31
+ v-for="(item, headerIndex) in panel.title2"
32
+ :key="headerIndex"
33
+ class="info-item"
34
+ :style="config.title2Style">
35
+ <!-- 根据showTitle是否显示键名 -->
36
+ <span v-if="item.showTitle">{{ item.key }};</span>
37
+ <span>{{ item.value }}</span>
38
+ </span>
39
+ </template>
40
+ <!-- 当有 title3 数据时显示时间项(与 title2 一致:支持数组/单项) -->
41
+ <template v-if="panel.title3 && Array.isArray(panel.title3) && panel.title3.length">
42
+ <span
43
+ v-for="(item, t3Index) in panel.title3"
44
+ :key="t3Index"
45
+ :class="['time-item', { 'time-first': t3Index === 0 }]"
46
+ :style="config.title3Style">
47
+ <span v-if="item.showTitle">{{ item.key }}:</span>
48
+ <span>{{ item.value }}</span>
49
+ </span>
50
+ </template>
51
+ <span
52
+ v-else-if="panel.title3"
53
+ class="time-item time-first"
54
+ :style="config.title3Style">
55
+ {{ panel.title3 }}
56
+ </span>
57
+ <!-- 修改搜索框的显示条件 -->
58
+ <a-input-search
59
+ v-if="panel.search"
60
+ v-model="searchText[panelIndex]"
61
+ :placeholder="panel.searchPlace"
62
+ class="search-input"
63
+ @search="(value) => onSearch(value, panelIndex)"
64
+ @click.stop/>
65
+ </div>
66
+ </template>
67
+
68
+ <!-- 新增设置图标,根据配置显示 -->
69
+ <template #extra v-if="config.showSettingIcon">
70
+ <a-icon
71
+ v-if="activeKey.includes(panelIndex.toString())"
72
+ :type="config.settingIconType || 'setting'"
73
+ class="setting-icon"
74
+ @click.stop="handleSettingClick(panel, panelIndex)"/>
75
+ </template>
76
+
77
+ <!-- 根据类型显示不同内容 -->
78
+ <template v-if="panel.type === 'picture'">
79
+ <img :src="panel.configName" alt="图片" style="width: 100%; max-width: 500px;"/>
80
+ </template>
81
+ <template v-else-if="['x-image-report','x-form-table','x-simple-table','x-add-native-form','x-tree-pro', 'x-his-editor', 'x-tab', 'x-form-group', 'x-report', 'x-buttons', 'x-label-select', 'x-conversation', 'x-check-list', 'x-cardSet', 'x-collapse','x-h-descriptions', 'x-sidebar', 'x-list','x-input','x-time-line', 'x-radio', 'x-text-card','x-tree-rows'].includes(panel.type)">
82
+ <component
83
+ :is="getComponentName(panel.type)"
84
+ :ref="`dynamicComponent_${ panel.type }_${ panelIndex }`"
85
+ :serverName="panel.serverName || 'af-his'"
86
+ :queryParamsName="panel.configName"
87
+ :parameter="panel.parameter"
88
+ :countVisible="false"
89
+ :env="env"
90
+ :style="config.componentStyle || ''"
91
+ v-bind="panel.attrs || {}"
92
+ :class="panel.className"
93
+ :ipanelIndex="panelIndex"
94
+ @deleteData="deleteData"
95
+ @add="add"
96
+ @listClick="listClick"
97
+ @click="click"
98
+ @component-mounted="handleMounted"
99
+ @search-complete="handleSearchComplete" />
100
+ </template>
101
+ </a-collapse-panel>
102
+ </a-collapse>
103
+ <div v-if="shouldShowPagination" class="xcollapse-pagination">
104
+ <div class="pagination-extras">
105
+ <a-button-group size="small">
106
+ <a-button icon="vertical-right" :disabled="paginationCurrent === 1" @click="goFirstPage"/>
107
+ <a-pagination
108
+ :current="paginationCurrent"
109
+ :pageSize="paginationPageSize"
110
+ :total="paginationTotal"
111
+ :showSizeChanger="false"
112
+ show-less-items
113
+ @change="onPageChange"/>
114
+ <a-button icon="vertical-left" :disabled="paginationCurrent === maxPage" @click="goLastPage"/>
115
+ </a-button-group>
116
+ <span class="pagination-info">共 {{ maxPage }} 页, {{ paginationTotal }} 条</span>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </template>
122
+
123
+ <script>
124
+ import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
125
+
126
+ export default {
127
+ name: 'XCollapse',
128
+ components: {
129
+ XFormTable: () => import('@vue2-client/base-client/components/common/XFormTable/XFormTable.vue'),
130
+ XAddNativeForm: () => import('@vue2-client/base-client/components/common/XAddNativeForm/XAddNativeForm.vue'),
131
+ XFormGroup: () => import('@vue2-client/base-client/components/common/XFormGroup/XFormGroup.vue'),
132
+ XTreePro: () => import('@vue2-client/base-client/components/common/XTree/XTreePro.vue'),
133
+ XHisEditor: () => import('@vue2-client/base-client/components/his/XHisEditor/XHisEditor.vue'),
134
+ XTab: () => import('@vue2-client/base-client/components/common/XTab/XTab.vue'),
135
+ XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
136
+ XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
137
+ XLabelSelect: () => import('@vue2-client/base-client/components/common/XLabelSelect/XLabelSelect.vue'),
138
+ XConversation: () => import('@vue2-client/base-client/components/common/XConversation/XConversation.vue'),
139
+ XCheckList: () => import('@vue2-client/base-client/components/common/XCheckList/XCheckList.vue'),
140
+ XCardSet: () => import('@vue2-client/base-client/components/common/XCardSet/XCardSet.vue'),
141
+ XCollapse: () => import('@vue2-client/base-client/components/common/XCollapse/XCollapse.vue'),
142
+ XHDescriptions: () => import('@vue2-client/base-client/components/his/XHDescriptions/XHDescriptions.vue'),
143
+ XImageReport: () => import('@vue2-client/base-client/components/his/XImageReport/XImageReport.vue'),
144
+ XSidebar: () => import('@vue2-client/base-client/components/his/XSidebar/XSidebar.vue'),
145
+ XList: () => import('@vue2-client/base-client/components/his/XList/XList.vue'),
146
+ XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
147
+ XTimeLine: () => import('@vue2-client/base-client/components/common/XTimeline/XTimeline.vue'),
148
+ XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
149
+ XTextCard: () => import('@vue2-client/base-client/components/his/XTextCard/XTextCard.vue'),
150
+ XTreeRows: () => import('@vue2-client/base-client/components/his/XTreeRows/XTreeRows.vue'),
151
+ XSimpleTable: () => import('@vue2-client/base-client/components/his/XSimpleTable/XSimpleTable.vue')
152
+ },
153
+ data () {
154
+ return {
155
+ activeKey: [],
156
+ config: {},
157
+ configName: '',
158
+ searchText: {},
159
+ sonInstances: [], // 存储子组件实例
160
+ paginationCurrent: 1,
161
+ paginationPageSize: 5,
162
+ lastParams: {}, // 缓存最近一次非分页参数
163
+ paginationTotal: 0, // 后端返回的 total(仅分页模式使用)
164
+ }
165
+ },
166
+ props: {
167
+ // 环境
168
+ env: {
169
+ type: String,
170
+ default: 'prod'
171
+ },
172
+ // json名
173
+ queryParamsName: {
174
+ type: Object,
175
+ default: null
176
+ },
177
+ parameter: {
178
+ type: Object,
179
+ default: () => {
180
+ return {}
181
+ }
182
+ }
183
+ },
184
+ created () {
185
+ this.getData(this.queryParamsName, this.parameter)
186
+ },
187
+ beforeDestroy () {},
188
+ computed: {
189
+ // 基于 $attrs 的样式类开关(与 XHDescriptions.vue 思路一致)
190
+ wrapperClassObject () {
191
+ const attrs = this.$attrs || {}
192
+ const classes = {}
193
+ const booleanStyleKeys = [
194
+ 'style1'
195
+ ]
196
+ booleanStyleKeys.forEach(key => {
197
+ const val = attrs[key]
198
+ const truthy = val === true || val === '' || val === 'true'
199
+ if (truthy) classes[`xcollapse-${key}`] = true
200
+ })
201
+ const size = attrs.size
202
+ if (size && typeof size === 'string') classes[`xcollapse-size-${size}`] = true
203
+ if (this.shouldShowPagination) classes['with-pagination'] = true
204
+ return classes
205
+ },
206
+ enablePagination () { return !!this.config?.pagination },
207
+ shouldShowPagination () { return this.enablePagination },
208
+ maxPage () {
209
+ const size = Number(this.paginationPageSize || 1)
210
+ const total = this.paginationTotal
211
+ return Math.max(1, Math.ceil(total / size))
212
+ },
213
+ pagedPanels () {
214
+ const showData = this.config?.showData || []
215
+ if (this.enablePagination) return showData
216
+ const start = (this.paginationCurrent - 1) * this.paginationPageSize
217
+ const end = start + this.paginationPageSize
218
+ return showData.slice(start, end)
219
+ }
220
+ },
221
+ methods: {
222
+ handleMounted (instance) {
223
+ this.sonInstances.push(instance)
224
+ },
225
+ handleSearchComplete ({ hasMatch, panelIndex }) {
226
+ if (hasMatch && !this.activeKey.includes(panelIndex.toString())) {
227
+ // 只展开包含搜索结果的面板
228
+ this.activeKey = [panelIndex.toString()]
229
+ }
230
+ },
231
+ getComponentName (componentName) {
232
+ return componentName
233
+ },
234
+ getPanelTitle (panel) {
235
+ // 兼容数据为纯对象数组(无 title 字段)时的显示
236
+ if (panel && panel.title) return panel.title
237
+ if (panel && panel.name) return panel.name
238
+ if (panel && panel.code) return panel.code
239
+ return ''
240
+ },
241
+ add (data) {
242
+ this.$emit('add', data)
243
+ },
244
+ deleteData (data) {
245
+ this.$emit('deleteData', data)
246
+ },
247
+ listClick (data) {
248
+ this.$emit('listClick', data)
249
+ },
250
+ click (data) {
251
+ this.$emit('click', data)
252
+ },
253
+ getConfigByName (componentName) {
254
+ const refKey = `dynamicComponent_${componentName}`
255
+ return this.$refs[refKey]
256
+ },
257
+ // xTreeRow 组件搜素功能
258
+ searchTreeRows (title) {
259
+ this.$nextTick(() => {
260
+ const instances = this.sonInstances
261
+ instances.forEach(comp => {
262
+ try {
263
+ comp.searchTitleToggle?.(title)
264
+ } catch (e) {
265
+ console.error('调用失败:', e)
266
+ }
267
+ })
268
+ })
269
+ },
270
+ async getData (config, parameter) {
271
+ this.configName = config
272
+ getConfigByName(config, 'af-his', res => {
273
+ // 合并配置,保留已有的 pagination 等开关
274
+ const original = this.config || {}
275
+ this.config = { ...original, ...res }
276
+ // 同步分页初值(仅当配置里提供时)
277
+ const p = this.config?.pagination || null
278
+ if (p && p.pageSize) this.paginationPageSize = Number(p.pageSize)
279
+ // 只在初始化时设置默认页码,避免每次重置
280
+ if (p && p.current && this.paginationCurrent === 1) {
281
+ this.paginationCurrent = Number(p.current)
282
+ }
283
+
284
+ // 组合请求参数,启用分页时传递页码与页大小
285
+ const baseParams = (parameter && Object.keys(parameter).length ? parameter : this.lastParams) || {}
286
+ this.lastParams = { ...baseParams }
287
+ const requestParams = { ...baseParams }
288
+ if (this.enablePagination) {
289
+ requestParams.page = this.paginationCurrent
290
+ requestParams.pageSize = this.paginationPageSize
291
+ }
292
+
293
+ runLogic(res.mainLogic, requestParams, 'af-his').then(result => {
294
+ let showData = []
295
+ let totalCount = 0
296
+
297
+ if (this.enablePagination) {
298
+ // 启用分页:后端固定返回 { total, data: [], page, pageSize }
299
+ showData = result.data
300
+ this.paginationTotal = Number(result.total) || 0
301
+ totalCount = this.paginationTotal
302
+ } else {
303
+ // 未启用分页:直接取数组
304
+ showData = Array.isArray(result && result.data) ? result.data : (Array.isArray(result) ? result : [])
305
+ totalCount = showData.length
306
+ }
307
+
308
+ // 赋值
309
+ this.$set(this.config, 'showData', showData)
310
+ this.$set(this.config, 'totalCount', Number(totalCount))
311
+
312
+ // 后端分页边界保护:当前页超界时回退到最后一页并重拉
313
+ if (this.enablePagination) {
314
+ const total = Number(this.config.totalCount || 0)
315
+ const size = Number(this.paginationPageSize || 1)
316
+ const maxPage = Math.max(1, Math.ceil(total / size))
317
+ if (this.paginationCurrent > maxPage) {
318
+ this.paginationCurrent = maxPage
319
+ this.getData(this.queryParamsName, this.parameter)
320
+ return
321
+ }
322
+ }
323
+
324
+ // 更具timeType更改时间类型
325
+ if (this.config.timeType && this.config.timeType === '.') {
326
+ this.config.showData.forEach(panel => {
327
+ // if (panel.title3) {
328
+ // panel.title3 = this.convertToCustomFormat(panel.title3)
329
+ // }
330
+ })
331
+ this.$forceUpdate()
332
+ }
333
+ // 默认展开当前页的所有面板
334
+ this.activeKey = (this.config.showData || []).map((_, i) => i.toString())
335
+ // 初始化关闭所有折叠面板
336
+ const shouldCollapseAll = this.config.collapseAllByDefault || false
337
+ if (shouldCollapseAll) {
338
+ setTimeout(() => {
339
+ this.activeKey = [] // 关闭所有面板
340
+ }, 0.00001)
341
+ }
342
+ })
343
+ })
344
+ },
345
+ onPageChange (page) {
346
+ this.paginationCurrent = page
347
+ // 后端分页:重新拉数;前端分页:仅切片
348
+ this.getData(this.queryParamsName, this.lastParams)
349
+ },
350
+ goFirstPage () {
351
+ if (this.paginationCurrent === 1) return
352
+ this.onPageChange(1)
353
+ },
354
+ goLastPage () {
355
+ if (this.paginationCurrent === this.maxPage) return
356
+ this.onPageChange(this.maxPage)
357
+ },
358
+ refreshXCollapse () {
359
+ this.getData(this.queryParamsName, this.parameter)
360
+ },
361
+ convertToCustomFormat (dateString) {
362
+ // 创建一个新的 Date 对象
363
+ const date = new Date(dateString)
364
+ // 获取年、月、日
365
+ const year = date.getFullYear()
366
+ const month = date.getMonth() + 1 // 月份从0开始,所以需要加1
367
+ const day = date.getDate()
368
+ // 返回格式化后的字符串
369
+ return `${year}.${month}.${day}`
370
+ },
371
+ handleChange (keys) {
372
+ this.activeKey = keys
373
+ // console.log(this.activeKey)
374
+ },
375
+ onSearch (value, panelIndex) {
376
+ this.$emit('searchChange', { value: value, panelIndex: panelIndex })
377
+ },
378
+ handleSettingClick (panel, panelIndex) {
379
+ this.$emit('settingClick', { panel, panelIndex })
380
+ }
381
+ },
382
+ watch: {
383
+ queryParamsName: {
384
+ handler (newValue) {
385
+ this.getData(newValue, this.parameter)
386
+ },
387
+ deep: true
388
+ }
389
+ }
390
+ }
391
+ </script>
392
+
393
+ <style scoped lang="less">
394
+ .x-collapse-wrapper { position: relative; display: flex; flex-direction: column; min-height: 100%; height: 100%; }
395
+ .collapse-content-wrapper { flex: 1; min-height: 0; overflow: auto; position: relative; max-height: 79vh; }
396
+ .collapse-content-wrapper.with-pagination { padding-bottom: 0px; }
397
+ .header-content {
398
+ display: flex;
399
+ align-items: center;
400
+ justify-content: flex-start; // 左对齐,避免圆点被拉开
401
+ gap: 8px; // 圆点与标题的基础间距
402
+ white-space: nowrap;
403
+ overflow: hidden;
404
+ flex: 1; // 占满可用宽度,便于空间分配
405
+ }
406
+
407
+ .header-text {
408
+ margin-right: 14.17px;
409
+ font-size: 16px;
410
+ font-weight: 800;
411
+ flex-shrink: 0;
412
+ }
413
+
414
+ .info-item {
415
+ display: inline-flex;
416
+ align-items: center;
417
+ gap: 8px;
418
+ font-size: 12px;
419
+ color: #888888;
420
+ flex-shrink: 0;
421
+ }
422
+
423
+ .time-item {
424
+ margin-left: 12px;
425
+ text-align: right;
426
+ flex-shrink: 0;
427
+ }
428
+
429
+ :deep(.ant-collapse-header) {
430
+ display: flex;
431
+ position: relative;
432
+ border-bottom: v-bind('config.showLine ? "1px solid #000000" : "none"');
433
+ align-items: center !important;
434
+ background-color: #ffffff;
435
+ padding: 12px 16px !important; /* 确保头部有足够的内边距 */
436
+ min-height: 50px; /* 设置头部最小高度 */
437
+ }
438
+
439
+ :deep(.ant-collapse-header-text) {
440
+ flex: 1;
441
+ }
442
+
443
+ :deep(.ant-collapse-content > .ant-collapse-content-box) {
444
+ padding: 16px !important; /* 确保内容区域有足够的内边距 */
445
+ }
446
+
447
+ .search-input {
448
+ margin-left: 12px;
449
+ width: auto;
450
+ max-width: 40%;
451
+ }
452
+
453
+ // 右侧块定位:首个 info-item 或 time-item 推到右侧
454
+ .header-content .info-item:first-of-type { margin-left: auto; }
455
+ .header-content .time-item:first-of-type { margin-left: auto; }
456
+
457
+ // 右侧块内部兄弟间距统一
458
+ .info-item + .info-item { margin-left: 12px; }
459
+ :deep(.ant-collapse-item-disabled > .ant-collapse-header) {
460
+ cursor: default !important;
461
+ }
462
+
463
+ /* 新增样式 */
464
+ .blue-circle-icon {
465
+ width: 12px;
466
+ height: 12px;
467
+ border-radius: 6px;
468
+ background: #3362DA;
469
+ margin: 6px;
470
+ flex-shrink: 0;
471
+ }
472
+
473
+ .setting-icon {
474
+ font-size: 16px;
475
+ cursor: pointer;
476
+ display: inline-flex;
477
+ align-items: center;
478
+ justify-content: center;
479
+ height: 100%;
480
+ line-height: 1;
481
+ vertical-align: middle;
482
+ margin-top: -20px; /* 微调偏移量(根据实际情况调整) */
483
+ }
484
+
485
+ :deep(.ant-collapse-extra) {
486
+ display: flex !important;
487
+ align-items: center;
488
+ }
489
+
490
+ .configurable-area {
491
+ //padding: 16px;
492
+ min-height: 100px;
493
+ border: 1px dashed #d9d9d9;
494
+ border-radius: 4px;
495
+ background-color: #fafafa;
496
+ }
497
+
498
+ .empty-hint {
499
+ color: #999;
500
+ text-align: center;
501
+ margin: 20px 0;
502
+ }
503
+
504
+ .loading-message {
505
+ text-align: center;
506
+ padding: 20px;
507
+ color: #666;
508
+ background: #f5f5f5;
509
+ border-radius: 4px;
510
+ margin: 10px 0;
511
+ }
512
+
513
+ .empty-state {
514
+ text-align: center;
515
+ padding: 40px 20px;
516
+ color: #999;
517
+ background: #fafafa;
518
+ border-radius: 4px;
519
+ margin: 10px 0;
520
+ flex: 1;
521
+ display: flex;
522
+ align-items: center;
523
+ justify-content: center;
524
+ }
525
+
526
+ /* 分页组件:固定在组件容器底部(不影响全局布局) */
527
+ .with-pagination { padding-bottom: 0; }
528
+ .xcollapse-pagination { /* 粘在视口底部,宽度随容器 */
529
+ position: sticky;
530
+ bottom: 0;
531
+ display: flex;
532
+ justify-content: center;
533
+ padding: 12px 0;
534
+ background: #fff;
535
+ border-top: 1px solid #f0f0f0;
536
+ }
537
+ .pagination-extras { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
538
+ .pagination-extras .ant-btn-group { gap: 8px; }
539
+ .pagination-extras .ant-btn-group + .pagination-info { margin-left: 9px; }
540
+ .pagination-info {
541
+ color: #5D5C5C;
542
+ font-family: 'Source Han Sans', sans-serif;
543
+ font-size: 16px;
544
+ font-weight: normal;
545
+ line-height: normal;
546
+ letter-spacing: 0em;
547
+ font-feature-settings: "kern" on;
548
+ }
549
+
550
+ /* 自定义分页按钮样式 */
551
+ :deep(.pagination-extras .ant-btn) {
552
+ font-family: 'Source Han Sans', sans-serif;
553
+ font-size: 16px;
554
+ font-weight: normal;
555
+ line-height: normal;
556
+ letter-spacing: 0em;
557
+ font-feature-settings: "kern" on;
558
+ color: #5D5C5C;
559
+ border-radius: 4px;
560
+ border: 1px solid #D8D8D8;
561
+ background: #FFFFFF;
562
+ box-sizing: border-box;
563
+ margin-right: 8px;
564
+ }
565
+
566
+ :deep(.pagination-extras .ant-btn:last-child) {
567
+ margin-right: 0;
568
+ }
569
+
570
+ /* 直达首页末页按钮样式 */
571
+ :deep(.pagination-extras .ant-btn[icon="vertical-left"]),
572
+ :deep(.pagination-extras .ant-btn[icon="vertical-right"]) {
573
+ position: absolute;
574
+ left: 24px;
575
+ top: 24px;
576
+ width: 24px;
577
+ height: 24px;
578
+ transform: rotate(180deg);
579
+ border-radius: 4px;
580
+ opacity: 1;
581
+ background: #FFFFFF;
582
+ box-sizing: border-box;
583
+ border: 1px solid #D8D8D8;
584
+ }
585
+
586
+ /* 上一页下一页按钮样式 */
587
+ :deep(.ant-pagination .ant-pagination-prev),
588
+ :deep(.ant-pagination .ant-pagination-next) {
589
+ width: 24px;
590
+ height: 24px;
591
+ line-height: 24px;
592
+ border-radius: 4px;
593
+ opacity: 1;
594
+ background: #FFFFFF;
595
+ box-sizing: border-box;
596
+ border: 1px solid #D8D8D8;
597
+ }
598
+
599
+ /* 页码选择按钮样式 */
600
+ :deep(.ant-pagination .ant-pagination-item) {
601
+ width: 36px;
602
+ height: 24px;
603
+ border-radius: 4px;
604
+ border: 1px solid #D8D8D8;
605
+ background: #FFFFFF;
606
+ box-sizing: border-box;
607
+ font-family: 'Source Han Sans', sans-serif;
608
+ font-size: 16px;
609
+ font-weight: normal;
610
+ line-height: normal;
611
+ letter-spacing: 0em;
612
+ font-feature-settings: "kern" on;
613
+ color: #5D5C5C;
614
+ margin-right: 8px;
615
+ }
616
+
617
+ :deep(.ant-pagination .ant-pagination-item:last-child) {
618
+ margin-right: 0;
619
+ }
620
+
621
+ /* 当前页码样式 */
622
+ :deep(.ant-pagination .ant-pagination-item-active) {
623
+ background: #FFFFFF;
624
+ border: 1px solid #1890ff;
625
+ color: #1890ff;
626
+ }
627
+
628
+ /* 禁用状态样式 */
629
+ :deep(.pagination-extras .ant-btn:disabled) {
630
+ opacity: 0.5;
631
+ cursor: not-allowed;
632
+ }
633
+
634
+ /* 移除所有可能导致截断的固定高度 */
635
+ :deep(.ant-collapse-item) .ant-collapse-content .ant-collapse-content-box > * {
636
+ max-height: none !important; /* 移除可能的最大高度限制 */
637
+ height: auto !important; /* 使用自动高度 */
638
+ }
639
+
640
+ // 基于根容器类进行样式整合:x-collapse-wrapper.xcollapse-style1
641
+ .x-collapse-wrapper {
642
+ &.xcollapse-style1 {
643
+ height: auto; /* 允许根据内容自适应高度,避免滚动被限制 */
644
+ min-height: 600px;
645
+ .blue-circle-icon { margin: 0 !important; }
646
+ .header-text {
647
+ font-family: "Source Han Sans";
648
+ font-size: 16px;
649
+ font-weight: 700;
650
+ line-height: normal;
651
+ letter-spacing: 0em;
652
+ color: #313131;
653
+ margin-right: 0 !important;
654
+ }
655
+
656
+ .info-item {
657
+ font-family: "Source Han Sans";
658
+ font-size: 16px;
659
+ font-weight: 700;
660
+ line-height: normal;
661
+ text-align: right;
662
+ color: #313131;
663
+ margin-left: 28.14px;
664
+ letter-spacing: 0em
665
+ }
666
+
667
+ .time-item {
668
+ font-family: "Source Han Sans";
669
+ font-size: 16px;
670
+ font-weight: 400;
671
+ line-height: normal;
672
+ text-align: right;
673
+ color: #313131;
674
+ letter-spacing: 0em;
675
+ }
676
+
677
+ // 让每个面板成为独立卡片:去掉 antd 默认的分隔线,增加间距与圆角
678
+ :deep(.ant-collapse) {
679
+ background: transparent;
680
+ border: 0;
681
+ }
682
+
683
+ :deep(.ant-collapse > .ant-collapse-item) {
684
+ width: 564px;
685
+ min-height: auto;
686
+ height: auto;
687
+ margin: 3px 10px 26px 12px; // 面板之间留白
688
+ background: #FFFFFF; // 独立白底
689
+ box-sizing: border-box;
690
+ border: 1px solid #E5E9F0; // 每个面板自身边框
691
+ }
692
+
693
+ :deep(.ant-collapse > .ant-collapse-item:first-child) { margin-top: 0; }
694
+ :deep(.ant-collapse > .ant-collapse-item:last-child) { margin-bottom: 0; }
695
+
696
+ :deep(.ant-collapse-content > .ant-collapse-content-box) { background: #FFFFFF; }
697
+
698
+ // 表头:恢复合适的上下内边距,去掉顶部额外空白
699
+ :deep(.ant-collapse-header) {
700
+ background: #FFFFFF;
701
+ padding: 0px 0px;
702
+ align-items: center;
703
+ height: 39px;
704
+ border-bottom: none !important; // 移除标题处下边线,避免与外边框连为一体
705
+ }
706
+ }
707
+ }
708
+ </style>