stk-table-vue 0.6.10 → 0.6.12
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 +201 -201
- package/lib/stk-table-vue.js +24 -12
- package/package.json +74 -74
- package/src/StkTable/StkTable.vue +1546 -1541
- package/src/StkTable/components/DragHandle.vue +9 -9
- package/src/StkTable/components/SortIcon.vue +6 -6
- package/src/StkTable/const.ts +37 -37
- package/src/StkTable/index.ts +4 -4
- package/src/StkTable/style.less +553 -553
- package/src/StkTable/types/highlightDimOptions.ts +26 -26
- package/src/StkTable/types/index.ts +239 -239
- package/src/StkTable/useAutoResize.ts +91 -91
- package/src/StkTable/useColResize.ts +216 -216
- package/src/StkTable/useFixedCol.ts +148 -148
- package/src/StkTable/useFixedStyle.ts +75 -75
- package/src/StkTable/useGetFixedColPosition.ts +65 -65
- package/src/StkTable/useHighlight.ts +318 -318
- package/src/StkTable/useKeyboardArrowScroll.ts +112 -106
- package/src/StkTable/useThDrag.ts +102 -102
- package/src/StkTable/useTrDrag.ts +118 -118
- package/src/StkTable/useVirtualScroll.ts +447 -447
- package/src/StkTable/utils/constRefUtils.ts +29 -29
- package/src/StkTable/utils/index.ts +212 -212
- package/src/StkTable/utils/useTriggerRef.ts +33 -33
- package/src/VirtualTree.vue +622 -622
- package/src/VirtualTreeSelect.vue +367 -367
- 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,212 +1,212 @@
|
|
|
1
|
-
import { Order, SortConfig, SortOption, SortState, StkTableColumn } from '../types';
|
|
2
|
-
|
|
3
|
-
/** 是否空值 */
|
|
4
|
-
export function isEmptyValue(val: any, isNumber?: boolean) {
|
|
5
|
-
let isEmpty = val === null || val === void 0;
|
|
6
|
-
if (isNumber) {
|
|
7
|
-
isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
|
|
8
|
-
}
|
|
9
|
-
return isEmpty;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 对有序数组插入新数据
|
|
14
|
-
*
|
|
15
|
-
* 注意:不会改变原数组,返回新数组
|
|
16
|
-
* @param sortState
|
|
17
|
-
* @param sortState.dataIndex 排序的字段
|
|
18
|
-
* @param sortState.order 排序顺序
|
|
19
|
-
* @param sortState.sortType 排序方式
|
|
20
|
-
* @param newItem 要插入的数据
|
|
21
|
-
* @param targetArray 表格数据
|
|
22
|
-
* @return targetArray 的浅拷贝
|
|
23
|
-
*/
|
|
24
|
-
export function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig: SortConfig<T> = {}) {
|
|
25
|
-
const { dataIndex, order } = sortState;
|
|
26
|
-
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
27
|
-
let { sortType } = sortState;
|
|
28
|
-
if (!sortType) sortType = typeof newItem[dataIndex] as 'number' | 'string';
|
|
29
|
-
const data = targetArray.slice();
|
|
30
|
-
|
|
31
|
-
if (!order || !data.length) {
|
|
32
|
-
// 没有排序的情况,插入在最上方
|
|
33
|
-
data.unshift(newItem);
|
|
34
|
-
return data;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (sortConfig.emptyToBottom && isEmptyValue(newItem)) {
|
|
38
|
-
// 空值排在最下方
|
|
39
|
-
data.push(newItem);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const isNumber = sortType === 'number';
|
|
43
|
-
|
|
44
|
-
// 二分插入
|
|
45
|
-
const targetVal: any = newItem[dataIndex];
|
|
46
|
-
const sIndex = binarySearch(data, midIndex => {
|
|
47
|
-
const midVal: any = data[midIndex][dataIndex];
|
|
48
|
-
const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
|
|
49
|
-
return order === 'asc' ? compareRes : -compareRes;
|
|
50
|
-
});
|
|
51
|
-
data.splice(sIndex, 0, newItem);
|
|
52
|
-
return data;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* 二分查找
|
|
57
|
-
* @param searchArray 查找数组
|
|
58
|
-
* @param compareCallback 比较函数,返回 -1|0|1
|
|
59
|
-
*/
|
|
60
|
-
export function binarySearch(searchArray: any[], compareCallback: (midIndex: number) => number) {
|
|
61
|
-
let sIndex = 0;
|
|
62
|
-
let eIndex = searchArray.length - 1;
|
|
63
|
-
while (sIndex <= eIndex) {
|
|
64
|
-
const midIndex = Math.floor((sIndex + eIndex) / 2);
|
|
65
|
-
const compareRes = compareCallback(midIndex);
|
|
66
|
-
if (compareRes === 0) {
|
|
67
|
-
//midVal == targetVal
|
|
68
|
-
sIndex = midIndex;
|
|
69
|
-
break;
|
|
70
|
-
} else if (compareRes < 0) {
|
|
71
|
-
// midVal < targetVal
|
|
72
|
-
sIndex = midIndex + 1;
|
|
73
|
-
} else {
|
|
74
|
-
//midVal > targetVal
|
|
75
|
-
eIndex = midIndex - 1;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return sIndex;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* 字符串比较
|
|
82
|
-
* @param a
|
|
83
|
-
* @param b
|
|
84
|
-
* @param type 类型
|
|
85
|
-
* @param isNumber 是否是数字类型
|
|
86
|
-
* @param localeCompare 是否 使用Array.prototyshpe.localeCompare
|
|
87
|
-
* @return {number} <0: a < b, 0: a = b, >0: a > b
|
|
88
|
-
*/
|
|
89
|
-
export function strCompare(a: string, b: string, isNumber: boolean, localeCompare = false): number {
|
|
90
|
-
let _a: number | string = a;
|
|
91
|
-
let _b: number | string = b;
|
|
92
|
-
if (isNumber) {
|
|
93
|
-
// 是数字就转数字
|
|
94
|
-
_a = +a;
|
|
95
|
-
_b = +b;
|
|
96
|
-
} else if (localeCompare) {
|
|
97
|
-
// 字符串才可以localeCompare
|
|
98
|
-
return String(a).localeCompare(b);
|
|
99
|
-
}
|
|
100
|
-
if (_a > _b) return 1;
|
|
101
|
-
else if (_a === _b) return 0;
|
|
102
|
-
else return -1;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* 分离出空数据和非空数据成两个数组。NaN视为空数据。
|
|
107
|
-
* @param sortOption
|
|
108
|
-
* @param targetDataSource
|
|
109
|
-
* @param isNumber 是否数字
|
|
110
|
-
* @return [值数组,空数组]
|
|
111
|
-
*/
|
|
112
|
-
function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>, targetDataSource: T[], isNumber?: boolean) {
|
|
113
|
-
const emptyArr: T[] = [];
|
|
114
|
-
const valueArr: T[] = [];
|
|
115
|
-
|
|
116
|
-
for (let i = 0; i < targetDataSource.length; i++) {
|
|
117
|
-
const row = targetDataSource[i];
|
|
118
|
-
const sortField = sortOption.sortField || sortOption.dataIndex;
|
|
119
|
-
const isEmpty = isEmptyValue(row?.[sortField], isNumber); // deal row is null
|
|
120
|
-
if (isEmpty) {
|
|
121
|
-
emptyArr.push(row);
|
|
122
|
-
} else {
|
|
123
|
-
valueArr.push(row);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return [valueArr, emptyArr] as const;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 表格排序抽离
|
|
131
|
-
* 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
|
|
132
|
-
* 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
|
|
133
|
-
* TODO: key 唯一值,排序字段相同时,根据唯一值排序。
|
|
134
|
-
*
|
|
135
|
-
* sortConfig.defaultSort 会在order为null时生效
|
|
136
|
-
* @param sortOption 列配置
|
|
137
|
-
* @param order 排序方式
|
|
138
|
-
* @param dataSource 排序的数组
|
|
139
|
-
*/
|
|
140
|
-
export function tableSort<T extends Record<string, any>>(
|
|
141
|
-
sortOption: SortOption<T>,
|
|
142
|
-
order: Order,
|
|
143
|
-
dataSource: T[],
|
|
144
|
-
sortConfig: SortConfig<T> = {},
|
|
145
|
-
): T[] {
|
|
146
|
-
if (!dataSource?.length || !sortOption) return dataSource || [];
|
|
147
|
-
|
|
148
|
-
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
149
|
-
let targetDataSource = dataSource.slice();
|
|
150
|
-
let sortField = sortOption.sortField || sortOption.dataIndex;
|
|
151
|
-
|
|
152
|
-
if (!order && sortConfig.defaultSort) {
|
|
153
|
-
// 默认排序
|
|
154
|
-
order = sortConfig.defaultSort.order;
|
|
155
|
-
sortField = sortConfig.defaultSort.dataIndex;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (typeof sortOption.sorter === 'function') {
|
|
159
|
-
const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
|
|
160
|
-
if (customSorterData) targetDataSource = customSorterData;
|
|
161
|
-
} else if (order) {
|
|
162
|
-
let { sortType } = sortOption;
|
|
163
|
-
if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
|
|
164
|
-
|
|
165
|
-
const isNumber = sortType === 'number';
|
|
166
|
-
const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, isNumber);
|
|
167
|
-
|
|
168
|
-
if (order === 'asc') {
|
|
169
|
-
valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, sortConfig.stringLocaleCompare));
|
|
170
|
-
} else {
|
|
171
|
-
valueArr.sort((a, b) => strCompare(b[sortField], a[sortField], isNumber, sortConfig.stringLocaleCompare));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (order === 'desc' || sortConfig.emptyToBottom) {
|
|
175
|
-
targetDataSource = valueArr.concat(emptyArr);
|
|
176
|
-
} else {
|
|
177
|
-
targetDataSource = emptyArr.concat(valueArr);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return targetDataSource;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** 多级表头深度 从0开始为一级*/
|
|
184
|
-
export function howDeepTheHeader(arr: StkTableColumn<any>[], level = 0) {
|
|
185
|
-
const levels = [level];
|
|
186
|
-
arr.forEach(item => {
|
|
187
|
-
if (item.children?.length) {
|
|
188
|
-
levels.push(howDeepTheHeader(item.children, level + 1));
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
return Math.max(...levels);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** number width +px */
|
|
195
|
-
export function transformWidthToStr(width?: string | number) {
|
|
196
|
-
if (width === void 0) return;
|
|
197
|
-
const numberWidth = Number(width);
|
|
198
|
-
return width + (!Number.isNaN(numberWidth) ? 'px' : '');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function getBrowsersVersion(browserName: string) {
|
|
202
|
-
try {
|
|
203
|
-
const reg = new RegExp(`${browserName}/\\d+`, 'i');
|
|
204
|
-
const userAgent = navigator.userAgent.match(reg);
|
|
205
|
-
if (userAgent) {
|
|
206
|
-
return +userAgent[0].split('/')[1];
|
|
207
|
-
}
|
|
208
|
-
} catch (e) {
|
|
209
|
-
console.error('Cannot get version', e);
|
|
210
|
-
}
|
|
211
|
-
return 100;
|
|
212
|
-
}
|
|
1
|
+
import { Order, SortConfig, SortOption, SortState, StkTableColumn } from '../types';
|
|
2
|
+
|
|
3
|
+
/** 是否空值 */
|
|
4
|
+
export function isEmptyValue(val: any, isNumber?: boolean) {
|
|
5
|
+
let isEmpty = val === null || val === void 0;
|
|
6
|
+
if (isNumber) {
|
|
7
|
+
isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
|
|
8
|
+
}
|
|
9
|
+
return isEmpty;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 对有序数组插入新数据
|
|
14
|
+
*
|
|
15
|
+
* 注意:不会改变原数组,返回新数组
|
|
16
|
+
* @param sortState
|
|
17
|
+
* @param sortState.dataIndex 排序的字段
|
|
18
|
+
* @param sortState.order 排序顺序
|
|
19
|
+
* @param sortState.sortType 排序方式
|
|
20
|
+
* @param newItem 要插入的数据
|
|
21
|
+
* @param targetArray 表格数据
|
|
22
|
+
* @return targetArray 的浅拷贝
|
|
23
|
+
*/
|
|
24
|
+
export function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig: SortConfig<T> = {}) {
|
|
25
|
+
const { dataIndex, order } = sortState;
|
|
26
|
+
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
27
|
+
let { sortType } = sortState;
|
|
28
|
+
if (!sortType) sortType = typeof newItem[dataIndex] as 'number' | 'string';
|
|
29
|
+
const data = targetArray.slice();
|
|
30
|
+
|
|
31
|
+
if (!order || !data.length) {
|
|
32
|
+
// 没有排序的情况,插入在最上方
|
|
33
|
+
data.unshift(newItem);
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (sortConfig.emptyToBottom && isEmptyValue(newItem)) {
|
|
38
|
+
// 空值排在最下方
|
|
39
|
+
data.push(newItem);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const isNumber = sortType === 'number';
|
|
43
|
+
|
|
44
|
+
// 二分插入
|
|
45
|
+
const targetVal: any = newItem[dataIndex];
|
|
46
|
+
const sIndex = binarySearch(data, midIndex => {
|
|
47
|
+
const midVal: any = data[midIndex][dataIndex];
|
|
48
|
+
const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
|
|
49
|
+
return order === 'asc' ? compareRes : -compareRes;
|
|
50
|
+
});
|
|
51
|
+
data.splice(sIndex, 0, newItem);
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 二分查找
|
|
57
|
+
* @param searchArray 查找数组
|
|
58
|
+
* @param compareCallback 比较函数,返回 -1|0|1
|
|
59
|
+
*/
|
|
60
|
+
export function binarySearch(searchArray: any[], compareCallback: (midIndex: number) => number) {
|
|
61
|
+
let sIndex = 0;
|
|
62
|
+
let eIndex = searchArray.length - 1;
|
|
63
|
+
while (sIndex <= eIndex) {
|
|
64
|
+
const midIndex = Math.floor((sIndex + eIndex) / 2);
|
|
65
|
+
const compareRes = compareCallback(midIndex);
|
|
66
|
+
if (compareRes === 0) {
|
|
67
|
+
//midVal == targetVal
|
|
68
|
+
sIndex = midIndex;
|
|
69
|
+
break;
|
|
70
|
+
} else if (compareRes < 0) {
|
|
71
|
+
// midVal < targetVal
|
|
72
|
+
sIndex = midIndex + 1;
|
|
73
|
+
} else {
|
|
74
|
+
//midVal > targetVal
|
|
75
|
+
eIndex = midIndex - 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return sIndex;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 字符串比较
|
|
82
|
+
* @param a
|
|
83
|
+
* @param b
|
|
84
|
+
* @param type 类型
|
|
85
|
+
* @param isNumber 是否是数字类型
|
|
86
|
+
* @param localeCompare 是否 使用Array.prototyshpe.localeCompare
|
|
87
|
+
* @return {number} <0: a < b, 0: a = b, >0: a > b
|
|
88
|
+
*/
|
|
89
|
+
export function strCompare(a: string, b: string, isNumber: boolean, localeCompare = false): number {
|
|
90
|
+
let _a: number | string = a;
|
|
91
|
+
let _b: number | string = b;
|
|
92
|
+
if (isNumber) {
|
|
93
|
+
// 是数字就转数字
|
|
94
|
+
_a = +a;
|
|
95
|
+
_b = +b;
|
|
96
|
+
} else if (localeCompare) {
|
|
97
|
+
// 字符串才可以localeCompare
|
|
98
|
+
return String(a).localeCompare(b);
|
|
99
|
+
}
|
|
100
|
+
if (_a > _b) return 1;
|
|
101
|
+
else if (_a === _b) return 0;
|
|
102
|
+
else return -1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 分离出空数据和非空数据成两个数组。NaN视为空数据。
|
|
107
|
+
* @param sortOption
|
|
108
|
+
* @param targetDataSource
|
|
109
|
+
* @param isNumber 是否数字
|
|
110
|
+
* @return [值数组,空数组]
|
|
111
|
+
*/
|
|
112
|
+
function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>, targetDataSource: T[], isNumber?: boolean) {
|
|
113
|
+
const emptyArr: T[] = [];
|
|
114
|
+
const valueArr: T[] = [];
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < targetDataSource.length; i++) {
|
|
117
|
+
const row = targetDataSource[i];
|
|
118
|
+
const sortField = sortOption.sortField || sortOption.dataIndex;
|
|
119
|
+
const isEmpty = isEmptyValue(row?.[sortField], isNumber); // deal row is null
|
|
120
|
+
if (isEmpty) {
|
|
121
|
+
emptyArr.push(row);
|
|
122
|
+
} else {
|
|
123
|
+
valueArr.push(row);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return [valueArr, emptyArr] as const;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 表格排序抽离
|
|
131
|
+
* 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
|
|
132
|
+
* 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
|
|
133
|
+
* TODO: key 唯一值,排序字段相同时,根据唯一值排序。
|
|
134
|
+
*
|
|
135
|
+
* sortConfig.defaultSort 会在order为null时生效
|
|
136
|
+
* @param sortOption 列配置
|
|
137
|
+
* @param order 排序方式
|
|
138
|
+
* @param dataSource 排序的数组
|
|
139
|
+
*/
|
|
140
|
+
export function tableSort<T extends Record<string, any>>(
|
|
141
|
+
sortOption: SortOption<T>,
|
|
142
|
+
order: Order,
|
|
143
|
+
dataSource: T[],
|
|
144
|
+
sortConfig: SortConfig<T> = {},
|
|
145
|
+
): T[] {
|
|
146
|
+
if (!dataSource?.length || !sortOption) return dataSource || [];
|
|
147
|
+
|
|
148
|
+
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
149
|
+
let targetDataSource = dataSource.slice();
|
|
150
|
+
let sortField = sortOption.sortField || sortOption.dataIndex;
|
|
151
|
+
|
|
152
|
+
if (!order && sortConfig.defaultSort) {
|
|
153
|
+
// 默认排序
|
|
154
|
+
order = sortConfig.defaultSort.order;
|
|
155
|
+
sortField = sortConfig.defaultSort.dataIndex;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (typeof sortOption.sorter === 'function') {
|
|
159
|
+
const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
|
|
160
|
+
if (customSorterData) targetDataSource = customSorterData;
|
|
161
|
+
} else if (order) {
|
|
162
|
+
let { sortType } = sortOption;
|
|
163
|
+
if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
|
|
164
|
+
|
|
165
|
+
const isNumber = sortType === 'number';
|
|
166
|
+
const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, isNumber);
|
|
167
|
+
|
|
168
|
+
if (order === 'asc') {
|
|
169
|
+
valueArr.sort((a, b) => strCompare(a[sortField], b[sortField], isNumber, sortConfig.stringLocaleCompare));
|
|
170
|
+
} else {
|
|
171
|
+
valueArr.sort((a, b) => strCompare(b[sortField], a[sortField], isNumber, sortConfig.stringLocaleCompare));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (order === 'desc' || sortConfig.emptyToBottom) {
|
|
175
|
+
targetDataSource = valueArr.concat(emptyArr);
|
|
176
|
+
} else {
|
|
177
|
+
targetDataSource = emptyArr.concat(valueArr);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return targetDataSource;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** 多级表头深度 从0开始为一级*/
|
|
184
|
+
export function howDeepTheHeader(arr: StkTableColumn<any>[], level = 0) {
|
|
185
|
+
const levels = [level];
|
|
186
|
+
arr.forEach(item => {
|
|
187
|
+
if (item.children?.length) {
|
|
188
|
+
levels.push(howDeepTheHeader(item.children, level + 1));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return Math.max(...levels);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** number width +px */
|
|
195
|
+
export function transformWidthToStr(width?: string | number) {
|
|
196
|
+
if (width === void 0) return;
|
|
197
|
+
const numberWidth = Number(width);
|
|
198
|
+
return width + (!Number.isNaN(numberWidth) ? 'px' : '');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getBrowsersVersion(browserName: string) {
|
|
202
|
+
try {
|
|
203
|
+
const reg = new RegExp(`${browserName}/\\d+`, 'i');
|
|
204
|
+
const userAgent = navigator.userAgent.match(reg);
|
|
205
|
+
if (userAgent) {
|
|
206
|
+
return +userAgent[0].split('/')[1];
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
console.error('Cannot get version', e);
|
|
210
|
+
}
|
|
211
|
+
return 100;
|
|
212
|
+
}
|
|
@@ -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
|
+
}
|