vue2-client 1.18.38 → 1.18.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) 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/CitySelect/index.js +3 -3
  11. package/src/base-client/components/common/CitySelect/index.md +109 -109
  12. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  13. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  14. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  15. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  16. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  17. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  18. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  19. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  20. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  21. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  22. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  23. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  24. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  25. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  26. package/src/base-client/components/common/Tree/index.js +2 -2
  27. package/src/base-client/components/common/Upload/index.js +3 -3
  28. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  29. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  30. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  31. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  32. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  33. package/src/base-client/components/common/XDatePicker/demo.vue +31 -0
  34. package/src/base-client/components/common/XDatePicker/index.vue +61 -0
  35. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  36. package/src/base-client/components/common/XDescriptions/index.md +322 -322
  37. package/src/base-client/components/common/XForm/XForm.vue +5 -3
  38. package/src/base-client/components/common/XForm/XFormItem.vue +1 -1
  39. package/src/base-client/components/common/XForm/index.md +178 -178
  40. package/src/base-client/components/common/XFormTable/demo.vue +125 -125
  41. package/src/base-client/components/common/XReport/print.js +186 -186
  42. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  43. package/src/base-client/components/common/XStepView/index.js +3 -3
  44. package/src/base-client/components/common/XStepView/index.md +31 -31
  45. package/src/base-client/components/common/XTable/XTable.vue +1715 -1715
  46. package/src/base-client/components/common/XTable/XTableWrapper.vue +769 -769
  47. package/src/base-client/components/common/XTable/index.md +255 -255
  48. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  49. package/src/base-client/plugins/Config.js +19 -19
  50. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  51. package/src/components/Charts/Bar.vue +62 -62
  52. package/src/components/Charts/ChartCard.vue +134 -134
  53. package/src/components/Charts/Liquid.vue +67 -67
  54. package/src/components/Charts/MiniArea.vue +39 -39
  55. package/src/components/Charts/MiniBar.vue +39 -39
  56. package/src/components/Charts/MiniProgress.vue +75 -75
  57. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  58. package/src/components/Charts/Radar.vue +68 -68
  59. package/src/components/Charts/RankList.vue +77 -77
  60. package/src/components/Charts/TagCloud.vue +113 -113
  61. package/src/components/Charts/TransferBar.vue +64 -64
  62. package/src/components/Charts/Trend.vue +82 -82
  63. package/src/components/Charts/chart.less +12 -12
  64. package/src/components/Charts/smooth.area.less +13 -13
  65. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  66. package/src/components/NumberInfo/index.js +3 -3
  67. package/src/components/NumberInfo/index.less +54 -54
  68. package/src/components/NumberInfo/index.md +43 -43
  69. package/src/components/card/ChartCard.vue +79 -79
  70. package/src/components/chart/Bar.vue +60 -60
  71. package/src/components/chart/MiniArea.vue +67 -67
  72. package/src/components/chart/MiniBar.vue +59 -59
  73. package/src/components/chart/MiniProgress.vue +57 -57
  74. package/src/components/chart/Radar.vue +80 -80
  75. package/src/components/chart/RankingList.vue +60 -60
  76. package/src/components/chart/Trend.vue +79 -79
  77. package/src/components/chart/index.less +9 -9
  78. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  79. package/src/components/input/IInput.vue +66 -66
  80. package/src/components/menu/SideMenu.vue +75 -75
  81. package/src/components/menu/menu.js +273 -273
  82. package/src/components/tool/AStepItem.vue +60 -60
  83. package/src/layouts/CommonLayout.vue +56 -56
  84. package/src/layouts/header/HeaderNotice.vue +177 -177
  85. package/src/lib.js +1 -1
  86. package/src/mock/extend/index.js +84 -84
  87. package/src/mock/goods/index.js +108 -108
  88. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  89. package/src/pages/system/dictionary/index.vue +44 -44
  90. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  91. package/src/pages/system/monitor/operLog/index.vue +37 -37
  92. package/src/services/api/cas.js +79 -79
  93. package/src/store/modules/setting.js +119 -119
  94. package/src/utils/authority-utils.js +85 -85
  95. package/src/utils/errorCode.js +6 -6
  96. package/src/utils/map-utils.js +47 -47
  97. package/vue.config.js +4 -4
  98. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
@@ -1,769 +1,769 @@
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=" 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>