stk-table-vue 0.3.2 → 0.3.4
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 +23 -5
- package/lib/src/StkTable/StkTable.vue.d.ts +23 -4
- package/lib/src/StkTable/useColResize.d.ts +3 -3
- package/lib/src/StkTable/useFixedCol.d.ts +4 -4
- package/lib/src/StkTable/useHighlight.d.ts +3 -3
- package/lib/src/StkTable/useVirtualScroll.d.ts +4 -1
- package/lib/stk-table-vue.js +81 -70
- package/lib/style.css +6 -12
- package/package.json +8 -3
- package/src/StkTable/StkTable.vue +39 -27
- package/src/StkTable/const.ts +1 -0
- package/src/StkTable/style.less +16 -37
- package/src/StkTable/useColResize.ts +5 -5
- package/src/StkTable/useFixedCol.ts +4 -4
- package/src/StkTable/useFixedStyle.ts +29 -8
- package/src/StkTable/useHighlight.ts +26 -25
- package/src/StkTable/useVirtualScroll.ts +21 -24
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ repo:
|
|
|
16
16
|
- [x] 虚拟滚动默认通过js计算行高亮背景色,可通过 `useCss` 设置为css @keyframe实现 。
|
|
17
17
|
- [x] 支持配置高亮参数(持续时间,颜色,频率(虚拟滚动))。(`v0.2.9`)
|
|
18
18
|
- [x] `setHighlightDimRow`/`setHighlightCellRow`支持自定义高亮css类名。(`v0.2.9`)
|
|
19
|
-
- [x] 使用 `Web Animations API` 实现高亮。
|
|
19
|
+
- [x] 使用 `Web Animations API` 实现高亮。(`v0.3.4` 变更为默认值)
|
|
20
20
|
* [x] 虚拟滚动。
|
|
21
21
|
- [x] 纵向。
|
|
22
22
|
- [x] 横向(必须设置列宽)。
|
|
@@ -223,7 +223,7 @@ export type StkProps = {
|
|
|
223
223
|
highlightConfig?: {
|
|
224
224
|
/** 高亮持续时间(s) */
|
|
225
225
|
duration?: number;
|
|
226
|
-
/**
|
|
226
|
+
/** 高亮帧率*/
|
|
227
227
|
fps?: number;
|
|
228
228
|
};
|
|
229
229
|
/** 序号列配置 */
|
|
@@ -231,6 +231,14 @@ export type StkProps = {
|
|
|
231
231
|
/** 序号列起始下标 用于适配分页 */
|
|
232
232
|
startIndex?: number;
|
|
233
233
|
};
|
|
234
|
+
/**
|
|
235
|
+
* 固定头,固定列实现方式。
|
|
236
|
+
*
|
|
237
|
+
* relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
|
|
238
|
+
*
|
|
239
|
+
* 低版本浏览器只能为'relative',
|
|
240
|
+
*/
|
|
241
|
+
cellFixedMode?: 'sticky' | 'relative';
|
|
234
242
|
};
|
|
235
243
|
```
|
|
236
244
|
|
|
@@ -541,10 +549,20 @@ export type SortConfig<T extends Record<string, any>> = {
|
|
|
541
549
|
### 鼠标悬浮表头时,不展示title
|
|
542
550
|
* 将 `StkTableColumn` 中的 `title` 字段置为 "" 空字符串。这样th中就没有title了。
|
|
543
551
|
* 使用 `StkTableColumn` 中的 `customHeaderCell` 属性中,自定义表头渲染。
|
|
544
|
-
###
|
|
545
|
-
|
|
546
|
-
*
|
|
552
|
+
### 性能
|
|
553
|
+
#### highlight
|
|
554
|
+
* 在虚拟滚动下高亮强制使用 `css @keyframes` 实现动画。`setHighlightDimRow`/`setHighlightCellRow` 最后一个参数传入 `{method: 'css'}` 即可。(滚动后动画会中断)
|
|
555
|
+
* 指定 `{method:'animation'}` 在虚拟滚动下使用animation api实现动画。好处是动画流畅,且滚动后动画不中断。坏处是不支持帧率配置。
|
|
547
556
|
* 配置 `props.highlightConfig.fps` 指定高亮帧率。降低帧率有利于性能。
|
|
557
|
+
#### relative fixed
|
|
558
|
+
* 配置 `props.cellFixedMode` 为 `relative` 时,将使用相对定位实现固定列与固定表头,相较于`sticky`的实现,渲染合成层更少。
|
|
559
|
+
* 问题:若开启了纵向虚拟滚动,不开启横向虚拟滚动,且不设置某些列宽时。如果纵向滚动导致某些列宽变化,则会导致右侧固定列计算错误。
|
|
560
|
+
#### tr 分层
|
|
561
|
+
* 通过css选择器将 stk-table tbody tr 配置 `transform:translateZ(0)` 对每行 tr 进行分层。在 `customCell` 较多且复杂时,尝试开启此功能可能对性能有帮助。
|
|
548
562
|
|
|
549
563
|
## Other
|
|
550
564
|
* `$*$` 兼容注释
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
### Planed removal APi
|
|
568
|
+
* `setHighlightDimRow` 中的 `method="js"`。观察animation Api 是否足够满足使用场景。若足够满足计划在后期移除,并且可以移除 `d3-interpolate` 依赖。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, UniqKeyProp } from './types/index';
|
|
1
|
+
import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, UniqKey, UniqKeyProp } from './types/index';
|
|
2
2
|
/** Generic stands for DataType */
|
|
3
3
|
type DT = any;
|
|
4
4
|
/**
|
|
@@ -126,6 +126,14 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
|
|
|
126
126
|
highlightConfig?: HighlightConfig | undefined;
|
|
127
127
|
/** 序号列配置 */
|
|
128
128
|
seqConfig?: SeqConfig | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* 固定头,固定列实现方式。
|
|
131
|
+
*
|
|
132
|
+
* relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
|
|
133
|
+
*
|
|
134
|
+
* 低版本浏览器只能为'relative',
|
|
135
|
+
*/
|
|
136
|
+
cellFixedMode?: "sticky" | "relative" | undefined;
|
|
129
137
|
}>, {
|
|
130
138
|
width: string;
|
|
131
139
|
fixedMode: boolean;
|
|
@@ -165,6 +173,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
|
|
|
165
173
|
hideHeaderTitle: boolean;
|
|
166
174
|
highlightConfig: () => {};
|
|
167
175
|
seqConfig: () => {};
|
|
176
|
+
cellFixedMode: string;
|
|
168
177
|
}>, {
|
|
169
178
|
/** 初始化横向纵向虚拟滚动 */
|
|
170
179
|
initVirtualScroll: (height?: number | undefined) => void;
|
|
@@ -177,13 +186,13 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
|
|
|
177
186
|
/** 设置高亮渐暗单元格 */
|
|
178
187
|
setHighlightDimCell: (rowKeyValue: string, dataIndex: string, option?: {
|
|
179
188
|
className?: string | undefined;
|
|
180
|
-
method?: "
|
|
189
|
+
method?: "animation" | "css" | undefined;
|
|
181
190
|
keyframe?: Keyframe[] | PropertyIndexedKeyframes | null | undefined;
|
|
182
191
|
duration?: number | undefined;
|
|
183
192
|
}) => void;
|
|
184
193
|
/** 设置高亮渐暗行 */
|
|
185
|
-
setHighlightDimRow: (rowKeyValues:
|
|
186
|
-
method?: "
|
|
194
|
+
setHighlightDimRow: (rowKeyValues: UniqKey[], option?: {
|
|
195
|
+
method?: "animation" | "css" | "js" | undefined;
|
|
187
196
|
useCss?: boolean | undefined;
|
|
188
197
|
className?: string | undefined;
|
|
189
198
|
keyframe?: Keyframe[] | PropertyIndexedKeyframes | null | undefined;
|
|
@@ -314,6 +323,14 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
|
|
|
314
323
|
highlightConfig?: HighlightConfig | undefined;
|
|
315
324
|
/** 序号列配置 */
|
|
316
325
|
seqConfig?: SeqConfig | undefined;
|
|
326
|
+
/**
|
|
327
|
+
* 固定头,固定列实现方式。
|
|
328
|
+
*
|
|
329
|
+
* relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
|
|
330
|
+
*
|
|
331
|
+
* 低版本浏览器只能为'relative',
|
|
332
|
+
*/
|
|
333
|
+
cellFixedMode?: "sticky" | "relative" | undefined;
|
|
317
334
|
}>, {
|
|
318
335
|
width: string;
|
|
319
336
|
fixedMode: boolean;
|
|
@@ -353,6 +370,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
|
|
|
353
370
|
hideHeaderTitle: boolean;
|
|
354
371
|
highlightConfig: () => {};
|
|
355
372
|
seqConfig: () => {};
|
|
373
|
+
cellFixedMode: string;
|
|
356
374
|
}>>> & {
|
|
357
375
|
onScroll?: ((ev: Event, data: {
|
|
358
376
|
startIndex: number;
|
|
@@ -415,6 +433,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
|
|
|
415
433
|
hideHeaderTitle: boolean | string[];
|
|
416
434
|
highlightConfig: HighlightConfig;
|
|
417
435
|
seqConfig: SeqConfig;
|
|
436
|
+
cellFixedMode: "sticky" | "relative";
|
|
418
437
|
}, {}>, {
|
|
419
438
|
tableHeader?(_: {
|
|
420
439
|
col: StkTableColumn<any>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Ref, ShallowRef } from 'vue';
|
|
2
|
-
import { StkTableColumn } from './types';
|
|
1
|
+
import { ComputedRef, Ref, ShallowRef } from 'vue';
|
|
2
|
+
import { StkTableColumn, UniqKey } from './types';
|
|
3
3
|
type Params<DT extends Record<string, any>> = {
|
|
4
4
|
props: any;
|
|
5
5
|
emits: any;
|
|
6
6
|
tableContainerRef: Ref<HTMLElement | undefined>;
|
|
7
7
|
tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
|
|
8
8
|
colResizeIndicatorRef: Ref<HTMLElement | undefined>;
|
|
9
|
-
colKeyGen: (p: any) =>
|
|
9
|
+
colKeyGen: ComputedRef<(p: any) => UniqKey>;
|
|
10
10
|
};
|
|
11
11
|
/** 列宽拖动 */
|
|
12
12
|
export declare function useColResize<DT extends Record<string, any>>({ tableContainerRef, tableHeaderLast, colResizeIndicatorRef, props, emits, colKeyGen, }: Params<DT>): {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Ref, ShallowRef } from 'vue';
|
|
2
|
-
import { StkTableColumn } from './types';
|
|
1
|
+
import { ComputedRef, Ref, ShallowRef } from 'vue';
|
|
2
|
+
import { StkTableColumn, UniqKey } from './types';
|
|
3
3
|
type Params<T extends Record<string, any>> = {
|
|
4
4
|
props: any;
|
|
5
|
-
colKeyGen: (col: StkTableColumn<T>) =>
|
|
5
|
+
colKeyGen: ComputedRef<(col: StkTableColumn<T>) => UniqKey>;
|
|
6
6
|
tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
|
|
7
7
|
tableHeaderLast: ShallowRef<StkTableColumn<T>[]>;
|
|
8
8
|
tableContainerRef: Ref<HTMLDivElement | undefined>;
|
|
@@ -13,7 +13,7 @@ type Params<T extends Record<string, any>> = {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, tableHeaders, tableHeaderLast, tableContainerRef }: Params<DT>): {
|
|
15
15
|
/** 固定列class */
|
|
16
|
-
fixedColClassMap:
|
|
16
|
+
fixedColClassMap: ComputedRef<Map<any, any>>;
|
|
17
17
|
/** 处理固定列阴影 */
|
|
18
18
|
dealFixedColShadow: () => void;
|
|
19
19
|
/** 滚动条变化时,更新需要展示阴影的列 */
|
|
@@ -9,9 +9,9 @@ type Params = {
|
|
|
9
9
|
* 高亮单元格,行
|
|
10
10
|
*/
|
|
11
11
|
export declare function useHighlight({ props, stkTableId, tableContainerRef }: Params): {
|
|
12
|
-
highlightSteps: number;
|
|
12
|
+
highlightSteps: number | null;
|
|
13
13
|
setHighlightDimRow: (rowKeyValues: UniqKey[], option?: {
|
|
14
|
-
method?: '
|
|
14
|
+
method?: 'animation' | 'css' | 'js';
|
|
15
15
|
/** @deprecated 请使用method */
|
|
16
16
|
useCss?: boolean;
|
|
17
17
|
className?: string;
|
|
@@ -20,7 +20,7 @@ export declare function useHighlight({ props, stkTableId, tableContainerRef }: P
|
|
|
20
20
|
}) => void;
|
|
21
21
|
setHighlightDimCell: (rowKeyValue: string, dataIndex: string, option?: {
|
|
22
22
|
className?: string;
|
|
23
|
-
method?: '
|
|
23
|
+
method?: 'animation' | 'css';
|
|
24
24
|
keyframe?: Parameters<Animatable['animate']>['0'];
|
|
25
25
|
duration?: number;
|
|
26
26
|
}) => void;
|
|
@@ -26,8 +26,10 @@ export type VirtualScrollStore = {
|
|
|
26
26
|
};
|
|
27
27
|
/** 暂存横向虚拟滚动的数据 */
|
|
28
28
|
export type VirtualScrollXStore = {
|
|
29
|
-
/**
|
|
29
|
+
/** 父容器宽度 */
|
|
30
30
|
containerWidth: number;
|
|
31
|
+
/** 滚动容器的宽度 */
|
|
32
|
+
scrollWidth: number;
|
|
31
33
|
/** 开始位置 */
|
|
32
34
|
startIndex: number;
|
|
33
35
|
/** 结束始位置 */
|
|
@@ -54,6 +56,7 @@ export declare function useVirtualScroll<DT extends Record<string, any>>({ props
|
|
|
54
56
|
}>;
|
|
55
57
|
virtualScrollX: Ref<{
|
|
56
58
|
containerWidth: number;
|
|
59
|
+
scrollWidth: number;
|
|
57
60
|
startIndex: number;
|
|
58
61
|
endIndex: number;
|
|
59
62
|
offsetLeft: number;
|
package/lib/stk-table-vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { onMounted, onBeforeUnmount, watch, ref, shallowRef, computed, defineComponent, toRaw, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createCommentVNode, createElementVNode, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, renderSlot, createTextVNode } from "vue";
|
|
1
|
+
import { onMounted, onBeforeUnmount, watch, ref, shallowRef, computed, defineComponent, nextTick, toRaw, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createCommentVNode, createElementVNode, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, renderSlot, createTextVNode } from "vue";
|
|
2
2
|
import { interpolateRgb } from "d3-interpolate";
|
|
3
3
|
const DEFAULT_COL_WIDTH = "100";
|
|
4
4
|
const DEFAULT_TABLE_HEIGHT = 100;
|
|
@@ -260,7 +260,7 @@ function useColResize({
|
|
|
260
260
|
const { clientX } = e;
|
|
261
261
|
const { scrollLeft, scrollTop } = tableContainerRef.value;
|
|
262
262
|
const { left } = tableContainerRef.value.getBoundingClientRect();
|
|
263
|
-
let colIndex = tableHeaderLast.value.findIndex((it) => colKeyGen(it) === colKeyGen(col));
|
|
263
|
+
let colIndex = tableHeaderLast.value.findIndex((it) => colKeyGen.value(it) === colKeyGen.value(col));
|
|
264
264
|
if (isPrev) {
|
|
265
265
|
colIndex -= 1;
|
|
266
266
|
col = tableHeaderLast.value[colIndex];
|
|
@@ -307,7 +307,7 @@ function useColResize({
|
|
|
307
307
|
let width = getCalculatedColWidth(lastCol) + moveX;
|
|
308
308
|
if (width < props.colMinWidth)
|
|
309
309
|
width = props.colMinWidth;
|
|
310
|
-
const curCol = tableHeaderLast.value.find((it) => colKeyGen(it) === colKeyGen(lastCol));
|
|
310
|
+
const curCol = tableHeaderLast.value.find((it) => colKeyGen.value(it) === colKeyGen.value(lastCol));
|
|
311
311
|
if (!curCol)
|
|
312
312
|
return;
|
|
313
313
|
curCol.width = width + "px";
|
|
@@ -359,7 +359,7 @@ function useFixedCol({ props, colKeyGen, tableHeaders, tableHeaderLast, tableCon
|
|
|
359
359
|
["fixed-cell--" + col.fixed]: col.fixed,
|
|
360
360
|
"fixed-cell--shadow": showShadow
|
|
361
361
|
};
|
|
362
|
-
colMap.set(colKeyGen(col), classObj);
|
|
362
|
+
colMap.set(colKeyGen.value(col), classObj);
|
|
363
363
|
});
|
|
364
364
|
});
|
|
365
365
|
return colMap;
|
|
@@ -453,33 +453,45 @@ function useFixedStyle({
|
|
|
453
453
|
const { fixed } = col;
|
|
454
454
|
if (tagType === TagType.TD && !fixed)
|
|
455
455
|
return null;
|
|
456
|
-
const isFixedLeft = fixed === "left";
|
|
457
456
|
const style = {};
|
|
458
457
|
const { colKeyStore, refStore } = fixedColumnsPositionStore.value;
|
|
458
|
+
let isRelativeMode = true;
|
|
459
|
+
if (props.cellFixedMode === "sticky") {
|
|
460
|
+
isRelativeMode = false;
|
|
461
|
+
}
|
|
459
462
|
if (IS_LEGACY_MODE) {
|
|
463
|
+
isRelativeMode = true;
|
|
464
|
+
}
|
|
465
|
+
const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
|
|
466
|
+
const scrollRight = scrollWidth - containerWidth - scrollLeft;
|
|
467
|
+
if (virtualScrollX.value.scrollLeft === 0 && fixed === "left" && tagType === TagType.TD) {
|
|
468
|
+
style.position = void 0;
|
|
469
|
+
} else if (scrollRight === 0 && fixed === "right" && tagType === TagType.TD) {
|
|
470
|
+
style.position = void 0;
|
|
471
|
+
} else if (isRelativeMode) {
|
|
460
472
|
style.position = "relative";
|
|
461
473
|
} else {
|
|
462
474
|
style.position = "sticky";
|
|
463
475
|
}
|
|
476
|
+
const isFixedLeft = fixed === "left";
|
|
464
477
|
if (tagType === TagType.TH) {
|
|
465
|
-
if (
|
|
478
|
+
if (isRelativeMode) {
|
|
466
479
|
style.top = virtualScroll.value.scrollTop + "px";
|
|
467
480
|
} else {
|
|
468
481
|
style.top = depth * props.rowHeight + "px";
|
|
469
482
|
}
|
|
470
483
|
style.zIndex = isFixedLeft ? "3" : "2";
|
|
471
484
|
} else {
|
|
472
|
-
|
|
485
|
+
if (isFixedLeft) {
|
|
486
|
+
style.zIndex = "2";
|
|
487
|
+
}
|
|
473
488
|
}
|
|
474
489
|
if (fixed === "left" || fixed === "right") {
|
|
475
|
-
if (
|
|
490
|
+
if (isRelativeMode) {
|
|
476
491
|
if (isFixedLeft) {
|
|
477
|
-
|
|
478
|
-
style.left = virtualScrollX.value.scrollLeft - virtualScrollX.value.offsetLeft + "px";
|
|
479
|
-
else
|
|
480
|
-
style.left = virtualScrollX.value.scrollLeft + "px";
|
|
492
|
+
style.left = scrollLeft - (virtualX_on.value ? offsetLeft : 0) + "px";
|
|
481
493
|
} else {
|
|
482
|
-
style.right =
|
|
494
|
+
style.right = Math.max(scrollRight - (virtualX_on.value ? virtualX_offsetRight.value : 0), 0) + "px";
|
|
483
495
|
}
|
|
484
496
|
} else {
|
|
485
497
|
const lr = (col.dataIndex ? colKeyStore[col.dataIndex] : refStore.get(col)) + "px";
|
|
@@ -498,13 +510,13 @@ function useFixedStyle({
|
|
|
498
510
|
}
|
|
499
511
|
function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
500
512
|
const config = props.highlightConfig;
|
|
501
|
-
const highlightDuration = config.duration ? config.duration * 1e3 : HIGHLIGHT_DURATION;
|
|
502
|
-
const highlightFrequency = config.fps ? 1e3 / config.fps : HIGHLIGHT_FREQ;
|
|
503
513
|
const highlightColor = {
|
|
504
514
|
light: HIGHLIGHT_COLOR.light,
|
|
505
515
|
dark: HIGHLIGHT_COLOR.dark
|
|
506
516
|
};
|
|
507
|
-
const
|
|
517
|
+
const highlightDuration = config.duration ? config.duration * 1e3 : HIGHLIGHT_DURATION;
|
|
518
|
+
const highlightFrequency = config.fps && config.fps > 0 ? 1e3 / config.fps : null;
|
|
519
|
+
const highlightSteps = highlightFrequency ? Math.round(highlightDuration / highlightFrequency) : null;
|
|
508
520
|
const highlightFrom = computed(() => highlightColor[props.theme].from);
|
|
509
521
|
const highlightTo = computed(() => highlightColor[props.theme].to);
|
|
510
522
|
const highlightInter = computed(() => interpolateRgb(highlightFrom.value, highlightTo.value));
|
|
@@ -514,6 +526,13 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
514
526
|
let calcHighlightDimLoopAnimation = false;
|
|
515
527
|
const highlightDimRowsTimeout = /* @__PURE__ */ new Map();
|
|
516
528
|
const highlightDimCellsTimeout = /* @__PURE__ */ new Map();
|
|
529
|
+
const defaultHighlightDimOption = (() => {
|
|
530
|
+
const keyframe = { backgroundColor: [highlightFrom.value, highlightTo.value] };
|
|
531
|
+
if (highlightSteps) {
|
|
532
|
+
keyframe.easing = `steps(${highlightSteps})`;
|
|
533
|
+
}
|
|
534
|
+
return { duration: highlightDuration, keyframe };
|
|
535
|
+
})();
|
|
517
536
|
function calcRowHighlightLoop() {
|
|
518
537
|
if (calcHighlightDimLoopAnimation)
|
|
519
538
|
return;
|
|
@@ -565,20 +584,16 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
565
584
|
calcHighlightDimLoopJs = false;
|
|
566
585
|
highlightDimRowsJs.clear();
|
|
567
586
|
}
|
|
568
|
-
}, highlightFrequency);
|
|
587
|
+
}, highlightFrequency || HIGHLIGHT_FREQ);
|
|
569
588
|
};
|
|
570
589
|
recursion();
|
|
571
590
|
}
|
|
572
|
-
const defaultHighlightDimOption = {
|
|
573
|
-
keyframe: [{ backgroundColor: highlightFrom.value }, { backgroundColor: highlightTo.value }],
|
|
574
|
-
duration: highlightDuration
|
|
575
|
-
};
|
|
576
591
|
function setHighlightDimCell(rowKeyValue, dataIndex, option = {}) {
|
|
577
592
|
var _a;
|
|
578
593
|
const cellEl = (_a = tableContainerRef.value) == null ? void 0 : _a.querySelector(`[data-row-key="${rowKeyValue}"]>[data-index="${dataIndex}"]`);
|
|
579
594
|
const { className, method, duration, keyframe } = {
|
|
580
595
|
className: HIGHLIGHT_CELL_CLASS,
|
|
581
|
-
method: "
|
|
596
|
+
method: "animation",
|
|
582
597
|
...defaultHighlightDimOption,
|
|
583
598
|
...option
|
|
584
599
|
};
|
|
@@ -595,7 +610,7 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
595
610
|
rowKeyValues = [rowKeyValues];
|
|
596
611
|
const { className, method, useCss, keyframe, duration } = {
|
|
597
612
|
className: HIGHLIGHT_ROW_CLASS,
|
|
598
|
-
method:
|
|
613
|
+
method: "animation",
|
|
599
614
|
...defaultHighlightDimOption,
|
|
600
615
|
...option
|
|
601
616
|
};
|
|
@@ -837,6 +852,7 @@ function useVirtualScroll({
|
|
|
837
852
|
});
|
|
838
853
|
const virtualScrollX = ref({
|
|
839
854
|
containerWidth: 0,
|
|
855
|
+
scrollWidth: 0,
|
|
840
856
|
startIndex: 0,
|
|
841
857
|
endIndex: 0,
|
|
842
858
|
offsetLeft: 0,
|
|
@@ -897,12 +913,7 @@ function useVirtualScroll({
|
|
|
897
913
|
return;
|
|
898
914
|
const { offsetHeight, scrollTop } = tableContainerRef.value || {};
|
|
899
915
|
const { rowHeight } = virtualScroll.value;
|
|
900
|
-
|
|
901
|
-
if (typeof height === "number") {
|
|
902
|
-
containerHeight = height;
|
|
903
|
-
} else {
|
|
904
|
-
containerHeight = offsetHeight || DEFAULT_TABLE_HEIGHT;
|
|
905
|
-
}
|
|
916
|
+
const containerHeight = height ?? (offsetHeight || DEFAULT_TABLE_HEIGHT);
|
|
906
917
|
const { headless, headerRowHeight } = props;
|
|
907
918
|
let pageSize = Math.ceil(containerHeight / rowHeight);
|
|
908
919
|
if (!headless) {
|
|
@@ -913,10 +924,9 @@ function useVirtualScroll({
|
|
|
913
924
|
updateVirtualScrollY(scrollTop);
|
|
914
925
|
}
|
|
915
926
|
function initVirtualScrollX() {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
virtualScrollX.value.containerWidth = offsetWidth || DEFAULT_TABLE_WIDTH;
|
|
927
|
+
const { clientWidth, scrollLeft, scrollWidth } = tableContainerRef.value || {};
|
|
928
|
+
virtualScrollX.value.containerWidth = clientWidth || DEFAULT_TABLE_WIDTH;
|
|
929
|
+
virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
|
|
920
930
|
updateVirtualScrollX(scrollLeft);
|
|
921
931
|
}
|
|
922
932
|
function initVirtualScroll(height) {
|
|
@@ -930,17 +940,12 @@ function useVirtualScroll({
|
|
|
930
940
|
if (!virtual_on.value)
|
|
931
941
|
return;
|
|
932
942
|
let startIndex = Math.floor(sTop / rowHeight);
|
|
933
|
-
if (props.stripe) {
|
|
934
|
-
startIndex -= 1;
|
|
935
|
-
}
|
|
936
943
|
if (startIndex < 0) {
|
|
937
944
|
startIndex = 0;
|
|
938
945
|
}
|
|
939
946
|
if (props.stripe && startIndex !== 0) {
|
|
940
947
|
const scrollRows = Math.abs(oldStartIndex - startIndex);
|
|
941
|
-
if (scrollRows
|
|
942
|
-
return;
|
|
943
|
-
} else if (scrollRows % 2) {
|
|
948
|
+
if (scrollRows % 2) {
|
|
944
949
|
startIndex -= 1;
|
|
945
950
|
}
|
|
946
951
|
}
|
|
@@ -969,18 +974,21 @@ function useVirtualScroll({
|
|
|
969
974
|
let vue2ScrollXTimeout = null;
|
|
970
975
|
function updateVirtualScrollX(sLeft = 0) {
|
|
971
976
|
var _a;
|
|
977
|
+
if (!props.virtualX)
|
|
978
|
+
return;
|
|
972
979
|
const headerLength = (_a = tableHeaderLast.value) == null ? void 0 : _a.length;
|
|
973
|
-
const { scrollLeft } = virtualScrollX.value;
|
|
974
980
|
if (!headerLength)
|
|
975
981
|
return;
|
|
982
|
+
const { scrollLeft } = virtualScrollX.value;
|
|
976
983
|
let startIndex = 0;
|
|
977
984
|
let offsetLeft = 0;
|
|
978
985
|
let colWidthSum = 0;
|
|
979
986
|
let leftColWidthSum = 0;
|
|
987
|
+
let leftFirstColRestWidth = 0;
|
|
980
988
|
for (let colIndex = 0; colIndex < headerLength; colIndex++) {
|
|
981
|
-
startIndex++;
|
|
982
989
|
const col = tableHeaderLast.value[colIndex];
|
|
983
990
|
const colWidth = getCalculatedColWidth(col);
|
|
991
|
+
startIndex++;
|
|
984
992
|
if (col.fixed === "left") {
|
|
985
993
|
leftColWidthSum += colWidth;
|
|
986
994
|
continue;
|
|
@@ -989,10 +997,11 @@ function useVirtualScroll({
|
|
|
989
997
|
if (colWidthSum >= sLeft) {
|
|
990
998
|
offsetLeft = colWidthSum - colWidth;
|
|
991
999
|
startIndex--;
|
|
1000
|
+
leftFirstColRestWidth = colWidthSum - sLeft;
|
|
992
1001
|
break;
|
|
993
1002
|
}
|
|
994
1003
|
}
|
|
995
|
-
colWidthSum =
|
|
1004
|
+
colWidthSum = leftFirstColRestWidth;
|
|
996
1005
|
const containerWidth = virtualScrollX.value.containerWidth - leftColWidthSum;
|
|
997
1006
|
let endIndex = headerLength;
|
|
998
1007
|
for (let colIndex = startIndex + 1; colIndex < headerLength; colIndex++) {
|
|
@@ -1068,14 +1077,12 @@ const _hoisted_9 = ["onMousedown"];
|
|
|
1068
1077
|
const _hoisted_10 = ["onMousedown"];
|
|
1069
1078
|
const _hoisted_11 = {
|
|
1070
1079
|
key: 0,
|
|
1071
|
-
class: "virtual-x-left"
|
|
1072
|
-
style: { "padding": "0" }
|
|
1080
|
+
class: "virtual-x-left"
|
|
1073
1081
|
};
|
|
1074
1082
|
const _hoisted_12 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
|
|
1075
1083
|
const _hoisted_13 = {
|
|
1076
1084
|
key: 0,
|
|
1077
|
-
class: "virtual-x-left"
|
|
1078
|
-
style: { "padding": "0" }
|
|
1085
|
+
class: "virtual-x-left"
|
|
1079
1086
|
};
|
|
1080
1087
|
const _hoisted_14 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
|
|
1081
1088
|
const _hoisted_15 = ["title"];
|
|
@@ -1119,7 +1126,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1119
1126
|
}) },
|
|
1120
1127
|
hideHeaderTitle: { type: [Boolean, Array], default: false },
|
|
1121
1128
|
highlightConfig: { default: () => ({}) },
|
|
1122
|
-
seqConfig: { default: () => ({}) }
|
|
1129
|
+
seqConfig: { default: () => ({}) },
|
|
1130
|
+
cellFixedMode: { default: "sticky" }
|
|
1123
1131
|
},
|
|
1124
1132
|
emits: ["sort-change", "row-click", "current-change", "row-dblclick", "header-row-menu", "row-menu", "cell-click", "cell-mouseenter", "cell-mouseleave", "cell-mouseover", "header-cell-click", "scroll", "scroll-x", "col-order-change", "th-drag-start", "th-drop", "update:columns"],
|
|
1125
1133
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
@@ -1138,6 +1146,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1138
1146
|
const tableHeaders = shallowRef([]);
|
|
1139
1147
|
const tableHeaderLast = shallowRef([]);
|
|
1140
1148
|
const dataSourceCopy = shallowRef([...props.dataSource]);
|
|
1149
|
+
const colKeyGen = computed(() => {
|
|
1150
|
+
if (typeof props.colKey === "function") {
|
|
1151
|
+
return (col) => props.colKey(col);
|
|
1152
|
+
} else {
|
|
1153
|
+
return (col) => col[props.colKey];
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1141
1156
|
const getEmptyCellText = computed(() => {
|
|
1142
1157
|
if (typeof props.emptyCellText === "string") {
|
|
1143
1158
|
return () => props.emptyCellText;
|
|
@@ -1201,14 +1216,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1201
1216
|
() => props.columns,
|
|
1202
1217
|
() => {
|
|
1203
1218
|
dealColumns();
|
|
1204
|
-
initVirtualScrollX
|
|
1219
|
+
nextTick(initVirtualScrollX);
|
|
1205
1220
|
}
|
|
1206
1221
|
);
|
|
1207
1222
|
watch(
|
|
1208
1223
|
() => props.virtualX,
|
|
1209
|
-
() =>
|
|
1224
|
+
() => {
|
|
1225
|
+
dealColumns();
|
|
1226
|
+
nextTick(initVirtualScrollX);
|
|
1227
|
+
}
|
|
1210
1228
|
);
|
|
1211
|
-
dealColumns();
|
|
1212
1229
|
watch(
|
|
1213
1230
|
() => props.dataSource,
|
|
1214
1231
|
(val) => {
|
|
@@ -1234,6 +1251,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1234
1251
|
}
|
|
1235
1252
|
);
|
|
1236
1253
|
watch(() => props.fixedColShadow, dealFixedColShadow);
|
|
1254
|
+
dealColumns();
|
|
1237
1255
|
onMounted(() => {
|
|
1238
1256
|
initVirtualScroll();
|
|
1239
1257
|
updateFixedShadow();
|
|
@@ -1305,15 +1323,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1305
1323
|
}
|
|
1306
1324
|
return key;
|
|
1307
1325
|
}
|
|
1308
|
-
function colKeyGen(col) {
|
|
1309
|
-
return typeof props.colKey === "function" ? props.colKey(col) : col[props.colKey];
|
|
1310
|
-
}
|
|
1311
1326
|
const cellStyleMap = computed(() => {
|
|
1312
1327
|
const thMap = /* @__PURE__ */ new Map();
|
|
1313
1328
|
const tdMap = /* @__PURE__ */ new Map();
|
|
1314
1329
|
tableHeaders.value.forEach((cols, depth) => {
|
|
1315
1330
|
cols.forEach((col) => {
|
|
1316
|
-
const colKey = colKeyGen(col);
|
|
1331
|
+
const colKey = colKeyGen.value(col);
|
|
1317
1332
|
const width = props.virtualX ? getCalculatedColWidth(col) + "px" : transformWidthToStr(col.width);
|
|
1318
1333
|
const style = {
|
|
1319
1334
|
width
|
|
@@ -1550,16 +1565,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1550
1565
|
"text-overflow": props.showOverflow,
|
|
1551
1566
|
"header-text-overflow": props.showHeaderOverflow
|
|
1552
1567
|
}]),
|
|
1553
|
-
style: normalizeStyle(
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
"--highlight-duration": props.highlightConfig.duration && props.highlightConfig.duration + "s",
|
|
1560
|
-
"--highlight-timing-function": unref(highlightSteps) ? `steps(${unref(highlightSteps)})` : ""
|
|
1561
|
-
}
|
|
1562
|
-
]),
|
|
1568
|
+
style: normalizeStyle({
|
|
1569
|
+
"--row-height": unref(virtualScroll).rowHeight + "px",
|
|
1570
|
+
"--header-row-height": (props.headerRowHeight || props.rowHeight) + "px",
|
|
1571
|
+
"--highlight-duration": props.highlightConfig.duration && props.highlightConfig.duration + "s",
|
|
1572
|
+
"--highlight-timing-function": unref(highlightSteps) ? `steps(${unref(highlightSteps)})` : ""
|
|
1573
|
+
}),
|
|
1563
1574
|
onScroll: onTableScroll,
|
|
1564
1575
|
onWheel: onTableWheel
|
|
1565
1576
|
}, [
|
|
@@ -1592,17 +1603,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1592
1603
|
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_on) && rowIndex === tableHeaders.value.length - 1 ? unref(virtualX_columnPart) : row, (col, colIndex) => {
|
|
1593
1604
|
return openBlock(), createElementBlock("th", {
|
|
1594
1605
|
key: col.dataIndex,
|
|
1595
|
-
"data-col-key": colKeyGen(col),
|
|
1606
|
+
"data-col-key": colKeyGen.value(col),
|
|
1596
1607
|
draggable: unref(isHeaderDraggable)(col) ? "true" : "false",
|
|
1597
1608
|
rowspan: unref(virtualX_on) ? 1 : col.rowSpan,
|
|
1598
1609
|
colspan: col.colSpan,
|
|
1599
|
-
style: normalizeStyle(cellStyleMap.value[unref(TagType).TH].get(colKeyGen(col))),
|
|
1610
|
+
style: normalizeStyle(cellStyleMap.value[unref(TagType).TH].get(colKeyGen.value(col))),
|
|
1600
1611
|
title: getHeaderTitle(col),
|
|
1601
1612
|
class: normalizeClass([
|
|
1602
1613
|
col.sorter ? "sortable" : "",
|
|
1603
1614
|
col.dataIndex === unref(sortCol) && unref(sortOrderIndex) !== 0 && "sorter-" + sortSwitchOrder[unref(sortOrderIndex)],
|
|
1604
1615
|
col.headerClassName,
|
|
1605
|
-
unref(fixedColClassMap).get(colKeyGen(col))
|
|
1616
|
+
unref(fixedColClassMap).get(colKeyGen.value(col))
|
|
1606
1617
|
]),
|
|
1607
1618
|
onClick: (e) => {
|
|
1608
1619
|
onColumnSort(col);
|
|
@@ -1660,7 +1671,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1660
1671
|
_ctx.fixedMode && _ctx.headless ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(virtualX_columnPart), (col) => {
|
|
1661
1672
|
return openBlock(), createElementBlock("td", {
|
|
1662
1673
|
key: col.dataIndex,
|
|
1663
|
-
style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen(col)))
|
|
1674
|
+
style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen.value(col)))
|
|
1664
1675
|
}, null, 4);
|
|
1665
1676
|
}), 128)) : createCommentVNode("", true)
|
|
1666
1677
|
], 4)) : createCommentVNode("", true),
|
|
@@ -1684,8 +1695,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1684
1695
|
return openBlock(), createElementBlock("td", {
|
|
1685
1696
|
key: col.dataIndex,
|
|
1686
1697
|
"data-index": col.dataIndex,
|
|
1687
|
-
style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen(col))),
|
|
1688
|
-
class: normalizeClass([col.className, unref(fixedColClassMap).get(colKeyGen(col)), col.type === "seq" ? "seq-column" : ""]),
|
|
1698
|
+
style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen.value(col))),
|
|
1699
|
+
class: normalizeClass([col.className, unref(fixedColClassMap).get(colKeyGen.value(col)), col.type === "seq" ? "seq-column" : ""]),
|
|
1689
1700
|
onClick: (e) => onCellClick(e, row, col),
|
|
1690
1701
|
onMouseenter: (e) => onCellMouseEnter(e, row, col),
|
|
1691
1702
|
onMouseleave: (e) => onCellMouseLeave(e, row, col),
|
package/lib/style.css
CHANGED
|
@@ -88,15 +88,6 @@
|
|
|
88
88
|
.stk-table.border thead tr:first-child th{
|
|
89
89
|
background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
90
90
|
}
|
|
91
|
-
.stk-table.border.virtual-x .virtual-x-left{
|
|
92
|
-
background:none;
|
|
93
|
-
pointer-events:none;
|
|
94
|
-
}
|
|
95
|
-
.stk-table.border.virtual-x .virtual-x-right{
|
|
96
|
-
padding:0;
|
|
97
|
-
background:none;
|
|
98
|
-
pointer-events:none;
|
|
99
|
-
}
|
|
100
91
|
.stk-table.border-body-v tbody{
|
|
101
92
|
--bg-border-bottom:linear-gradient(transparent, transparent);
|
|
102
93
|
}
|
|
@@ -136,9 +127,6 @@
|
|
|
136
127
|
.stk-table.virtual .padding-top-tr td{
|
|
137
128
|
height:0;
|
|
138
129
|
}
|
|
139
|
-
.stk-table.virtual-x .virtual-x-left{
|
|
140
|
-
padding:0;
|
|
141
|
-
}
|
|
142
130
|
.stk-table th,
|
|
143
131
|
.stk-table td{
|
|
144
132
|
z-index:1;
|
|
@@ -197,6 +185,12 @@
|
|
|
197
185
|
.stk-table .stk-table-main tbody tr.active{
|
|
198
186
|
background-color:var(--tr-active-bgc);
|
|
199
187
|
}
|
|
188
|
+
.stk-table .virtual-x-left,
|
|
189
|
+
.stk-table .virtual-x-right{
|
|
190
|
+
padding:0;
|
|
191
|
+
background:none;
|
|
192
|
+
pointer-events:none;
|
|
193
|
+
}
|
|
200
194
|
.stk-table .column-resize-indicator{
|
|
201
195
|
width:0;
|
|
202
196
|
height:100%;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stk-table-vue",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Simple realtime virtual table for vue3
|
|
3
|
+
"version": "0.3.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",
|
|
7
7
|
"packageManager": "pnpm@8.14.3",
|
|
@@ -17,8 +17,13 @@
|
|
|
17
17
|
"keywords": [
|
|
18
18
|
"virtual table",
|
|
19
19
|
"vue",
|
|
20
|
+
"vue2",
|
|
21
|
+
"vue3",
|
|
20
22
|
"highlight",
|
|
21
|
-
"sticky"
|
|
23
|
+
"sticky",
|
|
24
|
+
"virtual",
|
|
25
|
+
"table",
|
|
26
|
+
"list"
|
|
22
27
|
],
|
|
23
28
|
"files": [
|
|
24
29
|
"lib",
|
|
@@ -18,16 +18,12 @@
|
|
|
18
18
|
'text-overflow': props.showOverflow,
|
|
19
19
|
'header-text-overflow': props.showHeaderOverflow,
|
|
20
20
|
}"
|
|
21
|
-
:style="
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
'--highlight-duration': props.highlightConfig.duration && props.highlightConfig.duration + 's',
|
|
28
|
-
'--highlight-timing-function': highlightSteps ? `steps(${highlightSteps})` : '',
|
|
29
|
-
},
|
|
30
|
-
]"
|
|
21
|
+
:style="{
|
|
22
|
+
'--row-height': virtualScroll.rowHeight + 'px',
|
|
23
|
+
'--header-row-height': (props.headerRowHeight || props.rowHeight) + 'px',
|
|
24
|
+
'--highlight-duration': props.highlightConfig.duration && props.highlightConfig.duration + 's',
|
|
25
|
+
'--highlight-timing-function': highlightSteps ? `steps(${highlightSteps})` : '',
|
|
26
|
+
}"
|
|
31
27
|
@scroll="onTableScroll"
|
|
32
28
|
@wheel="onTableWheel"
|
|
33
29
|
>
|
|
@@ -142,7 +138,7 @@
|
|
|
142
138
|
<tbody>
|
|
143
139
|
<tr v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }" class="padding-top-tr">
|
|
144
140
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
145
|
-
<td v-if="virtualX_on && fixedMode && headless" class="virtual-x-left"
|
|
141
|
+
<td v-if="virtualX_on && fixedMode && headless" class="virtual-x-left"></td>
|
|
146
142
|
<template v-if="fixedMode && headless">
|
|
147
143
|
<td v-for="col in virtualX_columnPart" :key="col.dataIndex" :style="cellStyleMap[TagType.TD].get(colKeyGen(col))"></td
|
|
148
144
|
></template>
|
|
@@ -163,7 +159,7 @@
|
|
|
163
159
|
@mouseover="e => onTrMouseOver(e, row)"
|
|
164
160
|
>
|
|
165
161
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
166
|
-
<td v-if="virtualX_on" class="virtual-x-left"
|
|
162
|
+
<td v-if="virtualX_on" class="virtual-x-left"></td>
|
|
167
163
|
<td
|
|
168
164
|
v-for="(col, colIndex) in virtualX_columnPart"
|
|
169
165
|
:key="col.dataIndex"
|
|
@@ -212,9 +208,9 @@
|
|
|
212
208
|
* [] 计算的高亮颜色,挂在数据源上对象上,若多个表格使用同一个数据源对象会有问题。需要深拷贝。(解决方案:获取组件uid)
|
|
213
209
|
* [] highlight-row 颜色不能恢复到active的颜色
|
|
214
210
|
*/
|
|
215
|
-
import { CSSProperties, computed, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
211
|
+
import { CSSProperties, computed, nextTick, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
216
212
|
import { DEFAULT_ROW_HEIGHT } from './const';
|
|
217
|
-
import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKeyProp } from './types/index';
|
|
213
|
+
import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TagType, UniqKey, UniqKeyProp } from './types/index';
|
|
218
214
|
import { useAutoResize } from './useAutoResize';
|
|
219
215
|
import { useColResize } from './useColResize';
|
|
220
216
|
import { useFixedCol } from './useFixedCol';
|
|
@@ -320,6 +316,14 @@ const props = withDefaults(
|
|
|
320
316
|
highlightConfig?: HighlightConfig;
|
|
321
317
|
/** 序号列配置 */
|
|
322
318
|
seqConfig?: SeqConfig;
|
|
319
|
+
/**
|
|
320
|
+
* 固定头,固定列实现方式。
|
|
321
|
+
*
|
|
322
|
+
* relative:固定列只能放在props.columns的两侧。如果列宽会变动则谨慎使用。
|
|
323
|
+
*
|
|
324
|
+
* 低版本浏览器只能为'relative',
|
|
325
|
+
*/
|
|
326
|
+
cellFixedMode?: 'sticky' | 'relative';
|
|
323
327
|
}>(),
|
|
324
328
|
{
|
|
325
329
|
width: '',
|
|
@@ -360,6 +364,7 @@ const props = withDefaults(
|
|
|
360
364
|
hideHeaderTitle: false,
|
|
361
365
|
highlightConfig: () => ({}),
|
|
362
366
|
seqConfig: () => ({}),
|
|
367
|
+
cellFixedMode: 'sticky',
|
|
363
368
|
},
|
|
364
369
|
);
|
|
365
370
|
|
|
@@ -496,6 +501,18 @@ const tableHeaderLast = shallowRef<StkTableColumn<DT>[]>([]);
|
|
|
496
501
|
|
|
497
502
|
const dataSourceCopy = shallowRef<DT[]>([...props.dataSource]);
|
|
498
503
|
|
|
504
|
+
/**
|
|
505
|
+
* 列唯一键
|
|
506
|
+
* @param col
|
|
507
|
+
*/
|
|
508
|
+
const colKeyGen = computed(() => {
|
|
509
|
+
if (typeof props.colKey === 'function') {
|
|
510
|
+
return (col: StkTableColumn<DT>) => (props.colKey as (col: StkTableColumn<DT>) => string)(col);
|
|
511
|
+
} else {
|
|
512
|
+
return (col: StkTableColumn<DT>) => (col as any)[props.colKey as string];
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
499
516
|
/**高亮帧间隔
|
|
500
517
|
const highlightStepDuration = Highlight_Color_Change_Freq / 1000 + 's';*/
|
|
501
518
|
|
|
@@ -579,16 +596,17 @@ watch(
|
|
|
579
596
|
() => props.columns,
|
|
580
597
|
() => {
|
|
581
598
|
dealColumns();
|
|
582
|
-
initVirtualScrollX
|
|
599
|
+
nextTick(initVirtualScrollX);
|
|
583
600
|
},
|
|
584
601
|
);
|
|
585
602
|
watch(
|
|
586
603
|
() => props.virtualX,
|
|
587
|
-
() =>
|
|
604
|
+
() => {
|
|
605
|
+
dealColumns();
|
|
606
|
+
nextTick(initVirtualScrollX);
|
|
607
|
+
},
|
|
588
608
|
);
|
|
589
609
|
|
|
590
|
-
dealColumns();
|
|
591
|
-
|
|
592
610
|
watch(
|
|
593
611
|
() => props.dataSource,
|
|
594
612
|
val => {
|
|
@@ -618,6 +636,8 @@ watch(
|
|
|
618
636
|
|
|
619
637
|
watch(() => props.fixedColShadow, dealFixedColShadow);
|
|
620
638
|
|
|
639
|
+
dealColumns();
|
|
640
|
+
|
|
621
641
|
onMounted(() => {
|
|
622
642
|
initVirtualScroll();
|
|
623
643
|
updateFixedShadow();
|
|
@@ -717,21 +737,13 @@ function rowKeyGen(row: DT) {
|
|
|
717
737
|
return key;
|
|
718
738
|
}
|
|
719
739
|
|
|
720
|
-
/**
|
|
721
|
-
* 列唯一键
|
|
722
|
-
* @param col
|
|
723
|
-
*/
|
|
724
|
-
function colKeyGen(col: StkTableColumn<DT>) {
|
|
725
|
-
return typeof props.colKey === 'function' ? props.colKey(col) : (col as any)[props.colKey];
|
|
726
|
-
}
|
|
727
|
-
|
|
728
740
|
/** 单元格样式 */
|
|
729
741
|
const cellStyleMap = computed(() => {
|
|
730
742
|
const thMap = new Map();
|
|
731
743
|
const tdMap = new Map();
|
|
732
744
|
tableHeaders.value.forEach((cols, depth) => {
|
|
733
745
|
cols.forEach(col => {
|
|
734
|
-
const colKey = colKeyGen(col);
|
|
746
|
+
const colKey = colKeyGen.value(col);
|
|
735
747
|
const width = props.virtualX ? getCalculatedColWidth(col) + 'px' : transformWidthToStr(col.width);
|
|
736
748
|
const style: CSSProperties = {
|
|
737
749
|
width,
|
package/src/StkTable/const.ts
CHANGED
package/src/StkTable/style.less
CHANGED
|
@@ -116,28 +116,8 @@
|
|
|
116
116
|
background-image: var(--bg-border-right), var(--bg-border-bottom);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
thead {
|
|
120
|
-
|
|
121
|
-
&:first-child th {
|
|
122
|
-
background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
&.virtual-x {
|
|
130
|
-
.virtual-x-left {
|
|
131
|
-
background: none;
|
|
132
|
-
pointer-events: none;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.virtual-x-right {
|
|
136
|
-
padding: 0;
|
|
137
|
-
background: none;
|
|
138
|
-
pointer-events: none;
|
|
139
|
-
}
|
|
140
|
-
|
|
119
|
+
thead tr:first-child th {
|
|
120
|
+
background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
141
121
|
}
|
|
142
122
|
}
|
|
143
123
|
|
|
@@ -191,15 +171,13 @@
|
|
|
191
171
|
max-height: var(--header-row-height);
|
|
192
172
|
}
|
|
193
173
|
|
|
194
|
-
tbody {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
overflow: hidden;
|
|
202
|
-
}
|
|
174
|
+
tbody td {
|
|
175
|
+
height: var(--row-height);
|
|
176
|
+
line-height: 1;
|
|
177
|
+
|
|
178
|
+
.table-cell-wrapper {
|
|
179
|
+
max-height: var(--row-height);
|
|
180
|
+
overflow: hidden;
|
|
203
181
|
}
|
|
204
182
|
}
|
|
205
183
|
|
|
@@ -208,12 +186,6 @@
|
|
|
208
186
|
}
|
|
209
187
|
}
|
|
210
188
|
|
|
211
|
-
&.virtual-x {
|
|
212
|
-
.virtual-x-left {
|
|
213
|
-
padding: 0;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
189
|
th,
|
|
218
190
|
td {
|
|
219
191
|
z-index: 1;
|
|
@@ -294,6 +266,13 @@
|
|
|
294
266
|
}
|
|
295
267
|
}
|
|
296
268
|
|
|
269
|
+
.virtual-x-left,
|
|
270
|
+
.virtual-x-right {
|
|
271
|
+
padding: 0;
|
|
272
|
+
background: none;
|
|
273
|
+
pointer-events: none;
|
|
274
|
+
}
|
|
275
|
+
|
|
297
276
|
|
|
298
277
|
/** 列宽调整指示器 */
|
|
299
278
|
.column-resize-indicator {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Ref, ShallowRef, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
2
|
-
import { StkTableColumn } from './types';
|
|
1
|
+
import { ComputedRef, Ref, ShallowRef, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
2
|
+
import { StkTableColumn, UniqKey } from './types';
|
|
3
3
|
import { getCalculatedColWidth } from './utils';
|
|
4
4
|
|
|
5
5
|
type ColResizeState<DT extends Record<string, any>> = {
|
|
@@ -21,7 +21,7 @@ type Params<DT extends Record<string, any>> = {
|
|
|
21
21
|
tableContainerRef: Ref<HTMLElement | undefined>;
|
|
22
22
|
tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>;
|
|
23
23
|
colResizeIndicatorRef: Ref<HTMLElement | undefined>;
|
|
24
|
-
colKeyGen: (p: any) =>
|
|
24
|
+
colKeyGen: ComputedRef<(p: any) => UniqKey>;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
/** 列宽拖动 */
|
|
@@ -78,7 +78,7 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
78
78
|
const { scrollLeft, scrollTop } = tableContainerRef.value;
|
|
79
79
|
const { left } = tableContainerRef.value.getBoundingClientRect();
|
|
80
80
|
/** 列下标 */
|
|
81
|
-
let colIndex = tableHeaderLast.value.findIndex(it => colKeyGen(it) === colKeyGen(col));
|
|
81
|
+
let colIndex = tableHeaderLast.value.findIndex(it => colKeyGen.value(it) === colKeyGen.value(col));
|
|
82
82
|
if (isPrev) {
|
|
83
83
|
// 上一列
|
|
84
84
|
colIndex -= 1;
|
|
@@ -139,7 +139,7 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
139
139
|
let width = getCalculatedColWidth(lastCol) + moveX;
|
|
140
140
|
if (width < props.colMinWidth) width = props.colMinWidth;
|
|
141
141
|
|
|
142
|
-
const curCol = tableHeaderLast.value.find(it => colKeyGen(it) === colKeyGen(lastCol));
|
|
142
|
+
const curCol = tableHeaderLast.value.find(it => colKeyGen.value(it) === colKeyGen.value(lastCol));
|
|
143
143
|
if (!curCol) return;
|
|
144
144
|
curCol.width = width + 'px';
|
|
145
145
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { computed, ref, Ref, ShallowRef, shallowRef } from 'vue';
|
|
2
|
-
import { StkTableColumn } from './types';
|
|
1
|
+
import { computed, ComputedRef, ref, Ref, ShallowRef, shallowRef } from 'vue';
|
|
2
|
+
import { StkTableColumn, UniqKey } from './types';
|
|
3
3
|
|
|
4
4
|
type Params<T extends Record<string, any>> = {
|
|
5
5
|
props: any;
|
|
6
|
-
colKeyGen: (col: StkTableColumn<T>) =>
|
|
6
|
+
colKeyGen: ComputedRef<(col: StkTableColumn<T>) => UniqKey>;
|
|
7
7
|
tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
|
|
8
8
|
tableHeaderLast: ShallowRef<StkTableColumn<T>[]>;
|
|
9
9
|
tableContainerRef: Ref<HTMLDivElement | undefined>;
|
|
@@ -42,7 +42,7 @@ export function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen,
|
|
|
42
42
|
['fixed-cell--' + col.fixed]: col.fixed,
|
|
43
43
|
'fixed-cell--shadow': showShadow,
|
|
44
44
|
};
|
|
45
|
-
colMap.set(colKeyGen(col), classObj);
|
|
45
|
+
colMap.set(colKeyGen.value(col), classObj);
|
|
46
46
|
});
|
|
47
47
|
});
|
|
48
48
|
return colMap;
|
|
@@ -75,19 +75,39 @@ export function useFixedStyle<DT extends Record<string, any>>({
|
|
|
75
75
|
const { fixed } = col;
|
|
76
76
|
if (tagType === TagType.TD && !fixed) return null;
|
|
77
77
|
|
|
78
|
-
const isFixedLeft = fixed === 'left';
|
|
79
78
|
const style: CSSProperties = {};
|
|
80
79
|
const { colKeyStore, refStore } = fixedColumnsPositionStore.value;
|
|
81
80
|
|
|
81
|
+
/** 是否是relative模式完成固定列 */
|
|
82
|
+
let isRelativeMode = true;
|
|
83
|
+
if (props.cellFixedMode === 'sticky') {
|
|
84
|
+
isRelativeMode = false;
|
|
85
|
+
}
|
|
86
|
+
|
|
82
87
|
if (IS_LEGACY_MODE) {
|
|
88
|
+
// 低版本浏览器只能为固定列设置position: sticky
|
|
89
|
+
isRelativeMode = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
|
|
93
|
+
const scrollRight = scrollWidth - containerWidth - scrollLeft;
|
|
94
|
+
|
|
95
|
+
if (virtualScrollX.value.scrollLeft === 0 && fixed === 'left' && tagType === TagType.TD) {
|
|
96
|
+
// 滚动条在最左侧时,左侧固定列不需要,防止分层
|
|
97
|
+
style.position = void 0;
|
|
98
|
+
} else if (scrollRight === 0 && fixed === 'right' && tagType === TagType.TD) {
|
|
99
|
+
// 滚动条在最右侧时,右侧固定列不需要,防止分层
|
|
100
|
+
style.position = void 0;
|
|
101
|
+
} else if (isRelativeMode) {
|
|
83
102
|
style.position = 'relative';
|
|
84
103
|
} else {
|
|
85
104
|
style.position = 'sticky';
|
|
86
105
|
}
|
|
87
106
|
|
|
107
|
+
const isFixedLeft = fixed === 'left';
|
|
88
108
|
if (tagType === TagType.TH) {
|
|
89
109
|
// TH
|
|
90
|
-
if (
|
|
110
|
+
if (isRelativeMode) {
|
|
91
111
|
style.top = virtualScroll.value.scrollTop + 'px';
|
|
92
112
|
} else {
|
|
93
113
|
style.top = depth * props.rowHeight + 'px';
|
|
@@ -95,17 +115,18 @@ export function useFixedStyle<DT extends Record<string, any>>({
|
|
|
95
115
|
style.zIndex = isFixedLeft ? '3' : '2'; // 保证固定列高于其他单元格
|
|
96
116
|
} else {
|
|
97
117
|
// TD
|
|
98
|
-
|
|
118
|
+
if (isFixedLeft) {
|
|
119
|
+
style.zIndex = '2';
|
|
120
|
+
}
|
|
99
121
|
}
|
|
100
122
|
|
|
101
123
|
if (fixed === 'left' || fixed === 'right') {
|
|
102
|
-
if (
|
|
124
|
+
if (isRelativeMode) {
|
|
103
125
|
if (isFixedLeft) {
|
|
104
|
-
|
|
105
|
-
else style.left = virtualScrollX.value.scrollLeft + 'px';
|
|
126
|
+
style.left = scrollLeft - (virtualX_on.value ? offsetLeft : 0) + 'px';
|
|
106
127
|
} else {
|
|
107
|
-
//
|
|
108
|
-
style.right =
|
|
128
|
+
// fixed right
|
|
129
|
+
style.right = Math.max(scrollRight - (virtualX_on.value ? virtualX_offsetRight.value : 0), 0) + 'px';
|
|
109
130
|
}
|
|
110
131
|
} else {
|
|
111
132
|
const lr = (col.dataIndex ? colKeyStore[col.dataIndex] : refStore.get(col)) + 'px';
|
|
@@ -26,18 +26,17 @@ type HighlightDimRowStore = {
|
|
|
26
26
|
export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
27
27
|
const config: HighlightConfig = props.highlightConfig;
|
|
28
28
|
|
|
29
|
-
/** 持续时间 */
|
|
30
|
-
const highlightDuration = config.duration ? config.duration * 1000 : HIGHLIGHT_DURATION;
|
|
31
|
-
/** 高亮频率(仅虚拟滚动生效) */
|
|
32
|
-
const highlightFrequency = config.fps ? 1000 / config.fps : HIGHLIGHT_FREQ;
|
|
33
29
|
/** 高亮颜色 */
|
|
34
30
|
const highlightColor = {
|
|
35
31
|
light: HIGHLIGHT_COLOR.light,
|
|
36
32
|
dark: HIGHLIGHT_COLOR.dark,
|
|
37
33
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
/** 持续时间 */
|
|
35
|
+
const highlightDuration = config.duration ? config.duration * 1000 : HIGHLIGHT_DURATION;
|
|
36
|
+
/** 高亮频率*/
|
|
37
|
+
const highlightFrequency = config.fps && config.fps > 0 ? 1000 / config.fps : null;
|
|
38
|
+
/** 高亮帧数(非帧率),用于 timing-function: steps() */
|
|
39
|
+
const highlightSteps = highlightFrequency ? Math.round(highlightDuration / highlightFrequency) : null;
|
|
41
40
|
/** 高亮开始 */
|
|
42
41
|
const highlightFrom = computed(() => highlightColor[props.theme as 'light' | 'dark'].from);
|
|
43
42
|
/** 高亮结束 */
|
|
@@ -66,6 +65,15 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
66
65
|
/** 高亮后渐暗的单元格定时器 */
|
|
67
66
|
const highlightDimCellsTimeout = new Map();
|
|
68
67
|
|
|
68
|
+
/** 高亮函数的默认参数 */
|
|
69
|
+
const defaultHighlightDimOption = (() => {
|
|
70
|
+
const keyframe: PropertyIndexedKeyframes = { backgroundColor: [highlightFrom.value, highlightTo.value] };
|
|
71
|
+
if (highlightSteps) {
|
|
72
|
+
keyframe.easing = `steps(${highlightSteps})`;
|
|
73
|
+
}
|
|
74
|
+
return { duration: highlightDuration, keyframe };
|
|
75
|
+
})();
|
|
76
|
+
|
|
69
77
|
/**
|
|
70
78
|
* 计算高亮渐暗颜色的循环
|
|
71
79
|
*/
|
|
@@ -130,22 +138,16 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
130
138
|
calcHighlightDimLoopJs = false;
|
|
131
139
|
highlightDimRowsJs.clear(); // TODO: 是否需要 清除
|
|
132
140
|
}
|
|
133
|
-
}, highlightFrequency);
|
|
141
|
+
}, highlightFrequency || HIGHLIGHT_FREQ);
|
|
134
142
|
};
|
|
135
143
|
recursion();
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
/** 高亮函数的默认参数 */
|
|
139
|
-
const defaultHighlightDimOption = {
|
|
140
|
-
keyframe: [{ backgroundColor: highlightFrom.value }, { backgroundColor: highlightTo.value }],
|
|
141
|
-
duration: highlightDuration,
|
|
142
|
-
};
|
|
143
|
-
|
|
144
146
|
/**
|
|
145
|
-
*
|
|
147
|
+
* 高亮一个单元格。暂不支持虚拟滚动高亮状态记忆。
|
|
146
148
|
* @param rowKeyValue 一行的key
|
|
147
149
|
* @param dataIndex 列key
|
|
148
|
-
* @param options.method css-使用css渲染,animation-使用animation api。默认
|
|
150
|
+
* @param options.method css-使用css渲染,animation-使用animation api。默认animation;
|
|
149
151
|
* @param option.className 自定义css动画的class。
|
|
150
152
|
* @param option.keyframe 同Keyframe https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
|
|
151
153
|
* @param option.duration 动画时长。method='css'状态下,用于移除class,如果传入了className则需要与自定义的动画时间一致。
|
|
@@ -153,13 +155,12 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
153
155
|
function setHighlightDimCell(
|
|
154
156
|
rowKeyValue: string,
|
|
155
157
|
dataIndex: string,
|
|
156
|
-
option: { className?: string; method?: '
|
|
158
|
+
option: { className?: string; method?: 'animation' | 'css'; keyframe?: Parameters<Animatable['animate']>['0']; duration?: number } = {},
|
|
157
159
|
) {
|
|
158
|
-
// TODO: 支持动态计算高亮颜色。不易实现。需记录每一个单元格的颜色情况。
|
|
159
160
|
const cellEl = tableContainerRef.value?.querySelector<HTMLElement>(`[data-row-key="${rowKeyValue}"]>[data-index="${dataIndex}"]`);
|
|
160
161
|
const { className, method, duration, keyframe } = {
|
|
161
162
|
className: HIGHLIGHT_CELL_CLASS,
|
|
162
|
-
method: '
|
|
163
|
+
method: 'animation',
|
|
163
164
|
...defaultHighlightDimOption,
|
|
164
165
|
...option,
|
|
165
166
|
};
|
|
@@ -174,15 +175,15 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
174
175
|
/**
|
|
175
176
|
* 高亮一行
|
|
176
177
|
* @param rowKeyValues 行唯一键的数组
|
|
177
|
-
* @param option.method css-使用css渲染,animation-使用animation api,js-使用js
|
|
178
|
+
* @param option.method css-使用css渲染,animation-使用animation api,js-使用js计算颜色。默认animation
|
|
178
179
|
* @param option.className 自定义css动画的class。
|
|
179
|
-
* @param option.keyframe 同Keyframe
|
|
180
|
+
* @param option.keyframe 同Keyframe。 https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
|
|
180
181
|
* @param option.duration 动画时长。method='css'状态下,用于移除class,如果传入了className则需要与自定义的动画时间一致。。
|
|
181
182
|
*/
|
|
182
183
|
function setHighlightDimRow(
|
|
183
184
|
rowKeyValues: UniqKey[],
|
|
184
185
|
option: {
|
|
185
|
-
method?: '
|
|
186
|
+
method?: 'animation' | 'css' | 'js';
|
|
186
187
|
/** @deprecated 请使用method */
|
|
187
188
|
useCss?: boolean;
|
|
188
189
|
className?: string;
|
|
@@ -193,7 +194,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
193
194
|
if (!Array.isArray(rowKeyValues)) rowKeyValues = [rowKeyValues];
|
|
194
195
|
const { className, method, useCss, keyframe, duration } = {
|
|
195
196
|
className: HIGHLIGHT_ROW_CLASS,
|
|
196
|
-
method:
|
|
197
|
+
method: 'animation',
|
|
197
198
|
...defaultHighlightDimOption,
|
|
198
199
|
...option,
|
|
199
200
|
};
|
|
@@ -234,7 +235,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
234
235
|
|
|
235
236
|
/**
|
|
236
237
|
* 使用css @keyframes动画,实现高亮行动画
|
|
237
|
-
* 此方案作为兼容方式。v0.3.
|
|
238
|
+
* 此方案作为兼容方式。v0.3.4 将使用Element.animate 接口实现动画。
|
|
238
239
|
*/
|
|
239
240
|
function highlightRowsInCssKeyframe(rowKeyValues: UniqKey[], className: string, duration: number) {
|
|
240
241
|
/**是否需要重绘 */
|
|
@@ -267,7 +268,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
267
268
|
|
|
268
269
|
/**
|
|
269
270
|
* 使用css @keyframes动画,实现高亮单元格动画
|
|
270
|
-
* 此方案作为兼容方式。v0.3.
|
|
271
|
+
* 此方案作为兼容方式。v0.3.4 将使用Element.animate 接口实现动画。
|
|
271
272
|
*/
|
|
272
273
|
function highlightCellsInCssKeyFrame(cellEl: HTMLElement, rowKeyValue: UniqKey, className: string, duration: number) {
|
|
273
274
|
if (cellEl.classList.contains(className)) {
|
|
@@ -30,8 +30,10 @@ export type VirtualScrollStore = {
|
|
|
30
30
|
};
|
|
31
31
|
/** 暂存横向虚拟滚动的数据 */
|
|
32
32
|
export type VirtualScrollXStore = {
|
|
33
|
-
/**
|
|
33
|
+
/** 父容器宽度 */
|
|
34
34
|
containerWidth: number;
|
|
35
|
+
/** 滚动容器的宽度 */
|
|
36
|
+
scrollWidth: number;
|
|
35
37
|
/** 开始位置 */
|
|
36
38
|
startIndex: number;
|
|
37
39
|
/** 结束始位置 */
|
|
@@ -69,6 +71,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
69
71
|
|
|
70
72
|
const virtualScrollX = ref<VirtualScrollXStore>({
|
|
71
73
|
containerWidth: 0,
|
|
74
|
+
scrollWidth: 0,
|
|
72
75
|
startIndex: 0,
|
|
73
76
|
endIndex: 0,
|
|
74
77
|
offsetLeft: 0,
|
|
@@ -143,13 +146,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
143
146
|
if (!virtual_on.value) return;
|
|
144
147
|
const { offsetHeight, scrollTop } = tableContainerRef.value || {};
|
|
145
148
|
const { rowHeight } = virtualScroll.value;
|
|
146
|
-
|
|
147
|
-
// FIXME: 可能多次获取offsetHeight 会导致浏览器频繁重排
|
|
148
|
-
if (typeof height === 'number') {
|
|
149
|
-
containerHeight = height;
|
|
150
|
-
} else {
|
|
151
|
-
containerHeight = offsetHeight || DEFAULT_TABLE_HEIGHT;
|
|
152
|
-
}
|
|
149
|
+
const containerHeight = height ?? (offsetHeight || DEFAULT_TABLE_HEIGHT);
|
|
153
150
|
const { headless, headerRowHeight } = props;
|
|
154
151
|
let pageSize = Math.ceil(containerHeight / rowHeight);
|
|
155
152
|
if (!headless) {
|
|
@@ -162,10 +159,9 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
function initVirtualScrollX() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
virtualScrollX.value.containerWidth = offsetWidth || DEFAULT_TABLE_WIDTH;
|
|
162
|
+
const { clientWidth, scrollLeft, scrollWidth } = tableContainerRef.value || {};
|
|
163
|
+
virtualScrollX.value.containerWidth = clientWidth || DEFAULT_TABLE_WIDTH;
|
|
164
|
+
virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
|
|
169
165
|
updateVirtualScrollX(scrollLeft);
|
|
170
166
|
}
|
|
171
167
|
/**
|
|
@@ -189,25 +185,19 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
189
185
|
if (!virtual_on.value) return;
|
|
190
186
|
|
|
191
187
|
let startIndex = Math.floor(sTop / rowHeight);
|
|
192
|
-
if (props.stripe) {
|
|
193
|
-
startIndex -= 1; //预渲染1行
|
|
194
|
-
}
|
|
195
188
|
if (startIndex < 0) {
|
|
196
189
|
startIndex = 0;
|
|
197
190
|
}
|
|
198
191
|
if (props.stripe && startIndex !== 0) {
|
|
199
192
|
const scrollRows = Math.abs(oldStartIndex - startIndex);
|
|
200
193
|
// 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
|
|
201
|
-
if (scrollRows
|
|
202
|
-
return;
|
|
203
|
-
} else if (scrollRows % 2) {
|
|
194
|
+
if (scrollRows % 2) {
|
|
204
195
|
startIndex -= 1; // 奇数-1变成偶数
|
|
205
196
|
}
|
|
206
197
|
}
|
|
207
198
|
let endIndex = startIndex + pageSize;
|
|
208
199
|
if (props.stripe) {
|
|
209
|
-
//
|
|
210
|
-
endIndex += 1;
|
|
200
|
+
endIndex += 1; // 斑马纹下多渲染一些
|
|
211
201
|
}
|
|
212
202
|
const offsetTop = startIndex * rowHeight; // startIndex之前的高度
|
|
213
203
|
endIndex = Math.min(endIndex, dataSourceCopy.value.length); // 溢出index修正
|
|
@@ -237,18 +227,24 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
237
227
|
|
|
238
228
|
/** 通过横向滚动条位置,计算横向虚拟滚动的参数 */
|
|
239
229
|
function updateVirtualScrollX(sLeft = 0) {
|
|
230
|
+
if (!props.virtualX) return;
|
|
240
231
|
const headerLength = tableHeaderLast.value?.length;
|
|
241
|
-
const { scrollLeft } = virtualScrollX.value;
|
|
242
232
|
if (!headerLength) return;
|
|
233
|
+
|
|
234
|
+
const { scrollLeft } = virtualScrollX.value;
|
|
243
235
|
let startIndex = 0;
|
|
244
236
|
let offsetLeft = 0;
|
|
245
|
-
|
|
237
|
+
/** 列宽累加 */
|
|
246
238
|
let colWidthSum = 0;
|
|
239
|
+
/** 固定左侧列宽 */
|
|
247
240
|
let leftColWidthSum = 0;
|
|
241
|
+
/** 横向滚动时,第一列的剩余宽度 */
|
|
242
|
+
let leftFirstColRestWidth = 0;
|
|
243
|
+
|
|
248
244
|
for (let colIndex = 0; colIndex < headerLength; colIndex++) {
|
|
249
|
-
startIndex++;
|
|
250
245
|
const col = tableHeaderLast.value[colIndex];
|
|
251
246
|
const colWidth = getCalculatedColWidth(col);
|
|
247
|
+
startIndex++;
|
|
252
248
|
// fixed left 不进入计算列宽
|
|
253
249
|
if (col.fixed === 'left') {
|
|
254
250
|
leftColWidthSum += colWidth;
|
|
@@ -259,11 +255,12 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
259
255
|
if (colWidthSum >= sLeft) {
|
|
260
256
|
offsetLeft = colWidthSum - colWidth;
|
|
261
257
|
startIndex--;
|
|
258
|
+
leftFirstColRestWidth = colWidthSum - sLeft;
|
|
262
259
|
break;
|
|
263
260
|
}
|
|
264
261
|
}
|
|
265
262
|
// -----
|
|
266
|
-
colWidthSum =
|
|
263
|
+
colWidthSum = leftFirstColRestWidth;
|
|
267
264
|
const containerWidth = virtualScrollX.value.containerWidth - leftColWidthSum;
|
|
268
265
|
let endIndex = headerLength;
|
|
269
266
|
for (let colIndex = startIndex + 1; colIndex < headerLength; colIndex++) {
|