stk-table-vue 0.8.14 → 0.9.0-beta.1

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.
Files changed (38) hide show
  1. package/README.md +172 -172
  2. package/lib/src/StkTable/StkTable.vue.d.ts +19 -1
  3. package/lib/src/StkTable/useScrollbar.d.ts +57 -0
  4. package/lib/src/StkTable/utils/index.d.ts +7 -0
  5. package/lib/stk-table-vue.js +396 -205
  6. package/lib/style.css +42 -1
  7. package/package.json +74 -74
  8. package/src/StkTable/StkTable.vue +1730 -1665
  9. package/src/StkTable/components/DragHandle.vue +9 -9
  10. package/src/StkTable/components/SortIcon.vue +6 -6
  11. package/src/StkTable/components/TriangleIcon.vue +3 -3
  12. package/src/StkTable/const.ts +50 -50
  13. package/src/StkTable/index.ts +4 -4
  14. package/src/StkTable/style.less +627 -578
  15. package/src/StkTable/types/highlightDimOptions.ts +26 -26
  16. package/src/StkTable/types/index.ts +297 -297
  17. package/src/StkTable/useAutoResize.ts +91 -91
  18. package/src/StkTable/useColResize.ts +216 -216
  19. package/src/StkTable/useFixedCol.ts +150 -150
  20. package/src/StkTable/useFixedStyle.ts +75 -75
  21. package/src/StkTable/useGetFixedColPosition.ts +65 -65
  22. package/src/StkTable/useHighlight.ts +257 -257
  23. package/src/StkTable/useKeyboardArrowScroll.ts +112 -112
  24. package/src/StkTable/useMaxRowSpan.ts +55 -55
  25. package/src/StkTable/useMergeCells.ts +120 -120
  26. package/src/StkTable/useRowExpand.ts +88 -88
  27. package/src/StkTable/useScrollRowByRow.ts +113 -113
  28. package/src/StkTable/useScrollbar.ts +187 -0
  29. package/src/StkTable/useThDrag.ts +102 -102
  30. package/src/StkTable/useTrDrag.ts +113 -113
  31. package/src/StkTable/useTree.ts +161 -161
  32. package/src/StkTable/useVirtualScroll.ts +494 -494
  33. package/src/StkTable/utils/constRefUtils.ts +29 -29
  34. package/src/StkTable/utils/index.ts +287 -258
  35. package/src/StkTable/utils/useTriggerRef.ts +33 -33
  36. package/src/VirtualTree.vue +622 -622
  37. package/src/VirtualTreeSelect.vue +367 -367
  38. package/src/vite-env.d.ts +10 -10
@@ -1,29 +1,29 @@
1
- import { DEFAULT_COL_WIDTH, STK_ID_PREFIX } from '../const';
2
- import { PrivateStkTableColumn, StkTableColumn } from '../types';
3
-
4
- /**
5
- * 获取列宽
6
- *
7
- * 关于列宽的操作往往在横向滚动中使用。既然已经有横向滚动了,则列宽会被压缩至minWidth,所以优先取minWidth
8
- */
9
- export function getColWidth(col: StkTableColumn<any> | null): number {
10
- const val = col?.minWidth ?? col?.width ?? DEFAULT_COL_WIDTH;
11
- if (typeof val === 'number') {
12
- return Math.floor(val);
13
- }
14
- return parseInt(val);
15
- }
16
-
17
- /** 获取计算后的宽度 */
18
- export function getCalculatedColWidth(col: PrivateStkTableColumn<any> | null) {
19
- return (col && col.__WIDTH__) ?? +DEFAULT_COL_WIDTH;
20
- }
21
-
22
- /** 创建组件唯一标识 */
23
- export function createStkTableId() {
24
- let id = window.__STK_TB_ID_COUNT__;
25
- if (!id) id = 0;
26
- id += 1;
27
- window.__STK_TB_ID_COUNT__ = id;
28
- return STK_ID_PREFIX + id.toString(36);
29
- }
1
+ import { DEFAULT_COL_WIDTH, STK_ID_PREFIX } from '../const';
2
+ import { PrivateStkTableColumn, StkTableColumn } from '../types';
3
+
4
+ /**
5
+ * 获取列宽
6
+ *
7
+ * 关于列宽的操作往往在横向滚动中使用。既然已经有横向滚动了,则列宽会被压缩至minWidth,所以优先取minWidth
8
+ */
9
+ export function getColWidth(col: StkTableColumn<any> | null): number {
10
+ const val = col?.minWidth ?? col?.width ?? DEFAULT_COL_WIDTH;
11
+ if (typeof val === 'number') {
12
+ return Math.floor(val);
13
+ }
14
+ return parseInt(val);
15
+ }
16
+
17
+ /** 获取计算后的宽度 */
18
+ export function getCalculatedColWidth(col: PrivateStkTableColumn<any> | null) {
19
+ return (col && col.__WIDTH__) ?? +DEFAULT_COL_WIDTH;
20
+ }
21
+
22
+ /** 创建组件唯一标识 */
23
+ export function createStkTableId() {
24
+ let id = window.__STK_TB_ID_COUNT__;
25
+ if (!id) id = 0;
26
+ id += 1;
27
+ window.__STK_TB_ID_COUNT__ = id;
28
+ return STK_ID_PREFIX + id.toString(36);
29
+ }
@@ -1,258 +1,287 @@
1
- import { CELL_KEY_SEPARATE, DEFAULT_SORT_CONFIG } from '../const';
2
- import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from '../types';
3
-
4
- /** 是否空值 */
5
- export function isEmptyValue(val: any, isNumber?: boolean) {
6
- let isEmpty = val === null || val === void 0;
7
- if (isNumber) {
8
- isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
9
- }
10
- return isEmpty;
11
- }
12
-
13
- /**
14
- * 对有序数组插入新数据
15
- *
16
- * 注意:不会改变原数组,返回新数组
17
- * @param sortState
18
- * @param sortState.dataIndex 排序的字段
19
- * @param sortState.order 排序顺序
20
- * @param sortState.sortType 排序方式
21
- * @param newItem 要插入的数据
22
- * @param targetArray 表格数据
23
- * @return targetArray 的浅拷贝
24
- */
25
- export function insertToOrderedArray<T extends object>(
26
- sortState: SortState<T>,
27
- newItem: T,
28
- targetArray: T[],
29
- sortConfig: SortConfig<T> & { customCompare?: (a: T, b: T) => number } = {},
30
- ) {
31
- const { dataIndex, sortField, order } = sortState;
32
- let { sortType } = sortState;
33
- const field = sortField || dataIndex;
34
- if (!sortType) sortType = typeof newItem[field] as 'number' | 'string';
35
- const data = targetArray.slice();
36
-
37
- if (!order || !data.length) {
38
- // 没有排序的情况,插入在最上方
39
- data.unshift(newItem);
40
- return data;
41
- }
42
-
43
- const { emptyToBottom, customCompare, stringLocaleCompare } = { emptyToBottom: false, ...sortConfig };
44
-
45
- const targetVal: any = newItem[field];
46
- if (emptyToBottom && isEmptyValue(targetVal)) {
47
- // 空值排在最下方
48
- data.push(newItem);
49
- } else {
50
- const customCompareFn =
51
- customCompare ||
52
- ((a, b) => {
53
- const midVal: any = a[field];
54
- const compareRes = strCompare(midVal, targetVal, isNumber, stringLocaleCompare);
55
- return order === 'asc' ? compareRes : -compareRes;
56
- });
57
- const isNumber = sortType === 'number';
58
- // 二分插入
59
- const sIndex = binarySearch(data, midIndex => {
60
- return customCompareFn(data[midIndex], newItem);
61
- });
62
- data.splice(sIndex, 0, newItem);
63
- }
64
-
65
- return data;
66
- }
67
-
68
- /**
69
- * 二分查找
70
- * @param searchArray 查找数组
71
- * @param compareCallback 比较函数,返回 -1|0|1
72
- */
73
- export function binarySearch(searchArray: any[], compareCallback: (midIndex: number) => number) {
74
- let sIndex = 0;
75
- let eIndex = searchArray.length - 1;
76
- while (sIndex <= eIndex) {
77
- const midIndex = Math.floor((sIndex + eIndex) / 2);
78
- const compareRes = compareCallback(midIndex);
79
- if (compareRes === 0) {
80
- //midVal == targetVal
81
- sIndex = midIndex;
82
- break;
83
- } else if (compareRes < 0) {
84
- // midVal < targetVal
85
- sIndex = midIndex + 1;
86
- } else {
87
- //midVal > targetVal
88
- eIndex = midIndex - 1;
89
- }
90
- }
91
- return sIndex;
92
- }
93
- /**
94
- * 字符串比较
95
- * @param a
96
- * @param b
97
- * @param type 类型
98
- * @param isNumber 是否是数字类型
99
- * @param localeCompare 是否 使用Array.prototyshpe.localeCompare
100
- * @return {number} <0: a < b, 0: a = b, >0: a > b
101
- */
102
- export function strCompare(a: string, b: string, isNumber: boolean, localeCompare = false): number {
103
- let _a: number | string = a;
104
- let _b: number | string = b;
105
- if (isNumber) {
106
- // 是数字就转数字
107
- _a = +a;
108
- _b = +b;
109
- } else if (localeCompare) {
110
- // 字符串才可以localeCompare
111
- return String(a).localeCompare(b);
112
- }
113
- if (_a > _b) return 1;
114
- else if (_a === _b) return 0;
115
- else return -1;
116
- }
117
-
118
- /**
119
- * 分离出空数据和非空数据成两个数组。NaN视为空数据。
120
- * @param sortOption
121
- * @param targetDataSource
122
- * @param isNumber 是否数字
123
- * @return [值数组,空数组]
124
- */
125
- function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>, targetDataSource: T[], isNumber?: boolean) {
126
- const emptyArr: T[] = [];
127
- const valueArr: T[] = [];
128
-
129
- for (let i = 0; i < targetDataSource.length; i++) {
130
- const row = targetDataSource[i];
131
- const sortField = sortOption.sortField || sortOption.dataIndex;
132
- const isEmpty = isEmptyValue(row?.[sortField], isNumber); // deal row is null
133
- if (isEmpty) {
134
- emptyArr.push(row);
135
- } else {
136
- valueArr.push(row);
137
- }
138
- }
139
- return [valueArr, emptyArr] as const;
140
- }
141
-
142
- /**
143
- * 表格排序抽离
144
- * 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
145
- * 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
146
- * TODO: key 唯一值,排序字段相同时,根据唯一值排序。
147
- *
148
- * sortConfig.defaultSort 会在order为null时生效
149
- * @param sortOption 列配置
150
- * @param order 排序方式
151
- * @param dataSource 排序的数组
152
- */
153
- export function tableSort<T extends Record<string, any>>(
154
- sortOption: SortOption<T>,
155
- order: Order,
156
- dataSource: T[],
157
- sortConfig: SortConfig<T> = {},
158
- ): T[] {
159
- if (!dataSource?.length || !sortOption) return dataSource || [];
160
-
161
- sortConfig = { ...DEFAULT_SORT_CONFIG, ...sortConfig };
162
- let targetDataSource = dataSource.slice();
163
- let sortField = sortOption.sortField || sortOption.dataIndex;
164
- const { defaultSort, stringLocaleCompare, emptyToBottom, sortChildren } = sortConfig;
165
-
166
- if (!order && defaultSort) {
167
- // 默认排序
168
- order = defaultSort.order;
169
- sortField = defaultSort.dataIndex;
170
- }
171
-
172
- if (typeof sortOption.sorter === 'function') {
173
- const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
174
- if (customSorterData) targetDataSource = customSorterData;
175
-
176
- // 如果开启了子节点排序且使用了自定义排序器,递归排序children
177
- if (sortChildren) {
178
- targetDataSource.forEach(item => {
179
- if (!item.children?.length) return;
180
- (item as any).children = tableSort(sortOption, order, item.children, sortConfig);
181
- });
182
- }
183
- } else if (order) {
184
- let { sortType } = sortOption;
185
- if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
186
-
187
- const isNumber = sortType === 'number';
188
- const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, isNumber);
189
-
190
- if (order === 'asc') {
191
- valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, stringLocaleCompare));
192
- } else {
193
- valueArr.sort((a, b) => strCompare(b[sortField], a[sortField], isNumber, stringLocaleCompare));
194
- }
195
-
196
- targetDataSource = order === 'desc' || emptyToBottom ? valueArr.concat(emptyArr) : emptyArr.concat(valueArr);
197
-
198
- // 递归排序子节点
199
- if (sortChildren) {
200
- targetDataSource.forEach(item => {
201
- if (!item.children?.length) return;
202
- (item as any).children = tableSort(sortOption, order, item.children, sortConfig);
203
- });
204
- }
205
- }
206
- return targetDataSource;
207
- }
208
-
209
- /** 多级表头深度 从0开始为一级*/
210
- export function howDeepTheHeader(arr: StkTableColumn<any>[], level = 0) {
211
- const levels = [level];
212
- arr.forEach(item => {
213
- if (item.children?.length) {
214
- levels.push(howDeepTheHeader(item.children, level + 1));
215
- }
216
- });
217
- return Math.max(...levels);
218
- }
219
-
220
- /** number width +px */
221
- export function transformWidthToStr(width?: string | number) {
222
- if (width === void 0) return;
223
- const numberWidth = Number(width);
224
- return width + (!Number.isNaN(numberWidth) ? 'px' : '');
225
- }
226
-
227
- export function getBrowsersVersion(browserName: string) {
228
- try {
229
- const reg = new RegExp(`${browserName}/\\d+`, 'i');
230
- const userAgent = navigator.userAgent.match(reg);
231
- if (userAgent) {
232
- return +userAgent[0].split('/')[1];
233
- }
234
- } catch (e) {
235
- console.error('Cannot get version', e);
236
- }
237
- return 100;
238
- }
239
-
240
- export function pureCellKeyGen(rowKey: UniqKey, colKey: UniqKey) {
241
- return rowKey + CELL_KEY_SEPARATE + colKey;
242
- }
243
-
244
- export function getClosestTr(e: MouseEvent) {
245
- const target = e.target as HTMLElement;
246
- const tr = target?.closest('tr');
247
- return tr;
248
- }
249
-
250
- export function getClosestTrIndex(e: MouseEvent) {
251
- const tr = getClosestTr(e);
252
- if (!tr) return -1;
253
- return Number(tr.dataset.rowI);
254
- }
255
-
256
- export function getClosestColKey(e: MouseEvent) {
257
- return (e.target as HTMLElement)?.closest('td')?.dataset.colKey;
258
- }
1
+ import { CELL_KEY_SEPARATE, DEFAULT_SORT_CONFIG } from '../const';
2
+ import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from '../types';
3
+
4
+ /** 是否空值 */
5
+ export function isEmptyValue(val: any, isNumber?: boolean) {
6
+ let isEmpty = val === null || val === void 0;
7
+ if (isNumber) {
8
+ isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
9
+ }
10
+ return isEmpty;
11
+ }
12
+
13
+ /**
14
+ * 对有序数组插入新数据
15
+ *
16
+ * 注意:不会改变原数组,返回新数组
17
+ * @param sortState
18
+ * @param sortState.dataIndex 排序的字段
19
+ * @param sortState.order 排序顺序
20
+ * @param sortState.sortType 排序方式
21
+ * @param newItem 要插入的数据
22
+ * @param targetArray 表格数据
23
+ * @return targetArray 的浅拷贝
24
+ */
25
+ export function insertToOrderedArray<T extends object>(
26
+ sortState: SortState<T>,
27
+ newItem: T,
28
+ targetArray: T[],
29
+ sortConfig: SortConfig<T> & { customCompare?: (a: T, b: T) => number } = {},
30
+ ) {
31
+ const { dataIndex, sortField, order } = sortState;
32
+ let { sortType } = sortState;
33
+ const field = sortField || dataIndex;
34
+ if (!sortType) sortType = typeof newItem[field] as 'number' | 'string';
35
+ const data = targetArray.slice();
36
+
37
+ if (!order || !data.length) {
38
+ // 没有排序的情况,插入在最上方
39
+ data.unshift(newItem);
40
+ return data;
41
+ }
42
+
43
+ const { emptyToBottom, customCompare, stringLocaleCompare } = { emptyToBottom: false, ...sortConfig };
44
+
45
+ const targetVal: any = newItem[field];
46
+ if (emptyToBottom && isEmptyValue(targetVal)) {
47
+ // 空值排在最下方
48
+ data.push(newItem);
49
+ } else {
50
+ const customCompareFn =
51
+ customCompare ||
52
+ ((a, b) => {
53
+ const midVal: any = a[field];
54
+ const compareRes = strCompare(midVal, targetVal, isNumber, stringLocaleCompare);
55
+ return order === 'asc' ? compareRes : -compareRes;
56
+ });
57
+ const isNumber = sortType === 'number';
58
+ // 二分插入
59
+ const sIndex = binarySearch(data, midIndex => {
60
+ return customCompareFn(data[midIndex], newItem);
61
+ });
62
+ data.splice(sIndex, 0, newItem);
63
+ }
64
+
65
+ return data;
66
+ }
67
+
68
+ /**
69
+ * 二分查找
70
+ * @param searchArray 查找数组
71
+ * @param compareCallback 比较函数,返回 -1|0|1
72
+ */
73
+ export function binarySearch(searchArray: any[], compareCallback: (midIndex: number) => number) {
74
+ let sIndex = 0;
75
+ let eIndex = searchArray.length - 1;
76
+ while (sIndex <= eIndex) {
77
+ const midIndex = Math.floor((sIndex + eIndex) / 2);
78
+ const compareRes = compareCallback(midIndex);
79
+ if (compareRes === 0) {
80
+ //midVal == targetVal
81
+ sIndex = midIndex;
82
+ break;
83
+ } else if (compareRes < 0) {
84
+ // midVal < targetVal
85
+ sIndex = midIndex + 1;
86
+ } else {
87
+ //midVal > targetVal
88
+ eIndex = midIndex - 1;
89
+ }
90
+ }
91
+ return sIndex;
92
+ }
93
+ /**
94
+ * 字符串比较
95
+ * @param a
96
+ * @param b
97
+ * @param type 类型
98
+ * @param isNumber 是否是数字类型
99
+ * @param localeCompare 是否 使用Array.prototyshpe.localeCompare
100
+ * @return {number} <0: a < b, 0: a = b, >0: a > b
101
+ */
102
+ export function strCompare(a: string, b: string, isNumber: boolean, localeCompare = false): number {
103
+ let _a: number | string = a;
104
+ let _b: number | string = b;
105
+ if (isNumber) {
106
+ // 是数字就转数字
107
+ _a = +a;
108
+ _b = +b;
109
+ } else if (localeCompare) {
110
+ // 字符串才可以localeCompare
111
+ return String(a).localeCompare(b);
112
+ }
113
+ if (_a > _b) return 1;
114
+ else if (_a === _b) return 0;
115
+ else return -1;
116
+ }
117
+
118
+ /**
119
+ * 分离出空数据和非空数据成两个数组。NaN视为空数据。
120
+ * @param sortOption
121
+ * @param targetDataSource
122
+ * @param isNumber 是否数字
123
+ * @return [值数组,空数组]
124
+ */
125
+ function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>, targetDataSource: T[], isNumber?: boolean) {
126
+ const emptyArr: T[] = [];
127
+ const valueArr: T[] = [];
128
+
129
+ for (let i = 0; i < targetDataSource.length; i++) {
130
+ const row = targetDataSource[i];
131
+ const sortField = sortOption.sortField || sortOption.dataIndex;
132
+ const isEmpty = isEmptyValue(row?.[sortField], isNumber); // deal row is null
133
+ if (isEmpty) {
134
+ emptyArr.push(row);
135
+ } else {
136
+ valueArr.push(row);
137
+ }
138
+ }
139
+ return [valueArr, emptyArr] as const;
140
+ }
141
+
142
+ /**
143
+ * 表格排序抽离
144
+ * 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
145
+ * 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
146
+ * TODO: key 唯一值,排序字段相同时,根据唯一值排序。
147
+ *
148
+ * sortConfig.defaultSort 会在order为null时生效
149
+ * @param sortOption 列配置
150
+ * @param order 排序方式
151
+ * @param dataSource 排序的数组
152
+ */
153
+ export function tableSort<T extends Record<string, any>>(
154
+ sortOption: SortOption<T>,
155
+ order: Order,
156
+ dataSource: T[],
157
+ sortConfig: SortConfig<T> = {},
158
+ ): T[] {
159
+ if (!dataSource?.length || !sortOption) return dataSource || [];
160
+
161
+ sortConfig = { ...DEFAULT_SORT_CONFIG, ...sortConfig };
162
+ let targetDataSource = dataSource.slice();
163
+ let sortField = sortOption.sortField || sortOption.dataIndex;
164
+ const { defaultSort, stringLocaleCompare, emptyToBottom, sortChildren } = sortConfig;
165
+
166
+ if (!order && defaultSort) {
167
+ // 默认排序
168
+ order = defaultSort.order;
169
+ sortField = defaultSort.dataIndex;
170
+ }
171
+
172
+ if (typeof sortOption.sorter === 'function') {
173
+ const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
174
+ if (customSorterData) targetDataSource = customSorterData;
175
+
176
+ // 如果开启了子节点排序且使用了自定义排序器,递归排序children
177
+ if (sortChildren) {
178
+ targetDataSource.forEach(item => {
179
+ if (!item.children?.length) return;
180
+ (item as any).children = tableSort(sortOption, order, item.children, sortConfig);
181
+ });
182
+ }
183
+ } else if (order) {
184
+ let { sortType } = sortOption;
185
+ if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
186
+
187
+ const isNumber = sortType === 'number';
188
+ const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, isNumber);
189
+
190
+ if (order === 'asc') {
191
+ valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, stringLocaleCompare));
192
+ } else {
193
+ valueArr.sort((a, b) => strCompare(b[sortField], a[sortField], isNumber, stringLocaleCompare));
194
+ }
195
+
196
+ targetDataSource = order === 'desc' || emptyToBottom ? valueArr.concat(emptyArr) : emptyArr.concat(valueArr);
197
+
198
+ // 递归排序子节点
199
+ if (sortChildren) {
200
+ targetDataSource.forEach(item => {
201
+ if (!item.children?.length) return;
202
+ (item as any).children = tableSort(sortOption, order, item.children, sortConfig);
203
+ });
204
+ }
205
+ }
206
+ return targetDataSource;
207
+ }
208
+
209
+ /** 多级表头深度 从0开始为一级*/
210
+ export function howDeepTheHeader(arr: StkTableColumn<any>[], level = 0) {
211
+ const levels = [level];
212
+ arr.forEach(item => {
213
+ if (item.children?.length) {
214
+ levels.push(howDeepTheHeader(item.children, level + 1));
215
+ }
216
+ });
217
+ return Math.max(...levels);
218
+ }
219
+
220
+ /** number width +px */
221
+ export function transformWidthToStr(width?: string | number) {
222
+ if (width === void 0) return;
223
+ const numberWidth = Number(width);
224
+ return width + (!Number.isNaN(numberWidth) ? 'px' : '');
225
+ }
226
+
227
+ export function getBrowsersVersion(browserName: string) {
228
+ try {
229
+ const reg = new RegExp(`${browserName}/\\d+`, 'i');
230
+ const userAgent = navigator.userAgent.match(reg);
231
+ if (userAgent) {
232
+ return +userAgent[0].split('/')[1];
233
+ }
234
+ } catch (e) {
235
+ console.error('Cannot get version', e);
236
+ }
237
+ return 100;
238
+ }
239
+
240
+ export function pureCellKeyGen(rowKey: UniqKey, colKey: UniqKey) {
241
+ return rowKey + CELL_KEY_SEPARATE + colKey;
242
+ }
243
+
244
+ export function getClosestTr(e: MouseEvent) {
245
+ const target = e.target as HTMLElement;
246
+ const tr = target?.closest('tr');
247
+ return tr;
248
+ }
249
+
250
+ export function getClosestTrIndex(e: MouseEvent) {
251
+ const tr = getClosestTr(e);
252
+ if (!tr) return -1;
253
+ return Number(tr.dataset.rowI);
254
+ }
255
+
256
+ export function getClosestColKey(e: MouseEvent) {
257
+ return (e.target as HTMLElement)?.closest('td')?.dataset.colKey;
258
+ }
259
+
260
+ /**
261
+ * 改进的节流函数,确保最后一个调用不会被丢弃
262
+ * @param fn 要执行的函数
263
+ * @param delay 延迟时间(毫秒)
264
+ * @returns 节流处理后的函数
265
+ */
266
+ export function throttle<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {
267
+ let timer: number;
268
+ let lastArgs: Parameters<T> | null = null;
269
+
270
+ const callFn = function () {
271
+ if (lastArgs) {
272
+ fn(...lastArgs);
273
+ lastArgs = null;
274
+ }
275
+ };
276
+
277
+ return function (...args: Parameters<T>) {
278
+ lastArgs = args;
279
+ if (!timer) {
280
+ callFn();
281
+ timer = self.setTimeout(() => {
282
+ callFn();
283
+ timer = 0;
284
+ }, delay);
285
+ }
286
+ };
287
+ }
@@ -1,33 +1,33 @@
1
- import { Ref, shallowRef } from 'vue';
2
-
3
- type UseTriggerRef<T> = {
4
- getRef: () => Ref<T>;
5
- getValue: () => T;
6
- setValue: (v: T) => void;
7
- triggerRef: () => void;
8
- };
9
- /**
10
- * 创建一个可触发更新的引用对象。
11
- * @template T 引用对象的类型。
12
- * @param initialValue 初始值。
13
- * @returns 包含获取引用、获取值、设置值和触发更新的函数的对象。
14
- */
15
- export function useTriggerRef<T>(initialValue: T): UseTriggerRef<T> {
16
- let value = initialValue;
17
- const ref = shallowRef(value);
18
-
19
- function getValue() {
20
- return value;
21
- }
22
- function setValue(v: T) {
23
- value = v;
24
- }
25
- function getRef() {
26
- return ref;
27
- }
28
- function triggerRef() {
29
- ref.value = value;
30
- }
31
-
32
- return { getRef, getValue, setValue, triggerRef };
33
- }
1
+ import { Ref, shallowRef } from 'vue';
2
+
3
+ type UseTriggerRef<T> = {
4
+ getRef: () => Ref<T>;
5
+ getValue: () => T;
6
+ setValue: (v: T) => void;
7
+ triggerRef: () => void;
8
+ };
9
+ /**
10
+ * 创建一个可触发更新的引用对象。
11
+ * @template T 引用对象的类型。
12
+ * @param initialValue 初始值。
13
+ * @returns 包含获取引用、获取值、设置值和触发更新的函数的对象。
14
+ */
15
+ export function useTriggerRef<T>(initialValue: T): UseTriggerRef<T> {
16
+ let value = initialValue;
17
+ const ref = shallowRef(value);
18
+
19
+ function getValue() {
20
+ return value;
21
+ }
22
+ function setValue(v: T) {
23
+ value = v;
24
+ }
25
+ function getRef() {
26
+ return ref;
27
+ }
28
+ function triggerRef() {
29
+ ref.value = value;
30
+ }
31
+
32
+ return { getRef, getValue, setValue, triggerRef };
33
+ }