vue2-client 1.17.50 → 1.18.1

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