xt-element-ui 1.2.1 → 1.2.3
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/lib/index.common.js +1306 -1398
- package/lib/index.css +1 -1
- package/lib/index.umd.js +1311 -1403
- package/lib/index.umd.min.js +5 -5
- package/package.json +3 -4
- package/src/components/ex-button/index.vue +8 -2
- package/src/components/ex-button/style/index.scss +42 -6
- package/src/components/ex-card/index.vue +0 -82
- package/src/components/ex-card/style/index.scss +7 -3
- package/src/components/ex-chart/theme/dark.js +1 -1
- package/src/components/ex-chart/utils.js +1 -1
- package/src/components/ex-page/index.js +2 -0
- package/src/components/ex-select-tree/index.js +2 -0
- package/src/components/{xt-select-tree → ex-select-tree}/index.vue +1 -1
- package/src/components/ex-table/ExTableCell.vue +3 -2
- package/src/components/ex-table/VirtualElTable.vue +234 -0
- package/src/components/ex-table/index.vue +188 -625
- package/src/components/ex-table/virtualScrollData.js +35 -0
- package/src/components/ex-upload/index.js +2 -0
- package/src/components/xt-config-provider/index.js +2 -2
- package/src/components/xt-config-provider/index.vue +17 -13
- package/src/components/xt-text/style/index.scss +13 -13
- package/src/index.js +4 -1
- package/src/styles/css-variables.scss +1 -2
- package/src/styles/element-theme.scss +12 -4
- package/src/utils/index.js +11 -2
- package/src/components/xt-page/index.js +0 -0
- package/src/components/xt-select-tree/index.js +0 -0
- package/src/components/xt-upload/index.js +0 -2
- /package/src/components/{xt-page → ex-page}/index.vue +0 -0
- /package/src/components/{xt-upload → ex-upload}/index.vue +0 -0
- /package/src/components/{xt-upload → ex-upload}/preview.vue +0 -0
|
@@ -8,104 +8,16 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
|
-
<!--
|
|
12
|
-
<div
|
|
13
|
-
|
|
14
|
-
ref="fixedHeader"
|
|
15
|
-
class="ex-table-fixed-header"
|
|
16
|
-
:style="{ marginLeft: -scrollLeft + 'px' }"
|
|
17
|
-
>
|
|
18
|
-
<table class="ex-table-fixed-header-table">
|
|
19
|
-
<colgroup>
|
|
20
|
-
<col v-for="col in headerCols" :key="col._key" :name="col._key" :width="col._width" :min-width="col._minWidth" />
|
|
21
|
-
<col name="gutter" width="12" />
|
|
22
|
-
</colgroup>
|
|
23
|
-
<thead>
|
|
24
|
-
<tr v-for="(row, rowIdx) in headerRows" :key="rowIdx">
|
|
25
|
-
<template v-for="cell in row">
|
|
26
|
-
<th
|
|
27
|
-
v-if="!cell._hidden"
|
|
28
|
-
:key="cell._key"
|
|
29
|
-
:rowspan="cell._rowspan"
|
|
30
|
-
:colspan="cell._colspan"
|
|
31
|
-
:style="cell._style || {}"
|
|
32
|
-
>{{ cell.label }}</th>
|
|
33
|
-
</template>
|
|
34
|
-
</tr>
|
|
35
|
-
</thead>
|
|
36
|
-
</table>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<!-- 表格主体 -->
|
|
40
|
-
<div
|
|
41
|
-
class="ex-table-body"
|
|
42
|
-
:class="{ 'ex-table-virtual': virtualScroll }"
|
|
43
|
-
:style="virtualScroll ? { height: computedHeight + 'px' } : {}"
|
|
44
|
-
ref="tableBody"
|
|
45
|
-
@scroll="handleBodyScroll"
|
|
46
|
-
>
|
|
47
|
-
<!-- 虚拟滚动:包裹层 + 占位撑开 -->
|
|
48
|
-
<div v-if="virtualScroll" class="ex-table-virtual-inner" :style="{ height: virtualTotalHeight + 'px' }">
|
|
49
|
-
<div :style="{ transform: 'translateY(' + virtualOffsetY + 'px)' }">
|
|
50
|
-
<el-table
|
|
51
|
-
ref="table"
|
|
52
|
-
:data="visibleData"
|
|
53
|
-
:span-method="groupColumns.length ? handleSpanMethod : undefined"
|
|
54
|
-
:row-class-name="getRowClassName"
|
|
55
|
-
:show-header="false"
|
|
56
|
-
v-bind="$attrs"
|
|
57
|
-
v-on="$listeners"
|
|
58
|
-
@selection-change="handleSelectionChange"
|
|
59
|
-
@sort-change="handleSortChange"
|
|
60
|
-
class="ex-table"
|
|
61
|
-
>
|
|
62
|
-
<el-table-column v-if="selection" type="selection" width="55" :fixed="selectionFixed" />
|
|
63
|
-
<el-table-column
|
|
64
|
-
v-if="showIndex"
|
|
65
|
-
type="index"
|
|
66
|
-
width="60"
|
|
67
|
-
label="#"
|
|
68
|
-
:fixed="indexFixed"
|
|
69
|
-
:index="indexMethod"
|
|
70
|
-
/>
|
|
71
|
-
<template v-for="col in flattenedColumns">
|
|
72
|
-
<el-table-column
|
|
73
|
-
v-if="col.children && col.children.length"
|
|
74
|
-
:key="col._key"
|
|
75
|
-
v-bind="getColumnProps(col)"
|
|
76
|
-
>
|
|
77
|
-
<template v-for="child in col.children">
|
|
78
|
-
<el-table-column :key="child._key" v-bind="getColumnProps(child)">
|
|
79
|
-
<template v-if="child.render" v-slot="scope">{{ child.render(scope) }}</template>
|
|
80
|
-
<template v-else-if="child.formatter" v-slot="scope">
|
|
81
|
-
<ExTableCell :row="scope.row" :index="scope.$index" :formatter="child.formatter" :column="child" />
|
|
82
|
-
</template>
|
|
83
|
-
<template v-else-if="child.slot" v-slot="scope">
|
|
84
|
-
<slot :name="child.slot" :row="scope.row" :index="scope.$index" :column="child" />
|
|
85
|
-
</template>
|
|
86
|
-
</el-table-column>
|
|
87
|
-
</template>
|
|
88
|
-
</el-table-column>
|
|
89
|
-
<el-table-column v-else :key="col._key" v-bind="getColumnProps(col)">
|
|
90
|
-
<template v-if="col.render" v-slot="scope">{{ col.render(scope) }}</template>
|
|
91
|
-
<template v-else-if="col.formatter" v-slot="scope">
|
|
92
|
-
<ExTableCell :row="scope.row" :index="scope.$index" :formatter="col.formatter" :column="col" />
|
|
93
|
-
</template>
|
|
94
|
-
<template v-else-if="col.slot" v-slot="scope">
|
|
95
|
-
<slot :name="col.slot" :row="scope.row" :index="scope.$index" :column="col" />
|
|
96
|
-
</template>
|
|
97
|
-
</el-table-column>
|
|
98
|
-
</template>
|
|
99
|
-
</el-table>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
|
|
103
|
-
<!-- 非虚拟滚动:直接渲染 -->
|
|
104
|
-
<el-table
|
|
105
|
-
v-else
|
|
11
|
+
<!-- 主体表格:单 Table + 内置虚拟滚动(核心改造) -->
|
|
12
|
+
<div class="ex-table-body">
|
|
13
|
+
<VirtualElTable
|
|
106
14
|
ref="table"
|
|
107
|
-
:data="
|
|
15
|
+
:data="processedTableData"
|
|
108
16
|
:height="computedHeight"
|
|
17
|
+
:max-height="computedMaxHeight"
|
|
18
|
+
:virtual-scroll="virtualScroll"
|
|
19
|
+
:row-height="rowInitHeight"
|
|
20
|
+
:buffer-size="bufferSize"
|
|
109
21
|
:span-method="groupColumns.length ? handleSpanMethod : undefined"
|
|
110
22
|
:row-class-name="getRowClassName"
|
|
111
23
|
v-bind="$attrs"
|
|
@@ -114,7 +26,14 @@
|
|
|
114
26
|
@sort-change="handleSortChange"
|
|
115
27
|
class="ex-table"
|
|
116
28
|
>
|
|
117
|
-
|
|
29
|
+
<!-- 选择列 -->
|
|
30
|
+
<el-table-column
|
|
31
|
+
v-if="selection"
|
|
32
|
+
type="selection"
|
|
33
|
+
width="55"
|
|
34
|
+
:fixed="selectionFixed"
|
|
35
|
+
/>
|
|
36
|
+
<!-- 序号列 -->
|
|
118
37
|
<el-table-column
|
|
119
38
|
v-if="showIndex"
|
|
120
39
|
type="index"
|
|
@@ -123,6 +42,7 @@
|
|
|
123
42
|
:fixed="indexFixed"
|
|
124
43
|
:index="indexMethod"
|
|
125
44
|
/>
|
|
45
|
+
<!-- 多级列配置 -->
|
|
126
46
|
<template v-for="col in flattenedColumns">
|
|
127
47
|
<el-table-column
|
|
128
48
|
v-if="col.children && col.children.length"
|
|
@@ -133,10 +53,20 @@
|
|
|
133
53
|
<el-table-column :key="child._key" v-bind="getColumnProps(child)">
|
|
134
54
|
<template v-if="child.render" v-slot="scope">{{ child.render(scope) }}</template>
|
|
135
55
|
<template v-else-if="child.formatter" v-slot="scope">
|
|
136
|
-
<ExTableCell
|
|
56
|
+
<ExTableCell
|
|
57
|
+
:row="scope.row"
|
|
58
|
+
:index="scope.$index"
|
|
59
|
+
:formatter="child.formatter"
|
|
60
|
+
:column="child"
|
|
61
|
+
/>
|
|
137
62
|
</template>
|
|
138
63
|
<template v-else-if="child.slot" v-slot="scope">
|
|
139
|
-
<slot
|
|
64
|
+
<slot
|
|
65
|
+
:name="child.slot"
|
|
66
|
+
:row="scope.row"
|
|
67
|
+
:index="scope.$index"
|
|
68
|
+
:column="child"
|
|
69
|
+
/>
|
|
140
70
|
</template>
|
|
141
71
|
</el-table-column>
|
|
142
72
|
</template>
|
|
@@ -144,14 +74,24 @@
|
|
|
144
74
|
<el-table-column v-else :key="col._key" v-bind="getColumnProps(col)">
|
|
145
75
|
<template v-if="col.render" v-slot="scope">{{ col.render(scope) }}</template>
|
|
146
76
|
<template v-else-if="col.formatter" v-slot="scope">
|
|
147
|
-
<ExTableCell
|
|
77
|
+
<ExTableCell
|
|
78
|
+
:row="scope.row"
|
|
79
|
+
:index="scope.$index"
|
|
80
|
+
:formatter="col.formatter"
|
|
81
|
+
:column="col"
|
|
82
|
+
/>
|
|
148
83
|
</template>
|
|
149
84
|
<template v-else-if="col.slot" v-slot="scope">
|
|
150
|
-
<slot
|
|
85
|
+
<slot
|
|
86
|
+
:name="col.slot"
|
|
87
|
+
:row="scope.row"
|
|
88
|
+
:index="scope.$index"
|
|
89
|
+
:column="col"
|
|
90
|
+
/>
|
|
151
91
|
</template>
|
|
152
92
|
</el-table-column>
|
|
153
93
|
</template>
|
|
154
|
-
</
|
|
94
|
+
</VirtualElTable>
|
|
155
95
|
</div>
|
|
156
96
|
|
|
157
97
|
<!-- 分页 -->
|
|
@@ -171,68 +111,39 @@
|
|
|
171
111
|
|
|
172
112
|
<script>
|
|
173
113
|
import ExTableCell from './ExTableCell.vue'
|
|
174
|
-
|
|
175
|
-
import { Table as ElTable, TableColumn as ElTableColumn } from 'element-ui'
|
|
114
|
+
import VirtualElTable from './VirtualElTable.vue'
|
|
176
115
|
|
|
177
116
|
export default {
|
|
178
117
|
name: 'ExTable',
|
|
179
118
|
inheritAttrs: false,
|
|
180
|
-
components: { ExTableCell,
|
|
119
|
+
components: { ExTableCell, VirtualElTable },
|
|
120
|
+
|
|
181
121
|
props: {
|
|
182
|
-
// 表格数据
|
|
183
122
|
tableData: { type: Array, default: () => [] },
|
|
184
|
-
// 列配置(支持 children 多级表头)
|
|
185
123
|
columns: { type: Array, default: () => [] },
|
|
186
|
-
// 分组列配置(按配置的层级合并单元格)
|
|
187
124
|
groupColumns: { type: Array, default: () => [] },
|
|
188
|
-
// 标题
|
|
189
125
|
title: { type: String, default: '' },
|
|
190
|
-
// 高度
|
|
191
126
|
height: { type: [Number, String], default: null },
|
|
192
|
-
|
|
127
|
+
maxHeight: { type: [Number, String], default: null },
|
|
193
128
|
virtualScroll: { type: Boolean, default: false },
|
|
194
|
-
// 预估行高
|
|
195
129
|
rowInitHeight: { type: Number, default: 48 },
|
|
196
|
-
// 缓冲区行数
|
|
197
130
|
bufferSize: { type: Number, default: 5 },
|
|
198
|
-
// 分页配置
|
|
199
131
|
pagination: { type: Object, default: null },
|
|
200
|
-
// 总条数
|
|
201
132
|
total: { type: Number, default: 0 },
|
|
202
|
-
// 是否显示序号
|
|
203
133
|
showIndex: { type: Boolean, default: false },
|
|
204
|
-
// 是否可选
|
|
205
134
|
selection: { type: Boolean, default: false },
|
|
206
|
-
// 选择列固定位置
|
|
207
135
|
selectionFixed: { type: [String, Boolean], default: false },
|
|
208
|
-
// 序号列固定位置
|
|
209
136
|
indexFixed: { type: [String, Boolean], default: false },
|
|
210
|
-
// 加载状态
|
|
211
137
|
loading: { type: Boolean, default: false },
|
|
212
|
-
// 空数据文案
|
|
213
138
|
emptyText: { type: String, default: '暂无数据' },
|
|
214
|
-
// 小计配置
|
|
215
139
|
subtotalConfig: { type: Object, default: () => ({ enabled: false }) },
|
|
216
|
-
// 总计配置
|
|
217
140
|
totalConfig: { type: Object, default: () => ({ enabled: false }) }
|
|
218
141
|
},
|
|
219
142
|
|
|
220
143
|
data() {
|
|
221
144
|
return {
|
|
222
|
-
// 虚拟滚动相关
|
|
223
|
-
scrollTop: 0,
|
|
224
|
-
scrollLeft: 0,
|
|
225
|
-
rowHeight: 48,
|
|
226
|
-
visibleStartIndex: 0,
|
|
227
|
-
visibleEndIndex: 0,
|
|
228
|
-
visibleCount: 0,
|
|
229
|
-
// 表格宽度(用于自适应列宽计算)
|
|
230
|
-
tableWidth: 0,
|
|
231
|
-
// 分组合并缓存
|
|
232
145
|
spanCache: {},
|
|
233
|
-
// 列扁平化缓存
|
|
234
146
|
flattenedColumnsCache: [],
|
|
235
|
-
// 选中数据
|
|
236
147
|
selectedRows: []
|
|
237
148
|
}
|
|
238
149
|
},
|
|
@@ -241,337 +152,100 @@ export default {
|
|
|
241
152
|
showPagination() {
|
|
242
153
|
return this.pagination && this.total > 0
|
|
243
154
|
},
|
|
244
|
-
|
|
245
155
|
computedHeight() {
|
|
246
|
-
if (this.virtualScroll) {
|
|
247
|
-
return this.height || 400
|
|
248
|
-
}
|
|
249
156
|
return this.height || undefined
|
|
250
157
|
},
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
fixedColumnWidth() {
|
|
254
|
-
let width = 0
|
|
255
|
-
// 选择列
|
|
256
|
-
if (this.selection) width += 55
|
|
257
|
-
// 序号列
|
|
258
|
-
if (this.showIndex) width += 60
|
|
259
|
-
// fixed 列
|
|
260
|
-
const sumFixedWidth = (cols) => {
|
|
261
|
-
cols.forEach(c => {
|
|
262
|
-
if (c.fixed && (c.width || c.minWidth)) {
|
|
263
|
-
width += typeof c.width === 'number' ? c.width : (typeof c.minWidth === 'number' ? c.minWidth : 100)
|
|
264
|
-
}
|
|
265
|
-
if (c.children && c.children.length) sumFixedWidth(c.children)
|
|
266
|
-
})
|
|
267
|
-
}
|
|
268
|
-
sumFixedWidth(this.columns)
|
|
269
|
-
return width
|
|
270
|
-
},
|
|
271
|
-
|
|
272
|
-
// 计算非固定列中已设置width的总宽度
|
|
273
|
-
nonFixedColumnWidthWithWidth() {
|
|
274
|
-
let width = 0
|
|
275
|
-
const sumWidth = (cols) => {
|
|
276
|
-
cols.forEach(c => {
|
|
277
|
-
if (!c.fixed && c.width) {
|
|
278
|
-
width += typeof c.width === 'number' ? c.width : parseInt(c.width) || 0
|
|
279
|
-
}
|
|
280
|
-
if (c.children && c.children.length) sumWidth(c.children)
|
|
281
|
-
})
|
|
282
|
-
}
|
|
283
|
-
sumWidth(this.columns)
|
|
284
|
-
return width
|
|
158
|
+
computedMaxHeight() {
|
|
159
|
+
return this.height ? undefined : this.maxHeight || undefined
|
|
285
160
|
},
|
|
286
161
|
|
|
287
|
-
//
|
|
288
|
-
autoColumnCount() {
|
|
289
|
-
let count = 0
|
|
290
|
-
const countCols = (cols) => {
|
|
291
|
-
cols.forEach(c => {
|
|
292
|
-
if (!c.fixed) {
|
|
293
|
-
if (c.children && c.children.length) {
|
|
294
|
-
countCols(c.children)
|
|
295
|
-
} else if (!c.width) {
|
|
296
|
-
// 只有未设置width的非固定列才参与自适应
|
|
297
|
-
count++
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
})
|
|
301
|
-
}
|
|
302
|
-
countCols(this.columns)
|
|
303
|
-
return count
|
|
304
|
-
},
|
|
305
|
-
|
|
306
|
-
// 自适应列宽(未设置width的非固定列均分剩余宽度)
|
|
307
|
-
autoColumnWidth() {
|
|
308
|
-
const totalWidth = this.tableWidth || 1000
|
|
309
|
-
const gutter = 12 // el-table 默认 gutter
|
|
310
|
-
const fixedWidth = this.fixedColumnWidth
|
|
311
|
-
const nonFixedWithWidth = this.nonFixedColumnWidthWithWidth
|
|
312
|
-
const autoCount = this.autoColumnCount
|
|
313
|
-
|
|
314
|
-
if (autoCount <= 0) return 100 // 默认宽度
|
|
315
|
-
|
|
316
|
-
// 自适应宽度 = (总宽度 - gutter - 固定列宽度 - 非固定列中已设置的width)/ 需要自适应的列数
|
|
317
|
-
const availableWidth = totalWidth - gutter - fixedWidth - nonFixedWithWidth
|
|
318
|
-
return Math.max(80, Math.floor(availableWidth / autoCount)) // 最小80px
|
|
319
|
-
},
|
|
320
|
-
|
|
321
|
-
// 表头列定义(用于 colgroup,与 headerRows 列数保持一致)
|
|
322
|
-
headerCols() {
|
|
323
|
-
const cols = []
|
|
324
|
-
|
|
325
|
-
// 选择列
|
|
326
|
-
if (this.selection) {
|
|
327
|
-
cols.push({ _key: 'prepend_selection', _width: 55, _minWidth: 55 })
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// 序号列
|
|
331
|
-
if (this.showIndex) {
|
|
332
|
-
cols.push({ _key: 'prepend_index', _width: 60, _minWidth: 60 })
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// 数据列(只取叶子节点,使用一致的key生成)
|
|
336
|
-
const addLeafCols = (columns, parentPath = '') => {
|
|
337
|
-
columns.forEach((col, index) => {
|
|
338
|
-
const path = parentPath ? `${parentPath}_${index}` : String(index)
|
|
339
|
-
const key = this.generateColKey(col, path)
|
|
340
|
-
|
|
341
|
-
if (col.children && col.children.length) {
|
|
342
|
-
addLeafCols(col.children, path)
|
|
343
|
-
} else {
|
|
344
|
-
cols.push({
|
|
345
|
-
_key: key,
|
|
346
|
-
_width: this.getColumnWidth(col),
|
|
347
|
-
_minWidth: col.minWidth
|
|
348
|
-
})
|
|
349
|
-
}
|
|
350
|
-
})
|
|
351
|
-
}
|
|
352
|
-
addLeafCols(this.columns)
|
|
353
|
-
|
|
354
|
-
return cols
|
|
355
|
-
},
|
|
356
|
-
|
|
357
|
-
// 表头层级结构(用于自定义固定表头渲染)
|
|
358
|
-
headerRows() {
|
|
359
|
-
const rows = []
|
|
360
|
-
let maxDepth = 1
|
|
361
|
-
|
|
362
|
-
// 计算最大深度
|
|
363
|
-
const calcDepth = (cols, depth) => {
|
|
364
|
-
maxDepth = Math.max(maxDepth, depth)
|
|
365
|
-
cols.forEach(c => { if (c.children && c.children.length) calcDepth(c.children, depth + 1) })
|
|
366
|
-
}
|
|
367
|
-
if (this.columns.length) calcDepth(this.columns, 1)
|
|
368
|
-
|
|
369
|
-
// 初始化行
|
|
370
|
-
for (let i = 0; i < maxDepth; i++) rows.push([])
|
|
371
|
-
|
|
372
|
-
// 计算叶子数量
|
|
373
|
-
const countLeaves = (cols) => {
|
|
374
|
-
let n = 0
|
|
375
|
-
cols.forEach(c => {
|
|
376
|
-
if (c.children && c.children.length) n += countLeaves(c.children)
|
|
377
|
-
else n++
|
|
378
|
-
})
|
|
379
|
-
return n
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// 选择列 / 序号列占位(仅第一行,rowspan 跨所有行)
|
|
383
|
-
if (this.selection) {
|
|
384
|
-
rows[0].push({
|
|
385
|
-
_key: 'prepend_selection',
|
|
386
|
-
_rowspan: maxDepth,
|
|
387
|
-
_colspan: 1,
|
|
388
|
-
_style: { width: '55px' },
|
|
389
|
-
_hidden: false,
|
|
390
|
-
label: ''
|
|
391
|
-
})
|
|
392
|
-
for (let i = 1; i < maxDepth; i++) {
|
|
393
|
-
rows[i].push({ _key: 'prepend_selection_' + i, _rowspan: 0, _colspan: 1, _hidden: true, label: '' })
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
if (this.showIndex) {
|
|
397
|
-
rows[0].push({
|
|
398
|
-
_key: 'prepend_index',
|
|
399
|
-
_rowspan: maxDepth,
|
|
400
|
-
_colspan: 1,
|
|
401
|
-
_style: { width: '60px' },
|
|
402
|
-
_hidden: false,
|
|
403
|
-
label: '#'
|
|
404
|
-
})
|
|
405
|
-
for (let i = 1; i < maxDepth; i++) {
|
|
406
|
-
rows[i].push({ _key: 'prepend_index_' + i, _rowspan: 0, _colspan: 1, _hidden: true, label: '' })
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// 填充列
|
|
411
|
-
if (this.columns.length) {
|
|
412
|
-
const fill = (cols, depth, parentPath = '') => {
|
|
413
|
-
cols.forEach((c, index) => {
|
|
414
|
-
const path = parentPath ? `${parentPath}_${index}` : String(index)
|
|
415
|
-
const key = this.generateColKey(c, path)
|
|
416
|
-
const hasKids = c.children && c.children.length
|
|
417
|
-
const style = {}
|
|
418
|
-
|
|
419
|
-
// 如果有固定宽度,使用固定宽度;否则使用自适应宽度
|
|
420
|
-
if (c.width) {
|
|
421
|
-
style.width = typeof c.width === 'number' ? c.width + 'px' : c.width
|
|
422
|
-
} else if (!c.fixed && this.virtualScroll) {
|
|
423
|
-
// 非固定列且虚拟滚动时使用自适应宽度
|
|
424
|
-
style.width = this.autoColumnWidth + 'px'
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (c.minWidth) style.minWidth = typeof c.minWidth === 'number' ? c.minWidth + 'px' : c.minWidth
|
|
428
|
-
|
|
429
|
-
rows[depth].push({
|
|
430
|
-
_key: key,
|
|
431
|
-
_rowspan: hasKids ? 1 : maxDepth - depth,
|
|
432
|
-
_colspan: hasKids ? countLeaves(c.children) : 1,
|
|
433
|
-
_style: style,
|
|
434
|
-
_hidden: !c.label && !c.prop,
|
|
435
|
-
label: c.label || ''
|
|
436
|
-
})
|
|
437
|
-
if (hasKids) fill(c.children, depth + 1, path)
|
|
438
|
-
})
|
|
439
|
-
}
|
|
440
|
-
fill(this.columns, 0)
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return rows
|
|
444
|
-
},
|
|
445
|
-
|
|
446
|
-
// 处理后的数据(注入小计/总计行)
|
|
162
|
+
// 处理小计、总计后的最终数据
|
|
447
163
|
processedTableData() {
|
|
448
164
|
if (!this.tableData.length) return []
|
|
449
|
-
|
|
450
165
|
let data = [...this.tableData]
|
|
451
166
|
const hasSubtotal = this.subtotalConfig && this.subtotalConfig.enabled
|
|
452
167
|
const hasTotal = this.totalConfig && this.totalConfig.enabled
|
|
453
168
|
|
|
454
169
|
if (!hasSubtotal && !hasTotal) return data
|
|
455
170
|
|
|
456
|
-
// 找到第一个数据列作为标签列
|
|
457
171
|
const labelColumn = this.findLabelColumn()
|
|
458
|
-
|
|
459
172
|
let result = []
|
|
460
173
|
|
|
461
174
|
if (hasSubtotal && this.subtotalConfig.groupBy && this.subtotalConfig.groupBy.length) {
|
|
462
|
-
// 按 groupBy 分组
|
|
463
175
|
const groups = this.groupData(data, this.subtotalConfig.groupBy)
|
|
464
|
-
groups.forEach((groupRows
|
|
465
|
-
// 插入数据行
|
|
176
|
+
groups.forEach((groupRows) => {
|
|
466
177
|
result.push(...groupRows)
|
|
467
|
-
|
|
468
|
-
const subtotalRow = this.createSubtotalRow(groupRows, groupKey)
|
|
178
|
+
const subtotalRow = this.createSubtotalRow(groupRows)
|
|
469
179
|
result.push(subtotalRow)
|
|
470
180
|
})
|
|
471
181
|
} else {
|
|
472
182
|
result = data
|
|
473
183
|
}
|
|
474
184
|
|
|
475
|
-
// 插入总计行
|
|
476
185
|
if (hasTotal) {
|
|
477
186
|
const totalRow = this.createTotalRow(data)
|
|
478
187
|
result.push(totalRow)
|
|
479
188
|
}
|
|
480
|
-
|
|
481
189
|
return result
|
|
482
190
|
},
|
|
483
191
|
|
|
484
|
-
//
|
|
485
|
-
virtualTotalHeight() {
|
|
486
|
-
const data = this.processedTableData
|
|
487
|
-
return data.length * this.rowHeight
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
// 虚拟滚动偏移量
|
|
491
|
-
virtualOffsetY() {
|
|
492
|
-
const start = Math.max(0, this.visibleStartIndex - this.bufferSize)
|
|
493
|
-
return start * this.rowHeight
|
|
494
|
-
},
|
|
495
|
-
|
|
496
|
-
// 扁平化列配置(处理 children 嵌套)
|
|
192
|
+
// 扁平化列配置,添加唯一 key
|
|
497
193
|
flattenedColumns() {
|
|
498
194
|
if (this.flattenedColumnsCache.length) return this.flattenedColumnsCache
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const key = col.prop || col.label ||
|
|
503
|
-
|
|
195
|
+
const assignKeys = (cols, parentPath = '') => {
|
|
196
|
+
return cols.map((col, index) => {
|
|
197
|
+
const path = parentPath ? `${parentPath}_${index}` : String(index)
|
|
198
|
+
const key = col.prop || col.slot || col.label || path
|
|
199
|
+
const item = { ...col, _key: key }
|
|
200
|
+
if (col.children && col.children.length) {
|
|
201
|
+
item.children = assignKeys(col.children, path)
|
|
202
|
+
}
|
|
203
|
+
return item
|
|
504
204
|
})
|
|
505
205
|
}
|
|
506
|
-
|
|
507
|
-
this.flattenedColumnsCache
|
|
508
|
-
return result
|
|
509
|
-
},
|
|
510
|
-
|
|
511
|
-
// 虚拟滚动时的可见数据
|
|
512
|
-
visibleData() {
|
|
513
|
-
const data = this.processedTableData
|
|
514
|
-
if (!this.virtualScroll || data.length === 0) {
|
|
515
|
-
return data
|
|
516
|
-
}
|
|
517
|
-
const start = Math.max(0, this.visibleStartIndex - this.bufferSize)
|
|
518
|
-
const end = Math.min(data.length, this.visibleEndIndex + this.bufferSize)
|
|
519
|
-
return data.slice(start, end)
|
|
206
|
+
this.flattenedColumnsCache = assignKeys(this.columns)
|
|
207
|
+
return this.flattenedColumnsCache
|
|
520
208
|
}
|
|
521
209
|
},
|
|
522
210
|
|
|
523
211
|
watch: {
|
|
524
|
-
tableData
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
this.flattenedColumnsCache = []
|
|
528
|
-
if (this.virtualScroll) {
|
|
529
|
-
this.$nextTick(() => {
|
|
530
|
-
this.measureRowHeight()
|
|
531
|
-
this.updateVisibleRange()
|
|
532
|
-
})
|
|
533
|
-
}
|
|
534
|
-
},
|
|
535
|
-
immediate: false
|
|
212
|
+
tableData() {
|
|
213
|
+
this.spanCache = {}
|
|
214
|
+
this.flattenedColumnsCache = []
|
|
536
215
|
},
|
|
537
|
-
|
|
538
216
|
columns: {
|
|
539
217
|
handler() {
|
|
540
218
|
this.flattenedColumnsCache = []
|
|
219
|
+
this.spanCache = {}
|
|
541
220
|
},
|
|
542
221
|
deep: true
|
|
543
222
|
},
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
this.destroyVirtualScroll()
|
|
550
|
-
}
|
|
223
|
+
groupColumns: {
|
|
224
|
+
handler() {
|
|
225
|
+
this.spanCache = {}
|
|
226
|
+
},
|
|
227
|
+
deep: true
|
|
551
228
|
}
|
|
552
229
|
},
|
|
553
230
|
|
|
554
231
|
mounted() {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
// 监听窗口resize,更新表格宽度
|
|
559
|
-
this.resizeHandler = () => {
|
|
560
|
-
if (this.virtualScroll) {
|
|
561
|
-
this.$nextTick(() => this.updateTableWidth())
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
window.addEventListener('resize', this.resizeHandler)
|
|
232
|
+
this.$nextTick(() => {
|
|
233
|
+
window.addEventListener('resize', this.handleResize)
|
|
234
|
+
})
|
|
565
235
|
},
|
|
566
236
|
|
|
567
237
|
beforeDestroy() {
|
|
568
|
-
this.
|
|
569
|
-
window.removeEventListener('resize', this.resizeHandler)
|
|
238
|
+
window.removeEventListener('resize', this.handleResize)
|
|
570
239
|
},
|
|
571
240
|
|
|
572
241
|
methods: {
|
|
573
|
-
|
|
574
|
-
|
|
242
|
+
handleResize() {
|
|
243
|
+
this.$nextTick(() => {
|
|
244
|
+
this.$refs.table && this.$refs.table.doLayout()
|
|
245
|
+
})
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// ========== 小计 / 总计 相关 ==========
|
|
575
249
|
findLabelColumn() {
|
|
576
250
|
for (const col of this.columns) {
|
|
577
251
|
if (col.children && col.children.length) {
|
|
@@ -585,33 +259,25 @@ export default {
|
|
|
585
259
|
return { prop: '', label: '' }
|
|
586
260
|
},
|
|
587
261
|
|
|
588
|
-
// 按指定字段分组(保持分组顺序)
|
|
589
262
|
groupData(data, groupBy) {
|
|
590
263
|
const map = new Map()
|
|
591
264
|
data.forEach(row => {
|
|
592
265
|
const key = groupBy.map(f => row[f]).join('|||')
|
|
593
|
-
if (!map.has(key))
|
|
594
|
-
map.set(key, [])
|
|
595
|
-
}
|
|
266
|
+
if (!map.has(key)) map.set(key, [])
|
|
596
267
|
map.get(key).push(row)
|
|
597
268
|
})
|
|
598
269
|
return map
|
|
599
270
|
},
|
|
600
271
|
|
|
601
|
-
// 计算单列的聚合值
|
|
602
272
|
_calcValue(rows, calc) {
|
|
603
|
-
// 自定义函数
|
|
604
273
|
if (typeof calc === 'function') return calc(rows)
|
|
605
|
-
|
|
606
|
-
// 字符串:'sum' | 'avg' | 'count' | 'min' | 'max'(需配合 prop 使用)
|
|
607
|
-
|
|
608
|
-
// 对象:{ prop: 'field', type: 'sum' } 或 { prop: 'field' } (默认 sum)
|
|
609
274
|
const prop = calc.prop
|
|
610
275
|
const type = calc.type || 'sum'
|
|
611
276
|
const vals = prop ? rows.map(r => parseFloat(r[prop]) || 0) : []
|
|
612
277
|
switch (type) {
|
|
613
278
|
case 'sum': return vals.reduce((s, v) => s + v, 0)
|
|
614
|
-
case 'avg':
|
|
279
|
+
case 'avg':
|
|
280
|
+
case 'average': return vals.length ? vals.reduce((s, v) => s + v, 0) / vals.length : 0
|
|
615
281
|
case 'count': return rows.length
|
|
616
282
|
case 'min': return vals.length ? Math.min(...vals) : 0
|
|
617
283
|
case 'max': return vals.length ? Math.max(...vals) : 0
|
|
@@ -619,20 +285,16 @@ export default {
|
|
|
619
285
|
}
|
|
620
286
|
},
|
|
621
287
|
|
|
622
|
-
|
|
623
|
-
createSubtotalRow(groupRows, groupKey) {
|
|
288
|
+
createSubtotalRow(groupRows) {
|
|
624
289
|
const config = this.subtotalConfig
|
|
625
290
|
const rawLabelProp = this.findLabelColumn()
|
|
626
|
-
|
|
627
|
-
// 获取第一个 groupBy 列的值显示
|
|
628
291
|
const labelField = config.groupBy ? config.groupBy[0] : ''
|
|
629
|
-
const groupValue = groupRows[0] ? groupRows[0][labelField] :
|
|
292
|
+
const groupValue = groupRows[0] ? groupRows[0][labelField] : ''
|
|
630
293
|
const labelText = config.labelText || `${groupValue} 小计`
|
|
631
294
|
|
|
632
|
-
const row = { _rowType: 'subtotal'
|
|
295
|
+
const row = { _rowType: 'subtotal' }
|
|
633
296
|
row[rawLabelProp.prop] = labelText
|
|
634
297
|
|
|
635
|
-
// 计算各列的聚合值
|
|
636
298
|
if (config.columns) {
|
|
637
299
|
Object.keys(config.columns).forEach(prop => {
|
|
638
300
|
const calc = config.columns[prop]
|
|
@@ -641,11 +303,9 @@ export default {
|
|
|
641
303
|
: this._calcValue(groupRows, calc)
|
|
642
304
|
})
|
|
643
305
|
}
|
|
644
|
-
|
|
645
306
|
return row
|
|
646
307
|
},
|
|
647
308
|
|
|
648
|
-
// 创建总计行
|
|
649
309
|
createTotalRow(allRows) {
|
|
650
310
|
const config = this.totalConfig
|
|
651
311
|
const rawLabelProp = this.findLabelColumn()
|
|
@@ -654,7 +314,6 @@ export default {
|
|
|
654
314
|
const row = { _rowType: 'total' }
|
|
655
315
|
row[rawLabelProp.prop] = labelText
|
|
656
316
|
|
|
657
|
-
// 计算各列的聚合值
|
|
658
317
|
if (config.columns) {
|
|
659
318
|
Object.keys(config.columns).forEach(prop => {
|
|
660
319
|
const calc = config.columns[prop]
|
|
@@ -663,55 +322,40 @@ export default {
|
|
|
663
322
|
: this._calcValue(allRows, calc)
|
|
664
323
|
})
|
|
665
324
|
}
|
|
666
|
-
|
|
667
325
|
return row
|
|
668
326
|
},
|
|
669
327
|
|
|
670
|
-
// 行类名(小计/总计行样式)
|
|
671
328
|
getRowClassName({ row }) {
|
|
672
|
-
if (!row) return ''
|
|
673
329
|
if (row._rowType === 'subtotal') return 'ex-table-row-subtotal'
|
|
674
330
|
if (row._rowType === 'total') return 'ex-table-row-total'
|
|
675
331
|
return ''
|
|
676
332
|
},
|
|
677
333
|
|
|
678
|
-
//
|
|
334
|
+
// ========== 列处理 ==========
|
|
679
335
|
getColumnProps(col) {
|
|
680
336
|
const { _key, children, render, formatter, slot, ...props } = col
|
|
681
337
|
return props
|
|
682
338
|
},
|
|
683
339
|
|
|
684
|
-
// ==================== 序号方法 ====================
|
|
685
340
|
indexMethod(index) {
|
|
686
|
-
if (this.virtualScroll && this.visibleData.length > 0) {
|
|
687
|
-
const row = this.visibleData[index]
|
|
688
|
-
if (row && row._realIndex !== undefined) {
|
|
689
|
-
return row._realIndex + 1
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
341
|
return index + 1
|
|
693
342
|
},
|
|
694
343
|
|
|
695
|
-
//
|
|
344
|
+
// ========== 合并单元格 ==========
|
|
696
345
|
handleSpanMethod({ row, column, rowIndex }) {
|
|
697
|
-
// 跳过小计/总计行,不做合并
|
|
698
346
|
if (row._rowType === 'subtotal' || row._rowType === 'total') {
|
|
699
347
|
return { rowspan: 1, colspan: 1 }
|
|
700
348
|
}
|
|
701
|
-
|
|
702
349
|
if (!this.groupColumns.length) return { rowspan: 1, colspan: 1 }
|
|
703
350
|
|
|
704
|
-
const data = this.
|
|
351
|
+
const data = this.processedTableData
|
|
705
352
|
const prop = column.property
|
|
706
353
|
const groupIndex = this.groupColumns.indexOf(prop)
|
|
707
|
-
|
|
708
354
|
if (groupIndex === -1) return { rowspan: 1, colspan: 1 }
|
|
709
355
|
|
|
710
|
-
// 检查缓存
|
|
711
356
|
const cacheKey = `${rowIndex}_${prop}`
|
|
712
357
|
if (this.spanCache[cacheKey]) return this.spanCache[cacheKey]
|
|
713
358
|
|
|
714
|
-
// 判断当前行是否与前一行在分组列上完全相同
|
|
715
359
|
if (rowIndex > 0) {
|
|
716
360
|
const prevRow = data[rowIndex - 1]
|
|
717
361
|
let isSame = true
|
|
@@ -723,190 +367,76 @@ export default {
|
|
|
723
367
|
}
|
|
724
368
|
}
|
|
725
369
|
if (isSame) {
|
|
726
|
-
const
|
|
727
|
-
this.spanCache[cacheKey] =
|
|
728
|
-
return
|
|
370
|
+
const res = { rowspan: 0, colspan: 1 }
|
|
371
|
+
this.spanCache[cacheKey] = res
|
|
372
|
+
return res
|
|
729
373
|
}
|
|
730
374
|
}
|
|
731
375
|
|
|
732
|
-
// 向下扫描计算合并行数
|
|
733
376
|
let count = 1
|
|
734
377
|
for (let i = rowIndex + 1; i < data.length; i++) {
|
|
735
378
|
const nextRow = data[i]
|
|
736
|
-
let
|
|
379
|
+
let same = true
|
|
737
380
|
for (let j = 0; j <= groupIndex; j++) {
|
|
738
381
|
const gp = this.groupColumns[j]
|
|
739
382
|
if (nextRow[gp] !== row[gp]) {
|
|
740
|
-
|
|
383
|
+
same = false
|
|
741
384
|
break
|
|
742
385
|
}
|
|
743
386
|
}
|
|
744
|
-
if (
|
|
745
|
-
|
|
746
|
-
} else {
|
|
747
|
-
break
|
|
748
|
-
}
|
|
387
|
+
if (same) count++
|
|
388
|
+
else break
|
|
749
389
|
}
|
|
750
390
|
|
|
751
|
-
const
|
|
752
|
-
this.spanCache[cacheKey] =
|
|
753
|
-
return
|
|
391
|
+
const res = { rowspan: count > 1 ? count : 1, colspan: 1 }
|
|
392
|
+
this.spanCache[cacheKey] = res
|
|
393
|
+
return res
|
|
754
394
|
},
|
|
755
395
|
|
|
756
|
-
//
|
|
396
|
+
// ========== 选择、排序、分页 ==========
|
|
757
397
|
handleSelectionChange(rows) {
|
|
758
|
-
// 过滤掉小计/总计行
|
|
759
398
|
this.selectedRows = rows.filter(r => !r._rowType)
|
|
760
399
|
this.$emit('selection-change', this.selectedRows)
|
|
761
400
|
},
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
handleSortChange({ prop, order }) {
|
|
765
|
-
this.$emit('sort-change', { prop, order })
|
|
401
|
+
handleSortChange(info) {
|
|
402
|
+
this.$emit('sort-change', info)
|
|
766
403
|
},
|
|
767
|
-
|
|
768
|
-
// ==================== 分页 ====================
|
|
769
404
|
handleSizeChange(size) {
|
|
770
405
|
this.$emit('size-change', size)
|
|
771
406
|
},
|
|
772
|
-
|
|
773
407
|
handleCurrentChange(page) {
|
|
774
408
|
this.$emit('page-change', page)
|
|
775
409
|
},
|
|
776
410
|
|
|
777
|
-
//
|
|
411
|
+
// ========== 对外暴露方法 ==========
|
|
778
412
|
getSelection() {
|
|
779
413
|
return this.selectedRows
|
|
780
414
|
},
|
|
781
|
-
|
|
782
415
|
clearSelection() {
|
|
783
416
|
this.$refs.table && this.$refs.table.clearSelection()
|
|
784
417
|
this.selectedRows = []
|
|
785
418
|
},
|
|
786
|
-
|
|
787
419
|
toggleRowSelection(row, selected) {
|
|
788
420
|
this.$refs.table && this.$refs.table.toggleRowSelection(row, selected)
|
|
789
421
|
},
|
|
790
|
-
|
|
791
422
|
toggleRowsSelection(rows, selected) {
|
|
792
|
-
if (rows
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
423
|
+
if (!rows || !rows.length) return
|
|
424
|
+
rows.forEach(row => {
|
|
425
|
+
this.$refs.table && this.$refs.table.toggleRowSelection(row, selected)
|
|
426
|
+
})
|
|
797
427
|
},
|
|
798
|
-
|
|
799
428
|
doLayout() {
|
|
800
429
|
this.$refs.table && this.$refs.table.doLayout()
|
|
801
|
-
},
|
|
802
|
-
|
|
803
|
-
// 获取列宽度(支持自适应)
|
|
804
|
-
getColumnWidth(col) {
|
|
805
|
-
// 如果有固定宽度,使用固定宽度
|
|
806
|
-
if (col.width) {
|
|
807
|
-
return typeof col.width === 'number' ? col.width : col.width.replace('px', '')
|
|
808
|
-
}
|
|
809
|
-
// 非固定列且虚拟滚动时使用自适应宽度
|
|
810
|
-
if (!col.fixed && this.virtualScroll) {
|
|
811
|
-
return this.autoColumnWidth
|
|
812
|
-
}
|
|
813
|
-
// 默认宽度
|
|
814
|
-
if (col.minWidth) {
|
|
815
|
-
return typeof col.minWidth === 'number' ? col.minWidth : col.minWidth.replace('px', '')
|
|
816
|
-
}
|
|
817
|
-
return 100
|
|
818
|
-
},
|
|
819
|
-
|
|
820
|
-
// 更新表格宽度(用于自适应列宽计算)
|
|
821
|
-
updateTableWidth() {
|
|
822
|
-
const tableWrapper = this.$refs.tableBody
|
|
823
|
-
if (tableWrapper) {
|
|
824
|
-
this.tableWidth = tableWrapper.offsetWidth || 1000
|
|
825
|
-
}
|
|
826
|
-
},
|
|
827
|
-
|
|
828
|
-
// ==================== 虚拟滚动 ====================
|
|
829
|
-
initVirtualScroll() {
|
|
830
|
-
this.updateTableWidth()
|
|
831
|
-
this.measureRowHeight()
|
|
832
|
-
if (this.processedTableData.length > 0) {
|
|
833
|
-
this.updateVisibleRange()
|
|
834
|
-
}
|
|
835
|
-
},
|
|
836
|
-
|
|
837
|
-
measureRowHeight() {
|
|
838
|
-
const el = this.$refs.table && this.$refs.table.$el
|
|
839
|
-
if (!el) return
|
|
840
|
-
const row = el.querySelector('.el-table__row')
|
|
841
|
-
if (row) {
|
|
842
|
-
this.rowHeight = row.offsetHeight || this.rowInitHeight
|
|
843
|
-
}
|
|
844
|
-
},
|
|
845
|
-
|
|
846
|
-
handleBodyScroll(event) {
|
|
847
|
-
const target = event.target
|
|
848
|
-
this.scrollLeft = target.scrollLeft
|
|
849
|
-
if (this.virtualScroll) {
|
|
850
|
-
this.scrollTop = target.scrollTop
|
|
851
|
-
this.updateVisibleRange()
|
|
852
|
-
}
|
|
853
|
-
},
|
|
854
|
-
|
|
855
|
-
updateVisibleRange() {
|
|
856
|
-
const totalCount = this.processedTableData.length
|
|
857
|
-
if (totalCount === 0 || this.rowHeight === 0) return
|
|
858
|
-
|
|
859
|
-
const visibleCount = Math.ceil((this.computedHeight || 400) / this.rowHeight)
|
|
860
|
-
|
|
861
|
-
this.visibleStartIndex = Math.max(0, Math.floor(this.scrollTop / this.rowHeight))
|
|
862
|
-
this.visibleEndIndex = Math.min(
|
|
863
|
-
totalCount,
|
|
864
|
-
this.visibleStartIndex + visibleCount
|
|
865
|
-
)
|
|
866
|
-
this.visibleCount = visibleCount
|
|
867
|
-
},
|
|
868
|
-
|
|
869
|
-
destroyVirtualScroll() {
|
|
870
|
-
this.scrollTop = 0
|
|
871
|
-
this.visibleStartIndex = 0
|
|
872
|
-
this.visibleEndIndex = 0
|
|
873
430
|
}
|
|
874
431
|
}
|
|
875
432
|
}
|
|
876
433
|
</script>
|
|
877
|
-
|
|
878
434
|
<style scoped>
|
|
879
435
|
.ex-table-wrapper {
|
|
880
436
|
width: 100%;
|
|
881
437
|
}
|
|
882
438
|
|
|
883
|
-
/*
|
|
884
|
-
.ex-table-wrapper >>> .ex-table-row-subtotal,
|
|
885
|
-
.ex-table >>> .ex-table-row-subtotal {
|
|
886
|
-
background-color: #f5f7fa;
|
|
887
|
-
font-weight: 600;
|
|
888
|
-
color: #303133;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/* 总计行样式 */
|
|
892
|
-
.ex-table-wrapper >>> .ex-table-row-total,
|
|
893
|
-
.ex-table >>> .ex-table-row-total {
|
|
894
|
-
background-color: #e8eaed;
|
|
895
|
-
font-weight: 700;
|
|
896
|
-
color: #303133;
|
|
897
|
-
border-top: 2px solid #c0c4cc;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
.ex-table-wrapper >>> .ex-table-row-subtotal:hover > td,
|
|
901
|
-
.ex-table >>> .ex-table-row-subtotal:hover > td {
|
|
902
|
-
background-color: #ebeef5 !important;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
.ex-table-wrapper >>> .ex-table-row-total:hover > td,
|
|
906
|
-
.ex-table >>> .ex-table-row-total:hover > td {
|
|
907
|
-
background-color: #dcdfe6 !important;
|
|
908
|
-
}
|
|
909
|
-
|
|
439
|
+
/* 表头栏 */
|
|
910
440
|
.ex-table-header {
|
|
911
441
|
display: flex;
|
|
912
442
|
align-items: center;
|
|
@@ -914,7 +444,6 @@ export default {
|
|
|
914
444
|
padding: 12px 0;
|
|
915
445
|
margin-bottom: 8px;
|
|
916
446
|
}
|
|
917
|
-
|
|
918
447
|
.ex-table-title {
|
|
919
448
|
font-size: 16px;
|
|
920
449
|
font-weight: 600;
|
|
@@ -922,7 +451,6 @@ export default {
|
|
|
922
451
|
position: relative;
|
|
923
452
|
padding-left: 12px;
|
|
924
453
|
}
|
|
925
|
-
|
|
926
454
|
.ex-table-title::before {
|
|
927
455
|
content: '';
|
|
928
456
|
position: absolute;
|
|
@@ -934,7 +462,6 @@ export default {
|
|
|
934
462
|
background: #409eff;
|
|
935
463
|
border-radius: 2px;
|
|
936
464
|
}
|
|
937
|
-
|
|
938
465
|
.ex-table-toolbar {
|
|
939
466
|
display: flex;
|
|
940
467
|
align-items: center;
|
|
@@ -943,62 +470,98 @@ export default {
|
|
|
943
470
|
|
|
944
471
|
.ex-table-body {
|
|
945
472
|
position: relative;
|
|
473
|
+
z-index: 1;
|
|
946
474
|
}
|
|
947
475
|
|
|
948
|
-
/*
|
|
949
|
-
.ex-table-
|
|
950
|
-
|
|
951
|
-
|
|
476
|
+
/* 分页区域 */
|
|
477
|
+
.ex-table-footer {
|
|
478
|
+
display: flex;
|
|
479
|
+
justify-content: flex-end;
|
|
480
|
+
padding: 16px 0;
|
|
952
481
|
}
|
|
953
482
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
483
|
+
/* ========== 小计/合计行样式 ========== */
|
|
484
|
+
::v-deep .ex-table-row-subtotal {
|
|
485
|
+
background-color: #f5f7fa;
|
|
486
|
+
font-weight: 600;
|
|
487
|
+
color: #303133;
|
|
957
488
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
489
|
+
::v-deep .ex-table-row-total {
|
|
490
|
+
background-color: #e8eaed;
|
|
491
|
+
font-weight: 700;
|
|
492
|
+
color: #303133;
|
|
493
|
+
border-top: 2px solid #c0c4cc;
|
|
494
|
+
}
|
|
495
|
+
::v-deep .ex-table-row-subtotal:hover > td {
|
|
496
|
+
background-color: #ebeef5 !important;
|
|
497
|
+
}
|
|
498
|
+
::v-deep .ex-table-row-total:hover > td {
|
|
499
|
+
background-color: #dcdfe6 !important;
|
|
961
500
|
}
|
|
962
501
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
502
|
+
/* ========== 滚动条:Y轴 + X轴 统一处理 ========== */
|
|
503
|
+
/* 合并为一个选择器块,避免重复导致的优先级问题 */
|
|
504
|
+
::v-deep .el-table__body-wrapper {
|
|
505
|
+
overflow-y: auto !important;
|
|
506
|
+
overflow-x: auto !important;
|
|
507
|
+
user-select: none;
|
|
508
|
+
-webkit-user-select: none;
|
|
509
|
+
}
|
|
510
|
+
::v-deep .el-table__body-wrapper::-webkit-scrollbar {
|
|
511
|
+
width: 8px;
|
|
512
|
+
height: 8px;
|
|
513
|
+
}
|
|
514
|
+
::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb {
|
|
515
|
+
background: #c1c1c1;
|
|
516
|
+
border-radius: 4px;
|
|
517
|
+
}
|
|
518
|
+
::v-deep .el-table__body-wrapper::-webkit-scrollbar-track {
|
|
519
|
+
background: #f1f1f1;
|
|
520
|
+
}
|
|
521
|
+
/* 滚动条角落(Y+X 交汇处) */
|
|
522
|
+
::v-deep .el-table__body-wrapper::-webkit-scrollbar-corner {
|
|
523
|
+
background: #f1f1f1;
|
|
967
524
|
}
|
|
968
525
|
|
|
969
|
-
/*
|
|
970
|
-
.
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
background: #fafafa;
|
|
526
|
+
/* 允许单元格内文字选中,但容器本身不可选中 */
|
|
527
|
+
::v-deep .el-table__body-wrapper .el-table__cell {
|
|
528
|
+
user-select: text;
|
|
529
|
+
-webkit-user-select: text;
|
|
974
530
|
}
|
|
975
531
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
532
|
+
/* ========== 固定列层级 + 背景 防文字穿透 ========== */
|
|
533
|
+
::v-deep .el-table__fixed {
|
|
534
|
+
z-index: 10 !important;
|
|
535
|
+
box-shadow: 4px 0 8px rgba(0, 0, 0, 0.08);
|
|
536
|
+
}
|
|
537
|
+
::v-deep .el-table__fixed-right {
|
|
538
|
+
z-index: 10 !important;
|
|
539
|
+
box-shadow: -4px 0 8px rgba(0, 0, 0, 0.08);
|
|
980
540
|
}
|
|
981
541
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
background: #
|
|
994
|
-
box-sizing: border-box;
|
|
542
|
+
/* 固定列单元格底色(纯白兜底,盖住滚动穿透文字) */
|
|
543
|
+
::v-deep .el-table__fixed .el-table__cell,
|
|
544
|
+
::v-deep .el-table__fixed-right .el-table__cell {
|
|
545
|
+
background-color: #ffffff !important;
|
|
546
|
+
}
|
|
547
|
+
::v-deep .el-table__fixed .el-table__row:nth-child(even) .el-table__cell,
|
|
548
|
+
::v-deep .el-table__fixed-right .el-table__row:nth-child(even) .el-table__cell {
|
|
549
|
+
background-color: #fafafa !important;
|
|
550
|
+
}
|
|
551
|
+
::v-deep .el-table__fixed .el-table__row:hover .el-table__cell,
|
|
552
|
+
::v-deep .el-table__fixed-right .el-table__row:hover .el-table__cell {
|
|
553
|
+
background-color: #f5f7fa !important;
|
|
995
554
|
}
|
|
996
555
|
|
|
997
|
-
.
|
|
998
|
-
|
|
556
|
+
::v-deep .el-table__fixed-header-wrapper,
|
|
557
|
+
::v-deep .el-table__fixed-right-header-wrapper {
|
|
558
|
+
z-index: 11 !important;
|
|
999
559
|
}
|
|
1000
560
|
|
|
1001
|
-
|
|
1002
|
-
|
|
561
|
+
/* ========== 虚拟滚动容器:正常布局流撑开滚动条 ========== */
|
|
562
|
+
::v-deep .vs-phantom {
|
|
563
|
+
position: relative;
|
|
564
|
+
box-sizing: border-box;
|
|
565
|
+
z-index: 1 !important;
|
|
1003
566
|
}
|
|
1004
|
-
</style>
|
|
567
|
+
</style>
|