stk-table-vue 0.2.3 → 0.2.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 +18 -2
- package/lib/src/StkTable/StkTable.vue.d.ts +10 -1
- package/lib/src/StkTable/index.d.ts +1 -0
- package/lib/src/StkTable/types/index.d.ts +5 -0
- package/lib/src/StkTable/useFixedStyle.d.ts +3 -3
- package/lib/src/StkTable/useVirtualScroll.d.ts +3 -2
- package/lib/src/StkTable/utils.d.ts +3 -1
- package/lib/stk-table-vue.js +165 -91
- package/package.json +60 -60
- package/src/StkTable/StkTable.vue +41 -17
- package/src/StkTable/types/index.ts +5 -0
- package/src/StkTable/useFixedStyle.ts +49 -28
- package/src/StkTable/useHighlight.ts +3 -3
- package/src/StkTable/useKeyboardArrowScroll.ts +8 -4
- package/src/StkTable/useVirtualScroll.ts +42 -11
- package/src/StkTable/utils.ts +66 -41
package/package.json
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "stk-table-vue",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Simple realtime virtual table for vue3&vue2.7",
|
|
5
|
-
"main": "./lib/stk-table-vue.js",
|
|
6
|
-
"types": "./lib/StkTable/index.d.ts",
|
|
7
|
-
"packageManager": "pnpm@8.14.3",
|
|
8
|
-
"directories": {
|
|
9
|
-
"test": "test"
|
|
10
|
-
},
|
|
11
|
-
"type": "module",
|
|
12
|
-
"scripts": {
|
|
13
|
-
"dev": "vite",
|
|
14
|
-
"build": "vite build",
|
|
15
|
-
"test": "vitest"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"virtual table",
|
|
19
|
-
"vue",
|
|
20
|
-
"highlight",
|
|
21
|
-
"sticky"
|
|
22
|
-
],
|
|
23
|
-
"files": [
|
|
24
|
-
"lib",
|
|
25
|
-
"src"
|
|
26
|
-
],
|
|
27
|
-
"author": "japlus",
|
|
28
|
-
"repository": {
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "https://gitee.com/japlus/stk-table-vue"
|
|
31
|
-
},
|
|
32
|
-
"license": "MIT",
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@types/d3-interpolate": "^3.0.4",
|
|
35
|
-
"@types/node": "^20.11.14",
|
|
36
|
-
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
37
|
-
"@typescript-eslint/parser": "^6.14.0",
|
|
38
|
-
"@vitejs/plugin-vue": "^5.0.3",
|
|
39
|
-
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
|
40
|
-
"@vue/test-utils": "2.4.4",
|
|
41
|
-
"eslint": "^8.55.0",
|
|
42
|
-
"eslint-config-prettier": "^9.1.0",
|
|
43
|
-
"eslint-plugin-html": "^7.1.0",
|
|
44
|
-
"eslint-plugin-prettier": "^5.0.1",
|
|
45
|
-
"eslint-plugin-vue": "^9.19.2",
|
|
46
|
-
"happy-dom": "^12.10.3",
|
|
47
|
-
"less": "^4.2.0",
|
|
48
|
-
"postcss-discard-comments": "^6.0.1",
|
|
49
|
-
"postcss-preset-env": "^9.3.0",
|
|
50
|
-
"prettier": "^3.1.1",
|
|
51
|
-
"typescript": "^5.3.3",
|
|
52
|
-
"vite": "^5.
|
|
53
|
-
"vite-plugin-dts": "^3.
|
|
54
|
-
"vitest": "^1.1.0",
|
|
55
|
-
"vue": "^3.
|
|
56
|
-
"vue-eslint-parser": "^9.3.2"
|
|
57
|
-
},
|
|
58
|
-
"dependencies": {
|
|
59
|
-
"d3-interpolate": "^3.0.1"
|
|
60
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "stk-table-vue",
|
|
3
|
+
"version": "0.2.5",
|
|
4
|
+
"description": "Simple realtime virtual table for vue3&vue2.7",
|
|
5
|
+
"main": "./lib/stk-table-vue.js",
|
|
6
|
+
"types": "./lib/StkTable/index.d.ts",
|
|
7
|
+
"packageManager": "pnpm@8.14.3",
|
|
8
|
+
"directories": {
|
|
9
|
+
"test": "test"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "vite",
|
|
14
|
+
"build": "vite build",
|
|
15
|
+
"test": "vitest"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"virtual table",
|
|
19
|
+
"vue",
|
|
20
|
+
"highlight",
|
|
21
|
+
"sticky"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"lib",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"author": "japlus",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://gitee.com/japlus/stk-table-vue"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/d3-interpolate": "^3.0.4",
|
|
35
|
+
"@types/node": "^20.11.14",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
37
|
+
"@typescript-eslint/parser": "^6.14.0",
|
|
38
|
+
"@vitejs/plugin-vue": "^5.0.3",
|
|
39
|
+
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
|
40
|
+
"@vue/test-utils": "2.4.4",
|
|
41
|
+
"eslint": "^8.55.0",
|
|
42
|
+
"eslint-config-prettier": "^9.1.0",
|
|
43
|
+
"eslint-plugin-html": "^7.1.0",
|
|
44
|
+
"eslint-plugin-prettier": "^5.0.1",
|
|
45
|
+
"eslint-plugin-vue": "^9.19.2",
|
|
46
|
+
"happy-dom": "^12.10.3",
|
|
47
|
+
"less": "^4.2.0",
|
|
48
|
+
"postcss-discard-comments": "^6.0.1",
|
|
49
|
+
"postcss-preset-env": "^9.3.0",
|
|
50
|
+
"prettier": "^3.1.1",
|
|
51
|
+
"typescript": "^5.3.3",
|
|
52
|
+
"vite": "^5.1.4",
|
|
53
|
+
"vite-plugin-dts": "^3.7.3",
|
|
54
|
+
"vitest": "^1.1.0",
|
|
55
|
+
"vue": "^3.4.19",
|
|
56
|
+
"vue-eslint-parser": "^9.3.2"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"d3-interpolate": "^3.0.1"
|
|
60
|
+
}
|
|
61
61
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
:rowspan="virtualX_on ? 1 : col.rowSpan"
|
|
62
62
|
:colspan="col.colSpan"
|
|
63
63
|
:style="getCellStyle(1, col, rowIndex)"
|
|
64
|
-
:title="col
|
|
64
|
+
:title="getHeaderTitle(col)"
|
|
65
65
|
:class="[
|
|
66
66
|
col.sorter ? 'sortable' : '',
|
|
67
67
|
col.dataIndex === sortCol && sortOrderIndex !== 0 && 'sorter-' + sortSwitchOrder[sortOrderIndex],
|
|
@@ -200,7 +200,7 @@ import { useHighlight } from './useHighlight';
|
|
|
200
200
|
import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
|
|
201
201
|
import { useThDrag } from './useThDrag';
|
|
202
202
|
import { useVirtualScroll } from './useVirtualScroll';
|
|
203
|
-
import { howDeepTheHeader, tableSort } from './utils';
|
|
203
|
+
import { getColWidth, getColWidthStr, howDeepTheHeader, tableSort } from './utils';
|
|
204
204
|
|
|
205
205
|
/** Generic stands for DataType */
|
|
206
206
|
type DT = any;
|
|
@@ -239,7 +239,7 @@ const props = withDefaults(
|
|
|
239
239
|
/** 列唯一键 */
|
|
240
240
|
colKey?: UniqKeyProp;
|
|
241
241
|
/** 空值展示文字 */
|
|
242
|
-
emptyCellText?: string;
|
|
242
|
+
emptyCellText?: string; //TODO: 支持传入方法
|
|
243
243
|
/** 暂无数据兜底高度是否撑满 */
|
|
244
244
|
noDataFull?: boolean;
|
|
245
245
|
/** 是否展示暂无数据 */
|
|
@@ -287,6 +287,8 @@ const props = withDefaults(
|
|
|
287
287
|
optimizeVue2Scroll?: boolean;
|
|
288
288
|
/** 排序配置 */
|
|
289
289
|
sortConfig?: SortConfig<DT>;
|
|
290
|
+
/** 隐藏头部title。可传入dataIndex数组 */
|
|
291
|
+
hideHeaderTitle?: boolean | string[];
|
|
290
292
|
}>(),
|
|
291
293
|
{
|
|
292
294
|
width: '',
|
|
@@ -321,7 +323,9 @@ const props = withDefaults(
|
|
|
321
323
|
optimizeVue2Scroll: false,
|
|
322
324
|
sortConfig: () => ({
|
|
323
325
|
emptyToBottom: false,
|
|
326
|
+
stringLocaleCompare: true,
|
|
324
327
|
}),
|
|
328
|
+
hideHeaderTitle: false,
|
|
325
329
|
},
|
|
326
330
|
);
|
|
327
331
|
|
|
@@ -470,11 +474,11 @@ const {
|
|
|
470
474
|
initVirtualScrollX,
|
|
471
475
|
updateVirtualScrollY,
|
|
472
476
|
updateVirtualScrollX,
|
|
473
|
-
} = useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast });
|
|
477
|
+
} = useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast, tableHeaders });
|
|
474
478
|
|
|
475
|
-
const { getFixedStyle } = useFixedStyle({
|
|
479
|
+
const { getFixedStyle } = useFixedStyle<DT>({
|
|
476
480
|
props,
|
|
477
|
-
|
|
481
|
+
tableHeaders,
|
|
478
482
|
virtualScroll,
|
|
479
483
|
virtualScrollX,
|
|
480
484
|
virtualX_on,
|
|
@@ -587,6 +591,7 @@ function dealColumns() {
|
|
|
587
591
|
}
|
|
588
592
|
/** 所有子节点数量 */
|
|
589
593
|
let allChildrenLen = 0;
|
|
594
|
+
let allChildrenWidthSum = 0;
|
|
590
595
|
arr.forEach(col => {
|
|
591
596
|
// TODO: 继承父节点固定列配置
|
|
592
597
|
// if (parentFixed) {
|
|
@@ -596,10 +601,15 @@ function dealColumns() {
|
|
|
596
601
|
col.__PARENT__ = parent;
|
|
597
602
|
/** 一列中的子节点数量 */
|
|
598
603
|
let colChildrenLen = 1;
|
|
604
|
+
/** 多级表头的父节点宽度,通过叶子节点宽度计算得到 */
|
|
605
|
+
let colWidth = 0;
|
|
599
606
|
if (col.children) {
|
|
600
607
|
// DFS
|
|
601
|
-
|
|
608
|
+
const [len, widthSum] = flat(col.children, col, depth + 1 /* , col.fixed */);
|
|
609
|
+
colChildrenLen = len;
|
|
610
|
+
colWidth = widthSum;
|
|
602
611
|
} else {
|
|
612
|
+
colWidth = getColWidth(col);
|
|
603
613
|
tempHeaderLast.push(col); // 没有children的列作为colgroup
|
|
604
614
|
}
|
|
605
615
|
// 回溯
|
|
@@ -612,9 +622,14 @@ function dealColumns() {
|
|
|
612
622
|
if (colSpan !== 1) {
|
|
613
623
|
col.colSpan = colSpan;
|
|
614
624
|
}
|
|
625
|
+
if (!props.fixedMode && col.width === void 0) {
|
|
626
|
+
// 列赋值默认列宽。由于有些场景不需要设置width。
|
|
627
|
+
col.width = colWidth + 'px';
|
|
628
|
+
}
|
|
615
629
|
allChildrenLen += colChildrenLen;
|
|
630
|
+
allChildrenWidthSum += colWidth;
|
|
616
631
|
});
|
|
617
|
-
return allChildrenLen;
|
|
632
|
+
return [allChildrenLen, allChildrenWidthSum];
|
|
618
633
|
}
|
|
619
634
|
|
|
620
635
|
flat(copyColumn, null);
|
|
@@ -646,17 +661,17 @@ function colKeyGen(col: StkTableColumn<DT>) {
|
|
|
646
661
|
|
|
647
662
|
/** 获取列宽度样式 */
|
|
648
663
|
function getColWidthStyle(col: StkTableColumn<DT>) {
|
|
664
|
+
const width = getColWidthStr(col);
|
|
665
|
+
const minWidth = getColWidthStr(col, 'minWidth');
|
|
666
|
+
const maxWidth = getColWidthStr(col, 'maxWidth');
|
|
649
667
|
const style: CSSProperties = {
|
|
650
|
-
width
|
|
651
|
-
minWidth:
|
|
652
|
-
maxWidth:
|
|
668
|
+
width,
|
|
669
|
+
minWidth: minWidth ?? width,
|
|
670
|
+
maxWidth: maxWidth ?? width,
|
|
653
671
|
};
|
|
654
672
|
if (props.colResizable) {
|
|
655
|
-
style.minWidth =
|
|
656
|
-
style.maxWidth =
|
|
657
|
-
} else {
|
|
658
|
-
style.minWidth = col.minWidth === void 0 ? col.width : col.minWidth;
|
|
659
|
-
style.maxWidth = col.maxWidth === void 0 ? col.width : col.maxWidth;
|
|
673
|
+
style.minWidth = width;
|
|
674
|
+
style.maxWidth = width;
|
|
660
675
|
}
|
|
661
676
|
|
|
662
677
|
return style;
|
|
@@ -686,6 +701,15 @@ function getCellStyle(tagType: 1 | 2, col: StkTableColumn<DT>, depth?: number):
|
|
|
686
701
|
return style;
|
|
687
702
|
}
|
|
688
703
|
|
|
704
|
+
/** th title */
|
|
705
|
+
function getHeaderTitle(col: StkTableColumn<DT>): string {
|
|
706
|
+
// 不展示title
|
|
707
|
+
if (props.hideHeaderTitle === true || (Array.isArray(props.hideHeaderTitle) && props.hideHeaderTitle.includes(col.dataIndex))) {
|
|
708
|
+
return '';
|
|
709
|
+
}
|
|
710
|
+
return col.title || '';
|
|
711
|
+
}
|
|
712
|
+
|
|
689
713
|
/**
|
|
690
714
|
* 表头点击排序
|
|
691
715
|
* @param click 是否为点击表头触发
|
|
@@ -832,7 +856,7 @@ function setCurrentRow(rowKey: string, option = { silent: false }) {
|
|
|
832
856
|
* 设置表头排序状态
|
|
833
857
|
* @param dataIndex 列字段
|
|
834
858
|
* @param order 正序倒序
|
|
835
|
-
* @param option.sortOption
|
|
859
|
+
* @param option.sortOption 指定排序参数。同 StkTableColumn 中排序相关字段。建议从columns中find得到。
|
|
836
860
|
* @param option.sort 是否触发排序-默认true
|
|
837
861
|
* @param option.silent 是否禁止触发回调-默认true
|
|
838
862
|
*/
|
|
@@ -4,9 +4,9 @@ import { StkTableColumn } from './types';
|
|
|
4
4
|
import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
|
|
5
5
|
import { getColWidth } from './utils';
|
|
6
6
|
|
|
7
|
-
type Options = {
|
|
7
|
+
type Options<T extends Record<string, any>> = {
|
|
8
8
|
props: any;
|
|
9
|
-
|
|
9
|
+
tableHeaders: Ref<StkTableColumn<T>[][]>;
|
|
10
10
|
virtualScroll: Ref<VirtualScrollStore>;
|
|
11
11
|
virtualScrollX: Ref<VirtualScrollXStore>;
|
|
12
12
|
virtualX_on: Ref<boolean>;
|
|
@@ -17,33 +17,52 @@ type Options = {
|
|
|
17
17
|
* @param param0
|
|
18
18
|
* @returns
|
|
19
19
|
*/
|
|
20
|
-
export function useFixedStyle
|
|
20
|
+
export function useFixedStyle<DT extends Record<string, any>>({
|
|
21
|
+
props,
|
|
22
|
+
tableHeaders,
|
|
23
|
+
virtualScroll,
|
|
24
|
+
virtualScrollX,
|
|
25
|
+
virtualX_on,
|
|
26
|
+
virtualX_offsetRight,
|
|
27
|
+
}: Options<DT>) {
|
|
21
28
|
const fixedColumnsPositionStore = computed(() => {
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
/** dataIndex 作为唯一标识 */
|
|
30
|
+
const colKeyStore: Record<string, number> = {};
|
|
31
|
+
/** 没有dataIndex 的多级表头列,使用对象引用做标识 */
|
|
32
|
+
const refStore = new WeakMap<StkTableColumn<DT>, number>();
|
|
33
|
+
tableHeaders.value.forEach(cols => {
|
|
34
|
+
let left = 0;
|
|
35
|
+
/**遍历右侧fixed时,因为left已经遍历过一次了。所以,可以拿到right遍历边界 */
|
|
36
|
+
let rightStartIndex = 0;
|
|
37
|
+
for (let i = 0; i < cols.length; i++) {
|
|
38
|
+
const item = cols[i];
|
|
39
|
+
if (item.fixed === 'left') {
|
|
40
|
+
if (item.dataIndex) {
|
|
41
|
+
colKeyStore[item.dataIndex] = left;
|
|
42
|
+
} else {
|
|
43
|
+
refStore.set(item, left);
|
|
44
|
+
}
|
|
45
|
+
left += getColWidth(item);
|
|
46
|
+
}
|
|
47
|
+
if (!rightStartIndex && item.fixed === 'right') {
|
|
48
|
+
rightStartIndex = i;
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
let right = 0;
|
|
52
|
+
for (let i = cols.length - 1; i >= rightStartIndex; i--) {
|
|
53
|
+
const item = cols[i];
|
|
54
|
+
if (item.fixed === 'right') {
|
|
55
|
+
if (item.dataIndex) {
|
|
56
|
+
colKeyStore[item.dataIndex] = right;
|
|
57
|
+
} else {
|
|
58
|
+
refStore.set(item, right);
|
|
59
|
+
}
|
|
60
|
+
right += getColWidth(item);
|
|
61
|
+
}
|
|
43
62
|
}
|
|
44
|
-
}
|
|
63
|
+
});
|
|
45
64
|
|
|
46
|
-
return
|
|
65
|
+
return { refStore, colKeyStore };
|
|
47
66
|
});
|
|
48
67
|
/**
|
|
49
68
|
* 固定列的style
|
|
@@ -52,9 +71,10 @@ export function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualSc
|
|
|
52
71
|
* @param depth 深度。tagType = 1时使用
|
|
53
72
|
*/
|
|
54
73
|
function getFixedStyle(tagType: 1 | 2, col: StkTableColumn<any>, depth = 0): CSSProperties {
|
|
55
|
-
const { fixed
|
|
74
|
+
const { fixed } = col;
|
|
56
75
|
const isFixedLeft = fixed === 'left';
|
|
57
76
|
const style: CSSProperties = {};
|
|
77
|
+
const { colKeyStore, refStore } = fixedColumnsPositionStore.value;
|
|
58
78
|
// TD
|
|
59
79
|
if (Is_Legacy_Mode) {
|
|
60
80
|
style.position = 'relative';
|
|
@@ -83,10 +103,11 @@ export function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualSc
|
|
|
83
103
|
style.right = `${virtualX_offsetRight.value}px`;
|
|
84
104
|
}
|
|
85
105
|
} else {
|
|
106
|
+
const lr = (col.dataIndex ? colKeyStore[col.dataIndex] : refStore.get(col)) + 'px';
|
|
86
107
|
if (isFixedLeft) {
|
|
87
|
-
style.left =
|
|
108
|
+
style.left = lr;
|
|
88
109
|
} else {
|
|
89
|
-
style.right =
|
|
110
|
+
style.right = lr;
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
113
|
}
|
|
@@ -30,9 +30,9 @@ export function useHighlight({ props, tableContainer }: Params) {
|
|
|
30
30
|
*/
|
|
31
31
|
const highlightRowStore = ref<Record<UniqKey, HighlightRowStore>>({});
|
|
32
32
|
|
|
33
|
-
const highlightFrom = Highlight_Color[props.theme].from;
|
|
34
|
-
const highlightTo = Highlight_Color[props.theme].to;
|
|
35
|
-
const highlightInter = computed(() => interpolateRgb(highlightFrom, highlightTo));
|
|
33
|
+
const highlightFrom = computed(() => Highlight_Color[props.theme].from);
|
|
34
|
+
const highlightTo = computed(() => Highlight_Color[props.theme].to);
|
|
35
|
+
const highlightInter = computed(() => interpolateRgb(highlightFrom.value, highlightTo.value));
|
|
36
36
|
|
|
37
37
|
/** 存放高亮行的key*/
|
|
38
38
|
const highlightDimRowKeys = new Set<UniqKey>();
|
|
@@ -44,11 +44,15 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
|
|
|
44
44
|
if (!isMouseOver) return; // 不悬浮还是要触发键盘事件的
|
|
45
45
|
e.preventDefault(); // 不触发键盘默认的箭头事件
|
|
46
46
|
|
|
47
|
-
const { scrollTop, rowHeight,
|
|
47
|
+
const { scrollTop, rowHeight, containerHeight } = virtualScroll.value;
|
|
48
48
|
const { scrollLeft } = virtualScrollX.value;
|
|
49
49
|
const { headless, headerRowHeight } = props;
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
// 这里不用virtualScroll 中的pageSize,因为我需要上一页的最后一条放在下一页的第一条
|
|
51
52
|
const headerHeight = headless ? 0 : tableHeaders.value.length * (headerRowHeight || rowHeight);
|
|
53
|
+
/** 表体的page */
|
|
54
|
+
const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
|
|
55
|
+
|
|
52
56
|
if (e.code === SCROLL_CODES[0]) {
|
|
53
57
|
scrollTo(scrollTop - rowHeight, null);
|
|
54
58
|
} else if (e.code === SCROLL_CODES[1]) {
|
|
@@ -58,9 +62,9 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
|
|
|
58
62
|
} else if (e.code === SCROLL_CODES[3]) {
|
|
59
63
|
scrollTo(null, scrollLeft - rowHeight);
|
|
60
64
|
} else if (e.code === SCROLL_CODES[4]) {
|
|
61
|
-
scrollTo(scrollTop - rowHeight *
|
|
65
|
+
scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
|
|
62
66
|
} else if (e.code === SCROLL_CODES[5]) {
|
|
63
|
-
scrollTo(scrollTop + rowHeight *
|
|
67
|
+
scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -4,10 +4,11 @@ import { StkTableColumn } from './types';
|
|
|
4
4
|
import { getColWidth } from './utils';
|
|
5
5
|
|
|
6
6
|
type Option<DT extends Record<string, any>> = {
|
|
7
|
-
tableContainer: Ref<HTMLElement | undefined>;
|
|
8
7
|
props: any;
|
|
8
|
+
tableContainer: Ref<HTMLElement | undefined>;
|
|
9
9
|
dataSourceCopy: ShallowRef<DT[]>;
|
|
10
10
|
tableHeaderLast: Ref<StkTableColumn<DT>[]>;
|
|
11
|
+
tableHeaders: Ref<StkTableColumn<DT>[][]>;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
/** 暂存纵向虚拟滚动的数据 */
|
|
@@ -49,7 +50,13 @@ const VUE2_SCROLL_TIMEOUT_MS = 200;
|
|
|
49
50
|
* @param param0
|
|
50
51
|
* @returns
|
|
51
52
|
*/
|
|
52
|
-
export function useVirtualScroll<DT extends Record<string, any>>({
|
|
53
|
+
export function useVirtualScroll<DT extends Record<string, any>>({
|
|
54
|
+
props,
|
|
55
|
+
tableContainer,
|
|
56
|
+
dataSourceCopy,
|
|
57
|
+
tableHeaderLast,
|
|
58
|
+
tableHeaders,
|
|
59
|
+
}: Option<DT>) {
|
|
53
60
|
const virtualScroll = ref<VirtualScrollStore>({
|
|
54
61
|
containerHeight: 0,
|
|
55
62
|
rowHeight: props.rowHeight,
|
|
@@ -140,10 +147,14 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
|
|
|
140
147
|
} else {
|
|
141
148
|
containerHeight = offsetHeight || Default_Table_Height;
|
|
142
149
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
const { headless, headerRowHeight } = props;
|
|
151
|
+
let pageSize = Math.ceil(containerHeight / rowHeight);
|
|
152
|
+
if (!headless) {
|
|
153
|
+
/** 表头高度占几行表体高度数 */
|
|
154
|
+
const headerToBodyRowHeightCount = Math.floor((tableHeaders.value.length * (headerRowHeight || rowHeight)) / rowHeight);
|
|
155
|
+
pageSize -= headerToBodyRowHeightCount; //减去表头行数
|
|
156
|
+
}
|
|
157
|
+
Object.assign(virtualScroll.value, { containerHeight, pageSize });
|
|
147
158
|
updateVirtualScrollY(scrollTop);
|
|
148
159
|
}
|
|
149
160
|
|
|
@@ -168,9 +179,30 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
|
|
|
168
179
|
/** 通过滚动条位置,计算虚拟滚动的参数 */
|
|
169
180
|
function updateVirtualScrollY(sTop = 0) {
|
|
170
181
|
const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
|
|
171
|
-
|
|
172
|
-
|
|
182
|
+
// 先更新滚动条位置记录,其他地方可能有依赖。(stripe 时ArrowUp/Down滚动依赖)
|
|
183
|
+
virtualScroll.value.scrollTop = sTop;
|
|
184
|
+
let startIndex = Math.floor(sTop / rowHeight);
|
|
185
|
+
if (props.stripe) {
|
|
186
|
+
startIndex -= 1; //预渲染1行
|
|
187
|
+
}
|
|
188
|
+
if (startIndex < 0) {
|
|
189
|
+
startIndex = 0;
|
|
190
|
+
}
|
|
191
|
+
if (props.stripe && startIndex !== 0) {
|
|
192
|
+
const scrollRows = Math.abs(oldStartIndex - startIndex);
|
|
193
|
+
// 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
|
|
194
|
+
if (scrollRows < 2) {
|
|
195
|
+
return;
|
|
196
|
+
} else if (scrollRows % 2) {
|
|
197
|
+
startIndex -= 1; // 奇数-1变成偶数
|
|
198
|
+
}
|
|
199
|
+
}
|
|
173
200
|
let endIndex = startIndex + pageSize;
|
|
201
|
+
if (props.stripe) {
|
|
202
|
+
// 由于上方预渲染一行,这里也要预渲染1+1行
|
|
203
|
+
endIndex += 2;
|
|
204
|
+
}
|
|
205
|
+
const offsetTop = startIndex * rowHeight; // startIndex之前的高度
|
|
174
206
|
if (endIndex > dataSourceCopy.value.length) {
|
|
175
207
|
endIndex = dataSourceCopy.value.length; // 溢出index修正
|
|
176
208
|
}
|
|
@@ -184,13 +216,12 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
|
|
|
184
216
|
// 向上滚动
|
|
185
217
|
Object.assign(virtualScroll.value, {
|
|
186
218
|
startIndex,
|
|
187
|
-
offsetTop,
|
|
188
219
|
endIndex,
|
|
189
|
-
|
|
220
|
+
offsetTop,
|
|
190
221
|
});
|
|
191
222
|
} else {
|
|
192
223
|
// vue2向下滚动优化
|
|
193
|
-
|
|
224
|
+
virtualScroll.value.endIndex = endIndex;
|
|
194
225
|
vue2ScrollYTimeout = window.setTimeout(() => {
|
|
195
226
|
Object.assign(virtualScroll.value, { startIndex, offsetTop });
|
|
196
227
|
}, VUE2_SCROLL_TIMEOUT_MS);
|