stk-table-vue 0.6.3 → 0.6.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 +10 -2
- package/lib/src/StkTable/StkTable.vue.d.ts +7 -7
- package/lib/src/StkTable/types/index.d.ts +6 -0
- package/lib/src/StkTable/useColResize.d.ts +1 -0
- package/lib/src/StkTable/useFixedCol.d.ts +2 -2
- package/lib/src/StkTable/utils/index.d.ts +3 -1
- package/lib/stk-table-vue.js +82 -65
- package/lib/style.css +12 -10
- package/package.json +9 -5
- package/src/StkTable/StkTable.vue +13 -16
- package/src/StkTable/style.less +17 -10
- package/src/StkTable/types/index.ts +7 -0
- package/src/StkTable/useColResize.ts +13 -3
- package/src/StkTable/useFixedCol.ts +46 -40
- package/src/StkTable/useHighlight.ts +3 -3
- package/src/StkTable/useThDrag.ts +2 -2
- package/src/StkTable/useVirtualScroll.ts +44 -18
- package/src/StkTable/utils/index.ts +6 -7
package/README.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# StkTable (Sticky Table)
|
|
2
|
+

|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
2
6
|
|
|
3
7
|
Vue3 简易虚拟滚动表格。用于实时数据展示,新数据行高亮渐暗动效。
|
|
4
8
|
|
|
5
9
|
Vue2.7支持引入源码(**ts**)使用。
|
|
6
10
|
|
|
11
|
+
[Stk Table Vue 文档](https://ja-plus.github.io/stk-table-vue/)
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
MIT
|
|
7
15
|
repo:
|
|
8
16
|
- [Github](https://github.com/ja-plus/stk-table-vue)
|
|
9
17
|
- [Gitee](https://gitee.com/japlus/stk-table-vue) 🇨🇳
|
|
@@ -227,7 +235,7 @@ export type StkProps = {
|
|
|
227
235
|
* 列宽拖动时,每一列都必须要有width,且minWidth/maxWidth不生效。table width会变为"fit-content"。
|
|
228
236
|
* - 会自动更新props.columns中的with属性
|
|
229
237
|
*/
|
|
230
|
-
colResizable?: boolean;
|
|
238
|
+
colResizable?: boolean | ColResizableConfig;
|
|
231
239
|
/** 可拖动至最小的列宽 */
|
|
232
240
|
colMinWidth?: number;
|
|
233
241
|
/**
|
|
@@ -281,7 +289,7 @@ export type StkProps = {
|
|
|
281
289
|
expandConfig?: {
|
|
282
290
|
height?: number;
|
|
283
291
|
};
|
|
284
|
-
/**
|
|
292
|
+
/** 行拖动配置 */
|
|
285
293
|
dragRowConfig?: {
|
|
286
294
|
mode?: 'none' | 'insert' | 'swap';
|
|
287
295
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoRowHeightConfig, DragRowConfig, ExpandConfig, HeaderDragConfig, HighlightConfig, Order, PrivateRowDT, SeqConfig, SortConfig, SortOption, StkTableColumn, UniqKeyProp } from './types/index';
|
|
1
|
+
import { AutoRowHeightConfig, DragRowConfig, ExpandConfig, HeaderDragConfig, HighlightConfig, Order, PrivateRowDT, SeqConfig, SortConfig, SortOption, StkTableColumn, UniqKeyProp, ColResizableConfig } from './types/index';
|
|
2
2
|
/** Generic stands for DataType */
|
|
3
3
|
type DT = any & PrivateRowDT;
|
|
4
4
|
/**
|
|
@@ -138,7 +138,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
|
|
|
138
138
|
* 列宽拖动时,每一列都必须要有width,且minWidth/maxWidth不生效。table width会变为"fit-content"。
|
|
139
139
|
* - 会自动更新props.columns中的with属性
|
|
140
140
|
*/
|
|
141
|
-
colResizable?: boolean | undefined;
|
|
141
|
+
colResizable?: boolean | ColResizableConfig<any> | undefined;
|
|
142
142
|
/** 可拖动至最小的列宽 */
|
|
143
143
|
colMinWidth?: number | undefined;
|
|
144
144
|
/**
|
|
@@ -169,7 +169,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
|
|
|
169
169
|
seqConfig?: SeqConfig | undefined;
|
|
170
170
|
/** 展开行配置 */
|
|
171
171
|
expandConfig?: ExpandConfig | undefined;
|
|
172
|
-
/**
|
|
172
|
+
/** 行拖动配置 */
|
|
173
173
|
dragRowConfig?: DragRowConfig | undefined;
|
|
174
174
|
/**
|
|
175
175
|
* 固定头,固定列实现方式。(非响应式)
|
|
@@ -383,7 +383,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
|
|
|
383
383
|
* 列宽拖动时,每一列都必须要有width,且minWidth/maxWidth不生效。table width会变为"fit-content"。
|
|
384
384
|
* - 会自动更新props.columns中的with属性
|
|
385
385
|
*/
|
|
386
|
-
colResizable?: boolean | undefined;
|
|
386
|
+
colResizable?: boolean | ColResizableConfig<any> | undefined;
|
|
387
387
|
/** 可拖动至最小的列宽 */
|
|
388
388
|
colMinWidth?: number | undefined;
|
|
389
389
|
/**
|
|
@@ -414,7 +414,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
|
|
|
414
414
|
seqConfig?: SeqConfig | undefined;
|
|
415
415
|
/** 展开行配置 */
|
|
416
416
|
expandConfig?: ExpandConfig | undefined;
|
|
417
|
-
/**
|
|
417
|
+
/** 行拖动配置 */
|
|
418
418
|
dragRowConfig?: DragRowConfig | undefined;
|
|
419
419
|
/**
|
|
420
420
|
* 固定头,固定列实现方式。(非响应式)
|
|
@@ -438,6 +438,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
|
|
|
438
438
|
width: string;
|
|
439
439
|
minWidth: string;
|
|
440
440
|
maxWidth: string;
|
|
441
|
+
sortConfig: SortConfig<any>;
|
|
441
442
|
rowHeight: number;
|
|
442
443
|
headerRowHeight: number | null;
|
|
443
444
|
headless: boolean;
|
|
@@ -470,12 +471,11 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
|
|
|
470
471
|
selectedCellRevokable: boolean;
|
|
471
472
|
headerDrag: HeaderDragConfig;
|
|
472
473
|
rowClassName: (row: any, i: number) => string;
|
|
473
|
-
colResizable: boolean
|
|
474
|
+
colResizable: boolean | ColResizableConfig<any>;
|
|
474
475
|
colMinWidth: number;
|
|
475
476
|
bordered: boolean | "h" | "v" | "body-v";
|
|
476
477
|
autoResize: boolean | (() => void);
|
|
477
478
|
fixedColShadow: boolean;
|
|
478
|
-
sortConfig: SortConfig<any>;
|
|
479
479
|
hideHeaderTitle: boolean | string[];
|
|
480
480
|
highlightConfig: HighlightConfig;
|
|
481
481
|
seqConfig: SeqConfig;
|
|
@@ -71,6 +71,8 @@ export type StkTableColumn<T extends Record<string, any>> = {
|
|
|
71
71
|
sortField?: keyof T;
|
|
72
72
|
/** 排序方式。按数字/字符串 */
|
|
73
73
|
sortType?: 'number' | 'string';
|
|
74
|
+
/** 配置当前列的排序规则 */
|
|
75
|
+
sortConfig?: Pick<SortConfig<T>, 'emptyToBottom' | 'stringLocaleCompare'>;
|
|
74
76
|
/** 固定列 */
|
|
75
77
|
fixed?: 'left' | 'right' | null;
|
|
76
78
|
/** private */ rowSpan?: number;
|
|
@@ -183,6 +185,7 @@ export type SeqConfig = {
|
|
|
183
185
|
};
|
|
184
186
|
/** Configuration options for the expand column */
|
|
185
187
|
export type ExpandConfig = {
|
|
188
|
+
/** worked in virtual mode */
|
|
186
189
|
height?: number;
|
|
187
190
|
};
|
|
188
191
|
export type ExpandedRow = PrivateRowDT & {
|
|
@@ -209,4 +212,7 @@ export type AutoRowHeightConfig<DT> = {
|
|
|
209
212
|
/** Estimated row height */
|
|
210
213
|
expectedHeight?: number | ((row: DT, index: number) => number);
|
|
211
214
|
};
|
|
215
|
+
export type ColResizableConfig<DT extends Record<string, any>> = {
|
|
216
|
+
disabled: (col: StkTableColumn<DT>) => boolean;
|
|
217
|
+
};
|
|
212
218
|
export {};
|
|
@@ -11,6 +11,7 @@ type Params<DT extends Record<string, any>> = {
|
|
|
11
11
|
};
|
|
12
12
|
/** 列宽拖动 */
|
|
13
13
|
export declare function useColResize<DT extends Record<string, any>>({ tableContainerRef, tableHeaderLast, colResizeIndicatorRef, props, emits, colKeyGen, fixedCols, }: Params<DT>): {
|
|
14
|
+
colResizeOn: ComputedRef<((col: StkTableColumn<DT>) => boolean) | (() => any)>;
|
|
14
15
|
isColResizing: Ref<boolean, boolean>;
|
|
15
16
|
onThResizeMouseDown: (e: MouseEvent, col: StkTableColumn<DT>, leftHandle?: boolean) => void;
|
|
16
17
|
};
|
|
@@ -6,14 +6,14 @@ type Params<T extends Record<string, any>> = {
|
|
|
6
6
|
colKeyGen: ComputedRef<(col: StkTableColumn<T>) => UniqKey>;
|
|
7
7
|
getFixedColPosition: ComputedRef<(col: StkTableColumn<T>) => number>;
|
|
8
8
|
tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
|
|
9
|
-
|
|
9
|
+
tableHeadersForCalc: ShallowRef<StkTableColumn<T>[][]>;
|
|
10
10
|
tableContainerRef: Ref<HTMLDivElement | undefined>;
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* 固定列处理
|
|
14
14
|
* @returns
|
|
15
15
|
*/
|
|
16
|
-
export declare function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, getFixedColPosition, tableHeaders,
|
|
16
|
+
export declare function useFixedCol<DT extends Record<string, any>>({ props, colKeyGen, getFixedColPosition, tableHeaders, tableHeadersForCalc, tableContainerRef, }: Params<DT>): {
|
|
17
17
|
/** 正在被固定的列 */
|
|
18
18
|
fixedCols: ShallowRef<StkTableColumn<DT>[], StkTableColumn<DT>[]>;
|
|
19
19
|
/** 固定列class */
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Order, SortConfig, SortOption, SortState, StkTableColumn } from '../types';
|
|
2
|
+
/** 是否空值 */
|
|
3
|
+
export declare function isEmptyValue(val: any, isNumber?: boolean): boolean;
|
|
2
4
|
/**
|
|
3
5
|
* 对有序数组插入新数据
|
|
4
6
|
*
|
|
@@ -36,6 +38,6 @@ export declare function strCompare(a: string, b: string, isNumber: boolean, loca
|
|
|
36
38
|
export declare function tableSort<T extends Record<string, any>>(sortOption: SortOption<T>, order: Order, dataSource: T[], sortConfig?: SortConfig<T>): T[];
|
|
37
39
|
/** 多级表头深度 从0开始为一级*/
|
|
38
40
|
export declare function howDeepTheHeader(arr: StkTableColumn<any>[], level?: number): number;
|
|
39
|
-
/** number
|
|
41
|
+
/** number width +px */
|
|
40
42
|
export declare function transformWidthToStr(width?: string | number): string | undefined;
|
|
41
43
|
export declare function getBrowsersVersion(browserName: string): number;
|
package/lib/stk-table-vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { openBlock, createElementBlock, createElementVNode, watch, onMounted, onBeforeUnmount, ref,
|
|
1
|
+
import { openBlock, createElementBlock, createElementVNode, watch, onMounted, onBeforeUnmount, ref, computed, shallowRef, defineComponent, nextTick, toRaw, normalizeClass, unref, normalizeStyle, createCommentVNode, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, renderSlot, createVNode, createTextVNode } from "vue";
|
|
2
2
|
import { interpolateRgb } from "d3-interpolate";
|
|
3
3
|
const _export_sfc = (sfc, props) => {
|
|
4
4
|
const target = sfc.__vccOpts || sfc;
|
|
@@ -48,7 +48,7 @@ function _sfc_render(_ctx, _cache) {
|
|
|
48
48
|
}
|
|
49
49
|
const SortIcon = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render]]);
|
|
50
50
|
function isEmptyValue(val, isNumber) {
|
|
51
|
-
let isEmpty = val === null || val ===
|
|
51
|
+
let isEmpty = val === null || val === void 0;
|
|
52
52
|
if (isNumber) {
|
|
53
53
|
isEmpty = isEmpty || typeof val === "boolean" || Number.isNaN(+val);
|
|
54
54
|
}
|
|
@@ -158,10 +158,9 @@ function howDeepTheHeader(arr, level = 0) {
|
|
|
158
158
|
return Math.max(...levels);
|
|
159
159
|
}
|
|
160
160
|
function transformWidthToStr(width) {
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return width;
|
|
161
|
+
if (width === void 0) return;
|
|
162
|
+
const numberWidth = Number(width);
|
|
163
|
+
return width + (!Number.isNaN(numberWidth) ? "px" : "");
|
|
165
164
|
}
|
|
166
165
|
function getBrowsersVersion(browserName) {
|
|
167
166
|
try {
|
|
@@ -305,6 +304,12 @@ function useColResize({
|
|
|
305
304
|
startOffsetTableX: 0,
|
|
306
305
|
revertMoveX: false
|
|
307
306
|
};
|
|
307
|
+
const colResizeOn = computed(() => {
|
|
308
|
+
if (Object.prototype.toString.call(props.colResizable) === "[object Object]") {
|
|
309
|
+
return (col) => !props.colResizable.disabled(col);
|
|
310
|
+
}
|
|
311
|
+
return () => props.colResizable;
|
|
312
|
+
});
|
|
308
313
|
onMounted(() => {
|
|
309
314
|
initColResizeEvent();
|
|
310
315
|
});
|
|
@@ -369,7 +374,8 @@ function useColResize({
|
|
|
369
374
|
const { clientX } = e;
|
|
370
375
|
let moveX = clientX - startX;
|
|
371
376
|
const currentColWidth = getCalculatedColWidth(lastCol);
|
|
372
|
-
|
|
377
|
+
const minWidth = (lastCol == null ? void 0 : lastCol.minWidth) ?? props.colMinWidth;
|
|
378
|
+
if (currentColWidth + moveX < minWidth) {
|
|
373
379
|
moveX = -currentColWidth;
|
|
374
380
|
}
|
|
375
381
|
const offsetTableX = startOffsetTableX + moveX;
|
|
@@ -412,6 +418,7 @@ function useColResize({
|
|
|
412
418
|
return column;
|
|
413
419
|
}
|
|
414
420
|
return {
|
|
421
|
+
colResizeOn,
|
|
415
422
|
isColResizing,
|
|
416
423
|
onThResizeMouseDown
|
|
417
424
|
};
|
|
@@ -421,7 +428,7 @@ function useFixedCol({
|
|
|
421
428
|
colKeyGen,
|
|
422
429
|
getFixedColPosition,
|
|
423
430
|
tableHeaders,
|
|
424
|
-
|
|
431
|
+
tableHeadersForCalc,
|
|
425
432
|
tableContainerRef
|
|
426
433
|
}) {
|
|
427
434
|
const fixedShadowCols = shallowRef([]);
|
|
@@ -444,23 +451,8 @@ function useFixedCol({
|
|
|
444
451
|
});
|
|
445
452
|
return colMap;
|
|
446
453
|
});
|
|
447
|
-
function getColAndParentCols(col, type = 1) {
|
|
448
|
-
if (!col) return [];
|
|
449
|
-
const colsTemp = [];
|
|
450
|
-
let node = { __PARENT__: col };
|
|
451
|
-
while (node = node.__PARENT__) {
|
|
452
|
-
if (type === 1 && node.fixed) {
|
|
453
|
-
colsTemp.push(node);
|
|
454
|
-
}
|
|
455
|
-
if (type === 2) {
|
|
456
|
-
colsTemp.push(node);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return colsTemp;
|
|
460
|
-
}
|
|
461
454
|
function updateFixedShadow(virtualScrollX) {
|
|
462
455
|
const fixedColsTemp = [];
|
|
463
|
-
const fixedShadowColsTemp = [];
|
|
464
456
|
let clientWidth, scrollLeft;
|
|
465
457
|
if (virtualScrollX == null ? void 0 : virtualScrollX.value) {
|
|
466
458
|
const {
|
|
@@ -479,26 +471,29 @@ function useFixedCol({
|
|
|
479
471
|
clientWidth = cw;
|
|
480
472
|
scrollLeft = sl;
|
|
481
473
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
474
|
+
const leftShadowCol = [];
|
|
475
|
+
const rightShadowCol = [];
|
|
476
|
+
tableHeadersForCalc.value.forEach((row, level) => {
|
|
477
|
+
let left = 0;
|
|
478
|
+
row.forEach((col) => {
|
|
479
|
+
const position = getFixedColPosition.value(col);
|
|
480
|
+
const isFixedLeft = col.fixed === "left";
|
|
481
|
+
const isFixedRight = col.fixed === "right";
|
|
482
|
+
if (isFixedLeft && position + scrollLeft > left) {
|
|
483
|
+
fixedColsTemp.push(col);
|
|
484
|
+
leftShadowCol[level] = col;
|
|
485
|
+
}
|
|
486
|
+
left += getCalculatedColWidth(col);
|
|
487
|
+
if (isFixedRight && scrollLeft + clientWidth - left < position) {
|
|
488
|
+
fixedColsTemp.push(col);
|
|
489
|
+
if (!rightShadowCol[level]) {
|
|
490
|
+
rightShadowCol[level] = col;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
});
|
|
498
494
|
});
|
|
499
495
|
if (props.fixedColShadow) {
|
|
500
|
-
|
|
501
|
-
fixedShadowCols.value = fixedShadowColsTemp.filter(Boolean);
|
|
496
|
+
fixedShadowCols.value = [...leftShadowCol, ...rightShadowCol].filter(Boolean);
|
|
502
497
|
}
|
|
503
498
|
fixedCols.value = fixedColsTemp;
|
|
504
499
|
}
|
|
@@ -634,7 +629,7 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
634
629
|
const { ts, duration } = store;
|
|
635
630
|
const timeOffset = nowTs - ts;
|
|
636
631
|
if (nowTs - ts < duration) {
|
|
637
|
-
|
|
632
|
+
updateRowAnimation(rowKeyValue, store, timeOffset);
|
|
638
633
|
} else {
|
|
639
634
|
highlightDimRowsAnimation.delete(rowKeyValue);
|
|
640
635
|
}
|
|
@@ -709,7 +704,7 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
709
704
|
const rowKeyValue = rowKeyValues[i];
|
|
710
705
|
const store = { ts: nowTs, visible: false, keyframe, duration };
|
|
711
706
|
highlightDimRowsAnimation.set(rowKeyValue, store);
|
|
712
|
-
|
|
707
|
+
updateRowAnimation(rowKeyValue, store, 0);
|
|
713
708
|
}
|
|
714
709
|
calcRowHighlightLoop();
|
|
715
710
|
} else {
|
|
@@ -771,7 +766,7 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
771
766
|
}, duration)
|
|
772
767
|
);
|
|
773
768
|
}
|
|
774
|
-
function
|
|
769
|
+
function updateRowAnimation(rowKeyValue, store, timeOffset) {
|
|
775
770
|
const rowEl = document.getElementById(stkTableId + "-" + String(rowKeyValue));
|
|
776
771
|
const { visible, keyframe, duration: initialDuration } = store;
|
|
777
772
|
if (!rowEl) {
|
|
@@ -917,7 +912,7 @@ function useThDrag({ props, emits, colKeyGen }) {
|
|
|
917
912
|
emits("th-drop", th.dataset.colKey);
|
|
918
913
|
}
|
|
919
914
|
function handleColOrderChange(dragStartKey, dragEndKey) {
|
|
920
|
-
if (
|
|
915
|
+
if (isEmptyValue(dragStartKey) || isEmptyValue(dragEndKey)) return;
|
|
921
916
|
if (dragConfig.value.mode !== "none") {
|
|
922
917
|
const columns = [...props.columns];
|
|
923
918
|
const dragStartIndex = columns.findIndex((col) => colKeyGen.value(col) === dragStartKey);
|
|
@@ -1065,6 +1060,9 @@ function useVirtualScroll({
|
|
|
1065
1060
|
offsetLeft: 0,
|
|
1066
1061
|
scrollLeft: 0
|
|
1067
1062
|
});
|
|
1063
|
+
const hasExpandCol = computed(() => {
|
|
1064
|
+
return tableHeaderLast.value.some((col) => col.type === "expand");
|
|
1065
|
+
});
|
|
1068
1066
|
const virtual_on = computed(() => {
|
|
1069
1067
|
return props.virtual && dataSourceCopy.value.length > virtualScroll.value.pageSize * 2;
|
|
1070
1068
|
});
|
|
@@ -1171,7 +1169,8 @@ function useVirtualScroll({
|
|
|
1171
1169
|
let expectedHeight;
|
|
1172
1170
|
if (storedHeight) {
|
|
1173
1171
|
return storedHeight;
|
|
1174
|
-
}
|
|
1172
|
+
}
|
|
1173
|
+
if (expectedHeight = (_a = props.autoRowHeight) == null ? void 0 : _a.expectedHeight) {
|
|
1175
1174
|
if (typeof expectedHeight === "function") {
|
|
1176
1175
|
return expectedHeight(row);
|
|
1177
1176
|
} else {
|
|
@@ -1180,6 +1179,18 @@ function useVirtualScroll({
|
|
|
1180
1179
|
}
|
|
1181
1180
|
return props.rowHeight || DEFAULT_ROW_HEIGHT;
|
|
1182
1181
|
};
|
|
1182
|
+
const createGetRowHeightFn = () => {
|
|
1183
|
+
var _a;
|
|
1184
|
+
if (props.autoRowHeight) {
|
|
1185
|
+
return (row) => getAutoRowHeight(row);
|
|
1186
|
+
}
|
|
1187
|
+
if (hasExpandCol.value) {
|
|
1188
|
+
const { rowHeight } = virtualScroll.value;
|
|
1189
|
+
const expandedRowHeight = ((_a = props.expandConfig) == null ? void 0 : _a.height) || rowHeight;
|
|
1190
|
+
return (row) => row.__EXPANDED_ROW__ ? expandedRowHeight : rowHeight;
|
|
1191
|
+
}
|
|
1192
|
+
return () => props.rowHeight || DEFAULT_ROW_HEIGHT;
|
|
1193
|
+
};
|
|
1183
1194
|
function updateVirtualScrollY(sTop = 0) {
|
|
1184
1195
|
var _a;
|
|
1185
1196
|
const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex } = virtualScroll.value;
|
|
@@ -1187,21 +1198,27 @@ function useVirtualScroll({
|
|
|
1187
1198
|
if (!virtual_on.value) {
|
|
1188
1199
|
return;
|
|
1189
1200
|
}
|
|
1201
|
+
const dataSourceCopyTemp = dataSourceCopy.value;
|
|
1190
1202
|
const { autoRowHeight, stripe, optimizeVue2Scroll } = props;
|
|
1191
1203
|
let startIndex = 0;
|
|
1192
1204
|
let autoRowHeightTop = 0;
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1205
|
+
let getRowHeight = null;
|
|
1206
|
+
const dataLength = dataSourceCopyTemp.length;
|
|
1207
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
1208
|
+
if (autoRowHeight) {
|
|
1209
|
+
(_a = trRef.value) == null ? void 0 : _a.forEach((tr) => {
|
|
1210
|
+
const { rowKey } = tr.dataset;
|
|
1211
|
+
if (!rowKey || autoRowHeightMap.has(rowKey)) return;
|
|
1212
|
+
autoRowHeightMap.set(rowKey, tr.offsetHeight);
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
getRowHeight = createGetRowHeightFn();
|
|
1216
|
+
for (let i = 0; i < dataLength; i++) {
|
|
1217
|
+
const height = getRowHeight(dataSourceCopyTemp[i]);
|
|
1201
1218
|
autoRowHeightTop += height;
|
|
1202
1219
|
if (autoRowHeightTop >= sTop) {
|
|
1203
1220
|
startIndex = i;
|
|
1204
|
-
autoRowHeightTop
|
|
1221
|
+
autoRowHeightTop -= height;
|
|
1205
1222
|
break;
|
|
1206
1223
|
}
|
|
1207
1224
|
}
|
|
@@ -1211,13 +1228,13 @@ function useVirtualScroll({
|
|
|
1211
1228
|
let endIndex = startIndex + pageSize;
|
|
1212
1229
|
if (stripe && startIndex > 0 && startIndex % 2) {
|
|
1213
1230
|
startIndex -= 1;
|
|
1214
|
-
if (autoRowHeight) {
|
|
1215
|
-
const height =
|
|
1231
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
1232
|
+
const height = getRowHeight(dataSourceCopyTemp[startIndex]);
|
|
1216
1233
|
autoRowHeightTop -= height;
|
|
1217
1234
|
}
|
|
1218
1235
|
}
|
|
1219
1236
|
startIndex = Math.max(0, startIndex);
|
|
1220
|
-
endIndex = Math.min(endIndex,
|
|
1237
|
+
endIndex = Math.min(endIndex, dataLength);
|
|
1221
1238
|
if (startIndex >= endIndex) {
|
|
1222
1239
|
startIndex = endIndex - pageSize;
|
|
1223
1240
|
}
|
|
@@ -1225,7 +1242,7 @@ function useVirtualScroll({
|
|
|
1225
1242
|
window.clearTimeout(vue2ScrollYTimeout);
|
|
1226
1243
|
}
|
|
1227
1244
|
let offsetTop = 0;
|
|
1228
|
-
if (autoRowHeight) {
|
|
1245
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
1229
1246
|
offsetTop = autoRowHeightTop;
|
|
1230
1247
|
} else {
|
|
1231
1248
|
if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
|
|
@@ -1474,9 +1491,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1474
1491
|
getFixedColPosition,
|
|
1475
1492
|
tableContainerRef,
|
|
1476
1493
|
tableHeaders,
|
|
1477
|
-
|
|
1494
|
+
tableHeadersForCalc
|
|
1478
1495
|
});
|
|
1479
|
-
const { isColResizing, onThResizeMouseDown } = useColResize({
|
|
1496
|
+
const { isColResizing, onThResizeMouseDown, colResizeOn } = useColResize({
|
|
1480
1497
|
props,
|
|
1481
1498
|
emits,
|
|
1482
1499
|
colKeyGen,
|
|
@@ -1701,7 +1718,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1701
1718
|
if (click) sortOrderIndex.value++;
|
|
1702
1719
|
sortOrderIndex.value = sortOrderIndex.value % 3;
|
|
1703
1720
|
let order = sortSwitchOrder[sortOrderIndex.value];
|
|
1704
|
-
const sortConfig = props.sortConfig;
|
|
1721
|
+
const sortConfig = { ...props.sortConfig, ...col.sortConfig };
|
|
1705
1722
|
const defaultSort = sortConfig.defaultSort;
|
|
1706
1723
|
if (!order && defaultSort) {
|
|
1707
1724
|
const colKey2 = defaultSort.key || defaultSort.dataIndex;
|
|
@@ -2176,12 +2193,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2176
2193
|
col.sorter ? (openBlock(), createElementBlock("span", _hoisted_4, [
|
|
2177
2194
|
createVNode(SortIcon)
|
|
2178
2195
|
])) : createCommentVNode("", true),
|
|
2179
|
-
|
|
2196
|
+
unref(colResizeOn)(col) && colIndex > 0 ? (openBlock(), createElementBlock("div", {
|
|
2180
2197
|
key: 4,
|
|
2181
2198
|
class: "table-header-resizer left",
|
|
2182
2199
|
onMousedown: (e) => unref(onThResizeMouseDown)(e, col, true)
|
|
2183
2200
|
}, null, 40, _hoisted_5)) : createCommentVNode("", true),
|
|
2184
|
-
|
|
2201
|
+
unref(colResizeOn)(col) ? (openBlock(), createElementBlock("div", {
|
|
2185
2202
|
key: 5,
|
|
2186
2203
|
class: "table-header-resizer right",
|
|
2187
2204
|
onMousedown: (e) => unref(onThResizeMouseDown)(e, col)
|
|
@@ -2236,7 +2253,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2236
2253
|
"expanded-row": row && row.__EXPANDED_ROW__
|
|
2237
2254
|
}),
|
|
2238
2255
|
style: normalizeStyle({
|
|
2239
|
-
"--row-height": row && row.__EXPANDED_ROW__ &&
|
|
2256
|
+
"--row-height": row && row.__EXPANDED_ROW__ && props.virtual && ((_a = props.expandConfig) == null ? void 0 : _a.height) && ((_b = props.expandConfig) == null ? void 0 : _b.height) + "px"
|
|
2240
2257
|
}),
|
|
2241
2258
|
onClick: (e) => onRowClick(e, row),
|
|
2242
2259
|
onDblclick: (e) => onRowDblclick(e, row),
|
package/lib/style.css
CHANGED
|
@@ -38,8 +38,6 @@
|
|
|
38
38
|
display:flex;
|
|
39
39
|
flex-direction:column;
|
|
40
40
|
box-sizing:border-box;
|
|
41
|
-
background-image:linear-gradient(180deg, #e8e8f4 1px, transparent 1px), linear-gradient(270deg, #e8e8f4 1px, transparent 1px), linear-gradient(0deg, #e8e8f4 1px, transparent 1px);
|
|
42
|
-
background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
43
41
|
}
|
|
44
42
|
.stk-table.dark{
|
|
45
43
|
--th-bgc:#202029;
|
|
@@ -63,21 +61,22 @@
|
|
|
63
61
|
--drag-handle-hover-color:#5d6064;
|
|
64
62
|
color:#d1d1e0;
|
|
65
63
|
}
|
|
66
|
-
.stk-table.headless{
|
|
64
|
+
.stk-table.headless.border{
|
|
67
65
|
border-top:1px solid var(--border-color);
|
|
68
66
|
background-image:var(--bg-border-right), var(--bg-border-bottom);
|
|
69
67
|
}
|
|
70
68
|
.stk-table.col-resizable .stk-table-main{
|
|
71
|
-
width:-moz-fit-content
|
|
72
|
-
width:fit-content
|
|
73
|
-
min-width:-moz-min-content
|
|
74
|
-
min-width:min-content
|
|
69
|
+
width:-moz-fit-content;
|
|
70
|
+
width:fit-content;
|
|
71
|
+
min-width:-moz-min-content;
|
|
72
|
+
min-width:min-content;
|
|
75
73
|
}
|
|
76
74
|
.stk-table.is-col-resizing th{
|
|
77
75
|
pointer-events:none;
|
|
78
76
|
}
|
|
79
77
|
.stk-table.border{
|
|
80
78
|
border-left:1px solid var(--border-color);
|
|
79
|
+
background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
81
80
|
}
|
|
82
81
|
.stk-table.border th,
|
|
83
82
|
.stk-table.border td{
|
|
@@ -104,13 +103,13 @@
|
|
|
104
103
|
.stk-table.stripe.vt-on .stk-tbody-main tr:nth-child(odd){
|
|
105
104
|
background-color:var(--stripe-bgc);
|
|
106
105
|
}
|
|
107
|
-
.stk-table.stripe.row-hover .stk-tbody-main tr:hover{
|
|
106
|
+
.stk-table.stripe.row-hover .stk-table-main .stk-tbody-main tr:hover{
|
|
108
107
|
background-color:var(--tr-hover-bgc);
|
|
109
108
|
}
|
|
110
|
-
.stk-table.stripe.row-active .stk-tbody-main tr.active{
|
|
109
|
+
.stk-table.stripe.row-active .stk-table-main .stk-tbody-main tr.active{
|
|
111
110
|
background-color:var(--tr-active-bgc);
|
|
112
111
|
}
|
|
113
|
-
.stk-table.row-hover .stk-tbody-main tr:hover{
|
|
112
|
+
.stk-table.row-hover .stk-table-main .stk-tbody-main tr:hover{
|
|
114
113
|
background-color:var(--tr-hover-bgc);
|
|
115
114
|
}
|
|
116
115
|
.stk-table.row-active .stk-tbody-main tr.active{
|
|
@@ -142,6 +141,9 @@
|
|
|
142
141
|
max-height:calc(var(--header-row-height) * 1);
|
|
143
142
|
max-height:calc(var(--header-row-height) * var(--row-span, 1));
|
|
144
143
|
}
|
|
144
|
+
.stk-table.virtual .table-header-cell-wrapper .table-header-title{
|
|
145
|
+
max-height:inherit;
|
|
146
|
+
}
|
|
145
147
|
.stk-table.virtual tbody td{
|
|
146
148
|
height:var(--row-height);
|
|
147
149
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stk-table-vue",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Simple realtime virtual table for vue3 and vue2.7",
|
|
5
5
|
"main": "./lib/stk-table-vue.js",
|
|
6
6
|
"types": "./lib/src/StkTable/index.d.ts",
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
"dev": "vite",
|
|
14
14
|
"build": "vite build",
|
|
15
15
|
"test": "vitest",
|
|
16
|
-
"docs:dev": "vitepress dev docs",
|
|
17
|
-
"docs:build": "vitepress build docs",
|
|
18
|
-
"docs:preview": "vitepress preview docs"
|
|
16
|
+
"docs:dev": "vitepress dev docs-src",
|
|
17
|
+
"docs:build": "vitepress build docs-src",
|
|
18
|
+
"docs:preview": "vitepress preview docs-src",
|
|
19
|
+
"docs:update": "cp -rf ./docs-src/.vitepress/dist/* ./docs"
|
|
19
20
|
},
|
|
20
21
|
"keywords": [
|
|
21
22
|
"virtual table",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"license": "MIT",
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/d3-interpolate": "^3.0.4",
|
|
44
|
+
"@types/mockjs": "^1.0.10",
|
|
43
45
|
"@types/node": "^20.12.10",
|
|
44
46
|
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
45
47
|
"@typescript-eslint/parser": "^7.7.0",
|
|
@@ -52,6 +54,8 @@
|
|
|
52
54
|
"eslint-plugin-vue": "^9.25.0",
|
|
53
55
|
"happy-dom": "^12.10.3",
|
|
54
56
|
"less": "^4.2.0",
|
|
57
|
+
"mitt": "^3.0.1",
|
|
58
|
+
"mockjs": "^1.1.0",
|
|
55
59
|
"postcss": "^8.4.47",
|
|
56
60
|
"postcss-discard-comments": "^6.0.2",
|
|
57
61
|
"postcss-preset-env": "^9.5.11",
|
|
@@ -60,7 +64,7 @@
|
|
|
60
64
|
"vite": "^5.4.10",
|
|
61
65
|
"vite-plugin-dts": "^4.3.0",
|
|
62
66
|
"vitepress": "^1.5.0",
|
|
63
|
-
"vitepress-demo-plugin": "^1.
|
|
67
|
+
"vitepress-demo-plugin": "^1.2.2",
|
|
64
68
|
"vitest": "^2.1.3",
|
|
65
69
|
"vue": "^3.5.12",
|
|
66
70
|
"vue-eslint-parser": "^9.4.2"
|
|
@@ -102,11 +102,11 @@
|
|
|
102
102
|
</span>
|
|
103
103
|
<!-- 列宽拖动handler -->
|
|
104
104
|
<div
|
|
105
|
-
v-if="
|
|
105
|
+
v-if="colResizeOn(col) && colIndex > 0"
|
|
106
106
|
class="table-header-resizer left"
|
|
107
107
|
@mousedown="e => onThResizeMouseDown(e, col, true)"
|
|
108
108
|
></div>
|
|
109
|
-
<div v-if="
|
|
109
|
+
<div v-if="colResizeOn(col)" class="table-header-resizer right" @mousedown="e => onThResizeMouseDown(e, col)"></div>
|
|
110
110
|
</div>
|
|
111
111
|
</th>
|
|
112
112
|
<!-- 这个th用于横向虚拟滚动表格右边距 width、maxWidth 用于兼容低版本浏览器-->
|
|
@@ -136,15 +136,11 @@
|
|
|
136
136
|
hover: props.showTrHoverClass && (rowKey ? rowKeyGen(row) === currentHoverRowKey : row === currentHoverRowKey),
|
|
137
137
|
[rowClassName(row, (virtual_on ? virtualScroll.startIndex : 0) + rowIndex)]: true,
|
|
138
138
|
expanded: row?.__EXPANDED__,
|
|
139
|
-
'expanded-row': row &&
|
|
139
|
+
'expanded-row': row && row.__EXPANDED_ROW__,
|
|
140
140
|
}"
|
|
141
141
|
:style="{
|
|
142
142
|
'--row-height':
|
|
143
|
-
row &&
|
|
144
|
-
(row as ExpandedRow).__EXPANDED_ROW__ &&
|
|
145
|
-
virtual_on &&
|
|
146
|
-
props.expandConfig?.height &&
|
|
147
|
-
props.expandConfig?.height + 'px',
|
|
143
|
+
row && row.__EXPANDED_ROW__ && props.virtual && props.expandConfig?.height && props.expandConfig?.height + 'px',
|
|
148
144
|
}"
|
|
149
145
|
@click="e => onRowClick(e, row)"
|
|
150
146
|
@dblclick="e => onRowDblclick(e, row)"
|
|
@@ -154,11 +150,11 @@
|
|
|
154
150
|
>
|
|
155
151
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
156
152
|
<td v-if="virtualX_on" class="vt-x-left"></td>
|
|
157
|
-
<td v-if="row &&
|
|
153
|
+
<td v-if="row && row.__EXPANDED_ROW__" :colspan="virtualX_columnPart.length">
|
|
158
154
|
<!-- TODO: support wheel -->
|
|
159
155
|
<div class="table-cell-wrapper">
|
|
160
|
-
<slot name="expand" :row="
|
|
161
|
-
{{
|
|
156
|
+
<slot name="expand" :row="row.__EXPANDED_ROW__" :col="row.__EXPANDED_COL__">
|
|
157
|
+
{{ row.__EXPANDED_ROW__?.[row.__EXPANDED_COL__.dataIndex] ?? '' }}
|
|
162
158
|
</slot>
|
|
163
159
|
</div>
|
|
164
160
|
</td>
|
|
@@ -258,6 +254,7 @@ import {
|
|
|
258
254
|
StkTableColumn,
|
|
259
255
|
TagType,
|
|
260
256
|
UniqKeyProp,
|
|
257
|
+
ColResizableConfig,
|
|
261
258
|
} from './types/index';
|
|
262
259
|
import { useAutoResize } from './useAutoResize';
|
|
263
260
|
import { useColResize } from './useColResize';
|
|
@@ -359,7 +356,7 @@ const props = withDefaults(
|
|
|
359
356
|
* 列宽拖动时,每一列都必须要有width,且minWidth/maxWidth不生效。table width会变为"fit-content"。
|
|
360
357
|
* - 会自动更新props.columns中的with属性
|
|
361
358
|
*/
|
|
362
|
-
colResizable?: boolean
|
|
359
|
+
colResizable?: boolean | ColResizableConfig<DT>;
|
|
363
360
|
/** 可拖动至最小的列宽 */
|
|
364
361
|
colMinWidth?: number;
|
|
365
362
|
/**
|
|
@@ -390,7 +387,7 @@ const props = withDefaults(
|
|
|
390
387
|
seqConfig?: SeqConfig;
|
|
391
388
|
/** 展开行配置 */
|
|
392
389
|
expandConfig?: ExpandConfig;
|
|
393
|
-
/**
|
|
390
|
+
/** 行拖动配置 */
|
|
394
391
|
dragRowConfig?: DragRowConfig;
|
|
395
392
|
/**
|
|
396
393
|
* 固定头,固定列实现方式。(非响应式)
|
|
@@ -752,10 +749,10 @@ const { fixedCols, fixedColClassMap, updateFixedShadow } = useFixedCol({
|
|
|
752
749
|
getFixedColPosition,
|
|
753
750
|
tableContainerRef,
|
|
754
751
|
tableHeaders,
|
|
755
|
-
|
|
752
|
+
tableHeadersForCalc,
|
|
756
753
|
});
|
|
757
754
|
|
|
758
|
-
const { isColResizing, onThResizeMouseDown } = useColResize({
|
|
755
|
+
const { isColResizing, onThResizeMouseDown, colResizeOn } = useColResize({
|
|
759
756
|
props,
|
|
760
757
|
emits,
|
|
761
758
|
colKeyGen,
|
|
@@ -1047,7 +1044,7 @@ function onColumnSort(col: StkTableColumn<DT> | undefined | null, click = true,
|
|
|
1047
1044
|
sortOrderIndex.value = sortOrderIndex.value % 3;
|
|
1048
1045
|
|
|
1049
1046
|
let order = sortSwitchOrder[sortOrderIndex.value];
|
|
1050
|
-
const sortConfig = props.sortConfig;
|
|
1047
|
+
const sortConfig = { ...props.sortConfig, ...col.sortConfig };
|
|
1051
1048
|
const defaultSort = sortConfig.defaultSort;
|
|
1052
1049
|
|
|
1053
1050
|
if (!order && defaultSort) {
|
package/src/StkTable/style.less
CHANGED
|
@@ -50,9 +50,6 @@
|
|
|
50
50
|
flex-direction: column;
|
|
51
51
|
box-sizing: border-box;
|
|
52
52
|
|
|
53
|
-
/* 下面border用于表格内容不满高度时,绘制表格边界线 */
|
|
54
|
-
background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
55
|
-
|
|
56
53
|
/**深色模式 */
|
|
57
54
|
&.dark {
|
|
58
55
|
--th-bgc: #202029;
|
|
@@ -85,17 +82,20 @@
|
|
|
85
82
|
/* ⭐这里加background-color会导致表格出滚动白屏*/
|
|
86
83
|
color: #d1d1e0;
|
|
87
84
|
}
|
|
85
|
+
|
|
88
86
|
|
|
89
87
|
&.headless {
|
|
90
|
-
border
|
|
91
|
-
|
|
88
|
+
&.border {
|
|
89
|
+
border-top: 1px solid var(--border-color);
|
|
90
|
+
background-image: var(--bg-border-right), var(--bg-border-bottom);
|
|
91
|
+
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/* 调整列宽的话,表格宽度应当自适应 */
|
|
95
95
|
&.col-resizable {
|
|
96
96
|
.stk-table-main {
|
|
97
|
-
width: fit-content
|
|
98
|
-
min-width: min-content
|
|
97
|
+
width: fit-content;
|
|
98
|
+
min-width: min-content;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -115,6 +115,8 @@
|
|
|
115
115
|
* - sticky 定位方案需要占用位置。高度为0。
|
|
116
116
|
*/
|
|
117
117
|
border-left: 1px solid var(--border-color);
|
|
118
|
+
/* 下面border用于表格内容不满高度时,绘制表格边界线 */
|
|
119
|
+
background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
|
|
118
120
|
|
|
119
121
|
th,
|
|
120
122
|
td {
|
|
@@ -155,17 +157,18 @@
|
|
|
155
157
|
background-color: var(--stripe-bgc);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
&.row-hover .stk-tbody-main tr:hover {
|
|
160
|
+
&.row-hover .stk-table-main .stk-tbody-main tr:hover {
|
|
159
161
|
background-color: var(--tr-hover-bgc);
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
&.row-active .stk-tbody-main tr.active {
|
|
164
|
+
&.row-active .stk-table-main .stk-tbody-main tr.active {
|
|
163
165
|
background-color: var(--tr-active-bgc);
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
/** more weight for custom row background*/
|
|
171
|
+
&.row-hover .stk-table-main .stk-tbody-main tr:hover {
|
|
169
172
|
background-color: var(--tr-hover-bgc);
|
|
170
173
|
}
|
|
171
174
|
|
|
@@ -213,6 +216,10 @@
|
|
|
213
216
|
.table-header-cell-wrapper {
|
|
214
217
|
overflow: hidden;
|
|
215
218
|
max-height: calc(var(--header-row-height) * var(--row-span, 1));
|
|
219
|
+
|
|
220
|
+
.table-header-title {
|
|
221
|
+
max-height: inherit;
|
|
222
|
+
}
|
|
216
223
|
}
|
|
217
224
|
|
|
218
225
|
tbody td {
|
|
@@ -77,6 +77,8 @@ export type StkTableColumn<T extends Record<string, any>> = {
|
|
|
77
77
|
sortField?: keyof T;
|
|
78
78
|
/** 排序方式。按数字/字符串 */
|
|
79
79
|
sortType?: 'number' | 'string';
|
|
80
|
+
/** 配置当前列的排序规则 */
|
|
81
|
+
sortConfig?: Pick<SortConfig<T>, 'emptyToBottom' | 'stringLocaleCompare'>;
|
|
80
82
|
/** 固定列 */
|
|
81
83
|
fixed?: 'left' | 'right' | null;
|
|
82
84
|
/** private */ rowSpan?: number;
|
|
@@ -198,6 +200,7 @@ export type SeqConfig = {
|
|
|
198
200
|
|
|
199
201
|
/** Configuration options for the expand column */
|
|
200
202
|
export type ExpandConfig = {
|
|
203
|
+
/** worked in virtual mode */
|
|
201
204
|
height?: number;
|
|
202
205
|
};
|
|
203
206
|
|
|
@@ -230,3 +233,7 @@ export type AutoRowHeightConfig<DT> = {
|
|
|
230
233
|
/** Estimated row height */
|
|
231
234
|
expectedHeight?: number | ((row: DT, index: number) => number);
|
|
232
235
|
};
|
|
236
|
+
|
|
237
|
+
export type ColResizableConfig<DT extends Record<string, any>> = {
|
|
238
|
+
disabled: (col: StkTableColumn<DT>) => boolean;
|
|
239
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComputedRef, Ref, ShallowRef, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
1
|
+
import { ComputedRef, Ref, ShallowRef, computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
2
2
|
import { StkTableColumn, UniqKey } from './types';
|
|
3
3
|
import { getCalculatedColWidth } from './utils/constRefUtils';
|
|
4
4
|
|
|
@@ -47,6 +47,14 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
47
47
|
revertMoveX: false,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
/** 是否可拖动 */
|
|
51
|
+
const colResizeOn = computed(() => {
|
|
52
|
+
if (Object.prototype.toString.call(props.colResizable) === '[object Object]') {
|
|
53
|
+
return (col: StkTableColumn<DT>) => !props.colResizable.disabled(col);
|
|
54
|
+
}
|
|
55
|
+
return () => props.colResizable;
|
|
56
|
+
});
|
|
57
|
+
|
|
50
58
|
onMounted(() => {
|
|
51
59
|
initColResizeEvent();
|
|
52
60
|
});
|
|
@@ -93,7 +101,7 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
93
101
|
// 对于固定右侧的列,拖动左侧的把,需要反向计算
|
|
94
102
|
revertMoveX = true;
|
|
95
103
|
} else {
|
|
96
|
-
//
|
|
104
|
+
// 取上一列
|
|
97
105
|
if (colIndex - 1 >= 0) {
|
|
98
106
|
col = tableHeaderLastValue[colIndex - 1];
|
|
99
107
|
}
|
|
@@ -139,8 +147,9 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
139
147
|
const { clientX } = e;
|
|
140
148
|
let moveX = clientX - startX;
|
|
141
149
|
const currentColWidth = getCalculatedColWidth(lastCol);
|
|
150
|
+
const minWidth = lastCol?.minWidth ?? props.colMinWidth;
|
|
142
151
|
// 移动量不小于最小列宽
|
|
143
|
-
if (currentColWidth + moveX <
|
|
152
|
+
if (currentColWidth + moveX < minWidth) {
|
|
144
153
|
moveX = -currentColWidth;
|
|
145
154
|
}
|
|
146
155
|
|
|
@@ -197,6 +206,7 @@ export function useColResize<DT extends Record<string, any>>({
|
|
|
197
206
|
}
|
|
198
207
|
|
|
199
208
|
return {
|
|
209
|
+
colResizeOn,
|
|
200
210
|
isColResizing,
|
|
201
211
|
onThResizeMouseDown,
|
|
202
212
|
};
|
|
@@ -8,7 +8,7 @@ type Params<T extends Record<string, any>> = {
|
|
|
8
8
|
colKeyGen: ComputedRef<(col: StkTableColumn<T>) => UniqKey>;
|
|
9
9
|
getFixedColPosition: ComputedRef<(col: StkTableColumn<T>) => number>;
|
|
10
10
|
tableHeaders: ShallowRef<StkTableColumn<T>[][]>;
|
|
11
|
-
|
|
11
|
+
tableHeadersForCalc: ShallowRef<StkTableColumn<T>[][]>;
|
|
12
12
|
tableContainerRef: Ref<HTMLDivElement | undefined>;
|
|
13
13
|
};
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ export function useFixedCol<DT extends Record<string, any>>({
|
|
|
21
21
|
colKeyGen,
|
|
22
22
|
getFixedColPosition,
|
|
23
23
|
tableHeaders,
|
|
24
|
-
|
|
24
|
+
tableHeadersForCalc,
|
|
25
25
|
tableContainerRef,
|
|
26
26
|
}: Params<DT>) {
|
|
27
27
|
/** 保存需要出现阴影的列 */
|
|
@@ -56,27 +56,26 @@ export function useFixedCol<DT extends Record<string, any>>({
|
|
|
56
56
|
* @param type 1-shadow(阴影) 2-active(被固定的列)
|
|
57
57
|
*
|
|
58
58
|
*/
|
|
59
|
-
function getColAndParentCols(col: StkTableColumn<DT> | null, type: 1 | 2 = 1) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
59
|
+
// function getColAndParentCols(col: StkTableColumn<DT> | null, type: 1 | 2 = 1) {
|
|
60
|
+
// if (!col) return [];
|
|
61
|
+
// const colsTemp: StkTableColumn<DT>[] = [];
|
|
62
|
+
// let node: any = { __PARENT__: col };
|
|
63
|
+
// while ((node = node.__PARENT__)) {
|
|
64
|
+
// if (type === 1 && node.fixed) {
|
|
65
|
+
// // shadow
|
|
66
|
+
// colsTemp.push(node);
|
|
67
|
+
// }
|
|
68
|
+
// if (type === 2) {
|
|
69
|
+
// // active
|
|
70
|
+
// colsTemp.push(node);
|
|
71
|
+
// }
|
|
72
|
+
// }
|
|
73
|
+
// return colsTemp;
|
|
74
|
+
// }
|
|
75
75
|
|
|
76
76
|
/** 滚动条变化时,更新需要展示阴影的列 */
|
|
77
77
|
function updateFixedShadow(virtualScrollX?: Ref<VirtualScrollXStore>) {
|
|
78
78
|
const fixedColsTemp: StkTableColumn<DT>[] = [];
|
|
79
|
-
const fixedShadowColsTemp: (StkTableColumn<DT> | null)[] = [];
|
|
80
79
|
let clientWidth, /* scrollWidth, */ scrollLeft;
|
|
81
80
|
|
|
82
81
|
if (virtualScrollX?.value) {
|
|
@@ -95,31 +94,38 @@ export function useFixedCol<DT extends Record<string, any>>({
|
|
|
95
94
|
* 根据横向滚动位置,计算出哪个列需要展示阴影
|
|
96
95
|
*****/
|
|
97
96
|
/** 左侧需要展示阴影的列 */
|
|
98
|
-
|
|
97
|
+
const leftShadowCol: StkTableColumn<DT>[] = [];
|
|
99
98
|
/** 右侧展示阴影的列 */
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
99
|
+
const rightShadowCol: StkTableColumn<DT>[] = [];
|
|
100
|
+
tableHeadersForCalc.value.forEach((row, level) => {
|
|
101
|
+
/**
|
|
102
|
+
* 左侧第n个fixed:left 计算要加上前面所有left 的列宽。
|
|
103
|
+
*/
|
|
104
|
+
let left = 0;
|
|
105
|
+
row.forEach(col => {
|
|
106
|
+
const position = getFixedColPosition.value(col);
|
|
107
|
+
const isFixedLeft = col.fixed === 'left';
|
|
108
|
+
const isFixedRight = col.fixed === 'right';
|
|
109
|
+
|
|
110
|
+
if (isFixedLeft && position + scrollLeft > left) {
|
|
111
|
+
fixedColsTemp.push(col);
|
|
112
|
+
leftShadowCol[level] = col;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
left += getCalculatedColWidth(col);
|
|
116
|
+
|
|
117
|
+
if (isFixedRight && scrollLeft + clientWidth - left < position) {
|
|
118
|
+
fixedColsTemp.push(col);
|
|
119
|
+
// 右固定列阴影,只要第一列
|
|
120
|
+
if (!rightShadowCol[level]) {
|
|
121
|
+
rightShadowCol[level] = col;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
118
125
|
});
|
|
119
126
|
|
|
120
127
|
if (props.fixedColShadow) {
|
|
121
|
-
|
|
122
|
-
fixedShadowCols.value = (fixedShadowColsTemp as (StkTableColumn<DT> | null)[]).filter(Boolean) as StkTableColumn<DT>[];
|
|
128
|
+
fixedShadowCols.value = [...leftShadowCol, ...rightShadowCol].filter(Boolean) as StkTableColumn<DT>[];
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
fixedCols.value = fixedColsTemp;
|
|
@@ -89,7 +89,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
89
89
|
const { ts, duration } = store;
|
|
90
90
|
const timeOffset = nowTs - ts;
|
|
91
91
|
if (nowTs - ts < duration) {
|
|
92
|
-
|
|
92
|
+
updateRowAnimation(rowKeyValue, store, timeOffset);
|
|
93
93
|
} else {
|
|
94
94
|
highlightDimRowsAnimation.delete(rowKeyValue);
|
|
95
95
|
}
|
|
@@ -197,7 +197,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
197
197
|
const rowKeyValue = rowKeyValues[i];
|
|
198
198
|
const store: HighlightDimRowStore = { ts: nowTs, visible: false, keyframe, duration };
|
|
199
199
|
highlightDimRowsAnimation.set(rowKeyValue, store);
|
|
200
|
-
|
|
200
|
+
updateRowAnimation(rowKeyValue, store, 0);
|
|
201
201
|
}
|
|
202
202
|
calcRowHighlightLoop();
|
|
203
203
|
} else {
|
|
@@ -279,7 +279,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
279
279
|
* @param store highlightDimRowStore 的引用对象
|
|
280
280
|
* @param timeOffset 距动画开始经过的时长
|
|
281
281
|
*/
|
|
282
|
-
function
|
|
282
|
+
function updateRowAnimation(rowKeyValue: UniqKey, store: HighlightDimRowStore, timeOffset: number) {
|
|
283
283
|
const rowEl = document.getElementById(stkTableId + '-' + String(rowKeyValue));
|
|
284
284
|
const { visible, keyframe, duration: initialDuration } = store;
|
|
285
285
|
if (!rowEl) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computed, ComputedRef } from 'vue';
|
|
2
2
|
import { StkTableColumn, UniqKey } from './types';
|
|
3
|
+
import { isEmptyValue } from './utils';
|
|
3
4
|
|
|
4
5
|
type Params<T extends Record<string, any>> = {
|
|
5
6
|
props: any;
|
|
@@ -57,7 +58,6 @@ export function useThDrag<DT extends Record<string, any>>({ props, emits, colKey
|
|
|
57
58
|
const th = findParentTH(e);
|
|
58
59
|
if (!th) return;
|
|
59
60
|
const dragStartKey = e.dataTransfer?.getData('text');
|
|
60
|
-
|
|
61
61
|
if (dragStartKey !== th.dataset.colKey) {
|
|
62
62
|
handleColOrderChange(dragStartKey, th.dataset.colKey);
|
|
63
63
|
}
|
|
@@ -66,7 +66,7 @@ export function useThDrag<DT extends Record<string, any>>({ props, emits, colKey
|
|
|
66
66
|
|
|
67
67
|
/** 列拖动交换顺序 */
|
|
68
68
|
function handleColOrderChange(dragStartKey: string | undefined, dragEndKey: string | undefined) {
|
|
69
|
-
if (
|
|
69
|
+
if (isEmptyValue(dragStartKey) || isEmptyValue(dragEndKey)) return;
|
|
70
70
|
|
|
71
71
|
if (dragConfig.value.mode !== 'none') {
|
|
72
72
|
const columns = [...props.columns];
|
|
@@ -79,6 +79,8 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
79
79
|
scrollHeight: 0,
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
// TODO: init pageSize
|
|
83
|
+
|
|
82
84
|
const virtualScrollX = ref<VirtualScrollXStore>({
|
|
83
85
|
containerWidth: 0,
|
|
84
86
|
scrollWidth: 0,
|
|
@@ -88,6 +90,10 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
88
90
|
scrollLeft: 0,
|
|
89
91
|
});
|
|
90
92
|
|
|
93
|
+
const hasExpandCol = computed(() => {
|
|
94
|
+
return tableHeaderLast.value.some(col => col.type === 'expand');
|
|
95
|
+
});
|
|
96
|
+
|
|
91
97
|
/** 是否虚拟滚动标志 */
|
|
92
98
|
const virtual_on = computed(() => {
|
|
93
99
|
return props.virtual && dataSourceCopy.value.length > virtualScroll.value.pageSize * 2;
|
|
@@ -198,10 +204,9 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
198
204
|
const headerToBodyRowHeightCount = Math.floor(headerHeight / rowHeight);
|
|
199
205
|
pageSize -= headerToBodyRowHeightCount; //减去表头行数
|
|
200
206
|
}
|
|
201
|
-
/** 最大的scrollTop */
|
|
202
207
|
const maxScrollTop = dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight;
|
|
203
208
|
if (scrollTop > maxScrollTop) {
|
|
204
|
-
/**
|
|
209
|
+
/** fix: 滚动条不在顶部时,表格数据变少,导致滚动条位置有误 */
|
|
205
210
|
scrollTop = maxScrollTop;
|
|
206
211
|
}
|
|
207
212
|
Object.assign(virtualScroll.value, { containerHeight, pageSize, scrollHeight });
|
|
@@ -237,7 +242,8 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
237
242
|
let expectedHeight;
|
|
238
243
|
if (storedHeight) {
|
|
239
244
|
return storedHeight;
|
|
240
|
-
}
|
|
245
|
+
}
|
|
246
|
+
if ((expectedHeight = props.autoRowHeight?.expectedHeight)) {
|
|
241
247
|
if (typeof expectedHeight === 'function') {
|
|
242
248
|
return expectedHeight(row);
|
|
243
249
|
} else {
|
|
@@ -247,6 +253,18 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
247
253
|
return props.rowHeight || DEFAULT_ROW_HEIGHT;
|
|
248
254
|
};
|
|
249
255
|
|
|
256
|
+
const createGetRowHeightFn: () => (row: DT) => number = () => {
|
|
257
|
+
if (props.autoRowHeight) {
|
|
258
|
+
return (row: DT) => getAutoRowHeight(row);
|
|
259
|
+
}
|
|
260
|
+
if (hasExpandCol.value) {
|
|
261
|
+
const { rowHeight } = virtualScroll.value;
|
|
262
|
+
const expandedRowHeight: number = props.expandConfig?.height || rowHeight;
|
|
263
|
+
return (row: DT) => (row.__EXPANDED_ROW__ ? expandedRowHeight : rowHeight);
|
|
264
|
+
}
|
|
265
|
+
return () => props.rowHeight || (DEFAULT_ROW_HEIGHT as number);
|
|
266
|
+
};
|
|
267
|
+
|
|
250
268
|
/** 通过滚动条位置,计算虚拟滚动的参数 */
|
|
251
269
|
function updateVirtualScrollY(sTop = 0) {
|
|
252
270
|
const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex } = virtualScroll.value;
|
|
@@ -257,24 +275,32 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
257
275
|
if (!virtual_on.value) {
|
|
258
276
|
return;
|
|
259
277
|
}
|
|
278
|
+
|
|
279
|
+
const dataSourceCopyTemp = dataSourceCopy.value;
|
|
260
280
|
const { autoRowHeight, stripe, optimizeVue2Scroll } = props;
|
|
261
281
|
|
|
262
282
|
let startIndex = 0;
|
|
263
|
-
|
|
264
283
|
let autoRowHeightTop = 0;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
284
|
+
let getRowHeight: ReturnType<typeof createGetRowHeightFn> | null = null;
|
|
285
|
+
const dataLength = dataSourceCopyTemp.length;
|
|
286
|
+
|
|
287
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
288
|
+
if (autoRowHeight) {
|
|
289
|
+
trRef.value?.forEach(tr => {
|
|
290
|
+
const { rowKey } = tr.dataset;
|
|
291
|
+
if (!rowKey || autoRowHeightMap.has(rowKey)) return;
|
|
292
|
+
autoRowHeightMap.set(rowKey, tr.offsetHeight);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
getRowHeight = createGetRowHeightFn();
|
|
297
|
+
|
|
298
|
+
for (let i = 0; i < dataLength; i++) {
|
|
299
|
+
const height = getRowHeight(dataSourceCopyTemp[i]);
|
|
274
300
|
autoRowHeightTop += height;
|
|
275
301
|
if (autoRowHeightTop >= sTop) {
|
|
276
302
|
startIndex = i;
|
|
277
|
-
autoRowHeightTop
|
|
303
|
+
autoRowHeightTop -= height;
|
|
278
304
|
break;
|
|
279
305
|
}
|
|
280
306
|
}
|
|
@@ -287,8 +313,8 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
287
313
|
if (stripe && startIndex > 0 && startIndex % 2) {
|
|
288
314
|
// 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
|
|
289
315
|
startIndex -= 1; // 奇数-1变成偶数
|
|
290
|
-
if (autoRowHeight) {
|
|
291
|
-
const height =
|
|
316
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
317
|
+
const height = getRowHeight!(dataSourceCopyTemp[startIndex]);
|
|
292
318
|
autoRowHeightTop -= height;
|
|
293
319
|
}
|
|
294
320
|
}
|
|
@@ -296,7 +322,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
296
322
|
startIndex = Math.max(0, startIndex);
|
|
297
323
|
|
|
298
324
|
// 溢出修正
|
|
299
|
-
endIndex = Math.min(endIndex,
|
|
325
|
+
endIndex = Math.min(endIndex, dataLength);
|
|
300
326
|
|
|
301
327
|
if (startIndex >= endIndex) {
|
|
302
328
|
// 兜底,不一定会执行到这里
|
|
@@ -308,7 +334,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
|
|
|
308
334
|
}
|
|
309
335
|
|
|
310
336
|
let offsetTop = 0;
|
|
311
|
-
if (autoRowHeight) {
|
|
337
|
+
if (autoRowHeight || hasExpandCol.value) {
|
|
312
338
|
offsetTop = autoRowHeightTop;
|
|
313
339
|
} else {
|
|
314
340
|
if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Order, SortConfig, SortOption, SortState, StkTableColumn } from '../types';
|
|
2
2
|
|
|
3
3
|
/** 是否空值 */
|
|
4
|
-
function isEmptyValue(val: any, isNumber?: boolean) {
|
|
5
|
-
let isEmpty = val === null || val ===
|
|
4
|
+
export function isEmptyValue(val: any, isNumber?: boolean) {
|
|
5
|
+
let isEmpty = val === null || val === void 0;
|
|
6
6
|
if (isNumber) {
|
|
7
7
|
isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
|
|
8
8
|
}
|
|
@@ -180,12 +180,11 @@ export function howDeepTheHeader(arr: StkTableColumn<any>[], level = 0) {
|
|
|
180
180
|
return Math.max(...levels);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
/** number
|
|
183
|
+
/** number width +px */
|
|
184
184
|
export function transformWidthToStr(width?: string | number) {
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return width;
|
|
185
|
+
if (width === void 0) return;
|
|
186
|
+
const numberWidth = Number(width);
|
|
187
|
+
return width + (!Number.isNaN(numberWidth) ? 'px' : '');
|
|
189
188
|
}
|
|
190
189
|
|
|
191
190
|
export function getBrowsersVersion(browserName: string) {
|