stk-table-vue 0.2.0 → 0.2.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/style.css CHANGED
@@ -1,4 +1,4 @@
1
- @keyframes stkTableDim{
1
+ @keyframes stk-table-dim{
2
2
  from{
3
3
  background-color:var(--highlight-color);
4
4
  }
@@ -32,8 +32,11 @@
32
32
  overflow:auto;
33
33
  display:flex;
34
34
  flex-direction:column;
35
+ box-sizing:border-box;
35
36
  border-left:1px solid #e8e8f4;
36
37
  border-left:1px solid var(--border-color);
38
+ background-image:linear-gradient(180deg, #e8e8f4 1px, transparent 1px), linear-gradient(270deg, #e8e8f4 1px, transparent 1px), linear-gradient(0deg, #e8e8f4 1px, transparent 1px);
39
+ background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
37
40
  }
38
41
  .stk-table.dark{
39
42
  --th-bgc:#202029;
@@ -56,6 +59,7 @@
56
59
  }
57
60
  .stk-table.headless{
58
61
  border-top:1px solid var(--border-color);
62
+ background-image:var(--bg-border-right), var(--bg-border-bottom);
59
63
  }
60
64
  .stk-table.col-resizable .stk-table-main{
61
65
  width:-moz-fit-content !important;
@@ -144,7 +148,7 @@
144
148
  background-color:inherit;
145
149
  }
146
150
  .stk-table .stk-table-main td.highlight-cell{
147
- animation:stkTableDim 2s linear;
151
+ animation:stk-table-dim 2s linear;
148
152
  }
149
153
  .stk-table .stk-table-main td.text-overflow .table-cell-wrapper{
150
154
  white-space:nowrap;
@@ -243,7 +247,7 @@
243
247
  height:var(--row-height);
244
248
  }
245
249
  .stk-table .stk-table-main tbody tr.highlight-row{
246
- animation:stkTableDim 2s linear;
250
+ animation:stk-table-dim 2s linear;
247
251
  }
248
252
  .stk-table .stk-table-main tbody tr.hover,
249
253
  .stk-table .stk-table-main tbody tr:hover{
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.2.0",
4
- "description": "simple realtime virtual table for vue3",
3
+ "version": "0.2.2",
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",
7
7
  "packageManager": "pnpm@8.14.3",
@@ -57,7 +57,7 @@
57
57
  v-for="(col, colIndex) in virtualX_on && rowIndex === tableHeaders.length - 1 ? virtualX_columnPart : row"
58
58
  :key="col.dataIndex"
59
59
  :data-col-key="colKeyGen(col)"
60
- :draggable="headerDrag ? 'true' : 'false'"
60
+ :draggable="isHeaderDraggable(col) ? 'true' : 'false'"
61
61
  :rowspan="virtualX_on ? 1 : col.rowSpan"
62
62
  :colspan="col.colSpan"
63
63
  :style="getCellStyle(1, col, rowIndex)"
@@ -180,7 +180,7 @@
180
180
  </div>
181
181
  </template>
182
182
 
183
- <script setup lang="tsx">
183
+ <script setup lang="ts">
184
184
  /**
185
185
  * @author JA+
186
186
  * 不支持低版本浏览器非虚拟滚动表格的表头固定,列固定,因为会卡。
@@ -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, StkTableColumn, UniqKey } from './types/index';
194
+ import { Order, SortConfig, SortOption, SortState, StkTableColumn, UniqKey } from './types/index';
195
195
  import { useAutoResize } from './useAutoResize';
196
196
  import { useColResize } from './useColResize';
197
197
  import { useFixedCol } from './useFixedCol';
@@ -252,8 +252,8 @@ const props = withDefaults(
252
252
  showOverflow?: boolean;
253
253
  /** 是否增加行hover class */
254
254
  showTrHoverClass?: boolean;
255
- /** 表头是否可拖动 */
256
- headerDrag?: boolean;
255
+ /** 表头是否可拖动。支持回调函数。 */
256
+ headerDrag?: boolean | ((col: StkTableColumn<DT>) => boolean);
257
257
  /**
258
258
  * 给行附加className<br>
259
259
  * FIXME: 是否需要优化,因为不传此prop会使表格行一直执行空函数,是否有影响
@@ -285,6 +285,8 @@ const props = withDefaults(
285
285
  fixedColShadow?: boolean;
286
286
  /** 优化vue2 滚动 */
287
287
  optimizeVue2Scroll?: boolean;
288
+ /** 排序配置 */
289
+ sortConfig?: SortConfig;
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): void;
329
334
  /**
330
335
  * 一行点击事件
331
336
  * ```(ev: MouseEvent, row: DT)```
@@ -449,7 +454,7 @@ const { isColResizing, onThResizeMouseDown } = useColResize({
449
454
  tableHeaderLast,
450
455
  });
451
456
 
452
- const { onThDragStart, onThDragOver, onThDrop } = useThDrag({ emits });
457
+ const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits });
453
458
 
454
459
  const {
455
460
  virtualScroll,
@@ -482,7 +487,7 @@ const { getFixedStyle } = useFixedStyle({
482
487
  const { setHighlightDimCell, setHighlightDimRow } = useHighlight({ props, tableContainer, rowKeyGen });
483
488
 
484
489
  if (props.autoResize) {
485
- useAutoResize({ tableContainer, initVirtualScroll, scrollTo, props, debounceMs: 500 });
490
+ useAutoResize({ tableContainer, initVirtualScroll, props, debounceMs: 200 });
486
491
  }
487
492
 
488
493
  /** 键盘箭头滚动 */
@@ -627,7 +632,7 @@ function rowKeyGen(row: DT) {
627
632
  * 列唯一键
628
633
  * @param col
629
634
  */
630
- function colKeyGen(col: StkTableColumn<any>) {
635
+ function colKeyGen(col: StkTableColumn<DT>) {
631
636
  return typeof props.colKey === 'function' ? props.colKey(col) : (col as any)[props.colKey];
632
637
  }
633
638
 
@@ -690,13 +695,13 @@ function onColumnSort(col?: StkTableColumn<any>, click = true, options: { force?
690
695
  sortOrderIndex.value = sortOrderIndex.value % 3;
691
696
 
692
697
  const order = sortSwitchOrder[sortOrderIndex.value];
693
-
698
+ const sortConfig = props.sortConfig;
694
699
  if (!props.sortRemote || options.force) {
695
- dataSourceCopy.value = tableSort(col, order, props.dataSource);
700
+ dataSourceCopy.value = tableSort(col, order, props.dataSource, sortConfig);
696
701
  }
697
702
  // 只有点击才触发事件
698
703
  if (click || options.emit) {
699
- emits('sort-change', col, order, toRaw(dataSourceCopy.value));
704
+ emits('sort-change', col, order, toRaw(dataSourceCopy.value), sortConfig);
700
705
  }
701
706
  }
702
707
 
@@ -769,6 +774,9 @@ function onTableScroll(e: Event) {
769
774
  updateFixedShadow();
770
775
  if (virtualX_on.value) {
771
776
  updateVirtualScrollX(scrollLeft);
777
+ } else {
778
+ // 非虚拟滚动也记录一下滚动条位置。用于判断isXScroll
779
+ virtualScrollX.value.scrollLeft = scrollLeft;
772
780
  }
773
781
  }
774
782
 
@@ -848,6 +856,13 @@ function getTableData() {
848
856
  return toRaw(dataSourceCopy.value);
849
857
  }
850
858
 
859
+ /** 获取当前排序列的信息 */
860
+ function getSortColumns(): SortState<DT>[] {
861
+ const sortOrder = sortSwitchOrder[sortOrderIndex.value];
862
+ if (!sortOrder) return [];
863
+ return [{ dataIndex: sortCol.value, order: sortOrder }];
864
+ }
865
+
851
866
  defineExpose({
852
867
  /** 初始化横向纵向虚拟滚动 */
853
868
  initVirtualScroll,
@@ -863,6 +878,8 @@ defineExpose({
863
878
  setHighlightDimRow,
864
879
  /** 表格排序列dataIndex */
865
880
  sortCol,
881
+ /** 获取当前排序状态 */
882
+ getSortColumns,
866
883
  /** 设置排序 */
867
884
  setSorter,
868
885
  /** 重置排序 */
@@ -1,5 +1,5 @@
1
1
  /**高亮渐暗 */
2
- @keyframes stkTableDim {
2
+ @keyframes stk-table-dim {
3
3
  from {
4
4
  background-color: var(--highlight-color);
5
5
  }
@@ -41,9 +41,11 @@
41
41
  overflow: auto;
42
42
  display: flex;
43
43
  flex-direction: column;
44
+ box-sizing: border-box;
44
45
  /* border-left: 此方案用于减少cell 中border-left 的css选择。同时利于多级表头border-left问题。利于横向滚动border-left*/
45
46
  border-left: 1px solid var(--border-color);
46
-
47
+ /* 下面border用于表格内容不满高度时,绘制表格边界线 */
48
+ background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
47
49
  /**深色模式 */
48
50
  &.dark {
49
51
  --th-bgc: #202029;
@@ -75,6 +77,7 @@
75
77
 
76
78
  &.headless {
77
79
  border-top: 1px solid var(--border-color);
80
+ background-image: var(--bg-border-right), var(--bg-border-bottom);
78
81
  }
79
82
 
80
83
  /* 调整列宽的话,表格宽度应当自适应 */
@@ -112,7 +115,6 @@
112
115
  tr {
113
116
  &:first-child th {
114
117
  background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
115
-
116
118
  }
117
119
 
118
120
  }
@@ -209,7 +211,7 @@
209
211
  }
210
212
 
211
213
  &.highlight-cell {
212
- animation: stkTableDim 2s linear;
214
+ animation: stk-table-dim 2s linear;
213
215
  }
214
216
 
215
217
  &.text-overflow {
@@ -349,7 +351,7 @@
349
351
 
350
352
  /* td inherit tr bgc*/
351
353
  &.highlight-row {
352
- animation: stkTableDim 2s linear;
354
+ animation: stk-table-dim 2s linear;
353
355
  }
354
356
 
355
357
  /* &.highlight-row-transition {
@@ -21,11 +21,11 @@ export type StkTableColumn<T extends Record<string, any>> = {
21
21
  /** 筛选 */
22
22
  sorter?: Sorter<T>;
23
23
  /** 列宽。横向虚拟滚动时必须设置。 */
24
- width?: string;
24
+ width?: string | number;
25
25
  /** 最小列宽。非x虚拟滚动生效。 */
26
- minWidth?: string;
26
+ minWidth?: string | number;
27
27
  /** 最大列宽。非x虚拟滚动生效。 */
28
- maxWidth?: string;
28
+ maxWidth?: string | number;
29
29
  /**th class */
30
30
  headerClassName?: string;
31
31
  /** td class */
@@ -68,3 +68,9 @@ export type SortState<T> = {
68
68
  };
69
69
 
70
70
  export type UniqKey = string | ((param: any) => string);
71
+
72
+ /** 排序配置 */
73
+ export type SortConfig = {
74
+ /** 空值始终排在列表末尾 */
75
+ emptyToBottom?: boolean;
76
+ };
@@ -1,10 +1,9 @@
1
1
  import { Ref, onBeforeUnmount, onMounted, watch } from 'vue';
2
2
 
3
3
  type Options = {
4
+ props: any;
4
5
  tableContainer: Ref<HTMLElement | undefined>;
5
6
  initVirtualScroll: () => void;
6
- scrollTo: () => void;
7
- props: any;
8
7
  /** 防抖延时 */
9
8
  debounceMs: number;
10
9
  };
@@ -12,7 +11,7 @@ type Options = {
12
11
  * 窗口变化自动重置虚拟滚动
13
12
  * @param param0
14
13
  */
15
- export function useAutoResize({ tableContainer, initVirtualScroll, scrollTo, props, debounceMs }: Options) {
14
+ export function useAutoResize({ tableContainer, initVirtualScroll, props, debounceMs }: Options) {
16
15
  let resizeObserver: ResizeObserver | null = null;
17
16
 
18
17
  onMounted(() => {
@@ -1,6 +1,6 @@
1
1
  import { Ref, onBeforeUnmount, onMounted, ref } from 'vue';
2
- import { Default_Col_Width } from './const';
3
2
  import { StkTableColumn } from './types';
3
+ import { getColWidth } from './utils';
4
4
 
5
5
  type ColResizeState<DT extends Record<string, any>> = {
6
6
  /** 当前被拖动的列*/
@@ -115,7 +115,7 @@ export function useColResize<DT extends Record<string, any>>({
115
115
  const { lastCol, startX, startOffsetTableX } = colResizeState;
116
116
  const { clientX } = e;
117
117
  let moveX = clientX - startX;
118
- const currentColWidth = parseInt(lastCol?.width || Default_Col_Width);
118
+ const currentColWidth = getColWidth(lastCol);
119
119
  // 移动量不小于最小列宽
120
120
  if (currentColWidth + moveX < props.colMinWidth) {
121
121
  moveX = -currentColWidth;
@@ -136,7 +136,7 @@ export function useColResize<DT extends Record<string, any>>({
136
136
  const moveX = clientX - startX;
137
137
 
138
138
  // 移动量不小于最小列宽
139
- let width = parseInt(lastCol?.width || Default_Col_Width) + moveX;
139
+ let width = getColWidth(lastCol) + moveX;
140
140
  if (width < props.colMinWidth) width = props.colMinWidth;
141
141
 
142
142
  const curCol = tableHeaderLast.value.find(it => colKeyGen(it) === colKeyGen(lastCol));
@@ -31,7 +31,7 @@ export function useFixedCol<DT extends Record<string, any>>({ props, tableHeader
31
31
  fixedShadowCols = [];
32
32
  // 找到最右边的固定列 findLast
33
33
  let lastLeftCol = null;
34
- for (let i = tableHeaderLast.value.length - 1; i > 0; i--) {
34
+ for (let i = tableHeaderLast.value.length - 1; i >= 0; i--) {
35
35
  const col = tableHeaderLast.value[i];
36
36
  if (col.fixed === 'left') {
37
37
  lastLeftCol = col;
@@ -1,7 +1,8 @@
1
1
  import { CSSProperties, Ref, computed } from 'vue';
2
- import { Default_Col_Width, Is_Legacy_Mode } from './const';
2
+ import { Is_Legacy_Mode } from './const';
3
3
  import { StkTableColumn } from './types';
4
4
  import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
5
+ import { getColWidth } from './utils';
5
6
 
6
7
  type Options = {
7
8
  props: any;
@@ -27,7 +28,7 @@ export function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualSc
27
28
  const item = cols[i];
28
29
  if (item.fixed === 'left') {
29
30
  store[item.dataIndex] = left;
30
- left += parseInt(item.width || Default_Col_Width);
31
+ left += getColWidth(item);
31
32
  }
32
33
  if (!rightStartIndex && item.fixed === 'right') {
33
34
  rightStartIndex = i;
@@ -38,7 +39,7 @@ export function useFixedStyle({ props, tableHeaderLast, virtualScroll, virtualSc
38
39
  const item = cols[i];
39
40
  if (item.fixed === 'right') {
40
41
  store[item.dataIndex] = right;
41
- right += parseInt(item.width || Default_Col_Width);
42
+ right += getColWidth(item);
42
43
  }
43
44
  }
44
45
 
@@ -47,6 +47,7 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
47
47
  // void rowEl.offsetHeight; // reflow
48
48
  // rowEl.classList.add('highlight-row-transition');
49
49
  // }
50
+
50
51
  /** 经过的时间 ÷ 高亮持续时间 计算出 颜色过渡进度 (0-1) */
51
52
  const progress = (nowTs - row._bgc_progress_ms) / Highlight_Duration;
52
53
  // row._bgc_progress = progress;
@@ -58,6 +59,7 @@ export function useHighlight({ props, tableContainer, rowKeyGen }: Params) {
58
59
  }
59
60
  });
60
61
  needDeleteRows.forEach((row: any) => highlightDimRows.delete(row));
62
+ // TODO: shallowRef 时,需要手动更新
61
63
 
62
64
  if (highlightDimRows.size > 0) {
63
65
  // 还有高亮的行,则下一次循环
@@ -1,4 +1,7 @@
1
+ import { StkTableColumn } from './types';
2
+
1
3
  type Params = {
4
+ props: any;
2
5
  emits: any;
3
6
  };
4
7
  /**
@@ -6,28 +9,43 @@ type Params = {
6
9
  * @param param0
7
10
  * @returns
8
11
  */
9
- export function useThDrag({ emits }: Params) {
12
+ export function useThDrag<DT extends Record<string, any>>({ props, emits }: Params) {
10
13
  let dragStartKey: string | undefined = void 0;
11
14
 
15
+ function findParentTH(el: HTMLElement | Node) {
16
+ let n: any = el;
17
+ while (n) {
18
+ if (n.tagName === 'TH') return n;
19
+ n = n.parentElement;
20
+ }
21
+ }
12
22
  /** 开始拖动记录th位置 */
13
23
  function onThDragStart(e: MouseEvent) {
14
24
  // const i = Array.prototype.indexOf.call(e.target.parentNode.children, e.target); // 得到是第几个子元素
15
- dragStartKey = (e.target as HTMLElement).dataset.colKey;
25
+ const th = findParentTH(e.target as HTMLElement | Node);
26
+ if (!th) return;
27
+
28
+ dragStartKey = th.dataset.colKey;
16
29
  emits('th-drag-start', dragStartKey);
17
30
  }
18
31
 
19
32
  function onThDragOver(e: MouseEvent) {
33
+ const th = findParentTH(e.target as HTMLElement | Node);
34
+ if (!th) return;
35
+
36
+ const isHeaderDraggable = th.getAttribute('draggable') === 'true';
37
+ if (!isHeaderDraggable) {
38
+ // 不可drag的表头不可被覆盖
39
+ return;
40
+ }
20
41
  e.preventDefault();
21
42
  }
22
43
 
23
44
  /** th拖动释放时 */
24
45
  function onThDrop(e: MouseEvent) {
25
- let th = e.target as HTMLElement;
26
- // 找到th元素
27
- while (th) {
28
- if (th.tagName === 'TH') break;
29
- th = th.parentNode as HTMLElement;
30
- }
46
+ const th = findParentTH(e.target as HTMLElement | Node);
47
+ if (!th) return;
48
+
31
49
  // const i = Array.prototype.indexOf.call(th.parentNode.children, th); // 得到是第几个子元素
32
50
  if (dragStartKey !== th.dataset.colKey) {
33
51
  emits('col-order-change', dragStartKey, th.dataset.colKey);
@@ -35,9 +53,20 @@ export function useThDrag({ emits }: Params) {
35
53
  emits('th-drop', th.dataset.colKey);
36
54
  }
37
55
 
56
+ const isHeaderDragFun = typeof props.headerDrag === 'function';
57
+ /** 是否可拖拽 */
58
+ function isHeaderDraggable(col: StkTableColumn<DT>) {
59
+ if (isHeaderDragFun) {
60
+ return props.headerDrag(col);
61
+ } else {
62
+ return props.headerDrag;
63
+ }
64
+ }
65
+
38
66
  return {
39
67
  onThDragStart,
40
68
  onThDragOver,
41
69
  onThDrop,
70
+ isHeaderDraggable,
42
71
  };
43
72
  }
@@ -1,6 +1,7 @@
1
1
  import { Ref, ShallowRef, computed, ref } from 'vue';
2
- import { Default_Col_Width, Default_Table_Height, Default_Table_Width } from './const';
2
+ import { Default_Table_Height, Default_Table_Width } from './const';
3
3
  import { StkTableColumn } from './types';
4
+ import { getColWidth } from './utils';
4
5
 
5
6
  type Option<DT extends Record<string, any>> = {
6
7
  tableContainer: Ref<HTMLElement | undefined>;
@@ -40,11 +41,6 @@ export type VirtualScrollXStore = {
40
41
  scrollLeft: number;
41
42
  };
42
43
 
43
- /**获取计算宽度 */
44
- function getCalcWidth<DT extends Record<string, any>>(col: StkTableColumn<DT>) {
45
- return parseInt(col.minWidth || col.width || Default_Col_Width);
46
- }
47
-
48
44
  /** vue2 优化滚动回收延时 */
49
45
  const VUE2_SCROLL_TIMEOUT_MS = 200;
50
46
 
@@ -90,9 +86,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
90
86
  });
91
87
 
92
88
  const virtualX_on = computed(() => {
93
- return (
94
- props.virtualX && tableHeaderLast.value.reduce((sum, col) => (sum += getCalcWidth(col)), 0) > virtualScrollX.value.containerWidth + 100
95
- );
89
+ return props.virtualX && tableHeaderLast.value.reduce((sum, col) => (sum += getColWidth(col)), 0) > virtualScrollX.value.containerWidth + 100;
96
90
  });
97
91
 
98
92
  const virtualX_columnPart = computed(() => {
@@ -125,7 +119,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
125
119
  for (let i = virtualScrollX.value.endIndex; i < tableHeaderLast.value.length; i++) {
126
120
  const col = tableHeaderLast.value[i];
127
121
  if (col.fixed !== 'right') {
128
- width += getCalcWidth(col);
122
+ width += getColWidth(col);
129
123
  }
130
124
  }
131
125
  return width;
@@ -219,7 +213,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
219
213
  const col = tableHeaderLast.value[colIndex];
220
214
  // fixed left 不进入计算列宽
221
215
  if (col.fixed === 'left') continue;
222
- const colWidth = getCalcWidth(col);
216
+ const colWidth = getColWidth(col);
223
217
  colWidthSum += colWidth;
224
218
  // 列宽(非固定列)加到超过scrollLeft的时候,表示startIndex从上一个开始下标
225
219
  if (colWidthSum >= sLeft) {
@@ -233,7 +227,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({ props, tableC
233
227
  let endIndex = headerLength;
234
228
  for (let colIndex = startIndex + 1; colIndex < headerLength; colIndex++) {
235
229
  const col = tableHeaderLast.value[colIndex];
236
- colWidthSum += getCalcWidth(col);
230
+ colWidthSum += getColWidth(col);
237
231
  // 列宽大于容器宽度则停止
238
232
  if (colWidthSum >= virtualScrollX.value.containerWidth) {
239
233
  endIndex = colIndex + 1; // TODO:预渲染的列数
@@ -1,4 +1,5 @@
1
- import { Order, SortOption, SortState, StkTableColumn } from './types';
1
+ import { Default_Col_Width } from './const';
2
+ import { Order, SortConfig, SortOption, SortState, StkTableColumn } from './types';
2
3
 
3
4
  /**
4
5
  * 对有序数组插入新数据
@@ -62,6 +63,34 @@ function strCompare(a: string, b: string, type: 'number' | 'string'): number {
62
63
  }
63
64
  }
64
65
 
66
+ /**
67
+ * 分离出空数据和非空数据成两个数组
68
+ * @param sortOption
69
+ * @param targetDataSource
70
+ * @param isNumber 1 数组
71
+ * @return [值数组,空数组]
72
+ */
73
+ function separatedData(sortOption: SortOption, targetDataSource: any[], isNumber?: boolean) {
74
+ const emptyArr: any[] = [];
75
+ const valueArr: any[] = [];
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
+
65
94
  /**
66
95
  * 表格排序抽离
67
96
  * 可以在组件外部自己实现表格排序,组件配置remote,使表格不排序。
@@ -71,9 +100,11 @@ function strCompare(a: string, b: string, type: 'number' | 'string'): number {
71
100
  * @param order 排序方式
72
101
  * @param dataSource 排序的数组
73
102
  */
74
- export function tableSort(sortOption: SortOption, order: Order, dataSource: any[]): any[] {
103
+ export function tableSort(sortOption: SortOption, order: Order, dataSource: any[], sortConfig: SortConfig = {}): any[] {
75
104
  if (!dataSource?.length) return dataSource || [];
105
+ sortConfig = Object.assign({ emptyToBottom: false } as SortConfig, sortConfig);
76
106
  let targetDataSource = [...dataSource];
107
+
77
108
  if (typeof sortOption.sorter === 'function') {
78
109
  const customSorterData = sortOption.sorter(targetDataSource, { order, column: sortOption });
79
110
  if (customSorterData) targetDataSource = customSorterData;
@@ -82,36 +113,32 @@ export function tableSort(sortOption: SortOption, order: Order, dataSource: any[
82
113
  let { sortType } = sortOption;
83
114
  if (!sortType) sortType = typeof dataSource[0][sortField] as 'number' | 'string';
84
115
 
116
+ const [valueArr, emptyArr] = separatedData(sortOption, targetDataSource, sortType === 'number');
117
+
85
118
  if (sortType === 'number') {
86
119
  // 按数字类型排序
87
- const nanArr: any[] = []; // 非数字
88
- const numArr: any[] = []; // 数字
89
-
90
- for (let i = 0; i < targetDataSource.length; i++) {
91
- const row = targetDataSource[i];
92
- if (row[sortField] === null || row[sortField] === '' || typeof row[sortField] === 'boolean' || Number.isNaN(+row[sortField])) {
93
- nanArr.push(row);
94
- } else {
95
- numArr.push(row);
96
- }
97
- }
98
120
  // 非数字当作最小值处理
99
121
  if (order === 'asc') {
100
- numArr.sort((a, b) => +a[sortField] - +b[sortField]);
101
- targetDataSource = [...nanArr, ...numArr];
122
+ valueArr.sort((a, b) => +a[sortField] - +b[sortField]);
123
+ targetDataSource = [...emptyArr, ...valueArr];
102
124
  } else {
103
- numArr.sort((a, b) => +b[sortField] - +a[sortField]);
104
- targetDataSource = [...numArr, ...nanArr];
125
+ valueArr.sort((a, b) => +b[sortField] - +a[sortField]);
126
+ targetDataSource = [...valueArr, ...emptyArr];
105
127
  }
106
- // targetDataSource = [...numArr, ...nanArr]; // 非数字不进入排序,一直排在最后
107
128
  } else {
108
129
  // 按string 排序
109
130
  if (order === 'asc') {
110
- targetDataSource.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]));
131
+ valueArr.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]));
132
+ targetDataSource = [...emptyArr, ...valueArr];
111
133
  } else {
112
- targetDataSource.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]) * -1);
134
+ valueArr.sort((a, b) => String(a[sortField]).localeCompare(b[sortField]) * -1);
135
+ targetDataSource = [...valueArr, ...emptyArr];
113
136
  }
114
137
  }
138
+
139
+ if (sortConfig.emptyToBottom) {
140
+ targetDataSource = [...valueArr, ...emptyArr];
141
+ }
115
142
  }
116
143
  return targetDataSource;
117
144
  }
@@ -126,3 +153,11 @@ export function howDeepTheHeader(arr: StkTableColumn<any>[], level = 1) {
126
153
  });
127
154
  return Math.max(...levels);
128
155
  }
156
+
157
+ /** 获取列宽 */
158
+ export function getColWidth(col: StkTableColumn<any> | null): number {
159
+ if (typeof col?.width === 'number') {
160
+ return Math.floor(col.width ?? Default_Col_Width);
161
+ }
162
+ return parseInt(col?.width ?? Default_Col_Width);
163
+ }