stk-table-vue 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,10 @@ Vue3 简易虚拟滚动表格。用于实时数据展示,新数据行高亮渐
4
4
 
5
5
  Vue2.7支持引入源码(**ts**)使用。
6
6
 
7
+ repo:
8
+ - [Github](https://github.com/ja-plus/stk-table-vue)
9
+ - [Gitee](https://gitee.com/japlus/stk-table-vue) 🇨🇳
10
+
7
11
  ## Bug TODO:
8
12
  * [x] props.dataSource 为 shallowRef 时,高亮行不生效。(bug:2024.02.21)(resolved:0.2.3)
9
13
 
@@ -18,7 +22,7 @@ Vue2.7支持引入源码(**ts**)使用。
18
22
  - [] sticky column 动态计算阴影位置。
19
23
  * [] 列筛选。
20
24
  * [x] 斑马纹。
21
- - [] 虚拟滚动斑马纹。
25
+ - [x] 虚拟滚动斑马纹。
22
26
  * [x] 表头拖动更改顺序。
23
27
  * [x] 表头拖动调整列宽。
24
28
  * [x] 排序
@@ -33,10 +37,10 @@ Vue2.7支持引入源码(**ts**)使用。
33
37
  * 鼠标悬浮在表格上,键盘滚动虚拟表格。
34
38
  - [x] 键盘 `ArrowUp`/`ArrowDown`/`ArrowLeft`/`ArrowRight` 按键支持。
35
39
  - [x] 键盘 `PageUp`/ `PageDown` 按键支持。
36
- * [] 不传row-key 时,自动按序号生成id。
37
40
  * [] 非虚拟滚动时,大数据分批加载。
38
41
  * [x] vue2.7支持(引入源码使用)。
39
42
  - [x] `props.optimizeVue2Scroll` 优化vue2虚拟滚动流畅度。
43
+ * [] `props.emptyCellText` 支持传入函数。
40
44
 
41
45
 
42
46
  ## Usage
@@ -188,7 +192,14 @@ export type StkProps = {
188
192
  dataIndex: keyof T;
189
193
  order: Order;
190
194
  };
195
+ /**
196
+ * string排序是否使用 String.prototype.localCompare
197
+ * 默认true (历史设计问题,为了兼容,默认true)
198
+ */
199
+ stringLocaleCompare?: boolean;
191
200
  },
201
+ /** 隐藏头部title。可传入dataIndex数组 */
202
+ hideHeaderTitle?: boolean | string[];
192
203
  };
193
204
  ```
194
205
  #### Emits
@@ -404,3 +415,8 @@ export type StkTableColumn<T extends Record<string, any>> = {
404
415
  ]
405
416
  </script>
406
417
  ```
418
+
419
+ ## Special Example
420
+ ### 鼠标悬浮表头时,不展示title
421
+ * 将 `StkTableColumn` 中的 `title` 字段置为 "" 空字符串。这样th中就没有title了。
422
+ * 使用 `StkTableColumn` 中的 `customHeaderCell` 属性中,自定义表头渲染。
@@ -13,7 +13,7 @@ declare function setCurrentRow(rowKey: string, option?: {
13
13
  * 设置表头排序状态
14
14
  * @param dataIndex 列字段
15
15
  * @param order 正序倒序
16
- * @param option.sortOption 指定排序参数
16
+ * @param option.sortOption 指定排序参数。同 StkTableColumn 中排序相关字段。建议从columns中find得到。
17
17
  * @param option.sort 是否触发排序-默认true
18
18
  * @param option.silent 是否禁止触发回调-默认true
19
19
  */
@@ -113,6 +113,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
113
113
  optimizeVue2Scroll?: boolean | undefined;
114
114
  /** 排序配置 */
115
115
  sortConfig?: SortConfig<any> | undefined;
116
+ /** 隐藏头部title。可传入dataIndex数组 */
117
+ hideHeaderTitle?: boolean | string[] | undefined;
116
118
  }>, {
117
119
  width: string;
118
120
  fixedMode: boolean;
@@ -146,7 +148,9 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
146
148
  optimizeVue2Scroll: boolean;
147
149
  sortConfig: () => {
148
150
  emptyToBottom: boolean;
151
+ stringLocaleCompare: boolean;
149
152
  };
153
+ hideHeaderTitle: boolean;
150
154
  }>, {
151
155
  /** 初始化横向纵向虚拟滚动 */
152
156
  initVirtualScroll: (height?: number | undefined) => void;
@@ -269,6 +273,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
269
273
  optimizeVue2Scroll?: boolean | undefined;
270
274
  /** 排序配置 */
271
275
  sortConfig?: SortConfig<any> | undefined;
276
+ /** 隐藏头部title。可传入dataIndex数组 */
277
+ hideHeaderTitle?: boolean | string[] | undefined;
272
278
  }>, {
273
279
  width: string;
274
280
  fixedMode: boolean;
@@ -302,7 +308,9 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
302
308
  optimizeVue2Scroll: boolean;
303
309
  sortConfig: () => {
304
310
  emptyToBottom: boolean;
311
+ stringLocaleCompare: boolean;
305
312
  };
313
+ hideHeaderTitle: boolean;
306
314
  }>>> & {
307
315
  onScroll?: ((ev: Event, data: {
308
316
  startIndex: number;
@@ -353,6 +361,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
353
361
  fixedColShadow: boolean;
354
362
  optimizeVue2Scroll: boolean;
355
363
  sortConfig: SortConfig<any>;
364
+ hideHeaderTitle: boolean | string[];
356
365
  }, {}>, {
357
366
  tableHeader?(_: {
358
367
  col: StkTableColumn<any>;
@@ -1,3 +1,4 @@
1
1
  export { default as StkTable } from './StkTable.vue';
2
2
  export { tableSort, insertToOrderedArray } from './utils';
3
3
  export type { StkTableColumn } from './types/index';
4
+ import './style.less';
@@ -83,5 +83,10 @@ export type SortConfig<T extends Record<string, any>> = {
83
83
  dataIndex: keyof T;
84
84
  order: Order;
85
85
  };
86
+ /**
87
+ * string排序是否使用 String.prototype.localCompare
88
+ * 默认true (&$&应该false)
89
+ */
90
+ stringLocaleCompare?: boolean;
86
91
  };
87
92
  export {};
@@ -1,9 +1,9 @@
1
1
  import { CSSProperties, Ref } from 'vue';
2
2
  import { StkTableColumn } from './types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
4
- type Options = {
4
+ type Options<T extends Record<string, any>> = {
5
5
  props: any;
6
- tableHeaderLast: Ref<StkTableColumn<any>[]>;
6
+ tableHeaders: Ref<StkTableColumn<T>[][]>;
7
7
  virtualScroll: Ref<VirtualScrollStore>;
8
8
  virtualScrollX: Ref<VirtualScrollXStore>;
9
9
  virtualX_on: Ref<boolean>;
@@ -14,7 +14,7 @@ type Options = {
14
14
  * @param param0
15
15
  * @returns
16
16
  */
17
- export declare function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualScrollX, virtualX_on, virtualX_offsetRight }: Options): {
17
+ export declare function useFixedStyle<DT extends Record<string, any>>({ props, tableHeaders, virtualScroll, virtualScrollX, virtualX_on, virtualX_offsetRight, }: Options<DT>): {
18
18
  getFixedStyle: (tagType: 1 | 2, col: StkTableColumn<any>, depth?: number) => CSSProperties;
19
19
  };
20
20
  export {};
@@ -1,10 +1,11 @@
1
1
  import { Ref, ShallowRef } from 'vue';
2
2
  import { StkTableColumn } from './types';
3
3
  type Option<DT extends Record<string, any>> = {
4
- tableContainer: Ref<HTMLElement | undefined>;
5
4
  props: any;
5
+ tableContainer: Ref<HTMLElement | undefined>;
6
6
  dataSourceCopy: ShallowRef<DT[]>;
7
7
  tableHeaderLast: Ref<StkTableColumn<DT>[]>;
8
+ tableHeaders: Ref<StkTableColumn<DT>[][]>;
8
9
  };
9
10
  /** 暂存纵向虚拟滚动的数据 */
10
11
  export type VirtualScrollStore = {
@@ -41,7 +42,7 @@ export type VirtualScrollXStore = {
41
42
  * @param param0
42
43
  * @returns
43
44
  */
44
- export declare function useVirtualScroll<DT extends Record<string, any>>({ props, tableContainer, dataSourceCopy, tableHeaderLast }: Option<DT>): {
45
+ export declare function useVirtualScroll<DT extends Record<string, any>>({ props, tableContainer, dataSourceCopy, tableHeaderLast, tableHeaders, }: Option<DT>): {
45
46
  virtualScroll: Ref<{
46
47
  containerHeight: number;
47
48
  pageSize: number;
@@ -8,7 +8,7 @@ import { Order, SortConfig, SortOption, SortState, StkTableColumn } from './type
8
8
  * @param newItem 要插入的数据
9
9
  * @param targetArray 表格数据
10
10
  */
11
- export declare function insertToOrderedArray<T extends object>(sortState: SortState<keyof T>, newItem: any, targetArray: T[]): T[];
11
+ export declare function insertToOrderedArray<T extends object>(sortState: SortState<keyof T>, newItem: any, targetArray: T[], sortConfig?: SortConfig<T>): T[];
12
12
  /**
13
13
  * 表格排序抽离
14
14
  * 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
@@ -25,3 +25,5 @@ export declare function tableSort<T extends Record<string, any>>(sortOption: Sor
25
25
  export declare function howDeepTheHeader(arr: StkTableColumn<any>[], level?: number): number;
26
26
  /** 获取列宽 */
27
27
  export declare function getColWidth(col: StkTableColumn<any> | null): number;
28
+ /** 获取列宽配置。用于支持列宽配置数字 */
29
+ export declare function getColWidthStr(col: StkTableColumn<any> | null | undefined, key?: 'width' | 'minWidth' | 'maxWidth'): string | undefined;
@@ -70,23 +70,35 @@ function useAutoResize({ tableContainer, initVirtualScroll, props, debounceMs })
70
70
  }, debounceMs);
71
71
  }
72
72
  }
73
- function insertToOrderedArray(sortState, newItem, targetArray) {
73
+ function isEmptyValue(val, isNumber) {
74
+ let isEmpty = val === null || val === "";
75
+ if (isNumber) {
76
+ isEmpty || (isEmpty = typeof val === "boolean" || Number.isNaN(+val));
77
+ }
78
+ return isEmpty;
79
+ }
80
+ function insertToOrderedArray(sortState, newItem, targetArray, sortConfig = {}) {
74
81
  const { dataIndex, order } = sortState;
82
+ sortConfig = { emptyToBottom: false, ...sortConfig };
75
83
  let { sortType } = sortState;
76
84
  if (!sortType)
77
85
  sortType = typeof newItem[dataIndex];
86
+ const isNumber = sortType === "number";
78
87
  const data = [...targetArray];
79
88
  if (!order) {
80
89
  data.unshift(newItem);
81
90
  return data;
82
91
  }
92
+ if (sortConfig.emptyToBottom && isEmptyValue(data)) {
93
+ data.push(newItem);
94
+ }
83
95
  let sIndex = 0;
84
96
  let eIndex = data.length - 1;
85
97
  const targetVal = newItem[dataIndex];
86
98
  while (sIndex <= eIndex) {
87
99
  const midIndex = Math.floor((sIndex + eIndex) / 2);
88
100
  const midVal = data[midIndex][dataIndex];
89
- const compareRes = strCompare(midVal, targetVal, sortType);
101
+ const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
90
102
  if (compareRes === 0) {
91
103
  sIndex = midIndex;
92
104
  break;
@@ -105,17 +117,21 @@ function insertToOrderedArray(sortState, newItem, targetArray) {
105
117
  data.splice(sIndex, 0, newItem);
106
118
  return data;
107
119
  }
108
- function strCompare(a, b, type) {
109
- if (type === "number") {
110
- if (+a > +b)
111
- return 1;
112
- else if (+a === +b)
113
- return 0;
114
- else
115
- return -1;
116
- } else {
120
+ function strCompare(a, b, isNumber, localeCompare = false) {
121
+ let _a = a;
122
+ let _b = b;
123
+ if (isNumber) {
124
+ _a = +a;
125
+ _b = +b;
126
+ } else if (localeCompare) {
117
127
  return String(a).localeCompare(b);
118
128
  }
129
+ if (_a > _b)
130
+ return 1;
131
+ else if (_a === _b)
132
+ return 0;
133
+ else
134
+ return -1;
119
135
  }
120
136
  function separatedData(sortOption, targetDataSource, isNumber) {
121
137
  const emptyArr = [];
@@ -123,10 +139,7 @@ function separatedData(sortOption, targetDataSource, isNumber) {
123
139
  for (let i = 0; i < targetDataSource.length; i++) {
124
140
  const row = targetDataSource[i];
125
141
  const sortField = sortOption.sortField || sortOption.dataIndex;
126
- let isEmpty = row[sortField] === null || row[sortField] === "";
127
- if (isNumber) {
128
- isEmpty || (isEmpty = typeof row[sortField] === "boolean" || Number.isNaN(+row[sortField]));
129
- }
142
+ const isEmpty = isEmptyValue(row[sortField], isNumber);
130
143
  if (isEmpty) {
131
144
  emptyArr.push(row);
132
145
  } else {
@@ -154,25 +167,16 @@ function tableSort(sortOption, order, dataSource, sortConfig = {}) {
154
167
  if (!sortType)
155
168
  sortType = typeof dataSource[0][sortField];
156
169
  const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, sortType === "number");
157
- if (sortType === "number") {
158
- if (order === "asc") {
159
- valueArr.sort((a, b) => +a[sortField] - +b[sortField]);
160
- targetDataSource = [...emptyArr, ...valueArr];
161
- } else {
162
- valueArr.sort((a, b) => +b[sortField] - +a[sortField]);
163
- targetDataSource = [...valueArr, ...emptyArr];
164
- }
170
+ const isNumber = sortType === "number";
171
+ if (order === "asc") {
172
+ valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, sortConfig.stringLocaleCompare));
165
173
  } else {
166
- if (order === "asc") {
167
- valueArr.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]));
168
- targetDataSource = [...emptyArr, ...valueArr];
169
- } else {
170
- valueArr.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]) * -1);
171
- targetDataSource = [...valueArr, ...emptyArr];
172
- }
174
+ valueArr.sort((a, b) => strCompare(b[sortField], a[sortField], isNumber, sortConfig.stringLocaleCompare));
173
175
  }
174
- if (sortConfig.emptyToBottom) {
176
+ if (order === "desc" || sortConfig.emptyToBottom) {
175
177
  targetDataSource = [...valueArr, ...emptyArr];
178
+ } else {
179
+ targetDataSource = [...emptyArr, ...valueArr];
176
180
  }
177
181
  }
178
182
  return targetDataSource;
@@ -188,10 +192,18 @@ function howDeepTheHeader(arr, level = 1) {
188
192
  return Math.max(...levels);
189
193
  }
190
194
  function getColWidth(col) {
191
- if (typeof (col == null ? void 0 : col.width) === "number") {
192
- return Math.floor(col.width ?? Default_Col_Width);
195
+ const val = (col == null ? void 0 : col.width) ?? Default_Col_Width;
196
+ if (typeof val === "number") {
197
+ return Math.floor(val);
193
198
  }
194
- return parseInt((col == null ? void 0 : col.width) ?? Default_Col_Width);
199
+ return parseInt(val);
200
+ }
201
+ function getColWidthStr(col, key = "width") {
202
+ const val = col == null ? void 0 : col[key];
203
+ if (typeof val === "number") {
204
+ return val + "px";
205
+ }
206
+ return val;
195
207
  }
196
208
  function useColResize({
197
209
  tableContainer,
@@ -371,36 +383,54 @@ function useFixedCol({ props, tableHeaderLast, tableContainer }) {
371
383
  updateFixedShadow
372
384
  };
373
385
  }
374
- function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualScrollX, virtualX_on, virtualX_offsetRight }) {
386
+ function useFixedStyle({
387
+ props,
388
+ tableHeaders,
389
+ virtualScroll,
390
+ virtualScrollX,
391
+ virtualX_on,
392
+ virtualX_offsetRight
393
+ }) {
375
394
  const fixedColumnsPositionStore = computed(() => {
376
- const store = {};
377
- const cols = [...tableHeaderLast.value];
378
- let left = 0;
379
- let rightStartIndex = 0;
380
- for (let i = 0; i < cols.length; i++) {
381
- const item = cols[i];
382
- if (item.fixed === "left") {
383
- store[item.dataIndex] = left;
384
- left += getColWidth(item);
385
- }
386
- if (!rightStartIndex && item.fixed === "right") {
387
- rightStartIndex = i;
395
+ const colKeyStore = {};
396
+ const refStore = /* @__PURE__ */ new WeakMap();
397
+ tableHeaders.value.forEach((cols) => {
398
+ let left = 0;
399
+ let rightStartIndex = 0;
400
+ for (let i = 0; i < cols.length; i++) {
401
+ const item = cols[i];
402
+ if (item.fixed === "left") {
403
+ if (item.dataIndex) {
404
+ colKeyStore[item.dataIndex] = left;
405
+ } else {
406
+ refStore.set(item, left);
407
+ }
408
+ left += getColWidth(item);
409
+ }
410
+ if (!rightStartIndex && item.fixed === "right") {
411
+ rightStartIndex = i;
412
+ }
388
413
  }
389
- }
390
- let right = 0;
391
- for (let i = cols.length - 1; i >= rightStartIndex; i--) {
392
- const item = cols[i];
393
- if (item.fixed === "right") {
394
- store[item.dataIndex] = right;
395
- right += getColWidth(item);
414
+ let right = 0;
415
+ for (let i = cols.length - 1; i >= rightStartIndex; i--) {
416
+ const item = cols[i];
417
+ if (item.fixed === "right") {
418
+ if (item.dataIndex) {
419
+ colKeyStore[item.dataIndex] = right;
420
+ } else {
421
+ refStore.set(item, right);
422
+ }
423
+ right += getColWidth(item);
424
+ }
396
425
  }
397
- }
398
- return store;
426
+ });
427
+ return { refStore, colKeyStore };
399
428
  });
400
429
  function getFixedStyle(tagType, col, depth = 0) {
401
- const { fixed, dataIndex } = col;
430
+ const { fixed } = col;
402
431
  const isFixedLeft = fixed === "left";
403
432
  const style = {};
433
+ const { colKeyStore, refStore } = fixedColumnsPositionStore.value;
404
434
  if (Is_Legacy_Mode) {
405
435
  style.position = "relative";
406
436
  } else {
@@ -427,10 +457,11 @@ function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualScrollX,
427
457
  style.right = `${virtualX_offsetRight.value}px`;
428
458
  }
429
459
  } else {
460
+ const lr = (col.dataIndex ? colKeyStore[col.dataIndex] : refStore.get(col)) + "px";
430
461
  if (isFixedLeft) {
431
- style.left = fixedColumnsPositionStore.value[dataIndex] + "px";
462
+ style.left = lr;
432
463
  } else {
433
- style.right = fixedColumnsPositionStore.value[dataIndex] + "px";
464
+ style.right = lr;
434
465
  }
435
466
  }
436
467
  }
@@ -444,9 +475,9 @@ const HIGHLIGHT_ROW_CLASS = "highlight-row";
444
475
  const HIGHLIGHT_CELL_CLASS = "highlight-cell";
445
476
  function useHighlight({ props, tableContainer }) {
446
477
  const highlightRowStore = ref({});
447
- const highlightFrom = Highlight_Color[props.theme].from;
448
- const highlightTo = Highlight_Color[props.theme].to;
449
- const highlightInter = computed(() => interpolateRgb(highlightFrom, highlightTo));
478
+ const highlightFrom = computed(() => Highlight_Color[props.theme].from);
479
+ const highlightTo = computed(() => Highlight_Color[props.theme].to);
480
+ const highlightInter = computed(() => interpolateRgb(highlightFrom.value, highlightTo.value));
450
481
  const highlightDimRowKeys = /* @__PURE__ */ new Set();
451
482
  const highlightDimRowsTimeout = /* @__PURE__ */ new Map();
452
483
  const highlightDimCellsTimeout = /* @__PURE__ */ new Map();
@@ -569,10 +600,11 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
569
600
  if (!isMouseOver)
570
601
  return;
571
602
  e.preventDefault();
572
- const { scrollTop, rowHeight, pageSize } = virtualScroll.value;
603
+ const { scrollTop, rowHeight, containerHeight } = virtualScroll.value;
573
604
  const { scrollLeft } = virtualScrollX.value;
574
605
  const { headless, headerRowHeight } = props;
575
606
  const headerHeight = headless ? 0 : tableHeaders.value.length * (headerRowHeight || rowHeight);
607
+ const bodyPageSize = Math.floor((containerHeight - headerHeight) / rowHeight);
576
608
  if (e.code === SCROLL_CODES[0]) {
577
609
  scrollTo(scrollTop - rowHeight, null);
578
610
  } else if (e.code === SCROLL_CODES[1]) {
@@ -582,9 +614,9 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
582
614
  } else if (e.code === SCROLL_CODES[3]) {
583
615
  scrollTo(null, scrollLeft - rowHeight);
584
616
  } else if (e.code === SCROLL_CODES[4]) {
585
- scrollTo(scrollTop - rowHeight * pageSize - headerHeight, null);
617
+ scrollTo(scrollTop - rowHeight * bodyPageSize + headerHeight, null);
586
618
  } else if (e.code === SCROLL_CODES[5]) {
587
- scrollTo(scrollTop + rowHeight * pageSize - headerHeight, null);
619
+ scrollTo(scrollTop + rowHeight * bodyPageSize - headerHeight, null);
588
620
  }
589
621
  }
590
622
  function handleMouseEnter() {
@@ -650,7 +682,13 @@ function useThDrag({ props, emits }) {
650
682
  };
651
683
  }
652
684
  const VUE2_SCROLL_TIMEOUT_MS = 200;
653
- function useVirtualScroll({ props, tableContainer, dataSourceCopy, tableHeaderLast }) {
685
+ function useVirtualScroll({
686
+ props,
687
+ tableContainer,
688
+ dataSourceCopy,
689
+ tableHeaderLast,
690
+ tableHeaders
691
+ }) {
654
692
  const virtualScroll = ref({
655
693
  containerHeight: 0,
656
694
  rowHeight: props.rowHeight,
@@ -728,11 +766,13 @@ function useVirtualScroll({ props, tableContainer, dataSourceCopy, tableHeaderLa
728
766
  } else {
729
767
  containerHeight = offsetHeight || Default_Table_Height;
730
768
  }
731
- Object.assign(virtualScroll.value, {
732
- containerHeight,
733
- pageSize: Math.ceil(containerHeight / rowHeight) + 1
734
- // 这里最终+1,因为headless=true无头时,需要上下各预渲染一行。
735
- });
769
+ const { headless, headerRowHeight } = props;
770
+ let pageSize = Math.ceil(containerHeight / rowHeight);
771
+ if (!headless) {
772
+ const headerToBodyRowHeightCount = Math.floor(tableHeaders.value.length * (headerRowHeight || rowHeight) / rowHeight);
773
+ pageSize -= headerToBodyRowHeightCount;
774
+ }
775
+ Object.assign(virtualScroll.value, { containerHeight, pageSize });
736
776
  updateVirtualScrollY(scrollTop);
737
777
  }
738
778
  function initVirtualScrollX() {
@@ -749,9 +789,27 @@ function useVirtualScroll({ props, tableContainer, dataSourceCopy, tableHeaderLa
749
789
  let vue2ScrollYTimeout = null;
750
790
  function updateVirtualScrollY(sTop = 0) {
751
791
  const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
752
- const startIndex = Math.floor(sTop / rowHeight);
753
- const offsetTop = startIndex * rowHeight;
792
+ virtualScroll.value.scrollTop = sTop;
793
+ let startIndex = Math.floor(sTop / rowHeight);
794
+ if (props.stripe) {
795
+ startIndex -= 1;
796
+ }
797
+ if (startIndex < 0) {
798
+ startIndex = 0;
799
+ }
800
+ if (props.stripe && startIndex !== 0) {
801
+ const scrollRows = Math.abs(oldStartIndex - startIndex);
802
+ if (scrollRows < 2) {
803
+ return;
804
+ } else if (scrollRows % 2) {
805
+ startIndex -= 1;
806
+ }
807
+ }
754
808
  let endIndex = startIndex + pageSize;
809
+ if (props.stripe) {
810
+ endIndex += 2;
811
+ }
812
+ const offsetTop = startIndex * rowHeight;
755
813
  if (endIndex > dataSourceCopy.value.length) {
756
814
  endIndex = dataSourceCopy.value.length;
757
815
  }
@@ -761,12 +819,11 @@ function useVirtualScroll({ props, tableContainer, dataSourceCopy, tableHeaderLa
761
819
  if (!props.optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
762
820
  Object.assign(virtualScroll.value, {
763
821
  startIndex,
764
- offsetTop,
765
822
  endIndex,
766
- scrollTop: sTop
823
+ offsetTop
767
824
  });
768
825
  } else {
769
- Object.assign(virtualScroll.value, { endIndex, scrollTop: sTop });
826
+ virtualScroll.value.endIndex = endIndex;
770
827
  vue2ScrollYTimeout = window.setTimeout(() => {
771
828
  Object.assign(virtualScroll.value, { startIndex, offsetTop });
772
829
  }, VUE2_SCROLL_TIMEOUT_MS);
@@ -913,8 +970,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
913
970
  fixedColShadow: { type: Boolean, default: false },
914
971
  optimizeVue2Scroll: { type: Boolean, default: false },
915
972
  sortConfig: { default: () => ({
916
- emptyToBottom: false
917
- }) }
973
+ emptyToBottom: false,
974
+ stringLocaleCompare: true
975
+ }) },
976
+ hideHeaderTitle: { type: [Boolean, Array], default: false }
918
977
  },
919
978
  emits: ["sort-change", "row-click", "current-change", "row-dblclick", "header-row-menu", "row-menu", "cell-click", "header-cell-click", "scroll", "scroll-x", "col-order-change", "th-drag-start", "th-drop", "update:columns"],
920
979
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -955,10 +1014,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
955
1014
  initVirtualScrollX,
956
1015
  updateVirtualScrollY,
957
1016
  updateVirtualScrollX
958
- } = useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast });
1017
+ } = useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast, tableHeaders });
959
1018
  const { getFixedStyle } = useFixedStyle({
960
1019
  props,
961
- tableHeaderLast,
1020
+ tableHeaders,
962
1021
  virtualScroll,
963
1022
  virtualScrollX,
964
1023
  virtualX_on,
@@ -1038,17 +1097,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1038
1097
  tableHeaders.value[depth] = [];
1039
1098
  }
1040
1099
  let allChildrenLen = 0;
1100
+ let allChildrenWidthSum = 0;
1041
1101
  arr.forEach((col) => {
1042
1102
  col.__PARENT__ = parent;
1043
1103
  let colChildrenLen = 1;
1104
+ let colWidth = 0;
1044
1105
  if (col.children) {
1045
- colChildrenLen = flat(
1106
+ const [len, widthSum] = flat(
1046
1107
  col.children,
1047
1108
  col,
1048
1109
  depth + 1
1049
1110
  /* , col.fixed */
1050
1111
  );
1112
+ colChildrenLen = len;
1113
+ colWidth = widthSum;
1051
1114
  } else {
1115
+ colWidth = getColWidth(col);
1052
1116
  tempHeaderLast.push(col);
1053
1117
  }
1054
1118
  tableHeaders.value[depth].push(col);
@@ -1060,9 +1124,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1060
1124
  if (colSpan !== 1) {
1061
1125
  col.colSpan = colSpan;
1062
1126
  }
1127
+ if (!props.fixedMode && col.width === void 0) {
1128
+ col.width = colWidth + "px";
1129
+ }
1063
1130
  allChildrenLen += colChildrenLen;
1131
+ allChildrenWidthSum += colWidth;
1064
1132
  });
1065
- return allChildrenLen;
1133
+ return [allChildrenLen, allChildrenWidthSum];
1066
1134
  }
1067
1135
  flat(copyColumn, null);
1068
1136
  tableHeaderLast.value = tempHeaderLast;
@@ -1082,17 +1150,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1082
1150
  return typeof props.colKey === "function" ? props.colKey(col) : col[props.colKey];
1083
1151
  }
1084
1152
  function getColWidthStyle(col) {
1153
+ const width = getColWidthStr(col);
1154
+ const minWidth = getColWidthStr(col, "minWidth");
1155
+ const maxWidth = getColWidthStr(col, "maxWidth");
1085
1156
  const style = {
1086
- width: col.width,
1087
- minWidth: col.minWidth,
1088
- maxWidth: col.maxWidth
1157
+ width,
1158
+ minWidth: minWidth ?? width,
1159
+ maxWidth: maxWidth ?? width
1089
1160
  };
1090
1161
  if (props.colResizable) {
1091
- style.minWidth = col.width;
1092
- style.maxWidth = col.width;
1093
- } else {
1094
- style.minWidth = col.minWidth === void 0 ? col.width : col.minWidth;
1095
- style.maxWidth = col.maxWidth === void 0 ? col.width : col.maxWidth;
1162
+ style.minWidth = width;
1163
+ style.maxWidth = width;
1096
1164
  }
1097
1165
  return style;
1098
1166
  }
@@ -1108,6 +1176,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1108
1176
  }
1109
1177
  return style;
1110
1178
  }
1179
+ function getHeaderTitle(col) {
1180
+ if (props.hideHeaderTitle === true || Array.isArray(props.hideHeaderTitle) && props.hideHeaderTitle.includes(col.dataIndex)) {
1181
+ return "";
1182
+ }
1183
+ return col.title || "";
1184
+ }
1111
1185
  function onColumnSort(col, click = true, options = {}) {
1112
1186
  if (!(col == null ? void 0 : col.sorter))
1113
1187
  return;
@@ -1329,7 +1403,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1329
1403
  rowspan: unref(virtualX_on) ? 1 : col.rowSpan,
1330
1404
  colspan: col.colSpan,
1331
1405
  style: normalizeStyle(getCellStyle(1, col, rowIndex)),
1332
- title: col.title,
1406
+ title: getHeaderTitle(col),
1333
1407
  class: normalizeClass([
1334
1408
  col.sorter ? "sortable" : "",
1335
1409
  col.dataIndex === unref(sortCol) && unref(sortOrderIndex) !== 0 && "sorter-" + sortSwitchOrder[unref(sortOrderIndex)],