stk-table-vue 0.2.2 → 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 CHANGED
@@ -5,7 +5,8 @@ Vue3 简易虚拟滚动表格。用于实时数据展示,新数据行高亮渐
5
5
  Vue2.7支持引入源码(**ts**)使用。
6
6
 
7
7
  ## Bug TODO:
8
- * [] props.dataSource 为 shallowRef 时,高亮行不生效。(2024.02.21)
8
+ * [x] props.dataSource 为 shallowRef 时,高亮行不生效。(bug:2024.02.21)(resolved:0.2.3)
9
+
9
10
  ## Feature TODO:
10
11
  * [x] 高亮行,单元格。
11
12
  * [x] 虚拟滚动。
@@ -22,7 +23,7 @@ Vue2.7支持引入源码(**ts**)使用。
22
23
  * [x] 表头拖动调整列宽。
23
24
  * [x] 排序
24
25
  - [x] 基本表头点击排序。
25
- - [] 支持配置 `null` | `undefined` 永远排最后。
26
+ - [x] 支持配置 `null` | `undefined` 永远排最后。
26
27
  - [] 支持配置 string 使用 `String.prototype.localCompare` 排序。
27
28
  * 多级表头。
28
29
  - [x] 支持最多`2级`表头。
@@ -126,9 +127,9 @@ export type StkProps = {
126
127
  /** 表格数据源 */
127
128
  dataSource?: any[];
128
129
  /** 行唯一键 */
129
- rowKey?: UniqKey;
130
+ rowKey?: UniqKeyProp;
130
131
  /** 列唯一键 */
131
- colKey?: UniqKey;
132
+ colKey?: UniqKeyProp;
132
133
  /** 空值展示文字 */
133
134
  emptyCellText?: string;
134
135
  /** 暂无数据兜底高度是否撑满 */
@@ -181,7 +182,12 @@ export type StkProps = {
181
182
  /** 排序配置 */
182
183
  sortConfig?: {
183
184
  /** 空值是否排最下面 */
184
- emptyToBottom: false,
185
+ emptyToBottom: boolean,
186
+ /** 默认排序(1.初始化时触发 2.排序方向为null时触发) */
187
+ defaultSort?: {
188
+ dataIndex: keyof T;
189
+ order: Order;
190
+ };
185
191
  },
186
192
  };
187
193
  ```
@@ -192,7 +198,7 @@ export type StkProps = {
192
198
  * 排序变更触发
193
199
  * ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
194
200
  */
195
- (e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig:SortConfig): void;
201
+ (e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig): void;
196
202
  /**
197
203
  * 一行点击事件
198
204
  * ```(ev: MouseEvent, row: DT)```
@@ -1,4 +1,4 @@
1
- import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from './types/index';
1
+ import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKeyProp } from './types/index';
2
2
  /** Generic stands for DataType */
3
3
  type DT = any;
4
4
  /**
@@ -11,14 +11,14 @@ declare function setCurrentRow(rowKey: string, option?: {
11
11
  }): void;
12
12
  /**
13
13
  * 设置表头排序状态
14
- * @param {string} dataIndex 列字段
15
- * @param {'asc'|'desc'|null} order
16
- * @param {object} option.sortOption 指定排序参数
17
- * @param {boolean} option.sort 是否触发排序
18
- * @param {boolean} option.silent 是否触发回调
14
+ * @param dataIndex 列字段
15
+ * @param order 正序倒序
16
+ * @param option.sortOption 指定排序参数
17
+ * @param option.sort 是否触发排序-默认true
18
+ * @param option.silent 是否禁止触发回调-默认true
19
19
  */
20
- declare function setSorter(dataIndex: string, order: null | 'asc' | 'desc', option?: {
21
- sortOption?: SortOption;
20
+ declare function setSorter(dataIndex: string, order: Order, option?: {
21
+ sortOption?: SortOption<DT>;
22
22
  silent?: boolean;
23
23
  sort?: boolean;
24
24
  }): any[];
@@ -61,9 +61,9 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
61
61
  /** 表格数据源 */
62
62
  dataSource?: any[] | undefined;
63
63
  /** 行唯一键 */
64
- rowKey?: UniqKey | undefined;
64
+ rowKey?: UniqKeyProp | undefined;
65
65
  /** 列唯一键 */
66
- colKey?: UniqKey | undefined;
66
+ colKey?: UniqKeyProp | undefined;
67
67
  /** 空值展示文字 */
68
68
  emptyCellText?: string | undefined;
69
69
  /** 暂无数据兜底高度是否撑满 */
@@ -112,7 +112,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
112
112
  /** 优化vue2 滚动 */
113
113
  optimizeVue2Scroll?: boolean | undefined;
114
114
  /** 排序配置 */
115
- sortConfig?: SortConfig | undefined;
115
+ sortConfig?: SortConfig<any> | undefined;
116
116
  }>, {
117
117
  width: string;
118
118
  fixedMode: boolean;
@@ -159,7 +159,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
159
159
  /** 设置高亮渐暗单元格 */
160
160
  setHighlightDimCell: (rowKeyValue: string, dataIndex: string) => void;
161
161
  /** 设置高亮渐暗行 */
162
- setHighlightDimRow: (rowKeyValues: (string | number)[]) => void;
162
+ setHighlightDimRow: (rowKeyValues: import("./types/index").UniqKey[]) => void;
163
163
  /** 表格排序列dataIndex */
164
164
  sortCol: import("vue").Ref<string | null | undefined>;
165
165
  /** 获取当前排序状态 */
@@ -173,7 +173,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
173
173
  /** 获取表格数据 */
174
174
  getTableData: typeof getTableData;
175
175
  }, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
176
- "sort-change": (col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig) => void;
176
+ "sort-change": (col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig<any>) => void;
177
177
  "row-click": (ev: MouseEvent, row: any) => void;
178
178
  "current-change": (ev: MouseEvent | null, row: any) => void;
179
179
  "row-dblclick": (ev: MouseEvent, row: any) => void;
@@ -217,9 +217,9 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
217
217
  /** 表格数据源 */
218
218
  dataSource?: any[] | undefined;
219
219
  /** 行唯一键 */
220
- rowKey?: UniqKey | undefined;
220
+ rowKey?: UniqKeyProp | undefined;
221
221
  /** 列唯一键 */
222
- colKey?: UniqKey | undefined;
222
+ colKey?: UniqKeyProp | undefined;
223
223
  /** 空值展示文字 */
224
224
  emptyCellText?: string | undefined;
225
225
  /** 暂无数据兜底高度是否撑满 */
@@ -268,7 +268,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
268
268
  /** 优化vue2 滚动 */
269
269
  optimizeVue2Scroll?: boolean | undefined;
270
270
  /** 排序配置 */
271
- sortConfig?: SortConfig | undefined;
271
+ sortConfig?: SortConfig<any> | undefined;
272
272
  }>, {
273
273
  width: string;
274
274
  fixedMode: boolean;
@@ -312,7 +312,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
312
312
  "onTh-drag-start"?: ((dragStartKey: string) => any) | undefined;
313
313
  "onCol-order-change"?: ((dragStartKey: string, targetColKey: string) => any) | undefined;
314
314
  "onTh-drop"?: ((targetColKey: string) => any) | undefined;
315
- "onSort-change"?: ((col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig) => any) | undefined;
315
+ "onSort-change"?: ((col: StkTableColumn<any>, order: Order, data: any[], sortConfig: SortConfig<any>) => any) | undefined;
316
316
  "onRow-click"?: ((ev: MouseEvent, row: any) => any) | undefined;
317
317
  "onCurrent-change"?: ((ev: MouseEvent | null, row: any) => any) | undefined;
318
318
  "onRow-dblclick"?: ((ev: MouseEvent, row: any) => any) | undefined;
@@ -335,8 +335,8 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
335
335
  virtualX: boolean;
336
336
  columns: StkTableColumn<any>[];
337
337
  dataSource: any[];
338
- rowKey: UniqKey;
339
- colKey: UniqKey;
338
+ rowKey: UniqKeyProp;
339
+ colKey: UniqKeyProp;
340
340
  emptyCellText: string;
341
341
  noDataFull: boolean;
342
342
  showNoData: boolean;
@@ -352,7 +352,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
352
352
  autoResize: boolean | (() => void);
353
353
  fixedColShadow: boolean;
354
354
  optimizeVue2Scroll: boolean;
355
- sortConfig: SortConfig;
355
+ sortConfig: SortConfig<any>;
356
356
  }, {}>, {
357
357
  tableHeader?(_: {
358
358
  col: StkTableColumn<any>;
@@ -63,16 +63,25 @@ export type StkTableColumn<T extends Record<string, any>> = {
63
63
  /** 父节点引用 */
64
64
  __PARENT__?: StkTableColumn<T> | null;
65
65
  };
66
- export type SortOption = Pick<StkTableColumn<any>, 'sorter' | 'dataIndex' | 'sortField' | 'sortType'>;
66
+ export type SortOption<T extends Record<string, any>> = Pick<StkTableColumn<T>, 'sorter' | 'dataIndex' | 'sortField' | 'sortType'>;
67
+ /** 排序状态 */
67
68
  export type SortState<T> = {
68
69
  dataIndex: T;
69
70
  order: null | 'asc' | 'desc';
70
71
  sortType?: 'number' | 'string';
71
72
  };
72
- export type UniqKey = string | ((param: any) => string);
73
+ /** 唯一键 */
74
+ export type UniqKey = string | number;
75
+ export type UniqKeyFun = (param: any) => UniqKey;
76
+ export type UniqKeyProp = UniqKey | UniqKeyFun;
73
77
  /** 排序配置 */
74
- export type SortConfig = {
78
+ export type SortConfig<T extends Record<string, any>> = {
75
79
  /** 空值始终排在列表末尾 */
76
80
  emptyToBottom?: boolean;
81
+ /** 默认排序(1.初始化时触发 2.排序方向为null时触发) */
82
+ defaultSort?: {
83
+ dataIndex: keyof T;
84
+ order: Order;
85
+ };
77
86
  };
78
87
  export {};
@@ -1,4 +1,5 @@
1
1
  import { Ref } from 'vue';
2
+ import { UniqKey } from './types';
2
3
  type Params = {
3
4
  props: {
4
5
  theme: 'light' | 'dark';
@@ -6,14 +7,20 @@ type Params = {
6
7
  dataSource: any[];
7
8
  };
8
9
  tableContainer: Ref<HTMLElement | undefined>;
9
- rowKeyGen: (p: any) => string;
10
+ };
11
+ /** 高亮行保存的东西 */
12
+ type HighlightRowStore = {
13
+ bgc: string;
14
+ bgc_progress_ms: number;
15
+ bgc_progress: number;
10
16
  };
11
17
  /**
12
18
  * 高亮单元格,行
13
19
  * row中新增_bgc_progress_ms 属性控制高亮状态,_bgc控制颜色
14
20
  */
15
- export declare function useHighlight({ props, tableContainer, rowKeyGen }: Params): {
16
- setHighlightDimRow: (rowKeyValues: Array<string | number>) => void;
21
+ export declare function useHighlight({ props, tableContainer }: Params): {
22
+ highlightRowStore: Ref<Record<UniqKey, HighlightRowStore>>;
23
+ setHighlightDimRow: (rowKeyValues: UniqKey[]) => void;
17
24
  setHighlightDimCell: (rowKeyValue: string, dataIndex: string) => void;
18
25
  };
19
26
  export {};
@@ -14,11 +14,13 @@ export declare function insertToOrderedArray<T extends object>(sortState: SortSt
14
14
  * 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
15
15
  * 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
16
16
  * TODO: key 唯一值,排序字段相同时,根据唯一值排序。
17
+ *
18
+ * sortConfig.defaultSort 会在order为null时生效
17
19
  * @param sortOption 列配置
18
20
  * @param order 排序方式
19
21
  * @param dataSource 排序的数组
20
22
  */
21
- export declare function tableSort(sortOption: SortOption, order: Order, dataSource: any[], sortConfig?: SortConfig): any[];
23
+ export declare function tableSort<T extends Record<string, any>>(sortOption: SortOption<T>, order: Order, dataSource: T[], sortConfig?: SortConfig<T>): T[];
22
24
  /** 表头column配置的层级 */
23
25
  export declare function howDeepTheHeader(arr: StkTableColumn<any>[], level?: number): number;
24
26
  /** 获取列宽 */
@@ -138,14 +138,18 @@ function separatedData(sortOption, targetDataSource, isNumber) {
138
138
  function tableSort(sortOption, order, dataSource, sortConfig = {}) {
139
139
  if (!(dataSource == null ? void 0 : dataSource.length))
140
140
  return dataSource || [];
141
- sortConfig = Object.assign({ emptyToBottom: false }, sortConfig);
141
+ sortConfig = { emptyToBottom: false, ...sortConfig };
142
142
  let targetDataSource = [...dataSource];
143
+ let sortField = sortOption.sortField || sortOption.dataIndex;
144
+ if (!order && sortConfig.defaultSort) {
145
+ order = sortConfig.defaultSort.order;
146
+ sortField = sortConfig.defaultSort.dataIndex;
147
+ }
143
148
  if (typeof sortOption.sorter === "function") {
144
149
  const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
145
150
  if (customSorterData)
146
151
  targetDataSource = customSorterData;
147
152
  } else if (order) {
148
- const sortField = sortOption.sortField || sortOption.dataIndex;
149
153
  let { sortType } = sortOption;
150
154
  if (!sortType)
151
155
  sortType = typeof dataSource[0][sortField];
@@ -436,11 +440,14 @@ function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualScrollX,
436
440
  getFixedStyle
437
441
  };
438
442
  }
439
- function useHighlight({ props, tableContainer, rowKeyGen }) {
440
- const highlightInter = computed(() => {
441
- return interpolateRgb(Highlight_Color[props.theme].from, Highlight_Color[props.theme].to);
442
- });
443
- const highlightDimRows = /* @__PURE__ */ new Set();
443
+ const HIGHLIGHT_ROW_CLASS = "highlight-row";
444
+ const HIGHLIGHT_CELL_CLASS = "highlight-cell";
445
+ function useHighlight({ props, tableContainer }) {
446
+ const highlightRowStore = ref({});
447
+ const highlightFrom = Highlight_Color[props.theme].from;
448
+ const highlightTo = Highlight_Color[props.theme].to;
449
+ const highlightInter = computed(() => interpolateRgb(highlightFrom, highlightTo));
450
+ const highlightDimRowKeys = /* @__PURE__ */ new Set();
444
451
  const highlightDimRowsTimeout = /* @__PURE__ */ new Map();
445
452
  const highlightDimCellsTimeout = /* @__PURE__ */ new Map();
446
453
  let calcHighlightDimLoop = false;
@@ -451,18 +458,17 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
451
458
  const recursion = () => {
452
459
  window.setTimeout(() => {
453
460
  const nowTs = Date.now();
454
- const needDeleteRows = [];
455
- highlightDimRows.forEach((row) => {
456
- const progress = (nowTs - row._bgc_progress_ms) / Highlight_Duration;
461
+ highlightDimRowKeys.forEach((rowKeyValue) => {
462
+ const highlightItem = highlightRowStore.value[rowKeyValue];
463
+ const progress = (nowTs - highlightItem.bgc_progress_ms) / Highlight_Duration;
457
464
  if (0 < progress && progress < 1) {
458
- row._bgc = highlightInter.value(progress);
465
+ highlightItem.bgc = highlightInter.value(progress);
459
466
  } else {
460
- row._bgc = "";
461
- needDeleteRows.push(row);
467
+ highlightItem.bgc = "";
468
+ highlightDimRowKeys.delete(rowKeyValue);
462
469
  }
463
470
  });
464
- needDeleteRows.forEach((row) => highlightDimRows.delete(row));
465
- if (highlightDimRows.size > 0) {
471
+ if (highlightDimRowKeys.size > 0) {
466
472
  recursion();
467
473
  } else {
468
474
  calcHighlightDimLoop = false;
@@ -476,16 +482,16 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
476
482
  const cellEl = (_a = tableContainer.value) == null ? void 0 : _a.querySelector(`[data-row-key="${rowKeyValue}"]>[data-index="${dataIndex}"]`);
477
483
  if (!cellEl)
478
484
  return;
479
- if (cellEl.classList.contains("highlight-cell")) {
480
- cellEl.classList.remove("highlight-cell");
485
+ if (cellEl.classList.contains(HIGHLIGHT_CELL_CLASS)) {
486
+ cellEl.classList.remove(HIGHLIGHT_CELL_CLASS);
481
487
  void cellEl.offsetHeight;
482
488
  }
483
- cellEl.classList.add("highlight-cell");
489
+ cellEl.classList.add(HIGHLIGHT_CELL_CLASS);
484
490
  window.clearTimeout(highlightDimCellsTimeout.get(rowKeyValue));
485
491
  highlightDimCellsTimeout.set(
486
492
  rowKeyValue,
487
493
  window.setTimeout(() => {
488
- cellEl.classList.remove("highlight-cell");
494
+ cellEl.classList.remove(HIGHLIGHT_CELL_CLASS);
489
495
  highlightDimCellsTimeout.delete(rowKeyValue);
490
496
  }, Highlight_Duration)
491
497
  );
@@ -498,11 +504,12 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
498
504
  const nowTs = Date.now();
499
505
  for (let i = 0; i < rowKeyValues.length; i++) {
500
506
  const rowKeyValue = rowKeyValues[i];
501
- const row = props.dataSource.find((it) => rowKeyGen(it) === rowKeyValue);
502
- if (!row)
503
- continue;
504
- row._bgc_progress_ms = nowTs;
505
- highlightDimRows.add(row);
507
+ highlightRowStore.value[rowKeyValue] = {
508
+ bgc: "",
509
+ bgc_progress: 0,
510
+ bgc_progress_ms: nowTs
511
+ };
512
+ highlightDimRowKeys.add(rowKeyValue);
506
513
  }
507
514
  calcHighlightLoop();
508
515
  } else {
@@ -513,8 +520,8 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
513
520
  const rowEl = (_a = tableContainer.value) == null ? void 0 : _a.querySelector(`[data-row-key="${rowKeyValue}"]`);
514
521
  if (!rowEl)
515
522
  continue;
516
- if (rowEl.classList.contains("highlight-row")) {
517
- rowEl.classList.remove("highlight-row");
523
+ if (rowEl.classList.contains(HIGHLIGHT_ROW_CLASS)) {
524
+ rowEl.classList.remove(HIGHLIGHT_ROW_CLASS);
518
525
  needRepaint = true;
519
526
  }
520
527
  rowElTemp.push(rowEl);
@@ -522,7 +529,7 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
522
529
  highlightDimRowsTimeout.set(
523
530
  rowKeyValue,
524
531
  window.setTimeout(() => {
525
- rowEl.classList.remove("highlight-row");
532
+ rowEl.classList.remove(HIGHLIGHT_ROW_CLASS);
526
533
  highlightDimRowsTimeout.delete(rowKeyValue);
527
534
  }, Highlight_Duration)
528
535
  );
@@ -530,10 +537,11 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
530
537
  if (needRepaint) {
531
538
  void ((_b = tableContainer.value) == null ? void 0 : _b.offsetWidth);
532
539
  }
533
- rowElTemp.forEach((el) => el.classList.add("highlight-row"));
540
+ rowElTemp.forEach((el) => el.classList.add(HIGHLIGHT_ROW_CLASS));
534
541
  }
535
542
  }
536
543
  return {
544
+ highlightRowStore,
537
545
  setHighlightDimRow,
538
546
  setHighlightDimCell
539
547
  };
@@ -887,8 +895,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
887
895
  virtualX: { type: Boolean, default: false },
888
896
  columns: { default: () => [] },
889
897
  dataSource: { default: () => [] },
890
- rowKey: { type: [String, Function], default: "" },
891
- colKey: { type: [String, Function], default: "dataIndex" },
898
+ rowKey: { type: [String, Number, Function], default: "" },
899
+ colKey: { type: [String, Number, Function], default: "dataIndex" },
892
900
  emptyCellText: { default: "--" },
893
901
  noDataFull: { type: Boolean, default: false },
894
902
  showNoData: { type: Boolean, default: true },
@@ -956,7 +964,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
956
964
  virtualX_on,
957
965
  virtualX_offsetRight
958
966
  });
959
- const { setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer, rowKeyGen });
967
+ const { highlightRowStore, setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer });
960
968
  if (props.autoResize) {
961
969
  useAutoResize({ tableContainer, initVirtualScroll, props, debounceMs: 200 });
962
970
  }
@@ -1008,7 +1016,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1008
1016
  onMounted(() => {
1009
1017
  initVirtualScroll();
1010
1018
  updateFixedShadow();
1019
+ dealDefaultSorter();
1011
1020
  });
1021
+ function dealDefaultSorter() {
1022
+ if (!props.sortConfig.defaultSort)
1023
+ return;
1024
+ const { dataIndex, order } = props.sortConfig.defaultSort;
1025
+ setSorter(dataIndex, order);
1026
+ }
1012
1027
  function dealColumns() {
1013
1028
  tableHeaders.value = [];
1014
1029
  tableHeaderLast.value = [];
@@ -1104,8 +1119,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1104
1119
  if (click)
1105
1120
  sortOrderIndex.value++;
1106
1121
  sortOrderIndex.value = sortOrderIndex.value % 3;
1107
- const order = sortSwitchOrder[sortOrderIndex.value];
1122
+ let order = sortSwitchOrder[sortOrderIndex.value];
1108
1123
  const sortConfig = props.sortConfig;
1124
+ const defaultSort = sortConfig.defaultSort;
1125
+ if (!order && defaultSort) {
1126
+ order = defaultSort.order;
1127
+ sortOrderIndex.value = sortSwitchOrder.indexOf(order);
1128
+ sortCol.value = defaultSort.dataIndex;
1129
+ }
1109
1130
  if (!props.sortRemote || options.force) {
1110
1131
  dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
1111
1132
  }
@@ -1189,7 +1210,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1189
1210
  var _a;
1190
1211
  const newOption = { silent: true, sortOption: null, sort: true, ...option };
1191
1212
  sortCol.value = dataIndex;
1192
- sortOrderIndex.value = sortSwitchOrder.findIndex((it) => it === order);
1213
+ sortOrderIndex.value = sortSwitchOrder.indexOf(order);
1193
1214
  if (newOption.sort && ((_a = dataSourceCopy.value) == null ? void 0 : _a.length)) {
1194
1215
  const column = newOption.sortOption || tableHeaderLast.value.find((it) => it.dataIndex === sortCol.value);
1195
1216
  if (column)
@@ -1377,6 +1398,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1377
1398
  }), 128)) : createCommentVNode("", true)
1378
1399
  ], 4)) : createCommentVNode("", true),
1379
1400
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtual_dataSourcePart), (row, i) => {
1401
+ var _a;
1380
1402
  return openBlock(), createElementBlock("tr", {
1381
1403
  key: _ctx.rowKey ? rowKeyGen(row) : i,
1382
1404
  "data-row-key": _ctx.rowKey ? rowKeyGen(row) : i,
@@ -1386,7 +1408,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1386
1408
  [_ctx.rowClassName(row, i)]: true
1387
1409
  }),
1388
1410
  style: normalizeStyle({
1389
- backgroundColor: row._bgc
1411
+ backgroundColor: (_a = unref(highlightRowStore)[rowKeyGen(row)]) == null ? void 0 : _a.bgc
1390
1412
  }),
1391
1413
  onClick: (e) => onRowClick(e, row),
1392
1414
  onDblclick: (e) => onRowDblclick(e, row),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Simple realtime virtual table for vue3&vue2.7",
5
5
  "main": "./lib/stk-table-vue.js",
6
6
  "types": "./lib/StkTable/index.d.ts",
@@ -148,7 +148,7 @@
148
148
  [rowClassName(row, i)]: true,
149
149
  }"
150
150
  :style="{
151
- backgroundColor: row._bgc,
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, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from './types/index';
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?: UniqKey;
238
+ rowKey?: UniqKeyProp;
239
239
  /** 列唯一键 */
240
- colKey?: UniqKey;
240
+ colKey?: UniqKeyProp;
241
241
  /** 空值展示文字 */
242
242
  emptyCellText?: string;
243
243
  /** 暂无数据兜底高度是否撑满 */
@@ -286,7 +286,7 @@ const props = withDefaults(
286
286
  /** 优化vue2 滚动 */
287
287
  optimizeVue2Scroll?: boolean;
288
288
  /** 排序配置 */
289
- sortConfig?: SortConfig;
289
+ sortConfig?: SortConfig<DT>;
290
290
  }>(),
291
291
  {
292
292
  width: '',
@@ -330,7 +330,7 @@ const emits = defineEmits<{
330
330
  * 排序变更触发
331
331
  * ```(col: StkTableColumn<DT>, order: Order, data: DT[])```
332
332
  */
333
- (e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig): void;
333
+ (e: 'sort-change', col: StkTableColumn<DT>, order: Order, data: DT[], sortConfig: SortConfig<DT>): void;
334
334
  /**
335
335
  * 一行点击事件
336
336
  * ```(ev: MouseEvent, row: DT)```
@@ -484,7 +484,7 @@ const { getFixedStyle } = useFixedStyle({
484
484
  /**
485
485
  * 高亮行,高亮单元格
486
486
  */
487
- const { setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer, rowKeyGen });
487
+ const { highlightRowStore, setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer });
488
488
 
489
489
  if (props.autoResize) {
490
490
  useAutoResize({ tableContainer, initVirtualScroll, props, debounceMs: 200 });
@@ -548,8 +548,16 @@ watch(() => props.fixedColShadow, dealFixedColShadow);
548
548
  onMounted(() => {
549
549
  initVirtualScroll();
550
550
  updateFixedShadow();
551
+ dealDefaultSorter();
551
552
  });
552
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
+
553
561
  /**
554
562
  * 处理多级表头
555
563
  */
@@ -559,7 +567,7 @@ function dealColumns() {
559
567
  tableHeaderLast.value = [];
560
568
  const copyColumn = props.columns; // do not deep clone
561
569
  const deep = howDeepTheHeader(copyColumn);
562
- const tempHeaderLast: StkTableColumn<any>[] = [];
570
+ const tempHeaderLast: StkTableColumn<DT>[] = [];
563
571
 
564
572
  if (deep > 1 && props.virtualX) {
565
573
  console.error('多级表头不支持横向虚拟滚动');
@@ -637,7 +645,7 @@ function colKeyGen(col: StkTableColumn<DT>) {
637
645
  }
638
646
 
639
647
  /** 获取列宽度样式 */
640
- function getColWidthStyle(col: StkTableColumn<any>) {
648
+ function getColWidthStyle(col: StkTableColumn<DT>) {
641
649
  const style: CSSProperties = {
642
650
  width: col.width,
643
651
  minWidth: col.minWidth,
@@ -662,7 +670,7 @@ function getColWidthStyle(col: StkTableColumn<any>) {
662
670
  * @param col
663
671
  * @param depth 表头层级
664
672
  */
665
- function getCellStyle(tagType: 1 | 2, col: StkTableColumn<any>, depth?: number): CSSProperties {
673
+ function getCellStyle(tagType: 1 | 2, col: StkTableColumn<DT>, depth?: number): CSSProperties {
666
674
  const style: CSSProperties = {
667
675
  ...getColWidthStyle(col),
668
676
  ...getFixedStyle(tagType, col, depth),
@@ -680,10 +688,11 @@ function getCellStyle(tagType: 1 | 2, col: StkTableColumn<any>, depth?: number):
680
688
 
681
689
  /**
682
690
  * 表头点击排序
683
- * @param {boolean} options.force sort-remote 开启后是否强制排序
684
- * @param {boolean} options.emit 是否触发回调
691
+ * @param click 是否为点击表头触发
692
+ * @param options.force sort-remote 开启后是否强制排序
693
+ * @param options.emit 是否触发回调
685
694
  */
686
- function onColumnSort(col?: StkTableColumn<any>, click = true, options: { force?: boolean; emit?: boolean } = {}) {
695
+ function onColumnSort(col?: StkTableColumn<DT>, click = true, options: { force?: boolean; emit?: boolean } = {}) {
687
696
  if (!col?.sorter) return;
688
697
  options = { force: false, emit: false, ...options };
689
698
  if (sortCol.value !== col.dataIndex) {
@@ -694,8 +703,16 @@ function onColumnSort(col?: StkTableColumn<any>, click = true, options: { force?
694
703
  if (click) sortOrderIndex.value++;
695
704
  sortOrderIndex.value = sortOrderIndex.value % 3;
696
705
 
697
- const order = sortSwitchOrder[sortOrderIndex.value];
706
+ let order = sortSwitchOrder[sortOrderIndex.value];
698
707
  const sortConfig = props.sortConfig;
708
+ const defaultSort = sortConfig.defaultSort;
709
+
710
+ if (!order && defaultSort) {
711
+ // 没有排序时变成默认排序
712
+ order = defaultSort.order;
713
+ sortOrderIndex.value = sortSwitchOrder.indexOf(order);
714
+ sortCol.value = defaultSort.dataIndex as string;
715
+ }
699
716
  if (!props.sortRemote || options.force) {
700
717
  dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
701
718
  }
@@ -813,16 +830,16 @@ function setCurrentRow(rowKey: string, option = { silent: false }) {
813
830
 
814
831
  /**
815
832
  * 设置表头排序状态
816
- * @param {string} dataIndex 列字段
817
- * @param {'asc'|'desc'|null} order
818
- * @param {object} option.sortOption 指定排序参数
819
- * @param {boolean} option.sort 是否触发排序
820
- * @param {boolean} option.silent 是否触发回调
833
+ * @param dataIndex 列字段
834
+ * @param order 正序倒序
835
+ * @param option.sortOption 指定排序参数
836
+ * @param option.sort 是否触发排序-默认true
837
+ * @param option.silent 是否禁止触发回调-默认true
821
838
  */
822
- function setSorter(dataIndex: string, order: null | 'asc' | 'desc', option: { sortOption?: SortOption; silent?: boolean; sort?: boolean } = {}) {
839
+ function setSorter(dataIndex: string, order: Order, option: { sortOption?: SortOption<DT>; silent?: boolean; sort?: boolean } = {}) {
823
840
  const newOption = { silent: true, sortOption: null, sort: true, ...option };
824
841
  sortCol.value = dataIndex;
825
- sortOrderIndex.value = sortSwitchOrder.findIndex(it => it === order);
842
+ sortOrderIndex.value = sortSwitchOrder.indexOf(order);
826
843
 
827
844
  if (newOption.sort && dataSourceCopy.value?.length) {
828
845
  // 如果表格有数据,则进行排序
@@ -59,18 +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<any>, 'sorter' | 'dataIndex' | 'sortField' | 'sortType'>;
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
- export type UniqKey = string | ((param: any) => string);
71
+ /** 唯一键 */
72
+ export type UniqKey = string | number;
73
+ export type UniqKeyFun = (param: any) => UniqKey;
74
+ export type UniqKeyProp = UniqKey | UniqKeyFun;
71
75
 
72
76
  /** 排序配置 */
73
- export type SortConfig = {
77
+ export type SortConfig<T extends Record<string, any>> = {
74
78
  /** 空值始终排在列表末尾 */
75
79
  emptyToBottom?: boolean;
80
+ /** 默认排序(1.初始化时触发 2.排序方向为null时触发) */
81
+ defaultSort?: {
82
+ dataIndex: keyof T;
83
+ order: Order;
84
+ };
76
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, rowKeyGen }: Params) {
15
- const highlightInter = computed(() => {
16
- return interpolateRgb(Highlight_Color[props.theme].from, Highlight_Color[props.theme].to);
17
- });
18
- /** 存放高亮行的对象*/
19
- const highlightDimRows = new Set<any>();
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
- highlightDimRows.forEach(row => {
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 - row._bgc_progress_ms) / Highlight_Duration;
53
- // row._bgc_progress = progress;
70
+ const progress = (nowTs - highlightItem.bgc_progress_ms) / Highlight_Duration;
54
71
  if (0 < progress && progress < 1) {
55
- row._bgc = highlightInter.value(progress);
72
+ highlightItem.bgc = highlightInter.value(progress);
56
73
  } else {
57
- row._bgc = ''; // 清空颜色
58
- needDeleteRows.push(row);
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 (highlightDimRows.size > 0) {
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('highlight-cell')) {
82
- cellEl.classList.remove('highlight-cell');
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('highlight-cell');
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('highlight-cell');
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: Array<string | number>) {
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
- const row = props.dataSource.find((it: any) => rowKeyGen(it) === rowKeyValue);
108
- if (!row) continue;
109
- row._bgc_progress_ms = nowTs;
110
- // row._bgc_progress = 0;
111
- highlightDimRows.add(row);
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('highlight-row')) {
125
- rowEl.classList.remove('highlight-row');
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('highlight-row');
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('highlight-row')); // 统一添加动画
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
  };
@@ -67,12 +67,12 @@ function strCompare(a: string, b: string, type: 'number' | 'string'): number {
67
67
  * 分离出空数据和非空数据成两个数组
68
68
  * @param sortOption
69
69
  * @param targetDataSource
70
- * @param isNumber 1 数组
70
+ * @param isNumber 1 数字
71
71
  * @return [值数组,空数组]
72
72
  */
73
- function separatedData(sortOption: SortOption, targetDataSource: any[], isNumber?: boolean) {
74
- const emptyArr: any[] = [];
75
- const valueArr: any[] = [];
73
+ function separatedData<T extends Record<string, any>>(sortOption: SortOption<T>, targetDataSource: T[], isNumber?: boolean) {
74
+ const emptyArr: T[] = [];
75
+ const valueArr: T[] = [];
76
76
 
77
77
  for (let i = 0; i < targetDataSource.length; i++) {
78
78
  const row = targetDataSource[i];
@@ -96,20 +96,33 @@ function separatedData(sortOption: SortOption, targetDataSource: any[], isNumber
96
96
  * 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
97
97
  * 使用者在@sort-change事件中自行更改table props 'dataSource'完成排序。
98
98
  * TODO: key 唯一值,排序字段相同时,根据唯一值排序。
99
+ *
100
+ * sortConfig.defaultSort 会在order为null时生效
99
101
  * @param sortOption 列配置
100
102
  * @param order 排序方式
101
103
  * @param dataSource 排序的数组
102
104
  */
103
- export function tableSort(sortOption: SortOption, order: Order, dataSource: any[], sortConfig: SortConfig = {}): any[] {
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[] {
104
111
  if (!dataSource?.length) return dataSource || [];
105
- sortConfig = Object.assign({ emptyToBottom: false } as SortConfig, sortConfig);
112
+ sortConfig = { emptyToBottom: false, ...sortConfig };
106
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
+ }
107
121
 
108
122
  if (typeof sortOption.sorter === 'function') {
109
123
  const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
110
124
  if (customSorterData) targetDataSource = customSorterData;
111
125
  } else if (order) {
112
- const sortField = sortOption.sortField || sortOption.dataIndex;
113
126
  let { sortType } = sortOption;
114
127
  if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
115
128