stk-table-vue 0.11.0-beta.4 → 0.11.0

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.
@@ -107,6 +107,8 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
107
107
  rowCurrentRevokable?: boolean;
108
108
  /** 表头行高。default = rowHeight */
109
109
  headerRowHeight?: number | string | null;
110
+ /** 表尾行高。default = rowHeight */
111
+ footerRowHeight?: number | string | null;
110
112
  /** 虚拟滚动 */
111
113
  virtual?: boolean;
112
114
  /** x轴虚拟滚动(必须设置列宽)*/
@@ -224,6 +226,8 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
224
226
  * 实验性功能配置
225
227
  */
226
228
  experimental?: ExperimentalConfig;
229
+ /** 表格底部合计行数据 */
230
+ footerData?: DT[];
227
231
  }>, {
228
232
  width: string;
229
233
  fixedMode: boolean;
@@ -234,10 +238,12 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
234
238
  theme: string;
235
239
  rowHeight: number;
236
240
  autoRowHeight: () => false;
241
+ footerData: () => never[];
237
242
  rowHover: boolean;
238
243
  rowActive: () => Required<RowActiveOption<any>>;
239
244
  rowCurrentRevokable: boolean;
240
245
  headerRowHeight: number;
246
+ footerRowHeight: number;
241
247
  virtual: boolean;
242
248
  virtualX: boolean;
243
249
  columns: () => never[];
@@ -533,6 +539,8 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
533
539
  rowCurrentRevokable?: boolean;
534
540
  /** 表头行高。default = rowHeight */
535
541
  headerRowHeight?: number | string | null;
542
+ /** 表尾行高。default = rowHeight */
543
+ footerRowHeight?: number | string | null;
536
544
  /** 虚拟滚动 */
537
545
  virtual?: boolean;
538
546
  /** x轴虚拟滚动(必须设置列宽)*/
@@ -650,6 +658,8 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
650
658
  * 实验性功能配置
651
659
  */
652
660
  experimental?: ExperimentalConfig;
661
+ /** 表格底部合计行数据 */
662
+ footerData?: DT[];
653
663
  }>, {
654
664
  width: string;
655
665
  fixedMode: boolean;
@@ -660,10 +670,12 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
660
670
  theme: string;
661
671
  rowHeight: number;
662
672
  autoRowHeight: () => false;
673
+ footerData: () => never[];
663
674
  rowHover: boolean;
664
675
  rowActive: () => Required<RowActiveOption<any>>;
665
676
  rowCurrentRevokable: boolean;
666
677
  headerRowHeight: number;
678
+ footerRowHeight: number;
667
679
  virtual: boolean;
668
680
  virtualX: boolean;
669
681
  columns: () => never[];
@@ -780,6 +792,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
780
792
  rowHover: boolean;
781
793
  rowActive: boolean | RowActiveOption<DT>;
782
794
  rowCurrentRevokable: boolean;
795
+ footerRowHeight: number | string | null;
783
796
  virtual: boolean;
784
797
  virtualX: boolean;
785
798
  columns: StkTableColumn<DT>[];
@@ -815,6 +828,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
815
828
  smoothScroll: boolean;
816
829
  scrollRowByRow: boolean | "scrollbar";
817
830
  experimental: ExperimentalConfig;
831
+ footerData: DT[];
818
832
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
819
833
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
820
834
  export default _default;
@@ -27,6 +27,13 @@ export type CustomHeaderCellProps<T extends Record<string, any>> = {
27
27
  rowIndex: number;
28
28
  colIndex: number;
29
29
  };
30
+ export type CustomFooterCellProps<T extends Record<string, any>> = {
31
+ col: StkTableColumn<T>;
32
+ row: T;
33
+ cellValue: any;
34
+ rowIndex: number;
35
+ colIndex: number;
36
+ };
30
37
  export type MergeCellsParam<T extends Record<string, any>> = {
31
38
  row: T;
32
39
  col: StkTableColumn<T>;
@@ -111,6 +118,17 @@ export type StkTableColumn<T extends Record<string, any>> = {
111
118
  * @param props.colIndex 列索引
112
119
  */
113
120
  customHeaderCell?: CustomCell<CustomHeaderCellProps<T>, T>;
121
+ /**
122
+ * 自定义 tfoot td 渲染内容
123
+ *
124
+ * 组件prop入参:
125
+ * @param props.row tfoot行的记录。
126
+ * @param props.col 列配置
127
+ * @param props.cellValue row[col.dataIndex] 的值
128
+ * @param props.rowIndex tfoot行索引(从0开始)
129
+ * @param props.colIndex 列索引
130
+ */
131
+ customFooterCell?: CustomCell<CustomFooterCellProps<T>, T>;
114
132
  /** 二级表头 */
115
133
  children?: StkTableColumn<T>[];
116
134
  /** 单元格合并 */
@@ -220,7 +238,9 @@ export type SortConfig<T extends Record<string, any>> = {
220
238
  /** th td type */
221
239
  export declare const enum TagType {
222
240
  TH = 0,
223
- TD = 1
241
+ TD = 1,
242
+ /** tfoot */
243
+ TF = 2
224
244
  }
225
245
  export type HighlightConfig = {
226
246
  /** Duration of the highlight in seconds */
@@ -791,6 +791,7 @@ function registerFeature(feature) {
791
791
  var TagType = /* @__PURE__ */ ((TagType2) => {
792
792
  TagType2[TagType2["TH"] = 0] = "TH";
793
793
  TagType2[TagType2["TD"] = 1] = "TD";
794
+ TagType2[TagType2["TF"] = 2] = "TF";
794
795
  return TagType2;
795
796
  })(TagType || {});
796
797
  function useAutoResize(tableContainerRef, initVirtualScroll, props, debounceMs) {
@@ -1068,7 +1069,7 @@ function useFixedCol(props, colKeyGen, getFixedColPosition, tableHeaders, tableH
1068
1069
  function useFixedStyle(props, isRelativeMode, getFixedColPosition, virtualScroll, virtualScrollX, virtualX_on, virtualX_offsetRight) {
1069
1070
  function getFixedStyle(tagType, col, depth = 0) {
1070
1071
  const { fixed } = col;
1071
- if (tagType === TagType.TD && !fixed) return null;
1072
+ if ((tagType === TagType.TD || tagType === TagType.TF) && !fixed) return null;
1072
1073
  const style = {};
1073
1074
  const { headerRowHeight, rowHeight } = props;
1074
1075
  const isFixedLeft = fixed === "left";
@@ -1082,6 +1083,8 @@ function useFixedStyle(props, isRelativeMode, getFixedColPosition, virtualScroll
1082
1083
  } else {
1083
1084
  style.top = virtualScroll.value.scrollTop + "px";
1084
1085
  }
1086
+ } else if (tagType === TagType.TF) {
1087
+ style.bottom = "0px";
1085
1088
  }
1086
1089
  if (fixed) {
1087
1090
  if (!isRelativeMode.value) {
@@ -2430,25 +2433,36 @@ const _hoisted_4 = ["onClick"];
2430
2433
  const _hoisted_5 = ["onMousedown"];
2431
2434
  const _hoisted_6 = { class: "table-header-title" };
2432
2435
  const _hoisted_7 = ["onMousedown"];
2433
- const _hoisted_8 = {
2436
+ const _hoisted_8 = { key: 1 };
2437
+ const _hoisted_9 = {
2434
2438
  key: 0,
2435
2439
  class: "vt-x-left"
2436
2440
  };
2437
- const _hoisted_9 = ["onDrop"];
2438
- const _hoisted_10 = {
2441
+ const _hoisted_10 = ["title"];
2442
+ const _hoisted_11 = { key: 0 };
2443
+ const _hoisted_12 = {
2444
+ key: 1,
2445
+ class: "vt-x-right"
2446
+ };
2447
+ const _hoisted_13 = {
2448
+ key: 0,
2449
+ class: "vt-x-left"
2450
+ };
2451
+ const _hoisted_14 = ["onDrop"];
2452
+ const _hoisted_15 = {
2439
2453
  key: 0,
2440
2454
  class: "vt-x-left"
2441
2455
  };
2442
- const _hoisted_11 = ["colspan"];
2443
- const _hoisted_12 = { class: "table-cell-wrapper" };
2444
- const _hoisted_13 = ["title"];
2445
- const _hoisted_14 = {
2456
+ const _hoisted_16 = ["colspan"];
2457
+ const _hoisted_17 = { class: "table-cell-wrapper" };
2458
+ const _hoisted_18 = ["title"];
2459
+ const _hoisted_19 = {
2446
2460
  key: 2,
2447
2461
  class: "table-cell-wrapper"
2448
2462
  };
2449
- const _hoisted_15 = ["title"];
2450
- const _hoisted_16 = { key: 2 };
2451
- const _hoisted_17 = {
2463
+ const _hoisted_20 = ["title"];
2464
+ const _hoisted_21 = { key: 2 };
2465
+ const _hoisted_22 = {
2452
2466
  key: 3,
2453
2467
  class: "vt-x-right"
2454
2468
  };
@@ -2469,6 +2483,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2469
2483
  rowActive: { type: [Boolean, Object], default: () => DEFAULT_ROW_ACTIVE_CONFIG },
2470
2484
  rowCurrentRevokable: { type: Boolean, default: true },
2471
2485
  headerRowHeight: { default: DEFAULT_ROW_HEIGHT },
2486
+ footerRowHeight: { default: DEFAULT_ROW_HEIGHT },
2472
2487
  virtual: { type: Boolean, default: false },
2473
2488
  virtualX: { type: Boolean, default: false },
2474
2489
  columns: { default: () => [] },
@@ -2505,7 +2520,8 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2505
2520
  smoothScroll: { type: Boolean, default: DEFAULT_SMOOTH_SCROLL },
2506
2521
  scrollRowByRow: { type: [Boolean, String], default: false },
2507
2522
  scrollbar: { type: [Boolean, Object], default: false },
2508
- experimental: { default: () => ({}) }
2523
+ experimental: { default: () => ({}) },
2524
+ footerData: { default: () => [] }
2509
2525
  },
2510
2526
  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", "area-selection-change", "filter-change", "update:columns"],
2511
2527
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -2807,6 +2823,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2807
2823
  const cellStyleMap = computed(() => {
2808
2824
  const thMap = /* @__PURE__ */ new Map();
2809
2825
  const tdMap = /* @__PURE__ */ new Map();
2826
+ const tfMap = /* @__PURE__ */ new Map();
2810
2827
  const { virtualX } = props;
2811
2828
  const headers = tableHeaders.value;
2812
2829
  const colKeyGenValue = colKeyGen.value;
@@ -2824,11 +2841,13 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2824
2841
  const colKey = colKeyGenValue(col);
2825
2842
  thMap.set(colKey, Object.assign({}, style, getFixedStyle(TagType.TH, col, depth)));
2826
2843
  tdMap.set(colKey, Object.assign({}, style, getFixedStyle(TagType.TD, col, depth)));
2844
+ tfMap.set(colKey, Object.assign({ position: "sticky" }, style, getFixedStyle(TagType.TF, col, depth)));
2827
2845
  }
2828
2846
  }
2829
2847
  return {
2830
2848
  [TagType.TH]: thMap,
2831
- [TagType.TD]: tdMap
2849
+ [TagType.TD]: tdMap,
2850
+ [TagType.TF]: tfMap
2832
2851
  };
2833
2852
  });
2834
2853
  function getRowIndex(rowIndex) {
@@ -2879,7 +2898,20 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2879
2898
  colKey === sortCol.value && sortOrderIndex.value !== 0 && "sorter-" + sortSwitchOrder[sortOrderIndex.value],
2880
2899
  col.headerClassName,
2881
2900
  fixedColClassMap.value.get(colKey),
2882
- col.headerAlign && (col.headerAlign === "left" ? "text-l" : col.headerAlign === "right" ? "text-r" : null)
2901
+ col.headerAlign && (col.headerAlign === "left" ? "text-l" : col.headerAlign === "right" ? "text-r" : col.headerAlign === "center" ? "text-c" : null)
2902
+ ]
2903
+ };
2904
+ }
2905
+ function getTFProps(col) {
2906
+ const colKey = colKeyGen.value(col);
2907
+ return {
2908
+ "data-col-key": colKey,
2909
+ style: cellStyleMap.value[TagType.TF].get(colKey),
2910
+ class: [
2911
+ col.className,
2912
+ fixedColClassMap.value.get(colKey),
2913
+ col.type === "seq" ? "seq-column" : "",
2914
+ col.align === "center" ? "text-c" : col.align === "right" ? "text-r" : ""
2883
2915
  ]
2884
2916
  };
2885
2917
  }
@@ -3444,6 +3476,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3444
3476
  style: normalizeStyle({
3445
3477
  "--row-height": props.autoRowHeight ? void 0 : unref(virtualScroll).rowHeight + "px",
3446
3478
  "--header-row-height": props.headerRowHeight + "px",
3479
+ "--footer-row-height": props.footerRowHeight + "px",
3447
3480
  "--highlight-duration": props.highlightConfig.duration && props.highlightConfig.duration + "s",
3448
3481
  "--highlight-timing-function": unref(highlightSteps) ? `steps(${unref(highlightSteps)})` : void 0,
3449
3482
  "--sb-width": `${scrollbarOptions.value.width}px`,
@@ -3543,6 +3576,35 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3543
3576
  ], 32);
3544
3577
  }), 128))
3545
3578
  ])) : createCommentVNode("", true),
3579
+ __props.footerData && __props.footerData.length > 0 ? (openBlock(), createElementBlock("tfoot", _hoisted_8, [
3580
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.footerData, (footRow, footRowIndex) => {
3581
+ return openBlock(), createElementBlock("tr", { key: footRowIndex }, [
3582
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_9)) : createCommentVNode("", true),
3583
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(virtualX_columnPart), (col, colIndex) => {
3584
+ return openBlock(), createElementBlock("td", mergeProps({
3585
+ key: colKeyGen.value(col)
3586
+ }, { ref_for: true }, getTFProps(col)), [
3587
+ col.customFooterCell ? (openBlock(), createBlock(resolveDynamicComponent(col.customFooterCell), {
3588
+ key: 0,
3589
+ class: "table-cell-wrapper",
3590
+ col,
3591
+ row: footRow,
3592
+ rowIndex: footRowIndex,
3593
+ colIndex,
3594
+ cellValue: footRow[col.dataIndex]
3595
+ }, null, 8, ["col", "row", "rowIndex", "colIndex", "cellValue"])) : createCommentVNode("", true),
3596
+ createElementVNode("div", {
3597
+ class: "table-cell-wrapper",
3598
+ title: footRow[col.dataIndex] || ""
3599
+ }, [
3600
+ footRow[col.dataIndex] != null ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(footRow[col.dataIndex]), 1)) : createCommentVNode("", true)
3601
+ ], 8, _hoisted_10)
3602
+ ], 16);
3603
+ }), 128)),
3604
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_12)) : createCommentVNode("", true)
3605
+ ]);
3606
+ }), 128))
3607
+ ])) : createCommentVNode("", true),
3546
3608
  createElementVNode("tbody", {
3547
3609
  class: "stk-tbody-main",
3548
3610
  style: normalizeStyle({ transform: `translateY(${unref(virtualScroll).translateY}px)` }),
@@ -3555,7 +3617,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3555
3617
  style: normalizeStyle(`height:${unref(virtualScroll).offsetTop}px`),
3556
3618
  class: "padding-top-tr"
3557
3619
  }, [
3558
- unref(virtualX_on) && __props.fixedMode && __props.headless ? (openBlock(), createElementBlock("td", _hoisted_8)) : createCommentVNode("", true),
3620
+ unref(virtualX_on) && __props.fixedMode && __props.headless ? (openBlock(), createElementBlock("td", _hoisted_13)) : createCommentVNode("", true),
3559
3621
  __props.fixedMode && __props.headless ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(virtualX_columnPart), (col) => {
3560
3622
  return openBlock(), createElementBlock("td", {
3561
3623
  key: colKeyGen.value(col),
@@ -3573,12 +3635,12 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3573
3635
  onDrop: ($event) => unref(onTrDrop)($event, getRowIndex(rowIndex)),
3574
3636
  onMouseleave: onTrMouseLeave
3575
3637
  }), [
3576
- unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_10)) : createCommentVNode("", true),
3638
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_15)) : createCommentVNode("", true),
3577
3639
  row && row.__EXP_R__ ? (openBlock(), createElementBlock("td", {
3578
3640
  key: 1,
3579
3641
  colspan: unref(virtualX_columnPart).length
3580
3642
  }, [
3581
- createElementVNode("div", _hoisted_12, [
3643
+ createElementVNode("div", _hoisted_17, [
3582
3644
  renderSlot(_ctx.$slots, "expand", {
3583
3645
  row: row.__EXP_R__,
3584
3646
  col: row.__EXP_C__
@@ -3589,7 +3651,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3589
3651
  ];
3590
3652
  })
3591
3653
  ])
3592
- ], 8, _hoisted_11)) : (openBlock(true), createElementBlock(Fragment, { key: 2 }, renderList(unref(virtualX_columnPart), (col, colIndex) => {
3654
+ ], 8, _hoisted_16)) : (openBlock(true), createElementBlock(Fragment, { key: 2 }, renderList(unref(virtualX_columnPart), (col, colIndex) => {
3593
3655
  var _a2;
3594
3656
  return openBlock(), createElementBlock(Fragment, null, [
3595
3657
  !unref(hiddenCellMap) || !((_a2 = unref(hiddenCellMap)[rowKeyGen(row)]) == null ? void 0 : _a2.has(colKeyGen.value(col))) ? (openBlock(), createElementBlock("td", mergeProps({
@@ -3624,7 +3686,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3624
3686
  key: 1,
3625
3687
  class: "table-cell-wrapper",
3626
3688
  title: row[col.dataIndex] || ""
3627
- }, toDisplayString((row && row[col.dataIndex]) ?? getEmptyCellText.value(col, row)), 9, _hoisted_13)) : col.type === "seq" ? (openBlock(), createElementBlock("div", _hoisted_14, toDisplayString((props.seqConfig.startIndex || 0) + getRowIndex(rowIndex) + 1), 1)) : col.type === "tree-node" ? (openBlock(), createBlock(_sfc_main$2, {
3689
+ }, toDisplayString((row && row[col.dataIndex]) ?? getEmptyCellText.value(col, row)), 9, _hoisted_18)) : col.type === "seq" ? (openBlock(), createElementBlock("div", _hoisted_19, toDisplayString((props.seqConfig.startIndex || 0) + getRowIndex(rowIndex) + 1), 1)) : col.type === "tree-node" ? (openBlock(), createBlock(_sfc_main$2, {
3628
3690
  key: 3,
3629
3691
  class: "table-cell-wrapper",
3630
3692
  col,
@@ -3642,13 +3704,13 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3642
3704
  key: 1,
3643
3705
  onClick: ($event) => triangleClick($event, row, col)
3644
3706
  }, null, 8, ["onClick"])) : createCommentVNode("", true),
3645
- row[col.dataIndex] != null ? (openBlock(), createElementBlock("span", _hoisted_16, toDisplayString(row[col.dataIndex]), 1)) : createCommentVNode("", true)
3646
- ], 8, _hoisted_15))
3707
+ row[col.dataIndex] != null ? (openBlock(), createElementBlock("span", _hoisted_21, toDisplayString(row[col.dataIndex]), 1)) : createCommentVNode("", true)
3708
+ ], 8, _hoisted_20))
3647
3709
  ], 16)) : createCommentVNode("", true)
3648
3710
  ], 64);
3649
3711
  }), 256)),
3650
- unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_17)) : createCommentVNode("", true)
3651
- ], 16, _hoisted_9);
3712
+ unref(virtualX_on) ? (openBlock(), createElementBlock("td", _hoisted_22)) : createCommentVNode("", true)
3713
+ ], 16, _hoisted_14);
3652
3714
  }), 128)),
3653
3715
  !((_d = __props.experimental) == null ? void 0 : _d.scrollY) ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
3654
3716
  unref(virtual_on) && !unref(isSRBRActive) ? (openBlock(), createElementBlock("tr", {
package/lib/style.css CHANGED
@@ -108,6 +108,7 @@
108
108
  .stk-table{
109
109
  --row-height:28px;
110
110
  --header-row-height:var(--row-height);
111
+ --footer-row-height:var(--row-height);
111
112
  --cell-padding-y:0;
112
113
  --cell-padding-x:8px;
113
114
  --resize-handle-width:4px;
@@ -117,6 +118,7 @@
117
118
  --td-hover-color:hsl(207, 90%, 70%);
118
119
  --td-active-color:hsl(207, 90%, 54%);
119
120
  --th-bgc:#f1f1f9;
121
+ --tf-bgc:#f1f1f9;
120
122
  --th-color:#272841;
121
123
  --tr-active-bgc:#e6f7ff;
122
124
  --tr-hover-bgc:#e6f7ff;
@@ -154,6 +156,7 @@
154
156
  }
155
157
  .stk-table.dark{
156
158
  --th-bgc:#202029;
159
+ --tf-bgc:#202029;
157
160
  --th-color:#c0c0d1;
158
161
  --td-bgc:#1b1b24;
159
162
  --td-hover-color:hsl(219, 59%, 60%);
@@ -368,6 +371,18 @@
368
371
  background-color:var(--th-bgc);
369
372
  height:var(--header-row-height);
370
373
  }
374
+ .stk-table tfoot{
375
+ position:sticky;
376
+ bottom:0;
377
+ z-index:1;
378
+ }
379
+ .stk-table tfoot tr{
380
+ background-color:var(--tf-bgc);
381
+ height:var(--footer-row-height);
382
+ }
383
+ .stk-table tfoot tr td{
384
+ background-color:inherit;
385
+ }
371
386
  .stk-table tbody tr{
372
387
  background-color:var(--td-bgc);
373
388
  height:var(--row-height);
@@ -489,6 +504,9 @@
489
504
  .stk-table td.fixed-cell--active{
490
505
  z-index:1;
491
506
  }
507
+ .stk-table tfoot td.fixed-cell--active{
508
+ z-index:2;
509
+ }
492
510
  .stk-table .table-header-cell-wrapper{
493
511
  max-width:100%;
494
512
  display:inline-flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.11.0-beta.4",
3
+ "version": "0.11.0",
4
4
  "description": "High performance 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",
@@ -32,6 +32,7 @@
32
32
  :style="{
33
33
  '--row-height': props.autoRowHeight ? void 0 : virtualScroll.rowHeight + 'px',
34
34
  '--header-row-height': props.headerRowHeight + 'px',
35
+ '--footer-row-height': props.footerRowHeight + 'px',
35
36
  '--highlight-duration': props.highlightConfig.duration && props.highlightConfig.duration + 's',
36
37
  '--highlight-timing-function': highlightSteps ? `steps(${highlightSteps})` : void 0,
37
38
  '--sb-width': `${scrollbarOptions.width}px`,
@@ -101,6 +102,28 @@
101
102
  </tr>
102
103
  </thead>
103
104
 
105
+ <tfoot v-if="footerData && footerData.length > 0">
106
+ <tr v-for="(footRow, footRowIndex) in footerData" :key="footRowIndex">
107
+ <td v-if="virtualX_on" class="vt-x-left"></td>
108
+ <td v-for="(col, colIndex) in virtualX_columnPart" :key="colKeyGen(col)" v-bind="getTFProps(col)">
109
+ <component
110
+ :is="col.customFooterCell"
111
+ v-if="col.customFooterCell"
112
+ class="table-cell-wrapper"
113
+ :col="col"
114
+ :row="footRow"
115
+ :rowIndex="footRowIndex"
116
+ :colIndex="colIndex"
117
+ :cellValue="footRow[col.dataIndex]"
118
+ />
119
+ <div class="table-cell-wrapper" :title="footRow[col.dataIndex] || ''">
120
+ <span v-if="footRow[col.dataIndex] != null">{{ footRow[col.dataIndex] }}</span>
121
+ </div>
122
+ </td>
123
+ <td v-if="virtualX_on" class="vt-x-right"></td>
124
+ </tr>
125
+ </tfoot>
126
+
104
127
  <tbody
105
128
  class="stk-tbody-main"
106
129
  :style="{ transform: `translateY(${virtualScroll.translateY}px)` }"
@@ -329,6 +352,8 @@ const props = withDefaults(
329
352
  rowCurrentRevokable?: boolean;
330
353
  /** 表头行高。default = rowHeight */
331
354
  headerRowHeight?: number | string | null;
355
+ /** 表尾行高。default = rowHeight */
356
+ footerRowHeight?: number | string | null;
332
357
  /** 虚拟滚动 */
333
358
  virtual?: boolean;
334
359
  /** x轴虚拟滚动(必须设置列宽)*/
@@ -443,6 +468,8 @@ const props = withDefaults(
443
468
  * 实验性功能配置
444
469
  */
445
470
  experimental?: ExperimentalConfig;
471
+ /** 表格底部合计行数据 */
472
+ footerData?: DT[];
446
473
  }>(),
447
474
  {
448
475
  width: '',
@@ -454,10 +481,12 @@ const props = withDefaults(
454
481
  theme: 'light',
455
482
  rowHeight: DEFAULT_ROW_HEIGHT,
456
483
  autoRowHeight: () => false,
484
+ footerData: () => [],
457
485
  rowHover: true,
458
486
  rowActive: () => DEFAULT_ROW_ACTIVE_CONFIG,
459
487
  rowCurrentRevokable: true,
460
488
  headerRowHeight: DEFAULT_ROW_HEIGHT,
489
+ footerRowHeight: DEFAULT_ROW_HEIGHT,
461
490
  virtual: false,
462
491
  virtualX: false,
463
492
  columns: () => [],
@@ -1046,6 +1075,7 @@ function cellKeyGen(row: DT | null | undefined, col: StkTableColumn<DT>) {
1046
1075
  const cellStyleMap = computed(() => {
1047
1076
  const thMap = new Map();
1048
1077
  const tdMap = new Map();
1078
+ const tfMap = new Map();
1049
1079
  const { virtualX } = props;
1050
1080
  const headers = tableHeaders.value;
1051
1081
  const colKeyGenValue = colKeyGen.value;
@@ -1063,11 +1093,13 @@ const cellStyleMap = computed(() => {
1063
1093
  const colKey = colKeyGenValue(col);
1064
1094
  thMap.set(colKey, Object.assign({}, style, getFixedStyle(TagType.TH, col, depth)));
1065
1095
  tdMap.set(colKey, Object.assign({}, style, getFixedStyle(TagType.TD, col, depth)));
1096
+ tfMap.set(colKey, Object.assign({ position: 'sticky' }, style, getFixedStyle(TagType.TF, col, depth)));
1066
1097
  }
1067
1098
  }
1068
1099
  return {
1069
1100
  [TagType.TH]: thMap,
1070
1101
  [TagType.TD]: tdMap,
1102
+ [TagType.TF]: tfMap,
1071
1103
  };
1072
1104
  });
1073
1105
 
@@ -1126,7 +1158,22 @@ function getTHProps(col: PrivateStkTableColumn<DT>) {
1126
1158
  colKey === sortCol.value && sortOrderIndex.value !== 0 && 'sorter-' + sortSwitchOrder[sortOrderIndex.value],
1127
1159
  col.headerClassName,
1128
1160
  fixedColClassMap.value.get(colKey),
1129
- col.headerAlign && (col.headerAlign === 'left' ? 'text-l' : col.headerAlign === 'right' ? 'text-r' : null),
1161
+ col.headerAlign &&
1162
+ (col.headerAlign === 'left' ? 'text-l' : col.headerAlign === 'right' ? 'text-r' : col.headerAlign === 'center' ? 'text-c' : null),
1163
+ ],
1164
+ };
1165
+ }
1166
+
1167
+ function getTFProps(col: StkTableColumn<DT>) {
1168
+ const colKey = colKeyGen.value(col);
1169
+ return {
1170
+ 'data-col-key': colKey,
1171
+ style: cellStyleMap.value[TagType.TF].get(colKey),
1172
+ class: [
1173
+ col.className,
1174
+ fixedColClassMap.value.get(colKey),
1175
+ col.type === 'seq' ? 'seq-column' : '',
1176
+ col.align === 'center' ? 'text-c' : col.align === 'right' ? 'text-r' : '',
1130
1177
  ],
1131
1178
  };
1132
1179
  }
@@ -8,6 +8,7 @@
8
8
  .stk-table {
9
9
  --row-height: 28px;
10
10
  --header-row-height: var(--row-height);
11
+ --footer-row-height: var(--row-height);
11
12
  --cell-padding-y: 0;
12
13
  --cell-padding-x: 8px;
13
14
  --resize-handle-width: 4px;
@@ -17,6 +18,7 @@
17
18
  --td-hover-color: hsl(207, 90%, 70%);
18
19
  --td-active-color: hsl(207, 90%, 54%);
19
20
  --th-bgc: #f1f1f9;
21
+ --tf-bgc: #f1f1f9;
20
22
  --th-color: #272841;
21
23
  --tr-active-bgc: rgb(230, 247, 255);
22
24
  --tr-hover-bgc: #e6f7ff;
@@ -71,6 +73,7 @@
71
73
  /**深色模式 */
72
74
  &.dark {
73
75
  --th-bgc: #202029;
76
+ --tf-bgc: #202029;
74
77
  --th-color: #c0c0d1;
75
78
  --td-bgc: #1b1b24;
76
79
  --td-hover-color: hsl(219, 59%, 60%);
@@ -396,6 +399,19 @@
396
399
  height: var(--header-row-height);
397
400
  }
398
401
 
402
+ tfoot{
403
+ position: sticky;
404
+ bottom: 0;
405
+ z-index: 1;
406
+ tr {
407
+ background-color: var(--tf-bgc);
408
+ height: var(--footer-row-height);
409
+ td {
410
+ background-color: inherit;
411
+ }
412
+ }
413
+ }
414
+
399
415
  tbody tr {
400
416
  background-color: var(--td-bgc);
401
417
  height: var(--row-height);
@@ -560,6 +576,10 @@
560
576
  z-index: 1;
561
577
  }
562
578
 
579
+ tfoot td.fixed-cell--active {
580
+ z-index: 2;
581
+ }
582
+
563
583
  .table-header-cell-wrapper {
564
584
  max-width: 100%;
565
585
  display: inline-flex;
@@ -28,6 +28,14 @@ export type CustomHeaderCellProps<T extends Record<string, any>> = {
28
28
  colIndex: number;
29
29
  };
30
30
 
31
+ export type CustomFooterCellProps<T extends Record<string, any>> = {
32
+ col: StkTableColumn<T>;
33
+ row: T;
34
+ cellValue: any;
35
+ rowIndex: number;
36
+ colIndex: number;
37
+ };
38
+
31
39
  export type MergeCellsParam<T extends Record<string, any>> = {
32
40
  row: T;
33
41
  col: StkTableColumn<T>;
@@ -115,6 +123,17 @@ export type StkTableColumn<T extends Record<string, any>> = {
115
123
  * @param props.colIndex 列索引
116
124
  */
117
125
  customHeaderCell?: CustomCell<CustomHeaderCellProps<T>, T>;
126
+ /**
127
+ * 自定义 tfoot td 渲染内容
128
+ *
129
+ * 组件prop入参:
130
+ * @param props.row tfoot行的记录。
131
+ * @param props.col 列配置
132
+ * @param props.cellValue row[col.dataIndex] 的值
133
+ * @param props.rowIndex tfoot行索引(从0开始)
134
+ * @param props.colIndex 列索引
135
+ */
136
+ customFooterCell?: CustomCell<CustomFooterCellProps<T>, T>;
118
137
  /** 二级表头 */
119
138
  children?: StkTableColumn<T>[];
120
139
  /** 单元格合并 */
@@ -232,6 +251,8 @@ export type SortConfig<T extends Record<string, any>> = {
232
251
  export const enum TagType {
233
252
  TH,
234
253
  TD,
254
+ /** tfoot */
255
+ TF,
235
256
  }
236
257
 
237
258
  export type HighlightConfig = {
@@ -20,7 +20,7 @@ export function useFixedStyle<DT extends Record<string, any>>(
20
20
  virtualScroll: Ref<VirtualScrollStore>,
21
21
  virtualScrollX: Ref<VirtualScrollXStore>,
22
22
  virtualX_on: Ref<boolean>,
23
- virtualX_offsetRight: Ref<number>
23
+ virtualX_offsetRight: Ref<number>,
24
24
  ) {
25
25
  /**
26
26
  * 固定列的style
@@ -30,7 +30,7 @@ export function useFixedStyle<DT extends Record<string, any>>(
30
30
  */
31
31
  function getFixedStyle(tagType: TagType, col: StkTableColumn<DT>, depth = 0): CSSProperties | null {
32
32
  const { fixed } = col;
33
- if (tagType === TagType.TD && !fixed) return null;
33
+ if ((tagType === TagType.TD || tagType === TagType.TF) && !fixed) return null;
34
34
 
35
35
  const style: CSSProperties = {};
36
36
  const { headerRowHeight, rowHeight } = props;
@@ -40,17 +40,19 @@ export function useFixedStyle<DT extends Record<string, any>>(
40
40
 
41
41
  if (tagType === TagType.TH) {
42
42
  if (!isRelativeMode.value) {
43
- if(depth){
44
- style.top = depth * (headerRowHeight ?? rowHeight) + 'px';
43
+ if (depth) {
44
+ style.top = depth * (headerRowHeight ?? rowHeight) + 'px';
45
45
  }
46
46
  } else {
47
47
  style.top = virtualScroll.value.scrollTop + 'px';
48
48
  }
49
+ } else if (tagType === TagType.TF) {
50
+ style.bottom = '0px';
49
51
  }
50
52
 
51
53
  if (fixed) {
52
54
  if (!isRelativeMode.value) {
53
- const lr = getFixedColPosition.value(col) + 'px';
55
+ const lr = getFixedColPosition.value(col) + 'px';
54
56
  if (isFixedLeft) {
55
57
  style.left = lr;
56
58
  } else {