stk-table-vue 0.4.5 → 0.4.8

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 CHANGED
@@ -13,6 +13,7 @@ repo:
13
13
  ## Bug TODO:
14
14
  * [x] props.dataSource 为 shallowRef 时,高亮行不生效。(bug:2024.02.21)(resolved:0.2.3)
15
15
  * [] 固定列列宽拖动目标。
16
+ * [] 惯性滚动优化。
16
17
 
17
18
  ## Feature TODO:
18
19
  * [x] 高亮行,单元格。
@@ -587,6 +588,8 @@ export type SortConfig<T extends Record<string, any>> = {
587
588
  - 以下情况尝试开启此功能。
588
589
  - 在 `customCell` 较多且复杂时。
589
590
  - 大量 highlight 动画时。
591
+ ### props.autoResize
592
+ * 手动设置为 `props.autoResize=false`。可取消监听的性能消耗。适用于宽度高度不变的表格。
590
593
 
591
594
  ## Tips
592
595
  ### props.fixedMode
@@ -1,7 +1,7 @@
1
1
  import { HighlightConfig, Order, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, UniqKeyProp } from './types/index';
2
2
 
3
3
  /** Generic stands for DataType */
4
- type DT = Record<string | number, any>;
4
+ type DT = any;
5
5
  /**
6
6
  * 选中一行,
7
7
  * @param {string} rowKey selected rowKey, null to unselect
@@ -24,7 +24,7 @@ declare function setSorter(dataIndex: string, order: Order, option?: {
24
24
  force?: boolean;
25
25
  silent?: boolean;
26
26
  sort?: boolean;
27
- }): DT[];
27
+ }): any[];
28
28
  /** 重置排序 */
29
29
  declare function resetSorter(): void;
30
30
  /**
@@ -34,7 +34,7 @@ declare function resetSorter(): void;
34
34
  */
35
35
  declare function scrollTo(top?: number | null, left?: number | null): void;
36
36
  /** 获取当前状态的表格数据 */
37
- declare function getTableData(): DT[];
37
+ declare function getTableData(): any[];
38
38
  /** 获取当前排序列的信息 */
39
39
  declare function getSortColumns(): Partial<SortState<DT>>[];
40
40
  declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
@@ -66,17 +66,17 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
66
66
  /** x轴虚拟滚动(必须设置列宽)*/
67
67
  virtualX?: boolean | undefined;
68
68
  /** 表格列配置 */
69
- columns?: StkTableColumn<DT>[] | undefined;
69
+ columns?: StkTableColumn<any>[] | undefined;
70
70
  /** 表格数据源 */
71
- dataSource?: DT[] | undefined;
71
+ dataSource?: any[] | undefined;
72
72
  /** 行唯一键 (行唯一值不能为undefined) */
73
73
  rowKey?: UniqKeyProp | undefined;
74
74
  /** 列唯一键 */
75
75
  colKey?: UniqKeyProp | undefined;
76
76
  /** 空值展示文字 */
77
77
  emptyCellText?: string | ((option: {
78
- row: DT;
79
- col: StkTableColumn<DT>;
78
+ row: any;
79
+ col: StkTableColumn<any>;
80
80
  }) => string) | undefined;
81
81
  /** 暂无数据兜底高度是否撑满 */
82
82
  noDataFull?: boolean | undefined;
@@ -93,12 +93,12 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
93
93
  /** 是否高亮鼠标悬浮的单元格 */
94
94
  cellHover?: boolean | undefined;
95
95
  /** 表头是否可拖动。支持回调函数。 */
96
- headerDrag?: boolean | ((col: StkTableColumn<DT>) => boolean) | undefined;
96
+ headerDrag?: boolean | ((col: StkTableColumn<any>) => boolean) | undefined;
97
97
  /**
98
98
  * 给行附加className<br>
99
99
  * FIXME: 是否需要优化,因为不传此prop会使表格行一直执行空函数,是否有影响
100
100
  */
101
- rowClassName?: ((row: DT, i: number) => string) | undefined;
101
+ rowClassName?: ((row: any, i: number) => string) | undefined;
102
102
  /**
103
103
  * 列宽是否可拖动<br>
104
104
  * **不要设置**列minWidth,**必须**设置width<br>
@@ -126,7 +126,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
126
126
  /** 优化vue2 滚动 */
127
127
  optimizeVue2Scroll?: boolean | undefined;
128
128
  /** 排序配置 */
129
- sortConfig?: SortConfig<DT> | undefined;
129
+ sortConfig?: SortConfig<any> | undefined;
130
130
  /** 隐藏头部title。可传入dataIndex数组 */
131
131
  hideHeaderTitle?: boolean | string[] | undefined;
132
132
  /** 高亮配置 */
@@ -197,7 +197,10 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
197
197
  setCurrentRow: typeof setCurrentRow;
198
198
  /** 设置高亮渐暗单元格 */
199
199
  setHighlightDimCell: (rowKeyValue: string, dataIndex: string, option?: {
200
- className?: string | undefined;
200
+ className?: string | undefined; /**
201
+ * 排序变更触发
202
+ * ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
203
+ */
201
204
  method?: "animation" | "css" | undefined;
202
205
  keyframe?: Keyframe[] | PropertyIndexedKeyframes | null | undefined;
203
206
  duration?: number | undefined;
@@ -211,7 +214,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
211
214
  duration?: number | undefined;
212
215
  }) => void;
213
216
  /** 表格排序列dataIndex */
214
- sortCol: import('vue').Ref<string | number | undefined>;
217
+ sortCol: import('vue').Ref<string | number | symbol | undefined>;
215
218
  /** 获取当前排序状态 */
216
219
  getSortColumns: typeof getSortColumns;
217
220
  /** 设置排序 */
@@ -223,19 +226,19 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
223
226
  /** 获取表格数据 */
224
227
  getTableData: typeof getTableData;
225
228
  }, unknown, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
226
- "sort-change": (col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig<DT>) => void;
227
- "row-click": (ev: MouseEvent, row: DT) => void;
228
- "current-change": (ev: MouseEvent | null, row: DT | undefined, data: {
229
+ "sort-change": (col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig<any>) => void;
230
+ "row-click": (ev: MouseEvent, row: any) => void;
231
+ "current-change": (ev: MouseEvent | null, row: any, data: {
229
232
  select: boolean;
230
233
  }) => void;
231
- "row-dblclick": (ev: MouseEvent, row: DT) => void;
234
+ "row-dblclick": (ev: MouseEvent, row: any) => void;
232
235
  "header-row-menu": (ev: MouseEvent) => void;
233
- "row-menu": (ev: MouseEvent, row: DT) => void;
234
- "cell-click": (ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => void;
235
- "cell-mouseenter": (ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => void;
236
- "cell-mouseleave": (ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => void;
237
- "cell-mouseover": (ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => void;
238
- "header-cell-click": (ev: MouseEvent, col: StkTableColumn<DT>) => void;
236
+ "row-menu": (ev: MouseEvent, row: any) => void;
237
+ "cell-click": (ev: MouseEvent, row: any, col: StkTableColumn<any>) => void;
238
+ "cell-mouseenter": (ev: MouseEvent, row: any, col: StkTableColumn<any>) => void;
239
+ "cell-mouseleave": (ev: MouseEvent, row: any, col: StkTableColumn<any>) => void;
240
+ "cell-mouseover": (ev: MouseEvent, row: any, col: StkTableColumn<any>) => void;
241
+ "header-cell-click": (ev: MouseEvent, col: StkTableColumn<any>) => void;
239
242
  scroll: (ev: Event, data: {
240
243
  startIndex: number;
241
244
  endIndex: number;
@@ -244,7 +247,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
244
247
  "col-order-change": (dragStartKey: string, targetColKey: string) => void;
245
248
  "th-drag-start": (dragStartKey: string) => void;
246
249
  "th-drop": (targetColKey: string) => void;
247
- "update:columns": (cols: StkTableColumn<DT>[]) => void;
250
+ "update:columns": (cols: StkTableColumn<any>[]) => void;
248
251
  }, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
249
252
  width?: string | undefined;
250
253
  /** 最小表格宽度 */
@@ -274,17 +277,17 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
274
277
  /** x轴虚拟滚动(必须设置列宽)*/
275
278
  virtualX?: boolean | undefined;
276
279
  /** 表格列配置 */
277
- columns?: StkTableColumn<DT>[] | undefined;
280
+ columns?: StkTableColumn<any>[] | undefined;
278
281
  /** 表格数据源 */
279
- dataSource?: DT[] | undefined;
282
+ dataSource?: any[] | undefined;
280
283
  /** 行唯一键 (行唯一值不能为undefined) */
281
284
  rowKey?: UniqKeyProp | undefined;
282
285
  /** 列唯一键 */
283
286
  colKey?: UniqKeyProp | undefined;
284
287
  /** 空值展示文字 */
285
288
  emptyCellText?: string | ((option: {
286
- row: DT;
287
- col: StkTableColumn<DT>;
289
+ row: any;
290
+ col: StkTableColumn<any>;
288
291
  }) => string) | undefined;
289
292
  /** 暂无数据兜底高度是否撑满 */
290
293
  noDataFull?: boolean | undefined;
@@ -301,12 +304,12 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
301
304
  /** 是否高亮鼠标悬浮的单元格 */
302
305
  cellHover?: boolean | undefined;
303
306
  /** 表头是否可拖动。支持回调函数。 */
304
- headerDrag?: boolean | ((col: StkTableColumn<DT>) => boolean) | undefined;
307
+ headerDrag?: boolean | ((col: StkTableColumn<any>) => boolean) | undefined;
305
308
  /**
306
309
  * 给行附加className<br>
307
310
  * FIXME: 是否需要优化,因为不传此prop会使表格行一直执行空函数,是否有影响
308
311
  */
309
- rowClassName?: ((row: DT, i: number) => string) | undefined;
312
+ rowClassName?: ((row: any, i: number) => string) | undefined;
310
313
  /**
311
314
  * 列宽是否可拖动<br>
312
315
  * **不要设置**列minWidth,**必须**设置width<br>
@@ -334,7 +337,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
334
337
  /** 优化vue2 滚动 */
335
338
  optimizeVue2Scroll?: boolean | undefined;
336
339
  /** 排序配置 */
337
- sortConfig?: SortConfig<DT> | undefined;
340
+ sortConfig?: SortConfig<any> | undefined;
338
341
  /** 隐藏头部title。可传入dataIndex数组 */
339
342
  hideHeaderTitle?: boolean | string[] | undefined;
340
343
  /** 高亮配置 */
@@ -399,23 +402,23 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
399
402
  startIndex: number;
400
403
  endIndex: number;
401
404
  }) => any) | undefined;
402
- "onUpdate:columns"?: ((cols: StkTableColumn<DT>[]) => any) | undefined;
405
+ "onUpdate:columns"?: ((cols: StkTableColumn<any>[]) => any) | undefined;
403
406
  "onTh-drag-start"?: ((dragStartKey: string) => any) | undefined;
404
407
  "onCol-order-change"?: ((dragStartKey: string, targetColKey: string) => any) | undefined;
405
408
  "onTh-drop"?: ((targetColKey: string) => any) | undefined;
406
- "onSort-change"?: ((col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig<DT>) => any) | undefined;
407
- "onRow-click"?: ((ev: MouseEvent, row: DT) => any) | undefined;
408
- "onCurrent-change"?: ((ev: MouseEvent | null, row: DT | undefined, data: {
409
+ "onSort-change"?: ((col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig<any>) => any) | undefined;
410
+ "onRow-click"?: ((ev: MouseEvent, row: any) => any) | undefined;
411
+ "onCurrent-change"?: ((ev: MouseEvent | null, row: any, data: {
409
412
  select: boolean;
410
413
  }) => any) | undefined;
411
- "onRow-dblclick"?: ((ev: MouseEvent, row: DT) => any) | undefined;
414
+ "onRow-dblclick"?: ((ev: MouseEvent, row: any) => any) | undefined;
412
415
  "onHeader-row-menu"?: ((ev: MouseEvent) => any) | undefined;
413
- "onRow-menu"?: ((ev: MouseEvent, row: DT) => any) | undefined;
414
- "onCell-click"?: ((ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => any) | undefined;
415
- "onCell-mouseenter"?: ((ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => any) | undefined;
416
- "onCell-mouseleave"?: ((ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => any) | undefined;
417
- "onCell-mouseover"?: ((ev: MouseEvent, row: DT, col: StkTableColumn<DT>) => any) | undefined;
418
- "onHeader-cell-click"?: ((ev: MouseEvent, col: StkTableColumn<DT>) => any) | undefined;
416
+ "onRow-menu"?: ((ev: MouseEvent, row: any) => any) | undefined;
417
+ "onCell-click"?: ((ev: MouseEvent, row: any, col: StkTableColumn<any>) => any) | undefined;
418
+ "onCell-mouseenter"?: ((ev: MouseEvent, row: any, col: StkTableColumn<any>) => any) | undefined;
419
+ "onCell-mouseleave"?: ((ev: MouseEvent, row: any, col: StkTableColumn<any>) => any) | undefined;
420
+ "onCell-mouseover"?: ((ev: MouseEvent, row: any, col: StkTableColumn<any>) => any) | undefined;
421
+ "onHeader-cell-click"?: ((ev: MouseEvent, col: StkTableColumn<any>) => any) | undefined;
419
422
  "onScroll-x"?: ((ev: Event) => any) | undefined;
420
423
  }, {
421
424
  width: string;
@@ -432,13 +435,13 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
432
435
  rowCurrentRevokable: boolean;
433
436
  virtual: boolean;
434
437
  virtualX: boolean;
435
- columns: StkTableColumn<DT>[];
436
- dataSource: DT[];
438
+ columns: StkTableColumn<any>[];
439
+ dataSource: any[];
437
440
  rowKey: UniqKeyProp;
438
441
  colKey: UniqKeyProp;
439
442
  emptyCellText: string | ((option: {
440
- row: DT;
441
- col: StkTableColumn<DT>;
443
+ row: any;
444
+ col: StkTableColumn<any>;
442
445
  }) => string);
443
446
  noDataFull: boolean;
444
447
  showNoData: boolean;
@@ -447,22 +450,22 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<__
447
450
  showOverflow: boolean;
448
451
  showTrHoverClass: boolean;
449
452
  cellHover: boolean;
450
- headerDrag: boolean | ((col: StkTableColumn<DT>) => boolean);
451
- rowClassName: (row: DT, i: number) => string;
453
+ headerDrag: boolean | ((col: StkTableColumn<any>) => boolean);
454
+ rowClassName: (row: any, i: number) => string;
452
455
  colResizable: boolean;
453
456
  colMinWidth: number;
454
457
  bordered: boolean | "h" | "v" | "body-v";
455
458
  autoResize: boolean | (() => void);
456
459
  fixedColShadow: boolean;
457
460
  optimizeVue2Scroll: boolean;
458
- sortConfig: SortConfig<DT>;
461
+ sortConfig: SortConfig<any>;
459
462
  hideHeaderTitle: boolean | string[];
460
463
  highlightConfig: HighlightConfig;
461
464
  seqConfig: SeqConfig;
462
465
  cellFixedMode: "sticky" | "relative";
463
466
  }, {}>, {
464
467
  tableHeader?(_: {
465
- col: StkTableColumn<DT>;
468
+ col: StkTableColumn<any>;
466
469
  }): any;
467
470
  empty?(_: {}): any;
468
471
  }>;
@@ -455,8 +455,6 @@ function useFixedStyle({
455
455
  if (tagType === TagType.TD && !fixed)
456
456
  return null;
457
457
  const style = {};
458
- const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
459
- const scrollRight = scrollWidth - containerWidth - scrollLeft;
460
458
  const isFixedLeft = fixed === "left";
461
459
  if (tagType === TagType.TH) {
462
460
  if (isRelativeMode.value) {
@@ -465,6 +463,8 @@ function useFixedStyle({
465
463
  style.top = depth * props.rowHeight + "px";
466
464
  }
467
465
  }
466
+ const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
467
+ const scrollRight = scrollWidth - containerWidth - scrollLeft;
468
468
  if (fixed === "left" || fixed === "right") {
469
469
  if (isRelativeMode.value) {
470
470
  if (isFixedLeft) {
@@ -740,7 +740,16 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
740
740
  setHighlightDimCell
741
741
  };
742
742
  }
743
- const SCROLL_CODES = ["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft", "PageUp", "PageDown"];
743
+ var ScrollCodes = /* @__PURE__ */ ((ScrollCodes2) => {
744
+ ScrollCodes2["ArrowUp"] = "ArrowUp";
745
+ ScrollCodes2["ArrowRight"] = "ArrowRight";
746
+ ScrollCodes2["ArrowDown"] = "ArrowDown";
747
+ ScrollCodes2["ArrowLeft"] = "ArrowLeft";
748
+ ScrollCodes2["PageUp"] = "PageUp";
749
+ ScrollCodes2["PageDown"] = "PageDown";
750
+ return ScrollCodes2;
751
+ })(ScrollCodes || {});
752
+ const ScrollCodesValues = Object.values(ScrollCodes);
744
753
  function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on }) {
745
754
  let isMouseOver = false;
746
755
  watch(virtual_on, (val) => {
@@ -767,7 +776,9 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
767
776
  (_c = targetElement.value) == null ? void 0 : _c.removeEventListener("mousedown", handleMouseDown);
768
777
  }
769
778
  function handleKeydown(e) {
770
- if (!SCROLL_CODES.includes(e.code))
779
+ if (!virtual_on.value)
780
+ return;
781
+ if (!ScrollCodesValues.includes(e.code))
771
782
  return;
772
783
  if (!isMouseOver)
773
784
  return;
@@ -777,17 +788,17 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
777
788
  const { headless, headerRowHeight } = props;
778
789
  const headerHeight = headless ? 0 : tableHeaders.value.length * (headerRowHeight || rowHeight);
779
790
  const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
780
- if (e.code === SCROLL_CODES[0]) {
791
+ if (e.code === "ArrowUp") {
781
792
  scrollTo(scrollTop - rowHeight, null);
782
- } else if (e.code === SCROLL_CODES[1]) {
793
+ } else if (e.code === "ArrowRight") {
783
794
  scrollTo(null, scrollLeft + rowHeight);
784
- } else if (e.code === SCROLL_CODES[2]) {
795
+ } else if (e.code === "ArrowDown") {
785
796
  scrollTo(scrollTop + rowHeight, null);
786
- } else if (e.code === SCROLL_CODES[3]) {
797
+ } else if (e.code === "ArrowLeft") {
787
798
  scrollTo(null, scrollLeft - rowHeight);
788
- } else if (e.code === SCROLL_CODES[4]) {
799
+ } else if (e.code === "PageUp") {
789
800
  scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
790
- } else if (e.code === SCROLL_CODES[5]) {
801
+ } else if (e.code === "PageDown") {
791
802
  scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
792
803
  }
793
804
  }
@@ -937,6 +948,10 @@ function useVirtualScroll({
937
948
  const { headerRowHeight } = props;
938
949
  return headerRowHeight * tableHeaders.value.length;
939
950
  }
951
+ function initVirtualScroll(height) {
952
+ initVirtualScrollY(height);
953
+ initVirtualScrollX();
954
+ }
940
955
  function initVirtualScrollY(height) {
941
956
  var _a;
942
957
  if (height !== void 0 && typeof height !== "number") {
@@ -954,7 +969,7 @@ function useVirtualScroll({
954
969
  const headerHeight = getTableHeaderHeight();
955
970
  tableHeaderHeight.value = headerHeight;
956
971
  if (!headless) {
957
- const headerToBodyRowHeightCount = Math.floor(headerHeight) / rowHeight;
972
+ const headerToBodyRowHeightCount = Math.floor(headerHeight / rowHeight);
958
973
  pageSize -= headerToBodyRowHeightCount;
959
974
  }
960
975
  const maxScrollTop = dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight;
@@ -970,31 +985,21 @@ function useVirtualScroll({
970
985
  virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
971
986
  updateVirtualScrollX(scrollLeft);
972
987
  }
973
- function initVirtualScroll(height) {
974
- initVirtualScrollY(height);
975
- initVirtualScrollX();
976
- }
977
988
  let vue2ScrollYTimeout = null;
978
989
  function updateVirtualScrollY(sTop = 0) {
979
- const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
990
+ const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex } = virtualScroll.value;
980
991
  virtualScroll.value.scrollTop = sTop;
981
992
  if (!virtual_on.value) {
982
993
  return;
983
994
  }
984
995
  let startIndex = Math.floor(sTop / rowHeight);
985
- if (startIndex < 0) {
986
- startIndex = 0;
987
- }
996
+ let endIndex = startIndex + pageSize;
988
997
  if (props.stripe && startIndex !== 0) {
989
- const scrollRows = Math.abs(oldStartIndex - startIndex);
990
- if (scrollRows % 2) {
998
+ if (startIndex % 2) {
991
999
  startIndex -= 1;
992
1000
  }
993
1001
  }
994
- let endIndex = startIndex + pageSize;
995
- if (props.stripe) {
996
- endIndex += 1;
997
- }
1002
+ startIndex = Math.max(0, startIndex);
998
1003
  endIndex = Math.min(endIndex, dataSourceCopy.value.length);
999
1004
  if (startIndex >= endIndex) {
1000
1005
  startIndex = endIndex - pageSize;
@@ -1002,13 +1007,12 @@ function useVirtualScroll({
1002
1007
  if (vue2ScrollYTimeout) {
1003
1008
  window.clearTimeout(vue2ScrollYTimeout);
1004
1009
  }
1010
+ if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
1011
+ return;
1012
+ }
1005
1013
  const offsetTop = startIndex * rowHeight;
1006
1014
  if (!props.optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
1007
- Object.assign(virtualScroll.value, {
1008
- startIndex,
1009
- endIndex,
1010
- offsetTop
1011
- });
1015
+ Object.assign(virtualScroll.value, { startIndex, endIndex, offsetTop });
1012
1016
  } else {
1013
1017
  virtualScroll.value.endIndex = endIndex;
1014
1018
  vue2ScrollYTimeout = window.setTimeout(() => {
@@ -1087,17 +1091,16 @@ function useVirtualScroll({
1087
1091
  };
1088
1092
  }
1089
1093
  const _hoisted_1 = ["data-col-key", "draggable", "rowspan", "colspan", "title", "onClick"];
1090
- const _hoisted_2 = { class: "table-header-cell-wrapper" };
1091
- const _hoisted_3 = {
1094
+ const _hoisted_2 = {
1092
1095
  key: 1,
1093
1096
  class: "table-header-title"
1094
1097
  };
1095
- const _hoisted_4 = { class: "table-header-title" };
1096
- const _hoisted_5 = {
1098
+ const _hoisted_3 = { class: "table-header-title" };
1099
+ const _hoisted_4 = {
1097
1100
  key: 3,
1098
1101
  class: "table-header-sorter"
1099
1102
  };
1100
- const _hoisted_6 = /* @__PURE__ */ createElementVNode("svg", {
1103
+ const _hoisted_5 = /* @__PURE__ */ createElementVNode("svg", {
1101
1104
  xmlns: "http://www.w3.org/2000/svg",
1102
1105
  width: "16px",
1103
1106
  height: "16px",
@@ -1114,31 +1117,22 @@ const _hoisted_6 = /* @__PURE__ */ createElementVNode("svg", {
1114
1117
  points: "8 10 4.8 14 11.2 14"
1115
1118
  })
1116
1119
  ], -1);
1117
- const _hoisted_7 = [
1118
- _hoisted_6
1120
+ const _hoisted_6 = [
1121
+ _hoisted_5
1119
1122
  ];
1123
+ const _hoisted_7 = ["onMousedown"];
1120
1124
  const _hoisted_8 = ["onMousedown"];
1121
- const _hoisted_9 = ["onMousedown"];
1122
- const _hoisted_10 = {
1123
- key: 1,
1124
- class: "virtual-top"
1125
- };
1126
- const _hoisted_11 = {
1125
+ const _hoisted_9 = {
1127
1126
  key: 0,
1128
- class: "virtual-x-left"
1127
+ class: "vt-x-left"
1129
1128
  };
1130
- const _hoisted_12 = { class: "stk-tbody-main" };
1131
- const _hoisted_13 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1132
- const _hoisted_14 = {
1129
+ const _hoisted_10 = ["id", "data-row-key", "onClick", "onDblclick", "onContextmenu", "onMouseover"];
1130
+ const _hoisted_11 = {
1133
1131
  key: 0,
1134
- class: "virtual-x-left"
1135
- };
1136
- const _hoisted_15 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1137
- const _hoisted_16 = ["title"];
1138
- const _hoisted_17 = {
1139
- key: 2,
1140
- class: "virtual-bottom"
1132
+ class: "vt-x-left"
1141
1133
  };
1134
+ const _hoisted_12 = ["data-index", "onClick", "onMouseenter", "onMouseleave", "onMouseover"];
1135
+ const _hoisted_13 = ["title"];
1142
1136
  const _sfc_main = /* @__PURE__ */ defineComponent({
1143
1137
  __name: "StkTable",
1144
1138
  props: {
@@ -1283,6 +1277,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1283
1277
  });
1284
1278
  }
1285
1279
  );
1280
+ watch(
1281
+ () => props.virtual,
1282
+ () => {
1283
+ nextTick(() => {
1284
+ initVirtualScrollY();
1285
+ });
1286
+ }
1287
+ );
1286
1288
  watch(
1287
1289
  () => props.virtualX,
1288
1290
  () => {
@@ -1580,15 +1582,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1580
1582
  function setCurrentRow(rowKey, option = { silent: false }) {
1581
1583
  if (!dataSourceCopy.value.length)
1582
1584
  return;
1583
- currentRow.value = dataSourceCopy.value.find((it) => rowKeyGen(it) === rowKey);
1584
- currentRowKey.value = rowKeyGen(currentRow.value);
1585
+ const row = dataSourceCopy.value.find((it) => rowKeyGen(it) === rowKey);
1586
+ if (!row) {
1587
+ console.warn("setCurrentRow failed.rowKey:", rowKey);
1588
+ return;
1589
+ }
1590
+ currentRow.value = row;
1591
+ currentRowKey.value = rowKey;
1585
1592
  if (!option.silent) {
1586
1593
  emits(
1587
1594
  "current-change",
1588
1595
  /** no Event */
1589
1596
  null,
1590
1597
  currentRow.value,
1591
- { select: Boolean(currentRowKey.value) }
1598
+ { select: true }
1592
1599
  );
1593
1600
  }
1594
1601
  }
@@ -1710,11 +1717,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1710
1717
  }, [
1711
1718
  unref(virtualX_on) ? (openBlock(), createElementBlock("th", {
1712
1719
  key: 0,
1713
- class: "virtual-x-left",
1714
- style: normalizeStyle({
1715
- minWidth: unref(virtualScrollX).offsetLeft + "px",
1716
- width: unref(virtualScrollX).offsetLeft + "px"
1717
- })
1720
+ class: "vt-x-left",
1721
+ style: normalizeStyle(`min-width:${unref(virtualScrollX).offsetLeft}px;width:${unref(virtualScrollX).offsetLeft}px`)
1718
1722
  }, null, 4)) : createCommentVNode("", true),
1719
1723
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_on) && rowIndex === tableHeaders.value.length - 1 ? unref(virtualX_columnPart) : row, (col, colIndex) => {
1720
1724
  return openBlock(), createElementBlock("th", {
@@ -1742,65 +1746,66 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1742
1746
  onDragover: _cache[2] || (_cache[2] = //@ts-ignore
1743
1747
  (...args) => unref(onThDragOver) && unref(onThDragOver)(...args))
1744
1748
  }, [
1745
- createElementVNode("div", _hoisted_2, [
1749
+ createElementVNode("div", {
1750
+ class: "table-header-cell-wrapper",
1751
+ style: normalizeStyle(`--row-span:${unref(virtualX_on) ? 1 : col.rowSpan}`)
1752
+ }, [
1746
1753
  col.customHeaderCell ? (openBlock(), createBlock(resolveDynamicComponent(col.customHeaderCell), {
1747
1754
  key: 0,
1748
1755
  col,
1749
1756
  colIndex,
1750
1757
  rowIndex
1751
- }, null, 8, ["col", "colIndex", "rowIndex"])) : col.type === "seq" ? (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(col.title), 1)) : renderSlot(_ctx.$slots, "tableHeader", {
1758
+ }, null, 8, ["col", "colIndex", "rowIndex"])) : col.type === "seq" ? (openBlock(), createElementBlock("span", _hoisted_2, toDisplayString(col.title), 1)) : renderSlot(_ctx.$slots, "tableHeader", {
1752
1759
  key: 2,
1753
1760
  col
1754
1761
  }, () => [
1755
- createElementVNode("span", _hoisted_4, toDisplayString(col.title), 1)
1762
+ createElementVNode("span", _hoisted_3, toDisplayString(col.title), 1)
1756
1763
  ]),
1757
- col.sorter ? (openBlock(), createElementBlock("span", _hoisted_5, _hoisted_7)) : createCommentVNode("", true),
1764
+ col.sorter ? (openBlock(), createElementBlock("span", _hoisted_4, _hoisted_6)) : createCommentVNode("", true),
1758
1765
  _ctx.colResizable && colIndex > 0 ? (openBlock(), createElementBlock("div", {
1759
1766
  key: 4,
1760
1767
  class: "table-header-resizer left",
1761
1768
  onMousedown: (e) => unref(onThResizeMouseDown)(e, col, true)
1762
- }, null, 40, _hoisted_8)) : createCommentVNode("", true),
1769
+ }, null, 40, _hoisted_7)) : createCommentVNode("", true),
1763
1770
  _ctx.colResizable ? (openBlock(), createElementBlock("div", {
1764
1771
  key: 5,
1765
1772
  class: "table-header-resizer right",
1766
1773
  onMousedown: (e) => unref(onThResizeMouseDown)(e, col)
1767
- }, null, 40, _hoisted_9)) : createCommentVNode("", true)
1768
- ])
1774
+ }, null, 40, _hoisted_8)) : createCommentVNode("", true)
1775
+ ], 4)
1769
1776
  ], 46, _hoisted_1);
1770
1777
  }), 128)),
1771
1778
  unref(virtualX_on) ? (openBlock(), createElementBlock("th", {
1772
1779
  key: 1,
1773
- class: "virtual-x-right",
1774
- style: normalizeStyle({
1775
- minWidth: unref(virtualX_offsetRight) + "px",
1776
- width: unref(virtualX_offsetRight) + "px"
1777
- })
1780
+ class: "vt-x-right",
1781
+ style: normalizeStyle(`min-width:${unref(virtualX_offsetRight)}px;width:${unref(virtualX_offsetRight)}px`)
1778
1782
  }, null, 4)) : createCommentVNode("", true)
1779
1783
  ], 32);
1780
1784
  }), 128))
1781
1785
  ], 512)) : createCommentVNode("", true),
1782
- unref(virtual_on) ? (openBlock(), createElementBlock("tbody", _hoisted_10, [
1783
- createElementVNode("tr", {
1784
- style: normalizeStyle({ height: `${unref(virtualScroll).offsetTop}px` }),
1786
+ createElementVNode("tbody", {
1787
+ class: normalizeClass(["stk-tbody-main", unref(virtual_on) ? "vt-on" : "vt-off"])
1788
+ }, [
1789
+ unref(virtual_on) ? (openBlock(), createElementBlock("tr", {
1790
+ key: 0,
1791
+ style: normalizeStyle(`height:${unref(virtualScroll).offsetTop}px`),
1785
1792
  class: "padding-top-tr"
1786
1793
  }, [
1787
- unref(virtualX_on) && _ctx.fixedMode && _ctx.headless ? (openBlock(), createElementBlock("td", _hoisted_11)) : createCommentVNode("", true),
1794
+ unref(virtualX_on) && _ctx.fixedMode && _ctx.headless ? (openBlock(), createElementBlock("td", _hoisted_9)) : createCommentVNode("", true),
1788
1795
  _ctx.fixedMode && _ctx.headless ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(virtualX_columnPart), (col) => {
1789
1796
  return openBlock(), createElementBlock("td", {
1790
1797
  key: col.dataIndex,
1791
1798
  style: normalizeStyle(cellStyleMap.value[unref(TagType).TD].get(colKeyGen.value(col)))
1792
1799
  }, null, 4);
1793
1800
  }), 128)) : createCommentVNode("", true)
1794
- ], 4)
1795
- ])) : createCommentVNode("", true),
1796
- createElementVNode("tbody", _hoisted_12, [
1801
+ ], 4)) : createCommentVNode("", true),
1797
1802
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtual_dataSourcePart), (row, rowIndex) => {
1798
1803
  return openBlock(), createElementBlock("tr", {
1799
1804
  id: unref(stkTableId) + "-" + (_ctx.rowKey ? rowKeyGen(row) : rowIndex),
1800
1805
  key: _ctx.rowKey ? rowKeyGen(row) : rowIndex,
1801
1806
  "data-row-key": _ctx.rowKey ? rowKeyGen(row) : rowIndex,
1802
1807
  class: normalizeClass({
1803
- active: _ctx.rowKey ? rowKeyGen(row) === rowKeyGen(currentRow.value) : row === currentRow.value,
1808
+ active: _ctx.rowKey ? rowKeyGen(row) === currentRowKey.value : row === currentRow.value,
1804
1809
  hover: props.showTrHoverClass && (_ctx.rowKey ? rowKeyGen(row) === currentHoverRowKey.value : row === currentHoverRowKey.value),
1805
1810
  [_ctx.rowClassName(row, rowIndex)]: true
1806
1811
  }),
@@ -1809,7 +1814,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1809
1814
  onContextmenu: (e) => onRowMenu(e, row),
1810
1815
  onMouseover: (e) => onTrMouseOver(e, row)
1811
1816
  }, [
1812
- unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_14)) : createCommentVNode("", true),
1817
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_11)) : createCommentVNode("", true),
1813
1818
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_columnPart), (col, colIndex) => {
1814
1819
  return openBlock(), createElementBlock("td", {
1815
1820
  key: col.dataIndex,
@@ -1838,17 +1843,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1838
1843
  ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
1839
1844
  createTextVNode(toDisplayString((row == null ? void 0 : row[col.dataIndex]) ?? getEmptyCellText.value(col, row)), 1)
1840
1845
  ], 64))
1841
- ], 8, _hoisted_16))
1842
- ], 46, _hoisted_15);
1846
+ ], 8, _hoisted_13))
1847
+ ], 46, _hoisted_12);
1843
1848
  }), 128))
1844
- ], 42, _hoisted_13);
1845
- }), 128))
1846
- ]),
1847
- unref(virtual_on) ? (openBlock(), createElementBlock("tbody", _hoisted_17, [
1848
- createElementVNode("tr", {
1849
- style: normalizeStyle({ height: `${unref(virtual_offsetBottom)}px` })
1850
- }, null, 4)
1851
- ])) : createCommentVNode("", true)
1849
+ ], 42, _hoisted_10);
1850
+ }), 128)),
1851
+ unref(virtual_on) ? (openBlock(), createElementBlock("tr", {
1852
+ key: 1,
1853
+ style: normalizeStyle(`height: ${unref(virtual_offsetBottom)}px`)
1854
+ }, null, 4)) : createCommentVNode("", true)
1855
+ ], 2)
1852
1856
  ], 6),
1853
1857
  (!dataSourceCopy.value || !dataSourceCopy.value.length) && _ctx.showNoData ? (openBlock(), createElementBlock("div", {
1854
1858
  key: 1,
package/lib/style.css CHANGED
@@ -91,7 +91,10 @@
91
91
  .stk-table.border-body-v tbody{
92
92
  --bg-border-bottom:linear-gradient(transparent, transparent);
93
93
  }
94
- .stk-table.stripe tbody tr:nth-child(even){
94
+ .stk-table.stripe .stk-tbody-main.vt-on tr:nth-child(odd){
95
+ background-color:var(--stripe-bgc);
96
+ }
97
+ .stk-table.stripe .stk-tbody-main.vt-off tr:nth-child(even){
95
98
  background-color:var(--stripe-bgc);
96
99
  }
97
100
  .stk-table.row-hover tbody tr:hover{
@@ -120,7 +123,7 @@
120
123
  }
121
124
  .stk-table.virtual .table-header-cell-wrapper{
122
125
  overflow:hidden;
123
- max-height:var(--header-row-height);
126
+ max-height:calc(var(--header-row-height) * var(--row-span));
124
127
  }
125
128
  .stk-table.virtual tbody td{
126
129
  height:var(--row-height);
@@ -187,8 +190,8 @@
187
190
  background-color:var(--td-bgc);
188
191
  height:var(--row-height);
189
192
  }
190
- .stk-table .virtual-x-left,
191
- .stk-table .virtual-x-right{
193
+ .stk-table .vt-x-left,
194
+ .stk-table .vt-x-right{
192
195
  padding:0;
193
196
  background:none;
194
197
  pointer-events:none;
package/package.json CHANGED
@@ -1,65 +1,65 @@
1
- {
2
- "name": "stk-table-vue",
3
- "version": "0.4.5",
4
- "description": "Simple realtime virtual table for vue3 and vue2.7",
5
- "main": "./lib/stk-table-vue.js",
6
- "types": "./lib/src/StkTable/index.d.ts",
7
- "packageManager": "pnpm@8.14.3",
8
- "directories": {
9
- "test": "test"
10
- },
11
- "type": "module",
12
- "scripts": {
13
- "dev": "vite",
14
- "build": "vite build",
15
- "test": "vitest"
16
- },
17
- "keywords": [
18
- "virtual table",
19
- "vue",
20
- "vue2",
21
- "vue3",
22
- "highlight",
23
- "sticky",
24
- "virtual",
25
- "table",
26
- "list"
27
- ],
28
- "files": [
29
- "lib",
30
- "src"
31
- ],
32
- "author": "japlus",
33
- "repository": {
34
- "type": "git",
35
- "url": "https://github.com/ja-plus/stk-table-vue"
36
- },
37
- "license": "MIT",
38
- "devDependencies": {
39
- "@types/d3-interpolate": "^3.0.4",
40
- "@types/node": "^20.12.10",
41
- "@typescript-eslint/eslint-plugin": "^7.7.0",
42
- "@typescript-eslint/parser": "^7.7.0",
43
- "@vitejs/plugin-vue": "^5.0.4",
44
- "@vue/test-utils": "2.4.4",
45
- "eslint": "^8.57.0",
46
- "eslint-config-prettier": "^9.1.0",
47
- "eslint-plugin-html": "^8.1.0",
48
- "eslint-plugin-prettier": "^5.1.3",
49
- "eslint-plugin-vue": "^9.25.0",
50
- "happy-dom": "^12.10.3",
51
- "less": "^4.2.0",
52
- "postcss-discard-comments": "^6.0.2",
53
- "postcss-preset-env": "^9.5.11",
54
- "prettier": "^3.2.5",
55
- "typescript": "^5.4.5",
56
- "vite": "^5.2.11",
57
- "vite-plugin-dts": "^3.9.1",
58
- "vitest": "^1.6.0",
59
- "vue": "^3.4.26",
60
- "vue-eslint-parser": "^9.4.2"
61
- },
62
- "dependencies": {
63
- "d3-interpolate": "^3.0.1"
64
- }
1
+ {
2
+ "name": "stk-table-vue",
3
+ "version": "0.4.8",
4
+ "description": "Simple realtime virtual table for vue3 and vue2.7",
5
+ "main": "./lib/stk-table-vue.js",
6
+ "types": "./lib/src/StkTable/index.d.ts",
7
+ "packageManager": "pnpm@8.14.3",
8
+ "directories": {
9
+ "test": "test"
10
+ },
11
+ "type": "module",
12
+ "scripts": {
13
+ "dev": "vite",
14
+ "build": "vite build",
15
+ "test": "vitest"
16
+ },
17
+ "keywords": [
18
+ "virtual table",
19
+ "vue",
20
+ "vue2",
21
+ "vue3",
22
+ "highlight",
23
+ "sticky",
24
+ "virtual",
25
+ "table",
26
+ "list"
27
+ ],
28
+ "files": [
29
+ "lib",
30
+ "src"
31
+ ],
32
+ "author": "japlus",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/ja-plus/stk-table-vue"
36
+ },
37
+ "license": "MIT",
38
+ "devDependencies": {
39
+ "@types/d3-interpolate": "^3.0.4",
40
+ "@types/node": "^20.12.10",
41
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
42
+ "@typescript-eslint/parser": "^7.7.0",
43
+ "@vitejs/plugin-vue": "^5.0.4",
44
+ "@vue/test-utils": "2.4.4",
45
+ "eslint": "^8.57.0",
46
+ "eslint-config-prettier": "^9.1.0",
47
+ "eslint-plugin-html": "^8.1.0",
48
+ "eslint-plugin-prettier": "^5.1.3",
49
+ "eslint-plugin-vue": "^9.25.0",
50
+ "happy-dom": "^12.10.3",
51
+ "less": "^4.2.0",
52
+ "postcss-discard-comments": "^6.0.2",
53
+ "postcss-preset-env": "^9.5.11",
54
+ "prettier": "^3.2.5",
55
+ "typescript": "^5.4.5",
56
+ "vite": "^5.2.11",
57
+ "vite-plugin-dts": "^3.9.1",
58
+ "vitest": "^1.6.0",
59
+ "vue": "^3.4.26",
60
+ "vue-eslint-parser": "^9.4.2"
61
+ },
62
+ "dependencies": {
63
+ "d3-interpolate": "^3.0.1"
64
+ }
65
65
  }
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/attribute-hyphenation -->
1
2
  <template>
2
3
  <div
3
4
  ref="tableContainerRef"
@@ -52,11 +53,8 @@
52
53
  <!-- 这个th用于横向虚拟滚动表格左边距,width、maxWidth 用于兼容低版本浏览器 -->
53
54
  <th
54
55
  v-if="virtualX_on"
55
- class="virtual-x-left"
56
- :style="{
57
- minWidth: virtualScrollX.offsetLeft + 'px',
58
- width: virtualScrollX.offsetLeft + 'px',
59
- }"
56
+ class="vt-x-left"
57
+ :style="`min-width:${virtualScrollX.offsetLeft}px;width:${virtualScrollX.offsetLeft}px`"
60
58
  ></th>
61
59
  <!-- v for中最后一行才用 切割。TODO:不支持多级表头虚拟横向滚动 -->
62
60
  <th
@@ -84,7 +82,7 @@
84
82
  @drop="onThDrop"
85
83
  @dragover="onThDragOver"
86
84
  >
87
- <div class="table-header-cell-wrapper">
85
+ <div class="table-header-cell-wrapper" :style="`--row-span:${virtualX_on ? 1 : col.rowSpan}`">
88
86
  <component :is="col.customHeaderCell" v-if="col.customHeaderCell" :col="col" :colIndex="colIndex" :rowIndex="rowIndex" />
89
87
  <template v-else-if="col.type === 'seq'">
90
88
  <span class="table-header-title">{{ col.title }}</span>
@@ -116,46 +114,28 @@
116
114
  </div>
117
115
  </th>
118
116
  <!-- 这个th用于横向虚拟滚动表格右边距 width、maxWidth 用于兼容低版本浏览器-->
119
- <th
120
- v-if="virtualX_on"
121
- class="virtual-x-right"
122
- :style="{
123
- minWidth: virtualX_offsetRight + 'px',
124
- width: virtualX_offsetRight + 'px',
125
- }"
126
- ></th>
117
+ <th v-if="virtualX_on" class="vt-x-right" :style="`min-width:${virtualX_offsetRight}px;width:${virtualX_offsetRight}px`"></th>
127
118
  </tr>
128
119
  </thead>
129
120
 
130
121
  <!-- 用于虚拟滚动表格内容定位 @deprecated 有兼容问题-->
131
- <!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }">
132
- <!==这个tr兼容火狐==>
133
- <tr></tr>
134
- </tbody> -->
135
- <!-- <td
136
- v-for="col in virtualX_on ? virtualX_columnPart : tableHeaderLast"
137
- :key="col.dataIndex"
138
- class="perch-td top"
139
- ></td> -->
122
+ <!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }"></tbody> -->
140
123
  <!-- <tbody :style="{ transform: `translateY(${virtualScroll.offsetTop}px)` }"> -->
141
- <tbody v-if="virtual_on" class="virtual-top">
142
- <!-- 由于斑马纹选择器nth-child 原因,占位tr单独包在tbody中 -->
143
- <tr :style="{ height: `${virtualScroll.offsetTop}px` }" class="padding-top-tr">
124
+ <tbody class="stk-tbody-main" :class="virtual_on ? 'vt-on' : 'vt-off'">
125
+ <tr v-if="virtual_on" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
144
126
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
145
- <td v-if="virtualX_on && fixedMode && headless" class="virtual-x-left"></td>
127
+ <td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
146
128
  <template v-if="fixedMode && headless">
147
129
  <td v-for="col in virtualX_columnPart" :key="col.dataIndex" :style="cellStyleMap[TagType.TD].get(colKeyGen(col))"></td>
148
130
  </template>
149
131
  </tr>
150
- </tbody>
151
- <tbody class="stk-tbody-main">
152
132
  <tr
153
133
  v-for="(row, rowIndex) in virtual_dataSourcePart"
154
134
  :id="stkTableId + '-' + (rowKey ? rowKeyGen(row) : rowIndex)"
155
135
  :key="rowKey ? rowKeyGen(row) : rowIndex"
156
136
  :data-row-key="rowKey ? rowKeyGen(row) : rowIndex"
157
137
  :class="{
158
- active: rowKey ? rowKeyGen(row) === rowKeyGen(currentRow) : row === currentRow,
138
+ active: rowKey ? rowKeyGen(row) === currentRowKey : row === currentRow,
159
139
  hover: props.showTrHoverClass && (rowKey ? rowKeyGen(row) === currentHoverRowKey : row === currentHoverRowKey),
160
140
  [rowClassName(row, rowIndex)]: true,
161
141
  }"
@@ -165,7 +145,7 @@
165
145
  @mouseover="e => onTrMouseOver(e, row)"
166
146
  >
167
147
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
168
- <td v-if="virtualX_on" class="virtual-x-left"></td>
148
+ <td v-if="virtualX_on" class="vt-x-left"></td>
169
149
  <td
170
150
  v-for="(col, colIndex) in virtualX_columnPart"
171
151
  :key="col.dataIndex"
@@ -196,9 +176,7 @@
196
176
  </div>
197
177
  </td>
198
178
  </tr>
199
- </tbody>
200
- <tbody v-if="virtual_on" class="virtual-bottom">
201
- <tr :style="{ height: `${virtual_offsetBottom}px` }"></tr>
179
+ <tr v-if="virtual_on" :style="`height: ${virtual_offsetBottom}px`"></tr>
202
180
  </tbody>
203
181
  </table>
204
182
  <div v-if="(!dataSourceCopy || !dataSourceCopy.length) && showNoData" class="stk-table-no-data" :class="{ 'no-data-full': noDataFull }">
@@ -228,7 +206,7 @@ import { useVirtualScroll } from './useVirtualScroll';
228
206
  import { createStkTableId, getCalculatedColWidth, getColWidth, howDeepTheHeader, tableSort, transformWidthToStr } from './utils/index';
229
207
 
230
208
  /** Generic stands for DataType */
231
- type DT = Record<string | number, any>;
209
+ type DT = any;
232
210
  /** 自己生成实例id */
233
211
  const stkTableId = createStkTableId();
234
212
  /**
@@ -633,6 +611,14 @@ watch(
633
611
  });
634
612
  },
635
613
  );
614
+ watch(
615
+ () => props.virtual,
616
+ () => {
617
+ nextTick(() => {
618
+ initVirtualScrollY();
619
+ });
620
+ },
621
+ );
636
622
  watch(
637
623
  () => props.virtualX,
638
624
  () => {
@@ -1036,10 +1022,15 @@ function onTrMouseOver(_e: MouseEvent, row: DT) {
1036
1022
  */
1037
1023
  function setCurrentRow(rowKey: string, option = { silent: false }) {
1038
1024
  if (!dataSourceCopy.value.length) return;
1039
- currentRow.value = dataSourceCopy.value.find(it => rowKeyGen(it) === rowKey);
1040
- currentRowKey.value = rowKeyGen(currentRow.value);
1025
+ const row = dataSourceCopy.value.find(it => rowKeyGen(it) === rowKey);
1026
+ if (!row) {
1027
+ console.warn('setCurrentRow failed.rowKey:', rowKey);
1028
+ return;
1029
+ }
1030
+ currentRow.value = row;
1031
+ currentRowKey.value = rowKey;
1041
1032
  if (!option.silent) {
1042
- emits('current-change', /** no Event */ null, currentRow.value, { select: Boolean(currentRowKey.value) });
1033
+ emits('current-change', /** no Event */ null, currentRow.value, { select: true });
1043
1034
  }
1044
1035
  }
1045
1036
 
@@ -134,9 +134,13 @@
134
134
  }
135
135
 
136
136
  /* 斑马纹*/
137
- &.stripe {
137
+ &.stripe .stk-tbody-main {
138
138
 
139
- tbody tr:nth-child(even) {
139
+ &.vt-on tr:nth-child(odd) {
140
+ background-color: var(--stripe-bgc);
141
+ }
142
+
143
+ &.vt-off tr:nth-child(even) {
140
144
  background-color: var(--stripe-bgc);
141
145
  }
142
146
  }
@@ -183,7 +187,7 @@
183
187
  /* 为不影响布局,表头行高要定死*/
184
188
  .table-header-cell-wrapper {
185
189
  overflow: hidden;
186
- max-height: var(--header-row-height);
190
+ max-height: calc(var(--header-row-height) * var(--row-span));
187
191
  }
188
192
 
189
193
  tbody td {
@@ -279,8 +283,8 @@
279
283
  }
280
284
 
281
285
 
282
- .virtual-x-left,
283
- .virtual-x-right {
286
+ .vt-x-left,
287
+ .vt-x-right {
284
288
  padding: 0;
285
289
  background: none;
286
290
  pointer-events: none;
@@ -37,9 +37,6 @@ export function useFixedStyle<DT extends Record<string, any>>({
37
37
 
38
38
  const style: CSSProperties = {};
39
39
 
40
- const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
41
- const scrollRight = scrollWidth - containerWidth - scrollLeft;
42
-
43
40
  const isFixedLeft = fixed === 'left';
44
41
  if (tagType === TagType.TH) {
45
42
  // TH
@@ -50,6 +47,9 @@ export function useFixedStyle<DT extends Record<string, any>>({
50
47
  }
51
48
  }
52
49
 
50
+ const { scrollLeft, scrollWidth, offsetLeft, containerWidth } = virtualScrollX.value;
51
+ const scrollRight = scrollWidth - containerWidth - scrollLeft;
52
+
53
53
  if (fixed === 'left' || fixed === 'right') {
54
54
  if (isRelativeMode.value) {
55
55
  if (isFixedLeft) {
@@ -3,7 +3,16 @@ import { StkTableColumn } from './types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
4
4
 
5
5
  /** 翻页按键 */
6
- const SCROLL_CODES = ['ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft', 'PageUp', 'PageDown'] as const;
6
+ enum ScrollCodes {
7
+ ArrowUp = 'ArrowUp',
8
+ ArrowRight = 'ArrowRight',
9
+ ArrowDown = 'ArrowDown',
10
+ ArrowLeft = 'ArrowLeft',
11
+ PageUp = 'PageUp',
12
+ PageDown = 'PageDown',
13
+ }
14
+ /** 所有翻页按键数组 */
15
+ const ScrollCodesValues = Object.values(ScrollCodes);
7
16
 
8
17
  type Options<DT extends Record<string, any>> = {
9
18
  props: any;
@@ -52,7 +61,8 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
52
61
 
53
62
  /** 键盘按下事件 */
54
63
  function handleKeydown(e: KeyboardEvent) {
55
- if (!SCROLL_CODES.includes(e.code as any)) return;
64
+ if (!virtual_on.value) return; // 非虚拟滚动使用浏览器默认滚动
65
+ if (!ScrollCodesValues.includes(e.code as any)) return;
56
66
  if (!isMouseOver) return; // 不悬浮还是要触发键盘事件的
57
67
  e.preventDefault(); // 不触发键盘默认的箭头事件
58
68
 
@@ -65,17 +75,17 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
65
75
  /** 表体的page */
66
76
  const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
67
77
 
68
- if (e.code === SCROLL_CODES[0]) {
78
+ if (e.code === ScrollCodes.ArrowUp) {
69
79
  scrollTo(scrollTop - rowHeight, null);
70
- } else if (e.code === SCROLL_CODES[1]) {
80
+ } else if (e.code === ScrollCodes.ArrowRight) {
71
81
  scrollTo(null, scrollLeft + rowHeight);
72
- } else if (e.code === SCROLL_CODES[2]) {
82
+ } else if (e.code === ScrollCodes.ArrowDown) {
73
83
  scrollTo(scrollTop + rowHeight, null);
74
- } else if (e.code === SCROLL_CODES[3]) {
84
+ } else if (e.code === ScrollCodes.ArrowLeft) {
75
85
  scrollTo(null, scrollLeft - rowHeight);
76
- } else if (e.code === SCROLL_CODES[4]) {
86
+ } else if (e.code === ScrollCodes.PageUp) {
77
87
  scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
78
- } else if (e.code === SCROLL_CODES[5]) {
88
+ } else if (e.code === ScrollCodes.PageDown) {
79
89
  scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
80
90
  }
81
91
  }
@@ -163,6 +163,15 @@ export function useVirtualScroll<DT extends Record<string, any>>({
163
163
  // return theadRef.value?.offsetHeight || 0;
164
164
  }
165
165
 
166
+ /**
167
+ * 初始化虚拟滚动参数
168
+ * @param {number} [height] 虚拟滚动的高度
169
+ */
170
+ function initVirtualScroll(height?: number) {
171
+ initVirtualScrollY(height);
172
+ initVirtualScrollX();
173
+ }
174
+
166
175
  /**
167
176
  * 初始化Y虚拟滚动参数
168
177
  * @param {number} [height] 虚拟滚动的高度
@@ -184,7 +193,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
184
193
  tableHeaderHeight.value = headerHeight;
185
194
  if (!headless) {
186
195
  /** 表头高度占几行表体高度数 */
187
- const headerToBodyRowHeightCount = Math.floor(headerHeight) / rowHeight;
196
+ const headerToBodyRowHeightCount = Math.floor(headerHeight / rowHeight);
188
197
  pageSize -= headerToBodyRowHeightCount; //减去表头行数
189
198
  }
190
199
  /** 最大的scrollTop */
@@ -203,20 +212,12 @@ export function useVirtualScroll<DT extends Record<string, any>>({
203
212
  virtualScrollX.value.scrollWidth = scrollWidth || DEFAULT_TABLE_WIDTH;
204
213
  updateVirtualScrollX(scrollLeft);
205
214
  }
206
- /**
207
- * 初始化虚拟滚动参数
208
- * @param {number} [height] 虚拟滚动的高度
209
- */
210
- function initVirtualScroll(height?: number) {
211
- initVirtualScrollY(height);
212
- initVirtualScrollX();
213
- }
214
215
 
215
216
  let vue2ScrollYTimeout: null | number = null;
216
217
 
217
218
  /** 通过滚动条位置,计算虚拟滚动的参数 */
218
219
  function updateVirtualScrollY(sTop = 0) {
219
- const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
220
+ const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex, endIndex: oldEndIndex } = virtualScroll.value;
220
221
  // 先更新滚动条位置记录,其他地方有依赖。(stripe 时ArrowUp/Down滚动依赖)
221
222
  virtualScroll.value.scrollTop = sTop;
222
223
 
@@ -226,22 +227,18 @@ export function useVirtualScroll<DT extends Record<string, any>>({
226
227
  }
227
228
 
228
229
  let startIndex = Math.floor(sTop / rowHeight);
229
- if (startIndex < 0) {
230
- startIndex = 0;
231
- }
230
+ let endIndex = startIndex + pageSize;
231
+
232
232
  if (props.stripe && startIndex !== 0) {
233
- const scrollRows = Math.abs(oldStartIndex - startIndex);
234
233
  // 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
235
- if (scrollRows % 2) {
234
+ if (startIndex % 2) {
236
235
  startIndex -= 1; // 奇数-1变成偶数
237
236
  }
238
237
  }
239
- let endIndex = startIndex + pageSize;
240
- if (props.stripe) {
241
- endIndex += 1; // 斑马纹下多渲染一些
242
- }
243
238
 
244
- // 溢出endIndex修正
239
+ startIndex = Math.max(0, startIndex);
240
+
241
+ // 溢出修正
245
242
  endIndex = Math.min(endIndex, dataSourceCopy.value.length);
246
243
 
247
244
  if (startIndex >= endIndex) {
@@ -253,6 +250,11 @@ export function useVirtualScroll<DT extends Record<string, any>>({
253
250
  window.clearTimeout(vue2ScrollYTimeout);
254
251
  }
255
252
 
253
+ if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
254
+ // 没有变化,不需要更新
255
+ return;
256
+ }
257
+
256
258
  const offsetTop = startIndex * rowHeight; // startIndex之前的高度
257
259
 
258
260
  /**
@@ -260,11 +262,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
260
262
  */
261
263
  if (!props.optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
262
264
  // 向上滚动
263
- Object.assign(virtualScroll.value, {
264
- startIndex,
265
- endIndex,
266
- offsetTop,
267
- });
265
+ Object.assign(virtualScroll.value, { startIndex, endIndex, offsetTop });
268
266
  } else {
269
267
  // vue2向下滚动优化
270
268
  virtualScroll.value.endIndex = endIndex;