vue2-client 1.18.35 → 1.18.37

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