n20-common-lib 3.0.22 → 3.0.23
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.
- package/package.json +4 -3
- package/src/assets/css/drag-list.scss +22 -14
- package/src/assets/css/normalize.scss +6 -0
- package/src/assets/css/show-column.scss +72 -14
- package/src/assets/css/table copy.scss +234 -0
- package/src/assets/css/table.scss +159 -13
- package/src/assets/realUrl.js +2 -1
- package/src/components/AdvancedFilter/index.vue +1 -1
- package/src/components/ApprovalButtons/showOtherAttrNew.vue +1 -1
- package/src/components/ApprovalRecord/index.vue +5 -5
- package/src/components/DragList/index.vue +33 -12
- package/src/components/Empty/img/searchNoData.png +0 -0
- package/src/components/HandlingAdvice/index.vue +1 -0
- package/src/components/ShowColumn/index copy.vue +18 -33
- package/src/components/ShowColumn/index.vue +244 -59
- package/src/components/TablePro/index copy.vue +462 -0
- package/src/components/TablePro/index.js +9 -2
- package/src/components/TablePro/index.vue +542 -28
- package/src/components/TableSetSize/index copy.vue +69 -0
- package/src/components/TableSetSize/index.vue +10 -13
- package/src/plugins/Print/print-js/src/js/print.js +0 -6
- package/theme/blue.css +3 -0
- package/theme/cctcRed.css +3 -0
- package/theme/fonts/SIMSUN.5e0c362c.ttf +0 -0
- package/theme/fonts/element-icons.535877f5.woff +0 -0
- package/theme/fonts/element-icons.732389de.ttf +0 -0
- package/theme/fonts/iconfont.09d221ee.woff +0 -0
- package/theme/fonts/iconfont.1c4bfacc.ttf +0 -0
- package/theme/fonts/iconfont.a6f34dc7.woff2 +0 -0
- package/theme/fonts/iconfont.f4c32765.ttf +0 -0
- package/theme/green.css +3 -0
- package/theme/lightBlue.css +3 -0
- package/theme/mapleLeafRed.css +3 -0
- package/theme/orange.css +3 -0
- package/theme/purple.css +3 -0
- package/theme/red.css +3 -0
- package/theme/yellow.css +3 -0
- package/src/components/ShowColumn/index copy 2.vue +0 -545
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div style="height: 100%; position: relative">
|
|
2
|
+
<div class="table-pro-wrapper" style="height: 100%; position: relative">
|
|
3
|
+
<!-- 骨架屏 - 仅覆盖内容区域,不覆盖表头 -->
|
|
4
|
+
<div v-if="loading" class="table-pro-skeleton">
|
|
5
|
+
<div class="skeleton-row" v-for="n in skeletonRows" :key="'skeleton-' + n">
|
|
6
|
+
<div class="skeleton-cell skeleton-checkbox"></div>
|
|
7
|
+
<div class="skeleton-cell" v-for="col in skeletonCols" :key="'col-' + col"></div>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
3
10
|
<vxe-table
|
|
4
11
|
ref="vxeTable"
|
|
5
12
|
:key="colsKey"
|
|
@@ -32,8 +39,6 @@
|
|
|
32
39
|
multiple: false,
|
|
33
40
|
remote: true,
|
|
34
41
|
trigger: 'cell',
|
|
35
|
-
iconAsc: 'n20-icon-daoxu',
|
|
36
|
-
iconDesc: 'n20-icon-shunxu',
|
|
37
42
|
...$attrs.sortConfig,
|
|
38
43
|
...$attrs['sort-config']
|
|
39
44
|
}"
|
|
@@ -42,12 +47,16 @@
|
|
|
42
47
|
iconNone: 'n20-icon-xiala-moren',
|
|
43
48
|
iconMatch: 'n20-icon-xiala-moren'
|
|
44
49
|
}"
|
|
50
|
+
:merge-cells="mergeCells"
|
|
45
51
|
v-bind="Object.assign({ size: size }, $attrs, sizeBind)"
|
|
46
52
|
v-on="$listeners"
|
|
47
53
|
@sort-change="(val) => customSortMethod(val)"
|
|
48
54
|
@filter-change="filterChange"
|
|
49
55
|
@checkbox-change="handleSelectionChange"
|
|
50
56
|
@cell-click="handleCellClick"
|
|
57
|
+
@cell-mouseenter="handleCellMouseEnter"
|
|
58
|
+
@cell-mouseleave="handleCellMouseLeave"
|
|
59
|
+
@scroll="handleTableScroll"
|
|
51
60
|
>
|
|
52
61
|
<template v-for="(item, i) in _columns">
|
|
53
62
|
<slot v-if="item.slotName" :name="item.slotName" :column="item"></slot>
|
|
@@ -77,16 +86,26 @@
|
|
|
77
86
|
>
|
|
78
87
|
<template #header="{ $table, checked, indeterminate, disabled }">
|
|
79
88
|
<span class="custom-checkbox" @click.stop="toggleAll($table, disabled)">
|
|
80
|
-
<el-checkbox
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
<el-checkbox
|
|
90
|
+
v-if="indeterminate"
|
|
91
|
+
:key="key + '_header_indeterminate'"
|
|
92
|
+
:indeterminate="indeterminate"
|
|
93
|
+
:disabled="disabled"
|
|
94
|
+
/>
|
|
95
|
+
<el-checkbox v-else-if="checked" :key="key + '_header_checked'" :value="checked" :disabled="disabled" />
|
|
96
|
+
<el-checkbox v-else :key="key + '_header_unchecked'" :value="checked" :disabled="disabled" />
|
|
83
97
|
</span>
|
|
84
98
|
</template>
|
|
85
99
|
<template #checkbox="{ $table, row, checked, indeterminate, disabled }">
|
|
86
100
|
<span class="custom-checkbox" @click.stop="toggleChecked($table, row, disabled)">
|
|
87
|
-
<el-checkbox
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
<el-checkbox
|
|
102
|
+
v-if="indeterminate"
|
|
103
|
+
:key="key + '-row-indeterminate'"
|
|
104
|
+
:indeterminate="indeterminate"
|
|
105
|
+
:disabled="disabled"
|
|
106
|
+
/>
|
|
107
|
+
<el-checkbox v-else-if="checked" :key="key + '-row-checked'" :value="checked" :disabled="disabled" />
|
|
108
|
+
<el-checkbox v-else :key="key + '-row-unchecked'" :value="checked" :disabled="disabled" />
|
|
90
109
|
</span>
|
|
91
110
|
</template>
|
|
92
111
|
</vxe-column>
|
|
@@ -109,7 +128,7 @@
|
|
|
109
128
|
</vxe-colgroup>
|
|
110
129
|
<vxe-column
|
|
111
130
|
v-else
|
|
112
|
-
:key="'vxe-table-' + i"
|
|
131
|
+
:key="'vxe-table-base__column' + i"
|
|
113
132
|
:class-name="`${item.wrap && `vxe-table-custom-wrap`} ${item.bold && `font-w600`}`"
|
|
114
133
|
:formatter="item.formatter ? item.formatter : 'formatName'"
|
|
115
134
|
:filters="item.filters"
|
|
@@ -117,15 +136,105 @@
|
|
|
117
136
|
:title="item.label"
|
|
118
137
|
:field="item.prop"
|
|
119
138
|
v-bind="item"
|
|
120
|
-
|
|
139
|
+
>
|
|
140
|
+
<template #header="{ column }">
|
|
141
|
+
<div
|
|
142
|
+
class="flex-box flex-v flex-c"
|
|
143
|
+
@mouseenter="hoverHeaderProp = item.prop"
|
|
144
|
+
@mouseleave="hoverHeaderProp = null"
|
|
145
|
+
>
|
|
146
|
+
<slot name="header" :column="column">{{ column.title }}</slot>
|
|
147
|
+
<i
|
|
148
|
+
v-if="item.tooltip"
|
|
149
|
+
class="n20-icon-xinxitishi vxe-table--column__icon m-l-ss"
|
|
150
|
+
v-title="item.tooltip"
|
|
151
|
+
></i>
|
|
152
|
+
<!-- 已固定列(fixed='left'):hover 时显示 lock 图标,点击取消固定 -->
|
|
153
|
+
<i
|
|
154
|
+
v-if="item.fixed === 'left'"
|
|
155
|
+
class="iconfont icon-lock vxe-table--column__icon m-l-ss pointer color-primary"
|
|
156
|
+
@click.stop="unlockColumn(item)"
|
|
157
|
+
></i>
|
|
158
|
+
<!-- 未固定列(且非静态列):hover 时显示 unlock 图标,点击设为固定 -->
|
|
159
|
+
<i
|
|
160
|
+
v-if="!item.fixed && !item.static"
|
|
161
|
+
v-show="hoverHeaderProp === item.prop"
|
|
162
|
+
class="iconfont icon-unlock vxe-table--column__icon m-l-ss pointer"
|
|
163
|
+
@click.stop="lockColumn(item)"
|
|
164
|
+
></i>
|
|
165
|
+
</div>
|
|
166
|
+
</template>
|
|
167
|
+
</vxe-column>
|
|
121
168
|
</template>
|
|
169
|
+
<vxe-column
|
|
170
|
+
fixed="right"
|
|
171
|
+
header-class-name="fixed-column__static"
|
|
172
|
+
:min-width="showColumn && showSetsize ? 80 : 50"
|
|
173
|
+
>
|
|
174
|
+
<div slot="header">
|
|
175
|
+
<i
|
|
176
|
+
v-if="showColumn"
|
|
177
|
+
v-title="$lc('设置显示列')"
|
|
178
|
+
class="iconfont icon-system-solution pointer"
|
|
179
|
+
@click="$emit('visible-column')"
|
|
180
|
+
></i>
|
|
181
|
+
<tableSetSize v-if="showSetsize" :size="sizeC" v-bind="$attrs" @update:size="sizeUp" @resize="sizeSet" />
|
|
182
|
+
</div>
|
|
183
|
+
</vxe-column>
|
|
122
184
|
<template #empty>
|
|
123
185
|
<slot name="empty">
|
|
124
186
|
<empty type="empty" :content="$lc('暂无数据')" :height="200" :width="200" />
|
|
125
187
|
</slot>
|
|
126
188
|
</template>
|
|
127
189
|
</vxe-table>
|
|
128
|
-
|
|
190
|
+
<!-- 操作列的悬浮按钮组 -->
|
|
191
|
+
<transition name="hover-btns-fade">
|
|
192
|
+
<div
|
|
193
|
+
v-if="showHoverBtns && visibleHoverBtns.length > 0"
|
|
194
|
+
class="table-hover-btns"
|
|
195
|
+
:style="hoverBtnsStyle"
|
|
196
|
+
@mouseenter="handleBtnGroupEnter"
|
|
197
|
+
@mouseleave="handleBtnGroupLeave"
|
|
198
|
+
>
|
|
199
|
+
<div class="hover-btns-wrapper">
|
|
200
|
+
<el-button
|
|
201
|
+
v-for="(btn, idx) in displayHoverBtns"
|
|
202
|
+
:key="'display-' + idx"
|
|
203
|
+
:type="btn.type || 'primary'"
|
|
204
|
+
:size="btn.size || 'mini'"
|
|
205
|
+
:icon="btn.icon"
|
|
206
|
+
:disabled="btn.disabled"
|
|
207
|
+
:class="[btn.class]"
|
|
208
|
+
@click.stop="handleHoverBtnClick(btn, $event)"
|
|
209
|
+
>
|
|
210
|
+
{{ btn.label }}
|
|
211
|
+
</el-button>
|
|
212
|
+
<!-- 更多按钮下拉菜单 -->
|
|
213
|
+
<el-dropdown
|
|
214
|
+
v-if="moreHoverBtns.length > 0"
|
|
215
|
+
class="hover-btns-more"
|
|
216
|
+
@command="handleMoreBtnCommand"
|
|
217
|
+
@visible-change="handleDropdownVisibleChange"
|
|
218
|
+
>
|
|
219
|
+
<el-button type="primary" size="mini">
|
|
220
|
+
{{ $lc('更多') }}<i class="el-icon-arrow-down el-icon--right"></i>
|
|
221
|
+
</el-button>
|
|
222
|
+
<el-dropdown-menu slot="dropdown">
|
|
223
|
+
<el-dropdown-item
|
|
224
|
+
v-for="(btn, idx) in moreHoverBtns"
|
|
225
|
+
:key="'more-' + idx"
|
|
226
|
+
:command="btn"
|
|
227
|
+
:disabled="btn.disabled"
|
|
228
|
+
>
|
|
229
|
+
<span :class="btn.type === 'danger' ? 'color-danger' : ''">
|
|
230
|
+
{{ btn.label }}
|
|
231
|
+
</span>
|
|
232
|
+
</el-dropdown-item>
|
|
233
|
+
</el-dropdown-menu>
|
|
234
|
+
</el-dropdown>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</transition>
|
|
129
238
|
</div>
|
|
130
239
|
</template>
|
|
131
240
|
|
|
@@ -133,7 +242,7 @@
|
|
|
133
242
|
import empty from '../Empty'
|
|
134
243
|
import tableSetSize from '../TableSetSize/index.vue'
|
|
135
244
|
import { $lc } from '../../utils/i18n/index.js'
|
|
136
|
-
|
|
245
|
+
|
|
137
246
|
const renderer = {
|
|
138
247
|
name: 'renderer',
|
|
139
248
|
props: {
|
|
@@ -197,6 +306,10 @@ export default {
|
|
|
197
306
|
return forbidSelect
|
|
198
307
|
}
|
|
199
308
|
},
|
|
309
|
+
showColumn: {
|
|
310
|
+
type: Boolean,
|
|
311
|
+
default: true
|
|
312
|
+
},
|
|
200
313
|
// 数据更新时是否自动清空已选
|
|
201
314
|
clearSelect: {
|
|
202
315
|
type: Boolean,
|
|
@@ -210,6 +323,63 @@ export default {
|
|
|
210
323
|
isAutoWidth: {
|
|
211
324
|
type: Boolean,
|
|
212
325
|
default: true
|
|
326
|
+
},
|
|
327
|
+
// 合并单元格配置,格式: [{ row: 0, col: 0, rowspan: 2, colspan: 0 }]
|
|
328
|
+
mergeCells: {
|
|
329
|
+
type: Array,
|
|
330
|
+
default: () => []
|
|
331
|
+
},
|
|
332
|
+
// 全选时是否排除被合并的行(从属行)
|
|
333
|
+
excludeMergedRows: {
|
|
334
|
+
type: Boolean,
|
|
335
|
+
default: false
|
|
336
|
+
},
|
|
337
|
+
// 是否显示骨架屏加载状态
|
|
338
|
+
loading: {
|
|
339
|
+
type: Boolean,
|
|
340
|
+
default: false
|
|
341
|
+
},
|
|
342
|
+
// 骨架屏行数
|
|
343
|
+
skeletonRows: {
|
|
344
|
+
type: Number,
|
|
345
|
+
default: 10
|
|
346
|
+
},
|
|
347
|
+
// 骨架屏列数
|
|
348
|
+
skeletonCols: {
|
|
349
|
+
type: Number,
|
|
350
|
+
default: 6
|
|
351
|
+
},
|
|
352
|
+
// 悬浮按钮列表配置
|
|
353
|
+
// 格式: [{ command: 'edit', label: '编辑', type: 'text', isHas: 'edit', click: (row) => {} }]
|
|
354
|
+
btnList: {
|
|
355
|
+
type: Array,
|
|
356
|
+
default: () => []
|
|
357
|
+
},
|
|
358
|
+
// 权限检查函数,返回当前行允许显示的按钮 key 列表
|
|
359
|
+
// 格式: (row, btns) => ['edit', 'delete'] 或 true(显示所有)或 false(不显示任何)
|
|
360
|
+
hoverBtnsPermission: {
|
|
361
|
+
type: Function,
|
|
362
|
+
default: null
|
|
363
|
+
},
|
|
364
|
+
// 悬浮按钮组是否显示分隔符
|
|
365
|
+
hoverBtnsDivider: {
|
|
366
|
+
type: Boolean,
|
|
367
|
+
default: true
|
|
368
|
+
},
|
|
369
|
+
// 悬浮按钮组显示延迟(毫秒)
|
|
370
|
+
hoverBtnsDelay: {
|
|
371
|
+
type: Number,
|
|
372
|
+
default: 50
|
|
373
|
+
},
|
|
374
|
+
// 悬浮按钮组隐藏延迟(毫秒)
|
|
375
|
+
hoverBtnsHideDelay: {
|
|
376
|
+
type: Number,
|
|
377
|
+
default: 150
|
|
378
|
+
},
|
|
379
|
+
// 悬浮按钮组最大显示数量,超过则收起到"更多"下拉菜单
|
|
380
|
+
hoverBtnsMaxShow: {
|
|
381
|
+
type: Number,
|
|
382
|
+
default: 4
|
|
213
383
|
}
|
|
214
384
|
},
|
|
215
385
|
data() {
|
|
@@ -218,15 +388,61 @@ export default {
|
|
|
218
388
|
colsKey: 0,
|
|
219
389
|
sizeC: localStorage.getItem('table-size') || _this.size,
|
|
220
390
|
sizeBind: undefined,
|
|
221
|
-
|
|
391
|
+
setTableSize: false,
|
|
392
|
+
key: 0,
|
|
393
|
+
// 悬浮按钮组相关状态
|
|
394
|
+
hoverRowData: null, // 当前悬停行数据
|
|
395
|
+
hoverRowIndex: -1, // 当前悬停行索引
|
|
396
|
+
showHoverBtns: false, // 是否显示悬浮按钮组
|
|
397
|
+
hoverBtnsPosition: { top: 0, left: 0, width: 0 }, // 按钮组位置
|
|
398
|
+
hoverShowTimer: null, // 显示延迟定时器
|
|
399
|
+
hoverHideTimer: null, // 隐藏延迟定时器
|
|
400
|
+
isHoverOnBtnGroup: false, // 鼠标是否在按钮组上
|
|
401
|
+
isDropdownVisible: false, // "更多"下拉菜单是否展开
|
|
402
|
+
hoverHeaderProp: null // 当前悬停表头列的 prop
|
|
222
403
|
}
|
|
223
404
|
},
|
|
224
405
|
computed: {
|
|
225
|
-
_columns
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
406
|
+
_columns: {
|
|
407
|
+
get() {
|
|
408
|
+
if (this.isAutoWidth) {
|
|
409
|
+
return this.calcColumnWidth(this.columns)
|
|
410
|
+
} else {
|
|
411
|
+
return this.columns
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
set(val) {
|
|
415
|
+
return val
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
// 计算当前行可见的悬浮按钮
|
|
419
|
+
visibleHoverBtns() {
|
|
420
|
+
if (!this.hoverRowData || !this.btnList || this.btnList.length === 0) {
|
|
421
|
+
return []
|
|
422
|
+
}
|
|
423
|
+
return this.btnList.filter((btn) => this.hasBtn(btn.isHas, this.hoverRowData))
|
|
424
|
+
},
|
|
425
|
+
// 直接显示的按钮(不超过阈值)
|
|
426
|
+
displayHoverBtns() {
|
|
427
|
+
if (this.visibleHoverBtns.length > this.hoverBtnsMaxShow) {
|
|
428
|
+
return this.visibleHoverBtns.slice(0, this.hoverBtnsMaxShow - 1)
|
|
429
|
+
}
|
|
430
|
+
return this.visibleHoverBtns
|
|
431
|
+
},
|
|
432
|
+
// 收起到"更多"下拉菜单的按钮
|
|
433
|
+
moreHoverBtns() {
|
|
434
|
+
if (this.visibleHoverBtns.length > this.hoverBtnsMaxShow) {
|
|
435
|
+
return this.visibleHoverBtns.slice(this.hoverBtnsMaxShow - 1)
|
|
436
|
+
}
|
|
437
|
+
return []
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
// 悬浮按钮组样式
|
|
441
|
+
hoverBtnsStyle() {
|
|
442
|
+
return {
|
|
443
|
+
top: `${this.hoverBtnsPosition.top}px`,
|
|
444
|
+
right: 0, // 固定在右侧设置列按钮左边
|
|
445
|
+
height: `${this.hoverBtnsPosition.height}px`
|
|
230
446
|
}
|
|
231
447
|
}
|
|
232
448
|
},
|
|
@@ -248,16 +464,84 @@ export default {
|
|
|
248
464
|
activated() {
|
|
249
465
|
this.$refs.vxeTable.loadData(this.data)
|
|
250
466
|
},
|
|
251
|
-
|
|
467
|
+
beforeDestroy() {
|
|
468
|
+
// 清理定时器
|
|
469
|
+
this.clearHoverTimers()
|
|
470
|
+
},
|
|
252
471
|
methods: {
|
|
253
|
-
//
|
|
472
|
+
// 锁定列:将该列 fixed 设为 'left',直接修改 prop 对象并刷新表格
|
|
473
|
+
lockColumn(item) {
|
|
474
|
+
if (item.fixed || item.static) return
|
|
475
|
+
this.$set(item, 'fixed', 'left')
|
|
476
|
+
// 手动触发 colsKey 刷新,让 vxe-table 重新渲染列配置
|
|
477
|
+
this.colsKey = this.colsKey + 1
|
|
478
|
+
},
|
|
479
|
+
// 解锁列:移除 fixed 属性,将该列恢复为普通列
|
|
480
|
+
unlockColumn(item) {
|
|
481
|
+
if (item.fixed !== 'left') return
|
|
482
|
+
this.$delete(item, 'fixed')
|
|
483
|
+
// 手动触发 colsKey 刷新,让 vxe-table 重新渲染列配置
|
|
484
|
+
this.colsKey = this.colsKey + 1
|
|
485
|
+
},
|
|
486
|
+
// 判断按鈕是否显示
|
|
487
|
+
hasBtn(isHas, row) {
|
|
488
|
+
if (isHas === undefined || isHas === null) {
|
|
489
|
+
return true
|
|
490
|
+
} else if (typeof isHas === 'boolean') {
|
|
491
|
+
return isHas
|
|
492
|
+
} else if (typeof isHas === 'string' || Array.isArray(isHas)) {
|
|
493
|
+
return this.$has(isHas)
|
|
494
|
+
} else if (typeof isHas === 'function') {
|
|
495
|
+
return isHas(row)
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
// 全选/反选
|
|
254
499
|
toggleAllSelection() {
|
|
255
|
-
if (this.$refs.vxeTable)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
500
|
+
if (!this.$refs.vxeTable) return
|
|
501
|
+
const allData = this.data || []
|
|
502
|
+
// 获取合并行索引集合
|
|
503
|
+
const mergedIndexes =
|
|
504
|
+
this.excludeMergedRows && this.mergeCells && this.mergeCells.length > 0 ? this.getMergedRowIndexes() : new Set()
|
|
505
|
+
// 筛选出可选择的行(排除合并从属行 + 排除禁止选择的行)
|
|
506
|
+
const selectableRows = allData.filter((row, index) => {
|
|
507
|
+
if (mergedIndexes.has(index)) return false
|
|
508
|
+
if (typeof this.forbidSelect === 'function' && this.forbidSelect({ row }) === true) return false
|
|
509
|
+
return true
|
|
510
|
+
})
|
|
511
|
+
// 判断当前可选行是否已全部选中
|
|
512
|
+
const checkedRows = this.$refs.vxeTable.getCheckboxRecords(false)
|
|
513
|
+
const checkedSet = new Set(checkedRows)
|
|
514
|
+
const isAllChecked = selectableRows.length > 0 && selectableRows.every((row) => checkedSet.has(row))
|
|
515
|
+
if (isAllChecked) {
|
|
516
|
+
// 已全选,则取消可选行的选中状态
|
|
517
|
+
selectableRows.forEach((row) => {
|
|
518
|
+
this.$refs.vxeTable.setCheckboxRow(row, false)
|
|
519
|
+
})
|
|
520
|
+
} else {
|
|
521
|
+
// 未全选,则选中所有可选行
|
|
522
|
+
selectableRows.forEach((row) => {
|
|
523
|
+
this.$refs.vxeTable.setCheckboxRow(row, true)
|
|
524
|
+
})
|
|
259
525
|
}
|
|
526
|
+
// 得手动触发
|
|
527
|
+
this.handleSelectionChange()
|
|
260
528
|
},
|
|
529
|
+
// 获取被合并的从属行索引集合
|
|
530
|
+
getMergedRowIndexes() {
|
|
531
|
+
const mergedIndexes = new Set()
|
|
532
|
+
if (this.mergeCells && this.mergeCells.length > 0) {
|
|
533
|
+
this.mergeCells.forEach((cell) => {
|
|
534
|
+
if (cell.rowspan > 1) {
|
|
535
|
+
// 从 row+1 到 row+rowspan-1 都是被合并的从属行
|
|
536
|
+
for (let i = 1; i < cell.rowspan; i++) {
|
|
537
|
+
mergedIndexes.add(cell.row + i)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
return mergedIndexes
|
|
543
|
+
},
|
|
544
|
+
|
|
261
545
|
// 清空选择
|
|
262
546
|
clearSelection() {
|
|
263
547
|
if (this.$refs.vxeTable) {
|
|
@@ -272,11 +556,54 @@ export default {
|
|
|
272
556
|
// 得手动触发
|
|
273
557
|
this.handleSelectionChange()
|
|
274
558
|
},
|
|
559
|
+
// 获取被合并的从属行索引集合
|
|
560
|
+
getMergedRowIndexes() {
|
|
561
|
+
const mergedIndexes = new Set()
|
|
562
|
+
if (this.mergeCells && this.mergeCells.length > 0) {
|
|
563
|
+
this.mergeCells.forEach((cell) => {
|
|
564
|
+
if (cell.rowspan > 1) {
|
|
565
|
+
// 从 row+1 到 row+rowspan-1 都是被合并的从属行
|
|
566
|
+
for (let i = 1; i < cell.rowspan; i++) {
|
|
567
|
+
mergedIndexes.add(cell.row + i)
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
return mergedIndexes
|
|
573
|
+
},
|
|
275
574
|
toggleAll($table, disabled) {
|
|
276
575
|
if (disabled) {
|
|
277
576
|
return false
|
|
278
577
|
}
|
|
279
|
-
|
|
578
|
+
// 如果配置了排除合并行,则手动处理非合并行的选中状态
|
|
579
|
+
if (this.excludeMergedRows && this.mergeCells && this.mergeCells.length > 0) {
|
|
580
|
+
const mergedIndexes = this.getMergedRowIndexes()
|
|
581
|
+
const allData = this.data || []
|
|
582
|
+
// 筛选出可选择的行(排除合并从属行 + 排除禁止选择的行)
|
|
583
|
+
const selectableRows = allData.filter((row, index) => {
|
|
584
|
+
if (mergedIndexes.has(index)) return false
|
|
585
|
+
if (typeof this.forbidSelect === 'function' && this.forbidSelect({ row }) === true) return false
|
|
586
|
+
return true
|
|
587
|
+
})
|
|
588
|
+
// 判断当前可选行是否已全部选中
|
|
589
|
+
const checkedRows = $table.getCheckboxRecords(false)
|
|
590
|
+
const checkedSet = new Set(checkedRows)
|
|
591
|
+
const isAllChecked = selectableRows.length > 0 && selectableRows.every((row) => checkedSet.has(row))
|
|
592
|
+
if (isAllChecked) {
|
|
593
|
+
// 已全选,则取消可选行的选中状态
|
|
594
|
+
selectableRows.forEach((row) => {
|
|
595
|
+
$table.setCheckboxRow(row, false)
|
|
596
|
+
})
|
|
597
|
+
} else {
|
|
598
|
+
// 未全选,则选中所有可选行
|
|
599
|
+
selectableRows.forEach((row) => {
|
|
600
|
+
$table.setCheckboxRow(row, true)
|
|
601
|
+
})
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
// 默认行为:全选/取消全选所有行
|
|
605
|
+
$table.toggleAllCheckboxRow()
|
|
606
|
+
}
|
|
280
607
|
this.key++
|
|
281
608
|
this.handleSelectionChange()
|
|
282
609
|
},
|
|
@@ -329,9 +656,10 @@ export default {
|
|
|
329
656
|
},
|
|
330
657
|
// row当前单次勾选的哪一行数据 包含checked字段
|
|
331
658
|
handleSelectionChange(row = '') {
|
|
332
|
-
const val = this.$refs.vxeTable.getCheckboxRecords()
|
|
659
|
+
const val = this.$refs.vxeTable.getCheckboxRecords(false)
|
|
333
660
|
// 支持跨页勾选
|
|
334
661
|
const val1 = this.$refs.vxeTable.getCheckboxReserveRecords()
|
|
662
|
+
|
|
335
663
|
this.$emit('selection-change-method', [...val, ...val1], row)
|
|
336
664
|
},
|
|
337
665
|
// 点击行勾选/取消勾选
|
|
@@ -392,6 +720,193 @@ export default {
|
|
|
392
720
|
this.sizeBind = el
|
|
393
721
|
},
|
|
394
722
|
// 计算列宽
|
|
723
|
+
// ========== 悬浮按钮组相关方法 ==========
|
|
724
|
+
// 清理所有定时器
|
|
725
|
+
clearHoverTimers() {
|
|
726
|
+
if (this.hoverShowTimer) {
|
|
727
|
+
clearTimeout(this.hoverShowTimer)
|
|
728
|
+
this.hoverShowTimer = null
|
|
729
|
+
}
|
|
730
|
+
if (this.hoverHideTimer) {
|
|
731
|
+
clearTimeout(this.hoverHideTimer)
|
|
732
|
+
this.hoverHideTimer = null
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
// 鼠标进入单元格事件(防抖处理)
|
|
736
|
+
handleCellMouseEnter({ row, rowIndex, $rowIndex, $event }) {
|
|
737
|
+
// 如果没有配置悬浮按钮,不处理
|
|
738
|
+
if (!this.btnList || this.btnList.length === 0) {
|
|
739
|
+
return
|
|
740
|
+
}
|
|
741
|
+
// 清理隐藏定时器
|
|
742
|
+
if (this.hoverHideTimer) {
|
|
743
|
+
clearTimeout(this.hoverHideTimer)
|
|
744
|
+
this.hoverHideTimer = null
|
|
745
|
+
}
|
|
746
|
+
// 如果是同一行,不重复处理
|
|
747
|
+
if (this.hoverRowIndex === rowIndex && this.showHoverBtns) {
|
|
748
|
+
return
|
|
749
|
+
}
|
|
750
|
+
// 清理之前的显示定时器
|
|
751
|
+
if (this.hoverShowTimer) {
|
|
752
|
+
clearTimeout(this.hoverShowTimer)
|
|
753
|
+
}
|
|
754
|
+
// 延迟显示(防抖)
|
|
755
|
+
this.hoverShowTimer = setTimeout(() => {
|
|
756
|
+
this.updateHoverRow(row, rowIndex, $event)
|
|
757
|
+
}, this.hoverBtnsDelay)
|
|
758
|
+
},
|
|
759
|
+
// 鼠标离开单元格事件(防抖处理)
|
|
760
|
+
handleCellMouseLeave({ row, rowIndex, $event }) {
|
|
761
|
+
// 如果没有配置悬浮按钮,不处理
|
|
762
|
+
if (!this.btnList || this.btnList.length === 0) {
|
|
763
|
+
return
|
|
764
|
+
}
|
|
765
|
+
// 清理显示定时器
|
|
766
|
+
if (this.hoverShowTimer) {
|
|
767
|
+
clearTimeout(this.hoverShowTimer)
|
|
768
|
+
this.hoverShowTimer = null
|
|
769
|
+
}
|
|
770
|
+
// 延迟隐藏(给鼠标移动到按钮组的时间)
|
|
771
|
+
this.hoverHideTimer = setTimeout(() => {
|
|
772
|
+
if (!this.isHoverOnBtnGroup) {
|
|
773
|
+
this.hideHoverBtns()
|
|
774
|
+
}
|
|
775
|
+
}, this.hoverBtnsHideDelay)
|
|
776
|
+
},
|
|
777
|
+
// 更新悬停行信息并显示按钮组
|
|
778
|
+
updateHoverRow(row, rowIndex, $event) {
|
|
779
|
+
this.hoverRowData = row
|
|
780
|
+
this.hoverRowIndex = rowIndex
|
|
781
|
+
// 计算按钮组位置
|
|
782
|
+
this.calculateBtnPosition($event)
|
|
783
|
+
// 显示按钮组
|
|
784
|
+
this.showHoverBtns = true
|
|
785
|
+
// 触发事件
|
|
786
|
+
this.$emit('row-hover-enter', { row, rowIndex })
|
|
787
|
+
},
|
|
788
|
+
// 计算按钮组位置
|
|
789
|
+
calculateBtnPosition($event) {
|
|
790
|
+
const tableEl = this.$refs.vxeTable?.$el
|
|
791
|
+
if (!tableEl) return
|
|
792
|
+
// 获取当前行的 DOM 元素
|
|
793
|
+
const rowEl = $event?.target?.closest('tr.vxe-body--row')
|
|
794
|
+
if (!rowEl) return
|
|
795
|
+
// 获取表格容器的位置信息
|
|
796
|
+
const tableRect = tableEl.getBoundingClientRect()
|
|
797
|
+
const rowRect = rowEl.getBoundingClientRect()
|
|
798
|
+
// 计算相对于表格容器的位置
|
|
799
|
+
this.hoverBtnsPosition = {
|
|
800
|
+
top: rowRect.top - tableRect.top,
|
|
801
|
+
height: rowRect.height
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
// 隐藏悬浮按钮组
|
|
805
|
+
hideHoverBtns() {
|
|
806
|
+
// 如果下拉菜单正在展开,不隐藏
|
|
807
|
+
if (this.isDropdownVisible) {
|
|
808
|
+
return
|
|
809
|
+
}
|
|
810
|
+
// 移除当前行的 hover 类名
|
|
811
|
+
this.removeRowHoverClass()
|
|
812
|
+
this.showHoverBtns = false
|
|
813
|
+
this.hoverRowData = null
|
|
814
|
+
this.hoverRowIndex = -1
|
|
815
|
+
this.isHoverOnBtnGroup = false
|
|
816
|
+
// 触发事件
|
|
817
|
+
this.$emit('row-hover-leave')
|
|
818
|
+
},
|
|
819
|
+
// 鼠标进入按钮组
|
|
820
|
+
handleBtnGroupEnter() {
|
|
821
|
+
this.isHoverOnBtnGroup = true
|
|
822
|
+
// 清理隐藏定时器
|
|
823
|
+
if (this.hoverHideTimer) {
|
|
824
|
+
clearTimeout(this.hoverHideTimer)
|
|
825
|
+
this.hoverHideTimer = null
|
|
826
|
+
}
|
|
827
|
+
// 给当前行添加 hover 类名
|
|
828
|
+
this.addRowHoverClass()
|
|
829
|
+
},
|
|
830
|
+
// 鼠标离开按钮组
|
|
831
|
+
handleBtnGroupLeave() {
|
|
832
|
+
this.isHoverOnBtnGroup = false
|
|
833
|
+
// 如果下拉菜单正在展开,不隐藏按钮组
|
|
834
|
+
if (this.isDropdownVisible) {
|
|
835
|
+
return
|
|
836
|
+
}
|
|
837
|
+
// 移除当前行的 hover 类名
|
|
838
|
+
this.removeRowHoverClass()
|
|
839
|
+
// 延迟隐藏
|
|
840
|
+
this.hoverHideTimer = setTimeout(() => {
|
|
841
|
+
// 再次检查下拉菜单状态
|
|
842
|
+
if (!this.isDropdownVisible) {
|
|
843
|
+
this.hideHoverBtns()
|
|
844
|
+
}
|
|
845
|
+
}, this.hoverBtnsHideDelay)
|
|
846
|
+
},
|
|
847
|
+
// 处理下拉菜单显示/隐藏状态变化
|
|
848
|
+
handleDropdownVisibleChange(visible) {
|
|
849
|
+
this.isDropdownVisible = visible
|
|
850
|
+
if (!visible) {
|
|
851
|
+
// 下拉菜单关闭后,延迟检查是否需要隐藏按钮组
|
|
852
|
+
this.hoverHideTimer = setTimeout(() => {
|
|
853
|
+
if (!this.isHoverOnBtnGroup && !this.isDropdownVisible) {
|
|
854
|
+
this.hideHoverBtns()
|
|
855
|
+
}
|
|
856
|
+
}, this.hoverBtnsHideDelay)
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
// 给当前悬停行添加 hover 类名
|
|
860
|
+
addRowHoverClass() {
|
|
861
|
+
const tableEl = this.$refs.vxeTable?.$el
|
|
862
|
+
if (!tableEl || this.hoverRowIndex < 0) return
|
|
863
|
+
// 查找当前行的 tr 元素
|
|
864
|
+
const rows = tableEl.querySelectorAll('tr.vxe-body--row')
|
|
865
|
+
rows.forEach((row, index) => {
|
|
866
|
+
if (index === this.hoverRowIndex) {
|
|
867
|
+
row.classList.add('row--hover')
|
|
868
|
+
}
|
|
869
|
+
})
|
|
870
|
+
},
|
|
871
|
+
// 移除当前悬停行的 hover 类名
|
|
872
|
+
removeRowHoverClass() {
|
|
873
|
+
const tableEl = this.$refs.vxeTable?.$el
|
|
874
|
+
if (!tableEl) return
|
|
875
|
+
// 移除所有行的 hover 类名
|
|
876
|
+
const rows = tableEl.querySelectorAll('tr.vxe-body--row.row--hover')
|
|
877
|
+
rows.forEach((row) => {
|
|
878
|
+
row.classList.remove('row--hover')
|
|
879
|
+
})
|
|
880
|
+
},
|
|
881
|
+
// 处理悬浮按钮点击
|
|
882
|
+
handleHoverBtnClick(btn, $event) {
|
|
883
|
+
if (btn.disabled) return
|
|
884
|
+
|
|
885
|
+
if (typeof btn.command === 'function') {
|
|
886
|
+
btn.command(this.hoverRowData, this.hoverRowIndex, $event)
|
|
887
|
+
} else if (typeof btn.command === 'string') {
|
|
888
|
+
this.$emit(btn.command, this.hoverRowData, this.hoverRowIndex, $event)
|
|
889
|
+
}
|
|
890
|
+
// 触发事件
|
|
891
|
+
this.$emit('hover-btn-click', {
|
|
892
|
+
btn,
|
|
893
|
+
row: this.hoverRowData,
|
|
894
|
+
rowIndex: this.hoverRowIndex,
|
|
895
|
+
$event
|
|
896
|
+
})
|
|
897
|
+
},
|
|
898
|
+
// 处理"更多"下拉菜单命令
|
|
899
|
+
handleMoreBtnCommand(btn) {
|
|
900
|
+
if (!btn || btn.disabled) return
|
|
901
|
+
this.handleHoverBtnClick(btn, null)
|
|
902
|
+
},
|
|
903
|
+
// 表格滚动时隐藏按钮组
|
|
904
|
+
handleTableScroll() {
|
|
905
|
+
if (this.showHoverBtns) {
|
|
906
|
+
this.hideHoverBtns()
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
// ========== 列宽计算相关方法 ==========
|
|
395
910
|
calcColumnWidth(columns) {
|
|
396
911
|
columns = columns.map((item) => {
|
|
397
912
|
return {
|
|
@@ -409,7 +924,6 @@ export default {
|
|
|
409
924
|
return columns
|
|
410
925
|
}
|
|
411
926
|
const { width: windowWidth } = wrapperEl.getBoundingClientRect()
|
|
412
|
-
console.log(windowWidth, 'windowWidth')
|
|
413
927
|
// 定义每个字符的平均宽度(像素)
|
|
414
928
|
const CHAR_WIDTH = 20 // 根据实际字体大小调整
|
|
415
929
|
const PADDING = 20 // 左右padding
|