stk-table-vue 0.2.3 → 0.2.6
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 +48 -5
- package/lib/src/StkTable/StkTable.vue.d.ts +24 -4
- package/lib/src/StkTable/index.d.ts +1 -0
- package/lib/src/StkTable/types/index.d.ts +11 -1
- 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 +178 -96
- package/package.json +5 -5
- package/src/StkTable/StkTable.vue +58 -24
- package/src/StkTable/types/index.ts +11 -1
- package/src/StkTable/useFixedStyle.ts +49 -28
- package/src/StkTable/useHighlight.ts +6 -3
- package/src/StkTable/useKeyboardArrowScroll.ts +8 -4
- package/src/StkTable/useVirtualScroll.ts +42 -11
- package/src/StkTable/utils.ts +66 -41
|
@@ -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],
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
>
|
|
168
168
|
<component :is="col.customCell" v-if="col.customCell" :col="col" :row="row" :cell-value="row[col.dataIndex]" />
|
|
169
169
|
<div v-else class="table-cell-wrapper" :title="row[col.dataIndex]">
|
|
170
|
-
{{ row[col.dataIndex] ??
|
|
170
|
+
{{ row[col.dataIndex] ?? getEmptyCellText(col, row) }}
|
|
171
171
|
</div>
|
|
172
172
|
</td>
|
|
173
173
|
</tr>
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
* [] 计算的高亮颜色,挂在数据源上对象上,若多个表格使用同一个数据源对象会有问题。需要深拷贝。(解决方案:获取组件uid)
|
|
190
190
|
* [] highlight-row 颜色不能恢复到active的颜色
|
|
191
191
|
*/
|
|
192
|
-
import { CSSProperties, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
192
|
+
import { CSSProperties, computed, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
193
193
|
import { Default_Row_Height } from './const';
|
|
194
194
|
import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKeyProp } from './types/index';
|
|
195
195
|
import { useAutoResize } from './useAutoResize';
|
|
@@ -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 | ((option: { row: DT; col: StkTableColumn<DT> }) => string);
|
|
243
243
|
/** 暂无数据兜底高度是否撑满 */
|
|
244
244
|
noDataFull?: boolean;
|
|
245
245
|
/** 是否展示暂无数据 */
|
|
@@ -258,7 +258,7 @@ const props = withDefaults(
|
|
|
258
258
|
* 给行附加className<br>
|
|
259
259
|
* FIXME: 是否需要优化,因为不传此prop会使表格行一直执行空函数,是否有影响
|
|
260
260
|
*/
|
|
261
|
-
rowClassName?: (row:
|
|
261
|
+
rowClassName?: (row: DT, i: number) => string;
|
|
262
262
|
/**
|
|
263
263
|
* 列宽是否可拖动<br>
|
|
264
264
|
* **不要设置**列minWidth,**必须**设置width<br>
|
|
@@ -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
|
|
|
@@ -442,6 +446,15 @@ const dataSourceCopy = shallowRef<DT[]>([...props.dataSource]);
|
|
|
442
446
|
/**高亮帧间隔
|
|
443
447
|
const highlightStepDuration = Highlight_Color_Change_Freq / 1000 + 's';*/
|
|
444
448
|
|
|
449
|
+
/** 空单元格占位字符 */
|
|
450
|
+
const getEmptyCellText = computed(() => {
|
|
451
|
+
if (typeof props.emptyCellText === 'string') {
|
|
452
|
+
return () => props.emptyCellText;
|
|
453
|
+
} else {
|
|
454
|
+
return (col: StkTableColumn<DT>, row: DT) => (props.emptyCellText as any)({ row, col });
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
445
458
|
/** rowKey缓存 */
|
|
446
459
|
const rowKeyGenStore = new WeakMap();
|
|
447
460
|
|
|
@@ -470,11 +483,11 @@ const {
|
|
|
470
483
|
initVirtualScrollX,
|
|
471
484
|
updateVirtualScrollY,
|
|
472
485
|
updateVirtualScrollX,
|
|
473
|
-
} = useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast });
|
|
486
|
+
} = useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast, tableHeaders });
|
|
474
487
|
|
|
475
|
-
const { getFixedStyle } = useFixedStyle({
|
|
488
|
+
const { getFixedStyle } = useFixedStyle<DT>({
|
|
476
489
|
props,
|
|
477
|
-
|
|
490
|
+
tableHeaders,
|
|
478
491
|
virtualScroll,
|
|
479
492
|
virtualScrollX,
|
|
480
493
|
virtualX_on,
|
|
@@ -554,8 +567,8 @@ onMounted(() => {
|
|
|
554
567
|
/** 处理默认排序 */
|
|
555
568
|
function dealDefaultSorter() {
|
|
556
569
|
if (!props.sortConfig.defaultSort) return;
|
|
557
|
-
const { dataIndex, order } = props.sortConfig.defaultSort;
|
|
558
|
-
setSorter(dataIndex as string, order);
|
|
570
|
+
const { dataIndex, order, silent } = { silent: false, ...props.sortConfig.defaultSort };
|
|
571
|
+
setSorter(dataIndex as string, order, { force: false, silent });
|
|
559
572
|
}
|
|
560
573
|
|
|
561
574
|
/**
|
|
@@ -587,6 +600,7 @@ function dealColumns() {
|
|
|
587
600
|
}
|
|
588
601
|
/** 所有子节点数量 */
|
|
589
602
|
let allChildrenLen = 0;
|
|
603
|
+
let allChildrenWidthSum = 0;
|
|
590
604
|
arr.forEach(col => {
|
|
591
605
|
// TODO: 继承父节点固定列配置
|
|
592
606
|
// if (parentFixed) {
|
|
@@ -596,10 +610,15 @@ function dealColumns() {
|
|
|
596
610
|
col.__PARENT__ = parent;
|
|
597
611
|
/** 一列中的子节点数量 */
|
|
598
612
|
let colChildrenLen = 1;
|
|
613
|
+
/** 多级表头的父节点宽度,通过叶子节点宽度计算得到 */
|
|
614
|
+
let colWidth = 0;
|
|
599
615
|
if (col.children) {
|
|
600
616
|
// DFS
|
|
601
|
-
|
|
617
|
+
const [len, widthSum] = flat(col.children, col, depth + 1 /* , col.fixed */);
|
|
618
|
+
colChildrenLen = len;
|
|
619
|
+
colWidth = widthSum;
|
|
602
620
|
} else {
|
|
621
|
+
colWidth = getColWidth(col);
|
|
603
622
|
tempHeaderLast.push(col); // 没有children的列作为colgroup
|
|
604
623
|
}
|
|
605
624
|
// 回溯
|
|
@@ -612,9 +631,14 @@ function dealColumns() {
|
|
|
612
631
|
if (colSpan !== 1) {
|
|
613
632
|
col.colSpan = colSpan;
|
|
614
633
|
}
|
|
634
|
+
if (!props.fixedMode && col.width === void 0) {
|
|
635
|
+
// 列赋值默认列宽。由于有些场景不需要设置width。
|
|
636
|
+
col.width = colWidth + 'px';
|
|
637
|
+
}
|
|
615
638
|
allChildrenLen += colChildrenLen;
|
|
639
|
+
allChildrenWidthSum += colWidth;
|
|
616
640
|
});
|
|
617
|
-
return allChildrenLen;
|
|
641
|
+
return [allChildrenLen, allChildrenWidthSum];
|
|
618
642
|
}
|
|
619
643
|
|
|
620
644
|
flat(copyColumn, null);
|
|
@@ -646,17 +670,17 @@ function colKeyGen(col: StkTableColumn<DT>) {
|
|
|
646
670
|
|
|
647
671
|
/** 获取列宽度样式 */
|
|
648
672
|
function getColWidthStyle(col: StkTableColumn<DT>) {
|
|
673
|
+
const width = getColWidthStr(col);
|
|
674
|
+
const minWidth = getColWidthStr(col, 'minWidth');
|
|
675
|
+
const maxWidth = getColWidthStr(col, 'maxWidth');
|
|
649
676
|
const style: CSSProperties = {
|
|
650
|
-
width
|
|
651
|
-
minWidth:
|
|
652
|
-
maxWidth:
|
|
677
|
+
width,
|
|
678
|
+
minWidth: minWidth ?? width,
|
|
679
|
+
maxWidth: maxWidth ?? width,
|
|
653
680
|
};
|
|
654
681
|
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;
|
|
682
|
+
style.minWidth = width;
|
|
683
|
+
style.maxWidth = width;
|
|
660
684
|
}
|
|
661
685
|
|
|
662
686
|
return style;
|
|
@@ -686,6 +710,15 @@ function getCellStyle(tagType: 1 | 2, col: StkTableColumn<DT>, depth?: number):
|
|
|
686
710
|
return style;
|
|
687
711
|
}
|
|
688
712
|
|
|
713
|
+
/** th title */
|
|
714
|
+
function getHeaderTitle(col: StkTableColumn<DT>): string {
|
|
715
|
+
// 不展示title
|
|
716
|
+
if (props.hideHeaderTitle === true || (Array.isArray(props.hideHeaderTitle) && props.hideHeaderTitle.includes(col.dataIndex))) {
|
|
717
|
+
return '';
|
|
718
|
+
}
|
|
719
|
+
return col.title || '';
|
|
720
|
+
}
|
|
721
|
+
|
|
689
722
|
/**
|
|
690
723
|
* 表头点击排序
|
|
691
724
|
* @param click 是否为点击表头触发
|
|
@@ -832,11 +865,12 @@ function setCurrentRow(rowKey: string, option = { silent: false }) {
|
|
|
832
865
|
* 设置表头排序状态
|
|
833
866
|
* @param dataIndex 列字段
|
|
834
867
|
* @param order 正序倒序
|
|
835
|
-
* @param option.sortOption
|
|
868
|
+
* @param option.sortOption 指定排序参数。同 StkTableColumn 中排序相关字段。建议从columns中find得到。
|
|
836
869
|
* @param option.sort 是否触发排序-默认true
|
|
837
870
|
* @param option.silent 是否禁止触发回调-默认true
|
|
871
|
+
* @param option.force 是否触发排序-默认true
|
|
838
872
|
*/
|
|
839
|
-
function setSorter(dataIndex: string, order: Order, option: { sortOption?: SortOption<DT>; silent?: boolean; sort?: boolean } = {}) {
|
|
873
|
+
function setSorter(dataIndex: string, order: Order, option: { sortOption?: SortOption<DT>; force?: boolean; silent?: boolean; sort?: boolean } = {}) {
|
|
840
874
|
const newOption = { silent: true, sortOption: null, sort: true, ...option };
|
|
841
875
|
sortCol.value = dataIndex;
|
|
842
876
|
sortOrderIndex.value = sortSwitchOrder.indexOf(order);
|
|
@@ -844,7 +878,7 @@ function setSorter(dataIndex: string, order: Order, option: { sortOption?: SortO
|
|
|
844
878
|
if (newOption.sort && dataSourceCopy.value?.length) {
|
|
845
879
|
// 如果表格有数据,则进行排序
|
|
846
880
|
const column = newOption.sortOption || tableHeaderLast.value.find(it => it.dataIndex === sortCol.value);
|
|
847
|
-
if (column) onColumnSort(column, false, { force: true, emit: !newOption.silent });
|
|
881
|
+
if (column) onColumnSort(column, false, { force: option.force ?? true, emit: !newOption.silent });
|
|
848
882
|
else console.warn('Can not find column by dataIndex:', sortCol.value);
|
|
849
883
|
}
|
|
850
884
|
return dataSourceCopy.value;
|
|
@@ -77,9 +77,19 @@ export type UniqKeyProp = UniqKey | UniqKeyFun;
|
|
|
77
77
|
export type SortConfig<T extends Record<string, any>> = {
|
|
78
78
|
/** 空值始终排在列表末尾 */
|
|
79
79
|
emptyToBottom?: boolean;
|
|
80
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* 默认排序(1.初始化时触发 2.排序方向为null时触发)
|
|
82
|
+
* 类似onMounted时,调用setSorter点了下表头。
|
|
83
|
+
*/
|
|
81
84
|
defaultSort?: {
|
|
82
85
|
dataIndex: keyof T;
|
|
83
86
|
order: Order;
|
|
87
|
+
/** 是否禁止触发sort-change事件。默认false,表示触发事件。 */
|
|
88
|
+
silent?: boolean;
|
|
84
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* string排序是否使用 String.prototype.localCompare
|
|
92
|
+
* 默认true (&$&应该false)
|
|
93
|
+
*/
|
|
94
|
+
stringLocaleCompare?: boolean;
|
|
85
95
|
};
|
|
@@ -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>();
|
|
@@ -76,6 +76,9 @@ export function useHighlight({ props, tableContainer }: Params) {
|
|
|
76
76
|
}
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
+
// $*$ 兼容vue2响应
|
|
80
|
+
highlightRowStore.value = { ...highlightRowStore.value };
|
|
81
|
+
|
|
79
82
|
if (highlightDimRowKeys.size > 0) {
|
|
80
83
|
// 还有高亮的行,则下一次循环
|
|
81
84
|
recursion();
|
|
@@ -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);
|