table-grid-plugin
v1.0.10
Published
开箱即用的 Vue3 高性能数据表格组件。纯 div + 虚拟滚动实现,支持百万级数据流畅渲染、树形、固定列与表头、多级表头、选择、自定义列模板与插槽、单元格合并、展开行与移动端卡片模式。渲染层统一 TSX,运行时零依赖。
Downloads
1,700
Maintainers
Readme
table-grid-plugin
开箱即用的 Vue3 高性能数据表格组件(XDataGrid)。
纯 div 实现 · 自研虚拟滚动内核 · 运行时零依赖(仅依赖 vue)· 百万级数据流畅渲染 · 移动端自动切换为卡片浏览。渲染层统一使用 TSX(h 函数),不依赖任何底层表格库。
内置能力:虚拟滚动 / 树形表格(含懒加载)/ 多级表头 / 固定列与表头 / 单选·多选·树形级联选择 / 单元格合并 / 展开行 / 排序 / 列筛选 / 分页(本地与远程)/ 滚动加载更多(load-more)/ 表尾合计 / 单元格编辑 / 对象字段(点路径 key)/ 工具栏(列设置·搜索·通用操作按钮)/ 提示框 Tooltip / 列设置缓存(IndexedDB)/ 骨架屏 / 移动端卡片模式。
目录
- table-grid-plugin
- 目录
- 1. 安装
- 2. 引入(快速开始)
- 3. 演示 Demo
- 4. API 文档
- 5. 功能详解
- 5.1 百万级数据渲染(虚拟滚动)
- 5.2 列配置与多级表头
- 5.3 固定列 / 固定表头
- 5.4 树形表格与懒加载
- 5.5 选择(多选 / 单选 / 全选 / 树形级联)
- 5.6 排序
- 5.7 列筛选
- 5.8 分页
- 5.9 表尾合计统计行
- 5.10 单元格编辑
- 5.11 工具栏
- 5.12 提示框 Tooltip
- 5.13 单元格合并
- 5.14 展开行
- 5.15 列设置与缓存
- 5.16 远程数据模式
- 5.17 加载态 / 骨架屏 / 空态
- 5.18 移动端卡片模式
- 5.19 密度 / 斑马纹 / 行高 / 边框
- 5.20 事件与实例方法
- 5.21 滚动到底部加载更多(load-more)
- 5.21.1 数据加载方式切换
- 5.22 对象字段(点路径 key)
- 5.23 数组对象自动合并
- 6. 主题定制(CSS 变量)
- 7. 国际化(i18n)
- 8. 浏览器兼容性
- 9. 常见问题 FAQ
- License
1. 安装
npm i table-grid-pluginvue(≥ 3.2)作为唯一的 peerDependency,安装本插件前请确认目标项目已安装 Vue3。
2. 引入(快速开始)
2.1 全局注册(推荐)
// main.ts
import { createApp } from 'vue'
import XDataGrid from 'table-grid-plugin'
// 样式随组件按需引入(sideEffects 已声明 scss)
import 'table-grid-plugin/dist/table-grid-plugin.css'
import App from './App.vue'
const app = createApp(App)
app.use(XDataGrid) // 自动注册为全局组件 <XDataGrid />
app.mount('#app')2.2 局部使用组件
<script setup>
import XDataGrid from 'table-grid-plugin'
const columns = [
{ key: 'name', label: '姓名', width: 140, fixed: 'left' },
{ key: 'age', label: '年龄', width: 100, sortable: true },
{ key: 'city', label: '城市', width: 140, filterable: true }
]
const data = [
{ id: 1, name: '张三', age: 18, city: '北京' },
{ id: 2, name: '李四', age: 22, city: '上海' }
]
</script>
<template>
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
style="height: 400px"
/>
</template>组件必须给定一个确定的高度(容器高度或
height属性),虚拟滚动依此计算可视行区间。
2.3 Composition API:useTable
useTable 提供与组件同源的响应式状态,可脱离组件单独使用(如自建视图层或后台逻辑复用):
import { ref } from 'vue'
import { useTable } from 'table-grid-plugin'
const columns = ref([{ key: 'name', label: '姓名', width: 140 }])
const data = ref([{ id: 1, name: '张三' }])
const containerRef = ref<HTMLElement | null>(null)
const state = useTable(
{ columns, data, rowKey: 'id' } as any,
() => {},
containerRef
)
// state.scroller.range.value → 当前可视行区间
// state.selection.getSelectedRows() → 选中行3. 演示 Demo
仓库 demo/ 目录内置完整演示项目,覆盖下方全部功能点,每个示例对应右侧「代码参考」栏:
| # | 演示面板 | 覆盖功能 |
| --- | --- | --- |
| ① | 百万渲染 | 虚拟滚动 · 固定列 · 斑马纹 · 密度 · 骨架屏 · 提示框插槽 |
| ② | 树形表格 | 多级表头 · 固定列 · 树形级联选择 · 严格勾选 · 默认选中 · 懒加载 · tree-toggle |
| ③ | 单元格合并 | span-method 的 rowspan / colspan 合并 |
| ④ | 排序筛选分页 | 工具栏(操作按钮)· 选择 · 排序 · 筛选 · 分页 · 合计 · 双击编辑 · 默认排序 |
| ⑤ | 展开行 | type:'expand' 列 / expandable / #expand-row 插槽 / expand-change |
| ⑥ | 提示框 | showOverflow:'tooltip' / showTooltip / tipRenderer / #tip-<key> 插槽 |
| ⑦ | 事件与实例 | row-click / header-click / scroll 等事件 · scrollTo 等实例方法 |
| ⑧ | 远程数据加载 | 远程排序/筛选/分页 · 骨架屏 · 空态 · defaultSort / columnFilters 初始条件 |
| ⑨ | 列设置 | 列设置抽屉 · 拖拽排序 · 显隐 · IndexedDB 缓存(id) |
| ⑩ | 移动端卡片 | 卡片模式 · 手势切换 · cardProps 字段配置 |
| ⑪ | 空数据 | 默认美化空态 · #empty 插槽自定义(内置按钮等交互) |
| ⑫ | 动态加载(分页/加载方式切换) | pagination + loadMode · 列设置抽屉切换「分页 / 动态加载」· @page-change 统一回调追加数据 · 底部「加载进度分页条」· 重置后已加载页码归零 |
| ⑬ | 对象字段 | 点路径 key({ key: 'chengjie.yw' } 读取嵌套对象)· 编辑/排序/合计全支持 |
启动演示:
npm i
npm run demo:dev # 本地开发(默认 http://localhost:5177)
npm run demo:build # 构建产物输出到 demo-dist/4. API 文档
4.1 Props 参数表
所有属性均非响应式强关联,声明即生效。
| 属性 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| columns | ColumnConfig[] | ✔ | — | 列配置(支持多级嵌套 → 自动多级表头) |
| rowKey | string \| (row)=>Key | ✔ | — | 行键解算:字段名或函数。选择/树/展开/合并的稳定追踪依赖它 |
| id | string | — | — | 表格唯一标识。配置后启用列设置缓存(IndexedDB)(按列配置签名自动失效);同时启用分页页码持久化(见 5.8) |
| data | TableRowRecord[] | — | [] | 表格数据(Record<string, unknown>[]) |
| rowHeight | number | — | 44 | 单行高度(px),虚拟滚动固定行高模型 |
| height | string \| number | — | 100% | 容器高度 |
| width | string \| number | — | 100% | 容器宽度 |
| scroll | ScrollConfig | — | — | 滚动配置(x 横向 / y 纵向) |
| fixedHeader | boolean | — | true | 是否固定表头 |
| selectMode | 'none'\|'single'\|'multiple' | — | 'none' | 选择模式 |
| selectable | (row)=>boolean | — | — | 行是否可被选择(返回 false 的行禁用勾选) |
| reserveSelection | boolean | — | false | 数据刷新后是否保留选择(基于行键) |
| checkStrictly | boolean | — | false | 是否严格勾选:false 启用上下级关联选择;true 时上下级独立勾选、不再联动 / 半选 |
| defaultCheckedKeys | Array<string\|number> | — | — | 默认选中的节点键数组;自动应用关联规则(连带选中下级、向上归约父级) |
| treeProps | TreeProps | — | — | 树形配置(children 字段 / 懒加载等,见 4.9) |
| expandable | boolean | — | false | 是否启用展开行(配合 #expand-row 插槽;未配置 expand 列时首个数据列自动挂展开箭头)。同一时刻仅一行可展开,高度自适应插槽内容 |
| spanMethod | SpanMethod | — | — | 单元格合并方法 |
| arrayMerge | boolean\|ArrayMergeConfig | — | false | 数组对象自动合并:把数组嵌套列(如 cj:[{xk,cj}])展开为多子行并对非数组列纵向合并。true 全量自动合并;{ mergeColumns: ['name'] } 仅合并指定列(见 4.11 / 5.23) |
| locale | Partial<LocaleMessages> | — | — | 表格级 key 型语言包覆盖(见 7.2) |
| translations | TranslationsMap | — | — | 表格级「文本词典」:语言码 -> {中文文本: 译文},翻译表头 label 与内置公共文案,粒度最优先(见 7.1) |
| stripe | boolean | — | false | 斑马纹 |
| border | boolean | — | true | 显示单元格边框 |
| showRowHover | boolean | — | true | 行 hover 高亮 |
| density | 'compact'\|'default'\|'loose' | — | 'default' | 密度(行高倍率 0.85 / 1 / 1.25) |
| mobileBreakpoint | number | — | 500 | 容器宽度 ≤ 该值时切换为卡片模式 |
| cardProps | CardFieldConfig | — | — | 卡片模式字段配置(见 4.10) |
| theme | ThemeTokens | — | — | 主题 token(注入 CSS 变量) |
| overscan | number | — | 8 | 虚拟滚动上下缓冲行数 |
| loading | boolean | — | false | 显示加载态 |
| skeleton | boolean | — | false | loading 为 true 时以骨架屏展示(默认转圈) |
| emptyText | string | — | '暂无数据' | 空数据文案 |
| hiddenColumns | string[] | — | [] | 需要隐藏的列键集合(与实例方法隐藏叠加) |
| columnOrder | Array<string\|number> | — | — | 列顺序控制(顶层层级键);与列 key 或组内任叶子 key 匹配 |
| defaultSort | SortInfo[] | — | — | 默认排序(可多列,[{ key, order }]) |
| sortMode | 'local'\|'remote' | — | 'local' | 排序方式:本地内部排序 / 远程触发 sort-change 由外层处理 |
| columnFilters | FilterState[] | — | — | 列筛选条件([{ key, values }]) |
| filterMode | 'local'\|'remote' | — | 'local' | 筛选方式:本地 / 远程 |
| pagination | Pagination | — | — | 分页配置;提供该对象即启用分页(见 4.6) |
| loadMore | LoadMoreConfig | — | — | 滚动到底部自动加载更多;提供该对象即启用,且自动隐藏分页条(见 4.7 / 5.21) |
| loadMode | 'page'\|'scroll' | — | 'page' | 数据加载方式:'page'(默认)常规分页显示;'scroll' 动态加载——滚动到底部触发 page-change{current,pageSize}(与分页回调一致),数据由外层按页追加累积(虚拟滚动承载),底部展示「加载进度分页条」。配置 pagination 时可在「列设置」抽屉切换该方式(见 5.21.1) |
| showFooter | boolean | — | false | 是否显示表尾合计行 |
| footerSummary | FooterSummaryConfig | — | — | 可配置合计统计行(见 4.8) |
| editable | boolean | — | false | 是否允许单元格编辑(配合列的 editable / editRules) |
| resizable | boolean | — | true | 是否允许拖拽调整列宽(列级 resizable 优先) |
| columnMount | 'body'\|'container' | — | 'container' | 列设置抽屉挂载位置:'container' 挂载到表格容器内部,与表格成整体单元;'body' 挂载到 <body>,全屏全局抽屉 |
| toolbar | ToolbarConfig | — | — | 工具栏配置(见 4.3) |
4.2 ColumnConfig 列配置
| 属性 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| key | string | ✔ | — | 列键:数据取值字段 / 插槽名;支持点路径读取嵌套对象(如 'chengjie.yw',见 5.22) |
| label | string | ✔ | — | 表头文本 |
| type | 'default'\|'selection'\|'checkbox'\|'radio'\|'expand'\|'index'\|'custom' | — | 'default' | 列类型(selection/checkbox 多选列、radio 单选列、expand 展开列、index 序号列) |
| width | number | — | — | 固定宽度(px),设置后不参与剩余空间分配 |
| minWidth | number | — | — | 最小宽度:未设 width 时自适应分配但不得小于该值 |
| fixed | 'left'\|'right' | — | — | 固定列表位置 |
| align | 'left'\|'center'\|'right' | — | 'left' | 单元格对齐 |
| headerAlign | 'left'\|'center'\|'right' | — | align | 表头对齐 |
| showOverflow | boolean\|'tooltip' | — | — | 内容溢出截断;'tooltip' 时悬停弹出全文(含一键复制) |
| showTooltip | boolean | — | — | 强制开启提示框(内容未截断也显示) |
| tipRenderer | TipRenderer | — | — | 提示框自定义渲染函数(优先级低于 #tip-<key> / #tip 插槽) |
| cellRender | CellRenderer | — | — | 单元格渲染函数(兜底,优先级低于插槽) |
| headerRender | HeaderRenderer | — | — | 表头渲染函数(兜底) |
| children | ColumnConfig[] | — | — | 多级表头子列 |
| sortable | boolean\|'custom' | — | — | 是否参与排序;'custom' 时配合 sorter 走自定义排序 |
| sorter | (a, b) => number | — | — | 自定义排序函数(优先级最高) |
| filterable | boolean | — | — | 是否显示列筛选图标并参与筛选 |
| filterOptions | string[] | — | — | 筛选候选选项(缺省取该列全部去重值) |
| filterMultiple | boolean | — | — | 是否多选筛选(默认单值切换) |
| summable | boolean\|FooterSummary | — | — | 是否参与表尾合计(列级旧配置,见 5.9) |
| editable | boolean | — | — | 是否可编辑(双击进入编辑,需启用 editable) |
| arrayField | boolean | — | — | 数组合并列标记:key 为点路径(如 cj.xk)时显式声明为数组列(首个字段为数组字段),用于数据为空或无法自动识别数组类型的兜底(见 5.23) |
| editRules | EditValidateResult\|(value, row)=>EditValidateResult | — | — | 编辑校验规则:返回 { pass, message? } |
| editType | 'text'\|'number'\|'select' | — | 'text' | 编辑输入类型 |
| editOptions | Array<{ label, value }> | — | — | 编辑下拉选项(editType='select' 时预留) |
| resizable | boolean | — | — | 是否允许拖拽调整该列列宽(覆盖全局 resizable) |
| slotName | string | — | — | 单元格插槽名:配置后该列优先取名为 slotName 的插槽渲染(默认用列 key 作为插槽名),典型用于头像 / 图片 / 富内容自定义展示(见 5.24) |
| actions | TableAction[] | — | — | 操作列按钮组配置:提供后本列渲染为操作列(PC 可见按钮不超过 actionLimit 时全量平铺,超出收起为「actionLimit-1 个 + 更多」下拉,示例:详情 + 更多;移动端卡片平铺为底部操作条),见 4.5 |
| actionLimit | number | — | 2 | 操作列展示的控件数量上限:可见按钮不超过该值时全量平铺;超出则收起为「前 actionLimit-1 个按钮 + 「更多」下拉」,需配合 actions |
4.3 ToolbarConfig 工具栏配置
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| columnSetting | boolean | — | 是否显示列设置入口(仅图标,打开列设置抽屉;挂载位置由顶层 columnMount 控制) |
| search | boolean | — | 是否显示搜索入口(点击触发 toolbar-search 事件,交互由使用方实现) |
| buttonNames | boolean | — | 是否显示按钮名称开关(配合操作按钮显隐文字) |
| actions | ToolbarAction[] | — | 通用操作(权限)按钮配置(见 4.4) |
| showTitle | boolean | — | 是否显示标题区域(#toolbar-title / #title 插槽) |
4.4 ToolbarAction 操作按钮
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| key | string | — | 权限标识:点击触发 toolbar-action 事件并回传(设置 onClick 时不再自动触发) |
| label | string | — | 按钮名称(跟随「按钮名」开关显隐) |
| icon | ToolbarIcon | — | 图标:全局注册图标名 / Element Plus 图标组件 / 自定义图标组件 / 渲染函数 / 内联 SVG 源码(?raw 导入) |
| disabled | boolean\|()=>boolean | — | 是否禁用(响应式函数典型用法:勾选数 === 0 时禁用) |
| loading | boolean\|()=>boolean | — | 是否加载中(旋转图标占位并禁用,任务完成后恢复) |
| iconSize | number | 14 | 图标像素大小(覆盖全局默认 --tbl-action-icon-size) |
| onClick | (action, event) => void | — | 自定义点击事件:提供后替代全局 toolbar-action |
| keepName | boolean | — | 是否始终显示名称(忽略「按钮名」开关) |
4.45 TableAction 操作列按钮
在列配置 ColumnConfig.actions 中声明(见 5.24)。每个按钮即一个 TableAction:
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| key | string | — | 操作唯一标识:点击触发统一 action 事件时回传该 key |
| label | string | — | 按钮文本(「更多 ⋯」下拉内亦显示) |
| type | 'default'\|'primary'\|'success'\|'warning'\|'danger' | 'default' | 按钮语义:影响默认配色(primary 主题色 / danger 危险红等) |
| visible | (row) => boolean | — | 权限 / 行判定:返回 false 时该按钮对当前行隐藏(典型:按角色 / 状态控制可见性) |
| disabled | boolean\|(row) => boolean | — | 是否禁用:布尔或基于当前行动态判定(返回 true 禁用,如「已完成」禁删) |
| render | (row) => string\|VNode | — | 自定义按钮内容渲染(优先级高于 label,可返回图标 + 文本) |
| onClick | (row) => boolean\|Promise<boolean>\|void | — | 点击回调(传入当前行):返回 false(或异步 resolve false)时阻止触发统一 action 事件 |
4.5 Pagination 分页配置
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| pageSize | number | — | 页大小 |
| current | number | — | 当前页(从 1 起) |
| pageSizes | number[] | — | 可选页大小 |
| remote | boolean | — | 是否远程分页(本地分页时内部切片) |
| total | number | — | 数据总条数(远程分页时由外层提供;本地分页未设时自动取数据条数) |
4.6 LoadMoreConfig 加载更多配置
提供 loadMore 对象即启用「滚动到底部自动加载更多」:滚动条滚动到距底部阈值内触发 load-more 回调,动态查询下一页并追加到 data 自动累加为大表格;该模式下自动隐藏分页条(无需「下一页」按钮)。
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| loading | boolean | — | 是否正在加载更多:加载中底部显示 loading 提示条并禁止重复触发;置回 false 后才允许再次触发 load-more |
| finished | boolean | — | 是否已无更多数据:底部显示「已加载全部数据」,停止触发 load-more |
| threshold | number | 40 | 距离底部多少像素内视为到达底部,触发 load-more |
| total | number | — | 数据总条数。提供后底部渲染「加载进度分页条」:展示与常规分页条一致(左侧「共 X 条」+ 上一页/下一页 + 页码窗口),页码始终最多 7 个、超出自动省略号折叠,省略号可点击滑动窗口,已加载页可点击、未加载页置灰禁点(见 5.21) |
| pageSize | number | 20 | 每页条数。与 total 配合计算总页数并判断某页是否已加载 |
4.7 FooterSummaryConfig 合计配置
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| label | string | '合计' | 统计行显示名称 |
| fields | FooterStatsField[] | — | 多字段统计配置 |
FooterStatsField:
| 属性 | 类型 | 说明 |
| --- | --- | --- |
| field | string | 目标字段名(表格字段,即 ColumnConfig.key) |
| method | 'sum'\|'average'\|'count'\|{ type, multiplier } | 统计方式:默认求和;{ type:'sum', multiplier } 为「每行值 × 系数再累积」 |
| fn | (rows) => number\|string | 自定义统计函数(优先级最高,覆盖 method) |
| formatter | (value) => string | 展示格式化 |
4.9 TreeProps 树形配置
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| children | string | 'children' | 子节点字段名 |
| hasChildren | string | 'children' | 是否存在子节点字段名(懒加载占位判断) |
| label | string | — | 展示文本字段名(配置后用于树形占位显示) |
| disabled | string\|(row) => boolean | — | 复选框禁用:字段名或判定函数 |
| lazy | boolean | false | 是否懒加载 |
| loadChildren | (row) => Promise<row[]> | — | 懒加载子节点回调 |
4.9 CardFieldConfig 卡片字段配置
| 属性 | 类型 | 说明 |
| --- | --- | --- |
| fields | string[] | 展示顺序(未给出则按列序) |
| labelMap | Record<string, string> | 字段展示标签映射(缺省用列 label) |
| visibleFields | string[] | 仅展示这些字段(未给出则展示全部非控制列) |
4.14 ArrayMergeConfig 数组合并配置
arrayMerge 接受 boolean 或如下对象:
| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| mergeColumns | string[] | 全部非数组列 | 指定需要纵向合并(rowspan)的非数组列 key 列表;仅列出的列跨子行合并,其余非数组列逐子行重复展示。序号 / 选择(复选框 / 单选)列属组级列,始终整组合并,不受此配置影响 |
| 取值 | 效果 |
| --- | --- |
| false / 缺省 | 关闭数组合并,数组列按原始数据原样显示 |
| true | 自动识别数组列并展开,所有非数组列纵向 rowspan 合并 |
| { mergeColumns: ['name'] } | 仅 name 列纵向合并,其余非数组列(班级 / 备注等)逐子行重复展示 |
4.9 事件
| 事件名 | 参数 | 说明 |
| --- | --- | --- |
| selection-change | (info: SelectionInfo) | 选择变化。返回完整选择信息,区分「完全选中」与「半选」 |
| expand-change | (row, expanded) | 展开行切换。点击展开/收起某行时触发,row 为目标行、expanded 是否展开。同一时刻仅一行可展开,可在展开时动态调用接口更新插槽数据 |
| tree-toggle | (row, expanded) | 树节点展开 / 收起 |
| scroll | ({ scrollLeft, scrollTop }) | 滚动事件 |
| card-change | ({ index, row }) | 卡片切换(移动端) |
| row-click | (row, index, evt) | 行点击 |
| row-dbl-click | (row, index, evt) | 行双击 |
| header-click | (column, evt) | 表头点击 |
| sort-change | ({ sort, key, order }) | 排序变化。key 为触发字段;order='asc' 上箭头升序 / 'desc' 下箭头降序 / null 清除 |
| filter-change | ({ filters, key }) | 列筛选变化(key 为触发字段) |
| page-change | ({ current, pageSize }) | 分页变化 |
| load-more | () | 滚动到底部自动加载更多(提供 loadMore 配置时生效)。到达底部阈值即触发一次;loadMore.loading 置回 false(或数据条数增长)后允许再次触发 |
| load-mode-change | (mode) | 在「列设置」抽屉切换数据加载方式时触发;mode 为 'page'(分页)或 'scroll'(动态加载) |
| edit-change | ({ row, column, value, trigger }) | 单元格编辑提交(校验通过后;trigger 为 double-click/enter/blur) |
| column-resize | ({ key, width }) | 拖拽调整列宽 |
| search | ({ keyword }) | 本地搜索触发(setSearch 内部) |
| toolbar-search | ({ event }) | 点击工具栏「搜索」按钮(交互由使用方实现) |
| toolbar-action | ({ key, event }) | 点击工具栏通用操作按钮(未设 onClick 时触发) |
| action | ({ key, row, event }) | 点击操作列按钮(key 为 TableAction.key;按钮配置了 onClick 且返回 false 时不触发本事件,见 5.24) |
4.11 实例方法
通过组件 ref 访问:
| 方法 | 说明 |
| --- | --- |
| getSelection() | 获取选中行数组(完全选中的行) |
| getSelectRowKeys() | 获取选中行键数组 |
| getSelectionInfo() | 手动获取完整选择信息 SelectionInfo(含半选) |
| toggleRowSelection(row, selected?) | 切换某行选择(树形关联模式下自动级联) |
| setTreeExpand(row, expanded) | 设置树节点展开状态 |
| refreshData() | 刷新数据(懒加载清缓存) |
| scrollTo(index) | 滚动到指定虚拟行 |
| getColumnWidths() | 获取各叶子列最终宽度 |
| recalcWidths() | 重新计算列宽 |
| setColumnOrder(keys) | 设置列顺序(顶层层级键数组) |
| setHiddenColumns(keys) | 设置列显隐(隐藏指定列键集合) |
| showColumn(key) / hideColumn(key) | 显示 / 隐藏某列 |
| setColumnWidth(key, width) | 更新某列宽 |
| clearSort() / getSort() | 清空排序 / 获取当前排序 |
| clearFilter() / getFilter() | 清空列筛选 / 获取当前筛选条件 |
| setCurrentPage(page) / setPageSize(size) | 设置当前页 / 页大小 |
| sortBy(key, order?) | 对指定列执行排序切换 |
| setSearch(keyword) | 设置全局搜索关键词(本地过滤),空串清除 |
| reset() | 重置内部状态 |
4.14 插槽 Slots
| 插槽名 | 作用域 | 说明 |
| --- | --- | --- |
| default | { row, column, $index, $rowIndex } | 单元格默认模板(兜底) |
| #<列key> | { row, column, $index, $rowIndex } | 指定列的单元格模板(默认插槽名 = 列 key) |
| #<slotName> | 同上 | 列配置了 slotName 时以该名插槽渲染(可显示图片 / 富内容,见 5.24) |
| #header | { column, level, colSpan, rowSpan } | 表头默认模板 |
| #header-<列key> | 同上 | 指定列表头模板 |
| #expand-row | { row } | 展开行全宽内容模板 |
| #tip | { row, column, $index, $rowIndex } | 全局提示框内容模板(自定义悬停浮层) |
| #tip-<列key> | 同上 | 指定列提示框内容模板(优先级最高) |
| #toolbar-title / #title | — | 工具栏标题 |
| #toolbar-top | — | 位于标题栏(#toolbar-title 标题行)与附加行之间的插槽行,供搜索条件展示等扩展使用。渲染在工具栏内部、标题行之下,而非工具栏顶部 |
| #toolbar-actions | { showNames, toggleNames } | 工具栏自定义操作插槽 |
| #empty | — | 空数据自定义内容(可内置按钮等快捷交互;开启指针事件以支持点击) |
插槽优先级高于
cellRender/headerRender。控制列(selection/expand/index)的点击事件已被组件内部接管。
4.15 类型定义
import type {
TableProps, TableEvents, TableInstance, SelectionInfo,
ColumnConfig, TreeProps, SpanMethod, SpanCellContext,
TableRowRecord, RowKeyResolver, CardFieldConfig,
ThemeTokens, SelectMode, SortInfo, FilterState,
Pagination, LoadMoreConfig, FooterSummaryConfig, ToolbarConfig, ToolbarAction, TableAction
} from 'table-grid-plugin'SelectionInfo(selection-change 回调与 getSelectionInfo() 的返回值,区分「完全选中」与「半选」):
interface SelectionInfo {
/** 完全选中节点键 */
checkedKeys: Array<string | number>
/** 半选(部分选中)节点键 */
indeterminateKeys: Array<string | number>
/** 完全选中节点的原始数据(含被级联选中的父级与其下级) */
checked: TableRowRecord[]
/** 半选(通常为父级)节点的原始数据 */
indeterminate: TableRowRecord[]
/** 快捷字段:完全选中节点原始数据,等价于 checked */
selection: TableRowRecord[]
}5. 功能详解
5.1 百万级数据渲染(虚拟滚动)
不含原生 <table>,Body 基于行高做数学定位,仅渲染可视区间 + 缓冲行,滚动复杂度为 O(1)。
<XDataGrid
:columns="columns"
:data="100000Rows" <!-- 十万行即可流畅,百万级亦无压力 -->
row-key="id"
stripe
:loading="loading"
:skeleton="skeleton"
:density="'compact'"
:row-height="44"
/>| 子项 | 说明 |
| --- | --- |
| 虚拟滚动内核 | src/core/virtual,固定行高、O(1) 区间计算 |
| 缓冲行 | overscan 默认 8,滚动前后预渲染 |
| 大列数量 | 叶子列线性序列渲染,minWidth 支持剩余空间自适应分配 |
| 密度切换 | density:compact(0.85) / default(1) / loose(1.25) |
| 加载态 | loading 转圈或 skeleton 骨架屏(见 5.17) |
5.2 列配置与多级表头
叶子的 children 自动生成多级表头,宽度按子树叶子累计。
const columns = [
{ key: 'name', label: '姓名', width: 140 },
{
label: '成绩(多级)',
children: [
{ key: 'chinese', label: '语文', width: 110 },
{ key: 'math', label: '数学', width: 110 },
{ key: 'english', label: '英语', width: 110 }
]
}
]| 子项 | 说明 |
| --- | --- |
| 多级表头 | 任意层级嵌套,自动计算 colspan / rowspan |
| 表头插槽 | #header 与 #header-<key> 自定义 |
| 宽度分配 | width 优先;未设走 minWidth 自适应 |
| 列显隐 | hiddenColumns 控制隐藏集合 |
5.3 固定列 / 固定表头
const columns = [
{ key: 'name', label: '姓名', width: 140, fixed: 'left' },
{ key: 'age', label: '年龄', width: 100 },
// ...中间滚动列
{ key: 'action', label: '操作', width: 120, fixed: 'right' }
]| 子项 | 说明 |
| --- | --- |
| 固定列 | fixed: 'left' | 'right',sticky 定位 + 边缘阴影 |
| 固定表头 | fixed-header + 表头随 Body 横向滚动同步 |
| 边缘状态 | 贴近左/右边缘时自动隐藏固定阴影 |
5.4 树形表格与懒加载
通过 treeProps 指定 children 字段;懒加载场景用 lazy + loadChildren。
<XDataGrid :columns="columns" :data="treeData" row-key="id" :tree-props="treeProps">
</XDataGrid>const treeProps = {
children: 'children', // 子节点字段名(默认 'children')
hasChildren: 'hasChildren', // 存在子节点字段(懒加载占位判断)
lazy: true, // 是否懒加载
loadChildren: async (row) => {
const res = await fetch('/api/children?id=' + row.id)
return res.json()
}
}| 子项 | 说明 |
| --- | --- |
| 展开/收起 | 首列缩进 + 箭头,非叶行才显示 |
| 懒加载 | 顶层仅声明 hasChildren: true,展开时经 loadChildren 异步取子节点;已加载节点缓存,重复展开不重复请求 |
| 深度缩进 | 每级 --tbl-tree-indent 像素 |
| 树状态 | treeProps 完全受控,可配合 setTreeExpand 编程展开 |
| 树节点事件 | tree-toggle 回传 (row, expanded) |
5.5 选择(多选 / 单选 / 全选 / 树形级联)
普通扁平数据选择:
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
select-mode="multiple"
:selectable="row => !row.disabled"
@selection-change="onChange"
>
</XDataGrid>// selection-change 回调收到完整选择信息,区分「完全选中」与「半选」
function onChange({ checked, checkedKeys, indeterminate, indeterminateKeys, selection }) {
console.log('完全选中(含级联)节点数', checked.length)
console.log('半选父级数', indeterminate.length)
console.log('完全选中原始数据', checked)
}树形级联选择(默认关联模式):
<XDataGrid
:columns="columns"
:data="treeData"
row-key="id"
select-mode="multiple"
:default-checked-keys="[11]" <!-- 默认选中 11,自动连带选中下级并归约父级 -->
:check-strictly="false" <!-- 省略即默认:上下级关联选择 -->
@selection-change="onChange"
>
</XDataGrid>const gridRef = ref(null)
function readSelection() {
const info = gridRef.value?.getSelectionInfo() // 手动获取
console.log(info.checkedKeys, info.indeterminateKeys)
}| 子项 | 说明 |
| --- | --- |
| 多选/单选 | select-mode 切换,自动在首列追加 checkbox / radio(或显式配置 type:'selection'/'radio' 列) |
| 表头全选 | 多选模式表头全选控件,支持半选(indeterminate) |
| 行禁用 | selectable 返回 false 的行勾选禁用 |
| 跨页保留 | 选择基于行键 Set<Key> 存储,数据变换仍稳定 |
| 编程控制 | toggleRowSelection / getSelection / getSelectRowKeys / getSelectionInfo |
| 上下级关联 | 默认开启:子级全选 → 父级自动选中;父级选中 → 子级全选;子级部分选中 → 父级半选。与节点是否展开无关,未展开节点的状态同样正确同步 |
| 严格勾选 | :check-strictly="true" 后上下级独立勾选,点击哪个节点仅切换该节点,不联动、无半选 |
| 默认选中 | defaultCheckedKeys 指定初始选中节点键,自动应用关联规则 |
| 结果区分 | 回调 / getSelectionInfo() 均返回 SelectionInfo,含 checked(完全选中)与 indeterminate(半选)两组原始数据与键 |
5.6 排序
列配置 sortable: true 即显示上/下三角(点上=升序、点下=降序、再次点击清除)。
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
:default-sort="[{ key: 'amount', order: 'desc' }]"
:sort-mode="'local'" <!-- 本地排序(默认);remote 则触发事件由外层处理 -->
@sort-change="onSortChange"
>
</XDataGrid>function onSortChange({ key, order }) {
// order:'asc' 上箭头升序 / 'desc' 下箭头降序 / null 清除
}| 子项 | 说明 |
| --- | --- |
| 本地排序 | sortMode='local'(默认):内部对数据排序 |
| 远程排序 | sortMode='remote':仅触发 sort-change,数据由外层请求后回填 |
| 默认排序 | defaultSort 初始条件 |
| 自定义排序 | 列 sorter 函数(优先级最高)或 sortable:'custom' |
| 编程控制 | sortBy(key, order) / clearSort() / getSort() |
5.7 列筛选
列配置 filterable: true 即显示筛选图标,点击弹出候选下拉。
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
:filter-mode="'local'" <!-- 本地筛选(默认);remote 触发事件由外层处理 -->
:column-filters="[{ key: 'city', values: ['北京'] }]"
@filter-change="onFilterChange"
>
</XDataGrid>const columns = [
{
key: 'city', label: '城市',
filterable: true,
filterOptions: ['北京', '上海', '广州'], // 候选;缺省取该列全部去重值
filterMultiple: true // 多选筛选
}
]
function onFilterChange({ key, filters }) {
// key:触发筛选的字段;filters:当前全部筛选条件
}| 子项 | 说明 |
| --- | --- |
| 本地筛选 | filterMode='local'(默认):内部过滤数据 |
| 远程筛选 | filterMode='remote':仅触发 filter-change |
| 候选选项 | filterOptions 自定义(缺省去重取值) |
| 单/多选 | filterMultiple 多选 |
| 编程控制 | clearFilter() / getFilter() |
5.8 分页
提供 pagination 对象即启用分页。
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
:pagination="{ pageSize: 20, pageSizes: [10, 20, 50], current: 1 }"
@page-change="onPageChange"
>
</XDataGrid>| 子项 | 说明 |
| --- | --- |
| 本地分页 | 未设 remote 时内部切片,total 自动取数据条数 |
| 远程分页 | pagination.remote = true + total 由外层提供,翻页触发 page-change |
| 页大小切换 | pageSizes 下拉,触发 page-change |
| 滑窗页码 | 常规分页与「动态加载」分页统一:页码始终最多 7 个(当前页 ±2 + 首尾页),超出自动折叠为可点击省略号,点击窗口向该侧滑动,布局稳定不随页数增多而变形 |
| 页码 tooltip | 悬停页码/省略号弹出带三角箭头的浮层(复用 tip 视觉),指向所悬停的页码 |
| 编程控制 | setCurrentPage(page) / setPageSize(size) |
| 页码持久化 | 配置 id 后,当前页码 / 每页条数经 IndexedDB 持久化;刷新 / 重进页面自动恢复上次页码(越界自动钳制;远程分页恢复时触发 page-change 由外层重新拉取)。数据不落库,避免占用过多存储 |
| 加载方式持久化 | 配置 id 后在「列设置」抽屉切换的「数据加载方式(分页 / 动态加载)」同样经 IndexedDB 持久化;刷新后自动恢复加载方式,并触发 load-mode-change 供外层按新方式处理 |
id 同时用于列设置缓存(5.15)与分页页码持久化——二者以独立子键存储,互不冲突:
<XDataGrid
id="order-table"
:columns="columns"
:data="data"
row-key="id"
:pagination="{ pageSize: 20, pageSizes: [10, 20, 50] }"
@page-change="onPageChange"
>
</XDataGrid>5.9 表尾合计统计行
showFooter 开启合计行;两种配置来源:
A. 可配置统计行(推荐):footerSummary 支持多字段、多统计方式:
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
show-footer
:footer-summary="footerSummary"
>
</XDataGrid>const footerSummary = {
label: '合计',
fields: [
{ field: 'amount', formatter: (v) => '¥' + Number(v).toLocaleString() }, // 默认求和
{ field: 'progress', method: 'average' }, // 平均
{ field: 'id', method: { type: 'sum', multiplier: 0.5 } }, // 系数求和(每行值 × 0.5 再累积)
{ field: 'status', fn: (rows) => rows.filter((r) => r.status === '已完成').length } // 自定义函数
]
}B. 列级配置(旧):列 summable: true 默认求和;或传入 { type, custom, formatter } 聚合对象。
| 子项 | 说明 |
| --- | --- |
| 统计方式 | sum 求和(默认)/ average 平均 / count 计数 / { type, multiplier } 系数求和 / fn 自定义函数 |
| 格式化 | 每个字段独立 formatter |
| 标签 | label 配置统计行名称(默认「合计」) |
| 横向联动 | 统计行独立横向滚动,与表体/表头 scrollLeft 同步,列宽对齐 |
5.10 单元格编辑
列配置 editable: true + 组件 editable 启用后,双击单元格进入编辑(回车提交 / 失焦提交 / Esc 取消)。
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
editable
@edit-change="onEditChange"
>
</XDataGrid>const columns = [
{
key: 'amount', label: '金额(元)',
editable: true,
editType: 'number', // 数字输入,提交自动 Number()
editRules: (v) => Number.isFinite(Number(v))
? { pass: true }
: { pass: false, message: '请输入数字' } // 校验失败不入库
}
]
function onEditChange({ row, column, value, trigger }) {
// trigger:'double-click' 进入 / 'enter' 回车 / 'blur' 失焦
}| 子项 | 说明 |
| --- | --- |
| 触发方式 | 双击进入,回车 / 失焦提交,Esc 取消 |
| 校验规则 | editRules 静态对象或函数,返回 { pass, message? };校验失败不入库 |
| 输入类型 | editType:text / number(提交自动转 Number) |
| 提交事件 | edit-change 回传 { row, column, value, trigger } |
5.11 工具栏
提供 toolbar 对象即显示顶部工具栏(零依赖,苹果风描边按钮)。
<XDataGrid
ref="tableRef"
:columns="columns"
:data="data"
row-key="id"
:toolbar="toolbar"
@toolbar-search="onToolbarSearch"
@toolbar-action="onToolbarAction"
>
<template #toolbar-title>订单明细</template>
</XDataGrid>import 导出Icon from '@/icons/export.svg?raw' // ?raw 内联 SVG 作为图标
const toolbar = reactive({
columnSetting: true, // 列设置入口(打开全局抽屉)
search: true, // 搜索按钮(触发 @toolbar-search)
buttonNames: true, // 按钮名开关(控制操作按钮文字显隐)
actions: [
{
key: 'export', label: '导出', icon: 导出Icon, iconSize: 12,
loading: () => exporting.value, // 响应式 loading(旋转图标 + 禁用)
onClick: async (action, event) => { /* 自定义点击,替代全局 toolbar-action */ }
},
{
key: 'delete', label: '删除',
icon: () => h('svg', { viewBox: '0 0 24 24' }, [h('path', { d: '...' })]),
disabled: () => checkedKeys.value.length === 0, // 未勾选即禁用
onClick: (action, event) => alert('删除')
}
]
})
// 搜索按钮:使用方自行实现交互(点击后展开本地搜索栏)
function onToolbarSearch() {
searchBar.value = !searchBar.value
tableRef.value?.setSearch(keyword.value) // 本地关键词过滤(可不清除时传 '')
}| 子项 | 说明 |
| --- | --- |
| 列设置 | 图标按钮打开列设置抽屉(默认挂载于表格容器内成整体单元;columnMount="body" 变全屏全局抽屉) |
| 搜索 | 触发 toolbar-search 事件,交互由使用方实现 |
| 按钮名开关 | 控制所有操作按钮文字显隐(状态持久化) |
| 操作按钮 | actions:图标(组件 / 渲染函数 / 内联 SVG 源码 / 全局注册名)+ 名称 |
| 响应式状态 | disabled / loading 支持函数式响应判定 |
| 自定义点击 | onClick 提供后替代全局 toolbar-action |
| 移动端折叠 | 按钮过多时前 3 个直接展示,其余收进「更多」⋯ 弹出面板 |
| 插槽 | #toolbar-title / #toolbar-actions |
5.12 提示框 Tooltip
悬停单元格弹出 Teleport 到 body 的暗色卡片浮层,智能定位(贴边自动翻转 + 方向箭头),不超出表格容器与视口。支持四种配置方式:
<XDataGrid :columns="columns" :data="data" row-key="id">
<!-- 方式四(优先级最高):插槽自定义浮层内容 -->
<template #tip-name="{ row }">
<div>{{ row.name }} · {{ row.city }} · {{ row.desc }}</div>
</template>
</XDataGrid>const columns = [
{ key: 'name', label: '姓名', width: 100 },
{
key: 'remark', label: '备注', minWidth: 180,
showOverflow: 'tooltip', // ① 溢出省略,悬停显示全文 + 一键复制
},
{
key: 'desc', label: '描述', width: 110,
showTooltip: true, // ② 强制开启,未截断也显示
},
{
key: 'tip', label: '提示', width: 130,
tipRenderer: ({ row }) => // ③ 列级渲染函数返回自定义浮层
h('div', { style: 'display:grid;gap:4px' }, [h('span', row.tip), h('span', row.name)]),
}
]| 子项 | 说明 |
| --- | --- |
| 溢出全文 | showOverflow:'tooltip',省略时弹出全文 + 一键复制 |
| 强制开启 | showTooltip: true,未截断也显示 |
| 渲染函数 | tipRenderer 列级函数返回自定义 VNode |
| 插槽 | #tip-<key> / #tip 作用域插槽(优先级最高) |
| 自适应列 | 未设 width 的自适应列默认开启溢出提示 |
| 智能定位 | 贴边自动左右对齐、上下翻转、方向箭头,约束在表格容器与视口内 |
5.13 单元格合并
通过 spanMethod 返回 { rowspan, colspan }。连续同名合并需自行扫描数据。
function spanMethod({ rowIndex, columnIndex, rowCount, columnCount, row }) {
if (columnIndex === 1) {
return { rowspan: groupSpan(rowIndex) } // 返回 0 表示被前一行吞噬
}
if (columnIndex === 4) {
return { colspan: 2 } // 横向合并(顶端对齐)
}
return undefined
}| 子项 | 说明 |
| --- | --- |
| 跨行合并 | rowspan 垂直合并 |
| 跨列合并 | colspan 水平合并(顶端对齐) |
| 行高自适应 | 合并行高按 rowspan × 行高 计算 |
| 上下文 | row / rowIndex / columnIndex / rowCount / columnCount |
5.14 展开行
两种触发方式:
- 显式展开列:配置
{ type: 'expand' }列,行内出现「展开/收起」按钮; - 隐式触发:
:expandable="true"且未配置 expand 列时,首个数据列自动挂上展开箭头。
<XDataGrid
ref="gridRef"
:columns="columns"
:data="data"
row-key="id"
expandable
@expand-change="(row, expanded) => { if (expanded) loadDetail(row) }"
>
<!-- 展开行全宽内容:订单明细 / 子表 / 图表等(高度自适应插槽内容) -->
<template #expand-row="{ row }">
<div class="expand-detail">
<div>订单号:{{ row.order }}</div>
<div v-for="d in row.detail" :key="d.goods">{{ d.goods }} ×{{ d.qty }} ¥{{ d.price }}</div>
</div>
</template>
</XDataGrid>const columns = [
{ type: 'expand', width: 60, align: 'center' }, // 展开按钮列
{ key: 'order', label: '订单号', width: 150 },
{ key: 'amount', label: '金额(元)', width: 140, align: 'right' },
]
// 展开回调:可在此动态调用接口更新插槽数据(row 为引用,赋值即触发插槽更新)
async function loadDetail(row) {
const detail = await fetchDetail(row.id) // 模拟请求接口
row.detail = detail // 更新后高度自动重新测量
}| 子项 | 说明 |
| --- | --- |
| 展开列 | type: 'expand' 独立按钮列 |
| 隐式触发 | expandable 未配置 expand 列时,首个数据列自动挂展开箭头 |
| 展开行插槽 | #expand-row 全宽内容(渲染为独立虚拟行插入坐标,滚动/合并统一计算) |
| 单行展开 | 同一时刻仅一行可展开:展开新行自动收起上一行(无需批量方法) |
| 高度自适应 | 展开行高度随插槽内容自动测量排布(虚拟滚动按实际高度计算) |
| 展开事件 | expand-change 回传 (row, expanded);展开时可动态加载接口数据更新插槽 |
5.15 列设置与缓存
通过工具栏「列设置」入口打开列设置抽屉:复选框控制字段显隐、拖拽调整非固定字段顺序(固定列锚定不可拖)。
抽屉挂载位置由顶层 columnMount 配置(默认 'container'):
'container'(默认):挂载到表格容器内部,遮罩/面板局限于表格区域,与表格形成一个整体单元;'body':挂载到<body>,全屏全局抽屉。
<XDataGrid
ref="gridRef"
id="demo-columns" <!-- 配置 id 启用 IndexedDB 缓存 -->
:columns="columns"
:data="data"
row-key="id"
:column-mount="'container'"
:toolbar="{ columnSetting: true }"
>
</XDataGrid>// 实例方法控制列显隐与顺序
gridRef.value?.hideColumn('status') // 隐藏某列
gridRef.value?.showColumn('status') // 显示某列
gridRef.value?.setHiddenColumns(['status']) // 批量隐藏
gridRef.value?.setColumnOrder(['id', 'name', 'amount', 'city']) // 列顺序| 子项 | 说明 |
| --- | --- |
| 列设置抽屉 | 勾选显隐 + 拖拽排序,固定列锚定不可拖 |
| 挂载位置 | columnMount:'container'(默认)表格容器内部成整体单元 / 'body' 全屏全局抽屉 |
| IndexedDB 缓存 | 仅配置 id 时启用;列顺序 / 显隐自动持久化,刷新页面保留 |
| 缓存失效 | 缓存版本 + 列配置签名双重校验:程序升级或 columns 结构变化时自动失效,恢复初始化 |
| 重置 | 抽屉「重置」按钮清除缓存并恢复初始化 |
5.16 远程数据模式
sort-mode='remote'、filter-mode='remote'、pagination.remote=true 时,组件只触发事件,数据由外层请求后回填。
<XDataGrid
:columns="columns"
:data="pageRows"
row-key="id"
:loading="loading"
:sort-mode="'remote'"
:filter-mode="'remote'"
:pagination="pagination"
:default-sort="[{ key: 'amount', order: 'desc' }]"
:column-filters="[{ key: 'city', values: ['北京'] }]"
@sort-change="onSortChange"
@filter-change="onFilterChange"
@page-change="onPageChange"
>
</XDataGrid>const pagination = ref({ pageSize: 20, current: 1, remote: true, total: 0 })
async function fetchData({ sort, filters, page, pageSize }) {
loading.value = true
const res = await api.query({ sort, filters, page, pageSize })
pageRows.value = res.list
pagination.value.total = res.total
loading.value = false
}
function onSortChange({ sort }) {
pagination.value.current = 1
fetchData({ sort, filters, page: 1, pageSize: pagination.value.pageSize })
}
function onPageChange({ current, pageSize }) {
fetchData({ sort, filters, page: current, pageSize })
}| 子项 | 说明 |
| --- | --- |
| 远程排序 | sortMode='remote':仅触发 sort-change |
| 远程筛选 | filterMode='remote':仅触发 filter-change |
| 远程分页 | pagination.remote=true + total 由外层提供 |
| 初始条件 | defaultSort / columnFilters 设置首屏条件 |
| 加载态 | 请求期间 loading 展示(可配 skeleton) |
5.17 加载态 / 骨架屏 / 空态
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
:loading="loading"
:skeleton="skeleton" <!-- true 时 loading 显示骨架屏(默认转圈) -->
empty-text="暂无数据" <!-- 空数据文案(默认内置图标+文案的美化空态) -->
>
<template #empty> <!-- 自定义空态:表头保留,仅表体内容区为空 -->
<div style="text-align: center; padding: 24px">
<p>暂无关联数据</p>
<button @click="loadData">加载示例数据</button>
</div>
</template>
</XDataGrid>| 子项 | 说明 |
| --- | --- |
| 加载转圈 | loading=true 默认展示加载中遮罩(不占据布局空间) |
| 骨架屏 | loading + skeleton 展示 6 行骨架占位 |
| 空态 | 无数据时表头保留,仅表体内容区为空;默认展示与表格风格一致的图标 + emptyText 文案 |
| 自定义空态 | #empty 插槽完全自定义(可内置图标 / 标题 / 描述 / 快捷按钮等交互元素,空态区域自动开启点击) |
5.18 移动端卡片模式
容器宽度 ≤ mobileBreakpoint 时自动切换为卡片逐条浏览(按容器宽度而非屏幕宽度判定)。默认断点 500px,可通过 mobile-breakpoint 自定义。
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
:mobile-breakpoint="900"
:card-props="cardProps"
:toolbar="toolbar"
:pagination="pagination"
@card-change="onCardChange"
>
</XDataGrid>const cardProps = {
fields: ['name', 'product', 'amount', 'city', 'status'], // 展示顺序
labelMap: { amount: '金额(元)', product: '商品名称' }, // 自定义标签
visibleFields: ['id', 'name', 'city', 'product', 'amount', 'status'] // 字段过滤
}
function onCardChange({ index, row }) {
console.log('当前卡片:', index + 1, row)
}| 子项 | 说明 |
| --- | --- |
| 断点切换 | 容器宽度 ≤ 断点(默认 500,可配 mobile-breakpoint)自动切换 |
| 卡片呈现 | 一行数据 = 一张卡,字段「标签-值」列转行;合并记录以整组呈现 |
| 手势导航 | 按下后左滑 / 右滑切换上一条 / 下一条(带动画) |
| 字段配置 | fields 排序 / visibleFields 过滤 / labelMap 标签 |
| 工具栏适配 | 按钮过多自动收进「更多」⋯;分页条压缩为「上一页 / 下一页 + 条数」 |
| 卡片切换事件 | card-change 携带 { index, row } |
5.19 密度 / 斑马纹 / 行高 / 边框
<XDataGrid
:columns="columns"
:data="data"
row-key="id"
:density="'compact'"
stripe
:row-height="44"
:show-row-hover="true"
:border="true"
>
</XDataGrid>| 子项 | 说明 |
| --- | --- |
| 密度 | compact(0.85) / default(1) / loose(1.25) 行高倍率 |
| 斑马纹 | stripe 偶数行底色 |
| 行悬停 | showRowHover 控制高亮 |
| 边框 | border 控制单元格分隔线 |
| 行高 | rowHeight 基准行高(默认 44,实际 = 行高 × 密度倍率) |
5.20 事件与实例方法
完整事件见 4.12 事件,实例方法见 4.13 实例方法。典型组合使用:
<XDataGrid
ref="gridRef"
:columns="columns"
:data="data"
row-key="id"
@row-click="(row, index, evt) => …"
@row-dbl-click="(row, index, evt) => …"
@header-click="(column, evt) => …"
@scroll="({ scrollLeft, scrollTop }) => …"
@selection-change="(info) => …"
@sort-change="({ key, order }) => …"
@edit-change="({ row, column, value }) => …"
@expand-change="(row, expanded) => …"
>
</XDataGrid>const gridRef = ref(null)
gridRef.value?.scrollTo(60) // 滚动到第 60 个虚拟行
gridRef.value?.toggleRowSelection(row, true) // 切换某行选择
gridRef.value?.setCurrentPage(2) // 设置当前页
gridRef.value?.sortBy('amount', 'desc') // 按字段排序
gridRef.value?.setColumnWidth('name', 220) // 更新列宽
gridRef.value?.clearSort() // 清空排序
gridRef.value?.clearFilter() // 清空筛选5.21 滚动到底部加载更多(load-more)
滚动条滚动到距底部阈值内自动触发回调,动态查询下一页数据并追加到 data,自动累加形成大表格数据(虚拟滚动承载,累计万级 / 十万级仍流畅)。该模式下自动隐藏分页条,无需「下一页」按钮。
<XDataGrid
:columns="columns"
:data="rows"
row-key="id"
:load-more="loadMore"
@load-more="fetchNextPage"
>
</XDataGrid>const rows = ref([])
const loadMore = ref({ loading: false, finished: false, threshold: 60, total: 400, pageSize: 40 })
async function fetchNextPage() {
if (loadMore.value.finished || loadMore.value.loading) return
loadMore.value.loading = true
const chunk = await api.queryNextPage() // 动态查询下一页
rows.value.push(...chunk) // 追加到 data,自动累加大表格
loadMore.value.loading = false // 置回 false 才允许再次触发
if (noMore) loadMore.value.finished = true // 无更多数据,停止触发并显示「已加载全部数据」
}| 子项 | 说明 |
| --- | --- |
| 触发方式 | 滚动条滚动到距底部 threshold(默认 40)px 内触发 load-more 回调 |
| 防重复 | 触发一次后置 pending,loadMore.loading 置回 false(或数据条数增长)后复位,才允许再次触发 |
| 底部提示 | loading 为 true 时底部显示「加载中…」提示条;finished 为 true 时显示「已加载全部数据」并停止触发 |
| 隐藏分页 | 提供 loadMore 后自动不渲染分页条,无需「下一页」按钮 |
| 页码窗口 | 页码始终最多展示 7 个(含首尾页),超出自动折叠为省略号,布局稳定不随页数增多而变形 |
| 滑动省略号 | 折叠的省略号可点击:每次点击窗口向该侧滑动一段以到达更远页码;已加载页码可点击滚动、未加载页码置灰禁点 |
| 已加载页码 | 已加载页(主色浅底):点击后滚动条滚动到该页首行 |
| 未加载页码 | 未加载页(虚线描边)置灰(disabled)禁止点击,滚动加载到该页后自动变为可点击 |
| 大表支持 | 数据不断追加由虚拟滚动承载,累计大表格数据依旧流畅 |
该进度分页条仅桌面表格模式展示(移动端卡片为顺序浏览)。数据无需持久化,加载进度随会话即时计算。
5.21.1 数据加载方式切换
当配置了 pagination(分页信息)后,除了固定的远程分页显示外,还可以在「列设置」抽屉中默认提供「数据加载方式」切换,让使用方在「分页显示」与「动态加载显示」之间自由切换:
- 分页(
page,默认):常规分页显示,点击页码/上一页/下一页触发page-change{current,pageSize},外层按页替换数据; - 动态加载(
scroll):底部展示「加载进度分页条」,滚动条滚动到距底部 40px 内触发page-change{current,pageSize}(与分页回调完全一致),外层按页追加数据并自动累积为大表格(虚拟滚动承载);已加载页码可点击滚动到该页首行、未加载页码置灰禁点。重置/清空数据后,已加载的页码随同归零(起始仅第 1 页已加载)。
<XDataGrid
:columns="columns"
:data="rows"
row-key="id"
:pagination="pagination" <!-- 配置分页信息即可在「列设置」抽屉切换加载方式 -->
:load-mode="loadMode" <!-- 'page' 分页显示 / 'scroll' 动态加载(默认 'page') -->
:toolbar="{ columnSetting: true }"
@page-change="onPageChange" <!-- 统一回调(两种模式一致) -->
@load-mode-change="onLoadModeChange" <!-- 抽屉切换加载方式时触发 -->
>
</XDataGrid>const pagination = { pageSize: 40, total: 4000, remote: true }
const loadMode = ref('scroll') // 默认动态加载,可经抽屉切换
function onPageChange({ current }) {
const chunk = api.queryPage(current) // 按页查询
if (loadMode.value === 'scroll') {
rows.value.push(...chunk) // 动态加载:追加累积
} else {
rows.value = chunk // 分页:按页替换
}
}
function onLoadModeChange(mode) {
loadMode.value = mode
loadFirstPage() // 切换后回到第 1 页重新加载
}
// 重置:清空数据后已加载页码随同归零(起始仅第 1 页已加载)
function reset() {
rows.value = []
loadFirstPage()
}| 子项 | 说明 |
| --- | --- |
| 开关入口 | 配置 pagination 且未配置 loadMore 时,「列设置」抽屉自动出现「数据加载方式」切换(分页 / 动态加载) |
| 统一回调 | 两种模式均通过 @page-change="{ current, pageSize }" 回调;外层据 loadMode 决定替换还是追加数据 |
| 切换复位 | 抽屉里切换方式时回到第 1 页并重新加载;@load-mode-change="(mode) => ..." 可感知切换 |
| 切换持久化 | 配置 id 后,切换的加载方式经 IndexedDB 持久化,刷新 / 重进页面自动恢复到上次选择的方式 |
| 动态加载 | 滚动到底部触发 page-change,数据由外层每次追加累积,虚拟滚动承载大表格,数据量达到 pagination.total 即停止 |
| 加载进度条 | scroll 模式下底部展示与常规分页条一致的「加载进度分页条」;页码窗口 ≤ 7、可点击省略号滑动,已加载页可点击滚动、未加载页置灰禁点 |
| 重置归零 | 动态加载模式下数据被清空/减少(重置)时,已加载页码随同归零,起始仅第 1 页已加载、可从头重新滚动加载 |
5.22 对象字段(点路径 key)
列 key 支持「点路径」形式,直接读取行数据中的嵌套对象字段。数据为 { name: '张三', chengjie: { yw: 92, sx: 88 } } 时,配置 { key: 'chengjie.yw' } 即读取 chengjie.yw 的值。
// 数据
const rows = ref([
{ id: 1, name: '张三', chengjie: { yw: 92, sx: 88, yy: 95 } },
{ id: 2, name: '李四', chengjie: { yw: 78, sx: 96, yy: 82 } },
])
// 列配置:key 使用点路径读取嵌套对象字段
const columns = [
{ type: 'index', label: '#', width: 60, align: 'center' },
{ key: 'name', label: '学生', width: 120, fixed: 'left', align: 'center' },
{ key: 'chengjie.yw', label: '语文', width: 110, align: 'center', sortable: true, editable: true, summable: true },
{ key: 'chengjie.sx', label: '数学', width: 110, align: 'center', sortable: true, editable: true, summable: true },
{ key: 'chengjie.yy', label: '英语', width: 110, align: 'center', sortable: true, editable: true, summable: true },
]<XDataGrid :columns="columns" :data="rows" row-key="id" :show-footer="true" editable>
</XDataGrid>| 子项 | 说明 |
| --- | --- |
| 取值显示 | 单元格自动按点路径读取嵌套对象字段渲染 |
| 编辑写回 | 双击编辑后按点路径写回嵌套对象(缺失中间节点自动创建) |
| 排序 / 筛选 | 排序、列筛选按点路径取值比较 / 过滤 |
| 表尾合计 | summable / footer-summary 字段同样支持点路径 |
| 移动端卡片 | 卡片标题与字段取值均支持点路径 |
| 全局搜索 | 工具栏搜索对点路径列同样命中 |
| 点路径插槽 | 点路径列的插槽名同样可用,如 #chengjie.yw="{ row }" |
5.23 数组对象自动合并
把「数组嵌套对象」数据按数组项展开为多子行,并对非数组合并列做纵向 rowspan 合并,适合「一对多」明细(如一个订单多行商品、一位学生多个科目)。
数据结构:
const rows = ref([
{ id: 1, name: '张三', cls: '一班', cj: [
{ xk: '语文', cj: 90 },
{ xk: '数学', cj: 100 },
] },
{ id: 2, name: '李四', cls: '一班', cj: [{ xk: '英语', cj: 88 }] },
])列配置:数组列用「点路径」key,首个段为数组字段,其余段为数组项字段。
const columns = [
{ key: 'name', label: '姓名', width: 110, fixed: 'left' },
{ key: 'cls', label: '班级', width: 90 },
{ key: 'cj.xk', label: '学科', width: 120 }, // 数组列:展开后逐项显示学科
{ key: 'cj.cj', label: '成绩', width: 90 }, // 数组列
{ key: 'remark', label: '备注', minWidth: 160 },
]启用(全量合并):
<XDataGrid :columns="columns" :data="rows" row-key="id" :array-merge="true" />启用(仅合并姓名列,其余非数组列逐子行重复展示):
<XDataGrid :columns="columns" :data="rows" row-key="id" :array-merge="{ mergeColumns: ['name'] }" />| 子项 | 说明 |
| --- | --- |
| 自动识别 | 由示例数据 + 点路径 key 自动判定数组列与父字段;无需额外声明 |
| 显式声明 | 数据为空或无法自动识别时,对数组列配置 arrayField: true 兜底 |
| 全量合并 | arrayMerge=true 对全部非数组合并列做 rowspan 纵向合并 |
| 按列合并 | arrayMerge={ mergeColumns: ['name'] } 仅合并指定列,其余列逐子行展示 |
| 组级列 | 序号 / 选择(复选框 / 单选)列代表整条记录,始终随数组展开整组合并,不受 mergeColumns 限制 |
| 斑马纹 | 合并组视为一行着色:同一组内所有子行背景一致,不再因展开子行交替条纹 |
| 多父字段 | 多个数组父字段按同下标「zip」展开(同一组内下标对应) |
| 段值读取 | 展开子行中父字段替换为对应数组项对象,点路径 cj.xk 自然命中项内字段 |
| 空数组 | 数组为空时保留 1 行占位(表头仍展示,非数组合并列不纵向合并) |
| 虚拟滚动 | 展开后的子行进入统一虚拟行坐标,百万级明细仍保持流畅 |
| 单元格能力 | 数组列上排序 / 筛选 / 提示框 / 列模板 / 插槽均有效 |
| 移动端卡片 | 卡片模式下,每个「展开组」自动合并为一张卡片,滑动或底部按钮切换 |
| 唯一行键 | 展开子行自动注入内部唯一键(源行键 + 下标),选择 / 展开互不串扰 |
5.24 操作列与列自定义插槽
A. 操作列(actions)
在列配置 columns 中某列的 actions 数组声明操作按钮(TableAction)。最适合「详情 / 修改 / 删除 / 审批」等业务操作;按钮可基于当前行数据做权限显隐(visible)与状态禁用(disabled)。
<XDataGrid :columns="columns" :data="rows" row-key="id" @action="onAction" />
<script setup>
const columns = [
{ key: 'name', label: '姓名' },
{
key: 'ops',
label: '操作',
fixed: 'right',
actionLimit: 2, // 最多直接展示 2 个(默认即 2),超出收进「更多 ⋯」
actions: [
{ key: 'detail', label: '详情' }, // 触发统一 @action
{ key: 'edit', label: '修改', type: 'primary',
onClick: (row) => openEdit(row) }, // 自带点击逻辑
{ key: 'approve', label: '审批', type: 'success',
visible: (row) => row.status === '进行中' }, // 权限 / 行判定
{ key: 'remove', label: '删除', type: 'danger',
disabled: (row) => row.status === '已完成', // 状态禁用
onClick: (row) => confirm('确认删除?') || false }, // false 阻止事件
{ key: 'copy', label: '复制' },
],
},
]
function onAction({ key, row }) {
// 未配置 onClick(或返回非 false)的按钮统一在此处理
console.log(key, row)
}
<\/script>| 子项 | 说明 |
| --- | --- |
| visible | 权限 / 显隐判定(返回 false 隐藏),典型按角色 / 状态控制 |
| disabled | 布尔或基于当前行函数(返回 true 禁用置灰,禁用态不触发事件) |
| type | default / primary / success / warning / danger,影响按钮配色 |
| icon | 按钮图标(置于文字之前):内联 SVG 字符串 / 渲染函数(返回 VNode)/ Vue 组件 / 全局注册图标名 |
| onClick | 传入当前行的点击回调;返回 false(或异步 resolve false)阻止统一 @action |
| actionLimit | 列级展示的控件数量上限,默认 2:可见按钮超出时收起为「前 actionLimit-1 个按钮 + 「更多」下拉」 |
| 折叠体验 | PC 可见按钮不超过 actionLimit 时全量平铺;超出则显示「前 actionLimit-1 个按钮 + 「更多」下拉触发按钮」(文本 + 箭头,点击展开,点击外部自动收起) |
| 移动端 | 卡片模式下 actions 列自动适配为卡片底部操作条,全部按钮平铺、可换行,点击不触发翻页 |
| 事件 | 未拦截的按钮点击触发统一 @action({ key, row, event }) |
| 列级插槽覆盖 | 若操作列还配置了 slotName / 插槽名,且模板提供了对应自定义插槽,则整个操作列交由插槽完全渲染(不再渲染按钮组),用于高度自定制的操作 UI |
| 数组合并 | 操作列属「非数组合并列」,在数组展开时整组合并(整组共享一组操作按钮) |
B. 列自定义插槽(slotName)
列默认以 key 作为单元格插槽名。配置 slotName 后,该列改用 #<slotName> 插槽渲染任意自定义内容(图片、徽章、混合富文本等),未提供对应插槽时回落到列 key 取值。
<XDataGrid :columns="columns" :data="rows" row-key="id">
<template #avatar="{ row }">
<img class="avatar" :src="row.avatar" />
<span>{{ row.name }}</span>
</template>
<template #status="{ row }">
<span class="tag" :class="`is-${row.tag}`">{{ row.status }}</span>
</template>
</XDataGrid>
<script setup>
const columns = [
{ key: 'name', label: '姓名', slotName: 'avatar' }, // 用 #avatar 插槽渲染头像
{ key: 'status', label: '状态', slotName: 'status' }, // 用 #status 插槽渲染徽章
]
<\/script>| 子项 | 说明 |
| --- | --- |
| 插槽名 | 配置 slotName 后使用 #<slotName>;缺省用列 key |
| 作用域 | 插槽作用域 { row, column, $index, $rowIndex },可直接取当前行数据 |
| 优先级 | 插槽 > slotName 对应插槽 > 列 cellRender > 默认取值 |
| 富内容 | 不限于纯文本,可渲染图片 / 徽章 / 按钮等任意 VNode |
| 移动端卡片 | cardProps.fields 不显式排除时,插槽列按列序展示;操作列自动变为操作条,不作为普通字段 |
6. 主题定制(CSS 变量)
所有主题通过 CSS 变量(--tbl-*)暴露,可全局覆盖或按 .tbl-root 作用域覆盖。字体渲染沿用 Element UI 默认策略,不强制自定义字体栈。
.tbl-root {
--tbl-primary-color: #409eff; /* 品牌色 */
--tbl-header-bg: #f5f7fa; /* 表头背景 */
--tbl-body-bg: #ffffff; /* 表体背景 */
--tbl-row-hover-bg: #f5f7fa; /* 行悬停 */
--tbl-row-selected-bg: #ecf5ff; /* 选中行 */
--tbl-stripe-even-bg: #fafafa; /* 斑马纹 */
--tbl-border-color: #ebeef5; /* 分割线 */
--tbl-text-color: #303133; /* 主文本 */
--tbl-text-secondary-color: #909399;/* 次要文本 */
--tbl-font-size: 14px; /* 字号 */
--tbl-cell-padding: 0 12px; /* 单元格内边距 */
--tbl-fixed-shadow: 6px 0 12px -6px rgba(0,0,0,.15); /* 左固定阴影 */
--tbl-fixed-shadow-inv: -6px 0 12px -6px rgba(0,0,0,.15); /* 右固定阴影 */
--tbl-control-selection-w: 48px; /* 选择列宽 */
--tbl-control-expand-w: 48px; /* 展开列宽 */
--tbl-control-index-w: 56px; /* 序号列宽 */
--tbl-tree-indent: 16px; /* 树缩进/级 */
--tbl-action-icon-size: 14px; /* 工具栏操作按钮图标尺寸 */
}6.1 主题颜色可配置(API)
交互元素(复选框 / 单选 / 开关 / 排序 / 筛选 / 分页)的品牌色跟随 --tbl-primary-color,默认值保持组件蓝色 #409eff。通过「全局主题」或「实例 theme prop」两种方式配置,颜色变更对所有已渲染与新渲染的表格实时生效。
import { setGlobalTheme, getGlobalTheme, resetGlobalTheme } from 'table-grid-plugin'
// 全局主题:对所有表格(已渲染 + 新渲染)实时生效
setGlobalTheme({ primaryColor: '#f56c6c' }) // 传子集即局部覆盖
const snap = getGlobalTheme() // 读取当前全局主题快照
resetGlobalTheme() // 恢复组件默认蓝色<!-- 实例级覆盖:仅当前表格生效,优先级高于全局主题 -->
<XDataGrid :columns="columns" :data="rows" row-key="id"
:theme="{ primaryColor: '#67c23a' }" />ThemeTokens 各键(颜色即可配置):
| Token | CSS 变量 | 默认 | 说明 |
| --- | --- | --- | --- |
| primaryColor | --tbl-primary-color | #409eff | 品牌色:控制复选框 / 单选 / 开关 / 排序 / 筛选 / 分页高亮 |
| headerBg | --tbl-header-bg | #f5f7fa | 表头背景 |
| bodyBg | --tbl-body-bg | #ffffff | 表体背景 |
| rowHoverBg | --tbl-row-hover-bg | #f5f7fa | 行悬停背景 |
| rowSelectedBg | --tbl-row-selected-bg | #ecf5ff | 选中行背景 |
| stripeEvenBg | --tbl-stripe-even-bg | #fafafa | 斑马纹偶数行背景 |
| borderColor | --tbl-border-color | #ebeef5 | 分割线颜色 |
| textColor | --tbl-text-color | #303133 | 主文本颜色 |
| textSecondaryColor | --tbl-text-secondary-color | #909399 | 次要文本颜色 |
| fontSize | --tbl-font-size | 14px | 字号 |
| cellPadding | --tbl-cell-padding | 0 12px | 单元格内边距 |
| fixedShadow | --tbl-fixed-shadow | — | 固定列阴影 |
优先级:实例
themeprop(根节点内联)> 全局主题(注入<style>)> SCSS 默认变量。二者可叠加:全局设品牌色,单个表格用themeprop 局部覆盖。
7. 国际化(i18n)
7.1 文本词典翻译(推荐):翻表头 / 按钮 / 分页等公共内容
默认语言为中文,label 即 key:传入目标语言译文词典即可翻译表头 label 以及分页、按钮、空态等内置公共文案;表体数据不翻译。切回中文时直接显示 label(中文为「无效语言包」,无需也不应收录)。
// 词典:语言码 -> { 中文文本: 译文 }
const dict = {
en: { 姓名: 'Name', 部门: 'Department', 得分: 'Score', 评级: 'Grade' },
}import { setLocale, setTranslations, setSystemLocale, lang, tr } from 'table-grid-plugin'
setTranslations(dict) // 全局注册词典,所有表格实时生效
setLocale({ lang: 'en-US', autoSystem: false }) // 切英文(切中文→直显 label)
setSystemLocale(false) // 关闭系统语言自动跟随
setLocale({ lang: 'zh-CN', autoSystem: false }) // 回中文
tr('姓名') // 按当前语言翻译:'Name'(中文下返回'姓名')表格列使用纯中文 label(即翻译 key):
<XDataGrid
:columns="[{ key:'name', label:'姓名' }, { key:'dept', label:'部门' }]"
:data="rows" row-key="id"
/>表格级可用 translations prop 做实例覆盖(粒度最优先),便于同一页面多语言差异:
<XDataGrid :columns="columns" :data="rows" row-key="id"
:translations="{ en: { 姓名: 'Name', 部门: 'Department', 得分: 'Score', 评级: 'Rating' } }" />内置已预置一份「中→英」默认词典(分页 / 空态 / 列设置 / 搜索 / 合计等公共文案开箱即英文);用户
setTranslations/:translations词典优先覆盖。
7.2 切换机制与内置语言包(key 型,进阶)
默认语言为中文,且不自动跟随系统:只要未显式调用 setLocale / setSystemLocale(true),任何表格(含未配置语言包的表)都保持中文。需要跟随或切换时再手动控制。语言码 en-US/en、zh-CN/zh 均可,词典按短码命中。
import { setLocale, setSystemLocale, lang, getMessages } from 'table-grid-plugin'
setLocale({ lang: 'en-US' }) // 手动切换为英文(默认不跟随系统,见下)
setSystemLocale(false) // 关闭系统语言自动跟随(默认即为关闭,可完全手动控制)
setSystemLocale(true) // 显式开启自动跟随(无配置时仍会随系统语言切换)
setLocale({ lang: 'zh-CN', autoSystem: false }) // 回中文且不跟随系统表格级 locale prop 做 key 型实例覆盖,粒度最优先:
<XDataGrid :columns="columns" :data="rows" row-key="id"
:locale="{ empty: '暂无数据', total: '共 {n} 条' }" />7.3 语言包结构(内置 zh-CN / en-US 完整覆盖)
文本使用 {n} 占位符插值。全部消息键:
| 消息 key | zh-CN 示例 | en-US 示例 | 说明 |
| --- | --- | --- | --- |
| loading | 加载中… | Loading… | 加载态 |
| empty | 暂无数据 | No data | 空态文案 |
| loadMoreLoading | 加载中… | Loading… | load-more 加载中 |
| loadMoreFinished | 已加载全部数据 | All data loaded | load-more 已加载全部 |
| expand / collapse | 展开 / 收起 | Expand / Collapse | 树 / 展开行按钮 title |
| sortAsc / sortDesc | 升序 / 降序 | Ascending / Descending | 排序提示 |
| filter / filterClear | 筛选 / 清除 | Filter / Clear | 筛选 |
| columnSetting | 列设置 | Columns | 列设置抽屉标题 |
| search | 搜索 | Search | 工具栏搜索占位 |
| buttonNames / buttonNamesTooltip | 按钮文字 | Labels | 按钮文字开关 |
| more | 更多 | More | 溢出更多 |
| prevPage / nextPage | 上一页 / 下一页 | Previous / Next | 分页翻页 |
| pageSizeUnit | 条/页 | / page | 每页条数单位 |
| total | 共 {n} 条 | Total {n} | 分页总条数({n} 插值) |
| footerLabel | 合计 | Total | 表尾合计名称 |
| prevItem / nextItem | 上一项 / 下一项 | Previous / Next | 卡片切换 |
| reset | 重置 | Reset | 列设置重置 |
| close | 关闭 | Close | 关闭 |
| dragHint | 拖动左侧把手即可调整字段顺序 | Drag the left handle to reorder fields | 列设置拖拽提示 |
| fixedNotHidden | 固定列不可隐藏 | Fixed columns are always visible | 固定列提示 |
| fieldCount | {n} 个字段 | {n} fields | 字段个数({n} 插值) |
| expandRow | 展开内容 | Detail | 展开行兜底内容 |
语言包是按扁平 key 全量收录的完整对象;传入
Partial<LocaleMessages>即可增量覆盖,未覆盖项回退内置语言包默认文案(props.locale> 全局setLocale> 内置语言包)。
8. 浏览器兼容性
- 使用
Set、CSS 变量、sticky定位、ResizeObserver、IndexedDB。 - 支持所有现代浏览器(Chrome / Edge / Firefox / Safari,含移动端)。
- 不做 IE 兼容。
9. 常见问题 FAQ
Q1:为什么表格空白 / 总高度为 0?
虚拟滚动依赖容器高度。请为 <XDataGrid> 或外层设置确定高度,例如 style="height: 400px"。
Q2:rowKey 不传会怎样?
选择、树形、展开、合并、宽高追踪都依赖稳定的行键。若省略可能导致跨页选择错乱。必须提供。
Q3:cellRender 返回的 HTML 字符串没有渲染成富文本?
返回值为 VNode/文本,非 innerHTML。如需富文本请返回 h() 创建的 VNode,或使用插槽 + v-html。
Q4:表格列宽与容器不一致?
width 为固定占位;未设 width 的列按 minWidth 在剩余空间内自适应分配。可调用实例方法 recalcWidths() 在容器变化后重算。
Q5:数据量非常大时还卡顿吗?
Body 只渲染可视区间(约 视图高度/行高 + 2×overscan 行)。滚动只更新偏移量,不做重计算,十万级数据首屏与滚动均保持在毫秒级。
Q6:展开行怎么触发?
两种方式:配置 type:'expand' 列(行内按钮),或 :expandable="true" 且不配置 expand 列(首个数据列自动挂展开箭头)。内容经 #expand-row 插槽渲染。同一时刻仅一行可展开,展开行高度自适应插槽内容;@expand-change="(row, expanded)" 回调可在展开时动态加载接口数据更新插槽。
Q7:列设置如何持久化?
配置 id 属性后,用户调整的列顺序 / 显隐自动写入 IndexedDB,刷新页面保留;columns 配置结构变化或点击「重置」时自动失效。
