stk-table-vue 0.8.3 → 0.8.5
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/README.md +21 -54
- package/lib/src/StkTable/useMaxRowSpan.d.ts +14 -0
- package/lib/src/StkTable/useMergeCells.d.ts +4 -2
- package/lib/src/StkTable/useVirtualScroll.d.ts +3 -2
- package/lib/stk-table-vue.js +2797 -2737
- package/lib/style.css +412 -410
- package/package.json +3 -3
- package/src/StkTable/StkTable.vue +25 -26
- package/src/StkTable/style.less +4 -2
- package/src/StkTable/useMaxRowSpan.ts +56 -0
- package/src/StkTable/useMergeCells.ts +18 -17
- package/src/StkTable/useVirtualScroll.ts +46 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stk-table-vue",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Simple realtime virtual table for vue3 and vue2.7",
|
|
5
5
|
"main": "./lib/stk-table-vue.js",
|
|
6
6
|
"types": "./lib/src/StkTable/index.d.ts",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"typescript": "^5.8.3",
|
|
64
64
|
"vite": "^7.0.2",
|
|
65
65
|
"vite-plugin-dts": "3.9.1",
|
|
66
|
-
"vitepress": "^1.6.
|
|
66
|
+
"vitepress": "^1.6.4",
|
|
67
67
|
"vitepress-demo-plugin": "^1.4.5",
|
|
68
|
-
"vitepress-plugin-llms": "^1.7.
|
|
68
|
+
"vitepress-plugin-llms": "^1.7.5",
|
|
69
69
|
"vitest": "^3.2.4",
|
|
70
70
|
"vue": "^3.5.17",
|
|
71
71
|
"vue-eslint-parser": "^9.4.2"
|
|
@@ -36,15 +36,14 @@
|
|
|
36
36
|
@scroll="onTableScroll"
|
|
37
37
|
@wheel="onTableWheel"
|
|
38
38
|
>
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
<div
|
|
41
41
|
v-if="isSRBRActive && virtual"
|
|
42
42
|
class="row-by-row-table-height"
|
|
43
43
|
:style="{ height: dataSourceCopy.length * virtualScroll.rowHeight + 'px' }"
|
|
44
|
-
|
|
44
|
+
><!-- 这个元素用于整数行虚拟滚动时,撑开父容器的高度) --></div>
|
|
45
45
|
|
|
46
46
|
<div v-if="colResizable" ref="colResizeIndicatorRef" class="column-resize-indicator"></div>
|
|
47
|
-
<!-- 表格主体 -->
|
|
48
47
|
<table
|
|
49
48
|
class="stk-table-main"
|
|
50
49
|
:style="{ width, minWidth, maxWidth }"
|
|
@@ -52,15 +51,13 @@
|
|
|
52
51
|
'fixed-mode': props.fixedMode,
|
|
53
52
|
}"
|
|
54
53
|
>
|
|
55
|
-
<thead v-if="!headless"
|
|
54
|
+
<thead v-if="!headless">
|
|
56
55
|
<tr v-for="(row, rowIndex) in tableHeaders" :key="rowIndex" @contextmenu="onHeaderMenu($event)">
|
|
57
|
-
<!-- 这个th用于横向虚拟滚动表格左边距,width、maxWidth 用于兼容低版本浏览器 -->
|
|
58
56
|
<th
|
|
59
57
|
v-if="virtualX_on"
|
|
60
58
|
class="vt-x-left"
|
|
61
59
|
:style="`min-width:${virtualScrollX.offsetLeft}px;width:${virtualScrollX.offsetLeft}px`"
|
|
62
60
|
></th>
|
|
63
|
-
<!-- v for中最后一行才用 切割。-->
|
|
64
61
|
<th
|
|
65
62
|
v-for="(col, colIndex) in virtualX_on && rowIndex === tableHeaders.length - 1 ? virtualX_columnPart : row"
|
|
66
63
|
:key="colKeyGen(col)"
|
|
@@ -101,17 +98,14 @@
|
|
|
101
98
|
</template>
|
|
102
99
|
<SortIcon v-if="col.sorter" class="table-header-sorter" />
|
|
103
100
|
</div>
|
|
104
|
-
<!-- 列宽拖动handler -->
|
|
105
101
|
<div v-if="colResizeOn(col)" class="table-header-resizer right" @mousedown="onThResizeMouseDown($event, col)"></div>
|
|
106
102
|
</th>
|
|
107
|
-
<!-- 这个th用于横向虚拟滚动表格右边距 width, min-width 用于兼容低版本浏览器-->
|
|
108
103
|
<th v-if="virtualX_on" class="vt-x-right" :style="`min-width:${virtualX_offsetRight}px;width:${virtualX_offsetRight}px`"></th>
|
|
109
104
|
</tr>
|
|
110
105
|
</thead>
|
|
111
106
|
|
|
112
107
|
<tbody class="stk-tbody-main" @dragover="onTrDragOver" @dragenter="onTrDragEnter" @dragend="onTrDragEnd">
|
|
113
108
|
<tr v-if="virtual_on && !isSRBRActive" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
|
|
114
|
-
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
115
109
|
<td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
|
|
116
110
|
<template v-if="fixedMode && headless">
|
|
117
111
|
<td v-for="col in virtualX_columnPart" :key="colKeyGen(col)" :style="cellStyleMap[TagType.TD].get(colKeyGen(col))"></td>
|
|
@@ -141,10 +135,8 @@
|
|
|
141
135
|
@mouseleave="onTrMouseLeave($event)"
|
|
142
136
|
@drop="onTrDrop($event, getRowIndex(rowIndex))"
|
|
143
137
|
>
|
|
144
|
-
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
145
138
|
<td v-if="virtualX_on" class="vt-x-left"></td>
|
|
146
139
|
<td v-if="row && row.__EXPANDED_ROW__" :colspan="virtualX_columnPart.length">
|
|
147
|
-
<!-- TODO: support wheel -->
|
|
148
140
|
<div class="table-cell-wrapper">
|
|
149
141
|
<slot name="expand" :row="row.__EXPANDED_ROW__" :col="row.__EXPANDED_COL__">
|
|
150
142
|
{{ row.__EXPANDED_ROW__?.[row.__EXPANDED_COL__.dataIndex] ?? '' }}
|
|
@@ -293,13 +285,14 @@ import { useFixedStyle } from './useFixedStyle';
|
|
|
293
285
|
import { useGetFixedColPosition } from './useGetFixedColPosition';
|
|
294
286
|
import { useHighlight } from './useHighlight';
|
|
295
287
|
import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
|
|
288
|
+
import { useMaxRowSpan } from './useMaxRowSpan';
|
|
289
|
+
import { useMergeCells } from './useMergeCells';
|
|
296
290
|
import { useRowExpand } from './useRowExpand';
|
|
297
291
|
import { useScrollRowByRow } from './useScrollRowByRow';
|
|
298
292
|
import { useThDrag } from './useThDrag';
|
|
299
293
|
import { useTrDrag } from './useTrDrag';
|
|
300
294
|
import { useTree } from './useTree';
|
|
301
295
|
import { useVirtualScroll } from './useVirtualScroll';
|
|
302
|
-
import { useMergeCells } from './useMergeCells';
|
|
303
296
|
import { createStkTableId, getCalculatedColWidth, getColWidth } from './utils/constRefUtils';
|
|
304
297
|
import { howDeepTheHeader, isEmptyValue, tableSort, transformWidthToStr } from './utils/index';
|
|
305
298
|
|
|
@@ -649,7 +642,6 @@ const emits = defineEmits<{
|
|
|
649
642
|
// }>();
|
|
650
643
|
|
|
651
644
|
const tableContainerRef = ref<HTMLDivElement>();
|
|
652
|
-
const theadRef = ref<HTMLElement>();
|
|
653
645
|
const colResizeIndicatorRef = ref<HTMLDivElement>();
|
|
654
646
|
const trRef = ref<HTMLTableRowElement[]>();
|
|
655
647
|
|
|
@@ -687,7 +679,7 @@ const sortSwitchOrder: Order[] = [null, 'desc', 'asc'];
|
|
|
687
679
|
* @eg
|
|
688
680
|
* ```js
|
|
689
681
|
* [
|
|
690
|
-
* [{
|
|
682
|
+
* [{dataIndex:'id',...}], // 第0行列配置
|
|
691
683
|
* [], // 第一行列配置
|
|
692
684
|
* //...
|
|
693
685
|
* ]
|
|
@@ -761,6 +753,8 @@ const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({
|
|
|
761
753
|
|
|
762
754
|
const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
|
|
763
755
|
|
|
756
|
+
const { maxRowSpan, updateMaxRowSpan } = useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy });
|
|
757
|
+
|
|
764
758
|
const {
|
|
765
759
|
virtualScroll,
|
|
766
760
|
virtualScrollX,
|
|
@@ -777,7 +771,16 @@ const {
|
|
|
777
771
|
updateVirtualScrollX,
|
|
778
772
|
setAutoHeight,
|
|
779
773
|
clearAllAutoHeight,
|
|
780
|
-
} = useVirtualScroll({ tableContainerRef, trRef, props, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen });
|
|
774
|
+
} = useVirtualScroll({ tableContainerRef, trRef, props, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen, maxRowSpan });
|
|
775
|
+
|
|
776
|
+
const {
|
|
777
|
+
hiddenCellMap, //
|
|
778
|
+
mergeCellsWrapper,
|
|
779
|
+
hoverMergedCells,
|
|
780
|
+
updateHoverMergedCells,
|
|
781
|
+
activeMergedCells,
|
|
782
|
+
updateActiveMergedCells,
|
|
783
|
+
} = useMergeCells({ props, tableHeaderLast, rowKeyGen, colKeyGen, virtual_dataSourcePart });
|
|
781
784
|
|
|
782
785
|
const getFixedColPosition = useGetFixedColPosition({ colKeyGen, tableHeadersForCalc });
|
|
783
786
|
|
|
@@ -831,19 +834,12 @@ const { toggleExpandRow, setRowExpand } = useRowExpand({ dataSourceCopy, rowKeyG
|
|
|
831
834
|
|
|
832
835
|
const { toggleTreeNode, setTreeExpand, flatTreeData } = useTree({ props, dataSourceCopy, rowKeyGen, emits });
|
|
833
836
|
|
|
834
|
-
const { hiddenCellMap, mergeCellsWrapper, hoverMergedCells, updateHoverMergedCells, activeMergedCells, updateActiveMergedCells } = useMergeCells({
|
|
835
|
-
props,
|
|
836
|
-
tableHeaderLast,
|
|
837
|
-
rowKeyGen,
|
|
838
|
-
colKeyGen,
|
|
839
|
-
virtual_dataSourcePart,
|
|
840
|
-
});
|
|
841
|
-
|
|
842
837
|
watch(
|
|
843
838
|
() => props.columns,
|
|
844
839
|
() => {
|
|
845
840
|
dealColumns();
|
|
846
|
-
|
|
841
|
+
updateMaxRowSpan();
|
|
842
|
+
// nextTick: initVirtualScrollX need get container width。
|
|
847
843
|
nextTick(() => {
|
|
848
844
|
initVirtualScrollX();
|
|
849
845
|
updateFixedShadow();
|
|
@@ -885,12 +881,13 @@ watch(
|
|
|
885
881
|
console.warn('invalid dataSource');
|
|
886
882
|
return;
|
|
887
883
|
}
|
|
888
|
-
|
|
884
|
+
|
|
889
885
|
let needInitVirtualScrollY = false;
|
|
890
886
|
if (dataSourceCopy.value.length !== val.length) {
|
|
891
887
|
needInitVirtualScrollY = true;
|
|
892
888
|
}
|
|
893
889
|
initDataSource(val);
|
|
890
|
+
updateMaxRowSpan();
|
|
894
891
|
// if data length is not change, not init virtual scroll
|
|
895
892
|
if (needInitVirtualScrollY) {
|
|
896
893
|
// wait for table render,initVirtualScrollY has get `dom` operation.
|
|
@@ -913,6 +910,7 @@ watch(
|
|
|
913
910
|
|
|
914
911
|
dealColumns();
|
|
915
912
|
initDataSource();
|
|
913
|
+
updateMaxRowSpan();
|
|
916
914
|
|
|
917
915
|
onMounted(() => {
|
|
918
916
|
initVirtualScroll();
|
|
@@ -1327,7 +1325,7 @@ function onTableScroll(e: Event) {
|
|
|
1327
1325
|
if (!e?.target) return;
|
|
1328
1326
|
|
|
1329
1327
|
const { scrollTop, scrollLeft } = e.target as HTMLElement;
|
|
1330
|
-
const { scrollTop: vScrollTop
|
|
1328
|
+
const { scrollTop: vScrollTop } = virtualScroll.value;
|
|
1331
1329
|
const { scrollLeft: vScrollLeft } = virtualScrollX.value;
|
|
1332
1330
|
const isYScroll = scrollTop !== vScrollTop;
|
|
1333
1331
|
const isXScroll = scrollLeft !== vScrollLeft;
|
|
@@ -1349,6 +1347,7 @@ function onTableScroll(e: Event) {
|
|
|
1349
1347
|
}
|
|
1350
1348
|
|
|
1351
1349
|
if (isYScroll) {
|
|
1350
|
+
const { startIndex, endIndex } = virtualScroll.value;
|
|
1352
1351
|
emits('scroll', e, { startIndex, endIndex });
|
|
1353
1352
|
}
|
|
1354
1353
|
if (isXScroll) {
|
package/src/StkTable/style.less
CHANGED
|
@@ -381,7 +381,8 @@
|
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
.highlight-cell {
|
|
384
|
-
animation: stk-table-dim
|
|
384
|
+
animation-name: stk-table-dim;
|
|
385
|
+
animation-duration: var(--highlight-duration);
|
|
385
386
|
animation-timing-function: var(--highlight-timing-function);
|
|
386
387
|
/* 必须分开写,否则var(step(x))不兼容旧浏览器*/
|
|
387
388
|
}
|
|
@@ -504,7 +505,8 @@
|
|
|
504
505
|
|
|
505
506
|
/* td inherit tr bgc*/
|
|
506
507
|
.highlight-row {
|
|
507
|
-
animation: stk-table-dim
|
|
508
|
+
animation-name: stk-table-dim;
|
|
509
|
+
animation-duration: var(--highlight-duration);
|
|
508
510
|
/* 必须分开写,否则var(step(x))不兼容旧浏览器*/
|
|
509
511
|
animation-timing-function: var(--highlight-timing-function);
|
|
510
512
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ShallowRef } from "vue";
|
|
2
|
+
import { PrivateStkTableColumn, RowKeyGen, UniqKey } from "./types";
|
|
3
|
+
|
|
4
|
+
type Options = {
|
|
5
|
+
props:any,
|
|
6
|
+
tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
|
|
7
|
+
rowKeyGen: RowKeyGen;
|
|
8
|
+
dataSourceCopy: ShallowRef<any[]>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy }: Options) {
|
|
12
|
+
/** max rowspan of each row */
|
|
13
|
+
const maxRowSpan = new Map<UniqKey, number>();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Use dataSourceCopy and tableHeaderLast to calculate maxRowSpan
|
|
17
|
+
* @link {maxRowSpan}
|
|
18
|
+
*/
|
|
19
|
+
function updateMaxRowSpan() {
|
|
20
|
+
if(!props.virtual) {
|
|
21
|
+
if(maxRowSpan.size) maxRowSpan.clear();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
maxRowSpan.clear();
|
|
25
|
+
|
|
26
|
+
const data = dataSourceCopy.value;
|
|
27
|
+
const columns = tableHeaderLast.value;
|
|
28
|
+
|
|
29
|
+
const columnsWithMerge = columns.filter(col => col.mergeCells);
|
|
30
|
+
if (!columnsWithMerge.length) return;
|
|
31
|
+
|
|
32
|
+
const dataLength = data.length;
|
|
33
|
+
const mergeColumnsLength = columnsWithMerge.length;
|
|
34
|
+
|
|
35
|
+
for (let rowIndex = 0; rowIndex < dataLength; rowIndex++) {
|
|
36
|
+
const row = data[rowIndex];
|
|
37
|
+
const rowKey = rowKeyGen(row);
|
|
38
|
+
let currentMax = maxRowSpan.get(rowKey) || 0;
|
|
39
|
+
|
|
40
|
+
for (let colIndex = 0; colIndex < mergeColumnsLength; colIndex++) {
|
|
41
|
+
const col = columnsWithMerge[colIndex];
|
|
42
|
+
const { rowspan = 1 } = col.mergeCells!({ row, col, rowIndex, colIndex }) || {};
|
|
43
|
+
|
|
44
|
+
if (rowspan > 1 && rowspan > currentMax) {
|
|
45
|
+
currentMax = rowspan;
|
|
46
|
+
maxRowSpan.set(rowKey, currentMax);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
maxRowSpan,
|
|
54
|
+
updateMaxRowSpan
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { ref, ShallowRef, watch } from 'vue';
|
|
2
2
|
import { ColKeyGen, MergeCellsParam, PrivateStkTableColumn, RowKeyGen, UniqKey } from './types';
|
|
3
3
|
import { pureCellKeyGen } from './utils';
|
|
4
|
-
|
|
4
|
+
type Options = {
|
|
5
|
+
props: any;
|
|
6
|
+
tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
|
|
7
|
+
rowKeyGen: RowKeyGen;
|
|
8
|
+
colKeyGen: ColKeyGen;
|
|
9
|
+
virtual_dataSourcePart: ShallowRef<any[]>;
|
|
10
|
+
}
|
|
5
11
|
export function useMergeCells({
|
|
6
12
|
props,
|
|
7
13
|
tableHeaderLast,
|
|
8
14
|
rowKeyGen,
|
|
9
15
|
colKeyGen,
|
|
10
16
|
virtual_dataSourcePart,
|
|
11
|
-
}: {
|
|
12
|
-
props: any;
|
|
13
|
-
tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
|
|
14
|
-
rowKeyGen: RowKeyGen;
|
|
15
|
-
colKeyGen: ColKeyGen;
|
|
16
|
-
virtual_dataSourcePart: ShallowRef<any[]>;
|
|
17
|
-
}) {
|
|
17
|
+
}:Options ) {
|
|
18
18
|
/**
|
|
19
19
|
* which cell need be hidden
|
|
20
20
|
* - key: rowKey
|
|
@@ -33,16 +33,19 @@ export function useMergeCells({
|
|
|
33
33
|
/** click current row , which rowspan cells should be highlight */
|
|
34
34
|
const activeMergedCells = ref(new Set<string>());
|
|
35
35
|
|
|
36
|
+
|
|
36
37
|
watch([virtual_dataSourcePart, tableHeaderLast], () => {
|
|
37
38
|
hiddenCellMap.value = {};
|
|
38
39
|
hoverRowMap.value = {};
|
|
39
40
|
});
|
|
40
41
|
|
|
41
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* abstract the logic of hiding cells
|
|
44
|
+
*/
|
|
42
45
|
function hideCells(rowKey: UniqKey, startIndex: number, count: number, isSelfRow = false, mergeCellKey: string) {
|
|
43
46
|
for (let i = startIndex; i < startIndex + count; i++) {
|
|
44
47
|
if (!isSelfRow || i !== startIndex) {
|
|
45
|
-
//
|
|
48
|
+
// self row does not need to be hidden
|
|
46
49
|
const nextCol = tableHeaderLast.value[i];
|
|
47
50
|
if (!nextCol) break;
|
|
48
51
|
const nextColKey = colKeyGen.value(nextCol);
|
|
@@ -81,20 +84,18 @@ export function useMergeCells({
|
|
|
81
84
|
if (colspan === 1 && rowspan === 1) return;
|
|
82
85
|
|
|
83
86
|
const rowKey = rowKeyGen(row);
|
|
87
|
+
|
|
84
88
|
const colKey = colKeyGen.value(col);
|
|
85
|
-
const dataSourceSlice = virtual_dataSourcePart.value.slice();
|
|
86
89
|
const curColIndex = tableHeaderLast.value.findIndex(item => colKeyGen.value(item) === colKey);
|
|
87
|
-
const curRowIndex =
|
|
90
|
+
const curRowIndex = virtual_dataSourcePart.value.findIndex(item => rowKeyGen(item) === rowKey);
|
|
88
91
|
const mergedCellKey = pureCellKeyGen(rowKey, colKey);
|
|
89
92
|
|
|
90
93
|
if (curRowIndex === -1) return;
|
|
91
|
-
|
|
94
|
+
|
|
92
95
|
for (let i = curRowIndex; i < curRowIndex + rowspan; i++) {
|
|
93
|
-
const row =
|
|
96
|
+
const row = virtual_dataSourcePart.value[i];
|
|
94
97
|
if (!row) break;
|
|
95
|
-
|
|
96
|
-
const isSelfRow = i === curRowIndex;
|
|
97
|
-
hideCells(rKey, curColIndex, colspan, isSelfRow, mergedCellKey);
|
|
98
|
+
hideCells(rowKeyGen(row), curColIndex, colspan, i === curRowIndex, mergedCellKey);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
return { colspan, rowspan };
|
|
@@ -11,6 +11,7 @@ type Option<DT extends Record<string, any>> = {
|
|
|
11
11
|
tableHeaderLast: ShallowRef<PrivateStkTableColumn<DT>[]>;
|
|
12
12
|
tableHeaders: ShallowRef<PrivateStkTableColumn<DT>[][]>;
|
|
13
13
|
rowKeyGen: RowKeyGen;
|
|
14
|
+
maxRowSpan: Map<UniqKey, number>;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
/** 暂存纵向虚拟滚动的数据 */
|
|
@@ -52,7 +53,7 @@ export type VirtualScrollXStore = {
|
|
|
52
53
|
const VUE2_SCROLL_TIMEOUT_MS = 200;
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
+
* virtual scroll
|
|
56
57
|
* @param param0
|
|
57
58
|
* @returns
|
|
58
59
|
*/
|
|
@@ -64,8 +65,8 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
64
65
|
tableHeaderLast,
|
|
65
66
|
tableHeaders,
|
|
66
67
|
rowKeyGen,
|
|
68
|
+
maxRowSpan,
|
|
67
69
|
}: Option<DT>) {
|
|
68
|
-
/** 表头高度 */
|
|
69
70
|
const tableHeaderHeight = ref(props.headerRowHeight);
|
|
70
71
|
|
|
71
72
|
const virtualScroll = ref<VirtualScrollStore>({
|
|
@@ -279,7 +280,6 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
279
280
|
// 先更新滚动条位置记录,其他地方有依赖。(stripe 时ArrowUp/Down滚动依赖)
|
|
280
281
|
virtualScroll.value.scrollTop = sTop;
|
|
281
282
|
|
|
282
|
-
// 非虚拟滚动不往下执行
|
|
283
283
|
if (!virtual_on.value) {
|
|
284
284
|
return;
|
|
285
285
|
}
|
|
@@ -325,6 +325,41 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
325
325
|
endIndex = startIndex + pageSize;
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
+
if (maxRowSpan.size) {
|
|
329
|
+
// fix startIndex:查找是否有合并行跨越当前startIndex
|
|
330
|
+
let correctedStartIndex = startIndex;
|
|
331
|
+
let correctedEndIndex = endIndex;
|
|
332
|
+
|
|
333
|
+
for (let i = 0; i < startIndex; i++) {
|
|
334
|
+
const row = dataSourceCopyTemp[i];
|
|
335
|
+
if (!row) continue;
|
|
336
|
+
const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
|
|
337
|
+
if (spanEndIndex > startIndex) {
|
|
338
|
+
// 找到跨越startIndex的合并行,将startIndex修正为合并行的起始索引
|
|
339
|
+
correctedStartIndex = i;
|
|
340
|
+
if (spanEndIndex > endIndex) {
|
|
341
|
+
// 合并行跨越了整个可视区
|
|
342
|
+
correctedEndIndex = spanEndIndex;
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// fix endIndex:查找是否有合并行跨越当前endIndex
|
|
349
|
+
for (let i = correctedStartIndex; i < endIndex; i++) {
|
|
350
|
+
const row = dataSourceCopyTemp[i];
|
|
351
|
+
if (!row) continue;
|
|
352
|
+
const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
|
|
353
|
+
if (spanEndIndex > correctedEndIndex) {
|
|
354
|
+
// 找到跨越endIndex的合并行,将endIndex修正为合并行的结束索引
|
|
355
|
+
correctedEndIndex = Math.max(spanEndIndex, correctedEndIndex);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
startIndex = correctedStartIndex;
|
|
360
|
+
endIndex = correctedEndIndex;
|
|
361
|
+
}
|
|
362
|
+
|
|
328
363
|
if (stripe && startIndex > 0 && startIndex % 2) {
|
|
329
364
|
// 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
|
|
330
365
|
startIndex -= 1; // 奇数-1变成偶数
|
|
@@ -338,7 +373,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
338
373
|
endIndex = Math.min(endIndex, dataLength);
|
|
339
374
|
|
|
340
375
|
if (startIndex >= endIndex) {
|
|
341
|
-
//
|
|
376
|
+
// fallback
|
|
342
377
|
startIndex = endIndex - pageSize;
|
|
343
378
|
}
|
|
344
379
|
|
|
@@ -351,20 +386,20 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
351
386
|
offsetTop = autoRowHeightTop;
|
|
352
387
|
} else {
|
|
353
388
|
if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
|
|
354
|
-
//
|
|
389
|
+
// Not change: not update
|
|
355
390
|
return;
|
|
356
391
|
}
|
|
357
392
|
offsetTop = startIndex * rowHeight;
|
|
358
393
|
}
|
|
359
394
|
|
|
360
395
|
/**
|
|
361
|
-
*
|
|
396
|
+
* en: If scroll faster than one page, roll back
|
|
362
397
|
*/
|
|
363
398
|
if (!optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
|
|
364
|
-
//
|
|
399
|
+
// scroll up
|
|
365
400
|
Object.assign(virtualScroll.value, { startIndex, endIndex, offsetTop });
|
|
366
401
|
} else {
|
|
367
|
-
// vue2
|
|
402
|
+
// vue2 scroll down optimize
|
|
368
403
|
virtualScroll.value.endIndex = endIndex;
|
|
369
404
|
vue2ScrollYTimeout = window.setTimeout(() => {
|
|
370
405
|
Object.assign(virtualScroll.value, { startIndex, offsetTop });
|
|
@@ -374,7 +409,9 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
374
409
|
|
|
375
410
|
let vue2ScrollXTimeout: null | number = null;
|
|
376
411
|
|
|
377
|
-
/**
|
|
412
|
+
/**
|
|
413
|
+
* Calculate virtual scroll parameters based on horizontal scroll bar position
|
|
414
|
+
*/
|
|
378
415
|
function updateVirtualScrollX(sLeft = 0) {
|
|
379
416
|
if (!props.virtualX) return;
|
|
380
417
|
const tableHeaderLastValue = tableHeaderLast.value;
|
|
@@ -384,7 +421,6 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
384
421
|
const { scrollLeft, containerWidth } = virtualScrollX.value;
|
|
385
422
|
let startIndex = 0;
|
|
386
423
|
let offsetLeft = 0;
|
|
387
|
-
/** 列宽累加 */
|
|
388
424
|
let colWidthSum = 0;
|
|
389
425
|
/** 固定左侧列宽 */
|
|
390
426
|
let leftColWidthSum = 0;
|