更新记录
2.0.0(2026-11-01)
核心引擎
- 纯 Canvas 2D 渲染,零 DOM 表格;行 + 主区列双虚拟化,2 万行流畅滚动。
- 固定列(左 / 右)与边缘阴影、冻结行、多级表头、汇总页脚。
- 排序(多列)、筛选、树形(含懒加载)、逻辑分页。
rowKey受控选择、合并单元格、变高行、自定义渲染器。- 惯性滚动 + 边界回弹;浅色 / 暗色主题 + 自定义色板。
- 高倍屏(dpr 2/3)按运行环境区分「位图已缩放 / 需自行缩放」,移动端不再只画出表格一角。
渲染与交互修正(首版发布前修复)
- H5 首屏不再「只渲染左上角一块」:uni-app 的 H5 canvas 上下文带
__hidpi__(平台已把坐标乘过 dpr),旧实现又额外setTransform(dpr),缩放叠加成 dpr²。现由contextScaled标记区分,原生端仍按 dpr。 - 固定列里的高亮不再消失:激活框 / 框选 / 查找命中改为按「左固定 / 主区 / 右固定」三条竖带逐段裁剪,既不越界也不画不出来。
- 被固定列盖住的主区列不再露出手柄:列宽手柄与拖拽指示线的绘制、命中共用同一份「本带内是否可见」判据。
- 冻结行上的行高拖拽命中正确的行(旧实现按屏幕坐标反查,改的是屏幕外那行)。
- 右侧固定列阴影只在右边还有内容时出现,无横向余量时不再常驻。
- 键盘方向键会把激活格横向滚入视口(此前只滚纵向),与查找的「滚入视口」收敛为一份实现。
- 单层绘制异常不再让整表被裁成一条:裁剪统一走
paint.withClip(),异常路径也保证restore。 - 窗口尺寸变化后文字不再上移:
canvas.width =会静默重置 2D 上下文(字体 / 基线 / 对齐回默认),而Paint.bind()因 ctx 引用未变而早退;现在位图尺寸变更后强制paint.resetCtx()重发状态。 - resize 不再闪一帧空白:canvas 尺寸只在 render 帧首修改,避免「清空画布 → 下一帧才重绘」的中间透明白帧。
主题
- 可覆盖字段 37 个,全部生效;
theme传null= 撤掉全部覆盖,切dark同理。 - 暗色滚动条按主题灰阶重做:轨道取表头色半透明(贴合任意行色,不再出现比行更黑的竖条),滑块
#5d6875、拖拽态#93a2b3,与正文形成四级递进。 - 暗色 tooltip 浮层提亮至
rgba(53,60,71,.96),不再与背景融成一片。
数据筛选
- 受控 prop
filters({ 列的 key 或 field: 值 },多条件 AND),filterMethod提供表级自定义匹配,列级优先。 - 内置匹配按值类型分派:字符串 = 包含、数组 = IN、数字 / 布尔 = 等价比较;
''/[]/null视为不筛。 - 事件
filter-change只在setFilters/clearFilters后回抛,改 prop 不抛(避免受控侧死循环)。 - 与分页 / 树 / 分组叠加,顺序为「先筛选 → 再分页 / 建树 / 分组」。
- 组件不画筛选 UI:入口由业务自建,改
filters即生效。
交互能力
- 列宽 / 行高拖拽:常驻手柄,移动端按输入源自动取长按阈值,按下即反馈。
- 行拖拽排序(
rowReorder)、列拖拽重排(columnReorder),落位回抛新顺序。 - 列显隐 / 列选择器;条件格式(
cellStyle→conditional→ 主题)。 - 行选择:行级勾选与单元格选区两套独立 API。
行内编辑
editable一处开关,串起编辑 → 校验 → 历史。- 双击 / Enter / F2 三入口;列级
editType(text / number / textarea);只读、序号、手柄、分组行自动拒编。 - 事件
edit-commit/edit-cancel,携带rowKey/field/oldValue/newValue/valid/message。 - 键盘导航、TSV 复制粘贴、校验(required / number / integer / email / regex / min / max / custom)、撤销重做。
分组 / 聚合
groupBy+ 列级aggregate;组头为行投影伪行,虚拟滚动 / 导出全自动,与页脚口径独立。
导出
exportImage()导出当前帧位图(返回值随运行环境而定,可能是 data URL 或临时文件路径),只生成不落盘。getExportData()返回{ columns, rows };exportCsv()返回纯字符串 CSV。
插件化
registerCellRenderer/registerCellEditor支持自定义类型而不改引擎。table.use()全局插件注册表;TooltipPlugin拆为独立产物(core-tooltip.esm.min.js,2.2kb)按需启用。
文档与类型
- 44 个 prop 的说明在四处(
props.ts/TableProps/l-table-x.uvue/l-table-x.vue)逐字一致,字段顺序统一,说明只保留一句话作用,并由unit_props_alignment守卫。 props.ts去掉as PropType<...>断言:类型契约统一由TableProps承载,运行时对象只留type/default。- 对外类型注释精简,去掉版本注记与长篇取舍叙述。
- readme 顶部补插件元信息(
name/description/tags/componentTag/plugin/category/dependencies),与「插件标签说明」表及真实依赖保持一致。 - 插件文档收敛为
readme.md单文件:68 个实例方法、37 个主题字段(含浅色 / 暗色默认值)、ColumnDefine等类型定义全部内联,不再引用docs/目录,包内docs/与build:docs一并移除。
示例
- 示例画廊六个分类:基础 / 选择 / 树形 / 表头渲染 / 分组聚合 / 编辑导出。
- 移动端一屏可见:控制区压到最小,说明文字下移,表格高度按视口自适应;390×844 下六个分类均无溢出。
平台兼容性
uni-app(5.31)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | - | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.31)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
name: lime-table-x 高性能表格组件 description: 高性能表格组件,纯 Canvas 2D 绘制,行与主区列双向虚拟化,支持固定列、冻结行、多级表头、排序、筛选、树形、分组聚合、行内编辑与导出,2 万行流畅滚动,适用于数据列表、报表、后台管理等场景。 tags:
- table
- 表格
- canvas
- 虚拟滚动
- 高性能
- 数据展示 componentTag: l-table-x plugin: lime-table-x category: 展示组件 dependencies:
-
lime-shared
lime-table-x 高性能 Canvas 表格
一款纯 Canvas 2D 绘制的 uni-app / uni-app x 表格组件:零 DOM 表格结构,行 + 主区列双向虚拟化,2 万行流畅滚动。支持固定列 / 冻结行 / 多级表头 / 排序 / 筛选 / 树形 / 分组聚合 / 行内编辑 / 导出。
插件依赖:lime-shared
安装方法
- 在 uni-app 插件市场中搜索并导入
lime-table-x - 确保依赖
lime-shared已安装(首次导入后可能需要重新编译项目) - 在页面中使用
<l-table-x>组件
代码演示
基础用法
通过 :data 与 :columns 传入数据与列定义即可。
<l-table-x
ref="tableRef"
:data="data"
:columns="columns"
row-key="id"
width="100%"
:height="430"
@cell-click="onCellClick" />
import { ref } from 'vue'
const tableRef = ref(null)
const data = ref([
{ id: 1, name: '用户1', email: 'user1@example.com' },
{ id: 2, name: '用户2', email: 'user2@example.com' }
])
const columns = ref([
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: '姓名', width: 120 },
{ field: 'email', title: '邮箱', width: 160 }
])
const onCellClick = (info) => {
console.log('点击单元格:', info.rowIndex, info.field, info.value)
}
固定列 / 排序 / 汇总页脚
fixed 固定左 / 右列,sortable 开启排序,列级 footer 声明全表汇总口径。
const columns = [
{ field: 'id', title: 'ID', width: 72, fixed: 'left', sortable: true },
{ field: 'name', title: '姓名', width: 110, fixed: 'left' },
{ field: 'salary', title: '薪资', width: 120, align: 'right', sortable: true, footer: { type: 'sum' } }
]
<l-table-x :data="data" :columns="columns" :footer-height="40" @sort-change="onSort" />
footer 与 aggregate 支持 sum / avg / count / min / max / custom(fn)/ text(固定文案)。
多级表头与合并单元格
列上写 children 即多级表头;span 返回 { rowspan, colspan } 声明合并(两者都必填)。
const columns = [
{
title: '基本信息',
children: [
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: '姓名', width: 120 }
]
},
{ field: 'dept', title: '部门', width: 120, span: (row) => ({ rowspan: 2, colspan: 1 }) }
]
树形(含懒加载)
tree 配置开启树形,childrenKey 指定子节点字段,lazy + load 实现异步懒加载。
// 预置子节点
const tree = { childrenKey: 'children', defaultExpandAll: false }
// 懒加载(点开才拉取子节点)
const treeLazy = {
childrenKey: 'children',
lazy: true,
load: (row) => fetchChildren(row) // 返回 Promise<any[]>
}
<l-table-x :data="data" :columns="columns" :tree="tree" :indent="20" @row-expand="onExpand" />
分组 / 聚合
groupBy 按字段分组,组头行跟随虚拟滚动;列级 aggregate 声明组内口径。
const groupBy = { field: 'dept', defaultExpanded: true }
const columns = [
{ field: 'dept', title: '部门', width: 120 },
{ field: 'salary', title: '薪资', width: 120, aggregate: { type: 'avg' } }
]
<l-table-x :data="data" :columns="columns" :group-by="groupBy" @group-toggle="onGroupToggle" />
受控展开态::group-expanded-keys + @update:group-expanded-keys。
数据筛选(受控)
filters 是受控筛选值 { 列的 key 或 field: 值 },多条件取 AND;组件不画筛选 UI,入口自己画(表头菜单 / 搜索框 / 外部表单),改值即生效。
内置匹配够用就不必写 filterMethod:字符串 = 包含(忽略大小写)、数组 = IN、数字与布尔 = 等价比较;'' / [] / null 视为不筛。
<l-table-x :data="data" :columns="columns" :filters="filters" @filter-change="" />
const filters = ref({ name: '张', dept: ['研发部', '财务部'] }) // 姓名含「张」且部门属于两者之一
const = ({ filters: f, count }) => {
console.log('筛选后行数', count) // 只在调 setFilters / clearFilters 时回抛,改 props 不抛
}
// 内置表达不了的(日期 / 数值区间):值可以是任意对象,交给 filterMethod 解释
const filterMethod = (v, row) => row.salary >= v[0] && row.salary <= v[1]
// filters: { salary: [3000, 8000] }
行内编辑与校验
editable 一处开关开启编辑;列上 editType: 'text' | 'number' | 'textarea',editable: false 置灰。双击 / Enter / F2 进入编辑,Enter 提交、Esc 取消。
const columns = [
{ field: 'name', title: '姓名', width: 120, editType: 'text' },
{
field: 'salary', title: '薪资', width: 120, editType: 'number',
validators: [{ type: 'required' }, { type: 'min', value: 0 }]
},
{ field: 'id', title: 'ID', width: 80, editable: false }
]
<l-table-x :data="data" :columns="columns" editable @edit-commit="onCommit" />
配套实例方法:beginEditCell / commitEdit / cancelEdit / undoEdit / redoEdit / pasteText。
自定义单元格
render(params) 函数式绘制,注入 drawText / drawRoundRect / drawCircle / drawPolyline / drawSmoothLine / drawArea / registerClickArea(禁止返回 VNode;后三个为点集原语,画 sparkline / 趋势图)。
const columns = [
{
field: 'op', title: '操作', width: 150,
render: (p) => {
p.drawRoundRect(p.rect.x + 10, p.rect.y + 10, 60, 24, 5, '#2f7ef7', null)
p.drawText('查看', p.rect.x + 10, p.rect.y + p.rect.height / 2, 60, '#ffffff', 'center')
p.registerClickArea(
{ x: p.rect.x + 10, y: p.rect.y + 10, width: 60, height: 24 },
() => console.log('点击查看', p.row)
)
}
}
]
条件格式
列上 cellStyle(函数式)或 conditional(声明式规则数组),优先级:cellStyle → conditional → 主题。
const columns = [
{
field: 'salary', title: '薪资', width: 120,
conditional: [
{ when: (row) => row.salary > 20000, style: { backgroundColor: '#fff1f0' } },
{ when: (row) => row.salary < 5000, style: { backgroundColor: '#f0f9ff' } }
]
}
]
列宽 / 行高拖拽
colResizable 默认开(按列 resizable: false 可单独关闭),rowResizable 默认关(会占用数据区的纵向拖拽手势)。showResizeHandle 控制是否常驻显示手柄短线。
<l-table-x :data="data" :columns="columns" row-resizable show-resize-handle
@column-width-change="onColWidth" @row-height-change="onRowHeight" />
移动端与桌面共用一份实现:鼠标按下即拖,手指需长按(阈值由 resizePressDelay 控制,-1 = 按输入源自适应)。
导出
exportImage() 无参导出当前渲染画面位图(只生成不落盘);getExportData() 返回结构化行列,可接任意 xlsx 库自建 Excel 导出;exportCsv() 返回纯字符串 CSV。
const img = await tableRef.value?.exportImage() // 返回值随运行环境而定:data URL 或临时文件路径
const { columns, rows } = tableRef.value?.getExportData()
const csv = tableRef.value?.exportCsv()
可选插件(tooltip 浮层)
浮层提示(文本溢出 / 校验错误文案)默认不打包,需要时从独立产物启用一次即可——全局启用:
import { table } from '@/uni_modules/lime-table-x'
import { TooltipPlugin } from '@/uni_modules/lime-table-x/plugins/tooltip'
// use() 不做去重,重复注册会重复挂图层(浮层画两遍);在可能多次调用的地方先判一下
if (table.plugins().indexOf(TooltipPlugin) < 0) table.use(TooltipPlugin)
只让某一张表启用:tableRef.value.usePlugin(TooltipPlugin)。
自定义插件
插件是一个普通对象 / 类,install(engine) 里挂图层或订阅信号即可(usePlugin 传类或实例都行,引擎未就绪时自动缓冲):
import type { TablePlugin } from '@/uni_modules/lime-table-x'
const MyPlugin: TablePlugin = {
name: 'my-plugin',
install(engine) { /* 注册时调用一次:engine.addLayer(...) / 订阅信号 */ },
onBeforeRender(state) { /* 每帧在重绘前调用:把状态写进 RenderState,必须纯函数 */ },
dispose() { /* 引擎销毁时清理,需幂等 */ }
}
tableRef.value?.usePlugin(MyPlugin)
API 文档
Props 分类
🎯 基础配置(最常用)
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| data | 表格数据(行对象数组) | any[] | [] |
- |
| columns | 列定义,支持 children 多级表头 | ColumnDefine[] | [] |
- |
| width | 表格宽度:数字为 px,字符串支持百分比 | number | string | '100%' |
- |
| height | 表格高度:数字为 px,字符串支持百分比 | number | string | 300 |
- |
| rowHeight | 行高(px) | number | 44 |
- |
| headerHeight | 表头单层高度(px),多级表头逐层累加 | number | 44 |
- |
| footerHeight | 页脚高度(px),0 = 不显示 | number | 0 |
- |
| fontSize | 基础字号(px) | number | 14 |
- |
| rowKey | 行主键字段名 | string | 'id' |
- |
🎨 主题与外观
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| stripe | 斑马纹 | boolean | true |
true, false |
| hover | 悬浮高亮 | boolean | true |
true, false |
| dark | 暗色主题 | boolean | false |
true, false |
| theme | 主题色板覆盖 | TableTheme | - | - |
| cellStyle | 旧版兼容:单元格样式(等价 theme) | TableCellStyle | - | - |
✅ 选择 / 编辑 / 拖拽
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| showCheckbox | 显示复选框列 | boolean | false |
true, false |
| selectedRowKeys | 受控选中行 key | string[] | [] |
- |
| selectionOverridesCellStyle | 选中底色是否覆盖单元格底色 | boolean | true |
true, false |
| cellNavigation | 键盘导航开关(方向键移动激活格) | boolean | true |
true, false |
| cellSelection | 单元格选区开关(Shift 框选 / Ctrl+A) | boolean | true |
true, false |
| editable | 行内编辑开关(双击 / Enter 进入编辑) | boolean | false |
true, false |
| rowReorder | 行拖拽排序(左侧手柄列) | boolean | false |
true, false |
| columnReorder | 列拖拽重排 | boolean | false |
true, false |
| frozenRowCount | 冻结行数(钉在表体顶部) | number | 0 |
- |
| rowResizable | 行高拖拽开关 | boolean | false |
true, false |
| colResizable | 列宽拖拽开关 | boolean | true |
true, false |
| showResizeHandle | 常驻显示拖拽手柄短线 | boolean | false |
true, false |
| resizePressDelay | 拖拽长按阈值(ms),-1 = 按输入源自动 | number | -1 |
- |
| resizeScrollGuard | 滚动优先窗口(ms),0 = 关闭 | number | 240 |
- |
🌲 树形与分组
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| tree | 树形配置 | TreeConfig | - | - |
| indent | 树形缩进(px) | number | 20 |
- |
| groupBy | 分组 / 聚合配置 | GroupConfig | - | - |
| groupHeaderHeight | 组头行高(px),0 = 与普通行同高 | number | 0 |
- |
| groupExpandedKeys | 受控展开组 key | string[] | - | - |
🔍 数据筛选
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| filters | 受控筛选值(列 key → 值,多列 AND) | FilterMap | - | - |
| filterMethod | 表级自定义筛选匹配 | FilterMethod | - | - |
📄 分页与文案
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| pageSize | 每页条数,0 = 不分页 | number | 0 |
- |
| currentPage | 当前页(1 起) | number | 1 |
- |
| locale | 内置文案语言 | string | 'zh-CN' |
zh-CN, en-US |
| emptyText | 空数据文案(留空取语言包) | string | '' |
- |
| loading | 加载中(遮罩 + 文案) | boolean | false |
true, false |
⚡ 性能与滚动
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|---|---|---|---|---|
| overscan | 虚拟滚动缓冲区行数 | number | 4 |
- |
| degradeOnScroll | 快滚降级(缩小 overscan、跳过静止方向网格线) | boolean | true |
true, false |
| settleDelay | 滚动停止后补帧延迟(ms) | number | 40 |
- |
| scrollbar | 自定义滚动条显隐 | string | 'auto' |
auto, always, none |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| cell-click | 点击单元格 | CellInfo(行 / 列下标、field、value、row、rect) |
| row-click | 点击行(含落点列信息) | CellInfo |
| header-click | 点击表头 | ColumnNode |
| sort-change | 排序变化 | { key, order, states } |
| selection-change | 行勾选集合变化 | { keys, rows } |
| row-select | 单行勾选(先于 selection-change) | { key, row, rowIndex, checked, keys, rows } |
| select-all | 表头全选 / 全不选(先于 selection-change) | { checked, keys, rows } |
| update:selectedRowKeys | 配合 v-model:selected-row-keys |
string[] |
| scroll | 滚动(已节流) | { top, left, maxTop, maxLeft } |
| row-expand | 树形展开 / 收起 | { row, expanded } |
| reach-bottom | 纵向滚动触底(加载更多) | - |
| column-width-change | 列宽拖拽结束 | { column, key, width } |
| row-height-change | 行高拖拽结束 | { rowIndex, height } |
| range-selection-change | 单元格框选变化 | { r0, c0, r1, c1 } \| null |
| ready | 引擎初始化完成(canvas 就绪) | - |
| row-reorder | 行拖拽落位(order = 重排后的数据) | { fromIndex, toIndex, order } |
| column-reorder | 列拖拽重排落位 | { fromKey, toIndex, order } |
| column-visible-change | 列显隐变化 | { key, visible, hiddenKeys } |
| group-toggle | 分组展开 / 收起 | { key, value, count, expanded, expandedKeys } |
| update:groupExpandedKeys | 配合 v-model:group-expanded-keys |
string[] |
| edit-commit | 提交编辑 | { rowKey, field, oldValue, newValue, valid, message } |
| edit-cancel | 取消编辑 | { rowKey, field, value } |
| filter-change | 筛选变化(仅 setFilters / clearFilters 触发) |
{ filters, count } |
实例方法(通过 ref)
实例上共 68 个公开方法,按用途分组(方法名与 TableInstance 类型逐项一致)。
布局与滚动(7)
| 方法 | 说明 | 返回 |
|---|---|---|
| resize() | 重新测量容器并重算布局(容器尺寸变了但窗口没变时调) | Promise<void> |
| scrollTo(x, y, animated?) | 滚动到指定偏移(逻辑 px) | void |
| scrollToIndex(index) | 滚动到指定行 | void |
| setColumnWidth(key, width) | 编程式设列宽,会回抛 column-width-change | void |
| setRowHeight(index, height) | 编程式设行高(按行下标) | void |
| getColumnRect(key) | 列在视口内的矩形(浮层对齐 / 自动化测试用) | Rect \| null |
| getRowRect(index) | 行在视口内的矩形(同上) | Rect \| null |
排序与筛选(6)
| 方法 | 说明 | 返回 |
|---|---|---|
| clearSort() | 清空全部列的排序态 | void |
| setFilters(filters) | 设置筛选值,会回抛 filter-change | void |
| clearFilters() | 清空全部筛选 | void |
| getFilters() | 当前生效的筛选值(只读引用) | FilterMap |
| getFilteredCount() | 筛选后、分页前的行数 | number |
| isFiltered() | 是否有任意一列筛选生效('' / [] / null 不算) |
boolean |
行选择(4)
| 方法 | 说明 | 返回 |
|---|---|---|
| getSelectedKeys() | 选中行 key(顺序 = 当前数据顺序) | string[] |
| getSelectedRows() | 选中行对象 | any[] |
| clearRowSelection() | 清空行勾选(≠ clearSelection) |
void |
| selectAllRows(checked?) | 全选 / 全不选,返回选中 key | string[] |
单元格选区与剪贴板(8)
| 方法 | 说明 | 返回 |
|---|---|---|
| getSelection() | 当前选区,无选区返回 null | { r0, c0, r1, c1 } \| null |
| setActiveCell(row, col) | 设置激活格(按行列下标) | void |
| setRange(ar, ac, fr, fc) | 设置选区(锚点行 / 列 → 焦点行 / 列) | void |
| clearSelection() | 清空单元格选区 | void |
| scrollActiveIntoView() | 把激活格滚进视口 | void |
| copySelection() | 复制选区为 TSV 并写入剪贴板 | string |
| pasteText(tsv) | 粘贴 TSV 文本(不读剪贴板) | void |
| pasteFromClipboard() | 读剪贴板并粘贴(部分平台需用户手势) | void |
查找(5)
| 方法 | 说明 | 返回 |
|---|---|---|
| find(keyword) | 跳到首个命中并高亮,返回命中行数 | number |
| findNext() | 下一个命中 | FindResult \| null |
| findPrev() | 上一个命中 | FindResult \| null |
| clearFind() | 清除查找态 | void |
| getFindResult() | 当前查找状态 | FindResult |
行内编辑(9)
| 方法 | 说明 | 返回 |
|---|---|---|
| beginEditCell(row, col) | 进入编辑态,不可编辑返回 false | boolean |
| commitEdit(value) | 提交编辑(校验 → 记历史 → emit edit-commit) | void |
| cancelEdit() | 取消编辑 | void |
| isEditing() | 是否处于编辑态 | boolean |
| getValueByKey(key, field) | 按行 key + 字段取值 | any |
| undoEdit() | 撤销上一次编辑 | void |
| redoEdit() | 重做 | void |
| canUndoEdit() | 是否可撤销 | boolean |
| canRedoEdit() | 是否可重做 | boolean |
列显隐(5)
| 方法 | 说明 | 返回 |
|---|---|---|
| setColumnVisible(key, visible) | 显示 / 隐藏列,返回是否生效(最后一列不可隐藏) | boolean |
| getColumnVisible(key) | 某列当前是否可见 | boolean |
| getColumnList() | 全部叶列(含已隐藏),供业务渲染列选择器 | ColumnNode[] |
| getHiddenColumnKeys() | 当前被隐藏的列 key | string[] |
| resetColumnVisibility() | 一次性显示全部列 | void |
分组(6)
| 方法 | 说明 | 返回 |
|---|---|---|
| setGroupBy(cfg) | 运行时切分组,传 null 关闭 | void |
| toggleGroup(key) | 展开 / 收起某组,返回操作后的展开态 | boolean |
| expandAllGroups() | 展开全部组 | void |
| collapseAllGroups() | 收起全部组 | void |
| getGroupKeys() | 全部组 key | string[] |
| getExpandedGroupKeys() | 当前展开的组 key | string[] |
数据与行序(3)
| 方法 | 说明 | 返回 |
|---|---|---|
| updateData(data) | 显式提交数据(原地改数组后调用,绕过引用去重) | void |
| updateColumns(columns) | 显式提交列定义 | void |
| reorderRow(from, to) | 把第 from 行移到 to 行,回抛 row-reorder | void |
导出(3)
| 方法 | 说明 | 返回 |
|---|---|---|
| exportCsv(delimiter?) | 导出可见数据为 CSV 字符串(纯数据,落盘由调用方处理) | string |
| exportImage() | 导出位图(H5 返回 dataURL,原生返回临时文件路径) | Promise<string> |
| getExportData() | 全部列 + 全部行的结构化数据(不受视口裁剪) | { columns, rows } |
渲染器与插件(6)
| 方法 | 说明 | 返回 |
|---|---|---|
| registerCellRenderer(type, impl) | 注册自定义单元格渲染器(按列 type 分发) |
void |
| unregisterCellRenderer(type) | 注销渲染器 | void |
| registerCellEditor(type, impl) | 注册自定义编辑器(仅登记,暂不参与编辑流程) | void |
| unregisterCellEditor(type) | 注销编辑器 | void |
| getRegisteredCellEditor(type) | 查询某 type 已注册的编辑器 | CellEditorImpl \| null |
| usePlugin(plugin) | 注册引擎插件(类或实例,引擎未就绪时自动缓冲) | void |
性能与调试(6)
| 方法 | 说明 | 返回 |
|---|---|---|
| debug() | 一帧快照:帧耗时 / 可见范围 / 滚动条矩形 | TableDebugInfo \| null |
| startProfiler(frames?) | 开启滚动帧率采样 | void |
| stopProfiler() | 关闭帧率采样 | void |
| enableFastScroll() | 开启快滚降级 | void |
| disableFastScroll() | 关闭快滚降级 | void |
| toggleFastScroll() | 切换快滚降级 | void |
常用写法:
const t = tableRef.value
t?.resize() // 容器尺寸变了
t?.scrollToIndex(200) // 滚到第 200 行
t?.setFilters({ dept: '研发部' }) // 设筛选(null / {} = 清空)
t?.beginEditCell(0, 1) // 进入编辑(行下标, 列下标)
t?.exportCsv() // 导出 CSV 字符串
TypeScript 下建议给 ref 标上实例类型(省得手写一长串签名):
import type { TableInstance } from '@/uni_modules/lime-table-x'
const tableRef = ref<TableInstance | null>(null)
类型定义
ColumnDefine(列定义)
columns 数组的每一项,也是 children 递归的类型。
| 字段 | 类型 | 说明 |
|---|---|---|
| field | string |
数据字段名,支持 'a.b.c' 路径 |
| key | string |
列标识,默认等于 field |
| title | string |
表头文案 |
| width | number |
列宽(px),默认 120;总宽不足视口时按比例拉伸 |
| minWidth | number |
最小列宽(px),拖拽与自适应都不会越过 |
| maxWidth | number |
最大列宽(px) |
| align | AlignType |
单元格水平对齐:left / center / right |
| headerAlign | AlignType |
表头对齐,默认 center |
| fixed | 'left' \| 'right' |
固定列 |
| sortable | boolean |
是否可排序 |
| sorter | (a, b) => number |
自定义排序函数 |
| filterMethod | FilterMethod |
列级自定义筛选匹配,优先于表级 filterMethod |
| resizable | boolean |
是否可拖拽改宽(表格 colResizable 开启时才有意义) |
| visible | boolean |
是否显示(false = 初始隐藏) |
| cellOverflow | OverflowType |
文本溢出策略:ellipsis / wrap / clip |
| maxLines | number |
wrap 模式下的最大行数 |
| tooltip | boolean |
溢出时显示浮层提示(需引入 tooltip 插件) |
| type | string |
内置单元格类型:image = 图片列;不填 = 文本 |
| formatter | (value, row, rowIndex) => string |
文本格式化 |
| render | (params) => void |
自定义绘制(函数式,禁止返回 VNode) |
| cellStyle | CellStyleResolver |
条件格式:函数式,返回 null 表示不覆盖 |
| conditional | ConditionalRule[] |
条件格式:声明式规则,按顺序取首个命中 |
| footer | FooterDef |
页脚汇总口径(全表) |
| aggregate | FooterDef |
组头聚合口径(组内),与 footer 独立 |
| span | SpanDef \| (row, i) => SpanDef \| null |
合并单元格 |
| editable | boolean |
是否可编辑:不填跟随表格 editable,false = 强制只读 |
| editType | 'text' \| 'number' \| 'textarea' |
行内编辑器类型,默认 text |
| validators | ValidatorRule[] |
校验规则,提交时逐条校验 |
| children | ColumnDefine[] |
多级表头子列 |
TreeConfig(树形配置)
| 字段 | 类型 | 说明 |
|---|---|---|
| childrenKey | string |
子节点字段名 |
| hasChildrenKey | string |
「是否有子节点」的字段名(懒加载时用) |
| indent | number |
缩进(px),默认取表格 indent |
| defaultExpandAll | boolean |
默认全部展开 |
| lazy | boolean |
懒加载开关 |
| load | (row) => Promise<any[]> |
加载子节点 |
GroupConfig(分组配置)
| 字段 | 类型 | 说明 |
|---|---|---|
| field | string |
分组字段(支持 'a.b.c' 路径),组头显示该值 |
| order | 'asc' \| 'desc' \| 'none' |
组间顺序,不填 = 按首次出现顺序 |
| defaultExpanded | boolean |
初始是否展开,默认 true(受控 groupExpandedKeys 优先) |
| headerHeight | number |
组头行高(px),不填 = 与普通行同高 |
| showCount | boolean |
组头是否附带行数,默认 true |
FooterDef(汇总 / 聚合口径)
footer 与 aggregate 共用这一结构。
| 字段 | 类型 | 说明 |
|---|---|---|
| type | 'sum' \| 'avg' \| 'count' \| 'min' \| 'max' \| 'custom' \| 'text' |
汇总口径 |
| text | string |
type: 'text' 时的固定文案 |
| fn | (values, rows) => string |
type: 'custom' 时的自定义计算 |
SpanDef
合并单元格的描述对象。
| 字段 / 方法 | 类型 | 说明 |
|---|---|---|
| rowspan | number |
跨行数 |
| colspan | number |
跨列数 |
ConditionalRule
声明式条件格式的一条规则。
| 字段 / 方法 | 类型 | 说明 |
|---|---|---|
| when | (value, row, rowIndex) => boolean |
命中条件 |
| style | CellStyleRule |
命中后的样式覆盖 |
ValidatorRule
行内编辑的校验规则。
| 字段 / 方法 | 类型 | 说明 |
|---|---|---|
| type | ValidatorType |
required / min / max / regex / custom |
| value | number |
min / max 的阈值 |
| pattern | string \| RegExp |
regex 模式的正则 |
| validator | (value, row, i) => boolean |
custom 规则的判定,返回 false 即不合法 |
| message | string |
自定义报错文案,缺省用内置中文文案 |
CellInfo
点击类事件回传的单元格信息。
| 字段 / 方法 | 类型 | 说明 |
|---|---|---|
| rowIndex / colIndex | number |
行、列下标 |
| field / key | string |
列字段名 / 列标识 |
| value / row / rowKey | any |
单元格值、整行对象、行主键 |
| column | ColumnNode |
列节点 |
| rect | Rect |
单元格在视口内的矩形 |
| area | 'header' \| 'body' \| 'footer' |
落点所在区域 |
CellRenderParams
render(params) 收到的上下文与绘制原语。
| 字段 / 方法 | 类型 | 说明 |
|---|---|---|
| ctx / paint / theme | any |
底层上下文与绘制器(一般用下面的原语即可) |
| value / row / rowIndex | any |
当前单元格的值、整行、行下标 |
| column / colIndex / rect | any |
列节点、列下标、单元格矩形 |
| selected / hovered | boolean |
当前单元格是否被选中 / 悬浮 |
| drawText(text, x, y, maxWidth, color?, align?) | void |
画文本 |
| drawRect / drawRoundRect / drawCircle | void |
矩形 / 圆角矩形 / 圆 |
| drawPolyline(points, color, lineWidth?) | void |
折线(sparkline) |
| drawSmoothLine(points, color, lineWidth?) | void |
平滑曲线 |
| drawArea(points, baseY, color, alpha?) | void |
面积图 |
| registerClickArea(rect, handler) | void |
登记可点击热区 |
主题定制
组件通过 props 定制主题,无需额外 CSS 变量。
方式一:暗色主题(推荐)
<l-table-x :data="data" :columns="columns" :dark="true" />
方式二:自定义色板
<l-table-x :data="data" :columns="columns" :theme="myTheme" />
// 覆盖部分色板字段(只写要改的,未写的取当前 dark 模式的默认值)
const myTheme = { textColor: '#333', borderColor: '#e5e6eb', selectedColor: '#e8f0fe' }
⚠️ 选中底色是
selectedColor(不是selectedBgColor),选中文字色是selectedTextColor。mergeTheme()只认同名字段,拼错等于没写,且不会有任何提示。可覆盖字段共 37 个,下表的后两列是内置浅色 / 暗色默认值(暗色下只写要改的字段,其余取暗色默认)。
| 字段 | 说明 | 浅色默认 | 暗色默认 |
|---|---|---|---|
backgroundColor |
表格底色 | '#ffffff' |
'#1b1f24' |
borderColor |
网格线 / 外框色 | '#e6e8eb' |
'#2b323b' |
borderWidth |
网格线宽度(px,0 = 不画) | 1 |
1 |
headerBackgroundColor |
表头底色 | '#f5f7fa' |
'#232a33' |
headerTextColor |
表头文字色 | '#303133' |
'#e6edf5' |
headerFontSize |
表头字号(px) | 14 |
14 |
headerFontWeight |
表头字重(字符串,如 '600') |
'600' |
'600' |
footerBackgroundColor |
页脚底色 | '#f5f7fa' |
'#232a33' |
footerTextColor |
页脚文字色 | '#303133' |
'#e6edf5' |
textColor |
单元格文字色 | '#3f4a56' |
'#c9d4e0' |
fontSize |
单元格字号(px) | 14 |
14 |
fontFamily |
字体族 | 'sans-serif' |
'sans-serif' |
rowBackgroundColor |
行底色 | '#ffffff' |
'#1b1f24' |
stripeColor |
斑马纹底色 | '#fafbfc' |
'#1f242b' |
hoverColor |
悬浮行底色 | '#eef6ff' |
'#26303c' |
selectedColor |
选中行 / 单元格底色 | '#e3f0ff' |
'#24384f' |
selectedTextColor |
选中行文字色 | '#1a6dd8' |
'#6cb1ff' |
treeIndentColor |
树形缩进引导线色 | '#98a2b3' |
'#5d6875' |
shadowColor |
固定列 / 冻结行投影色 | 'rgba(0, 0, 0, 0.12)' |
'rgba(0, 0, 0, 0.45)' |
emptyTextColor |
空数据 / 占位文案色 | '#9aa5b1' |
'#5d6875' |
loadingMaskColor |
加载遮罩色 | 'rgba(255, 255, 255, 0.6)' |
'rgba(27, 31, 36, 0.6)' |
cellPadding |
单元格左右内边距(px) | 10 |
10 |
checkboxSize |
复选框边长(px) | 16 |
16 |
checkboxActiveColor |
复选框选中色 | '#2f7ef7' |
'#3b8ef0' |
selectionFill |
框选矩形填充色 | 'rgba(47, 126, 247, 0.12)' |
'rgba(59, 142, 240, 0.22)' |
activeBorder |
激活格边框色 | '#2f7ef7' |
'#3b8ef0' |
findFill |
查找命中填充色 | 'rgba(245, 158, 11, 0.24)' |
'rgba(245, 158, 11, 0.28)' |
findBorder |
查找命中边框色 | '#f59e0b' |
'#f0a52a' |
reorderHintBg |
拖拽重排:悬停提示底(半透明) | 'rgba(47, 126, 247, 0.08)' |
'rgba(59, 142, 240, 0.14)' |
reorderActiveBg |
拖拽重排:抓取态底(半透明,深于 hint) | 'rgba(47, 126, 247, 0.20)' |
'rgba(59, 142, 240, 0.30)' |
reorderAccent |
拖拽重排:强调条 / 抓手图标 | '#2f7ef7' |
'#3b8ef0' |
groupBackgroundColor |
分组组头底色 | '#eef2f7' |
'#252d37' |
groupTextColor |
分组组头文字色 | '#303133' |
'#e6edf5' |
groupAggregateTextColor |
组头聚合值文字色(比组名浅一档) | '#4a5568' |
'#93a2b3' |
errorBackgroundColor |
校验错误单元格底色 | '#fef0f0' |
'#3a2326' |
errorBorderColor |
校验错误单元格边框色 | '#f56c6c' |
'#f56c6c' |
errorTextColor |
校验错误单元格文字色 | '#f56c6c' |
'#f9a3a3' |
⚠️ 重要提示
关于组件使用
- 对外只有一个业务组件
<l-table-x>。components/lime-table-x/(含lime-table-x.uvue)是示例演示页面,内置写死的演示数据与画廊,业务代码请勿使用。 - 尺寸请用
width/height控制:支持数字(px)或百分比字符串(如'100%')。不要依赖外部 class / style 去改变内部 Canvas 的绘制尺寸。 exportImage()只生成不落盘:返回值格式随运行环境而定,是否存相册 / 上传 / 预览由调用方按实际返回值自行处理。- 行内编辑需两端齐备:组件上传
editable,且目标列未被editable: false置灰。 - 包里只有壳层 + 压缩产物:引擎(
core/*.ts)属于源码,不随包发布,包内是core.esm.min.js与自包含的core.esm.min.d.ts。
快速预览
导入插件后,可以直接使用以下标签查看完整功能演示(含基础性能、树形、分组、编辑、导出等分类画廊):
<!-- 代码位于 uni_modules/lime-table-x/components/lime-table-x -->
<lime-table-x />
插件标签说明
| 标签类型 | 示例 | 说明 |
|---|---|---|
| 组件标签 | l-table-x |
表格核心业务组件,项目里请使用它 |
| 演示标签 | lime-table-x |
演示组件,用于查看完整功能演示(含示例数据,勿用于业务) |
注意事项
- 组件初始化时需要获取 Canvas 的真实尺寸,请确保容器已具备有效宽高后再渲染。
rowKey默认'id',若业务主键字段不同请显式设置,否则选择 / 编辑 / 展开态会错乱。- 自定义单元格
render为函数式绘制,禁止返回 VNode / 组件,只能调用注入的绘制 API。 - 浅色 / 暗色通过
dark切换,theme仅做色板覆盖,二者独立。 - 树形 / 分组
tree与groupBy同时给时 树形优先。
常见问题
在弹窗(Popup / Dialog)中使用时组件无法正常显示?
组件初始化时需要获取 Canvas 的真实尺寸,如果弹窗尚未展开就渲染组件,Canvas 可能获取到 0 尺寸导致绘制异常。请确保弹窗展开后再渲染组件:
<l-popup v-model="show">
<l-table-x v-if="show" :data="data" :columns="columns" />
</l-popup>
也可以使用 nextTick 延迟渲染:
const mounted = ref(false)
watch(() => show, (v) => {
if (v) nextTick(() => { mounted.value = true })
else mounted.value = false
})
改了 prop 没反应?
- 组件按引用比较
data/columns:原地修改数组(data.value.push(...))不会被识别,请整体替换,或调updateData()/updateColumns()。 - 引擎是预打包产物,升级插件后请重新编译,H5 端可能是缓存没刷。
容器宽度变了但表格没跟着变?
组件只在自身尺寸变化时自动重排。外层布局变化但组件本身尺寸未变(如侧栏收起导致父容器重新分配)时,请手动调 tableRef.value?.resize()。
支持与赞赏
如果你觉得本插件解决了你的问题,可以考虑支持作者:
| 支付宝赞助 | 微信赞助 |
|---|---|
![]() |
![]() |

收藏人数:
https://gitee.com/liangei/lime-table-x
购买源码授权版(
试用
赞赏(0)


下载 74515
赞赏 604
下载 12731366
赞赏 1959
赞赏
京公网安备:11010802035340号