stk-table-vue 0.7.0 → 0.7.2
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/lib/src/StkTable/StkTable.vue.d.ts +15 -9
- package/lib/src/StkTable/types/index.d.ts +1 -1
- package/lib/src/StkTable/useScrollRowByRow.d.ts +11 -0
- package/lib/src/StkTable/utils/index.d.ts +3 -1
- package/lib/stk-table-vue.js +199 -137
- package/package.json +2 -3
- package/src/StkTable/StkTable.vue +16 -10
- package/src/StkTable/types/index.ts +1 -1
- package/src/StkTable/useHighlight.ts +10 -73
- package/src/StkTable/useScrollRowByRow.ts +76 -0
- package/src/StkTable/utils/index.ts +17 -6
|
@@ -94,7 +94,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
94
94
|
/** 当前行再次点击否可以取消 (rowActive=true)*/
|
|
95
95
|
rowCurrentRevokable?: boolean;
|
|
96
96
|
/** 表头行高。default = rowHeight */
|
|
97
|
-
headerRowHeight?: number | null;
|
|
97
|
+
headerRowHeight?: number | string | null;
|
|
98
98
|
/** 虚拟滚动 */
|
|
99
99
|
virtual?: boolean;
|
|
100
100
|
/** x轴虚拟滚动(必须设置列宽)*/
|
|
@@ -194,8 +194,11 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
194
194
|
* - true: 不使用 onwheel 滚动。鼠标滚轮滚动时更加平滑。滚动过快时会白屏。
|
|
195
195
|
*/
|
|
196
196
|
smoothScroll?: boolean;
|
|
197
|
-
/**
|
|
198
|
-
|
|
197
|
+
/**
|
|
198
|
+
* 按整数行纵向滚动
|
|
199
|
+
* - scrollbar:仅拖动滚动条生效
|
|
200
|
+
*/
|
|
201
|
+
scrollRowByRow?: boolean | "scrollbar";
|
|
199
202
|
}>, {
|
|
200
203
|
width: string;
|
|
201
204
|
fixedMode: boolean;
|
|
@@ -372,7 +375,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
372
375
|
})) | (import('./types/index').UniqKey | (PrivateRowDT & {
|
|
373
376
|
children?: (PrivateRowDT & /*elided*/ any)[];
|
|
374
377
|
}))[], option?: {
|
|
375
|
-
expand
|
|
378
|
+
expand?: boolean;
|
|
376
379
|
}) => void;
|
|
377
380
|
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
378
381
|
"sort-change": (col: StkTableColumn<any> | null, order: Order, data: any[], sortConfig: SortConfig<any>) => void;
|
|
@@ -456,7 +459,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
456
459
|
/** 当前行再次点击否可以取消 (rowActive=true)*/
|
|
457
460
|
rowCurrentRevokable?: boolean;
|
|
458
461
|
/** 表头行高。default = rowHeight */
|
|
459
|
-
headerRowHeight?: number | null;
|
|
462
|
+
headerRowHeight?: number | string | null;
|
|
460
463
|
/** 虚拟滚动 */
|
|
461
464
|
virtual?: boolean;
|
|
462
465
|
/** x轴虚拟滚动(必须设置列宽)*/
|
|
@@ -556,8 +559,11 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
556
559
|
* - true: 不使用 onwheel 滚动。鼠标滚轮滚动时更加平滑。滚动过快时会白屏。
|
|
557
560
|
*/
|
|
558
561
|
smoothScroll?: boolean;
|
|
559
|
-
/**
|
|
560
|
-
|
|
562
|
+
/**
|
|
563
|
+
* 按整数行纵向滚动
|
|
564
|
+
* - scrollbar:仅拖动滚动条生效
|
|
565
|
+
*/
|
|
566
|
+
scrollRowByRow?: boolean | "scrollbar";
|
|
561
567
|
}>, {
|
|
562
568
|
width: string;
|
|
563
569
|
fixedMode: boolean;
|
|
@@ -670,7 +676,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
670
676
|
stripe: boolean;
|
|
671
677
|
optimizeVue2Scroll: boolean;
|
|
672
678
|
rowKey: UniqKeyProp;
|
|
673
|
-
headerRowHeight: number | null;
|
|
679
|
+
headerRowHeight: number | string | null;
|
|
674
680
|
fixedMode: boolean;
|
|
675
681
|
theme: "light" | "dark";
|
|
676
682
|
rowHover: boolean;
|
|
@@ -708,7 +714,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
|
|
|
708
714
|
treeConfig: TreeConfig;
|
|
709
715
|
cellFixedMode: "sticky" | "relative";
|
|
710
716
|
smoothScroll: boolean;
|
|
711
|
-
scrollRowByRow: boolean;
|
|
717
|
+
scrollRowByRow: boolean | "scrollbar";
|
|
712
718
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
713
719
|
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
|
|
714
720
|
export default _default;
|
|
@@ -38,7 +38,7 @@ export type CustomCell<T extends CustomCellProps<U> | CustomHeaderCellProps<U>,
|
|
|
38
38
|
/** 表格列配置 */
|
|
39
39
|
export type StkTableColumn<T extends Record<string, any>> = {
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* 列唯一键,(可选),不传则默认取dataIndex 字段作为列唯一键。
|
|
42
42
|
*/
|
|
43
43
|
key?: any;
|
|
44
44
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
type Params = {
|
|
4
|
+
props: any;
|
|
5
|
+
tableContainerRef: Ref<HTMLElement | undefined>;
|
|
6
|
+
};
|
|
7
|
+
export declare function useScrollRowByRow({ props, tableContainerRef }: Params): {
|
|
8
|
+
isSRBRActive: import('vue').ComputedRef<any>;
|
|
9
|
+
isDragScroll: Ref<boolean, boolean>;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -14,7 +14,9 @@ export declare function isEmptyValue(val: any, isNumber?: boolean): boolean;
|
|
|
14
14
|
* @param targetArray 表格数据
|
|
15
15
|
* @return targetArray 的浅拷贝
|
|
16
16
|
*/
|
|
17
|
-
export declare function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig?: SortConfig<T>
|
|
17
|
+
export declare function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig?: SortConfig<T> & {
|
|
18
|
+
customCompare?: (a: T, b: T) => number;
|
|
19
|
+
}): T[];
|
|
18
20
|
/**
|
|
19
21
|
* 二分查找
|
|
20
22
|
* @param searchArray 查找数组
|
package/lib/stk-table-vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createElementBlock, openBlock, createElementVNode, watch, onMounted, onBeforeUnmount, ref, computed, shallowRef, defineComponent, nextTick, toRaw, normalizeStyle, normalizeClass, unref, createCommentVNode, renderSlot, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, createTextVNode, withCtx, createVNode } from "vue";
|
|
1
|
+
import { createElementBlock, openBlock, createElementVNode, watch, onMounted, onBeforeUnmount, ref, computed, shallowRef, onUnmounted, defineComponent, nextTick, toRaw, normalizeStyle, normalizeClass, unref, createCommentVNode, renderSlot, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, createTextVNode, withCtx, createVNode } from "vue";
|
|
2
2
|
const _export_sfc = (sfc, props) => {
|
|
3
3
|
const target = sfc.__vccOpts || sfc;
|
|
4
4
|
for (const [key, val] of props) {
|
|
@@ -61,7 +61,6 @@ function isEmptyValue(val, isNumber) {
|
|
|
61
61
|
}
|
|
62
62
|
function insertToOrderedArray(sortState, newItem, targetArray, sortConfig = {}) {
|
|
63
63
|
const { dataIndex, sortField, order } = sortState;
|
|
64
|
-
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
65
64
|
let { sortType } = sortState;
|
|
66
65
|
const field = sortField || dataIndex;
|
|
67
66
|
if (!sortType) sortType = typeof newItem[field];
|
|
@@ -70,15 +69,19 @@ function insertToOrderedArray(sortState, newItem, targetArray, sortConfig = {})
|
|
|
70
69
|
data.unshift(newItem);
|
|
71
70
|
return data;
|
|
72
71
|
}
|
|
72
|
+
const { emptyToBottom, customCompare, stringLocaleCompare } = { emptyToBottom: false, ...sortConfig };
|
|
73
73
|
const targetVal = newItem[field];
|
|
74
|
-
if (
|
|
74
|
+
if (emptyToBottom && isEmptyValue(targetVal)) {
|
|
75
75
|
data.push(newItem);
|
|
76
76
|
} else {
|
|
77
|
+
const customCompareFn = customCompare || ((a, b) => {
|
|
78
|
+
const midVal = a[field];
|
|
79
|
+
const compareRes = strCompare(midVal, targetVal, isNumber, stringLocaleCompare);
|
|
80
|
+
return order === "asc" ? compareRes : -compareRes;
|
|
81
|
+
});
|
|
77
82
|
const isNumber = sortType === "number";
|
|
78
83
|
const sIndex = binarySearch(data, (midIndex) => {
|
|
79
|
-
|
|
80
|
-
const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
|
|
81
|
-
return order === "asc" ? compareRes : -compareRes;
|
|
84
|
+
return customCompareFn(data[midIndex], newItem);
|
|
82
85
|
});
|
|
83
86
|
data.splice(sIndex, 0, newItem);
|
|
84
87
|
}
|
|
@@ -636,7 +639,7 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
636
639
|
const recursion = () => {
|
|
637
640
|
window.requestAnimationFrame(
|
|
638
641
|
() => {
|
|
639
|
-
const nowTs =
|
|
642
|
+
const nowTs = performance.now();
|
|
640
643
|
highlightDimRowsAnimation.forEach((store, rowKeyValue) => {
|
|
641
644
|
const { ts, duration } = store;
|
|
642
645
|
const timeOffset = nowTs - ts;
|
|
@@ -660,13 +663,13 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
660
663
|
function setHighlightDimCell(rowKeyValue, colKeyValue, option = {}) {
|
|
661
664
|
var _a;
|
|
662
665
|
const cellEl = (_a = tableContainerRef.value) == null ? void 0 : _a.querySelector(`[data-cell-key="${rowKeyValue}--${colKeyValue}"]`);
|
|
666
|
+
if (!cellEl) return;
|
|
663
667
|
const { className, method, duration, keyframe } = {
|
|
664
668
|
className: HIGHLIGHT_CELL_CLASS,
|
|
665
669
|
method: "animation",
|
|
666
670
|
...defaultHighlightDimOption.value,
|
|
667
671
|
...option
|
|
668
672
|
};
|
|
669
|
-
if (!cellEl) return;
|
|
670
673
|
if (method === "animation") {
|
|
671
674
|
cellEl.animate(keyframe, duration);
|
|
672
675
|
} else {
|
|
@@ -675,17 +678,16 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
675
678
|
}
|
|
676
679
|
function setHighlightDimRow(rowKeyValues, option = {}) {
|
|
677
680
|
if (!Array.isArray(rowKeyValues)) rowKeyValues = [rowKeyValues];
|
|
681
|
+
if (!rowKeyValues.length) return;
|
|
678
682
|
const { className, method, keyframe, duration } = {
|
|
679
683
|
className: HIGHLIGHT_ROW_CLASS,
|
|
680
684
|
method: "animation",
|
|
681
685
|
...defaultHighlightDimOption.value,
|
|
682
686
|
...option
|
|
683
687
|
};
|
|
684
|
-
if (method === "
|
|
685
|
-
highlightRowsInCssKeyframe(rowKeyValues, className, duration);
|
|
686
|
-
} else if (method === "animation") {
|
|
688
|
+
if (method === "animation") {
|
|
687
689
|
if (props.virtual) {
|
|
688
|
-
const nowTs =
|
|
690
|
+
const nowTs = performance.now();
|
|
689
691
|
for (let i = 0; i < rowKeyValues.length; i++) {
|
|
690
692
|
const rowKeyValue = rowKeyValues[i];
|
|
691
693
|
const store = { ts: nowTs, visible: false, keyframe, duration };
|
|
@@ -700,6 +702,8 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
|
|
|
700
702
|
rowEl.animate(keyframe, duration);
|
|
701
703
|
}
|
|
702
704
|
}
|
|
705
|
+
} else {
|
|
706
|
+
highlightRowsInCssKeyframe(rowKeyValues, className, duration);
|
|
703
707
|
}
|
|
704
708
|
}
|
|
705
709
|
function highlightRowsInCssKeyframe(rowKeyValues, className, duration) {
|
|
@@ -897,6 +901,63 @@ function useRowExpand({ dataSourceCopy, rowKeyGen, emits }) {
|
|
|
897
901
|
setRowExpand
|
|
898
902
|
};
|
|
899
903
|
}
|
|
904
|
+
function useScrollRowByRow({ props, tableContainerRef }) {
|
|
905
|
+
let isMouseDown = false;
|
|
906
|
+
let isAddListeners = false;
|
|
907
|
+
const isDragScroll = ref(false);
|
|
908
|
+
const onlyDragScroll = computed(() => props.scrollRowByRow === "scrollbar");
|
|
909
|
+
const isSRBRActive = computed(() => {
|
|
910
|
+
if (onlyDragScroll.value) {
|
|
911
|
+
return isDragScroll.value;
|
|
912
|
+
}
|
|
913
|
+
return props.scrollRowByRow;
|
|
914
|
+
});
|
|
915
|
+
watch(
|
|
916
|
+
() => onlyDragScroll.value,
|
|
917
|
+
(v) => {
|
|
918
|
+
if (v) {
|
|
919
|
+
addEventListener();
|
|
920
|
+
} else {
|
|
921
|
+
removeEventListener();
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
onMounted(() => {
|
|
926
|
+
addEventListener();
|
|
927
|
+
});
|
|
928
|
+
onUnmounted(() => {
|
|
929
|
+
removeEventListener();
|
|
930
|
+
});
|
|
931
|
+
function addEventListener() {
|
|
932
|
+
if (isAddListeners || !onlyDragScroll.value) return;
|
|
933
|
+
const container = tableContainerRef.value;
|
|
934
|
+
if (!container) return;
|
|
935
|
+
container.addEventListener("mousedown", handleMouseDown);
|
|
936
|
+
container.addEventListener("mouseup", handleMouseUp);
|
|
937
|
+
container.addEventListener("scroll", handleScroll);
|
|
938
|
+
isAddListeners = true;
|
|
939
|
+
}
|
|
940
|
+
function removeEventListener() {
|
|
941
|
+
const container = tableContainerRef.value;
|
|
942
|
+
if (!container) return;
|
|
943
|
+
container.removeEventListener("mousedown", handleMouseDown);
|
|
944
|
+
container.removeEventListener("mouseup", handleMouseUp);
|
|
945
|
+
container.removeEventListener("scroll", handleScroll);
|
|
946
|
+
isAddListeners = false;
|
|
947
|
+
}
|
|
948
|
+
function handleMouseDown() {
|
|
949
|
+
isMouseDown = true;
|
|
950
|
+
}
|
|
951
|
+
function handleMouseUp() {
|
|
952
|
+
isMouseDown = false;
|
|
953
|
+
isDragScroll.value = false;
|
|
954
|
+
}
|
|
955
|
+
function handleScroll() {
|
|
956
|
+
if (!isMouseDown) return;
|
|
957
|
+
isDragScroll.value = true;
|
|
958
|
+
}
|
|
959
|
+
return { isSRBRActive, isDragScroll };
|
|
960
|
+
}
|
|
900
961
|
function useThDrag({ props, emits, colKeyGen }) {
|
|
901
962
|
const findParentTH = (e) => e.target.closest("th");
|
|
902
963
|
const dragConfig = computed(() => {
|
|
@@ -1061,6 +1122,125 @@ function useTrDrag({ props, emits, dataSourceCopy }) {
|
|
|
1061
1122
|
onTrDragEnd
|
|
1062
1123
|
};
|
|
1063
1124
|
}
|
|
1125
|
+
function useTree({ props, dataSourceCopy, rowKeyGen, emits }) {
|
|
1126
|
+
const { defaultExpandAll, defaultExpandKeys, defaultExpandLevel } = props.treeConfig;
|
|
1127
|
+
function toggleTreeNode(row, col) {
|
|
1128
|
+
const expand = row ? !row.__T_EXPANDED__ : false;
|
|
1129
|
+
privateSetTreeExpand(row, { expand, col, isClick: true });
|
|
1130
|
+
}
|
|
1131
|
+
function privateSetTreeExpand(row, option) {
|
|
1132
|
+
const rowKeyOrRowArr = Array.isArray(row) ? row : [row];
|
|
1133
|
+
const tempData = dataSourceCopy.value.slice();
|
|
1134
|
+
for (let i = 0; i < rowKeyOrRowArr.length; i++) {
|
|
1135
|
+
const rowKeyOrRow = rowKeyOrRowArr[i];
|
|
1136
|
+
let rowKey;
|
|
1137
|
+
if (typeof rowKeyOrRow === "string") {
|
|
1138
|
+
rowKey = rowKeyOrRow;
|
|
1139
|
+
} else {
|
|
1140
|
+
rowKey = rowKeyGen(rowKeyOrRow);
|
|
1141
|
+
}
|
|
1142
|
+
const index = tempData.findIndex((it) => rowKeyGen(it) === rowKey);
|
|
1143
|
+
if (index === -1) {
|
|
1144
|
+
console.warn("treeExpandRow failed.rowKey:", rowKey);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const row2 = tempData[index];
|
|
1148
|
+
const level = row2.__T_LV__ || 0;
|
|
1149
|
+
let expanded = option == null ? void 0 : option.expand;
|
|
1150
|
+
if (expanded === void 0) {
|
|
1151
|
+
expanded = !row2.__T_EXPANDED__;
|
|
1152
|
+
}
|
|
1153
|
+
if (expanded) {
|
|
1154
|
+
const children = expandNode(row2, level);
|
|
1155
|
+
tempData.splice(index + 1, 0, ...children);
|
|
1156
|
+
} else {
|
|
1157
|
+
const deleteCount = foldNode(index, tempData, level);
|
|
1158
|
+
tempData.splice(index + 1, deleteCount);
|
|
1159
|
+
}
|
|
1160
|
+
setNodeExpanded(row2, expanded, level);
|
|
1161
|
+
if (option.isClick) {
|
|
1162
|
+
emits("toggle-tree-expand", { expanded: Boolean(expanded), row: row2, col: option.col });
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
dataSourceCopy.value = tempData;
|
|
1166
|
+
}
|
|
1167
|
+
function setTreeExpand(row, option) {
|
|
1168
|
+
privateSetTreeExpand(row, { ...option, isClick: false });
|
|
1169
|
+
}
|
|
1170
|
+
function setNodeExpanded(row, expanded, level, parent) {
|
|
1171
|
+
row.__T_EXPANDED__ = expanded;
|
|
1172
|
+
if (level !== void 0) {
|
|
1173
|
+
row.__T_LV__ = level;
|
|
1174
|
+
}
|
|
1175
|
+
if (parent) {
|
|
1176
|
+
row.__T_PARENT_K__ = rowKeyGen(parent);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
function flatTreeData(data) {
|
|
1180
|
+
const result = [];
|
|
1181
|
+
(function recursion(data2, level, parent) {
|
|
1182
|
+
if (!data2) return;
|
|
1183
|
+
for (let i = 0; i < data2.length; i++) {
|
|
1184
|
+
const item = data2[i];
|
|
1185
|
+
result.push(item);
|
|
1186
|
+
const isExpanded = Boolean(item.__T_EXPANDED__);
|
|
1187
|
+
setNodeExpanded(item, isExpanded, level, parent);
|
|
1188
|
+
if (!isExpanded) {
|
|
1189
|
+
if (defaultExpandAll) {
|
|
1190
|
+
setNodeExpanded(item, true);
|
|
1191
|
+
} else {
|
|
1192
|
+
if (defaultExpandLevel) {
|
|
1193
|
+
if (level < defaultExpandLevel) {
|
|
1194
|
+
setNodeExpanded(item, true);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (defaultExpandKeys) {
|
|
1198
|
+
if (defaultExpandKeys.includes(rowKeyGen(item))) {
|
|
1199
|
+
setNodeExpanded(item, true);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (!item.__T_EXPANDED__) {
|
|
1203
|
+
continue;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
recursion(item.children, level + 1, item);
|
|
1208
|
+
}
|
|
1209
|
+
})(data, 0);
|
|
1210
|
+
return result;
|
|
1211
|
+
}
|
|
1212
|
+
function expandNode(row, level) {
|
|
1213
|
+
let result = [];
|
|
1214
|
+
row.children && row.children.forEach((child) => {
|
|
1215
|
+
result.push(child);
|
|
1216
|
+
const childLv = level + 1;
|
|
1217
|
+
if (child.__T_EXPANDED__ && child.children) {
|
|
1218
|
+
const res = expandNode(child, childLv);
|
|
1219
|
+
result = result.concat(res);
|
|
1220
|
+
} else {
|
|
1221
|
+
setNodeExpanded(child, false, childLv, row);
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
return result;
|
|
1225
|
+
}
|
|
1226
|
+
function foldNode(index, tempData, level) {
|
|
1227
|
+
let deleteCount = 0;
|
|
1228
|
+
for (let i = index + 1; i < tempData.length; i++) {
|
|
1229
|
+
const child = tempData[i];
|
|
1230
|
+
if (child.__T_LV__ && child.__T_LV__ > level) {
|
|
1231
|
+
deleteCount++;
|
|
1232
|
+
} else {
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return deleteCount;
|
|
1237
|
+
}
|
|
1238
|
+
return {
|
|
1239
|
+
toggleTreeNode,
|
|
1240
|
+
setTreeExpand,
|
|
1241
|
+
flatTreeData
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1064
1244
|
const VUE2_SCROLL_TIMEOUT_MS = 200;
|
|
1065
1245
|
function useVirtualScroll({
|
|
1066
1246
|
props,
|
|
@@ -1377,125 +1557,6 @@ function useVirtualScroll({
|
|
|
1377
1557
|
clearAllAutoHeight
|
|
1378
1558
|
};
|
|
1379
1559
|
}
|
|
1380
|
-
function useTree({ props, dataSourceCopy, rowKeyGen, emits }) {
|
|
1381
|
-
const { defaultExpandAll, defaultExpandKeys, defaultExpandLevel } = props.treeConfig;
|
|
1382
|
-
function toggleTreeNode(row, col) {
|
|
1383
|
-
const expand = row ? !row.__T_EXPANDED__ : false;
|
|
1384
|
-
privateSetTreeExpand(row, { expand, col, isClick: true });
|
|
1385
|
-
}
|
|
1386
|
-
function privateSetTreeExpand(row, option) {
|
|
1387
|
-
const rowKeyOrRowArr = Array.isArray(row) ? row : [row];
|
|
1388
|
-
const tempData = dataSourceCopy.value.slice();
|
|
1389
|
-
for (let i = 0; i < rowKeyOrRowArr.length; i++) {
|
|
1390
|
-
const rowKeyOrRow = rowKeyOrRowArr[i];
|
|
1391
|
-
let rowKey;
|
|
1392
|
-
if (typeof rowKeyOrRow === "string") {
|
|
1393
|
-
rowKey = rowKeyOrRow;
|
|
1394
|
-
} else {
|
|
1395
|
-
rowKey = rowKeyGen(rowKeyOrRow);
|
|
1396
|
-
}
|
|
1397
|
-
const index = tempData.findIndex((it) => rowKeyGen(it) === rowKey);
|
|
1398
|
-
if (index === -1) {
|
|
1399
|
-
console.warn("treeExpandRow failed.rowKey:", rowKey);
|
|
1400
|
-
return;
|
|
1401
|
-
}
|
|
1402
|
-
const row2 = tempData[index];
|
|
1403
|
-
const level = row2.__T_LV__ || 0;
|
|
1404
|
-
let expanded = option == null ? void 0 : option.expand;
|
|
1405
|
-
if (expanded === void 0) {
|
|
1406
|
-
expanded = !row2.__T_EXPANDED__;
|
|
1407
|
-
}
|
|
1408
|
-
if (expanded) {
|
|
1409
|
-
const children = expandNode(row2, level);
|
|
1410
|
-
tempData.splice(index + 1, 0, ...children);
|
|
1411
|
-
} else {
|
|
1412
|
-
const deleteCount = foldNode(index, tempData, level);
|
|
1413
|
-
tempData.splice(index + 1, deleteCount);
|
|
1414
|
-
}
|
|
1415
|
-
setNodeExpanded(row2, expanded, level);
|
|
1416
|
-
if (option.isClick) {
|
|
1417
|
-
emits("toggle-tree-expand", { expanded: Boolean(expanded), row: row2, col: option.col });
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
dataSourceCopy.value = tempData;
|
|
1421
|
-
}
|
|
1422
|
-
function setTreeExpand(row, option) {
|
|
1423
|
-
privateSetTreeExpand(row, { ...option, isClick: false });
|
|
1424
|
-
}
|
|
1425
|
-
function setNodeExpanded(row, expanded, level, parent) {
|
|
1426
|
-
row.__T_EXPANDED__ = expanded;
|
|
1427
|
-
if (level !== void 0) {
|
|
1428
|
-
row.__T_LV__ = level;
|
|
1429
|
-
}
|
|
1430
|
-
if (parent) {
|
|
1431
|
-
row.__T_PARENT_K__ = rowKeyGen(parent);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
function flatTreeData(data) {
|
|
1435
|
-
const result = [];
|
|
1436
|
-
(function recursion(data2, level, parent) {
|
|
1437
|
-
if (!data2) return;
|
|
1438
|
-
for (let i = 0; i < data2.length; i++) {
|
|
1439
|
-
const item = data2[i];
|
|
1440
|
-
result.push(item);
|
|
1441
|
-
const isExpanded = Boolean(item.__T_EXPANDED__);
|
|
1442
|
-
setNodeExpanded(item, isExpanded, level, parent);
|
|
1443
|
-
if (!isExpanded) {
|
|
1444
|
-
if (defaultExpandAll) {
|
|
1445
|
-
setNodeExpanded(item, true);
|
|
1446
|
-
} else {
|
|
1447
|
-
if (defaultExpandLevel) {
|
|
1448
|
-
if (level < defaultExpandLevel) {
|
|
1449
|
-
setNodeExpanded(item, true);
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
if (defaultExpandKeys) {
|
|
1453
|
-
if (defaultExpandKeys.includes(rowKeyGen(item))) {
|
|
1454
|
-
setNodeExpanded(item, true);
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
if (!item.__T_EXPANDED__) {
|
|
1458
|
-
continue;
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
recursion(item.children, level + 1, item);
|
|
1463
|
-
}
|
|
1464
|
-
})(data, 0);
|
|
1465
|
-
return result;
|
|
1466
|
-
}
|
|
1467
|
-
function expandNode(row, level) {
|
|
1468
|
-
let result = [];
|
|
1469
|
-
row.children && row.children.forEach((child) => {
|
|
1470
|
-
result.push(child);
|
|
1471
|
-
const childLv = level + 1;
|
|
1472
|
-
if (child.__T_EXPANDED__ && child.children) {
|
|
1473
|
-
const res = expandNode(child, childLv);
|
|
1474
|
-
result = result.concat(res);
|
|
1475
|
-
} else {
|
|
1476
|
-
setNodeExpanded(child, false, childLv, row);
|
|
1477
|
-
}
|
|
1478
|
-
});
|
|
1479
|
-
return result;
|
|
1480
|
-
}
|
|
1481
|
-
function foldNode(index, tempData, level) {
|
|
1482
|
-
let deleteCount = 0;
|
|
1483
|
-
for (let i = index + 1; i < tempData.length; i++) {
|
|
1484
|
-
const child = tempData[i];
|
|
1485
|
-
if (child.__T_LV__ && child.__T_LV__ > level) {
|
|
1486
|
-
deleteCount++;
|
|
1487
|
-
} else {
|
|
1488
|
-
break;
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
return deleteCount;
|
|
1492
|
-
}
|
|
1493
|
-
return {
|
|
1494
|
-
toggleTreeNode,
|
|
1495
|
-
setTreeExpand,
|
|
1496
|
-
flatTreeData
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
1560
|
const _hoisted_1 = ["data-col-key", "draggable", "rowspan", "colspan", "title", "onClick"];
|
|
1500
1561
|
const _hoisted_2 = ["onMousedown"];
|
|
1501
1562
|
const _hoisted_3 = { class: "table-header-title" };
|
|
@@ -1566,7 +1627,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1566
1627
|
treeConfig: { default: () => ({}) },
|
|
1567
1628
|
cellFixedMode: { default: "sticky" },
|
|
1568
1629
|
smoothScroll: { type: Boolean, default: DEFAULT_SMOOTH_SCROLL },
|
|
1569
|
-
scrollRowByRow: { type: Boolean, default: false }
|
|
1630
|
+
scrollRowByRow: { type: [Boolean, String], default: false }
|
|
1570
1631
|
},
|
|
1571
1632
|
emits: ["sort-change", "row-click", "current-change", "cell-selected", "row-dblclick", "header-row-menu", "row-menu", "cell-click", "cell-mouseenter", "cell-mouseleave", "cell-mouseover", "cell-mousedown", "header-cell-click", "scroll", "scroll-x", "col-order-change", "th-drag-start", "th-drop", "row-order-change", "col-resize", "toggle-row-expand", "toggle-tree-expand", "update:columns"],
|
|
1572
1633
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
@@ -1620,6 +1681,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1620
1681
|
}
|
|
1621
1682
|
});
|
|
1622
1683
|
const rowKeyGenCache = /* @__PURE__ */ new WeakMap();
|
|
1684
|
+
const { isSRBRActive } = useScrollRowByRow({ props, tableContainerRef });
|
|
1623
1685
|
const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits, colKeyGen });
|
|
1624
1686
|
const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
|
|
1625
1687
|
const {
|
|
@@ -1730,7 +1792,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1730
1792
|
nextTick(() => initVirtualScrollY());
|
|
1731
1793
|
}
|
|
1732
1794
|
const sortColValue = sortCol.value;
|
|
1733
|
-
if (sortColValue) {
|
|
1795
|
+
if (!isEmptyValue(sortColValue) && !props.sortRemote) {
|
|
1734
1796
|
const colKey = colKeyGen.value;
|
|
1735
1797
|
const column = tableHeaderLast.value.find((it) => colKey(it) === sortColValue);
|
|
1736
1798
|
onColumnSort(column, false);
|
|
@@ -2270,7 +2332,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2270
2332
|
"header-text-overflow": props.showHeaderOverflow,
|
|
2271
2333
|
"fixed-relative-mode": isRelativeMode.value,
|
|
2272
2334
|
"auto-row-height": props.autoRowHeight,
|
|
2273
|
-
"scroll-row-by-row":
|
|
2335
|
+
"scroll-row-by-row": unref(isSRBRActive)
|
|
2274
2336
|
}]),
|
|
2275
2337
|
style: normalizeStyle({
|
|
2276
2338
|
"--row-height": props.autoRowHeight ? void 0 : unref(virtualScroll).rowHeight + "px",
|
|
@@ -2281,7 +2343,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2281
2343
|
onScroll: onTableScroll,
|
|
2282
2344
|
onWheel: onTableWheel
|
|
2283
2345
|
}, [
|
|
2284
|
-
|
|
2346
|
+
unref(isSRBRActive) && _ctx.virtual ? (openBlock(), createElementBlock("div", {
|
|
2285
2347
|
key: 0,
|
|
2286
2348
|
class: "row-by-row-table-height",
|
|
2287
2349
|
style: normalizeStyle({ height: dataSourceCopy.value.length * unref(virtualScroll).rowHeight + "px" })
|
|
@@ -2388,7 +2450,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2388
2450
|
onDragend: _cache[6] || (_cache[6] = //@ts-ignore
|
|
2389
2451
|
(...args) => unref(onTrDragEnd) && unref(onTrDragEnd)(...args))
|
|
2390
2452
|
}, [
|
|
2391
|
-
unref(virtual_on) && !
|
|
2453
|
+
unref(virtual_on) && !unref(isSRBRActive) ? (openBlock(), createElementBlock("tr", {
|
|
2392
2454
|
key: 0,
|
|
2393
2455
|
style: normalizeStyle(`height:${unref(virtualScroll).offsetTop}px`),
|
|
2394
2456
|
class: "padding-top-tr"
|
|
@@ -2523,7 +2585,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2523
2585
|
}), 128))
|
|
2524
2586
|
], 46, _hoisted_6);
|
|
2525
2587
|
}), 128)),
|
|
2526
|
-
unref(virtual_on) && !
|
|
2588
|
+
unref(virtual_on) && !unref(isSRBRActive) ? (openBlock(), createElementBlock("tr", {
|
|
2527
2589
|
key: 1,
|
|
2528
2590
|
style: normalizeStyle(`height: ${unref(virtual_offsetBottom)}px`)
|
|
2529
2591
|
}, null, 4)) : createCommentVNode("", true)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stk-table-vue",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Simple realtime virtual table for vue3 and vue2.7",
|
|
5
5
|
"main": "./lib/stk-table-vue.js",
|
|
6
6
|
"types": "./lib/src/StkTable/index.d.ts",
|
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
},
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/d3-interpolate": "^3.0.4",
|
|
44
43
|
"@types/mockjs": "^1.0.10",
|
|
45
44
|
"@types/node": "^20.12.10",
|
|
46
45
|
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
@@ -60,7 +59,7 @@
|
|
|
60
59
|
"postcss-discard-comments": "^6.0.2",
|
|
61
60
|
"postcss-preset-env": "^9.5.11",
|
|
62
61
|
"prettier": "^3.2.5",
|
|
63
|
-
"stk-table-vue": "^0.
|
|
62
|
+
"stk-table-vue": "^0.7.1",
|
|
64
63
|
"typescript": "^5.4.5",
|
|
65
64
|
"vite": "^5.4.10",
|
|
66
65
|
"vite-plugin-dts": "3.9.1",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
'header-text-overflow': props.showHeaderOverflow,
|
|
26
26
|
'fixed-relative-mode': isRelativeMode,
|
|
27
27
|
'auto-row-height': props.autoRowHeight,
|
|
28
|
-
'scroll-row-by-row':
|
|
28
|
+
'scroll-row-by-row': isSRBRActive,
|
|
29
29
|
}"
|
|
30
30
|
:style="{
|
|
31
31
|
'--row-height': props.autoRowHeight ? void 0 : virtualScroll.rowHeight + 'px',
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
>
|
|
39
39
|
<!-- 这个元素用于整数行虚拟滚动时,撑开父容器的高度) -->
|
|
40
40
|
<div
|
|
41
|
-
v-if="
|
|
41
|
+
v-if="isSRBRActive && virtual"
|
|
42
42
|
class="row-by-row-table-height"
|
|
43
43
|
:style="{ height: dataSourceCopy.length * virtualScroll.rowHeight + 'px' }"
|
|
44
44
|
></div>
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
<!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }"></tbody> -->
|
|
115
115
|
<!-- <tbody :style="{ transform: `translateY(${virtualScroll.offsetTop}px)` }"> -->
|
|
116
116
|
<tbody class="stk-tbody-main" @dragover="onTrDragOver" @dragenter="onTrDragEnter" @dragend="onTrDragEnd">
|
|
117
|
-
<tr v-if="virtual_on && !
|
|
117
|
+
<tr v-if="virtual_on && !isSRBRActive" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
|
|
118
118
|
<!--这个td用于配合虚拟滚动的th对应,防止列错位-->
|
|
119
119
|
<td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
|
|
120
120
|
<template v-if="fixedMode && headless">
|
|
@@ -238,7 +238,7 @@
|
|
|
238
238
|
</td>
|
|
239
239
|
</template>
|
|
240
240
|
</tr>
|
|
241
|
-
<tr v-if="virtual_on && !
|
|
241
|
+
<tr v-if="virtual_on && !isSRBRActive" :style="`height: ${virtual_offsetBottom}px`"></tr>
|
|
242
242
|
</tbody>
|
|
243
243
|
</table>
|
|
244
244
|
<div v-if="(!dataSourceCopy || !dataSourceCopy.length) && showNoData" class="stk-table-no-data" :class="{ 'no-data-full': noDataFull }">
|
|
@@ -283,12 +283,13 @@ import { useGetFixedColPosition } from './useGetFixedColPosition';
|
|
|
283
283
|
import { useHighlight } from './useHighlight';
|
|
284
284
|
import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
|
|
285
285
|
import { useRowExpand } from './useRowExpand';
|
|
286
|
+
import { useScrollRowByRow } from './useScrollRowByRow';
|
|
286
287
|
import { useThDrag } from './useThDrag';
|
|
287
288
|
import { useTrDrag } from './useTrDrag';
|
|
289
|
+
import { useTree } from './useTree';
|
|
288
290
|
import { useVirtualScroll } from './useVirtualScroll';
|
|
289
291
|
import { createStkTableId, getCalculatedColWidth, getColWidth } from './utils/constRefUtils';
|
|
290
|
-
import { howDeepTheHeader, tableSort, transformWidthToStr } from './utils/index';
|
|
291
|
-
import { useTree } from './useTree';
|
|
292
|
+
import { howDeepTheHeader, isEmptyValue, tableSort, transformWidthToStr } from './utils/index';
|
|
292
293
|
|
|
293
294
|
/** Generic stands for DataType */
|
|
294
295
|
type DT = any & PrivateRowDT;
|
|
@@ -331,7 +332,7 @@ const props = withDefaults(
|
|
|
331
332
|
/** 当前行再次点击否可以取消 (rowActive=true)*/
|
|
332
333
|
rowCurrentRevokable?: boolean;
|
|
333
334
|
/** 表头行高。default = rowHeight */
|
|
334
|
-
headerRowHeight?: number | null;
|
|
335
|
+
headerRowHeight?: number | string | null;
|
|
335
336
|
/** 虚拟滚动 */
|
|
336
337
|
virtual?: boolean;
|
|
337
338
|
/** x轴虚拟滚动(必须设置列宽)*/
|
|
@@ -428,8 +429,11 @@ const props = withDefaults(
|
|
|
428
429
|
* - true: 不使用 onwheel 滚动。鼠标滚轮滚动时更加平滑。滚动过快时会白屏。
|
|
429
430
|
*/
|
|
430
431
|
smoothScroll?: boolean;
|
|
431
|
-
/**
|
|
432
|
-
|
|
432
|
+
/**
|
|
433
|
+
* 按整数行纵向滚动
|
|
434
|
+
* - scrollbar:仅拖动滚动条生效
|
|
435
|
+
*/
|
|
436
|
+
scrollRowByRow?: boolean | 'scrollbar';
|
|
433
437
|
}>(),
|
|
434
438
|
{
|
|
435
439
|
width: '',
|
|
@@ -738,6 +742,8 @@ const getEmptyCellText = computed(() => {
|
|
|
738
742
|
|
|
739
743
|
const rowKeyGenCache = new WeakMap();
|
|
740
744
|
|
|
745
|
+
const { isSRBRActive } = useScrollRowByRow({ props, tableContainerRef });
|
|
746
|
+
|
|
741
747
|
const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits, colKeyGen });
|
|
742
748
|
|
|
743
749
|
const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
|
|
@@ -870,7 +876,7 @@ watch(
|
|
|
870
876
|
nextTick(() => initVirtualScrollY());
|
|
871
877
|
}
|
|
872
878
|
const sortColValue = sortCol.value;
|
|
873
|
-
if (sortColValue) {
|
|
879
|
+
if (!isEmptyValue(sortColValue) && !props.sortRemote) {
|
|
874
880
|
// sort
|
|
875
881
|
const colKey = colKeyGen.value;
|
|
876
882
|
const column = tableHeaderLast.value.find(it => colKey(it) === sortColValue);
|
|
@@ -43,7 +43,7 @@ export type CustomCell<T extends CustomCellProps<U> | CustomHeaderCellProps<U>,
|
|
|
43
43
|
/** 表格列配置 */
|
|
44
44
|
export type StkTableColumn<T extends Record<string, any>> = {
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* 列唯一键,(可选),不传则默认取dataIndex 字段作为列唯一键。
|
|
47
47
|
*/
|
|
48
48
|
key?: any;
|
|
49
49
|
/**
|
|
@@ -40,18 +40,6 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
40
40
|
const highlightSteps = computed(() => (highlightFrequency.value ? Math.round(highlightDuration.value / highlightFrequency.value) : null));
|
|
41
41
|
/** 高亮开始 */
|
|
42
42
|
const highlightFrom = computed(() => highlightColor[props.theme as 'light' | 'dark'].from);
|
|
43
|
-
/** 高亮结束 */
|
|
44
|
-
// const highlightTo = computed(() => highlightColor[props.theme as 'light' | 'dark'].to);
|
|
45
|
-
// const highlightInter = computed(() => interpolateRgb(highlightFrom.value, highlightTo.value));
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 存放高亮行的状态-使用js计算颜色
|
|
49
|
-
* @key 行唯一键
|
|
50
|
-
* @value 记录高亮开始时间
|
|
51
|
-
*/
|
|
52
|
-
// const highlightDimRowsJs = new Map<UniqKey, number>();
|
|
53
|
-
/** 是否正在计算高亮行的循环-使用js计算颜色 */
|
|
54
|
-
// const calcHighlightDimLoopJs = false;
|
|
55
43
|
|
|
56
44
|
/**
|
|
57
45
|
* 存放高亮行的状态-使用animation api实现
|
|
@@ -85,7 +73,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
85
73
|
const recursion = () => {
|
|
86
74
|
window.requestAnimationFrame(
|
|
87
75
|
() => {
|
|
88
|
-
const nowTs =
|
|
76
|
+
const nowTs = performance.now();
|
|
89
77
|
highlightDimRowsAnimation.forEach((store, rowKeyValue) => {
|
|
90
78
|
const { ts, duration } = store;
|
|
91
79
|
const timeOffset = nowTs - ts;
|
|
@@ -110,59 +98,24 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
110
98
|
recursion();
|
|
111
99
|
}
|
|
112
100
|
|
|
113
|
-
/**
|
|
114
|
-
* js计算高亮渐暗颜色的循环
|
|
115
|
-
*/
|
|
116
|
-
// function calcRowHighlightLoopJs() {
|
|
117
|
-
// if (calcHighlightDimLoopJs) return;
|
|
118
|
-
// calcHighlightDimLoopJs = true;
|
|
119
|
-
// // js计算gradient
|
|
120
|
-
// const recursion = () => {
|
|
121
|
-
// window.setTimeout(() => {
|
|
122
|
-
// const nowTs = Date.now();
|
|
123
|
-
// highlightDimRowsJs.forEach((highlightStart, rowKeyValue) => {
|
|
124
|
-
// /** 经过的时间 ÷ 高亮持续时间 计算出 颜色过渡进度 (0-1) */
|
|
125
|
-
// const progress = (nowTs - highlightStart) / highlightDuration;
|
|
126
|
-
// let bgc = '';
|
|
127
|
-
// if (0 <= progress && progress <= 1) {
|
|
128
|
-
// bgc = highlightInter.value(progress);
|
|
129
|
-
// } else {
|
|
130
|
-
// highlightDimRowsJs.delete(rowKeyValue);
|
|
131
|
-
// }
|
|
132
|
-
// updateRowBgcJs(rowKeyValue, bgc);
|
|
133
|
-
// });
|
|
134
|
-
|
|
135
|
-
// if (highlightDimRowsJs.size > 0) {
|
|
136
|
-
// // 还有高亮的行,则下一次循环
|
|
137
|
-
// recursion();
|
|
138
|
-
// } else {
|
|
139
|
-
// // 没有则停止循环
|
|
140
|
-
// calcHighlightDimLoopJs = false;
|
|
141
|
-
// highlightDimRowsJs.clear(); // TODO: 是否需要 清除
|
|
142
|
-
// }
|
|
143
|
-
// }, highlightFrequency || HIGHLIGHT_FREQ);
|
|
144
|
-
// };
|
|
145
|
-
// recursion();
|
|
146
|
-
// }
|
|
147
|
-
|
|
148
101
|
/**
|
|
149
102
|
* 高亮一个单元格。暂不支持虚拟滚动高亮状态记忆。
|
|
150
103
|
* @param rowKeyValue 一行的key
|
|
151
104
|
* @param colKeyValue 列key
|
|
152
105
|
* @param options.method css-使用css渲染,animation-使用animation api。默认animation;
|
|
153
106
|
* @param option.className 自定义css动画的class。
|
|
154
|
-
* @param option.keyframe
|
|
107
|
+
* @param option.keyframe 如果自定义keyframe,则 highlightConfig.fps 将会失效。Keyframe:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
|
|
155
108
|
* @param option.duration 动画时长。method='css'状态下,用于移除class,如果传入了className则需要与自定义的动画时间一致。
|
|
156
109
|
*/
|
|
157
110
|
function setHighlightDimCell(rowKeyValue: UniqKey, colKeyValue: string, option: HighlightDimCellOption = {}) {
|
|
158
111
|
const cellEl = tableContainerRef.value?.querySelector<HTMLElement>(`[data-cell-key="${rowKeyValue}--${colKeyValue}"]`);
|
|
112
|
+
if (!cellEl) return;
|
|
159
113
|
const { className, method, duration, keyframe } = {
|
|
160
114
|
className: HIGHLIGHT_CELL_CLASS,
|
|
161
115
|
method: 'animation',
|
|
162
116
|
...defaultHighlightDimOption.value,
|
|
163
117
|
...option,
|
|
164
118
|
};
|
|
165
|
-
if (!cellEl) return;
|
|
166
119
|
if (method === 'animation') {
|
|
167
120
|
cellEl.animate(keyframe, duration);
|
|
168
121
|
} else {
|
|
@@ -175,11 +128,12 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
175
128
|
* @param rowKeyValues 行唯一键的数组
|
|
176
129
|
* @param option.method css-使用css渲染,animation-使用animation api,js-使用js计算颜色。默认animation
|
|
177
130
|
* @param option.className 自定义css动画的class。
|
|
178
|
-
* @param option.keyframe
|
|
131
|
+
* @param option.keyframe 如果自定义keyframe,则 highlightConfig.fps 将会失效。Keyframe:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats。
|
|
179
132
|
* @param option.duration 动画时长。method='css'状态下,用于移除class,如果传入了className则需要与自定义的动画时间一致。。
|
|
180
133
|
*/
|
|
181
134
|
function setHighlightDimRow(rowKeyValues: UniqKey[], option: HighlightDimRowOption = {}) {
|
|
182
135
|
if (!Array.isArray(rowKeyValues)) rowKeyValues = [rowKeyValues];
|
|
136
|
+
if (!rowKeyValues.length) return;
|
|
183
137
|
const { className, method, keyframe, duration } = {
|
|
184
138
|
className: HIGHLIGHT_ROW_CLASS,
|
|
185
139
|
method: 'animation',
|
|
@@ -187,13 +141,10 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
187
141
|
...option,
|
|
188
142
|
};
|
|
189
143
|
|
|
190
|
-
if (method === '
|
|
191
|
-
// -------- use css keyframe
|
|
192
|
-
highlightRowsInCssKeyframe(rowKeyValues, className, duration);
|
|
193
|
-
} else if (method === 'animation') {
|
|
144
|
+
if (method === 'animation') {
|
|
194
145
|
if (props.virtual) {
|
|
195
146
|
// -------- 用animation 接口实现动画
|
|
196
|
-
const nowTs =
|
|
147
|
+
const nowTs = performance.now();
|
|
197
148
|
for (let i = 0; i < rowKeyValues.length; i++) {
|
|
198
149
|
const rowKeyValue = rowKeyValues[i];
|
|
199
150
|
const store: HighlightDimRowStore = { ts: nowTs, visible: false, keyframe, duration };
|
|
@@ -209,17 +160,10 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
209
160
|
rowEl.animate(keyframe, duration);
|
|
210
161
|
}
|
|
211
162
|
}
|
|
163
|
+
} else {
|
|
164
|
+
// -------- use css keyframe
|
|
165
|
+
highlightRowsInCssKeyframe(rowKeyValues, className, duration);
|
|
212
166
|
}
|
|
213
|
-
// else if (method === 'js') {
|
|
214
|
-
// // -------- 用js计算颜色渐变的高亮方案
|
|
215
|
-
// const nowTs = Date.now();
|
|
216
|
-
// for (let i = 0; i < rowKeyValues.length; i++) {
|
|
217
|
-
// const rowKeyValue = rowKeyValues[i];
|
|
218
|
-
// highlightDimRowsJs.set(rowKeyValue, nowTs);
|
|
219
|
-
// updateRowBgcJs(rowKeyValue, highlightFrom.value);
|
|
220
|
-
// }
|
|
221
|
-
// calcRowHighlightLoopJs();
|
|
222
|
-
// }
|
|
223
167
|
}
|
|
224
168
|
|
|
225
169
|
/**
|
|
@@ -305,13 +249,6 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
|
|
|
305
249
|
}
|
|
306
250
|
}
|
|
307
251
|
|
|
308
|
-
/** 更新行状态 */
|
|
309
|
-
// function updateRowBgcJs(rowKeyValue: UniqKey, color: string) {
|
|
310
|
-
// const rowEl = document.getElementById(stkTableId + '-' + String(rowKeyValue));
|
|
311
|
-
// if (!rowEl) return;
|
|
312
|
-
// rowEl.style.backgroundColor = color;
|
|
313
|
-
// }
|
|
314
|
-
|
|
315
252
|
return {
|
|
316
253
|
highlightSteps,
|
|
317
254
|
setHighlightDimRow,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { computed, Ref, ref, onMounted, onUnmounted, watch } from 'vue';
|
|
2
|
+
|
|
3
|
+
type Params = {
|
|
4
|
+
props: any;
|
|
5
|
+
tableContainerRef: Ref<HTMLElement | undefined>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function useScrollRowByRow({ props, tableContainerRef }: Params) {
|
|
9
|
+
let isMouseDown = false;
|
|
10
|
+
let isAddListeners = false;
|
|
11
|
+
|
|
12
|
+
/**记录滚动条是否正在被拖动 */
|
|
13
|
+
const isDragScroll = ref(false);
|
|
14
|
+
const onlyDragScroll = computed(() => props.scrollRowByRow === 'scrollbar');
|
|
15
|
+
|
|
16
|
+
/** is ScrollRowByRow active */
|
|
17
|
+
const isSRBRActive = computed(() => {
|
|
18
|
+
if (onlyDragScroll.value) {
|
|
19
|
+
return isDragScroll.value;
|
|
20
|
+
}
|
|
21
|
+
return props.scrollRowByRow;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
watch(
|
|
25
|
+
() => onlyDragScroll.value,
|
|
26
|
+
v => {
|
|
27
|
+
if (v) {
|
|
28
|
+
addEventListener();
|
|
29
|
+
} else {
|
|
30
|
+
removeEventListener();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
onMounted(() => {
|
|
36
|
+
addEventListener();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
onUnmounted(() => {
|
|
40
|
+
removeEventListener();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function addEventListener() {
|
|
44
|
+
if (isAddListeners || !onlyDragScroll.value) return;
|
|
45
|
+
const container = tableContainerRef.value;
|
|
46
|
+
if (!container) return;
|
|
47
|
+
container.addEventListener('mousedown', handleMouseDown);
|
|
48
|
+
container.addEventListener('mouseup', handleMouseUp);
|
|
49
|
+
container.addEventListener('scroll', handleScroll);
|
|
50
|
+
isAddListeners = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function removeEventListener() {
|
|
54
|
+
const container = tableContainerRef.value;
|
|
55
|
+
if (!container) return;
|
|
56
|
+
container.removeEventListener('mousedown', handleMouseDown);
|
|
57
|
+
container.removeEventListener('mouseup', handleMouseUp);
|
|
58
|
+
container.removeEventListener('scroll', handleScroll);
|
|
59
|
+
isAddListeners = false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleMouseDown() {
|
|
63
|
+
isMouseDown = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function handleMouseUp() {
|
|
67
|
+
isMouseDown = false;
|
|
68
|
+
isDragScroll.value = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function handleScroll() {
|
|
72
|
+
if (!isMouseDown) return;
|
|
73
|
+
isDragScroll.value = true;
|
|
74
|
+
}
|
|
75
|
+
return { isSRBRActive, isDragScroll };
|
|
76
|
+
}
|
|
@@ -21,9 +21,13 @@ export function isEmptyValue(val: any, isNumber?: boolean) {
|
|
|
21
21
|
* @param targetArray 表格数据
|
|
22
22
|
* @return targetArray 的浅拷贝
|
|
23
23
|
*/
|
|
24
|
-
export function insertToOrderedArray<T extends object>(
|
|
24
|
+
export function insertToOrderedArray<T extends object>(
|
|
25
|
+
sortState: SortState<T>,
|
|
26
|
+
newItem: T,
|
|
27
|
+
targetArray: T[],
|
|
28
|
+
sortConfig: SortConfig<T> & { customCompare?: (a: T, b: T) => number } = {},
|
|
29
|
+
) {
|
|
25
30
|
const { dataIndex, sortField, order } = sortState;
|
|
26
|
-
sortConfig = { emptyToBottom: false, ...sortConfig };
|
|
27
31
|
let { sortType } = sortState;
|
|
28
32
|
const field = sortField || dataIndex;
|
|
29
33
|
if (!sortType) sortType = typeof newItem[field] as 'number' | 'string';
|
|
@@ -35,17 +39,24 @@ export function insertToOrderedArray<T extends object>(sortState: SortState<T>,
|
|
|
35
39
|
return data;
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
const { emptyToBottom, customCompare, stringLocaleCompare } = { emptyToBottom: false, ...sortConfig };
|
|
43
|
+
|
|
38
44
|
const targetVal: any = newItem[field];
|
|
39
|
-
if (
|
|
45
|
+
if (emptyToBottom && isEmptyValue(targetVal)) {
|
|
40
46
|
// 空值排在最下方
|
|
41
47
|
data.push(newItem);
|
|
42
48
|
} else {
|
|
49
|
+
const customCompareFn =
|
|
50
|
+
customCompare ||
|
|
51
|
+
((a, b) => {
|
|
52
|
+
const midVal: any = a[field];
|
|
53
|
+
const compareRes = strCompare(midVal, targetVal, isNumber, stringLocaleCompare);
|
|
54
|
+
return order === 'asc' ? compareRes : -compareRes;
|
|
55
|
+
});
|
|
43
56
|
const isNumber = sortType === 'number';
|
|
44
57
|
// 二分插入
|
|
45
58
|
const sIndex = binarySearch(data, midIndex => {
|
|
46
|
-
|
|
47
|
-
const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
|
|
48
|
-
return order === 'asc' ? compareRes : -compareRes;
|
|
59
|
+
return customCompareFn(data[midIndex], newItem);
|
|
49
60
|
});
|
|
50
61
|
data.splice(sIndex, 0, newItem);
|
|
51
62
|
}
|