stk-table-vue 0.2.1 → 0.2.3
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 +16 -5
- package/lib/src/StkTable/StkTable.vue.d.ts +384 -373
- package/lib/src/StkTable/const.d.ts +21 -21
- package/lib/src/StkTable/index.d.ts +3 -3
- package/lib/src/StkTable/types/index.d.ts +87 -73
- package/lib/src/StkTable/useAutoResize.d.ts +14 -14
- package/lib/src/StkTable/useColResize.d.ts +18 -18
- package/lib/src/StkTable/useFixedCol.d.ts +20 -20
- package/lib/src/StkTable/useFixedStyle.d.ts +20 -20
- package/lib/src/StkTable/useHighlight.d.ts +26 -19
- package/lib/src/StkTable/useKeyboardArrowScroll.d.ts +17 -17
- package/lib/src/StkTable/useThDrag.d.ts +17 -17
- package/lib/src/StkTable/useVirtualScroll.d.ts +73 -73
- package/lib/src/StkTable/utils.d.ts +27 -25
- package/lib/stk-table-vue.js +1463 -1422
- package/lib/style.css +298 -298
- package/lib/test/StkTable.browser.test.d.ts +1 -1
- package/lib/test/insertToOrderedArray.test.d.ts +1 -1
- package/lib/test/utils/DragResize.d.ts +28 -28
- package/lib/test/utils/h.d.ts +10 -10
- package/package.json +1 -1
- package/src/StkTable/StkTable.vue +44 -22
- package/src/StkTable/types/index.ts +17 -2
- package/src/StkTable/useHighlight.ts +50 -33
- package/src/StkTable/utils.ts +60 -21
package/package.json
CHANGED
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
[rowClassName(row, i)]: true,
|
|
149
149
|
}"
|
|
150
150
|
:style="{
|
|
151
|
-
backgroundColor: row
|
|
151
|
+
backgroundColor: highlightRowStore[rowKeyGen(row)]?.bgc,
|
|
152
152
|
}"
|
|
153
153
|
@click="e => onRowClick(e, row)"
|
|
154
154
|
@dblclick="e => onRowDblclick(e, row)"
|
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
*/
|
|
192
192
|
import { CSSProperties, onMounted, ref, shallowRef, toRaw, watch } from 'vue';
|
|
193
193
|
import { Default_Row_Height } from './const';
|
|
194
|
-
import { Order, SortOption, SortState, StkTableColumn,
|
|
194
|
+
import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKeyProp } from './types/index';
|
|
195
195
|
import { useAutoResize } from './useAutoResize';
|
|
196
196
|
import { useColResize } from './useColResize';
|
|
197
197
|
import { useFixedCol } from './useFixedCol';
|
|
@@ -235,9 +235,9 @@ const props = withDefaults(
|
|
|
235
235
|
/** 表格数据源 */
|
|
236
236
|
dataSource?: DT[];
|
|
237
237
|
/** 行唯一键 */
|
|
238
|
-
rowKey?:
|
|
238
|
+
rowKey?: UniqKeyProp;
|
|
239
239
|
/** 列唯一键 */
|
|
240
|
-
colKey?:
|
|
240
|
+
colKey?: UniqKeyProp;
|
|
241
241
|
/** 空值展示文字 */
|
|
242
242
|
emptyCellText?: string;
|
|
243
243
|
/** 暂无数据兜底高度是否撑满 */
|
|
@@ -285,6 +285,8 @@ const props = withDefaults(
|
|
|
285
285
|
fixedColShadow?: boolean;
|
|
286
286
|
/** 优化vue2 滚动 */
|
|
287
287
|
optimizeVue2Scroll?: boolean;
|
|
288
|
+
/** 排序配置 */
|
|
289
|
+
sortConfig?: SortConfig<DT>;
|
|
288
290
|
}>(),
|
|
289
291
|
{
|
|
290
292
|
width: '',
|
|
@@ -317,6 +319,9 @@ const props = withDefaults(
|
|
|
317
319
|
autoResize: true,
|
|
318
320
|
fixedColShadow: false,
|
|
319
321
|
optimizeVue2Scroll: false,
|
|
322
|
+
sortConfig: () => ({
|
|
323
|
+
emptyToBottom: false,
|
|
324
|
+
}),
|
|
320
325
|
},
|
|
321
326
|
);
|
|
322
327
|
|
|
@@ -325,7 +330,7 @@ const emits = defineEmits<{
|
|
|
325
330
|
* 排序变更触发
|
|
326
331
|
* ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
|
|
327
332
|
*/
|
|
328
|
-
(e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[]): void;
|
|
333
|
+
(e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig<DT>): void;
|
|
329
334
|
/**
|
|
330
335
|
* 一行点击事件
|
|
331
336
|
* ```(ev: MouseEvent, row: DT)```
|
|
@@ -479,7 +484,7 @@ const { getFixedStyle } = useFixedStyle({
|
|
|
479
484
|
/**
|
|
480
485
|
* 高亮行,高亮单元格
|
|
481
486
|
*/
|
|
482
|
-
const { setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer
|
|
487
|
+
const { highlightRowStore, setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer });
|
|
483
488
|
|
|
484
489
|
if (props.autoResize) {
|
|
485
490
|
useAutoResize({ tableContainer, initVirtualScroll, props, debounceMs: 200 });
|
|
@@ -543,8 +548,16 @@ watch(() => props.fixedColShadow, dealFixedColShadow);
|
|
|
543
548
|
onMounted(() => {
|
|
544
549
|
initVirtualScroll();
|
|
545
550
|
updateFixedShadow();
|
|
551
|
+
dealDefaultSorter();
|
|
546
552
|
});
|
|
547
553
|
|
|
554
|
+
/** 处理默认排序 */
|
|
555
|
+
function dealDefaultSorter() {
|
|
556
|
+
if (!props.sortConfig.defaultSort) return;
|
|
557
|
+
const { dataIndex, order } = props.sortConfig.defaultSort;
|
|
558
|
+
setSorter(dataIndex as string, order);
|
|
559
|
+
}
|
|
560
|
+
|
|
548
561
|
/**
|
|
549
562
|
* 处理多级表头
|
|
550
563
|
*/
|
|
@@ -554,7 +567,7 @@ function dealColumns() {
|
|
|
554
567
|
tableHeaderLast.value = [];
|
|
555
568
|
const copyColumn = props.columns; // do not deep clone
|
|
556
569
|
const deep = howDeepTheHeader(copyColumn);
|
|
557
|
-
const tempHeaderLast: StkTableColumn<
|
|
570
|
+
const tempHeaderLast: StkTableColumn<DT>[] = [];
|
|
558
571
|
|
|
559
572
|
if (deep > 1 && props.virtualX) {
|
|
560
573
|
console.error('多级表头不支持横向虚拟滚动');
|
|
@@ -632,7 +645,7 @@ function colKeyGen(col: StkTableColumn<DT>) {
|
|
|
632
645
|
}
|
|
633
646
|
|
|
634
647
|
/** 获取列宽度样式 */
|
|
635
|
-
function getColWidthStyle(col: StkTableColumn<
|
|
648
|
+
function getColWidthStyle(col: StkTableColumn<DT>) {
|
|
636
649
|
const style: CSSProperties = {
|
|
637
650
|
width: col.width,
|
|
638
651
|
minWidth: col.minWidth,
|
|
@@ -657,7 +670,7 @@ function getColWidthStyle(col: StkTableColumn<any>) {
|
|
|
657
670
|
* @param col
|
|
658
671
|
* @param depth 表头层级
|
|
659
672
|
*/
|
|
660
|
-
function getCellStyle(tagType: 1 | 2, col: StkTableColumn<
|
|
673
|
+
function getCellStyle(tagType: 1 | 2, col: StkTableColumn<DT>, depth?: number): CSSProperties {
|
|
661
674
|
const style: CSSProperties = {
|
|
662
675
|
...getColWidthStyle(col),
|
|
663
676
|
...getFixedStyle(tagType, col, depth),
|
|
@@ -675,10 +688,11 @@ function getCellStyle(tagType: 1 | 2, col: StkTableColumn<any>, depth?: number):
|
|
|
675
688
|
|
|
676
689
|
/**
|
|
677
690
|
* 表头点击排序
|
|
678
|
-
* @param
|
|
679
|
-
* @param
|
|
691
|
+
* @param click 是否为点击表头触发
|
|
692
|
+
* @param options.force sort-remote 开启后是否强制排序
|
|
693
|
+
* @param options.emit 是否触发回调
|
|
680
694
|
*/
|
|
681
|
-
function onColumnSort(col?: StkTableColumn<
|
|
695
|
+
function onColumnSort(col?: StkTableColumn<DT>, click = true, options: { force?: boolean; emit?: boolean } = {}) {
|
|
682
696
|
if (!col?.sorter) return;
|
|
683
697
|
options = { force: false, emit: false, ...options };
|
|
684
698
|
if (sortCol.value !== col.dataIndex) {
|
|
@@ -689,14 +703,22 @@ function onColumnSort(col?: StkTableColumn<any>, click = true, options: { force?
|
|
|
689
703
|
if (click) sortOrderIndex.value++;
|
|
690
704
|
sortOrderIndex.value = sortOrderIndex.value % 3;
|
|
691
705
|
|
|
692
|
-
|
|
706
|
+
let order = sortSwitchOrder[sortOrderIndex.value];
|
|
707
|
+
const sortConfig = props.sortConfig;
|
|
708
|
+
const defaultSort = sortConfig.defaultSort;
|
|
693
709
|
|
|
710
|
+
if (!order && defaultSort) {
|
|
711
|
+
// 没有排序时变成默认排序
|
|
712
|
+
order = defaultSort.order;
|
|
713
|
+
sortOrderIndex.value = sortSwitchOrder.indexOf(order);
|
|
714
|
+
sortCol.value = defaultSort.dataIndex as string;
|
|
715
|
+
}
|
|
694
716
|
if (!props.sortRemote || options.force) {
|
|
695
|
-
dataSourceCopy.value = tableSort(col, order, props.dataSource);
|
|
717
|
+
dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
|
|
696
718
|
}
|
|
697
719
|
// 只有点击才触发事件
|
|
698
720
|
if (click || options.emit) {
|
|
699
|
-
emits('sort-change', col, order, toRaw(dataSourceCopy.value));
|
|
721
|
+
emits('sort-change', col, order, toRaw(dataSourceCopy.value), sortConfig);
|
|
700
722
|
}
|
|
701
723
|
}
|
|
702
724
|
|
|
@@ -808,16 +830,16 @@ function setCurrentRow(rowKey: string, option = { silent: false }) {
|
|
|
808
830
|
|
|
809
831
|
/**
|
|
810
832
|
* 设置表头排序状态
|
|
811
|
-
* @param
|
|
812
|
-
* @param
|
|
813
|
-
* @param
|
|
814
|
-
* @param
|
|
815
|
-
* @param
|
|
833
|
+
* @param dataIndex 列字段
|
|
834
|
+
* @param order 正序倒序
|
|
835
|
+
* @param option.sortOption 指定排序参数
|
|
836
|
+
* @param option.sort 是否触发排序-默认true
|
|
837
|
+
* @param option.silent 是否禁止触发回调-默认true
|
|
816
838
|
*/
|
|
817
|
-
function setSorter(dataIndex: string, order:
|
|
839
|
+
function setSorter(dataIndex: string, order: Order, option: { sortOption?: SortOption<DT>; silent?: boolean; sort?: boolean } = {}) {
|
|
818
840
|
const newOption = { silent: true, sortOption: null, sort: true, ...option };
|
|
819
841
|
sortCol.value = dataIndex;
|
|
820
|
-
sortOrderIndex.value = sortSwitchOrder.
|
|
842
|
+
sortOrderIndex.value = sortSwitchOrder.indexOf(order);
|
|
821
843
|
|
|
822
844
|
if (newOption.sort && dataSourceCopy.value?.length) {
|
|
823
845
|
// 如果表格有数据,则进行排序
|
|
@@ -59,12 +59,27 @@ export type StkTableColumn<T extends Record<string, any>> = {
|
|
|
59
59
|
__PARENT__?: StkTableColumn<T> | null;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
export type SortOption = Pick<StkTableColumn<
|
|
62
|
+
export type SortOption<T extends Record<string, any>> = Pick<StkTableColumn<T>, 'sorter' | 'dataIndex' | 'sortField' | 'sortType'>;
|
|
63
63
|
|
|
64
|
+
/** 排序状态 */
|
|
64
65
|
export type SortState<T> = {
|
|
65
66
|
dataIndex: T;
|
|
66
67
|
order: null | 'asc' | 'desc';
|
|
67
68
|
sortType?: 'number' | 'string';
|
|
68
69
|
};
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
/** 唯一键 */
|
|
72
|
+
export type UniqKey = string | number;
|
|
73
|
+
export type UniqKeyFun = (param: any) => UniqKey;
|
|
74
|
+
export type UniqKeyProp = UniqKey | UniqKeyFun;
|
|
75
|
+
|
|
76
|
+
/** 排序配置 */
|
|
77
|
+
export type SortConfig<T extends Record<string, any>> = {
|
|
78
|
+
/** 空值始终排在列表末尾 */
|
|
79
|
+
emptyToBottom?: boolean;
|
|
80
|
+
/** 默认排序(1.初始化时触发 2.排序方向为null时触发) */
|
|
81
|
+
defaultSort?: {
|
|
82
|
+
dataIndex: keyof T;
|
|
83
|
+
order: Order;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
@@ -1,22 +1,41 @@
|
|
|
1
1
|
import { interpolateRgb } from 'd3-interpolate';
|
|
2
|
-
import { Ref, computed } from 'vue';
|
|
2
|
+
import { Ref, computed, ref } from 'vue';
|
|
3
3
|
import { Highlight_Color, Highlight_Color_Change_Freq, Highlight_Duration } from './const';
|
|
4
|
+
import { UniqKey } from './types';
|
|
4
5
|
|
|
5
6
|
type Params = {
|
|
6
7
|
props: { theme: 'light' | 'dark'; virtual: boolean; dataSource: any[] };
|
|
7
8
|
tableContainer: Ref<HTMLElement | undefined>;
|
|
8
|
-
rowKeyGen: (p: any) => string;
|
|
9
9
|
};
|
|
10
|
+
|
|
11
|
+
/** 高亮行保存的东西 */
|
|
12
|
+
type HighlightRowStore = {
|
|
13
|
+
bgc: string;
|
|
14
|
+
bgc_progress_ms: number;
|
|
15
|
+
bgc_progress: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** 高亮行class */
|
|
19
|
+
const HIGHLIGHT_ROW_CLASS = 'highlight-row';
|
|
20
|
+
/** 高连单元格class */
|
|
21
|
+
const HIGHLIGHT_CELL_CLASS = 'highlight-cell';
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
24
|
* 高亮单元格,行
|
|
12
25
|
* row中新增_bgc_progress_ms 属性控制高亮状态,_bgc控制颜色
|
|
13
26
|
*/
|
|
14
|
-
export function useHighlight({ props, tableContainer
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
export function useHighlight({ props, tableContainer }: Params) {
|
|
28
|
+
/**
|
|
29
|
+
* 高亮行记录 key-rowKey, value-obj
|
|
30
|
+
*/
|
|
31
|
+
const highlightRowStore = ref<Record<UniqKey, HighlightRowStore>>({});
|
|
32
|
+
|
|
33
|
+
const highlightFrom = Highlight_Color[props.theme].from;
|
|
34
|
+
const highlightTo = Highlight_Color[props.theme].to;
|
|
35
|
+
const highlightInter = computed(() => interpolateRgb(highlightFrom, highlightTo));
|
|
36
|
+
|
|
37
|
+
/** 存放高亮行的key*/
|
|
38
|
+
const highlightDimRowKeys = new Set<UniqKey>();
|
|
20
39
|
/** 高亮后渐暗的行定时器 */
|
|
21
40
|
const highlightDimRowsTimeout = new Map();
|
|
22
41
|
/** 高亮后渐暗的单元格定时器 */
|
|
@@ -36,9 +55,8 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
36
55
|
const recursion = () => {
|
|
37
56
|
window.setTimeout(() => {
|
|
38
57
|
const nowTs = Date.now();
|
|
39
|
-
const needDeleteRows: any = [];
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
highlightDimRowKeys.forEach(rowKeyValue => {
|
|
42
60
|
// const rowKeyValue = rowKeyGen(row);
|
|
43
61
|
// const rowEl = tableContainer.value?.querySelector<HTMLElement>(`[data-row-key="${rowKeyValue}"]`);
|
|
44
62
|
// if (rowEl && row._bgc_progress === 0) {
|
|
@@ -47,21 +65,18 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
47
65
|
// void rowEl.offsetHeight; // reflow
|
|
48
66
|
// rowEl.classList.add('highlight-row-transition');
|
|
49
67
|
// }
|
|
50
|
-
|
|
68
|
+
const highlightItem = highlightRowStore.value[rowKeyValue];
|
|
51
69
|
/** 经过的时间 ÷ 高亮持续时间 计算出 颜色过渡进度 (0-1) */
|
|
52
|
-
const progress = (nowTs -
|
|
53
|
-
// row._bgc_progress = progress;
|
|
70
|
+
const progress = (nowTs - highlightItem.bgc_progress_ms) / Highlight_Duration;
|
|
54
71
|
if (0 < progress && progress < 1) {
|
|
55
|
-
|
|
72
|
+
highlightItem.bgc = highlightInter.value(progress);
|
|
56
73
|
} else {
|
|
57
|
-
|
|
58
|
-
|
|
74
|
+
highlightItem.bgc = ''; // 清空颜色
|
|
75
|
+
highlightDimRowKeys.delete(rowKeyValue);
|
|
59
76
|
}
|
|
60
77
|
});
|
|
61
|
-
needDeleteRows.forEach((row: any) => highlightDimRows.delete(row));
|
|
62
|
-
// TODO: shallowRef 时,需要手动更新
|
|
63
78
|
|
|
64
|
-
if (
|
|
79
|
+
if (highlightDimRowKeys.size > 0) {
|
|
65
80
|
// 还有高亮的行,则下一次循环
|
|
66
81
|
recursion();
|
|
67
82
|
} else {
|
|
@@ -78,16 +93,16 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
78
93
|
// TODO: 支持动态计算高亮颜色。不易实现。需记录每一个单元格的颜色情况。
|
|
79
94
|
const cellEl = tableContainer.value?.querySelector<HTMLElement>(`[data-row-key="${rowKeyValue}"]>[data-index="${dataIndex}"]`);
|
|
80
95
|
if (!cellEl) return;
|
|
81
|
-
if (cellEl.classList.contains(
|
|
82
|
-
cellEl.classList.remove(
|
|
96
|
+
if (cellEl.classList.contains(HIGHLIGHT_CELL_CLASS)) {
|
|
97
|
+
cellEl.classList.remove(HIGHLIGHT_CELL_CLASS);
|
|
83
98
|
void cellEl.offsetHeight; // 通知浏览器重绘
|
|
84
99
|
}
|
|
85
|
-
cellEl.classList.add(
|
|
100
|
+
cellEl.classList.add(HIGHLIGHT_CELL_CLASS);
|
|
86
101
|
window.clearTimeout(highlightDimCellsTimeout.get(rowKeyValue));
|
|
87
102
|
highlightDimCellsTimeout.set(
|
|
88
103
|
rowKeyValue,
|
|
89
104
|
window.setTimeout(() => {
|
|
90
|
-
cellEl.classList.remove(
|
|
105
|
+
cellEl.classList.remove(HIGHLIGHT_CELL_CLASS);
|
|
91
106
|
highlightDimCellsTimeout.delete(rowKeyValue);
|
|
92
107
|
}, Highlight_Duration),
|
|
93
108
|
);
|
|
@@ -97,18 +112,19 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
97
112
|
* 高亮一行
|
|
98
113
|
* @param rowKeyValues
|
|
99
114
|
*/
|
|
100
|
-
function setHighlightDimRow(rowKeyValues:
|
|
115
|
+
function setHighlightDimRow(rowKeyValues: UniqKey[]) {
|
|
101
116
|
if (!Array.isArray(rowKeyValues)) rowKeyValues = [rowKeyValues];
|
|
102
117
|
if (props.virtual) {
|
|
103
118
|
// --------虚拟滚动用js计算颜色渐变的高亮方案
|
|
104
119
|
const nowTs = Date.now(); // 重置渐变进度
|
|
105
120
|
for (let i = 0; i < rowKeyValues.length; i++) {
|
|
106
121
|
const rowKeyValue = rowKeyValues[i];
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
122
|
+
highlightRowStore.value[rowKeyValue] = {
|
|
123
|
+
bgc: '',
|
|
124
|
+
bgc_progress: 0,
|
|
125
|
+
bgc_progress_ms: nowTs,
|
|
126
|
+
};
|
|
127
|
+
highlightDimRowKeys.add(rowKeyValue);
|
|
112
128
|
}
|
|
113
129
|
calcHighlightLoop();
|
|
114
130
|
} else {
|
|
@@ -121,8 +137,8 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
121
137
|
const rowKeyValue = rowKeyValues[i];
|
|
122
138
|
const rowEl = tableContainer.value?.querySelector<HTMLTableRowElement>(`[data-row-key="${rowKeyValue}"]`);
|
|
123
139
|
if (!rowEl) continue;
|
|
124
|
-
if (rowEl.classList.contains(
|
|
125
|
-
rowEl.classList.remove(
|
|
140
|
+
if (rowEl.classList.contains(HIGHLIGHT_ROW_CLASS)) {
|
|
141
|
+
rowEl.classList.remove(HIGHLIGHT_ROW_CLASS);
|
|
126
142
|
needRepaint = true;
|
|
127
143
|
}
|
|
128
144
|
rowElTemp.push(rowEl);
|
|
@@ -131,7 +147,7 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
131
147
|
highlightDimRowsTimeout.set(
|
|
132
148
|
rowKeyValue,
|
|
133
149
|
window.setTimeout(() => {
|
|
134
|
-
rowEl.classList.remove(
|
|
150
|
+
rowEl.classList.remove(HIGHLIGHT_ROW_CLASS);
|
|
135
151
|
highlightDimRowsTimeout.delete(rowKeyValue); // 回收内存
|
|
136
152
|
}, Highlight_Duration),
|
|
137
153
|
);
|
|
@@ -139,11 +155,12 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
|
|
|
139
155
|
if (needRepaint) {
|
|
140
156
|
void tableContainer.value?.offsetWidth; //强制浏览器重绘
|
|
141
157
|
}
|
|
142
|
-
rowElTemp.forEach(el => el.classList.add(
|
|
158
|
+
rowElTemp.forEach(el => el.classList.add(HIGHLIGHT_ROW_CLASS)); // 统一添加动画
|
|
143
159
|
}
|
|
144
160
|
}
|
|
145
161
|
|
|
146
162
|
return {
|
|
163
|
+
highlightRowStore,
|
|
147
164
|
setHighlightDimRow,
|
|
148
165
|
setHighlightDimCell,
|
|
149
166
|
};
|
package/src/StkTable/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Default_Col_Width } from './const';
|
|
2
|
-
import { Order, SortOption, SortState, StkTableColumn } from './types';
|
|
2
|
+
import { Order, SortConfig, SortOption, SortState, StkTableColumn } from './types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 对有序数组插入新数据
|
|
@@ -63,56 +63,95 @@ function strCompare(a: string, b: string, type: 'number' | 'string'): number {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* 分离出空数据和非空数据成两个数组
|
|
68
|
+
* @param sortOption
|
|
69
|
+
* @param targetDataSource
|
|
70
|
+
* @param isNumber 1 数字
|
|
71
|
+
* @return [值数组,空数组]
|
|
72
|
+
*/
|
|
73
|
+
function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>, targetDataSource: T[], isNumber?: boolean) {
|
|
74
|
+
const emptyArr: T[] = [];
|
|
75
|
+
const valueArr: T[] = [];
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < targetDataSource.length; i++) {
|
|
78
|
+
const row = targetDataSource[i];
|
|
79
|
+
const sortField = sortOption.sortField || sortOption.dataIndex;
|
|
80
|
+
let isEmpty = row[sortField] === null || row[sortField] === '';
|
|
81
|
+
if (isNumber) {
|
|
82
|
+
isEmpty ||= typeof row[sortField] === 'boolean' || Number.isNaN(+row[sortField]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (isEmpty) {
|
|
86
|
+
emptyArr.push(row);
|
|
87
|
+
} else {
|
|
88
|
+
valueArr.push(row);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return [valueArr, emptyArr] as const;
|
|
92
|
+
}
|
|
93
|
+
|
|
66
94
|
/**
|
|
67
95
|
* 表格排序抽离
|
|
68
96
|
* 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
|
|
69
97
|
* 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
|
|
70
98
|
* TODO: key 唯一值,排序字段相同时,根据唯一值排序。
|
|
99
|
+
*
|
|
100
|
+
* sortConfig.defaultSort 会在order为null时生效
|
|
71
101
|
* @param sortOption 列配置
|
|
72
102
|
* @param order 排序方式
|
|
73
103
|
* @param dataSource 排序的数组
|
|
74
104
|
*/
|
|
75
|
-
export function tableSort
|
|
105
|
+
export function tableSort<T extends Record<string, any>>(
|
|
106
|
+
sortOption: SortOption<T>,
|
|
107
|
+
order: Order,
|
|
108
|
+
dataSource: T[],
|
|
109
|
+
sortConfig: SortConfig<T> = {},
|
|
110
|
+
): T[] {
|
|
76
111
|
if (!dataSource?.length) return dataSource || [];
|
|
112
|
+
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
77
113
|
let targetDataSource = [...dataSource];
|
|
114
|
+
let sortField = sortOption.sortField || sortOption.dataIndex;
|
|
115
|
+
|
|
116
|
+
if (!order && sortConfig.defaultSort) {
|
|
117
|
+
// 默认排序
|
|
118
|
+
order = sortConfig.defaultSort.order;
|
|
119
|
+
sortField = sortConfig.defaultSort.dataIndex;
|
|
120
|
+
}
|
|
121
|
+
|
|
78
122
|
if (typeof sortOption.sorter === 'function') {
|
|
79
123
|
const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
|
|
80
124
|
if (customSorterData) targetDataSource = customSorterData;
|
|
81
125
|
} else if (order) {
|
|
82
|
-
const sortField = sortOption.sortField || sortOption.dataIndex;
|
|
83
126
|
let { sortType } = sortOption;
|
|
84
127
|
if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
|
|
85
128
|
|
|
129
|
+
const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, sortType === 'number');
|
|
130
|
+
|
|
86
131
|
if (sortType === 'number') {
|
|
87
132
|
// 按数字类型排序
|
|
88
|
-
const nanArr: any[] = []; // 非数字
|
|
89
|
-
const numArr: any[] = []; // 数字
|
|
90
|
-
|
|
91
|
-
for (let i = 0; i < targetDataSource.length; i++) {
|
|
92
|
-
const row = targetDataSource[i];
|
|
93
|
-
if (row[sortField] === null || row[sortField] === '' || typeof row[sortField] === 'boolean' || Number.isNaN(+row[sortField])) {
|
|
94
|
-
nanArr.push(row);
|
|
95
|
-
} else {
|
|
96
|
-
numArr.push(row);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
133
|
// 非数字当作最小值处理
|
|
100
134
|
if (order === 'asc') {
|
|
101
|
-
|
|
102
|
-
targetDataSource = [...
|
|
135
|
+
valueArr.sort((a, b) => +a[sortField] - +b[sortField]);
|
|
136
|
+
targetDataSource = [...emptyArr, ...valueArr];
|
|
103
137
|
} else {
|
|
104
|
-
|
|
105
|
-
targetDataSource = [...
|
|
138
|
+
valueArr.sort((a, b) => +b[sortField] - +a[sortField]);
|
|
139
|
+
targetDataSource = [...valueArr, ...emptyArr];
|
|
106
140
|
}
|
|
107
|
-
// targetDataSource = [...numArr, ...nanArr]; // 非数字不进入排序,一直排在最后
|
|
108
141
|
} else {
|
|
109
142
|
// 按string 排序
|
|
110
143
|
if (order === 'asc') {
|
|
111
|
-
|
|
144
|
+
valueArr.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]));
|
|
145
|
+
targetDataSource = [...emptyArr, ...valueArr];
|
|
112
146
|
} else {
|
|
113
|
-
|
|
147
|
+
valueArr.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]) * -1);
|
|
148
|
+
targetDataSource = [...valueArr, ...emptyArr];
|
|
114
149
|
}
|
|
115
150
|
}
|
|
151
|
+
|
|
152
|
+
if (sortConfig.emptyToBottom) {
|
|
153
|
+
targetDataSource = [...valueArr, ...emptyArr];
|
|
154
|
+
}
|
|
116
155
|
}
|
|
117
156
|
return targetDataSource;
|
|
118
157
|
}
|