Files
workspace/code/g3soft-libs/docs/content/ui/data/table.md
T
2026-10-16 17:30:17 +08:00

17 KiB
Raw Blame History

title, description
title description
Table 表格 高性能虚拟滚动表格,支持排序、固定列、多选、行内编辑、列宽 / 列序拖拽与行拖拽排序

Table 表格

Table 用于展示结构化数据,底层是 stk-table-vue,虚拟滚动 / 列宽拖拽 / 列序拖拽 / 区域选择 / 行拖拽排序都由它提供;组件负责性能门控、内部列管理与行内编辑态。

本组件与 fms-vue 的 FmsTable 同源同构(实现照抄,仅替换命名与令牌体系), 两边行为一致、可互相迁移。迁移时需要的改动见文末「从旧版迁移」。

何时使用

  • 数据量大(成百上千行)且需要滚动流畅时;
  • 需要行内编辑、多选批量操作、列宽 / 列序可调时;
  • 数据量很小、只要一张静态表时,原生 <table> 或 Grid 更轻。

基础用法

给 columns 与 dataSource 两份数据即可。序号列(seq)默认开启,边框(bordered)默认有。

固定列

列上写 fixed: 'left' | 'right' 即可固定(底座用 position: sticky 实现,非双表格叠加)。三件需要知道的事:

  • 序号列与选择列已经固定在左侧,不需要业务再配;业务列的 fixed: 'left' 会自动接在它们后面,fixed: 'right' 的列要排在 columns 最后。
  • 固定列必须写 width:左固定列的吸附位移由「它左侧那些固定列的列宽累加」算出,缺宽度就没有位移。
  • 固定列只有在总列宽超过容器宽时才看得出效果 —— 整表都看得见时,固定与不固定没有任何区别。
<!-- 左冻结首列、右冻结末列 -->
<G3Table :columns="columns" :data-source="data" />

虚拟滚动与性能

组件内置两条门控,自动决定是否启用虚拟滚动,不需要业务手动切换:

方向 阈值 低于阈值时 原因
纵向 行数 > 100 全量渲染 库的虚拟滚动在「行数略多于单屏行数」时会把最后几行算出视口外,必须滚动才可见;行数少时全量渲染没有性能代价,却换来确定性正确的渲染
横向 列数 > 20 关掉横向虚拟 库在横向虚拟开启时会把每列宽度钉死为 minWidth ?? width ?? 100,未设 width 的列会失去「按比例铺满容器」的原生自适应

可以用 virtual / virtualX 关掉,但不能把它们强制打开到阈值以下。

除了门控,还有两处性能相关设计:

  • 尺寸变化 150ms 防抖:折叠动画 / 窗口缩放期间 ResizeObserver 每帧都会通知,逐帧重算会强制 reflow 并重渲染可见行列;组件合并到尺寸稳定后只刷新一次,并区分「仅宽度变化」只重算横向;
  • 选择列用原生 <input type="checkbox">:视觉由 CSS 模拟成 Checkbox 的外观,但每行不创建组件实例。

多选与批量操作

传 rowSelection 即在最前注入选择列;getCheckboxProps 可禁用个别行(禁用行不计入「全选」的分母)。

排序

列上写 sorter: true 即可点表头排序(组件内排序,不请求接口)。sortType 决定按数字还是字符串比较,默认字符串;需要自定义规则时 sorter 也可以传函数。

行内编辑

列上加 editor 即可编辑:单击单元格进入(不需要双击)。

编辑器类型

editor.type 说明
'input'(默认) 用 Input 渲染,单行。细分形态由 inputType 决定:'text'(默认)/ 'password' / 'number' / 'textarea'
'select' 用 Select 渲染;options 可以是数组,也可以是接收当前行的函数
'checkbox' 点击即切换,无需先激活整行;约定 1 = 选中、0 = 未选中(与后端 tinyint 对齐)

不传 type 时按 'input' 处理,所以 editor: 这种写法也是合法的(走默认的文本输入)。

编辑器的输入值先写进内部草稿,失焦才落库并抛出 change。这样每敲一个字符不会触发父级重渲染(否则光标会丢、输入法会断)。select 与 checkbox 是「改即落库」,不走草稿。用 editable={false} 可整体禁用编辑。

select 列在非编辑态会把值解析成对应选项的 label 展示(找不到就原样显示值)。

键盘

按键 行为
Enter 全选当前格内容(直接输入即覆盖),不移动选区
Esc 退出编辑(失焦)
方向键 由表格接管、移动单元格选区
失焦(点别处 / 方向键走开) 提交并抛出 change

方向键移动会先让编辑器待命:按住连按时全程不挂编辑器(避免每帧创建 / 销毁组件导致掉帧),松手后再把编辑器挂到最终落点;快速离散连按(间隔 <150ms)也走 100ms 防抖合并。

⚠ 数据源必须是响应式的

行对象必须能被 Vue 追踪,否则编辑回写不会触发重渲染。典型的踩坑写法是用 computed 派生数据:

// ❌ 错:computed 返回的是普通对象数组,行字段不可追踪
const rows = computed(() => Array.from({ length: n.value }, (_, i) => makeRow(i)))

// ✅ 对:ref 会递归代理内部对象
const rows = ref<Row[]>([])
watch(n, (v) => { rows.value = Array.from({ length: v }, (_, i) => makeRow(i)) }, { immediate: true })

用错时的症状极具迷惑性:change 事件照常抛出(数据其实已经改了),但界面上仍显示旧值,点一下别处才「突然」更新;select 编辑器尤其像事件时序 bug(选完下拉关了、值却没变)。

按行禁用

editor.disabled 可以是布尔,也可以是接收当前行的函数 —— 「已放舱之后状态不许再改」这类条件随行数据变化,用函数形态:

{
  dataIndex: 'status',
  title: '状态',
  editor: { type: 'select', options: STATUS_OPTIONS, disabled: (row) => row.status === 2 },
}

被禁用的格走纯展示渲染,不会挂编辑器。

操作列

列上加 actions 渲染成一排小按钮;type: 'more' 的项会收进「更多」下拉。带 confirm 的操作先弹确认框,确认后才执行 onClick。

操作列建议显式写 resizable: false:它的宽度由内容决定,拖了不会落库,列一重建就丢。

列宽、列序与行拖拽

列宽 / 列序拖拽要求用 v-model:columns 绑定列定义——否则组件拖完无处回写(松手回弹),会直接关掉这两个功能。行拖拽排序用 row-draggable,顺序变化通过 row-order-change 回传。

API

Props

名称 类型 默认值 说明
columns G3TableColumn<T>[] [] 列定义,配合 v-model:columns 接收拖拽后的回写
dataSource T[] [] 数据源(必须响应式,见「行内编辑」)
rowKey string | (row) => string | number 'id' 行唯一键
virtual boolean true 纵向虚拟滚动总开关(实际是否开启受行数门控)
virtualX boolean true 横向虚拟滚动总开关(实际是否开启受列数门控)
rowHeight number 30 行高,同时驱动虚拟滚动计算
headerRowHeight number 34 表头行高
bordered boolean true 边框。true = 圆角外框 + 行分隔线;false = 无外框,只留行分隔线(嵌进卡片 / 面板时用)
colResizable boolean | object true 列宽拖拽;未绑定 v-model:columns 时自动关闭。可用 { disabled: (col) => boolean } 按列禁用
columnDraggable boolean | object true 列序拖拽;同上要求绑定 v-model:columns。{ mode: 'none' | 'insert' | 'swap', disabled }
rowDraggable boolean false 行拖拽排序
rowSelection TableRowSelection | null null 多选配置,见下
seq boolean | { title?, width? } true 序号列,固定左侧,默认宽 50
activeRowKey string | number | null null 当前高亮行
editable boolean true 行内编辑总开关
noDataFull boolean true 空态铺满容器高度
showOverflow boolean true 内容溢出省略
autoFitWidth boolean false 非横向虚拟时铺满容器宽度(开启后关闭列宽拖拽)
theme 'light' | 'dark' — 不传则跟随全局主题(html[data-g3-theme])
footerData T[] [] 表尾数据
footerConfig object `` 表尾配置

列定义 G3TableColumn<T>

在 stk-table-vue 的列类型基础上扩展(交叉类型,非 Omit);下面只列自有字段,其余(width / minWidth / fixed / align / headerAlign / sorter / sortType / children / mergeCells / customCell / customHeaderCell 等)原样透传。

字段 类型 说明
dataIndex keyof T & string 取值字段
title string 表头文字
resizable boolean 该列是否可拖宽;操作列 / 展开列建议设 false
editor TableEditor<T> 行内编辑器,见下
actions TableAction<T>[] 操作列按钮
customCell (props) => VNode 自定义单元格渲染(逃生舱),传了就不再自动包装

editor

字段 类型 说明
type 'input' | 'select' | 'checkbox' 编辑器类型,不传按 'input' 处理
inputType 'text' | 'password' | 'number' | 'textarea' 仅 type: 'input' 生效,默认 'text'
options TableSelectOption[] | ((row) => TableSelectOption[]) select 选项,可依赖当前行其它字段
disabled boolean | ((row) => boolean) 是否禁用该格编辑
allowClear boolean select 是否可清空,默认 true
emptyValue unknown select 清空后落库的值,默认 null
onChange (value, row, col) => void 本列值变化回调(早于组件的 change)

TableSelectOption:{ label: string; value: string | number | boolean | null; disabled?: boolean }

actions

字段 类型 说明
key string | number 唯一键;type: 'more' 的子项用它做下拉 key
label string 文案
type 'default' | 'danger' | 'delete' | 'more' 语义类型
icon Component 图标组件,渲染为 size=14
title string title 提示
danger boolean 危险色
visible boolean | ((row) => boolean) 是否显示
disabled boolean | ((row) => boolean) 是否禁用
confirm string | object 二次确认;字符串为标题,对象支持 title / description / content / okText / cancelText / okType / width / maskClosable
onClick (row) => void 点击回调(带 confirm 时在确认后执行)
items TableAction<T>[] type: 'more' 时的子项

rowSelection

字段 类型 说明
selectedRowKeys Array<string | number> 已选行的 key 数组(受控)
onChange (keys, rows) => void 选中变化回调
getCheckboxProps (row) => { disabled?: boolean } 单行复选框属性

Events

名称 参数 说明
update:columns (columns) 列宽 / 列序拖拽后回写(内部列已剥离)
change (row, col, value) 行内编辑的值变化
row-order-change (rows, dragStartKey, targetRowKey) 行拖拽排序完成。后两个参数是行 key,不是索引
area-selection-change (ranges) 单元格选区变化(行内编辑依赖它)

row-click 等底座事件未声明为 emit,而是随 $attrs 透传到内层 StkTable, 照常可用:@row-click="(_, row) => (activeKey = String(row.id))"。 注意这类监听器拿不到组件侧的类型推断,回调参数需显式标注 (如 (_: MouseEvent, row: TableRow) => ...)。

Exposed

名称 类型 说明
scrollToRow(rowKey) Promise<void> 滚动到指定行
editRow(rowKey, focusCol?) Promise<void> 滚动到指定行并进入编辑
initVirtualScroll() / refreshLayout() — 手动重算虚拟滚动布局
getSelectedArea() / clearSelectedArea() — 读取 / 清空单元格选区
setRowExpand(...) — 展开 / 收起行(需配 expand 列)

Slots

名称 说明
empty 空态内容,默认显示「暂无数据」
expand 展开行内容(需列配置 type: 'expand')

实现说明

  • 底层是 stk-table-vue:虚拟滚动、列宽 / 列序拖拽、区域选择、行拖拽、二级表头、固定列、合并单元格都由它实现。组件不重复造这些轮子,只做门控与适配。
  • 库样式已并入 dist/style.css:库的 CSS 是独立文件(其 JS 里不含任何 .css import),组件 @use 了它,消费方不需要额外引一次第三方样式。
    • 注意 SCSS 里不能写 @use 'stk-table-vue/lib/style.css':带 .css 后缀时 Sass 不会内联,只原样输出一条 CSS @import 语句,产物里会留下一条浏览器解析不了的裸包路径。去掉后缀才会真正内联。
  • 主题通过覆盖库的 CSS 变量实现:库把主题值都做成了 .stk-table 作用域内的变量,组件把它们映射到 --g3-* 令牌(覆盖层在 src/table/style/_overrides.scss)。改变量而不是逐个选择器改样式,库升级时不容易失效。
  • 区域选择按需自注册:库的单元格区域选择是全局注册的(registerFeature(useAreaSelection)),不注册则 area-selection-change 永不触发、行内编辑失效。而且注册必须早于任何 StkTable 实例的创建(库在 setup 顶层取值),所以不能放在 onMounted。组件在模块被求值时就注册;需要显式控制时可调用导出的 registerTableFeatures()。
  • 边框:外框自绘,横线走真 border。组件把 bordered 翻成底座的 'v' 档(只借它的竖线),再自己补外框与横线。
  • 行编辑态存在 WeakMap 里:按行对象存,不给业务数据打标记;行被回收时状态自动释放。
  • 编辑器按编辑态门控注入:没有任何行处于编辑态时,可编辑列走底座原生 td 渲染路径(减少每格一个函数式组件实例);checkbox 与 select 例外——前者始终可交互,后者需要解析 label 展示。某一行进入编辑态后,列上才会挂上按行分发的编辑器。
  • RTL:布局用逻辑属性,带符号位移走 --g3-dir-sign。

从旧版迁移

本组件曾有一版自研能力更多的实现(含校验、精度、日期编辑器、键盘跳格等)。为与 fms-vue 的 FmsTable 对齐,现已按 fms 重写;下列能力已移除,迁移时需改到业务侧:

已移除 迁移建议
editor.validate / editor.validateWarning / editor.errorStyle 校验改由业务侧负责:保存前遍历 dataSource 自行判定并提示
editor.precision / col.format 数值格式化与小数位规则移到业务侧(或列上 customCell 自定义渲染)
editor.type: 'date' / editor.format / editor.valueFormat / editor.placeholder 日期列改为纯展示(customCell),或换用可编辑的 input
checkPrecision / checkDate 导出 已移除;规则由业务侧自行实现
Tab / Shift+Tab 跳格、方向键切格与 Enter 下移 方向键仍由表格移动选区;Enter 改为「全选当前格」
editorSeamless prop 编辑器形态固定(fms 口径),不再可切换
只读格「灰底」视觉 已移除;不可编辑的列即无 editor
stripe / fixedColShadow prop 已移除;如需斑马纹自行写 CSS
scrollbar / scrollRowByRow / smoothScroll prop 已移除;固定走底座的默认滚动路径
update:selectedRowKeys 事件 用 rowSelection.onChange 接收勾选变化
row-click 声明式 emit 仍可用,但改由 $attrs 透传(见「Events」小节的提示)
getTableData() 暴露方法 已移除;需要当前顺序时用 row-order-change 回传的 rows

editor.type 的值域变了:旧版是 'text' | 'number' | 'date' | 'password' | 'select' | 'checkbox', 现在是 'input' | 'select' | 'checkbox'(输入框的细分形态走 inputType)。 迁移时 type: 'text' → type: 'input',type: 'number' → type: 'input', inputType: 'number'。