vue2-client 1.18.39 → 1.18.41

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 (103) hide show
  1. package/.eslintrc.js +90 -90
  2. package/Components.md +60 -60
  3. package/docs/index.md +30 -30
  4. package/docs//350/257/267/346/261/202/345/267/245/345/205/267/344/275/277/347/224/250/350/257/264/346/230/216.md +353 -0
  5. package/index.js +31 -31
  6. package/jest-transform-stub.js +8 -8
  7. package/jest.setup.js +7 -7
  8. package/package.json +1 -1
  9. package/src/assets/img/querySlotDemo.svg +15 -15
  10. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  11. package/src/base-client/components/common/CitySelect/index.js +3 -3
  12. package/src/base-client/components/common/CitySelect/index.md +109 -109
  13. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  14. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  15. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  16. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  17. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  18. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  19. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  20. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  21. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  22. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  23. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  24. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  25. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  26. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  27. package/src/base-client/components/common/Tree/index.js +2 -2
  28. package/src/base-client/components/common/Upload/index.js +3 -3
  29. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  30. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  31. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  32. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  33. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  34. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  35. package/src/base-client/components/common/XDescriptions/index.md +322 -322
  36. package/src/base-client/components/common/XForm/index.md +178 -178
  37. package/src/base-client/components/common/XFormTable/demo.vue +2 -4
  38. package/src/base-client/components/common/XReport/print.js +186 -186
  39. package/src/base-client/components/common/XReportGrid/XReportTrGroup.vue +1 -1
  40. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  41. package/src/base-client/components/common/XStepView/index.js +3 -3
  42. package/src/base-client/components/common/XStepView/index.md +31 -31
  43. package/src/base-client/components/common/XTable/XTable.vue +1715 -1715
  44. package/src/base-client/components/common/XTable/XTableWrapper.vue +786 -769
  45. package/src/base-client/components/common/XTable/index.md +255 -255
  46. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  47. package/src/base-client/plugins/Config.js +19 -19
  48. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  49. package/src/components/Charts/Bar.vue +62 -62
  50. package/src/components/Charts/ChartCard.vue +134 -134
  51. package/src/components/Charts/Liquid.vue +67 -67
  52. package/src/components/Charts/MiniArea.vue +39 -39
  53. package/src/components/Charts/MiniBar.vue +39 -39
  54. package/src/components/Charts/MiniProgress.vue +75 -75
  55. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  56. package/src/components/Charts/Radar.vue +68 -68
  57. package/src/components/Charts/RankList.vue +77 -77
  58. package/src/components/Charts/TagCloud.vue +113 -113
  59. package/src/components/Charts/TransferBar.vue +64 -64
  60. package/src/components/Charts/Trend.vue +82 -82
  61. package/src/components/Charts/chart.less +12 -12
  62. package/src/components/Charts/smooth.area.less +13 -13
  63. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  64. package/src/components/NumberInfo/index.js +3 -3
  65. package/src/components/NumberInfo/index.less +54 -54
  66. package/src/components/NumberInfo/index.md +43 -43
  67. package/src/components/card/ChartCard.vue +79 -79
  68. package/src/components/chart/Bar.vue +60 -60
  69. package/src/components/chart/MiniArea.vue +67 -67
  70. package/src/components/chart/MiniBar.vue +59 -59
  71. package/src/components/chart/MiniProgress.vue +57 -57
  72. package/src/components/chart/Radar.vue +80 -80
  73. package/src/components/chart/RankingList.vue +60 -60
  74. package/src/components/chart/Trend.vue +79 -79
  75. package/src/components/chart/index.less +9 -9
  76. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  77. package/src/components/input/IInput.vue +66 -66
  78. package/src/components/menu/SideMenu.vue +75 -75
  79. package/src/components/menu/menu.js +273 -273
  80. package/src/components/tool/AStepItem.vue +60 -60
  81. package/src/composables/demo/UseRequestDemo.vue +175 -0
  82. package/src/composables/index.js +6 -0
  83. package/src/composables/useGlobalLoading.js +206 -0
  84. package/src/composables/usePost.js +221 -0
  85. package/src/layouts/CommonLayout.vue +56 -56
  86. package/src/layouts/header/HeaderNotice.vue +177 -177
  87. package/src/lib.js +1 -1
  88. package/src/mock/extend/index.js +84 -84
  89. package/src/mock/goods/index.js +108 -108
  90. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  91. package/src/pages/system/dictionary/index.vue +44 -44
  92. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  93. package/src/pages/system/monitor/operLog/index.vue +37 -37
  94. package/src/services/api/cas.js +79 -79
  95. package/src/services/api/common.js +11 -11
  96. package/src/services/api/restTools.js +36 -46
  97. package/src/store/modules/setting.js +119 -119
  98. package/src/utils/authority-utils.js +85 -85
  99. package/src/utils/errorCode.js +6 -6
  100. package/src/utils/map-utils.js +47 -47
  101. package/src/utils/request.js +103 -0
  102. package/vue.config.js +9 -4
  103. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
@@ -1,769 +1,786 @@
1
- <template>
2
- <!-- 当expandedGrid存在时渲染带有expandedRowRender的表格 -->
3
- <s-table
4
- v-if="tableContext.expandedGrid"
5
- ref="expandableTable"
6
- :id="tableContext.uniqueId"
7
- :alert="true"
8
- :columns="realTableColumns"
9
- :data="loadData()"
10
- :rowKey="tableContext.rowKey"
11
- :showSummary="tableContext.showSummary"
12
- :rowSelection="tableContext.rowSelection"
13
- :scroll="{ x: tableContext.scrollXWidth, y: tableContext.scrollYHeight }"
14
- :showPagination="tableContext.showPagination"
15
- :hidePagination="tableContext.simpleMode"
16
- :customPagination="tableContext.customPagination"
17
- :showSelected="!tableContext.simpleMode"
18
- :default-page-size="defaultPageSize"
19
- :pageMaxSize="tableContext.pageMaxSize"
20
- :setScrollYHeight="tableContext.setScrollYHeight"
21
- :selectRowMode="tableContext.selectRowMode"
22
- :size="tableContext.tableSize"
23
- :components="components"
24
- :rowStyleFunction="tableContext.rowStyleFunction"
25
- @beforeDataChange="beforeDataChange"
26
- @expand="onExpand"
27
- @rowClick="handleRowClick"
28
- @rowDblClick="handleRowDblClick"
29
- >
30
- <template
31
- v-for="(item, c_index) in realTableColumns"
32
- :slot="item.dataIndex"
33
- slot-scope="text, record, index">
34
- <template v-if="tableContext.isEditMode && getFromItem(item.dataIndex,text, record, index)">
35
- <x-form-item
36
- class="innerTable"
37
- :style="{ paddingTop: 0, paddingBottom: 0, paddingLeft: 0, paddingRight: 0 }"
38
- :form="record"
39
- :attr="getFromItem(item.dataIndex,text, record, index)"
40
- :service-name="tableContext.serviceName"
41
- mode="新增/修改"
42
- :env="tableContext.env"
43
- :setForm="(obj)=>tableContext.setForm(record,obj)"
44
- @rowChoose="(row, attr, callback) => tableContext.rowChoose(row, attr, callback, record)"
45
- @x-form-item-emit-func="(func, attr, value) => handleFormItemEvent(func, attr, value, record, index)"
46
- :showLabel="false"
47
- :key="'editRow-' + c_index"
48
- :row-index="index"
49
- />
50
- </template>
51
- <span v-else-if="item.slotType === 'rate'" :key="'rate-' + c_index">
52
- <x-rate
53
- :value="text"
54
- :disabled="true"
55
- :allow-half="item.allowHalf"
56
- :icon="item.rateIcon"
57
- :max-count="item.maxCount"
58
- style="zoom:0.9"/>
59
- </span>
60
- <span v-else-if="item.slotType === 'index'" :key="'index-' + c_index">
61
- {{ index + 1 }}
62
- </span>
63
- <CustomFuncCel
64
- :text="text"
65
- :record="record"
66
- :index="index"
67
- :item="item"
68
- :localDataSource="activeTable?.localDataSource"
69
- v-else-if="item.slotCustomFunction"
70
- :key="'customJs-' + c_index"></CustomFuncCel>
71
- <span v-else-if="item.slotType === 'link'" :key="'link-' + c_index">
72
- <a @click="tableContext.columnClick(item.dataIndex,text,record)">{{ text }}</a>
73
- </span>
74
- <span v-else-if="item.slotType === 'gotoUserDetail'" :key="'gotoUserDetail-' + c_index">
75
- <a @click="tableContext.gotoUserDetail(item.dataIndex,text,record)">{{ text }}</a>
76
- </span>
77
- <span v-else-if="['ellipsis','fixed'].includes(item.slotType)" :key="'ellipsis-' + c_index">
78
- <a-tooltip placement="topLeft">
79
- <template slot="title">
80
- <span>{{ text === '' ? '--' : text }}</span>
81
- </template>
82
- {{ text === '' ? '--' : text }}
83
- </a-tooltip>
84
- </span>
85
- <span v-else-if="item.slotType === 'badge'" :key="'badge-' + c_index">
86
- <x-badge
87
- :service-name="tableContext.serviceName"
88
- :env="tableContext.env"
89
- v-if="text !== null && text !== undefined"
90
- :badge-key="item.slotKeyMap"
91
- :value="text"/>
92
- </span>
93
- <span v-else-if="item.slotType === 'date'" :key="'date-' + c_index">
94
- {{ format(text, 'yyyy-MM-dd') }}
95
- </span>
96
- <span v-else-if="item.slotType === 'dateTime'" :key="'dateTime-' + c_index">
97
- {{ format(text, 'yyyy-MM-dd hh:mm:ss') }}
98
- </span>
99
- <span v-else-if="item.slotType === 'towDecimal'" :key="'towDecimal-' + c_index">
100
- {{ numberFormat(text, 2) }}
101
- </span>
102
- <span v-else-if="item.slotType === 'fourDecimal'" :key="'fourDecimal-' + c_index">
103
- {{ numberFormat(text, 4) }}
104
- </span>
105
- <span v-else-if="item.slotType === 'int'" :key="'int-' + c_index">
106
- {{ numberFormat(text, 0) }}
107
- </span>
108
- <span v-else-if="item.slotType === 'action'" :key="'action-' + c_index">
109
- <template v-if="item.actionArr && item.actionArr.length > 0">
110
- <a-dropdown placement="bottomCenter" :getPopupContainer=" triggerNode => { return triggerNode.parentNode } ">
111
- <a class="ant-dropdown-link" @click="e => e.preventDefault()">
112
- {{ item.scopedSlots?.customRender || item.slotValue }} <a-icon type="down"/>
113
- </a>
114
- <a-menu slot="overlay" style="min-width: 60px">
115
- <a-menu-item
116
- v-for="(action_item, actionIndex) in item.actionArr"
117
- :key="actionIndex"
118
- v-show="!action_item.customFunction || tableContext.customFunctionShow(action_item.customFunction,record,c_index)">
119
- <a
120
- style="text-align: center"
121
- @click="tableContext.action(record, item.dataIndex, action_item.func)"
122
- >{{ action_item.text }}</a>
123
- </a-menu-item>
124
- </a-menu>
125
- </a-dropdown>
126
- </template>
127
- <template v-if="!item.actionArr || item.actionArr.length === 0">
128
- <a @click="tableContext.action(record, item.dataIndex,'action', index)">{{ item.slotValue }}</a>
129
- </template>
130
- </span>
131
- </template>
132
- <template slot="expandedRowRender" slot-scope="record">
133
- <slot
134
- name="expandedRowRender"
135
- :selectedRowKeys="tableContext.selectedRowKeys"
136
- :selectedRows="tableContext.selectedRows"
137
- :record="record"
138
- ></slot>
139
- </template>
140
- <template slot="footer">
141
- <slot
142
- name="footer"
143
- :selectedRowKeys="tableContext.selectedRowKeys"
144
- :selectedRows="tableContext.selectedRows"></slot>
145
- </template>
146
- <template slot="fixedfooter">
147
- <slot name="fixedfooter"></slot>
148
- </template>
149
- </s-table>
150
-
151
- <!-- 当expandedGrid不存在时渲染不带expandedRowRender的表格 -->
152
- <s-table
153
- v-else
154
- ref="simpleTable"
155
- :id="tableContext.uniqueId"
156
- :alert="true"
157
- :columns="realTableColumns"
158
- :data="loadData()"
159
- :rowKey="tableContext.rowKey"
160
- :showSummary="tableContext.showSummary"
161
- :rowSelection="tableContext.rowSelection"
162
- :scroll="{ x: tableContext.scrollXWidth, y: tableContext.scrollYHeight }"
163
- :showPagination="tableContext.showPagination"
164
- :hidePagination="tableContext.simpleMode"
165
- :customPagination="tableContext.customPagination"
166
- :showSelected="!tableContext.simpleMode"
167
- :default-page-size="defaultPageSize"
168
- :pageMaxSize="tableContext.pageMaxSize"
169
- :setScrollYHeight="tableContext.setScrollYHeight"
170
- :selectRowMode="tableContext.selectRowMode"
171
- :size="tableContext.tableSize"
172
- :components="components"
173
- :rowStyleFunction="tableContext.rowStyleFunction"
174
- @rowClick="handleRowClick"
175
- @rowDblClick="handleRowDblClick"
176
- @beforeDataChange="beforeDataChange"
177
- >
178
- <template
179
- v-for="(item, c_index) in realTableColumns"
180
- :slot="item.dataIndex"
181
- slot-scope="text, record, index">
182
- <template v-if="tableContext.isEditMode && getFromItem(item.dataIndex,text, record, index)">
183
- <x-form-item
184
- class="innerTable"
185
- :style="{ paddingTop: 0, paddingBottom: 0, paddingLeft: 0, paddingRight: 0 }"
186
- :form="record"
187
- :attr="getFromItem(item.dataIndex,text, record, index)"
188
- :service-name="tableContext.serviceName"
189
- mode="新增/修改"
190
- :env="tableContext.env"
191
- :setForm="(obj)=>tableContext.setForm(record,obj)"
192
- @rowChoose="(row, attr, callback) => tableContext.rowChoose(row, attr, callback, record)"
193
- @x-form-item-emit-func="(func, attr, value) => handleFormItemEvent(func, attr, value, record, index)"
194
- :showLabel="false"
195
- :key="'editRow-' + c_index"
196
- :row-index="index"
197
- />
198
- </template>
199
- <span v-else-if="item.slotType === 'rate'" :key="'rate-' + c_index">
200
- <x-rate
201
- :value="text"
202
- :disabled="true"
203
- :allow-half="item.allowHalf"
204
- :icon="item.rateIcon"
205
- :max-count="item.maxCount"
206
- style="zoom:0.9"/>
207
- </span>
208
- <span v-else-if="item.slotType === 'index'" :key="'index-' + c_index">
209
- {{ index + 1 }}
210
- </span>
211
- <CustomFuncCel
212
- :text="text"
213
- :record="record"
214
- :index="index"
215
- :item="item"
216
- :localDataSource="activeTable?.localDataSource"
217
- v-else-if="item.slotCustomFunction"
218
- :key="'customJs-' + c_index"></CustomFuncCel>
219
- <span v-else-if="item.slotType === 'link'" :key="'link-' + c_index">
220
- <a @click="tableContext.columnClick(item.dataIndex,text,record)">{{ text }}</a>
221
- </span>
222
- <span v-else-if="item.slotType === 'gotoUserDetail'" :key="'gotoUserDetail-' + c_index">
223
- <a @click="tableContext.gotoUserDetail(item.dataIndex,text,record)">{{ text }}</a>
224
- </span>
225
- <span v-else-if="['ellipsis','fixed'].includes(item.slotType)" :key="'ellipsis-' + c_index">
226
- <!-- <ellipsis :length="item.slotValue" tooltip>{{ text === '' ? '--' : text }}</ellipsis> -->
227
- <a-tooltip placement="topLeft">
228
- <template slot="title">
229
- <span>{{ text === '' ? '--' : text }}</span>
230
- </template>
231
- {{ text === '' ? '--' : text }}
232
- </a-tooltip>
233
- </span>
234
- <span v-else-if="item.slotType === 'progress'" :key="'progress-' + c_index">
235
- <!-- websocket-id (配置名称-字段名称-用户id-数据行id) -->
236
- <x-web-socket-progress
237
- v-if="item.webSocket"
238
- :key="`${tableContext.requestParameters?.pageNo || 1}-${item.dataIndex}-${record[tableContext.rowKey] || index}`"
239
- :websocket-id="`${queryParamsName}-${item.dataIndex}-${currUser.id}-${record[tableContext.rowKey] || index}`"
240
- :initial-value="text || 0"
241
- @progress-updated="(data) => handleProgressUpdated(data, record, item.dataIndex)"
242
- />
243
- <!-- 表格进度组件 -->
244
- <a-progress v-else :percent="text" status="active" style="padding-right: 20px;" />
245
- </span>
246
- <span v-else-if="item.slotType === 'badge'" :key="'badge-' + c_index">
247
- <x-badge
248
- :service-name="tableContext.serviceName"
249
- :env="tableContext.env"
250
- v-if="text !== null && text !== undefined"
251
- :badge-key="item.slotKeyMap"
252
- :value="text"/>
253
- </span>
254
- <span v-else-if="item.slotType === 'date'" :key="'date-' + c_index">
255
- {{ format(text, 'yyyy-MM-dd') }}
256
- </span>
257
- <span v-else-if="item.slotType === 'dateTime'" :key="'dateTime-' + c_index">
258
- {{ format(text, 'yyyy-MM-dd hh:mm:ss') }}
259
- </span>
260
- <span v-else-if="item.slotType === 'towDecimal'" :key="'towDecimal-' + c_index">
261
- {{ numberFormat(text, 2) }}
262
- </span>
263
- <span v-else-if="item.slotType === 'fourDecimal'" :key="'fourDecimal-' + c_index">
264
- {{ numberFormat(text, 4) }}
265
- </span>
266
- <span v-else-if="item.slotType === 'int'" :key="'int-' + c_index">
267
- {{ numberFormat(text, 0) }}
268
- </span>
269
- <span v-else-if="item.slotType === 'action'" :key="'action-' + c_index">
270
- <template v-if="item.actionArr && item.actionArr.length > 0">
271
- <a-dropdown
272
- placement="bottomRight"
273
- overlayClassName="x-table-action-dropdown"
274
- :overlayStyle="{
275
- whiteSpace: 'nowrap',
276
- maxWidth: '250px',
277
- overflow: 'hidden',
278
- textOverflow: 'ellipsis'
279
- }"
280
- >
281
- <a class="ant-dropdown-link" @click="e => e.preventDefault()">
282
- {{ item.scopedSlots?.customRender || item.slotValue }} <a-icon type="down"/>
283
- </a>
284
- <a-menu slot="overlay" style="min-width: 60px">
285
- <a-menu-item
286
- v-for="(action_item, actionIndex) in item.actionArr"
287
- :key="actionIndex"
288
- v-show="!action_item.customFunction || tableContext.customFunctionShow(action_item.customFunction,record,c_index)">
289
- <a
290
- style="text-align: center"
291
- @click="tableContext.action(record, item.dataIndex, action_item.func)"
292
- >{{ action_item.text }}</a>
293
- </a-menu-item>
294
- </a-menu>
295
- </a-dropdown>
296
- </template>
297
- <template v-if="!item.actionArr || item.actionArr.length === 0">
298
- <a @click="tableContext.action(record, item.dataIndex,'action', index)">{{ item.slotValue }}</a>
299
- </template>
300
- </span>
301
- </template>
302
- <template slot="footer">
303
- <slot
304
- name="footer"
305
- :selectedRowKeys="tableContext.selectedRowKeys"
306
- :selectedRows="tableContext.selectedRows"></slot>
307
- </template>
308
- <template slot="fixedfooter">
309
- <slot name="fixedfooter"></slot>
310
- </template>
311
- </s-table>
312
- </template>
313
-
314
- <script>
315
- import { Ellipsis, STable } from '@vue2-client/components'
316
- import { formatDate } from '@vue2-client/utils/util'
317
- import XBadge from '@vue2-client/base-client/components/common/XBadge'
318
- import XFormItem from '@vue2-client/base-client/components/common/XForm/XFormItem'
319
- import CustomFuncCel from '@vue2-client/base-client/components/common/XTable/CustomFuncCel.vue'
320
- import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
321
- import XRate from '@vue2-client/base-client/components/common/XRate/index.vue'
322
- import XWebSocketProgress from '@vue2-client/base-client/components/common/XWebSocketProgress'
323
- import VueDraggableResizable from 'vue-draggable-resizable'
324
- import { mapState } from 'vuex'
325
-
326
- export default {
327
- name: 'XTableWrapper',
328
- components: {
329
- Ellipsis,
330
- STable,
331
- XBadge,
332
- XFormItem,
333
- XRate,
334
- CustomFuncCel,
335
- XWebSocketProgress,
336
- VueDraggableResizable
337
- },
338
- data () {
339
- return {
340
- isDragging: false, // 添加拖拽状态标记
341
- // eslint-disable-next-line vue/no-reserved-keys
342
- _rafId: null // 添加requestAnimationFrame ID标记
343
- }
344
- },
345
- computed: {
346
- ...mapState('account', { currUser: 'user' }),
347
- localDataSource () {
348
- return this.activeTable?.localDataSource
349
- },
350
- // 获取当前活动的表格实例
351
- activeTable () {
352
- return this.tableContext.expandedGrid ? this.$refs.expandableTable : this.$refs.simpleTable
353
- },
354
- realTableColumns () {
355
- // 1. 先过滤列并设置 ellipsis 属性
356
- const filteredColumns = this.tableContext.tableColumns
357
- .filter(item => item.slotType !== 'action' || !this.disableAction)
358
- .map((item) => {
359
- // 设置 ellipsis 属性
360
- if (['ellipsis', 'badge', 'towDecimal', 'date', 'dateTime', 'fourDecimal', 'int', 'customJs'].includes(item.slotType)) {
361
- item.ellipsis = true
362
- }
363
- return item
364
- })
365
-
366
- // 2. 分组:左固定、普通、右固定
367
- const leftFixedColumns = []
368
- const normalColumns = []
369
- const rightFixedColumns = []
370
-
371
- filteredColumns.forEach(col => {
372
- if (col.fixed === 'left') {
373
- leftFixedColumns.push(col)
374
- } else if (col.fixed === 'right') {
375
- rightFixedColumns.push(col)
376
- } else {
377
- normalColumns.push(col)
378
- }
379
- })
380
-
381
- // 3. 按顺序合并:左固定 + 普通 + 右固定
382
- return [...leftFixedColumns, ...normalColumns, ...rightFixedColumns]
383
- },
384
- components () {
385
- return {
386
- header: {
387
- cell: (h, props, children) => {
388
- const { key, ...restProps } = props
389
- // 此处的this.realTableColumns 是定义的table的表头属性变量
390
- const col = this.realTableColumns.find((col) => {
391
- const k = col.dataIndex || col.key
392
- return k === key
393
- })
394
- if (!col || !col.width || col.slotType === 'action') {
395
- if (children) {
396
- return h('th', { ...restProps }, [...children])
397
- } else {
398
- return h('th', { ...restProps })
399
- }
400
- }
401
-
402
- // 创建一个防止点击排序的容器
403
- const preventSortProps = {
404
- style: {
405
- position: 'absolute',
406
- right: 0,
407
- top: 0,
408
- width: '20px',
409
- height: '100%',
410
- zIndex: 10
411
- },
412
- on: {
413
- mousedown: (e) => {
414
- e.stopPropagation()
415
- e.preventDefault()
416
- this.isDragging = true
417
- }
418
- }
419
- }
420
-
421
- const dragProps = {
422
- key: col.dataIndex || col.key,
423
- class: 'table-draggable-handle',
424
- attrs: {
425
- w: 10,
426
- x: col.width,
427
- z: 1,
428
- axis: 'x',
429
- draggable: true,
430
- resizable: false,
431
- },
432
- on: {
433
- dragging: (x, y) => {
434
- // 使用requestAnimationFrame优化性能,减少卡顿
435
- if (!this._rafId) {
436
- this._rafId = requestAnimationFrame(() => {
437
- col.width = Math.max(x, 50) // 设置最小列宽为50px
438
- this.isDragging = true // 设置拖拽状态
439
- this._rafId = null
440
- })
441
- }
442
- },
443
- mousedown: (e) => {
444
- // 阻止事件冒泡,防止触发排序
445
- e.stopPropagation()
446
- e.preventDefault()
447
- this.isDragging = true // 设置拖拽状态
448
- },
449
- dragstop: () => {
450
- // 清除可能存在的动画帧请求
451
- if (this._rafId) {
452
- cancelAnimationFrame(this._rafId)
453
- this._rafId = null
454
- }
455
-
456
- // 拖拽结束时,延迟重置拖拽状态,以防止排序被触发
457
- setTimeout(() => {
458
- this.isDragging = false
459
- }, 100)
460
- }
461
- },
462
- nativeOn: {
463
- mousedown: (e) => {
464
- e.stopPropagation()
465
- e.preventDefault()
466
- this.isDragging = true // 设置拖拽状态
467
- }
468
- }
469
- }
470
-
471
- const preventSort = h('div', preventSortProps)
472
- const drag = h('vue-draggable-resizable', { ...dragProps })
473
-
474
- // 修改th的点击事件,在拖拽状态下阻止排序
475
- const newRestProps = { ...restProps }
476
- const originalClick = newRestProps.on && newRestProps.on.click
477
- if (originalClick) {
478
- newRestProps.on = {
479
- ...newRestProps.on,
480
- click: (e) => {
481
- if (this.isDragging) {
482
- e.stopPropagation()
483
- e.preventDefault()
484
- return
485
- }
486
- originalClick(e)
487
- }
488
- }
489
- }
490
-
491
- if (children) {
492
- return h('th', { ...newRestProps, class: 'resize-table-th' }, [...children, preventSort, drag])
493
- } else {
494
- return h('th', { ...newRestProps, class: 'resize-table-th' }, [preventSort, drag])
495
- }
496
- },
497
- }
498
- }
499
- }
500
- },
501
- props: {
502
- // 查询配置文件名
503
- queryParamsName: {
504
- type: String,
505
- default: () => {
506
- return ''
507
- }
508
- },
509
- loadSelectedData: {
510
- type: Boolean,
511
- required: false,
512
- default: false
513
- },
514
- disableAction: {
515
- type: Boolean,
516
- default: false
517
- },
518
- // 默认查询的当页行数
519
- defaultPageSize: {
520
- type: Number,
521
- default: 10
522
- },
523
- },
524
- inject: ['tableContext'],
525
- methods: {
526
- handleRowClick (record) {
527
- this.$emit('rowClick', record)
528
- },
529
- handleRowDblClick (record) {
530
- this.$emit('rowDblClick', record)
531
- },
532
- beforeDataChange (record) {
533
- this.$emit('beforeDataChange', record)
534
- },
535
- onExpand (expanded, record) {
536
- this.$emit('expand', expanded, record)
537
- },
538
- setLocalDataSource (data) {
539
- this.activeTable?.setLocalDataSource(data)
540
- },
541
- loadData () {
542
- if (this.loadSelectedData) {
543
- return this.loadSelectedDataGen
544
- } else {
545
- return this.tableContext.loadData
546
- }
547
- },
548
- loadSelectedDataGen (requestParameters) {
549
- console.log('loadSelectedDataGen', {
550
- pageNo: requestParameters?.pageNo,
551
- pageSize: requestParameters?.pageSize
552
- })
553
-
554
- const { pageNo = 1, pageSize = 10 } = requestParameters || {}
555
- const startIndex = (pageNo - 1) * pageSize
556
- const endIndex = startIndex + pageSize
557
- const paginatedData = this.tableContext.selectedRows.slice(startIndex, endIndex)
558
-
559
- return new Promise((resolve) => {
560
- resolve({
561
- data: paginatedData,
562
- pageNo,
563
- pageSize,
564
- totalPage: Math.ceil(this.tableContext.selectedRows.length / pageSize),
565
- totalCount: this.tableContext.selectedRows.length
566
- })
567
- })
568
- },
569
- updateSelect (selectedRowKeys, selectedRows) {
570
- this.activeTable?.updateSelect(selectedRowKeys, selectedRows)
571
- },
572
- clearSelected () {
573
- this.activeTable?.clearSelected()
574
- },
575
- refresh (bool) {
576
- this.activeTable?.refresh(bool)
577
- },
578
-
579
- /**
580
- * 格式化日期
581
- * @param date 日期字符串
582
- * @param format 格式化方式
583
- */
584
- format (date, format) {
585
- return formatDate(date, format)
586
- },
587
- /**
588
- * 格式化数字
589
- * @param number string 或者 number
590
- * @param decimalPlaces 小数位数
591
- */
592
- numberFormat (number, decimalPlaces = 2) {
593
- const value = parseFloat(number)
594
- if (!isNaN(value)) {
595
- return value.toFixed(decimalPlaces)
596
- } else {
597
- return ''
598
- }
599
- },
600
- getFromItem (model, text, record, index) {
601
- const aa = this.tableContext.formItems.reduce((acc, item) => {
602
- if (item.type === 'group') {
603
- const foundItem = item.groupItems.find(_item => _item.model === model && _item.editRow)
604
- if (foundItem) {
605
- acc = foundItem
606
- }
607
- } else if (item.model === model && item.editRow) {
608
- acc = item
609
- }
610
- return acc
611
- }, null)
612
- if (aa) {
613
- const tempConfig = JSON.parse(JSON.stringify(aa))
614
- // 如果找到了字段
615
- const ColumnIndex = this.realTableColumns.findIndex(item => item.dataIndex === model)
616
- // 并且表单项是日期框
617
- if (ColumnIndex !== -1 && ['yearPicker', 'monthPicker', 'datePicker', 'rangePicker'].includes(tempConfig.type)) {
618
- // 修改他的列宽
619
- this.realTableColumns[ColumnIndex].width = 220
620
- }
621
- // 如果有检验规则检验是数字
622
- if (ColumnIndex !== -1 && ['number', 'integer', 'float'].includes(tempConfig?.rule?.type)) {
623
- // 修改他的列宽
624
- tempConfig.numberInput = true
625
- }
626
- if (tempConfig.editRowShowFunc) {
627
- if (executeStrFunctionByContext(this.tableContext, tempConfig.editRowShowFunc, [text, record, index, tempConfig])) {
628
- return tempConfig
629
- }
630
- } else {
631
- return tempConfig
632
- }
633
- }
634
- return false
635
- },
636
- handleResizeColumn (w, col) {
637
- col.width = w
638
- },
639
-
640
- /**
641
- * 处理进度更新事件
642
- * @param {Object} data 进度数据
643
- * @param {Object} record 行数据
644
- * @param {String} dataIndex 列字段名
645
- */
646
- handleProgressUpdated (data, record, dataIndex) {
647
- // 更新行数据中的进度值
648
- if (record && dataIndex) {
649
- this.$set(record, dataIndex, data.value)
650
- }
651
- },
652
- /**
653
- * 处理表单项事件,增强事件数据
654
- * @param {String} func 事件函数名
655
- * @param {Object} attr 字段配置
656
- * @param {*} value 字段值
657
- * @param {Object} record 当前行数据
658
- * @param {Number} index 当前行索引
659
- */
660
- handleFormItemEvent (func, attr, value, record, index) {
661
- // 获取表格数据源
662
- const dataSource = this.localDataSource || []
663
-
664
- // 计算下一行的索引和数据
665
- const nextIndex = index + 1
666
- const nextRecord = nextIndex < dataSource.length ? dataSource[nextIndex] : null
667
-
668
- // 构建增强的上下文数据
669
- const enhancedContext = {
670
- func,
671
- attr,
672
- value,
673
- currentRecord: record,
674
- currentIndex: index,
675
- nextRecord,
676
- nextIndex: nextRecord ? nextIndex : null,
677
- tableContext: this.tableContext
678
- }
679
-
680
- // 调用 tableContext 的事件处理方法
681
- if (this.tableContext && typeof this.tableContext.handleFormItemEvent === 'function') {
682
- this.tableContext.handleFormItemEvent(enhancedContext)
683
- }
684
- }
685
- }
686
- }
687
- </script>
688
-
689
- <style scoped lang="less">
690
- /* 精确修复表头空白问题 */
691
- :deep(.ant-table-fixed-left table, .ant-table-fixed-right table) {
692
- width: min-content;
693
- }
694
- :deep(.ant-table-fixed-header .ant-table-scroll .ant-table-header) {
695
- margin-bottom: 0 !important;
696
- overflow-x: hidden !important;
697
- }
698
- /* 统一表头行高和垂直对齐 */
699
- :deep(.ant-table-thead > tr) {
700
- height: 54px !important;
701
- line-height: 54px !important;
702
- }
703
- :deep(.ant-table-thead > tr > th) {
704
- padding: 0 16px !important;
705
- vertical-align: middle !important;
706
- border-bottom: 1px solid #f0f0f0;
707
- }
708
- /* 分页组件不换行 */
709
- :deep(.ant-pagination > li) {
710
- white-space: nowrap;
711
- }
712
- </style>
713
- <style>
714
- /* 多操作下拉菜单边框与阴影 */
715
- .x-table-action-dropdown .ant-dropdown-menu {
716
- border: 1px solid #e8e8e8;
717
- border-radius: 4px;
718
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
719
- }
720
-
721
- /* vue-draggable-resizable 拖动手柄样式 */
722
- .table-draggable-handle {
723
- height: 100% !important;
724
- left: auto !important;
725
- right: 0;
726
- cursor: col-resize;
727
- touch-action: none;
728
- border: none;
729
- position: absolute;
730
- transform: none !important;
731
- bottom: 0;
732
- width: 10px !important; /* 减小到10px */
733
- z-index: 99 !important; /* 提高z-index */
734
- }
735
-
736
- /* 拖动手柄的分隔线 */
737
- .table-draggable-handle::after {
738
- content: "";
739
- position: absolute;
740
- top: 50%;
741
- height: 20px;
742
- width: 2px;
743
- background-color: #e8e8e8;
744
- transition: background-color 0.2s;
745
- transform: translateY(-50%);
746
- right: 0;
747
- }
748
-
749
- /* 悬停效果 - 只改变颜色 */
750
- .table-draggable-handle:hover::after {
751
- background-color: #1890ff;
752
- }
753
-
754
- /* 拖动时的效果 */
755
- .table-draggable-handle:active::after {
756
- background-color: #096dd9;
757
- }
758
-
759
- /* 表头单元格样式 */
760
- .resize-table-th {
761
- position: relative;
762
- overflow: visible !important; /* 确保内容不被裁剪 */
763
- }
764
-
765
- /* 表头单元格悬停时的手柄高亮 */
766
- .resize-table-th:hover .table-draggable-handle::after {
767
- background-color: rgba(24, 144, 255, 0.6);
768
- }
769
- </style>
1
+ <template>
2
+ <!-- 当expandedGrid存在时渲染带有expandedRowRender的表格 -->
3
+ <s-table
4
+ v-if="tableContext.expandedGrid"
5
+ ref="expandableTable"
6
+ :id="tableContext.uniqueId"
7
+ :alert="true"
8
+ :columns="realTableColumns"
9
+ :data="loadData()"
10
+ :rowKey="tableContext.rowKey"
11
+ :showSummary="tableContext.showSummary"
12
+ :rowSelection="tableContext.rowSelection"
13
+ :scroll="{ x: tableContext.scrollXWidth, y: tableContext.scrollYHeight }"
14
+ :showPagination="tableContext.showPagination"
15
+ :hidePagination="tableContext.simpleMode"
16
+ :customPagination="tableContext.customPagination"
17
+ :showSelected="!tableContext.simpleMode"
18
+ :default-page-size="defaultPageSize"
19
+ :pageMaxSize="tableContext.pageMaxSize"
20
+ :setScrollYHeight="tableContext.setScrollYHeight"
21
+ :selectRowMode="tableContext.selectRowMode"
22
+ :size="tableContext.tableSize"
23
+ :components="components"
24
+ :rowStyleFunction="tableContext.rowStyleFunction"
25
+ @beforeDataChange="beforeDataChange"
26
+ @expand="onExpand"
27
+ @rowClick="handleRowClick"
28
+ @rowDblClick="handleRowDblClick"
29
+ >
30
+ <template
31
+ v-for="(item, c_index) in realTableColumns"
32
+ :slot="item.dataIndex"
33
+ slot-scope="text, record, index">
34
+ <template v-if="tableContext.isEditMode && getFromItem(item.dataIndex,text, record, index)">
35
+ <x-form-item
36
+ class="innerTable"
37
+ :style="{ paddingTop: 0, paddingBottom: 0, paddingLeft: 0, paddingRight: 0 }"
38
+ :form="record"
39
+ :attr="getFromItem(item.dataIndex,text, record, index)"
40
+ :service-name="tableContext.serviceName"
41
+ mode="新增/修改"
42
+ :env="tableContext.env"
43
+ :setForm="(obj)=>tableContext.setForm(record,obj)"
44
+ @rowChoose="(row, attr, callback) => tableContext.rowChoose(row, attr, callback, record)"
45
+ @x-form-item-emit-func="(func, attr, value) => handleFormItemEvent(func, attr, value, record, index)"
46
+ :showLabel="false"
47
+ :key="'editRow-' + c_index"
48
+ :row-index="index"
49
+ />
50
+ </template>
51
+ <span v-else-if="item.slotType === 'rate'" :key="'rate-' + c_index">
52
+ <x-rate
53
+ :value="text"
54
+ :disabled="true"
55
+ :allow-half="item.allowHalf"
56
+ :icon="item.rateIcon"
57
+ :max-count="item.maxCount"
58
+ style="zoom:0.9"/>
59
+ </span>
60
+ <span v-else-if="item.slotType === 'index'" :key="'index-' + c_index">
61
+ {{ index + 1 }}
62
+ </span>
63
+ <CustomFuncCel
64
+ :text="text"
65
+ :record="record"
66
+ :index="index"
67
+ :item="item"
68
+ :localDataSource="activeTable?.localDataSource"
69
+ v-else-if="item.slotCustomFunction"
70
+ :key="'customJs-' + c_index"></CustomFuncCel>
71
+ <span v-else-if="item.slotType === 'link'" :key="'link-' + c_index">
72
+ <a @click="tableContext.columnClick(item.dataIndex,text,record)">{{ text }}</a>
73
+ </span>
74
+ <span v-else-if="item.slotType === 'gotoUserDetail'" :key="'gotoUserDetail-' + c_index">
75
+ <a @click="tableContext.gotoUserDetail(item.dataIndex,text,record)">{{ text }}</a>
76
+ </span>
77
+ <span v-else-if="['ellipsis','fixed'].includes(item.slotType)" :key="'ellipsis-' + c_index">
78
+ <a-tooltip placement="topLeft">
79
+ <template slot="title">
80
+ <span>{{ text === '' ? '--' : text }}</span>
81
+ </template>
82
+ {{ text === '' ? '--' : text }}
83
+ </a-tooltip>
84
+ </span>
85
+ <span v-else-if="item.slotType === 'badge'" :key="'badge-' + c_index">
86
+ <x-badge
87
+ :service-name="tableContext.serviceName"
88
+ :env="tableContext.env"
89
+ v-if="text !== null && text !== undefined"
90
+ :badge-key="item.slotKeyMap"
91
+ :value="text"/>
92
+ </span>
93
+ <span v-else-if="item.slotType === 'date'" :key="'date-' + c_index">
94
+ {{ format(text, 'yyyy-MM-dd') }}
95
+ </span>
96
+ <span v-else-if="item.slotType === 'dateTime'" :key="'dateTime-' + c_index">
97
+ {{ format(text, 'yyyy-MM-dd hh:mm:ss') }}
98
+ </span>
99
+ <span v-else-if="item.slotType === 'towDecimal'" :key="'towDecimal-' + c_index">
100
+ {{ numberFormat(text, 2) }}
101
+ </span>
102
+ <span v-else-if="item.slotType === 'fourDecimal'" :key="'fourDecimal-' + c_index">
103
+ {{ numberFormat(text, 4) }}
104
+ </span>
105
+ <span v-else-if="item.slotType === 'int'" :key="'int-' + c_index">
106
+ {{ numberFormat(text, 0) }}
107
+ </span>
108
+ <span v-else-if="item.slotType === 'action'" :key="'action-' + c_index">
109
+ <template v-if="item.actionArr && item.actionArr.length > 0">
110
+ <a-dropdown placement="bottomCenter" :getPopupContainer="getPopupContainer">
111
+ <a class="ant-dropdown-link" @click="e => e.preventDefault()">
112
+ {{ item.scopedSlots?.customRender || item.slotValue }} <a-icon type="down"/>
113
+ </a>
114
+ <a-menu slot="overlay" style="min-width: 60px">
115
+ <a-menu-item
116
+ v-for="(action_item, actionIndex) in item.actionArr"
117
+ :key="actionIndex"
118
+ v-show="!action_item.customFunction || tableContext.customFunctionShow(action_item.customFunction,record,c_index)">
119
+ <a
120
+ style="text-align: center"
121
+ @click="tableContext.action(record, item.dataIndex, action_item.func)"
122
+ >{{ action_item.text }}</a>
123
+ </a-menu-item>
124
+ </a-menu>
125
+ </a-dropdown>
126
+ </template>
127
+ <template v-if="!item.actionArr || item.actionArr.length === 0">
128
+ <a @click="tableContext.action(record, item.dataIndex,'action', index)">{{ item.slotValue }}</a>
129
+ </template>
130
+ </span>
131
+ </template>
132
+ <template slot="expandedRowRender" slot-scope="record">
133
+ <slot
134
+ name="expandedRowRender"
135
+ :selectedRowKeys="tableContext.selectedRowKeys"
136
+ :selectedRows="tableContext.selectedRows"
137
+ :record="record"
138
+ ></slot>
139
+ </template>
140
+ <template slot="footer">
141
+ <slot
142
+ name="footer"
143
+ :selectedRowKeys="tableContext.selectedRowKeys"
144
+ :selectedRows="tableContext.selectedRows"></slot>
145
+ </template>
146
+ <template slot="fixedfooter">
147
+ <slot name="fixedfooter"></slot>
148
+ </template>
149
+ </s-table>
150
+
151
+ <!-- 当expandedGrid不存在时渲染不带expandedRowRender的表格 -->
152
+ <s-table
153
+ v-else
154
+ ref="simpleTable"
155
+ :id="tableContext.uniqueId"
156
+ :alert="true"
157
+ :columns="realTableColumns"
158
+ :data="loadData()"
159
+ :rowKey="tableContext.rowKey"
160
+ :showSummary="tableContext.showSummary"
161
+ :rowSelection="tableContext.rowSelection"
162
+ :scroll="{ x: tableContext.scrollXWidth, y: tableContext.scrollYHeight }"
163
+ :showPagination="tableContext.showPagination"
164
+ :hidePagination="tableContext.simpleMode"
165
+ :customPagination="tableContext.customPagination"
166
+ :showSelected="!tableContext.simpleMode"
167
+ :default-page-size="defaultPageSize"
168
+ :pageMaxSize="tableContext.pageMaxSize"
169
+ :setScrollYHeight="tableContext.setScrollYHeight"
170
+ :selectRowMode="tableContext.selectRowMode"
171
+ :size="tableContext.tableSize"
172
+ :components="components"
173
+ :rowStyleFunction="tableContext.rowStyleFunction"
174
+ @rowClick="handleRowClick"
175
+ @rowDblClick="handleRowDblClick"
176
+ @beforeDataChange="beforeDataChange"
177
+ >
178
+ <template
179
+ v-for="(item, c_index) in realTableColumns"
180
+ :slot="item.dataIndex"
181
+ slot-scope="text, record, index">
182
+ <template v-if="tableContext.isEditMode && getFromItem(item.dataIndex,text, record, index)">
183
+ <x-form-item
184
+ class="innerTable"
185
+ :style="{ paddingTop: 0, paddingBottom: 0, paddingLeft: 0, paddingRight: 0 }"
186
+ :form="record"
187
+ :attr="getFromItem(item.dataIndex,text, record, index)"
188
+ :service-name="tableContext.serviceName"
189
+ mode="新增/修改"
190
+ :env="tableContext.env"
191
+ :setForm="(obj)=>tableContext.setForm(record,obj)"
192
+ @rowChoose="(row, attr, callback) => tableContext.rowChoose(row, attr, callback, record)"
193
+ @x-form-item-emit-func="(func, attr, value) => handleFormItemEvent(func, attr, value, record, index)"
194
+ :showLabel="false"
195
+ :key="'editRow-' + c_index"
196
+ :row-index="index"
197
+ />
198
+ </template>
199
+ <span v-else-if="item.slotType === 'rate'" :key="'rate-' + c_index">
200
+ <x-rate
201
+ :value="text"
202
+ :disabled="true"
203
+ :allow-half="item.allowHalf"
204
+ :icon="item.rateIcon"
205
+ :max-count="item.maxCount"
206
+ style="zoom:0.9"/>
207
+ </span>
208
+ <span v-else-if="item.slotType === 'index'" :key="'index-' + c_index">
209
+ {{ index + 1 }}
210
+ </span>
211
+ <CustomFuncCel
212
+ :text="text"
213
+ :record="record"
214
+ :index="index"
215
+ :item="item"
216
+ :localDataSource="activeTable?.localDataSource"
217
+ v-else-if="item.slotCustomFunction"
218
+ :key="'customJs-' + c_index"></CustomFuncCel>
219
+ <span v-else-if="item.slotType === 'link'" :key="'link-' + c_index">
220
+ <a @click="tableContext.columnClick(item.dataIndex,text,record)">{{ text }}</a>
221
+ </span>
222
+ <span v-else-if="item.slotType === 'gotoUserDetail'" :key="'gotoUserDetail-' + c_index">
223
+ <a @click="tableContext.gotoUserDetail(item.dataIndex,text,record)">{{ text }}</a>
224
+ </span>
225
+ <span v-else-if="['ellipsis','fixed'].includes(item.slotType)" :key="'ellipsis-' + c_index">
226
+ <!-- <ellipsis :length="item.slotValue" tooltip>{{ text === '' ? '--' : text }}</ellipsis> -->
227
+ <a-tooltip placement="topLeft">
228
+ <template slot="title">
229
+ <span>{{ text === '' ? '--' : text }}</span>
230
+ </template>
231
+ {{ text === '' ? '--' : text }}
232
+ </a-tooltip>
233
+ </span>
234
+ <span v-else-if="item.slotType === 'progress'" :key="'progress-' + c_index">
235
+ <!-- websocket-id (配置名称-字段名称-用户id-数据行id) -->
236
+ <x-web-socket-progress
237
+ v-if="item.webSocket"
238
+ :key="`${tableContext.requestParameters?.pageNo || 1}-${item.dataIndex}-${record[tableContext.rowKey] || index}`"
239
+ :websocket-id="`${queryParamsName}-${item.dataIndex}-${currUser.id}-${record[tableContext.rowKey] || index}`"
240
+ :initial-value="text || 0"
241
+ @progress-updated="(data) => handleProgressUpdated(data, record, item.dataIndex)"
242
+ />
243
+ <!-- 表格进度组件 -->
244
+ <a-progress v-else :percent="text" status="active" style="padding-right: 20px;" />
245
+ </span>
246
+ <span v-else-if="item.slotType === 'badge'" :key="'badge-' + c_index">
247
+ <x-badge
248
+ :service-name="tableContext.serviceName"
249
+ :env="tableContext.env"
250
+ v-if="text !== null && text !== undefined"
251
+ :badge-key="item.slotKeyMap"
252
+ :value="text"/>
253
+ </span>
254
+ <span v-else-if="item.slotType === 'date'" :key="'date-' + c_index">
255
+ {{ format(text, 'yyyy-MM-dd') }}
256
+ </span>
257
+ <span v-else-if="item.slotType === 'dateTime'" :key="'dateTime-' + c_index">
258
+ {{ format(text, 'yyyy-MM-dd hh:mm:ss') }}
259
+ </span>
260
+ <span v-else-if="item.slotType === 'towDecimal'" :key="'towDecimal-' + c_index">
261
+ {{ numberFormat(text, 2) }}
262
+ </span>
263
+ <span v-else-if="item.slotType === 'fourDecimal'" :key="'fourDecimal-' + c_index">
264
+ {{ numberFormat(text, 4) }}
265
+ </span>
266
+ <span v-else-if="item.slotType === 'int'" :key="'int-' + c_index">
267
+ {{ numberFormat(text, 0) }}
268
+ </span>
269
+ <span v-else-if="item.slotType === 'action'" :key="'action-' + c_index">
270
+ <template v-if="item.actionArr && item.actionArr.length > 0">
271
+ <a-dropdown
272
+ placement="bottomRight"
273
+ overlayClassName="x-table-action-dropdown"
274
+ :getPopupContainer="getPopupContainer"
275
+ :overlayStyle="{
276
+ whiteSpace: 'nowrap',
277
+ maxWidth: '250px',
278
+ overflow: 'hidden',
279
+ textOverflow: 'ellipsis'
280
+ }"
281
+ >
282
+ <a class="ant-dropdown-link" @click="e => e.preventDefault()">
283
+ {{ item.scopedSlots?.customRender || item.slotValue }} <a-icon type="down"/>
284
+ </a>
285
+ <a-menu slot="overlay" style="min-width: 60px">
286
+ <a-menu-item
287
+ v-for="(action_item, actionIndex) in item.actionArr"
288
+ :key="actionIndex"
289
+ v-show="!action_item.customFunction || tableContext.customFunctionShow(action_item.customFunction,record,c_index)">
290
+ <a
291
+ style="text-align: center"
292
+ @click="tableContext.action(record, item.dataIndex, action_item.func)"
293
+ >{{ action_item.text }}</a>
294
+ </a-menu-item>
295
+ </a-menu>
296
+ </a-dropdown>
297
+ </template>
298
+ <template v-if="!item.actionArr || item.actionArr.length === 0">
299
+ <a @click="tableContext.action(record, item.dataIndex,'action', index)">{{ item.slotValue }}</a>
300
+ </template>
301
+ </span>
302
+ </template>
303
+ <template slot="footer">
304
+ <slot
305
+ name="footer"
306
+ :selectedRowKeys="tableContext.selectedRowKeys"
307
+ :selectedRows="tableContext.selectedRows"></slot>
308
+ </template>
309
+ <template slot="fixedfooter">
310
+ <slot name="fixedfooter"></slot>
311
+ </template>
312
+ </s-table>
313
+ </template>
314
+
315
+ <script>
316
+ import { Ellipsis, STable } from '@vue2-client/components'
317
+ import { formatDate } from '@vue2-client/utils/util'
318
+ import XBadge from '@vue2-client/base-client/components/common/XBadge'
319
+ import XFormItem from '@vue2-client/base-client/components/common/XForm/XFormItem'
320
+ import CustomFuncCel from '@vue2-client/base-client/components/common/XTable/CustomFuncCel.vue'
321
+ import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
322
+ import XRate from '@vue2-client/base-client/components/common/XRate/index.vue'
323
+ import XWebSocketProgress from '@vue2-client/base-client/components/common/XWebSocketProgress'
324
+ import VueDraggableResizable from 'vue-draggable-resizable'
325
+ import { mapState } from 'vuex'
326
+
327
+ export default {
328
+ name: 'XTableWrapper',
329
+ components: {
330
+ Ellipsis,
331
+ STable,
332
+ XBadge,
333
+ XFormItem,
334
+ XRate,
335
+ CustomFuncCel,
336
+ XWebSocketProgress,
337
+ VueDraggableResizable
338
+ },
339
+ data () {
340
+ return {
341
+ isDragging: false, // 添加拖拽状态标记
342
+ // eslint-disable-next-line vue/no-reserved-keys
343
+ _rafId: null // 添加requestAnimationFrame ID标记
344
+ }
345
+ },
346
+ computed: {
347
+ ...mapState('account', { currUser: 'user' }),
348
+ localDataSource () {
349
+ return this.activeTable?.localDataSource
350
+ },
351
+ // 获取当前活动的表格实例
352
+ activeTable () {
353
+ return this.tableContext.expandedGrid ? this.$refs.expandableTable : this.$refs.simpleTable
354
+ },
355
+ realTableColumns () {
356
+ // 1. 先过滤列并设置 ellipsis 属性
357
+ const filteredColumns = this.tableContext.tableColumns
358
+ .filter(item => item.slotType !== 'action' || !this.disableAction)
359
+ .map((item) => {
360
+ // 设置 ellipsis 属性
361
+ if (['ellipsis', 'badge', 'towDecimal', 'date', 'dateTime', 'fourDecimal', 'int', 'customJs'].includes(item.slotType)) {
362
+ item.ellipsis = true
363
+ }
364
+ return item
365
+ })
366
+
367
+ // 2. 分组:左固定、普通、右固定
368
+ const leftFixedColumns = []
369
+ const normalColumns = []
370
+ const rightFixedColumns = []
371
+
372
+ filteredColumns.forEach(col => {
373
+ if (col.fixed === 'left') {
374
+ leftFixedColumns.push(col)
375
+ } else if (col.fixed === 'right') {
376
+ rightFixedColumns.push(col)
377
+ } else {
378
+ normalColumns.push(col)
379
+ }
380
+ })
381
+
382
+ // 3. 按顺序合并:左固定 + 普通 + 右固定
383
+ return [...leftFixedColumns, ...normalColumns, ...rightFixedColumns]
384
+ },
385
+ components () {
386
+ return {
387
+ header: {
388
+ cell: (h, props, children) => {
389
+ const { key, ...restProps } = props
390
+ // 此处的this.realTableColumns 是定义的table的表头属性变量
391
+ const col = this.realTableColumns.find((col) => {
392
+ const k = col.dataIndex || col.key
393
+ return k === key
394
+ })
395
+ if (!col || !col.width || col.slotType === 'action') {
396
+ if (children) {
397
+ return h('th', { ...restProps }, [...children])
398
+ } else {
399
+ return h('th', { ...restProps })
400
+ }
401
+ }
402
+
403
+ // 创建一个防止点击排序的容器
404
+ const preventSortProps = {
405
+ style: {
406
+ position: 'absolute',
407
+ right: 0,
408
+ top: 0,
409
+ width: '20px',
410
+ height: '100%',
411
+ zIndex: 10
412
+ },
413
+ on: {
414
+ mousedown: (e) => {
415
+ e.stopPropagation()
416
+ e.preventDefault()
417
+ this.isDragging = true
418
+ }
419
+ }
420
+ }
421
+
422
+ const dragProps = {
423
+ key: col.dataIndex || col.key,
424
+ class: 'table-draggable-handle',
425
+ attrs: {
426
+ w: 10,
427
+ x: col.width,
428
+ z: 1,
429
+ axis: 'x',
430
+ draggable: true,
431
+ resizable: false,
432
+ },
433
+ on: {
434
+ dragging: (x, y) => {
435
+ // 使用requestAnimationFrame优化性能,减少卡顿
436
+ if (!this._rafId) {
437
+ this._rafId = requestAnimationFrame(() => {
438
+ col.width = Math.max(x, 50) // 设置最小列宽为50px
439
+ this.isDragging = true // 设置拖拽状态
440
+ this._rafId = null
441
+ })
442
+ }
443
+ },
444
+ mousedown: (e) => {
445
+ // 阻止事件冒泡,防止触发排序
446
+ e.stopPropagation()
447
+ e.preventDefault()
448
+ this.isDragging = true // 设置拖拽状态
449
+ },
450
+ dragstop: () => {
451
+ // 清除可能存在的动画帧请求
452
+ if (this._rafId) {
453
+ cancelAnimationFrame(this._rafId)
454
+ this._rafId = null
455
+ }
456
+
457
+ // 拖拽结束时,延迟重置拖拽状态,以防止排序被触发
458
+ setTimeout(() => {
459
+ this.isDragging = false
460
+ }, 100)
461
+ }
462
+ },
463
+ nativeOn: {
464
+ mousedown: (e) => {
465
+ e.stopPropagation()
466
+ e.preventDefault()
467
+ this.isDragging = true // 设置拖拽状态
468
+ }
469
+ }
470
+ }
471
+
472
+ const preventSort = h('div', preventSortProps)
473
+ const drag = h('vue-draggable-resizable', { ...dragProps })
474
+
475
+ // 修改th的点击事件,在拖拽状态下阻止排序
476
+ const newRestProps = { ...restProps }
477
+ const originalClick = newRestProps.on && newRestProps.on.click
478
+ if (originalClick) {
479
+ newRestProps.on = {
480
+ ...newRestProps.on,
481
+ click: (e) => {
482
+ if (this.isDragging) {
483
+ e.stopPropagation()
484
+ e.preventDefault()
485
+ return
486
+ }
487
+ originalClick(e)
488
+ }
489
+ }
490
+ }
491
+
492
+ if (children) {
493
+ return h('th', { ...newRestProps, class: 'resize-table-th' }, [...children, preventSort, drag])
494
+ } else {
495
+ return h('th', { ...newRestProps, class: 'resize-table-th' }, [preventSort, drag])
496
+ }
497
+ },
498
+ }
499
+ }
500
+ }
501
+ },
502
+ props: {
503
+ // 查询配置文件名
504
+ queryParamsName: {
505
+ type: String,
506
+ default: () => {
507
+ return ''
508
+ }
509
+ },
510
+ loadSelectedData: {
511
+ type: Boolean,
512
+ required: false,
513
+ default: false
514
+ },
515
+ disableAction: {
516
+ type: Boolean,
517
+ default: false
518
+ },
519
+ // 默认查询的当页行数
520
+ defaultPageSize: {
521
+ type: Number,
522
+ default: 10
523
+ },
524
+ },
525
+ inject: ['tableContext'],
526
+ methods: {
527
+ getPopupContainer (triggerNode) {
528
+ // 限制最多向上查找5层
529
+ let parent = triggerNode.parentNode
530
+ let depth = 0
531
+ const maxDepth = 10
532
+
533
+ while (parent && parent !== document.body && depth < maxDepth) {
534
+ if (parent.tagName === 'TBODY') {
535
+ return parent
536
+ }
537
+ parent = parent.parentNode
538
+ depth++
539
+ }
540
+ // 如果5层内没找到,回退到body
541
+ return document.body
542
+ },
543
+ handleRowClick (record) {
544
+ this.$emit('rowClick', record)
545
+ },
546
+ handleRowDblClick (record) {
547
+ this.$emit('rowDblClick', record)
548
+ },
549
+ beforeDataChange (record) {
550
+ this.$emit('beforeDataChange', record)
551
+ },
552
+ onExpand (expanded, record) {
553
+ this.$emit('expand', expanded, record)
554
+ },
555
+ setLocalDataSource (data) {
556
+ this.activeTable?.setLocalDataSource(data)
557
+ },
558
+ loadData () {
559
+ if (this.loadSelectedData) {
560
+ return this.loadSelectedDataGen
561
+ } else {
562
+ return this.tableContext.loadData
563
+ }
564
+ },
565
+ loadSelectedDataGen (requestParameters) {
566
+ console.log('loadSelectedDataGen', {
567
+ pageNo: requestParameters?.pageNo,
568
+ pageSize: requestParameters?.pageSize
569
+ })
570
+
571
+ const { pageNo = 1, pageSize = 10 } = requestParameters || {}
572
+ const startIndex = (pageNo - 1) * pageSize
573
+ const endIndex = startIndex + pageSize
574
+ const paginatedData = this.tableContext.selectedRows.slice(startIndex, endIndex)
575
+
576
+ return new Promise((resolve) => {
577
+ resolve({
578
+ data: paginatedData,
579
+ pageNo,
580
+ pageSize,
581
+ totalPage: Math.ceil(this.tableContext.selectedRows.length / pageSize),
582
+ totalCount: this.tableContext.selectedRows.length
583
+ })
584
+ })
585
+ },
586
+ updateSelect (selectedRowKeys, selectedRows) {
587
+ this.activeTable?.updateSelect(selectedRowKeys, selectedRows)
588
+ },
589
+ clearSelected () {
590
+ this.activeTable?.clearSelected()
591
+ },
592
+ refresh (bool) {
593
+ this.activeTable?.refresh(bool)
594
+ },
595
+
596
+ /**
597
+ * 格式化日期
598
+ * @param date 日期字符串
599
+ * @param format 格式化方式
600
+ */
601
+ format (date, format) {
602
+ return formatDate(date, format)
603
+ },
604
+ /**
605
+ * 格式化数字
606
+ * @param number string 或者 number
607
+ * @param decimalPlaces 小数位数
608
+ */
609
+ numberFormat (number, decimalPlaces = 2) {
610
+ const value = parseFloat(number)
611
+ if (!isNaN(value)) {
612
+ return value.toFixed(decimalPlaces)
613
+ } else {
614
+ return ''
615
+ }
616
+ },
617
+ getFromItem (model, text, record, index) {
618
+ const aa = this.tableContext.formItems.reduce((acc, item) => {
619
+ if (item.type === 'group') {
620
+ const foundItem = item.groupItems.find(_item => _item.model === model && _item.editRow)
621
+ if (foundItem) {
622
+ acc = foundItem
623
+ }
624
+ } else if (item.model === model && item.editRow) {
625
+ acc = item
626
+ }
627
+ return acc
628
+ }, null)
629
+ if (aa) {
630
+ const tempConfig = JSON.parse(JSON.stringify(aa))
631
+ // 如果找到了字段
632
+ const ColumnIndex = this.realTableColumns.findIndex(item => item.dataIndex === model)
633
+ // 并且表单项是日期框
634
+ if (ColumnIndex !== -1 && ['yearPicker', 'monthPicker', 'datePicker', 'rangePicker'].includes(tempConfig.type)) {
635
+ // 修改他的列宽
636
+ this.realTableColumns[ColumnIndex].width = 220
637
+ }
638
+ // 如果有检验规则检验是数字
639
+ if (ColumnIndex !== -1 && ['number', 'integer', 'float'].includes(tempConfig?.rule?.type)) {
640
+ // 修改他的列宽
641
+ tempConfig.numberInput = true
642
+ }
643
+ if (tempConfig.editRowShowFunc) {
644
+ if (executeStrFunctionByContext(this.tableContext, tempConfig.editRowShowFunc, [text, record, index, tempConfig])) {
645
+ return tempConfig
646
+ }
647
+ } else {
648
+ return tempConfig
649
+ }
650
+ }
651
+ return false
652
+ },
653
+ handleResizeColumn (w, col) {
654
+ col.width = w
655
+ },
656
+
657
+ /**
658
+ * 处理进度更新事件
659
+ * @param {Object} data 进度数据
660
+ * @param {Object} record 行数据
661
+ * @param {String} dataIndex 列字段名
662
+ */
663
+ handleProgressUpdated (data, record, dataIndex) {
664
+ // 更新行数据中的进度值
665
+ if (record && dataIndex) {
666
+ this.$set(record, dataIndex, data.value)
667
+ }
668
+ },
669
+ /**
670
+ * 处理表单项事件,增强事件数据
671
+ * @param {String} func 事件函数名
672
+ * @param {Object} attr 字段配置
673
+ * @param {*} value 字段值
674
+ * @param {Object} record 当前行数据
675
+ * @param {Number} index 当前行索引
676
+ */
677
+ handleFormItemEvent (func, attr, value, record, index) {
678
+ // 获取表格数据源
679
+ const dataSource = this.localDataSource || []
680
+
681
+ // 计算下一行的索引和数据
682
+ const nextIndex = index + 1
683
+ const nextRecord = nextIndex < dataSource.length ? dataSource[nextIndex] : null
684
+
685
+ // 构建增强的上下文数据
686
+ const enhancedContext = {
687
+ func,
688
+ attr,
689
+ value,
690
+ currentRecord: record,
691
+ currentIndex: index,
692
+ nextRecord,
693
+ nextIndex: nextRecord ? nextIndex : null,
694
+ tableContext: this.tableContext
695
+ }
696
+
697
+ // 调用 tableContext 的事件处理方法
698
+ if (this.tableContext && typeof this.tableContext.handleFormItemEvent === 'function') {
699
+ this.tableContext.handleFormItemEvent(enhancedContext)
700
+ }
701
+ }
702
+ }
703
+ }
704
+ </script>
705
+
706
+ <style scoped lang="less">
707
+ /* 精确修复表头空白问题 */
708
+ :deep(.ant-table-fixed-left table, .ant-table-fixed-right table) {
709
+ width: min-content;
710
+ }
711
+ :deep(.ant-table-fixed-header .ant-table-scroll .ant-table-header) {
712
+ margin-bottom: 0 !important;
713
+ overflow-x: hidden !important;
714
+ }
715
+ /* 统一表头行高和垂直对齐 */
716
+ :deep(.ant-table-thead > tr) {
717
+ height: 54px !important;
718
+ line-height: 54px !important;
719
+ }
720
+ :deep(.ant-table-thead > tr > th) {
721
+ padding: 0 16px !important;
722
+ vertical-align: middle !important;
723
+ border-bottom: 1px solid #f0f0f0;
724
+ }
725
+ /* 分页组件不换行 */
726
+ :deep(.ant-pagination > li) {
727
+ white-space: nowrap;
728
+ }
729
+ </style>
730
+ <style>
731
+ /* 多操作下拉菜单边框与阴影 */
732
+ .x-table-action-dropdown .ant-dropdown-menu {
733
+ border: 1px solid #e8e8e8;
734
+ border-radius: 4px;
735
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
736
+ }
737
+
738
+ /* vue-draggable-resizable 拖动手柄样式 */
739
+ .table-draggable-handle {
740
+ height: 100% !important;
741
+ left: auto !important;
742
+ right: 0;
743
+ cursor: col-resize;
744
+ touch-action: none;
745
+ border: none;
746
+ position: absolute;
747
+ transform: none !important;
748
+ bottom: 0;
749
+ width: 10px !important; /* 减小到10px */
750
+ z-index: 99 !important; /* 提高z-index */
751
+ }
752
+
753
+ /* 拖动手柄的分隔线 */
754
+ .table-draggable-handle::after {
755
+ content: "";
756
+ position: absolute;
757
+ top: 50%;
758
+ height: 20px;
759
+ width: 2px;
760
+ background-color: #e8e8e8;
761
+ transition: background-color 0.2s;
762
+ transform: translateY(-50%);
763
+ right: 0;
764
+ }
765
+
766
+ /* 悬停效果 - 只改变颜色 */
767
+ .table-draggable-handle:hover::after {
768
+ background-color: #1890ff;
769
+ }
770
+
771
+ /* 拖动时的效果 */
772
+ .table-draggable-handle:active::after {
773
+ background-color: #096dd9;
774
+ }
775
+
776
+ /* 表头单元格样式 */
777
+ .resize-table-th {
778
+ position: relative;
779
+ overflow: visible !important; /* 确保内容不被裁剪 */
780
+ }
781
+
782
+ /* 表头单元格悬停时的手柄高亮 */
783
+ .resize-table-th:hover .table-draggable-handle::after {
784
+ background-color: rgba(24, 144, 255, 0.6);
785
+ }
786
+ </style>