stk-table-vue 0.11.3 → 0.11.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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: stk-table-vue
3
- * version: v0.11.3
3
+ * version: v0.11.4
4
4
  * description: High performance realtime virtual table for vue3 and vue2.7
5
5
  * author: japlus
6
6
  * homepage: https://ja-plus.github.io/stk-table-vue/
@@ -1,5 +1,5 @@
1
1
  import { FilterStatus } from './components/Filter/types';
2
- import { AreaSelectionConfig, AreaSelectionRange, AutoRowHeightConfig, ColResizableConfig, DragRowConfig, ExpandConfig, ExperimentalConfig, FooterConfig, HeaderDragConfig, HighlightConfig, Order, PrivateRowDT, PrivateStkTableColumn, RowActiveOption, SeqConfig, SortConfig, SortOption, SortState, StkTableColumn, TreeConfig, UniqKey, UniqKeyProp } from './types/index';
2
+ import { AreaSelectionConfig, AreaSelectionRange, AutoRowHeightConfig, ColResizableConfig, DragRowConfig, ExpandConfig, ExperimentalConfig, FooterConfig, HeaderDragConfig, HighlightConfig, Order, PrivateRowDT, PrivateStkTableColumn, RowActiveOption, SeqConfig, SortConfig, StkTableColumn, TreeConfig, UniqKey, UniqKeyProp } from './types/index';
3
3
  import { ScrollbarOptions } from './useScrollbar';
4
4
 
5
5
  /** Generic stands for DataType */
@@ -28,25 +28,6 @@ declare function setCurrentRow(rowKeyOrRow: string | undefined | DT, option?: {
28
28
  declare function setSelectedCell(row?: DT, col?: StkTableColumn<DT>, option?: {
29
29
  silent: boolean;
30
30
  }): void;
31
- /**
32
- * 设置表头排序状态。
33
- * @param colKey 列唯一键字段。如果你想要取消排序状态,请使用`resetSorter`
34
- * @param order 正序倒序
35
- * @param option.sortOption 指定排序参数。同 StkTableColumn 中排序相关字段。建议从columns中find得到。
36
- * @param option.sort 是否触发排序-默认true
37
- * @param option.silent 是否禁止触发回调-默认true
38
- * @param option.force 是否触发排序-默认true
39
- * @param option.append 是否追加排序(多列排序模式)。为true时保留现有排序状态,将新排序添加到最前面;为false时替换所有排序状态
40
- * @return 表格数据
41
- */
42
- declare function setSorter(colKey: string, order: Order, option?: {
43
- sortOption?: SortOption<DT>;
44
- force?: boolean;
45
- silent?: boolean;
46
- sort?: boolean;
47
- append?: boolean;
48
- }): any[];
49
- declare function resetSorter(): void;
50
31
  /**
51
32
  * set scroll bar position
52
33
  * @param top null to not change
@@ -55,14 +36,6 @@ declare function resetSorter(): void;
55
36
  declare function scrollTo(top?: number | null, left?: number | null): void;
56
37
  /** get current table data */
57
38
  declare function getTableData(): any[];
58
- /**
59
- * get current sort info
60
- * @return {{key:string,order:Order}[]}
61
- */
62
- declare function getSortColumns(): {
63
- key: keyof DT | undefined;
64
- order: Order;
65
- }[];
66
39
  declare function __VLS_template(): {
67
40
  tableHeader?(_: {
68
41
  col: PrivateStkTableColumn<PrivateRowDT>;
@@ -359,34 +332,43 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
359
332
  sortField?: string | number | symbol | undefined;
360
333
  sortType?: "number" | "string" | undefined;
361
334
  order: Order;
362
- }[], SortState<any>[] | {
335
+ }[], {
363
336
  key?: any;
364
337
  dataIndex: string;
365
338
  sortField?: string | number | symbol | undefined;
366
339
  sortType?: "number" | "string" | undefined;
367
340
  order: Order;
368
- }[]>;
341
+ }[] | import('.').SortState<any>[]>;
369
342
  /**
370
343
  * 表格排序列顺序
371
344
  *
372
345
  * en: get current sort info
373
346
  * @see {@link getSortColumns}
374
347
  */
375
- getSortColumns: typeof getSortColumns;
348
+ getSortColumns: () => {
349
+ key: string | number | symbol | undefined;
350
+ order: Order;
351
+ }[];
376
352
  /**
377
353
  * 设置表头排序状态
378
354
  *
379
355
  * en: Set the sort status of the table header
380
356
  * @see {@link setSorter}
381
357
  */
382
- setSorter: typeof setSorter;
358
+ setSorter: (colKey: string, order: Order, option?: {
359
+ sortOption?: import('.').SortOption<any> | undefined;
360
+ force?: boolean;
361
+ silent?: boolean;
362
+ sort?: boolean;
363
+ append?: boolean;
364
+ }) => any[];
383
365
  /**
384
366
  * 重置sorter状态
385
367
  *
386
368
  * en: Reset the sorter status
387
369
  * @see {@link resetSorter}
388
370
  */
389
- resetSorter: typeof resetSorter;
371
+ resetSorter: () => void;
390
372
  /**
391
373
  * 滚动至
392
374
  *
@@ -0,0 +1,38 @@
1
+ import { Ref } from 'vue';
2
+ import { Order, SortOption, SortState, StkTableColumn, UniqKey } from './types/index';
3
+
4
+ /**
5
+ * 排序 Hook
6
+ * 管理表格排序状态和相关操作
7
+ * @param props 表格 props
8
+ * @param colKeyGen 列 key 生成函数
9
+ * @param tableHeaderLast 表头最后一行(叶子节点)
10
+ * @param dataSourceCopy 数据源副本 ref
11
+ * @param initDataSource 初始化数据源函数
12
+ * @param emits 事件发射函数
13
+ * @returns 排序相关状态和方法
14
+ */
15
+ export declare function useSorter<DT extends Record<string, any>>(props: any, emits: any, colKeyGen: Ref<(col: StkTableColumn<DT>) => string>, tableHeaderLast: Ref<StkTableColumn<DT>[]>, dataSourceCopy: Ref<DT[]>, initDataSource: (data?: DT[], option?: {
16
+ forceSort?: boolean;
17
+ }) => void): readonly [Ref<{
18
+ key?: any;
19
+ dataIndex: import('vue').UnwrapRef<keyof DT & string>;
20
+ sortField?: import('vue').UnwrapRef<keyof DT> | undefined;
21
+ sortType?: "number" | "string" | undefined;
22
+ order: Order;
23
+ }[], SortState<DT>[] | {
24
+ key?: any;
25
+ dataIndex: import('vue').UnwrapRef<keyof DT & string>;
26
+ sortField?: import('vue').UnwrapRef<keyof DT> | undefined;
27
+ sortType?: "number" | "string" | undefined;
28
+ order: Order;
29
+ }[]>, import('vue').ComputedRef<keyof DT | undefined>, (col: StkTableColumn<DT> | undefined | null) => void, (colKey: string, order: Order, option?: {
30
+ sortOption?: SortOption<DT>;
31
+ force?: boolean;
32
+ silent?: boolean;
33
+ sort?: boolean;
34
+ append?: boolean;
35
+ }) => DT[], () => void, () => {
36
+ key: keyof DT | undefined;
37
+ order: Order;
38
+ }[], () => void, (colKey: UniqKey) => SortState<DT> | undefined, (dataSource: DT[]) => DT[]];
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * name: stk-table-vue
3
- * version: v0.11.3
3
+ * version: v0.11.4
4
4
  * description: High performance realtime virtual table for vue3 and vue2.7
5
5
  * author: japlus
6
6
  * homepage: https://ja-plus.github.io/stk-table-vue/
7
7
  * license: MIT
8
8
  */
9
- import { createElementBlock, openBlock, createElementVNode, defineComponent, normalizeStyle, createBlock, createCommentVNode, toDisplayString, ref, computed, onMounted, onBeforeUnmount, watch, shallowRef, onUnmounted, nextTick, customRef, provide, toRef, toRaw, normalizeClass, unref, renderSlot, Fragment, renderList, mergeProps, resolveDynamicComponent, withCtx, createTextVNode, createVNode, createApp, markRaw, getCurrentInstance, h } from "vue";
9
+ import { createElementBlock, openBlock, createElementVNode, defineComponent, normalizeStyle, createBlock, createCommentVNode, toDisplayString, ref, computed, onMounted, onBeforeUnmount, watch, shallowRef, onUnmounted, nextTick, customRef, toRaw, provide, toRef, normalizeClass, unref, renderSlot, Fragment, renderList, mergeProps, resolveDynamicComponent, withCtx, createTextVNode, createVNode, createApp, markRaw, getCurrentInstance, h } from "vue";
10
10
  const _export_sfc = (sfc, props) => {
11
11
  const target = sfc.__vccOpts || sfc;
12
12
  for (const [key, val] of props) {
@@ -2258,7 +2258,7 @@ function useVirtualScroll(props, tableContainerRef, trRef, dataSourceCopy, table
2258
2258
  const headerToBodyRowHeightCount = Math.floor(tableHeaderHeight.value / rowHeight);
2259
2259
  pageSize -= headerToBodyRowHeightCount;
2260
2260
  }
2261
- const maxScrollTop = dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight;
2261
+ const maxScrollTop = Math.max(0, dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight);
2262
2262
  if (scrollTop > maxScrollTop) {
2263
2263
  scrollTop = maxScrollTop;
2264
2264
  }
@@ -2306,7 +2306,13 @@ function useVirtualScroll(props, tableContainerRef, trRef, dataSourceCopy, table
2306
2306
  const dataSourceCopyTemp = dataSourceCopy.value;
2307
2307
  const dataLength = dataSourceCopyTemp.length;
2308
2308
  const rowHeight = getRowHeightFn.value();
2309
- const vsValue = {};
2309
+ const vsValue = {
2310
+ startIndex: 0,
2311
+ // github #34 init
2312
+ // endIndex: , // FIXME: endIndex may be old value
2313
+ offsetTop: 0
2314
+ // github #34 init
2315
+ };
2310
2316
  const scrollHeight = dataLength * rowHeight + tableHeaderHeight.value;
2311
2317
  const { enabled: scrollbarEnable } = scrollbarOptions.value;
2312
2318
  if (scrollbarEnable) {
@@ -2505,6 +2511,162 @@ function useWheeling(resetDelay = 500) {
2505
2511
  };
2506
2512
  return [get, set];
2507
2513
  }
2514
+ const SORT_SWITCH_ORDER = [null, "desc", "asc"];
2515
+ function useSorter(props, emits, colKeyGen, tableHeaderLast, dataSourceCopy, initDataSource) {
2516
+ const sortStates = ref([]);
2517
+ const isMultiSort = computed(() => props.sortConfig.multiSort ?? false);
2518
+ const multiSortLimit = computed(() => props.sortConfig.multiSortLimit ?? 3);
2519
+ const sortCol = computed(() => {
2520
+ var _a;
2521
+ return (_a = sortStates.value[0]) == null ? void 0 : _a.dataIndex;
2522
+ });
2523
+ function getColumnSortState(colKey) {
2524
+ return sortStates.value[getSortStateIndex(colKey)];
2525
+ }
2526
+ function getSortStateIndex(colKey) {
2527
+ return sortStates.value.findIndex((s) => s.key === colKey || s.dataIndex === colKey);
2528
+ }
2529
+ function addOrUpdateSortState(newState, mode) {
2530
+ const existingIndex = sortStates.value.findIndex((s) => s.key === newState.key || s.dataIndex === newState.dataIndex);
2531
+ if (existingIndex >= 0) {
2532
+ sortStates.value.splice(existingIndex, 1);
2533
+ }
2534
+ if (mode && isMultiSort.value) {
2535
+ if (sortStates.value.length >= multiSortLimit.value) {
2536
+ sortStates.value.pop();
2537
+ }
2538
+ sortStates.value.unshift(newState);
2539
+ } else {
2540
+ sortStates.value = [newState];
2541
+ }
2542
+ }
2543
+ function updateSortState(col, colKey) {
2544
+ const existingIndex = getSortStateIndex(colKey);
2545
+ let newOrder;
2546
+ if (existingIndex >= 0) {
2547
+ const currentOrder = sortStates.value[existingIndex].order;
2548
+ const currentIndex = SORT_SWITCH_ORDER.indexOf(currentOrder);
2549
+ newOrder = SORT_SWITCH_ORDER[(currentIndex + 1) % 3];
2550
+ if (newOrder === null) {
2551
+ sortStates.value.splice(existingIndex, 1);
2552
+ } else {
2553
+ const updatedState = { ...sortStates.value[existingIndex], order: newOrder };
2554
+ addOrUpdateSortState(updatedState, 1);
2555
+ }
2556
+ } else {
2557
+ newOrder = SORT_SWITCH_ORDER[1];
2558
+ const newState = {
2559
+ key: colKey,
2560
+ dataIndex: col.dataIndex,
2561
+ sortField: col.sortField,
2562
+ sortType: col.sortType,
2563
+ order: newOrder
2564
+ };
2565
+ addOrUpdateSortState(newState, 1);
2566
+ }
2567
+ return newOrder;
2568
+ }
2569
+ function sortData(dataSource) {
2570
+ if (!sortStates.value.length) return dataSource;
2571
+ const sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig };
2572
+ let result = dataSource.slice();
2573
+ for (let i = sortStates.value.length - 1; i >= 0; i--) {
2574
+ const state = sortStates.value[i];
2575
+ const col = tableHeaderLast.value.find((c) => state.key && colKeyGen.value(c) === state.key || c.dataIndex === state.dataIndex);
2576
+ if (col && state.order) {
2577
+ const colSortConfig = { ...sortConfig, ...col.sortConfig };
2578
+ result = tableSort(col, state.order, result, colSortConfig);
2579
+ }
2580
+ }
2581
+ return result;
2582
+ }
2583
+ function onColumnSort(col) {
2584
+ if (!col) {
2585
+ console.warn("onColumnSort: not found col:", col);
2586
+ return;
2587
+ }
2588
+ if (!col.sorter) {
2589
+ return;
2590
+ }
2591
+ const colKey = colKeyGen.value(col);
2592
+ let order = updateSortState(col, colKey);
2593
+ const sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig, ...col.sortConfig };
2594
+ if (!order && sortConfig.defaultSort) {
2595
+ const defaultColKey = sortConfig.defaultSort.key || sortConfig.defaultSort.dataIndex;
2596
+ if (defaultColKey) {
2597
+ const defaultCol = tableHeaderLast.value.find((item) => colKeyGen.value(item) === defaultColKey);
2598
+ if (defaultCol) {
2599
+ col = defaultCol;
2600
+ order = sortConfig.defaultSort.order;
2601
+ if (order) {
2602
+ addOrUpdateSortState({
2603
+ key: defaultColKey,
2604
+ dataIndex: defaultCol.dataIndex,
2605
+ sortField: defaultCol.sortField,
2606
+ sortType: defaultCol.sortType,
2607
+ order
2608
+ });
2609
+ }
2610
+ }
2611
+ }
2612
+ }
2613
+ if (!props.sortRemote) {
2614
+ initDataSource();
2615
+ }
2616
+ emits("sort-change", col, order, toRaw(dataSourceCopy.value), sortConfig);
2617
+ }
2618
+ function setSorter(colKey, order, option = {}) {
2619
+ var _a;
2620
+ const newOption = { silent: true, sortOption: null, sort: true, append: false, ...option };
2621
+ const colKeyGenValue = colKeyGen.value;
2622
+ let column;
2623
+ if (order) {
2624
+ column = newOption.sortOption || tableHeaderLast.value.find((it) => colKeyGenValue(it) === colKey);
2625
+ if (column) {
2626
+ const newState = {
2627
+ key: colKey,
2628
+ dataIndex: column.dataIndex,
2629
+ sortField: column.sortField,
2630
+ sortType: column.sortType,
2631
+ order
2632
+ };
2633
+ const mode = newOption.append && isMultiSort.value ? 1 : 0;
2634
+ addOrUpdateSortState(newState, mode);
2635
+ }
2636
+ } else {
2637
+ sortStates.value = [];
2638
+ }
2639
+ if (newOption.sort && ((_a = dataSourceCopy.value) == null ? void 0 : _a.length)) {
2640
+ if (!props.sortRemote || newOption.force) {
2641
+ initDataSource(props.dataSource, { forceSort: newOption.force });
2642
+ }
2643
+ }
2644
+ if (!newOption.silent) {
2645
+ if (!column) {
2646
+ column = newOption.sortOption || tableHeaderLast.value.find((it) => colKeyGenValue(it) === colKey);
2647
+ }
2648
+ if (column) {
2649
+ emits("sort-change", column, order, toRaw(dataSourceCopy.value), props.sortConfig);
2650
+ } else {
2651
+ console.warn("Can not find column by key:", colKey);
2652
+ }
2653
+ }
2654
+ return dataSourceCopy.value;
2655
+ }
2656
+ function resetSorter() {
2657
+ sortStates.value = [];
2658
+ initDataSource();
2659
+ }
2660
+ function getSortColumns() {
2661
+ return sortStates.value.map((s) => ({ key: s.key || s.dataIndex, order: s.order }));
2662
+ }
2663
+ function dealDefaultSorter() {
2664
+ if (!props.sortConfig.defaultSort) return;
2665
+ const { key, dataIndex, order, silent } = { silent: false, ...props.sortConfig.defaultSort };
2666
+ setSorter(key || dataIndex, order, { force: false, silent });
2667
+ }
2668
+ return [sortStates, sortCol, onColumnSort, setSorter, resetSorter, getSortColumns, dealDefaultSorter, getColumnSortState, sortData];
2669
+ }
2508
2670
  const _hoisted_1 = ["tabindex"];
2509
2671
  const _hoisted_2 = { class: "stk-table-scroll-container" };
2510
2672
  const _hoisted_3 = { key: 0 };
@@ -2622,14 +2784,6 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2622
2784
  const currentSelectedCellKey = ref();
2623
2785
  let currentHoverRow = null;
2624
2786
  const currentHoverRowKey = ref(null);
2625
- const sortSwitchOrder = [null, "desc", "asc"];
2626
- const isMultiSort = computed(() => props.sortConfig.multiSort ?? false);
2627
- const multiSortLimit = computed(() => props.sortConfig.multiSortLimit ?? 3);
2628
- const sortStates = ref([]);
2629
- const sortCol = computed(() => {
2630
- var _a;
2631
- return (_a = sortStates.value[0]) == null ? void 0 : _a.dataIndex;
2632
- });
2633
2787
  const [tableHeaders, tableHeadersForCalc, dealColumns] = useTableColumns(props.virtualX, isRelativeMode);
2634
2788
  const filterStatus = ref({});
2635
2789
  const tableHeaderLast = computed(() => tableHeadersForCalc.value.slice(-1)[0] || []);
@@ -2700,6 +2854,14 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2700
2854
  return (_b = props.experimental) == null ? void 0 : _b.scrollY;
2701
2855
  });
2702
2856
  const rowKeyGenCache = /* @__PURE__ */ new WeakMap();
2857
+ const [sortStates, sortCol, onColumnSort, setSorter, resetSorter, getSortColumns, dealDefaultSorter, getColumnSortState, sortData] = useSorter(
2858
+ props,
2859
+ emits,
2860
+ colKeyGen,
2861
+ tableHeaderLast,
2862
+ dataSourceCopy,
2863
+ initDataSource
2864
+ );
2703
2865
  const [isSRBRActive] = useScrollRowByRow(props, tableContainerRef);
2704
2866
  const [onThDragStart, onThDragOver, onThDrop, isHeaderDraggable] = useThDrag(props, emits, colKeyGen);
2705
2867
  const [onTrDragStart, onTrDragEnter, onTrDragOver, onTrDrop, onTrDragEnd] = useTrDrag(props, emits, dataSourceCopy);
@@ -2852,14 +3014,13 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2852
3014
  });
2853
3015
  function initDataSource(v = props.dataSource, option) {
2854
3016
  let dataSourceTemp = v.slice();
3017
+ if (!props.sortRemote || (option == null ? void 0 : option.forceSort)) {
3018
+ dataSourceTemp = sortData(dataSourceTemp);
3019
+ }
2855
3020
  if (isTreeData.value) {
2856
3021
  dataSourceTemp = flatTreeData(dataSourceTemp);
2857
3022
  }
2858
3023
  dataSourceTemp = filterDataSource(dataSourceTemp);
2859
- if ((!props.sortRemote || (option == null ? void 0 : option.forceSort)) && sortStates.value.length) {
2860
- const sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig };
2861
- dataSourceTemp = execMultiSort(dataSourceTemp, sortConfig);
2862
- }
2863
3024
  dataSourceCopy.value = dataSourceTemp;
2864
3025
  }
2865
3026
  function setFilter(status, option) {
@@ -2881,11 +3042,6 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2881
3042
  });
2882
3043
  });
2883
3044
  }
2884
- function dealDefaultSorter() {
2885
- if (!props.sortConfig.defaultSort) return;
2886
- const { key, dataIndex, order, silent } = { silent: false, ...props.sortConfig.defaultSort };
2887
- setSorter(key || dataIndex, order, { force: false, silent });
2888
- }
2889
3045
  function handleDealColumns() {
2890
3046
  dealColumns(props.columns);
2891
3047
  }
@@ -3068,99 +3224,6 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3068
3224
  ...mergeCellsWrapper(row, col, rowIndex, colIndex)
3069
3225
  };
3070
3226
  }
3071
- function getColumnSortState(colKey) {
3072
- return sortStates.value[getSortStateIndex(colKey)];
3073
- }
3074
- function getSortStateIndex(colKey) {
3075
- return sortStates.value.findIndex((s) => s.key === colKey || s.dataIndex === colKey);
3076
- }
3077
- function addOrUpdateSortState(newState, mode) {
3078
- const existingIndex = sortStates.value.findIndex((s) => s.key === newState.key || s.dataIndex === newState.dataIndex);
3079
- if (existingIndex >= 0) {
3080
- sortStates.value.splice(existingIndex, 1);
3081
- }
3082
- if (mode && isMultiSort.value) {
3083
- if (sortStates.value.length >= multiSortLimit.value) {
3084
- sortStates.value.pop();
3085
- }
3086
- sortStates.value.unshift(newState);
3087
- } else {
3088
- sortStates.value = [newState];
3089
- }
3090
- }
3091
- function updateSortState(col, colKey) {
3092
- const existingIndex = getSortStateIndex(colKey);
3093
- let newOrder;
3094
- if (existingIndex >= 0) {
3095
- const currentOrder = sortStates.value[existingIndex].order;
3096
- const currentIndex = sortSwitchOrder.indexOf(currentOrder);
3097
- newOrder = sortSwitchOrder[(currentIndex + 1) % 3];
3098
- if (newOrder === null) {
3099
- sortStates.value.splice(existingIndex, 1);
3100
- } else {
3101
- const updatedState = { ...sortStates.value[existingIndex], order: newOrder };
3102
- addOrUpdateSortState(updatedState, 1);
3103
- }
3104
- } else {
3105
- newOrder = sortSwitchOrder[1];
3106
- const newState = {
3107
- key: colKey,
3108
- dataIndex: col.dataIndex,
3109
- sortField: col.sortField,
3110
- sortType: col.sortType,
3111
- order: newOrder
3112
- };
3113
- addOrUpdateSortState(newState, 1);
3114
- }
3115
- return newOrder;
3116
- }
3117
- function execMultiSort(dataSource, sortConfig) {
3118
- let result = dataSource.slice();
3119
- for (let i = sortStates.value.length - 1; i >= 0; i--) {
3120
- const state = sortStates.value[i];
3121
- const col = tableHeaderLast.value.find((c) => state.key && colKeyGen.value(c) === state.key || c.dataIndex === state.dataIndex);
3122
- if (col && state.order) {
3123
- const colSortConfig = { ...sortConfig, ...col.sortConfig };
3124
- result = tableSort(col, state.order, result, colSortConfig);
3125
- }
3126
- }
3127
- return result;
3128
- }
3129
- function onColumnSort(col) {
3130
- if (!col) {
3131
- console.warn("onColumnSort: not found col:", col);
3132
- return;
3133
- }
3134
- if (!col.sorter) {
3135
- return;
3136
- }
3137
- const colKey = colKeyGen.value(col);
3138
- let order = updateSortState(col, colKey);
3139
- let sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig, ...col.sortConfig };
3140
- if (!order && sortConfig.defaultSort) {
3141
- const defaultColKey = sortConfig.defaultSort.key || sortConfig.defaultSort.dataIndex;
3142
- if (defaultColKey) {
3143
- const defaultCol = tableHeaderLast.value.find((item) => colKeyGen.value(item) === defaultColKey);
3144
- if (defaultCol) {
3145
- col = defaultCol;
3146
- order = sortConfig.defaultSort.order;
3147
- if (order) {
3148
- addOrUpdateSortState({
3149
- key: defaultColKey,
3150
- dataIndex: defaultCol.dataIndex,
3151
- sortField: defaultCol.sortField,
3152
- sortType: defaultCol.sortType,
3153
- order
3154
- });
3155
- }
3156
- }
3157
- }
3158
- }
3159
- if (!props.sortRemote) {
3160
- initDataSource();
3161
- }
3162
- emits("sort-change", col, order, toRaw(dataSourceCopy.value), sortConfig);
3163
- }
3164
3227
  function onRowClick(e) {
3165
3228
  var _a, _b;
3166
3229
  const rowIndex = getClosestTrIndex(e.target);
@@ -3300,7 +3363,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3300
3363
  const { scrollTop, scrollLeft } = e.target;
3301
3364
  const { scrollTop: vScrollTop } = virtualScroll.value;
3302
3365
  const { scrollLeft: vScrollLeft } = virtualScrollX.value;
3303
- const isYScroll = scrollTop !== vScrollTop;
3366
+ const isYScroll = isExperimentalScrollY.value ? false : scrollTop !== vScrollTop;
3304
3367
  const isXScroll = scrollLeft !== vScrollLeft;
3305
3368
  if (isYScroll) {
3306
3369
  updateVirtualScrollY(scrollTop);
@@ -3408,48 +3471,6 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3408
3471
  );
3409
3472
  }
3410
3473
  }
3411
- function setSorter(colKey, order, option = {}) {
3412
- var _a;
3413
- const newOption = { silent: true, sortOption: null, sort: true, append: false, ...option };
3414
- const colKeyGenValue = colKeyGen.value;
3415
- let column;
3416
- if (order) {
3417
- column = newOption.sortOption || tableHeaderLast.value.find((it) => colKeyGenValue(it) === colKey);
3418
- if (column) {
3419
- const newState = {
3420
- key: colKey,
3421
- dataIndex: column.dataIndex,
3422
- sortField: column.sortField,
3423
- sortType: column.sortType,
3424
- order
3425
- };
3426
- const mode = newOption.append && isMultiSort.value ? 1 : 0;
3427
- addOrUpdateSortState(newState, mode);
3428
- }
3429
- } else {
3430
- sortStates.value = [];
3431
- }
3432
- if (newOption.sort && ((_a = dataSourceCopy.value) == null ? void 0 : _a.length)) {
3433
- if (!props.sortRemote || newOption.force) {
3434
- initDataSource(props.dataSource, { forceSort: newOption.force });
3435
- }
3436
- }
3437
- if (!newOption.silent) {
3438
- if (!column) {
3439
- column = newOption.sortOption || tableHeaderLast.value.find((it) => colKeyGenValue(it) === colKey);
3440
- }
3441
- if (column) {
3442
- emits("sort-change", column, order, toRaw(dataSourceCopy.value), props.sortConfig);
3443
- } else {
3444
- console.warn("Can not find column by key:", colKey);
3445
- }
3446
- }
3447
- return dataSourceCopy.value;
3448
- }
3449
- function resetSorter() {
3450
- sortStates.value = [];
3451
- initDataSource();
3452
- }
3453
3474
  function scrollTo(top = 0, left = 0) {
3454
3475
  if (!tableContainerRef.value) return;
3455
3476
  if (top !== null) tableContainerRef.value.scrollTop = top;
@@ -3458,9 +3479,6 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3458
3479
  function getTableData() {
3459
3480
  return toRaw(dataSourceCopy.value);
3460
3481
  }
3461
- function getSortColumns() {
3462
- return sortStates.value.map((s) => ({ key: s.key || s.dataIndex, order: s.order }));
3463
- }
3464
3482
  __expose({
3465
3483
  /**
3466
3484
  * 重新计算虚拟列表宽高
package/lib/style.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: stk-table-vue
3
- * version: v0.11.3
3
+ * version: v0.11.4
4
4
  * description: High performance realtime virtual table for vue3 and vue2.7
5
5
  * author: japlus
6
6
  * homepage: https://ja-plus.github.io/stk-table-vue/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.11.3",
3
+ "version": "0.11.5",
4
4
  "description": "High performance 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",
@@ -283,8 +283,6 @@ import {
283
283
  RowActiveOption,
284
284
  SeqConfig,
285
285
  SortConfig,
286
- SortOption,
287
- SortState,
288
286
  StkTableColumn,
289
287
  TagType,
290
288
  TreeConfig,
@@ -309,8 +307,9 @@ import { useTrDrag } from './useTrDrag';
309
307
  import { useTree } from './useTree';
310
308
  import { useVirtualScroll } from './useVirtualScroll';
311
309
  import { useWheeling } from './useWheeling';
310
+ import { useSorter } from './useSorter';
312
311
  import { createStkTableId, getCalculatedColWidth } from './utils/constRefUtils';
313
- import { getClosestColKey, getClosestTr, getClosestTrIndex, isEmptyValue, rafThrottle, tableSort, transformWidthToStr } from './utils/index';
312
+ import { getClosestColKey, getClosestTr, getClosestTrIndex, rafThrottle, transformWidthToStr } from './utils/index';
314
313
  import { useAreaSelection } from './features';
315
314
 
316
315
  /** Generic stands for DataType */
@@ -730,24 +729,6 @@ const currentHoverRowKey = ref<UniqKey | null>(null);
730
729
  /** 当前hover的列的key */
731
730
  // const currentColHoverKey = ref(null);
732
731
 
733
- /** 排序切换顺序 */
734
- const sortSwitchOrder: Order[] = [null, 'desc', 'asc'] as const;
735
-
736
- /** 是否启用多列排序 */
737
- const isMultiSort = computed(() => props.sortConfig.multiSort ?? false);
738
-
739
- /** 多列排序限制 */
740
- const multiSortLimit = computed(() => props.sortConfig.multiSortLimit ?? 3);
741
-
742
- /**
743
- * 多列排序状态数组
744
- * 优先级按数组顺序,越靠前优先级越高(先点击的优先级高)
745
- */
746
- const sortStates = ref<SortState<DT>[]>([]);
747
-
748
- /** 对外暴露:当前排序的列 key(只读计算属性) */
749
- const sortCol = computed<keyof DT | undefined>(() => sortStates.value[0]?.dataIndex);
750
-
751
732
  const [tableHeaders, tableHeadersForCalc, dealColumns] = useTableColumns<DT>(props.virtualX, isRelativeMode);
752
733
 
753
734
  const filterStatus = ref<Record<UniqKey, FilterStatus>>({});
@@ -834,6 +815,16 @@ const isExperimentalScrollY = computed(() => {
834
815
 
835
816
  const rowKeyGenCache = new WeakMap();
836
817
 
818
+ // 使用 useSorter hook 管理排序逻辑
819
+ const [sortStates, sortCol, onColumnSort, setSorter, resetSorter, getSortColumns, dealDefaultSorter, getColumnSortState, sortData] = useSorter<DT>(
820
+ props,
821
+ emits,
822
+ colKeyGen,
823
+ tableHeaderLast,
824
+ dataSourceCopy,
825
+ initDataSource,
826
+ );
827
+
837
828
  const [isSRBRActive] = useScrollRowByRow(props, tableContainerRef);
838
829
 
839
830
  const [onThDragStart, onThDragOver, onThDrop, isHeaderDraggable] = useThDrag(props, emits, colKeyGen);
@@ -1016,15 +1007,16 @@ onMounted(() => {
1016
1007
 
1017
1008
  function initDataSource(v = props.dataSource, option?: { forceSort?: boolean }) {
1018
1009
  let dataSourceTemp = v.slice(); // shallow copy
1010
+
1011
+ // 排序(tableSort 内部会根据 sortChildren 自动处理树形递归排序)
1012
+ if (!props.sortRemote || option?.forceSort) {
1013
+ dataSourceTemp = sortData(dataSourceTemp);
1014
+ }
1015
+
1019
1016
  if (isTreeData.value) {
1020
- // only tree data need flat
1021
1017
  dataSourceTemp = flatTreeData(dataSourceTemp);
1022
1018
  }
1023
1019
  dataSourceTemp = filterDataSource(dataSourceTemp);
1024
- if ((!props.sortRemote || option?.forceSort) && sortStates.value.length) {
1025
- const sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig };
1026
- dataSourceTemp = execMultiSort(dataSourceTemp, sortConfig);
1027
- }
1028
1020
  dataSourceCopy.value = dataSourceTemp;
1029
1021
  }
1030
1022
 
@@ -1054,12 +1046,6 @@ function filterDataSource(dataSource: DT[]) {
1054
1046
  });
1055
1047
  }
1056
1048
 
1057
- function dealDefaultSorter() {
1058
- if (!props.sortConfig.defaultSort) return;
1059
- const { key, dataIndex, order, silent } = { silent: false, ...props.sortConfig.defaultSort };
1060
- setSorter((key || dataIndex) as string, order, { force: false, silent });
1061
- }
1062
-
1063
1049
  /**
1064
1050
  * Wrapper for dealColumns to pass props.columns
1065
1051
  */
@@ -1277,144 +1263,6 @@ function getTDProps(row: PrivateRowDT | null | undefined, col: StkTableColumn<Pr
1277
1263
  };
1278
1264
  }
1279
1265
 
1280
- function getColumnSortState(colKey: UniqKey): SortState<DT> | undefined {
1281
- return sortStates.value[getSortStateIndex(colKey)];
1282
- }
1283
- /**
1284
- * 获取列的排序状态索引
1285
- */
1286
- function getSortStateIndex(colKey: UniqKey): number {
1287
- return sortStates.value.findIndex(s => s.key === colKey || s.dataIndex === colKey);
1288
- }
1289
-
1290
- /**
1291
- * 添加或更新排序状态到 sortStates
1292
- * @param newState 新的排序状态
1293
- * @param mode '1' - 追加模式(多列排序),0 - 替换模式(单列排序)
1294
- */
1295
- function addOrUpdateSortState(newState: SortState<DT>, mode?: 1 | 0) {
1296
- const existingIndex = sortStates.value.findIndex(s => s.key === newState.key || s.dataIndex === newState.dataIndex);
1297
-
1298
- if (existingIndex >= 0) {
1299
- // 移除已存在的相同列
1300
- sortStates.value.splice(existingIndex, 1);
1301
- }
1302
-
1303
- if (mode && isMultiSort.value) {
1304
- // 多列排序模式:检查数量限制,然后添加到最前面
1305
- if (sortStates.value.length >= multiSortLimit.value) {
1306
- sortStates.value.pop();
1307
- }
1308
- sortStates.value.unshift(newState);
1309
- } else {
1310
- sortStates.value = [newState];
1311
- }
1312
- }
1313
-
1314
- function updateSortState(col: StkTableColumn<DT>, colKey: UniqKey): Order {
1315
- const existingIndex = getSortStateIndex(colKey);
1316
- let newOrder: Order;
1317
-
1318
- if (existingIndex >= 0) {
1319
- // 已存在该列的排序,切换排序顺序
1320
- const currentOrder = sortStates.value[existingIndex].order;
1321
- const currentIndex = sortSwitchOrder.indexOf(currentOrder);
1322
- newOrder = sortSwitchOrder[(currentIndex + 1) % 3];
1323
-
1324
- if (newOrder === null) {
1325
- // 取消排序,从数组中移除
1326
- sortStates.value.splice(existingIndex, 1);
1327
- } else {
1328
- // 更新排序顺序
1329
- const updatedState = { ...sortStates.value[existingIndex], order: newOrder };
1330
- addOrUpdateSortState(updatedState, 1);
1331
- }
1332
- } else {
1333
- newOrder = sortSwitchOrder[1];
1334
-
1335
- const newState: SortState<DT> = {
1336
- key: colKey,
1337
- dataIndex: col.dataIndex,
1338
- sortField: col.sortField,
1339
- sortType: col.sortType,
1340
- order: newOrder,
1341
- };
1342
-
1343
- addOrUpdateSortState(newState, 1);
1344
- }
1345
-
1346
- return newOrder;
1347
- }
1348
-
1349
- /**
1350
- * 执行多列排序
1351
- */
1352
- function execMultiSort(dataSource: DT[], sortConfig: SortConfig<DT>): DT[] {
1353
- let result = dataSource.slice();
1354
-
1355
- // 从后往前排序,这样前面的排序优先级更高
1356
- for (let i = sortStates.value.length - 1; i >= 0; i--) {
1357
- const state = sortStates.value[i];
1358
- const col = tableHeaderLast.value.find(c => (state.key && colKeyGen.value(c) === state.key) || c.dataIndex === state.dataIndex);
1359
- if (col && state.order) {
1360
- const colSortConfig = { ...sortConfig, ...col.sortConfig };
1361
- result = tableSort(col, state.order, result, colSortConfig);
1362
- }
1363
- }
1364
-
1365
- return result;
1366
- }
1367
-
1368
- /**
1369
- * 表头点击排序
1370
- *
1371
- * en: Sort a column
1372
- * @param click 是否为点击表头触发
1373
- * @param options.force sort-remote 开启后是否强制排序
1374
- * @param options.emit 是否触发回调
1375
- */
1376
- function onColumnSort(col: StkTableColumn<DT> | undefined | null) {
1377
- if (!col) {
1378
- console.warn('onColumnSort: not found col:', col);
1379
- return;
1380
- }
1381
- if (!col.sorter) {
1382
- // 点击表头触发的排序,如果列没有配置sorter则不处理。setSorter 触发的排序则保持通行。
1383
- return;
1384
- }
1385
- const colKey = colKeyGen.value(col);
1386
-
1387
- let order = updateSortState(col, colKey);
1388
- let sortConfig: SortConfig<DT> = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig, ...col.sortConfig };
1389
-
1390
- // 处理 defaultSort(当取消排序时)
1391
- if (!order && sortConfig.defaultSort) {
1392
- const defaultColKey = sortConfig.defaultSort.key || sortConfig.defaultSort.dataIndex;
1393
- if (defaultColKey) {
1394
- const defaultCol = tableHeaderLast.value.find(item => colKeyGen.value(item) === defaultColKey);
1395
- if (defaultCol) {
1396
- col = defaultCol;
1397
- order = sortConfig.defaultSort.order;
1398
- if (order) {
1399
- addOrUpdateSortState({
1400
- key: defaultColKey,
1401
- dataIndex: defaultCol.dataIndex,
1402
- sortField: defaultCol.sortField,
1403
- sortType: defaultCol.sortType,
1404
- order,
1405
- });
1406
- }
1407
- }
1408
- }
1409
- }
1410
-
1411
- if (!props.sortRemote) {
1412
- initDataSource();
1413
- }
1414
-
1415
- emits('sort-change', col, order, toRaw(dataSourceCopy.value), sortConfig);
1416
- }
1417
-
1418
1266
  function onRowClick(e: MouseEvent) {
1419
1267
  const rowIndex = getClosestTrIndex(e.target as HTMLElement);
1420
1268
  const row = dataSourceCopy.value[rowIndex];
@@ -1591,7 +1439,9 @@ function onTableScroll(e: Event) {
1591
1439
  const { scrollTop, scrollLeft } = e.target as HTMLElement;
1592
1440
  const { scrollTop: vScrollTop } = virtualScroll.value;
1593
1441
  const { scrollLeft: vScrollLeft } = virtualScrollX.value;
1594
- const isYScroll = scrollTop !== vScrollTop;
1442
+ // experimental.scrollY 模式下,纵向滚动通过 transform 模拟,DOM scrollTop 始终为 0,
1443
+ // 不能用来判断纵向滚动,否则横向滚动时会误触发纵向滚动重置
1444
+ const isYScroll = isExperimentalScrollY.value ? false : scrollTop !== vScrollTop;
1595
1445
  const isXScroll = scrollLeft !== vScrollLeft;
1596
1446
 
1597
1447
  if (isYScroll) {
@@ -1713,69 +1563,6 @@ function setSelectedCell(row?: DT, col?: StkTableColumn<DT>, option = { silent:
1713
1563
  }
1714
1564
  }
1715
1565
 
1716
- /**
1717
- * 设置表头排序状态。
1718
- * @param colKey 列唯一键字段。如果你想要取消排序状态,请使用`resetSorter`
1719
- * @param order 正序倒序
1720
- * @param option.sortOption 指定排序参数。同 StkTableColumn 中排序相关字段。建议从columns中find得到。
1721
- * @param option.sort 是否触发排序-默认true
1722
- * @param option.silent 是否禁止触发回调-默认true
1723
- * @param option.force 是否触发排序-默认true
1724
- * @param option.append 是否追加排序(多列排序模式)。为true时保留现有排序状态,将新排序添加到最前面;为false时替换所有排序状态
1725
- * @return 表格数据
1726
- */
1727
- function setSorter(
1728
- colKey: string,
1729
- order: Order,
1730
- option: { sortOption?: SortOption<DT>; force?: boolean; silent?: boolean; sort?: boolean; append?: boolean } = {},
1731
- ) {
1732
- const newOption = { silent: true, sortOption: null, sort: true, append: false, ...option };
1733
- const colKeyGenValue = colKeyGen.value;
1734
- let column: StkTableColumn<DT> | undefined;
1735
-
1736
- if (order) {
1737
- column = newOption.sortOption || tableHeaderLast.value.find(it => colKeyGenValue(it) === colKey);
1738
- if (column) {
1739
- const newState: SortState<DT> = {
1740
- key: colKey,
1741
- dataIndex: column.dataIndex,
1742
- sortField: column.sortField,
1743
- sortType: column.sortType,
1744
- order,
1745
- };
1746
-
1747
- const mode = newOption.append && isMultiSort.value ? 1 : 0;
1748
- addOrUpdateSortState(newState, mode);
1749
- }
1750
- } else {
1751
- sortStates.value = [];
1752
- }
1753
-
1754
- if (newOption.sort && dataSourceCopy.value?.length) {
1755
- if (!props.sortRemote || newOption.force) {
1756
- initDataSource(props.dataSource, { forceSort: newOption.force });
1757
- }
1758
- }
1759
-
1760
- if (!newOption.silent) {
1761
- if (!column) {
1762
- column = newOption.sortOption || tableHeaderLast.value.find(it => colKeyGenValue(it) === colKey);
1763
- }
1764
- if (column) {
1765
- emits('sort-change', column, order, toRaw(dataSourceCopy.value), props.sortConfig);
1766
- } else {
1767
- console.warn('Can not find column by key:', colKey);
1768
- }
1769
- }
1770
-
1771
- return dataSourceCopy.value;
1772
- }
1773
-
1774
- function resetSorter() {
1775
- sortStates.value = [];
1776
- initDataSource();
1777
- }
1778
-
1779
1566
  /**
1780
1567
  * set scroll bar position
1781
1568
  * @param top null to not change
@@ -1792,14 +1579,6 @@ function getTableData() {
1792
1579
  return toRaw(dataSourceCopy.value);
1793
1580
  }
1794
1581
 
1795
- /**
1796
- * get current sort info
1797
- * @return {{key:string,order:Order}[]}
1798
- */
1799
- function getSortColumns(): { key: keyof DT | undefined; order: Order }[] {
1800
- return sortStates.value.map(s => ({ key: s.key || s.dataIndex, order: s.order }));
1801
- }
1802
-
1803
1582
  defineExpose({
1804
1583
  /**
1805
1584
  * 重新计算虚拟列表宽高
@@ -0,0 +1,262 @@
1
+ import { computed, ref, toRaw, type Ref } from 'vue';
2
+ import { DEFAULT_SORT_CONFIG } from './const';
3
+ import type { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from './types/index';
4
+ import { tableSort } from './utils/index';
5
+
6
+ /**
7
+ * 排序切换顺序
8
+ */
9
+ const SORT_SWITCH_ORDER: Order[] = [null, 'desc', 'asc'] as const;
10
+
11
+ /**
12
+ * 排序 Hook
13
+ * 管理表格排序状态和相关操作
14
+ * @param props 表格 props
15
+ * @param colKeyGen 列 key 生成函数
16
+ * @param tableHeaderLast 表头最后一行(叶子节点)
17
+ * @param dataSourceCopy 数据源副本 ref
18
+ * @param initDataSource 初始化数据源函数
19
+ * @param emits 事件发射函数
20
+ * @returns 排序相关状态和方法
21
+ */
22
+ export function useSorter<DT extends Record<string, any>>(
23
+ props: any,
24
+ emits: any,
25
+ colKeyGen: Ref<(col: StkTableColumn<DT>) => string>,
26
+ tableHeaderLast: Ref<StkTableColumn<DT>[]>,
27
+ dataSourceCopy: Ref<DT[]>,
28
+ initDataSource: (data?: DT[], option?: { forceSort?: boolean }) => void,
29
+ ) {
30
+ /** 多列排序状态数组 */
31
+ const sortStates = ref<SortState<DT>[]>([]);
32
+
33
+ /** 是否启用多列排序 */
34
+ const isMultiSort = computed(() => props.sortConfig.multiSort ?? false);
35
+
36
+ /** 多列排序限制 */
37
+ const multiSortLimit = computed(() => props.sortConfig.multiSortLimit ?? 3);
38
+
39
+ /** 对外暴露:当前排序的列 key(只读计算属性) */
40
+ const sortCol = computed<keyof DT | undefined>(() => sortStates.value[0]?.dataIndex);
41
+
42
+ /**
43
+ * 获取列的排序状态
44
+ */
45
+ function getColumnSortState(colKey: UniqKey): SortState<DT> | undefined {
46
+ return sortStates.value[getSortStateIndex(colKey)] as SortState<DT> | undefined;
47
+ }
48
+
49
+ /**
50
+ * 获取列的排序状态索引
51
+ */
52
+ function getSortStateIndex(colKey: UniqKey): number {
53
+ return sortStates.value.findIndex(s => s.key === colKey || s.dataIndex === colKey);
54
+ }
55
+
56
+ /**
57
+ * 添加或更新排序状态到 sortStates
58
+ * @param newState 新的排序状态
59
+ * @param mode '1' - 追加模式(多列排序),0 - 替换模式(单列排序)
60
+ */
61
+ function addOrUpdateSortState(newState: SortState<DT>, mode?: 1 | 0) {
62
+ const existingIndex = sortStates.value.findIndex(s => s.key === newState.key || s.dataIndex === newState.dataIndex);
63
+
64
+ if (existingIndex >= 0) {
65
+ // 移除已存在的相同列
66
+ sortStates.value.splice(existingIndex, 1);
67
+ }
68
+
69
+ if (mode && isMultiSort.value) {
70
+ // 多列排序模式:检查数量限制,然后添加到最前面
71
+ if (sortStates.value.length >= multiSortLimit.value) {
72
+ sortStates.value.pop();
73
+ }
74
+ sortStates.value.unshift(newState as any);
75
+ } else {
76
+ sortStates.value = [newState as any];
77
+ }
78
+ }
79
+
80
+ /**
81
+ * 更新排序状态(点击表头时调用)
82
+ */
83
+ function updateSortState(col: StkTableColumn<DT>, colKey: UniqKey): Order {
84
+ const existingIndex = getSortStateIndex(colKey);
85
+ let newOrder: Order;
86
+
87
+ if (existingIndex >= 0) {
88
+ // 已存在该列的排序,切换排序顺序
89
+ const currentOrder = sortStates.value[existingIndex].order;
90
+ const currentIndex = SORT_SWITCH_ORDER.indexOf(currentOrder);
91
+ newOrder = SORT_SWITCH_ORDER[(currentIndex + 1) % 3];
92
+
93
+ if (newOrder === null) {
94
+ // 取消排序,从数组中移除
95
+ sortStates.value.splice(existingIndex, 1);
96
+ } else {
97
+ // 更新排序顺序
98
+ const updatedState = { ...sortStates.value[existingIndex], order: newOrder };
99
+ addOrUpdateSortState(updatedState as any, 1);
100
+ }
101
+ } else {
102
+ newOrder = SORT_SWITCH_ORDER[1];
103
+
104
+ const newState: SortState<DT> = {
105
+ key: colKey,
106
+ dataIndex: col.dataIndex,
107
+ sortField: col.sortField,
108
+ sortType: col.sortType,
109
+ order: newOrder,
110
+ };
111
+
112
+ addOrUpdateSortState(newState, 1);
113
+ }
114
+
115
+ return newOrder;
116
+ }
117
+
118
+ /**
119
+ * 对数据源执行排序
120
+ * tableSort 内部会根据 sortChildren 配置自动处理树形递归排序
121
+ */
122
+ function sortData(dataSource: DT[]): DT[] {
123
+ if (!sortStates.value.length) return dataSource;
124
+
125
+ const sortConfig = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig };
126
+ let result = dataSource.slice();
127
+
128
+ // 从后往前排序,这样前面的排序优先级更高
129
+ for (let i = sortStates.value.length - 1; i >= 0; i--) {
130
+ const state = sortStates.value[i];
131
+ const col = tableHeaderLast.value.find(c => (state.key && colKeyGen.value(c) === state.key) || c.dataIndex === state.dataIndex);
132
+ if (col && state.order) {
133
+ const colSortConfig = { ...sortConfig, ...col.sortConfig };
134
+ result = tableSort(col, state.order, result, colSortConfig);
135
+ }
136
+ }
137
+
138
+ return result;
139
+ }
140
+
141
+ /**
142
+ * 表头点击排序
143
+ */
144
+ function onColumnSort(col: StkTableColumn<DT> | undefined | null) {
145
+ if (!col) {
146
+ console.warn('onColumnSort: not found col:', col);
147
+ return;
148
+ }
149
+ if (!col.sorter) {
150
+ // 点击表头触发的排序,如果列没有配置sorter则不处理。setSorter 触发的排序则保持通行。
151
+ return;
152
+ }
153
+ const colKey = colKeyGen.value(col);
154
+
155
+ let order = updateSortState(col, colKey);
156
+ const sortConfig: SortConfig<DT> = { ...DEFAULT_SORT_CONFIG, ...props.sortConfig, ...col.sortConfig };
157
+
158
+ // 处理 defaultSort(当取消排序时)
159
+ if (!order && sortConfig.defaultSort) {
160
+ const defaultColKey = sortConfig.defaultSort.key || sortConfig.defaultSort.dataIndex;
161
+ if (defaultColKey) {
162
+ const defaultCol = tableHeaderLast.value.find(item => colKeyGen.value(item) === defaultColKey);
163
+ if (defaultCol) {
164
+ col = defaultCol;
165
+ order = sortConfig.defaultSort.order;
166
+ if (order) {
167
+ addOrUpdateSortState({
168
+ key: defaultColKey,
169
+ dataIndex: defaultCol.dataIndex,
170
+ sortField: defaultCol.sortField,
171
+ sortType: defaultCol.sortType,
172
+ order,
173
+ });
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ if (!props.sortRemote) {
180
+ initDataSource();
181
+ }
182
+
183
+ emits('sort-change', col, order, toRaw(dataSourceCopy.value), sortConfig);
184
+ }
185
+
186
+ /**
187
+ * 设置表头排序状态
188
+ */
189
+ function setSorter(
190
+ colKey: string,
191
+ order: Order,
192
+ option: { sortOption?: SortOption<DT>; force?: boolean; silent?: boolean; sort?: boolean; append?: boolean } = {},
193
+ ): DT[] {
194
+ const newOption = { silent: true, sortOption: null, sort: true, append: false, ...option };
195
+ const colKeyGenValue = colKeyGen.value;
196
+ let column: StkTableColumn<DT> | undefined;
197
+
198
+ if (order) {
199
+ column = newOption.sortOption || tableHeaderLast.value.find(it => colKeyGenValue(it) === colKey);
200
+ if (column) {
201
+ const newState: SortState<DT> = {
202
+ key: colKey,
203
+ dataIndex: column.dataIndex,
204
+ sortField: column.sortField,
205
+ sortType: column.sortType,
206
+ order,
207
+ };
208
+
209
+ const mode = newOption.append && isMultiSort.value ? 1 : 0;
210
+ addOrUpdateSortState(newState, mode);
211
+ }
212
+ } else {
213
+ sortStates.value = [];
214
+ }
215
+
216
+ if (newOption.sort && dataSourceCopy.value?.length) {
217
+ if (!props.sortRemote || newOption.force) {
218
+ initDataSource(props.dataSource, { forceSort: newOption.force });
219
+ }
220
+ }
221
+
222
+ if (!newOption.silent) {
223
+ if (!column) {
224
+ column = newOption.sortOption || tableHeaderLast.value.find(it => colKeyGenValue(it) === colKey);
225
+ }
226
+ if (column) {
227
+ emits('sort-change', column, order, toRaw(dataSourceCopy.value), props.sortConfig);
228
+ } else {
229
+ console.warn('Can not find column by key:', colKey);
230
+ }
231
+ }
232
+
233
+ return dataSourceCopy.value;
234
+ }
235
+
236
+ /**
237
+ * 重置排序器
238
+ */
239
+ function resetSorter() {
240
+ sortStates.value = [];
241
+ initDataSource();
242
+ }
243
+
244
+ /**
245
+ * 获取排序列信息
246
+ */
247
+ function getSortColumns(): { key: keyof DT | undefined; order: Order }[] {
248
+ return sortStates.value.map(s => ({ key: s.key || s.dataIndex, order: s.order }));
249
+ }
250
+
251
+ /**
252
+ * 处理默认排序
253
+ */
254
+ function dealDefaultSorter() {
255
+ if (!props.sortConfig.defaultSort) return;
256
+ const { key, dataIndex, order, silent } = { silent: false, ...props.sortConfig.defaultSort };
257
+ setSorter((key || dataIndex) as string, order, { force: false, silent });
258
+ }
259
+
260
+ // 只返回需要在组件外部使用的方法和状态
261
+ return [sortStates, sortCol, onColumnSort, setSorter, resetSorter, getSortColumns, dealDefaultSorter, getColumnSortState, sortData] as const;
262
+ }
@@ -212,7 +212,7 @@ export function useVirtualScroll<DT extends Record<string, any>>(
212
212
  const headerToBodyRowHeightCount = Math.floor(tableHeaderHeight.value / rowHeight);
213
213
  pageSize -= headerToBodyRowHeightCount; //减去表头行数
214
214
  }
215
- const maxScrollTop = dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight;
215
+ const maxScrollTop = Math.max(0, dataSourceCopy.value.length * rowHeight + tableHeaderHeight.value - containerHeight);
216
216
  if (scrollTop > maxScrollTop) {
217
217
  /** fix: 滚动条不在顶部时,表格数据变少,导致滚动条位置有误 */
218
218
  scrollTop = maxScrollTop;
@@ -272,7 +272,11 @@ export function useVirtualScroll<DT extends Record<string, any>>(
272
272
  const dataLength = dataSourceCopyTemp.length;
273
273
  const rowHeight = getRowHeightFn.value();
274
274
 
275
- const vsValue: any = {};
275
+ const vsValue: any = {
276
+ startIndex: 0, // github #34 init
277
+ // endIndex: , // FIXME: endIndex may be old value
278
+ offsetTop: 0, // github #34 init
279
+ };
276
280
  const scrollHeight = dataLength * rowHeight + tableHeaderHeight.value;
277
281
  const { enabled: scrollbarEnable } = scrollbarOptions.value;
278
282
  if (scrollbarEnable) {