Table 表格
数据表格 —— 多列排序、列拖拽 / 调宽、固定列、过滤、分页、行展开、树形数据、总计行、粘性表头、全局搜索 与 列可见性。
CfTable 是组件库里功能最完整的”主力”组件之一。设计取舍:
- 客户端模式默认派生 —— 全局搜索、列过滤、排序、分页全部在组件内完成;改成服务端模式只需要给
pagination.total一个数字。 - 列状态非受控为主 —— 隐藏 / 顺序 / 宽度走内置 state,必要时可通过
update:columnsState全受控。 - 0 第三方依赖 —— HTML5 DnD 处理列拖拽,PointerEvent 处理调宽,原生
position: sticky处理固定列与表头。
基础用法
columns + rows + 可选 rowKey。column.format 做展示格式化,column.render 接收渲染函数返回 VNode / 字符串。
| 订单号 | 客户 | 金额 | 状态 | 日期 |
|---|---|---|---|---|
| O-1042 | Alice Chen | ¥1,280 | 已支付 | 2026-05-09 |
| O-1041 | Bob Wang | ¥698 | 已发货 | 2026-05-08 |
| O-1040 | Carol Liu | ¥3,450 | 处理中 | 2026-05-08 |
| O-1039 | Dave Zhang | ¥218 | 已退款 | 2026-05-07 |
选择行
单选 selectable="single" / 多选 selectable="multiple"。v-model 接受 string 或 string[],由 rowKey 决定。
| 姓名 | 角色 | 团队 | |
|---|---|---|---|
| Alice | 前端 | Web | |
| Bob | 后端 | API | |
| Carol | 设计 | UX | |
| Dave | 运维 | Infra |
["1","3"]多列排序
打开 multi-sort 后,按住 Shift / ⌘ 点列头追加次级排序。同列再次点击循环 asc → desc → 取消。表头显示当前列在排序队列中的序号。
| 仓库 | 语言1 | Stars2 | Forks |
|---|---|---|---|
| facebook/react | JavaScript | 232,000 | 47,800 |
| preactjs/preact | JavaScript | 36,800 | 2,050 |
| angular/angular | TypeScript | 96,100 | 25,400 |
| sveltejs/svelte | TypeScript | 81,000 | 4,200 |
| vuejs/core | TypeScript | 47,800 | 8,200 |
| solidjs/solid | TypeScript | 32,400 | 1,100 |
| lit/lit | TypeScript | 19,200 | 980 |
点列头切换排序;按住 Shift 再点别的列追加次级排序。当前:按语言升序,再按 stars 降序。
排序优先级 = sort 数组顺序。完全受控时把 :sort 绑到自己的 ref 上即可。
拖拽调宽 + 拖拽换序
打开 resizable 后每列右边出现 6px 的 col-resize 区。拖拽实时更新内部 state,@update:columns-state 把新宽度送出来用于持久化。reorderable 让表头可拖拽换序。
| 包名 | 版本 | 周下载 | 体积 | 许可证 |
|---|---|---|---|---|
| react | 19.0.1 | 28,940,000 | 6.1 KB | MIT |
| vue | 3.5.34 | 4,920,000 | 36.8 KB | MIT |
| lodash | 4.17.21 | 53,200,000 | 24.3 KB | MIT |
| axios | 1.7.4 | 51,800,000 | 13.4 KB | MIT |
| tailwindcss | 4.1.0 | 12,400,000 | — | MIT |
↔ 拖拽列右边缘改宽度;按住表头拖到目标列改顺序。
单列可以用 resizable: false / reorderable: false 屏蔽。
固定列 + 粘性表头
column.fixed: 'left' | 'right' 把列钉在两侧,超长内容横向滚动时仍可见。配合 sticky-header + :height 表头同时粘顶。
| ID | 主机名 | Region | CPU | 内存 | 磁盘 | 网络 | 在线 | 状态 |
|---|---|---|---|---|---|---|---|---|
| srv-1000 | web-1.us-east.protoforge.io | us-east-1 | 30% | 50% | 20% | 1.0 GB/s | 42 天 | running |
| srv-1001 | web-2.eu-west.protoforge.io | eu-west-1 | 37% | 61% | 25% | 4.2 GB/s | 45 天 | idle |
| srv-1002 | web-3.ap-south.protoforge.io | ap-south-1 | 44% | 72% | 30% | 7.4 GB/s | 48 天 | warning |
| srv-1003 | web-1.us-east.protoforge.io | us-east-1 | 51% | 83% | 35% | 1.6 GB/s | 51 天 | running |
| srv-1004 | web-2.eu-west.protoforge.io | eu-west-1 | 58% | 54% | 40% | 4.8 GB/s | 54 天 | idle |
| srv-1005 | web-3.ap-south.protoforge.io | ap-south-1 | 65% | 65% | 45% | 7.1 GB/s | 57 天 | warning |
| srv-1006 | web-1.us-east.protoforge.io | us-east-1 | 72% | 76% | 50% | 1.3 GB/s | 60 天 | running |
| srv-1007 | web-2.eu-west.protoforge.io | eu-west-1 | 79% | 87% | 55% | 4.5 GB/s | 63 天 | idle |
| srv-1008 | web-3.ap-south.protoforge.io | ap-south-1 | 86% | 58% | 60% | 7.7 GB/s | 66 天 | warning |
| srv-1009 | web-1.us-east.protoforge.io | us-east-1 | 33% | 69% | 65% | 1.0 GB/s | 69 天 | running |
| srv-1010 | web-2.eu-west.protoforge.io | eu-west-1 | 40% | 80% | 70% | 4.2 GB/s | 72 天 | idle |
| srv-1011 | web-3.ap-south.protoforge.io | ap-south-1 | 47% | 51% | 75% | 7.4 GB/s | 75 天 | warning |
左侧 ID 和右侧 状态 列被钉住;中间区域可横向滚动;表头粘在顶部。
行展开 + 树形数据
expandable 给每行加展开按钮:
- 传
expand-render—— 渲染自定义展开内容(订单详情、嵌套图表等)。 - 行数据自带
children—— 自动当作树形数据递归渲染,每层tree-indent像素的缩进。
| 名称 | 大小 | 最近修改 | |
|---|---|---|---|
| src/ | — | 2026-05-09 | |
| components/ | — | 2026-05-09 | |
| Table.vue | 32.4 KB | 2026-05-09 | |
| variants.ts | 8.2 KB | 2026-05-09 | |
| index.ts | 1.1 KB | 2026-05-08 | |
| dist/ | — | 2026-05-10 | |
| README.md | 4.2 KB | 2026-05-09 |
v-model:expanded-row-keys 把展开状态外置到父组件,方便保存到 URL 或 localStorage。
工具栏 + 总计行 + 分页 + 过滤
把 toolbar="auto" 打开就出现全局搜索与列可见性菜单。column.filterable 给每列加漏斗按钮,filterType 切到 select / number-range / 默认文本。column.summary 接 'sum' | 'avg' | 'count' 或函数;表底自动出一行总计,跟着过滤结果实时变。
| 订单号 | 产品 | 套餐 | 单价 | 数量 | 合计 |
|---|---|---|---|---|---|
| S-10000 | Pro · 月付 | Pro | ¥12 | 1 | ¥12 |
| S-10001 | Pro · 年付 | Pro | ¥120 | 2 | ¥240 |
| S-10002 | Team · 月付 | Team | ¥49 | 3 | ¥147 |
| S-10003 | Team · 年付 | Team | ¥480 | 4 | ¥1,920 |
| S-10004 | Enterprise · 年付 | Enterprise | ¥1,290 | 5 | ¥6,450 |
| S-10005 | Pro · 月付 | Pro | ¥12 | 6 | ¥72 |
| S-10006 | Pro · 年付 | Pro | ¥120 | 7 | ¥840 |
| S-10007 | Team · 月付 | Team | ¥49 | 1 | ¥49 |
| S-10008 | Team · 年付 | Team | ¥480 | 2 | ¥960 |
| S-10009 | Enterprise · 年付 | Enterprise | ¥1,290 | 3 | ¥3,870 |
| S-10010 | Pro · 月付 | Pro | ¥12 | 4 | ¥48 |
| S-10011 | Pro · 年付 | Pro | ¥120 | 5 | ¥600 |
| S-10012 | Team · 月付 | Team | ¥49 | 6 | ¥294 |
| S-10013 | Team · 年付 | Team | ¥480 | 7 | ¥3,360 |
| S-10014 | Enterprise · 年付 | Enterprise | ¥1,290 | 1 | ¥1,290 |
| S-10015 | Pro · 月付 | Pro | ¥12 | 2 | ¥24 |
| S-10016 | Pro · 年付 | Pro | ¥120 | 3 | ¥360 |
| S-10017 | Team · 月付 | Team | ¥49 | 4 | ¥196 |
| S-10018 | Team · 年付 | Team | ¥480 | 5 | ¥2,400 |
| S-10019 | Enterprise · 年付 | Enterprise | ¥1,290 | 6 | ¥7,740 |
| S-10020 | Pro · 月付 | Pro | ¥12 | 7 | ¥84 |
| S-10021 | Pro · 年付 | Pro | ¥120 | 1 | ¥120 |
| S-10022 | Team · 月付 | Team | ¥49 | 2 | ¥98 |
| S-10023 | Team · 年付 | Team | ¥480 | 3 | ¥1,440 |
| 24 行 | 平均 ¥353 | 90 | ¥32,614 |
工具栏自带全局搜索;点列头漏斗图标做单列过滤;总计行自动求和 / 平均 / 计数;分页跟随过滤后结果。
:default-pagination="{ page: 1, pageSize: 8 }" 是非受控分页。完全受控用 pagination 绑当前对象,并监听 @update:pagination。
虚拟滚动
打开 virtual 后表体只渲染当前可见的行 + 上下 overscan 缓冲,DOM 节点数恒定在几十个。要求所有行使用固定 row-height(缺省 36px)。建议配合 :height + sticky-header。
数据集大小:50,000 行 · 实际渲染的 DOM 节点数始终 ~30。
| 订单 ID | 币对 | 价格 | 数量 | 方向 | 时间 |
|---|---|---|---|---|---|
| T-000000 | BTC | $ 1,000 | 0.13 | sell | 07:49:04 |
| T-000001 | ETH | $ 1,039.98 | 0.26 | buy | 07:49:03 |
| T-000002 | SOL | $ 1,079.87 | 0.39 | buy | 07:49:02 |
| T-000003 | AVAX | $ 1,119.55 | 0.52 | sell | 07:49:01 |
| T-000004 | DOT | $ 1,158.94 | 0.65 | buy | 07:49:00 |
| T-000005 | MATIC | $ 1,197.92 | 0.78 | buy | 07:48:59 |
| T-000006 | LINK | $ 1,236.42 | 0.91 | sell | 07:48:58 |
| T-000007 | ARB | $ 1,274.32 | 1.04 | buy | 07:48:57 |
| T-000008 | BTC | $ 1,311.53 | 1.17 | buy | 07:48:56 |
| T-000009 | ETH | $ 1,347.97 | 1.30 | sell | 07:48:55 |
| T-000010 | SOL | $ 1,383.54 | 1.43 | buy | 07:48:54 |
| T-000011 | AVAX | $ 1,418.15 | 1.56 | buy | 07:48:53 |
| T-000012 | DOT | $ 1,451.71 | 1.69 | sell | 07:48:52 |
| T-000013 | MATIC | $ 1,484.15 | 1.82 | buy | 07:48:51 |
| T-000014 | LINK | $ 1,515.37 | 1.95 | buy | 07:48:50 |
| T-000015 | ARB | $ 1,545.31 | 2.08 | sell | 07:48:49 |
| T-000016 | BTC | $ 1,573.88 | 2.21 | buy | 07:48:48 |
| T-000017 | ETH | $ 1,601.02 | 2.34 | buy | 07:48:47 |
| T-000018 | SOL | $ 1,626.66 | 2.47 | sell | 07:48:46 |
50000 行的数据集只渲染 ~30 个 <tr>。继续兼容排序、过滤、固定列、全局搜索。注意:虚拟模式下 expandable 的展开行高也被固定,复杂的展开内容请用 Modal 替代或关闭虚拟。
行拖拽换序
row-reorderable 在最左侧加一列 ⋮⋮ 拖把柄。拖动整行换序,@update:rows 把新数组送出来;想监听 from/to 索引就用 @row-reorder。
| 步骤 | 负责人 | 预计 | 状态 | |
|---|---|---|---|---|
| 需求评审 | Alice | 0.5d | done | |
| 设计稿 | Bob | 2d | in-progress | |
| API 草案 | Carol | 1d | in-progress | |
| 前端实现 | Dave | 3d | todo | |
| 联调 | Eve | 1d | todo | |
| 测试 + 上线 | Frank | 0.5d | todo |
左侧的 ⋮⋮ 把柄可拖动整行换序;上层通过 @update:rows 拿到新数组。
仅作用在顶层(level 0)行;树形子行自身不能拖。
内联编辑
给列加 editable: true + 可选 editType: 'text' | 'number' | 'select',双击单元格进入编辑:
editValidate拦截非法值,校验失败时保留编辑态。- 提交时机:Enter / blur;取消:Esc。
@cell-edit抛出{ row, column, oldValue, newValue, index },由你写回数据源。
| 姓名 | 角色 | 配额 |
|---|---|---|
| Jane | admin | 5 |
| Bob | editor | 3 |
| Alice | viewer | 1 |
双击单元格进入编辑:text / select / number。Enter / blur 提交,Esc 取消。 数字列加了 editValidate,负数会被拒绝。
组件不直接 mutate 你的
rows:所有提交都通过cell-edit事件,业务方决定要不要写回、是否调接口、是否合并。
CSV 导出
exportable 让工具栏出现 Export 按钮,点击导出当前过滤后的结果。导出文件含 UTF-8 BOM,Excel 直接打开不乱码。
- 跳过某列:
column.exportable: false - 自定义单元格序列化:
column.exportRender(value, row, i) => string - 文件名:
export-file-name,缺省'table' - 完全自定义:
@export事件接到 CSV 字符串,自己 download 或上传到服务端。 - 命令式调用:组件
exportCsv()已通过defineExpose暴露。
| 发票号 | 客户 | 产品 | 金额 | 状态 | 日期 |
|---|---|---|---|---|---|
| INV-001 | Acme Inc. | Pro Plan · 年付 | ¥12,000 | 已支付 | 2026-04-01 |
| INV-002 | Globex | Team · 月付 | ¥480 | 已支付 | 2026-04-12 |
| INV-003 | Initech | Enterprise · 年付 | ¥156,000 | 待支付 | 2026-04-15 |
| INV-004 | Vandelay | Pro Plan · 月付 | ¥120 | 已退款 | 2026-04-22 |
工具栏右边出现 Export 按钮;导出的是当前过滤 / 搜索后的结果,文件名带 BOM 的 UTF-8 CSV。 amount 列指定了 exportRender,导出时输出原始数字而不是格式化后的 ¥。
列状态持久化
persistKey="<key>" 让组件把当前 columnsState(隐藏 / 顺序 / 宽度)写到 localStorage['cf-table:<key>'],下次访问自动恢复。
| ID | 姓名 | 角色 | 团队 |
|---|---|---|---|
| 1 | Jane Liu | Engineer | Payments |
| 2 | Bob Wang | Designer | Brand |
| 3 | Alice Chen | PM | Growth |
| 4 | Tim Wong | Engineer | Platform |
| 5 | Sue Zhang | Engineer | Payments |
调宽 / 拖列 / 隐藏列 后刷新页面,状态从 localStorage['cf-table:people-list'] 恢复。
完全受控时(外部传 :columns-state)会跳过自动读写,这时由你决定怎么持久化。
自动 rowSpan 合并
给某列加 mergeRows: true,连续相同值的单元格会自动合并 rowSpan。需要更细粒度的合并条件可以传函数:mergeRows: (cur, prev) => cur.groupId === prev.groupId。
| 项目 | 分支 | 步骤 | 状态 | 耗时 |
|---|---|---|---|---|
| web | main | 安装依赖 | OK | 8s |
| 类型检查 | OK | 11s | ||
| 单测 | OK | 24s | ||
| feat/login | 安装依赖 | OK | 8s | |
| 单测 | FAIL | 6s | ||
| api | main | 编译 | OK | 14s |
| 集成测试 | OK | 38s |
项目 + 分支两列开了 mergeRows: true,连续相同值自动合并 rowSpan。
合并不会跨树形层级(不同 parentKey 的子行不会被合并到一起)。
单元格选区 + 一键复制
cell-selectable 让表格行为接近 Excel:
- 单击单元格 → 选中
- Shift 单击 / 鼠标拖动 → 矩形选区
- Ctrl/⌘ + C → 复制为 TSV 到剪贴板,可以直接粘到 Excel / Numbers / Google Sheets
| 姓名 | 数学 | 英语 | 物理 | 化学 | 生物 |
|---|---|---|---|---|---|
| 张三 | 92 | 88 | 79 | 84 | 91 |
| 李四 | 85 | 91 | 88 | 86 | 79 |
| 王五 | 78 | 95 | 81 | 73 | 85 |
| 赵六 | 96 | 82 | 94 | 90 | 88 |
| 钱七 | 73 | 79 | 71 | 80 | 75 |
像 Excel 一样:单击选单元格、Shift 单击 / 拖动扩展矩形选区,Ctrl/⌘ + C 复制为 TSV,可以直接粘到 Excel / Numbers。
column.format 会被尊重 —— 复制出去的就是显示的字符串。
列虚拟化(双向虚拟化)
100+ 列且每列内容简单(如二维数据矩阵)时,启用 col-virtual + 默认 col-width 让横向滚动也只渲染可见列;和 virtual 一起开就是双向虚拟化。
数据集:200 行 × 121 列 · 行 + 列双向虚拟化,DOM < 800 个节点。
| ID | c0 | c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c9 | c10 | c11 | c12 | c13 | c14 | c15 | c16 | c17 | c18 | c19 | c20 | c21 | c22 | c23 | c24 | c25 | c26 | c27 | c28 | c29 | c30 | c31 | c32 | c33 | c34 | c35 | c36 | c37 | c38 | c39 | c40 | c41 | c42 | c43 | c44 | c45 | c46 | c47 | c48 | c49 | c50 | c51 | c52 | c53 | c54 | c55 | c56 | c57 | c58 | c59 | c60 | c61 | c62 | c63 | c64 | c65 | c66 | c67 | c68 | c69 | c70 | c71 | c72 | c73 | c74 | c75 | c76 | c77 | c78 | c79 | c80 | c81 | c82 | c83 | c84 | c85 | c86 | c87 | c88 | c89 | c90 | c91 | c92 | c93 | c94 | c95 | c96 | c97 | c98 | c99 | c100 | c101 | c102 | c103 | c104 | c105 | c106 | c107 | c108 | c109 | c110 | c111 | c112 | c113 | c114 | c115 | c116 | c117 | c118 | c119 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| R-0000 | 0 | 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 | 77 | 84 | 91 | 98 | 105 | 112 | 119 | 126 | 133 | 140 | 147 | 154 | 161 | 168 | 175 | 182 | 189 | 196 | 203 | 210 | 217 | 224 | 231 | 238 | 245 | 252 | 259 | 266 | 273 | 280 | 287 | 294 | 301 | 308 | 315 | 322 | 329 | 336 | 343 | 350 | 357 | 364 | 371 | 378 | 385 | 392 | 399 | 406 | 413 | 420 | 427 | 434 | 441 | 448 | 455 | 462 | 469 | 476 | 483 | 490 | 497 | 504 | 511 | 518 | 525 | 532 | 539 | 546 | 553 | 560 | 567 | 574 | 581 | 588 | 595 | 602 | 609 | 616 | 623 | 630 | 637 | 644 | 651 | 658 | 665 | 672 | 679 | 686 | 693 | 700 | 707 | 714 | 721 | 728 | 735 | 742 | 749 | 756 | 763 | 770 | 777 | 784 | 791 | 798 | 805 | 812 | 819 | 826 | 833 |
| R-0001 | 13 | 20 | 27 | 34 | 41 | 48 | 55 | 62 | 69 | 76 | 83 | 90 | 97 | 104 | 111 | 118 | 125 | 132 | 139 | 146 | 153 | 160 | 167 | 174 | 181 | 188 | 195 | 202 | 209 | 216 | 223 | 230 | 237 | 244 | 251 | 258 | 265 | 272 | 279 | 286 | 293 | 300 | 307 | 314 | 321 | 328 | 335 | 342 | 349 | 356 | 363 | 370 | 377 | 384 | 391 | 398 | 405 | 412 | 419 | 426 | 433 | 440 | 447 | 454 | 461 | 468 | 475 | 482 | 489 | 496 | 503 | 510 | 517 | 524 | 531 | 538 | 545 | 552 | 559 | 566 | 573 | 580 | 587 | 594 | 601 | 608 | 615 | 622 | 629 | 636 | 643 | 650 | 657 | 664 | 671 | 678 | 685 | 692 | 699 | 706 | 713 | 720 | 727 | 734 | 741 | 748 | 755 | 762 | 769 | 776 | 783 | 790 | 797 | 804 | 811 | 818 | 825 | 832 | 839 | 846 |
| R-0002 | 26 | 33 | 40 | 47 | 54 | 61 | 68 | 75 | 82 | 89 | 96 | 103 | 110 | 117 | 124 | 131 | 138 | 145 | 152 | 159 | 166 | 173 | 180 | 187 | 194 | 201 | 208 | 215 | 222 | 229 | 236 | 243 | 250 | 257 | 264 | 271 | 278 | 285 | 292 | 299 | 306 | 313 | 320 | 327 | 334 | 341 | 348 | 355 | 362 | 369 | 376 | 383 | 390 | 397 | 404 | 411 | 418 | 425 | 432 | 439 | 446 | 453 | 460 | 467 | 474 | 481 | 488 | 495 | 502 | 509 | 516 | 523 | 530 | 537 | 544 | 551 | 558 | 565 | 572 | 579 | 586 | 593 | 600 | 607 | 614 | 621 | 628 | 635 | 642 | 649 | 656 | 663 | 670 | 677 | 684 | 691 | 698 | 705 | 712 | 719 | 726 | 733 | 740 | 747 | 754 | 761 | 768 | 775 | 782 | 789 | 796 | 803 | 810 | 817 | 824 | 831 | 838 | 845 | 852 | 859 |
| R-0003 | 39 | 46 | 53 | 60 | 67 | 74 | 81 | 88 | 95 | 102 | 109 | 116 | 123 | 130 | 137 | 144 | 151 | 158 | 165 | 172 | 179 | 186 | 193 | 200 | 207 | 214 | 221 | 228 | 235 | 242 | 249 | 256 | 263 | 270 | 277 | 284 | 291 | 298 | 305 | 312 | 319 | 326 | 333 | 340 | 347 | 354 | 361 | 368 | 375 | 382 | 389 | 396 | 403 | 410 | 417 | 424 | 431 | 438 | 445 | 452 | 459 | 466 | 473 | 480 | 487 | 494 | 501 | 508 | 515 | 522 | 529 | 536 | 543 | 550 | 557 | 564 | 571 | 578 | 585 | 592 | 599 | 606 | 613 | 620 | 627 | 634 | 641 | 648 | 655 | 662 | 669 | 676 | 683 | 690 | 697 | 704 | 711 | 718 | 725 | 732 | 739 | 746 | 753 | 760 | 767 | 774 | 781 | 788 | 795 | 802 | 809 | 816 | 823 | 830 | 837 | 844 | 851 | 858 | 865 | 872 |
| R-0004 | 52 | 59 | 66 | 73 | 80 | 87 | 94 | 101 | 108 | 115 | 122 | 129 | 136 | 143 | 150 | 157 | 164 | 171 | 178 | 185 | 192 | 199 | 206 | 213 | 220 | 227 | 234 | 241 | 248 | 255 | 262 | 269 | 276 | 283 | 290 | 297 | 304 | 311 | 318 | 325 | 332 | 339 | 346 | 353 | 360 | 367 | 374 | 381 | 388 | 395 | 402 | 409 | 416 | 423 | 430 | 437 | 444 | 451 | 458 | 465 | 472 | 479 | 486 | 493 | 500 | 507 | 514 | 521 | 528 | 535 | 542 | 549 | 556 | 563 | 570 | 577 | 584 | 591 | 598 | 605 | 612 | 619 | 626 | 633 | 640 | 647 | 654 | 661 | 668 | 675 | 682 | 689 | 696 | 703 | 710 | 717 | 724 | 731 | 738 | 745 | 752 | 759 | 766 | 773 | 780 | 787 | 794 | 801 | 808 | 815 | 822 | 829 | 836 | 843 | 850 | 857 | 864 | 871 | 878 | 885 |
| R-0005 | 65 | 72 | 79 | 86 | 93 | 100 | 107 | 114 | 121 | 128 | 135 | 142 | 149 | 156 | 163 | 170 | 177 | 184 | 191 | 198 | 205 | 212 | 219 | 226 | 233 | 240 | 247 | 254 | 261 | 268 | 275 | 282 | 289 | 296 | 303 | 310 | 317 | 324 | 331 | 338 | 345 | 352 | 359 | 366 | 373 | 380 | 387 | 394 | 401 | 408 | 415 | 422 | 429 | 436 | 443 | 450 | 457 | 464 | 471 | 478 | 485 | 492 | 499 | 506 | 513 | 520 | 527 | 534 | 541 | 548 | 555 | 562 | 569 | 576 | 583 | 590 | 597 | 604 | 611 | 618 | 625 | 632 | 639 | 646 | 653 | 660 | 667 | 674 | 681 | 688 | 695 | 702 | 709 | 716 | 723 | 730 | 737 | 744 | 751 | 758 | 765 | 772 | 779 | 786 | 793 | 800 | 807 | 814 | 821 | 828 | 835 | 842 | 849 | 856 | 863 | 870 | 877 | 884 | 891 | 898 |
| R-0006 | 78 | 85 | 92 | 99 | 106 | 113 | 120 | 127 | 134 | 141 | 148 | 155 | 162 | 169 | 176 | 183 | 190 | 197 | 204 | 211 | 218 | 225 | 232 | 239 | 246 | 253 | 260 | 267 | 274 | 281 | 288 | 295 | 302 | 309 | 316 | 323 | 330 | 337 | 344 | 351 | 358 | 365 | 372 | 379 | 386 | 393 | 400 | 407 | 414 | 421 | 428 | 435 | 442 | 449 | 456 | 463 | 470 | 477 | 484 | 491 | 498 | 505 | 512 | 519 | 526 | 533 | 540 | 547 | 554 | 561 | 568 | 575 | 582 | 589 | 596 | 603 | 610 | 617 | 624 | 631 | 638 | 645 | 652 | 659 | 666 | 673 | 680 | 687 | 694 | 701 | 708 | 715 | 722 | 729 | 736 | 743 | 750 | 757 | 764 | 771 | 778 | 785 | 792 | 799 | 806 | 813 | 820 | 827 | 834 | 841 | 848 | 855 | 862 | 869 | 876 | 883 | 890 | 897 | 904 | 911 |
| R-0007 | 91 | 98 | 105 | 112 | 119 | 126 | 133 | 140 | 147 | 154 | 161 | 168 | 175 | 182 | 189 | 196 | 203 | 210 | 217 | 224 | 231 | 238 | 245 | 252 | 259 | 266 | 273 | 280 | 287 | 294 | 301 | 308 | 315 | 322 | 329 | 336 | 343 | 350 | 357 | 364 | 371 | 378 | 385 | 392 | 399 | 406 | 413 | 420 | 427 | 434 | 441 | 448 | 455 | 462 | 469 | 476 | 483 | 490 | 497 | 504 | 511 | 518 | 525 | 532 | 539 | 546 | 553 | 560 | 567 | 574 | 581 | 588 | 595 | 602 | 609 | 616 | 623 | 630 | 637 | 644 | 651 | 658 | 665 | 672 | 679 | 686 | 693 | 700 | 707 | 714 | 721 | 728 | 735 | 742 | 749 | 756 | 763 | 770 | 777 | 784 | 791 | 798 | 805 | 812 | 819 | 826 | 833 | 840 | 847 | 854 | 861 | 868 | 875 | 882 | 889 | 896 | 903 | 910 | 917 | 924 |
| R-0008 | 104 | 111 | 118 | 125 | 132 | 139 | 146 | 153 | 160 | 167 | 174 | 181 | 188 | 195 | 202 | 209 | 216 | 223 | 230 | 237 | 244 | 251 | 258 | 265 | 272 | 279 | 286 | 293 | 300 | 307 | 314 | 321 | 328 | 335 | 342 | 349 | 356 | 363 | 370 | 377 | 384 | 391 | 398 | 405 | 412 | 419 | 426 | 433 | 440 | 447 | 454 | 461 | 468 | 475 | 482 | 489 | 496 | 503 | 510 | 517 | 524 | 531 | 538 | 545 | 552 | 559 | 566 | 573 | 580 | 587 | 594 | 601 | 608 | 615 | 622 | 629 | 636 | 643 | 650 | 657 | 664 | 671 | 678 | 685 | 692 | 699 | 706 | 713 | 720 | 727 | 734 | 741 | 748 | 755 | 762 | 769 | 776 | 783 | 790 | 797 | 804 | 811 | 818 | 825 | 832 | 839 | 846 | 853 | 860 | 867 | 874 | 881 | 888 | 895 | 902 | 909 | 916 | 923 | 930 | 937 |
| R-0009 | 117 | 124 | 131 | 138 | 145 | 152 | 159 | 166 | 173 | 180 | 187 | 194 | 201 | 208 | 215 | 222 | 229 | 236 | 243 | 250 | 257 | 264 | 271 | 278 | 285 | 292 | 299 | 306 | 313 | 320 | 327 | 334 | 341 | 348 | 355 | 362 | 369 | 376 | 383 | 390 | 397 | 404 | 411 | 418 | 425 | 432 | 439 | 446 | 453 | 460 | 467 | 474 | 481 | 488 | 495 | 502 | 509 | 516 | 523 | 530 | 537 | 544 | 551 | 558 | 565 | 572 | 579 | 586 | 593 | 600 | 607 | 614 | 621 | 628 | 635 | 642 | 649 | 656 | 663 | 670 | 677 | 684 | 691 | 698 | 705 | 712 | 719 | 726 | 733 | 740 | 747 | 754 | 761 | 768 | 775 | 782 | 789 | 796 | 803 | 810 | 817 | 824 | 831 | 838 | 845 | 852 | 859 | 866 | 873 | 880 | 887 | 894 | 901 | 908 | 915 | 922 | 929 | 936 | 943 | 950 |
| R-0010 | 130 | 137 | 144 | 151 | 158 | 165 | 172 | 179 | 186 | 193 | 200 | 207 | 214 | 221 | 228 | 235 | 242 | 249 | 256 | 263 | 270 | 277 | 284 | 291 | 298 | 305 | 312 | 319 | 326 | 333 | 340 | 347 | 354 | 361 | 368 | 375 | 382 | 389 | 396 | 403 | 410 | 417 | 424 | 431 | 438 | 445 | 452 | 459 | 466 | 473 | 480 | 487 | 494 | 501 | 508 | 515 | 522 | 529 | 536 | 543 | 550 | 557 | 564 | 571 | 578 | 585 | 592 | 599 | 606 | 613 | 620 | 627 | 634 | 641 | 648 | 655 | 662 | 669 | 676 | 683 | 690 | 697 | 704 | 711 | 718 | 725 | 732 | 739 | 746 | 753 | 760 | 767 | 774 | 781 | 788 | 795 | 802 | 809 | 816 | 823 | 830 | 837 | 844 | 851 | 858 | 865 | 872 | 879 | 886 | 893 | 900 | 907 | 914 | 921 | 928 | 935 | 942 | 949 | 956 | 963 |
| R-0011 | 143 | 150 | 157 | 164 | 171 | 178 | 185 | 192 | 199 | 206 | 213 | 220 | 227 | 234 | 241 | 248 | 255 | 262 | 269 | 276 | 283 | 290 | 297 | 304 | 311 | 318 | 325 | 332 | 339 | 346 | 353 | 360 | 367 | 374 | 381 | 388 | 395 | 402 | 409 | 416 | 423 | 430 | 437 | 444 | 451 | 458 | 465 | 472 | 479 | 486 | 493 | 500 | 507 | 514 | 521 | 528 | 535 | 542 | 549 | 556 | 563 | 570 | 577 | 584 | 591 | 598 | 605 | 612 | 619 | 626 | 633 | 640 | 647 | 654 | 661 | 668 | 675 | 682 | 689 | 696 | 703 | 710 | 717 | 724 | 731 | 738 | 745 | 752 | 759 | 766 | 773 | 780 | 787 | 794 | 801 | 808 | 815 | 822 | 829 | 836 | 843 | 850 | 857 | 864 | 871 | 878 | 885 | 892 | 899 | 906 | 913 | 920 | 927 | 934 | 941 | 948 | 955 | 962 | 969 | 976 |
| R-0012 | 156 | 163 | 170 | 177 | 184 | 191 | 198 | 205 | 212 | 219 | 226 | 233 | 240 | 247 | 254 | 261 | 268 | 275 | 282 | 289 | 296 | 303 | 310 | 317 | 324 | 331 | 338 | 345 | 352 | 359 | 366 | 373 | 380 | 387 | 394 | 401 | 408 | 415 | 422 | 429 | 436 | 443 | 450 | 457 | 464 | 471 | 478 | 485 | 492 | 499 | 506 | 513 | 520 | 527 | 534 | 541 | 548 | 555 | 562 | 569 | 576 | 583 | 590 | 597 | 604 | 611 | 618 | 625 | 632 | 639 | 646 | 653 | 660 | 667 | 674 | 681 | 688 | 695 | 702 | 709 | 716 | 723 | 730 | 737 | 744 | 751 | 758 | 765 | 772 | 779 | 786 | 793 | 800 | 807 | 814 | 821 | 828 | 835 | 842 | 849 | 856 | 863 | 870 | 877 | 884 | 891 | 898 | 905 | 912 | 919 | 926 | 933 | 940 | 947 | 954 | 961 | 968 | 975 | 982 | 989 |
| R-0013 | 169 | 176 | 183 | 190 | 197 | 204 | 211 | 218 | 225 | 232 | 239 | 246 | 253 | 260 | 267 | 274 | 281 | 288 | 295 | 302 | 309 | 316 | 323 | 330 | 337 | 344 | 351 | 358 | 365 | 372 | 379 | 386 | 393 | 400 | 407 | 414 | 421 | 428 | 435 | 442 | 449 | 456 | 463 | 470 | 477 | 484 | 491 | 498 | 505 | 512 | 519 | 526 | 533 | 540 | 547 | 554 | 561 | 568 | 575 | 582 | 589 | 596 | 603 | 610 | 617 | 624 | 631 | 638 | 645 | 652 | 659 | 666 | 673 | 680 | 687 | 694 | 701 | 708 | 715 | 722 | 729 | 736 | 743 | 750 | 757 | 764 | 771 | 778 | 785 | 792 | 799 | 806 | 813 | 820 | 827 | 834 | 841 | 848 | 855 | 862 | 869 | 876 | 883 | 890 | 897 | 904 | 911 | 918 | 925 | 932 | 939 | 946 | 953 | 960 | 967 | 974 | 981 | 988 | 995 | 3 |
| R-0014 | 182 | 189 | 196 | 203 | 210 | 217 | 224 | 231 | 238 | 245 | 252 | 259 | 266 | 273 | 280 | 287 | 294 | 301 | 308 | 315 | 322 | 329 | 336 | 343 | 350 | 357 | 364 | 371 | 378 | 385 | 392 | 399 | 406 | 413 | 420 | 427 | 434 | 441 | 448 | 455 | 462 | 469 | 476 | 483 | 490 | 497 | 504 | 511 | 518 | 525 | 532 | 539 | 546 | 553 | 560 | 567 | 574 | 581 | 588 | 595 | 602 | 609 | 616 | 623 | 630 | 637 | 644 | 651 | 658 | 665 | 672 | 679 | 686 | 693 | 700 | 707 | 714 | 721 | 728 | 735 | 742 | 749 | 756 | 763 | 770 | 777 | 784 | 791 | 798 | 805 | 812 | 819 | 826 | 833 | 840 | 847 | 854 | 861 | 868 | 875 | 882 | 889 | 896 | 903 | 910 | 917 | 924 | 931 | 938 | 945 | 952 | 959 | 966 | 973 | 980 | 987 | 994 | 2 | 9 | 16 |
| R-0015 | 195 | 202 | 209 | 216 | 223 | 230 | 237 | 244 | 251 | 258 | 265 | 272 | 279 | 286 | 293 | 300 | 307 | 314 | 321 | 328 | 335 | 342 | 349 | 356 | 363 | 370 | 377 | 384 | 391 | 398 | 405 | 412 | 419 | 426 | 433 | 440 | 447 | 454 | 461 | 468 | 475 | 482 | 489 | 496 | 503 | 510 | 517 | 524 | 531 | 538 | 545 | 552 | 559 | 566 | 573 | 580 | 587 | 594 | 601 | 608 | 615 | 622 | 629 | 636 | 643 | 650 | 657 | 664 | 671 | 678 | 685 | 692 | 699 | 706 | 713 | 720 | 727 | 734 | 741 | 748 | 755 | 762 | 769 | 776 | 783 | 790 | 797 | 804 | 811 | 818 | 825 | 832 | 839 | 846 | 853 | 860 | 867 | 874 | 881 | 888 | 895 | 902 | 909 | 916 | 923 | 930 | 937 | 944 | 951 | 958 | 965 | 972 | 979 | 986 | 993 | 1 | 8 | 15 | 22 | 29 |
| R-0016 | 208 | 215 | 222 | 229 | 236 | 243 | 250 | 257 | 264 | 271 | 278 | 285 | 292 | 299 | 306 | 313 | 320 | 327 | 334 | 341 | 348 | 355 | 362 | 369 | 376 | 383 | 390 | 397 | 404 | 411 | 418 | 425 | 432 | 439 | 446 | 453 | 460 | 467 | 474 | 481 | 488 | 495 | 502 | 509 | 516 | 523 | 530 | 537 | 544 | 551 | 558 | 565 | 572 | 579 | 586 | 593 | 600 | 607 | 614 | 621 | 628 | 635 | 642 | 649 | 656 | 663 | 670 | 677 | 684 | 691 | 698 | 705 | 712 | 719 | 726 | 733 | 740 | 747 | 754 | 761 | 768 | 775 | 782 | 789 | 796 | 803 | 810 | 817 | 824 | 831 | 838 | 845 | 852 | 859 | 866 | 873 | 880 | 887 | 894 | 901 | 908 | 915 | 922 | 929 | 936 | 943 | 950 | 957 | 964 | 971 | 978 | 985 | 992 | 0 | 7 | 14 | 21 | 28 | 35 | 42 |
| R-0017 | 221 | 228 | 235 | 242 | 249 | 256 | 263 | 270 | 277 | 284 | 291 | 298 | 305 | 312 | 319 | 326 | 333 | 340 | 347 | 354 | 361 | 368 | 375 | 382 | 389 | 396 | 403 | 410 | 417 | 424 | 431 | 438 | 445 | 452 | 459 | 466 | 473 | 480 | 487 | 494 | 501 | 508 | 515 | 522 | 529 | 536 | 543 | 550 | 557 | 564 | 571 | 578 | 585 | 592 | 599 | 606 | 613 | 620 | 627 | 634 | 641 | 648 | 655 | 662 | 669 | 676 | 683 | 690 | 697 | 704 | 711 | 718 | 725 | 732 | 739 | 746 | 753 | 760 | 767 | 774 | 781 | 788 | 795 | 802 | 809 | 816 | 823 | 830 | 837 | 844 | 851 | 858 | 865 | 872 | 879 | 886 | 893 | 900 | 907 | 914 | 921 | 928 | 935 | 942 | 949 | 956 | 963 | 970 | 977 | 984 | 991 | 998 | 6 | 13 | 20 | 27 | 34 | 41 | 48 | 55 |
| R-0018 | 234 | 241 | 248 | 255 | 262 | 269 | 276 | 283 | 290 | 297 | 304 | 311 | 318 | 325 | 332 | 339 | 346 | 353 | 360 | 367 | 374 | 381 | 388 | 395 | 402 | 409 | 416 | 423 | 430 | 437 | 444 | 451 | 458 | 465 | 472 | 479 | 486 | 493 | 500 | 507 | 514 | 521 | 528 | 535 | 542 | 549 | 556 | 563 | 570 | 577 | 584 | 591 | 598 | 605 | 612 | 619 | 626 | 633 | 640 | 647 | 654 | 661 | 668 | 675 | 682 | 689 | 696 | 703 | 710 | 717 | 724 | 731 | 738 | 745 | 752 | 759 | 766 | 773 | 780 | 787 | 794 | 801 | 808 | 815 | 822 | 829 | 836 | 843 | 850 | 857 | 864 | 871 | 878 | 885 | 892 | 899 | 906 | 913 | 920 | 927 | 934 | 941 | 948 | 955 | 962 | 969 | 976 | 983 | 990 | 997 | 5 | 12 | 19 | 26 | 33 | 40 | 47 | 54 | 61 | 68 |
200 行 × 121 列 = 24200 单元格,DOM 实际不到 800 个节点。注意:列虚拟化下 column.fixed 仍然钉住,但要求被钉的列总宽不要超过视口的 1/3,否则可视区会很拥挤。
树形拖拽(跨父节点)
在 row-reorderable 之上加 tree-reorderable,拖拽行为升级为 Excel / Finder / 看板风格的”上下/中间”三段:
- 拖到目标行上 1/4 → 放在它前面(同父)
- 拖到目标行下 1/4 → 放在它后面(同父)
- 拖到中间一半 → 拖进目标行作为它的子节点
不会让你把祖先放进自己的后代里。
| 名称 | 大小 | ||
|---|---|---|---|
| src/ | — | ||
| components/ | — | ||
| Table.vue | 32 KB | ||
| Modal.vue | 8 KB | ||
| utils/ | — | ||
| dom.ts | 4 KB | ||
| index.ts | 1 KB | ||
| tests/ | — | ||
| table.spec.ts | 12 KB |
拖某行的 ⋮⋮ 把柄到目标行:上 1/4 = 放到上面,下 1/4 = 放到下面,中间 = 拖**进**目标行做子节点。
@tree-reorder 事件给到 { fromKey, toKey, pos: 'above' | 'below' | 'inside', rows },业务方可以决定要不要持久化到后端。
TSV 反向粘贴
在 cell-selectable 上再加 cell-pastable:单击或框选起点单元格,按 Ctrl/⌘ + V 粘贴整片 TSV / CSV,从起点开始按行写回;column.editable 控制每格能否写、column.editValidate 拦截非法值。
| SKU | 价格 | 库存 |
|---|---|---|
| SKU-1000 | 100 | 50 |
| SKU-1001 | 113 | 46 |
| SKU-1002 | 126 | 42 |
| SKU-1003 | 139 | 38 |
| SKU-1004 | 152 | 34 |
| SKU-1005 | 165 | 30 |
| SKU-1006 | 178 | 26 |
| SKU-1007 | 191 | 22 |
操作步骤:① 单击任意单元格作为粘贴起点(或者 Shift 框选一片范围)。② 在 Excel / Numbers / Google Sheets 里复制一片单元格。③ 回到这里按 Ctrl/⌘ + V,TSV 自动按起点写回; editValidate 不通过的格子被跳过;负数库存会被拒绝。
@cell-paste 事件返回 { applied, skipped },可以拿来给个 toast 提示。
历史撤销
history-enabled 把 sort / filter / search / pagination / columnsState 的每一次变化记到内存栈,监听 Ctrl/⌘ + Z 撤销、Ctrl/⌘ + Shift + Z / Ctrl + Y 重做。history-depth 控制最多保留多少步(默认 50)。
| ID | 客户 | 金额 | 状态 |
|---|---|---|---|
| O-1000 | Alice | ¥100 | paid |
| O-1001 | Bob | ¥137 | pending |
| O-1002 | Carol | ¥174 | refunded |
| O-1003 | Dave | ¥211 | paid |
| O-1004 | Eve | ¥248 | pending |
| O-1005 | Frank | ¥285 | refunded |
| O-1006 | Alice | ¥322 | paid |
| O-1007 | Bob | ¥359 | pending |
| O-1008 | Carol | ¥396 | refunded |
| O-1009 | Dave | ¥433 | paid |
| O-1010 | Eve | ¥470 | pending |
| O-1011 | Frank | ¥507 | refunded |
| O-1012 | Alice | ¥544 | paid |
| O-1013 | Bob | ¥581 | pending |
| O-1014 | Carol | ¥618 | refunded |
| O-1015 | Dave | ¥655 | paid |
| O-1016 | Eve | ¥692 | pending |
| O-1017 | Frank | ¥729 | refunded |
| O-1018 | Alice | ¥766 | paid |
| O-1019 | Bob | ¥803 | pending |
| O-1020 | Carol | ¥840 | refunded |
| O-1021 | Dave | ¥877 | paid |
| O-1022 | Eve | ¥914 | pending |
| O-1023 | Frank | ¥951 | refunded |
| O-1024 | Alice | ¥988 | paid |
| O-1025 | Bob | ¥125 | pending |
| O-1026 | Carol | ¥162 | refunded |
| O-1027 | Dave | ¥199 | paid |
| O-1028 | Eve | ¥236 | pending |
| O-1029 | Frank | ¥273 | refunded |
点列头 / 改过滤 / 翻页都进历史栈。Ctrl/⌘ + Z 撤销,Ctrl/⌘ + Shift + Z 重做。 当前:可撤销 = 否 · 可重做 = 否
@history-change 事件吐 { canUndo, canRedo },方便业务方亮 / 灭对应按钮。组件实例上还暴露 undo() / redo() 命令式调用。
变高行虚拟滚动
virtual 默认要求所有行高一致。如果每行高度不固定(比如 body 是 markdown 渲染、有多张图片),传 :get-row-height="(idx) => 42" 让组件按累计偏移做二分查找定位可见窗口。
1000 行 · 每行高度通过 get-row-height 函数计算(短的 42px,长的 ~120px)。
| ID | 标题 | 正文 |
|---|---|---|
| N-0 | Note #1 | lorem ipsum dolor sit amet |
| N-1 | Note #2 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-2 | Note #3 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-3 | Note #4 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-4 | Note #5 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-5 | Note #6 | lorem ipsum dolor sit amet |
| N-6 | Note #7 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-7 | Note #8 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-8 | Note #9 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-9 | Note #10 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-10 | Note #11 | lorem ipsum dolor sit amet |
| N-11 | Note #12 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-12 | Note #13 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
| N-13 | Note #14 | lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet |
注意:getRowHeight 必须纯函数,组件会在每次行数变化时重算累计偏移。如果高度真的需要等 DOM 渲染后才知道,目前的建议是先返回一个估值,DOM 测量完成后再 setState 改回真实值;后续会出 auto-row-height 模式自动用 ResizeObserver。
行编辑模式
把 edit-mode="row" 打开,双击任意行进入整行编辑:所有 editable 列同时变为 input,行尾单独多出一行 Save / Cancel 按钮。Enter 提交、Esc 取消,校验失败保留编辑态。
| 姓名 | 邮箱 | 角色 | 配额 |
|---|---|---|---|
| Jane Liu | [email protected] | admin | 5 |
| Bob Wang | [email protected] | editor | 3 |
| Alice Chen | [email protected] | viewer | 1 |
双击任意行进入整行编辑:所有可编辑列同时变成 input。底部出现 Save / Cancel;Enter 也可提交,Esc 取消。
每个 cell 改动都通过 @cell-edit 事件抛出(与单格编辑一致),业务方决定是否调接口、是否合并提交。组件不 mutate rows。
批量操作工具栏
batch-actions + selectable="multiple":选中至少一行后,表头上方出现 sticky 的批量操作条,自带 计数 / 导出选中 / 清空,可以通过 #batch-actions 插槽塞自定义按钮(接到 selectedKeys)。
| 标题 | 负责人 | 截止 | 状态 | |
|---|---|---|---|---|
| Task #1 · fix bug | Alice | 2026-05-10 | todo | |
| Task #2 · design review | Bob | 2026-05-11 | doing | |
| Task #3 · API draft | Carol | 2026-05-12 | done | |
| Task #4 · merge PR | Dave | 2026-05-13 | todo | |
| Task #5 · release notes | Alice | 2026-05-14 | doing | |
| Task #6 · fix bug | Bob | 2026-05-15 | done | |
| Task #7 · design review | Carol | 2026-05-16 | todo | |
| Task #8 · API draft | Dave | 2026-05-17 | doing | |
| Task #9 · merge PR | Alice | 2026-05-18 | done | |
| Task #10 · release notes | Bob | 2026-05-19 | todo | |
| Task #11 · fix bug | Carol | 2026-05-20 | doing | |
| Task #12 · design review | Dave | 2026-05-21 | done |
勾选任意行后顶部出现批量操作条。内置 "导出选中" / "清空",再加上业务自定义的 "标记完成" / "删除"。
冻结行
pinned-row-keys 的列表里所有 rowKey 会置顶并 sticky 到表体顶部,下面的内容滚动时它们一直可见。常用于”自选”、“高优先级”等需要永远先看到的行。
| 币对 | 价格 | 24h | 成交量 |
|---|---|---|---|
| BTC/USDT | $ 67,842 | +1.24% | 32.4B |
| ETH/USDT | $ 3,521 | -0.82% | 18.9B |
| SOL/USDT | $ 184.1 | +3.41% | 4.7B |
| AVAX/USDT | $ 42.7 | +0.93% | 780M |
| DOT/USDT | $ 7.31 | -1.04% | 420M |
| MATIC/USDT | $ 0.84 | -2.18% | 690M |
| LINK/USDT | $ 18.4 | +0.21% | 510M |
| ARB/USDT | $ 0.94 | +1.92% | 320M |
| ATOM/USDT | $ 8.2 | +0.42% | 190M |
| NEAR/USDT | $ 5.6 | -0.74% | 230M |
BTC / ETH 被固定在表体顶部 sticky;下面的列表可以滚动,固定行始终可见。
auto-row-height
virtual 默认按固定 rowHeight。如果 row 内容确实需要等浏览器渲染才能知道高度(富文本、图片、嵌套布局),开启 auto-row-height:组件内置 ResizeObserver 监听每个可见行的真实高度并写回累积偏移表。
600 行,body 长度从 1× 到 6× 不等。auto-row-height 自动用 ResizeObserver 测每行真实高度。
| ID | 标题 | 正文 |
|---|---|---|
| N-0 | Note 1 | lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-1 | Note 2 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-2 | Note 3 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-3 | Note 4 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-4 | Note 5 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-5 | Note 6 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-6 | Note 7 | lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-7 | Note 8 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-8 | Note 9 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-9 | Note 10 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-10 | Note 11 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-11 | Note 12 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-12 | Note 13 | lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-13 | Note 14 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-14 | Note 15 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-15 | Note 16 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-16 | Note 17 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
| N-17 | Note 18 | lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem ipsum dolor sit amet, consectetur adipiscing elit. |
第一次渲染会用 rowHeight 作为估值;测量完成后 padTop / padBottom 自动重算。比手写 getRowHeight 函数更省心,但代价是首次滚动可能短暂看到滚动条抖动(系统校准位移)。
服务端模式 · 防抖
输入框 / 过滤面板的状态变化每一帧都触发会把后端打爆。给 :server-debounce="300"(ms)让 update:globalSearch 与 update:filters 在静默 300ms 后才发出。其它信号(点排序 / 翻页)是显式点击,不需要防抖,所以保持同步触发。
<CfTable
:columns="columns"
:rows="rows"
:pagination="{ page, pageSize, total }"
:server-debounce="300"
@update:globalSearch="(q) => loadData({ search: q })"
@update:filters="(f) => loadData({ filters: f })"
/>
服务端模式
只要 pagination.total 是个数字,组件就不自己做客户端 slice:sort / filter / pagination 都通过事件抛给上层,由你去请求服务端:
<CfTable
:columns="columns"
:rows="rows"
:sort="sort"
:filters="filters"
:pagination="{ page, pageSize, total }"
:loading="loading"
@update:sort="(s) => loadData({ sort: s, filters, page, pageSize })"
@update:filters="(f) => loadData({ sort, filters: f, page: 1, pageSize })"
@update:pagination="(p) => loadData({ sort, filters, ...p })"
@update:globalSearch="(q) => loadData({ search: q })"
/>
API
TableProps
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
columns | TableColumn[] | — | 列定义;可嵌套 children 形成多行表头 |
rows | T[] | — | 数据行;row[childrenField] 是子行 |
rowKey | string | (row, i) => string | i | 行唯一 key,影响选择 / 展开 |
size | 'sm' | 'md' | 'lg' | 'md' | 整体密度 |
variant | 'default' | 'bordered' | 'striped' | 'default' | 视觉变体 |
hoverable | boolean | true | 行 hover 高亮 |
stickyHeader | boolean | false | 表头粘顶(建议同时给 height) |
height | number | string | — | 表体最大高度,超出滚动 |
loading | boolean | false | 显示加载遮罩 |
emptyText | string | '暂无数据' | 空数据提示 |
sort | TableSort | TableSort[] | null | — | 当前排序(受控) |
defaultSort | 同上 | null | 初始排序(非受控) |
multiSort | boolean | false | 启用 Shift 多列排序 |
filters | Record<string, unknown> | — | 列过滤值(受控) |
defaultFilters | 同上 | {} | 初始过滤值(非受控) |
globalSearch | string | — | 全局搜索值(受控) |
defaultGlobalSearch | string | '' | 初始搜索(非受控) |
pagination | TablePagination | false | — | 分页配置;false 强制不分页 |
defaultPagination | TablePagination | — | 初始分页(非受控) |
selectable | 'single' | 'multiple' | — | 行选择模式 |
modelValue | string | string[] | null | null | 选中行 key |
expandable | boolean | false | 启用展开按钮 |
expandRender | (row, i) => any | — | 自定义展开内容 |
expandedRowKeys | string[] | — | 展开行(受控) |
defaultExpandedRowKeys | string[] | [] | 初始展开行(非受控) |
childrenField | string | 'children' | 树形数据字段名 |
treeIndent | number | 16 | 树形每层缩进(px) |
columnsState | TableColumnsState | — | 列状态:隐藏 / 顺序 / 宽度(受控) |
defaultColumnsState | 同上 | — | 初始列状态(非受控) |
resizable | boolean | false | 全局开启列调宽 |
reorderable | boolean | false | 全局开启列拖拽换序 |
showSummary | boolean | false | 自动按 column.summary 渲染总计行 |
summary | TableSummaryRow[] | — | 额外手动总计行 |
toolbar | 'auto' | 'none' | 'none' | 工具栏:搜索 + 列可见性 + 导出 |
virtual | boolean | false | 启用虚拟滚动(要求 height + 固定 rowHeight) |
rowHeight | number | 36 | 虚拟模式下每行高度(px) |
overscan | number | 6 | 虚拟模式上下额外渲染的行数 |
rowReorderable | boolean | false | 启用行拖拽换序(最左侧出现拖把柄) |
exportable | boolean | false | 工具栏出现 CSV 导出按钮 |
exportFileName | string | 'table' | 导出文件名(不含扩展名) |
persistKey | string | — | localStorage 自动持久化 columnsState 的 key |
serverDebounce | number | 0 | 全局搜索 / 过滤事件的防抖(ms) |
cellSelectable | boolean | false | Excel 风格单元格选区 + Ctrl/⌘+C 复制 TSV |
colVirtual | boolean | false | 启用列虚拟化(推荐 100+ 列时开启) |
colWidth | number | 120 | 列虚拟化的默认列宽(px) |
colOverscan | number | 4 | 列虚拟化左右额外渲染的列数 |
treeReorderable | boolean | false | 配合 rowReorderable 启用 above/below/inside 三段拖拽 |
cellPastable | boolean | false | 启用 Ctrl/⌘+V 粘贴 TSV 反向写回 |
historyEnabled | boolean | false | 启用快照栈:sort/filter/search/page/columnsState 全部入栈 |
historyDepth | number | 50 | 历史栈最大深度 |
getRowHeight | (index) => number | — | 虚拟滚动下指定每行高度,用于变高行 |
editMode | 'cell' | 'row' | 'cell' | 'row' 时双击整行进入编辑,所有 editable 列同时变 input |
batchActions | boolean | false | 多选状态下顶部出 sticky 操作条 |
pinnedRowKeys | string[] | — | 这些 rowKey 对应的行置顶 sticky |
autoRowHeight | boolean | false | ResizeObserver 自动测量变高行 |
TableColumn
| 字段 | 类型 | 说明 |
|---|---|---|
key | string | 必填唯一 ID |
title | string | 表头文字 |
dataIndex | string | 数据字段;缺省回退到 key |
width / minWidth / maxWidth | number | string | 几何 |
align | 'left' | 'center' | 'right' | 对齐 |
ellipsis | boolean | 文本溢出截断 |
sortable | boolean | 启用此列排序 |
sortFn | (a, b) => number | 自定义排序函数 |
filterable | boolean | 启用此列过滤 |
filterType | 'text' | 'select' | 'number-range' | 'date-range' | 过滤面板类型 |
filterOptions | {label,value}[] | select 模式下的选项 |
filterFn | (filterValue, row, i) => boolean | 自定义过滤函数 |
fixed | 'left' | 'right' | 固定列 |
resizable / reorderable / hideable | boolean | 单列覆盖全局开关 |
hidden | boolean | 默认隐藏 |
render | (value, row, i) => any | 自定义单元格渲染 |
format | (value, row, i) => string | 仅做字符串格式化 |
headerRender | () => any | 自定义表头 |
children | TableColumn[] | 多行表头分组 |
summary | 'sum' | 'avg' | 'count' | (rows) => any | 总计聚合 |
summaryRender | (value) => any | 总计单元格自定义渲染 |
mergeRows | boolean | 'consecutive' | (cur, prev) => boolean | 连续相同值自动合并 rowSpan |
editable | boolean | (row, i) => boolean | 该列是否可双击编辑 |
editType | 'text' | 'number' | 'select' | 编辑器类型 |
editOptions | {label,value}[] | select 模式选项 |
editValidate | (value, row, i) => boolean | 提交校验,返回 false 阻止 |
exportable | boolean | false 时 CSV 导出跳过该列 |
exportRender | (value, row, i) => string | 导出时单元格的字符串化方式 |
cellClass | string | (row, i) => string | 单元格 class |
Events / Slots
| Event / Slot | Payload | 说明 |
|---|---|---|
update:sort / sort-change | TableSort | TableSort[] | null | 排序变化 |
update:filters | Record<string, unknown> | 过滤值变化 |
update:globalSearch | string | 搜索框变化 |
update:pagination | TablePagination | 翻页 / 改页大小 |
update:modelValue | string | string[] | null | 选中行变化 |
update:columnsState | TableColumnsState | 拖拽 / 隐藏 / 调宽 |
update:expandedRowKeys | string[] | 展开行变化 |
update:rows / row-reorder | T[] / {from,to,rows} | 行拖拽换序 |
tree-reorder | {fromKey, toKey, pos, rows} | 树形拖拽(含 above/below/inside) |
cell-edit | {row, column, oldValue, newValue, index} | 单元格内联编辑提交 |
cell-paste | {applied, skipped} | TSV 粘贴完成后 |
export | string | CSV 导出(拿到 csv 字符串后自定义处理) |
history-change | {canUndo, canRedo} | 历史栈状态变化 |
row-click | (row, i) | 选择关闭时单击行 |
#header:<key> | — | 自定义表头节点 |
#cell:<key> | { row, index, value } | 自定义单元格 |
#empty | — | 自定义空状态 |
#toolbar-left / #toolbar-right | — | 工具栏左右扩展槽 |
性能笔记
- 内部所有派生(filtered → sorted → paginated → flat tree)都用
computed/useMemo,只在依赖变时重算。 - 列调宽 / 拖拽换序更新的是列状态对象,不会触发整张表重排——只有命中变化的列重新计算 style。
- 行 1k+ 的场景建议同时启用
pagination+sticky-header+height。如果必须一次渲染上万行,可以用expandable+children做”折叠树”分批展示;后续会出独立的<CfVirtualTable>做真正的虚拟滚动。 - 在 React 端
Row组件已经独立化,可在外部用memo进一步包装;在 Vue 端单元格 render 函数会被 v-for 自动 patch。
反馈与讨论
Table 表格 的讨论