Table
Data table — multi-column sort, column reorder & resize, pinned columns, filtering, pagination, row expansion, tree data, summary footer, sticky header, global search, column visibility.
CfTable is one of the most feature-complete components in the library. Design choices:
- Client-side derivation by default — global search, per-column filtering, sorting, and pagination all run inside the component. Switch to server mode by passing a numeric
pagination.total. - Column state is uncontrolled by default — visibility / order / widths live in internal state; emit through
update:columnsStateto fully control them. - Zero third-party dependencies — HTML5 DnD for column reorder, PointerEvent for resize, native
position: stickyfor pinned columns and the header.
Basic usage
columns + rows + optional rowKey. column.format formats values; column.render returns VNodes / strings.
| 订单号 | 客户 | 金额 | 状态 | 日期 |
|---|---|---|---|---|
| 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 |
Row selection
Single-select via selectable="single", multi-select via selectable="multiple". v-model accepts a string or string[] keyed by rowKey.
| 姓名 | 角色 | 团队 | |
|---|---|---|---|
| Alice | 前端 | Web | |
| Bob | 后端 | API | |
| Carol | 设计 | UX | |
| Dave | 运维 | Infra |
["1","3"]Multi-column sort
With multi-sort enabled, hold Shift / ⌘ while clicking a header to add a secondary sort. Clicking the same column again cycles asc → desc → off. The header shows the column’s index in the sort queue.
| 仓库 | 语言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 priority follows the order of the sort array. For full control bind :sort to your own ref.
Column resize + reorder
With resizable, every column gets a 6px col-resize handle on its right edge. Dragging updates internal state in real time; @update:columns-state exposes the new widths for persistence. reorderable makes headers drag-and-drop 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 |
↔ 拖拽列右边缘改宽度;按住表头拖到目标列改顺序。
Per-column overrides: resizable: false / reorderable: false.
Pinned columns + sticky header
column.fixed: 'left' | 'right' pins columns to either side so they stay visible during horizontal scroll. Combine with sticky-header + :height for a sticky top row.
| 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 和右侧 状态 列被钉住;中间区域可横向滚动;表头粘在顶部。
Row expansion + tree data
expandable adds an expand button per row:
- Pass
expand-renderto render custom expanded content (order details, nested charts, etc.). - If a row carries a
childrenarray, the table treats it as tree data and indents each level bytree-indentpixels.
| 名称 | 大小 | 最近修改 | |
|---|---|---|---|
| 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 lifts expansion state up to the parent — handy for syncing to the URL or localStorage.
Toolbar + summary + pagination + filters
Set toolbar="auto" to surface global search + column visibility menu. column.filterable puts a funnel button in each column header; filterType toggles between select / number-range / plain text. column.summary accepts 'sum' | 'avg' | 'count' or a function — a summary row is rendered at the bottom and updates with the filtered result.
| 订单号 | 产品 | 套餐 | 单价 | 数量 | 合计 |
|---|---|---|---|---|---|
| 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 }" is uncontrolled. For full control bind pagination to a current value and listen to @update:pagination.
Virtual scrolling
With virtual enabled, the body only renders visible rows + an overscan buffer above/below — DOM count stays in the dozens. Requires every row to use a fixed row-height (default 36px). Combine with :height + sticky-header.
数据集大小:50,000 行 · 实际渲染的 DOM 节点数始终 ~30。
| 订单 ID | 币对 | 价格 | 数量 | 方向 | 时间 |
|---|---|---|---|---|---|
| T-000000 | BTC | $ 1,000 | 0.13 | sell | 07:49:06 |
| T-000001 | ETH | $ 1,039.98 | 0.26 | buy | 07:49:05 |
| T-000002 | SOL | $ 1,079.87 | 0.39 | buy | 07:49:04 |
| T-000003 | AVAX | $ 1,119.55 | 0.52 | sell | 07:49:03 |
| T-000004 | DOT | $ 1,158.94 | 0.65 | buy | 07:49:02 |
| T-000005 | MATIC | $ 1,197.92 | 0.78 | buy | 07:49:01 |
| T-000006 | LINK | $ 1,236.42 | 0.91 | sell | 07:49:00 |
| T-000007 | ARB | $ 1,274.32 | 1.04 | buy | 07:48:59 |
| T-000008 | BTC | $ 1,311.53 | 1.17 | buy | 07:48:58 |
| T-000009 | ETH | $ 1,347.97 | 1.30 | sell | 07:48:57 |
| T-000010 | SOL | $ 1,383.54 | 1.43 | buy | 07:48:56 |
| T-000011 | AVAX | $ 1,418.15 | 1.56 | buy | 07:48:55 |
| T-000012 | DOT | $ 1,451.71 | 1.69 | sell | 07:48:54 |
| T-000013 | MATIC | $ 1,484.15 | 1.82 | buy | 07:48:53 |
| T-000014 | LINK | $ 1,515.37 | 1.95 | buy | 07:48:52 |
| T-000015 | ARB | $ 1,545.31 | 2.08 | sell | 07:48:51 |
| T-000016 | BTC | $ 1,573.88 | 2.21 | buy | 07:48:50 |
| T-000017 | ETH | $ 1,601.02 | 2.34 | buy | 07:48:49 |
| T-000018 | SOL | $ 1,626.66 | 2.47 | sell | 07:48:48 |
A 50,000-row dataset renders ~30 <tr> elements at any time. Sort / filter / pinned columns / global search all keep working. Note: in virtual mode, expandable rows are also constrained to the row height — for rich expanded content prefer a Modal or disable virtual scrolling.
Row reorder
row-reorderable adds a ⋮⋮ drag handle column on the left. Drag a row to reorder; @update:rows emits the new array. Use @row-reorder if you also want from/to indices.
| 步骤 | 负责人 | 预计 | 状态 | |
|---|---|---|---|---|
| 需求评审 | 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 拿到新数组。
Only top-level (level 0) rows are draggable; tree children stay put.
Inline editing
Add editable: true to a column with optional editType: 'text' | 'number' | 'select'. Double-click a cell to enter edit mode:
editValidaterejects invalid values; on failure the cell stays in edit mode.- Commit: Enter / blur. Cancel: Esc.
@cell-editemits{ row, column, oldValue, newValue, index }— the parent decides how to write back.
| 姓名 | 角色 | 配额 |
|---|---|---|
| Jane | admin | 5 |
| Bob | editor | 3 |
| Alice | viewer | 1 |
双击单元格进入编辑:text / select / number。Enter / blur 提交,Esc 取消。 数字列加了 editValidate,负数会被拒绝。
The component never mutates
rowsdirectly: every commit goes throughcell-edit, so you choose whether to apply locally, hit an API, or merge.
CSV export
exportable renders an Export button in the toolbar. Clicking it downloads the filtered result. The CSV is UTF-8 with a BOM, so Excel opens it correctly without garbling.
- Skip a column:
column.exportable: false - Custom cell serialization:
column.exportRender(value, row, i) => string - File name:
export-file-name, defaults to'table' - Fully custom:
@exportgives you the CSV string — download or upload as you wish. - Imperative: the component exposes
exportCsv()viadefineExpose.
| 发票号 | 客户 | 产品 | 金额 | 状态 | 日期 |
|---|---|---|---|---|---|
| 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,导出时输出原始数字而不是格式化后的 ¥。
Persist column state
Setting persistKey="<key>" makes the table write the current columnsState (hidden / order / widths) to localStorage['cf-table:<key>'] and restore it on next mount.
| 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'] 恢复。
When fully controlled (you pass :columns-state), persistence is skipped — you decide where to save it.
Auto rowSpan merge
Add mergeRows: true to a column and consecutive cells with equal values are merged into a single rowSpan. For finer control pass a function: 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。
Merges never cross tree levels (different parentKey rows are not joined).
Cell selection + one-tap copy
cell-selectable makes the table behave like Excel:
- Click a cell → select it
- Shift-click / mouse drag → rectangular selection
- Ctrl/⌘ + C → copies as TSV to the clipboard, ready to paste into 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 is honored — what you see is what you copy.
Column virtualization (bidirectional)
When you have 100+ columns of simple cells (think 2-D matrices), enabling col-virtual + a default col-width virtualizes the horizontal axis too. Combine with virtual for full bidirectional virtual scrolling.
数据集: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 rows × 121 columns = 24,200 cells, but the actual DOM stays under ~800 nodes. Note: column.fixed still pins columns, but try to keep total fixed width under one third of the viewport so the visible area isn’t crowded.
Tree-aware drag (across parents)
Add tree-reorderable on top of row-reorderable and the drop UI splits each target row into three zones, like Finder / Excel / Kanban tools:
- Top quarter → drop above (same parent)
- Bottom quarter → drop below (same parent)
- Middle half → drop inside as a child
Ancestor-into-descendant moves are blocked.
| 名称 | 大小 | ||
|---|---|---|---|
| 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 emits { fromKey, toKey, pos: 'above' | 'below' | 'inside', rows } so you can persist the new tree shape.
TSV paste-back
On top of cell-selectable, enable cell-pastable. Click (or shift-frame) a starting cell, paste TSV / CSV from your clipboard with Ctrl/⌘ + V, and the data flows back into cells starting at that anchor. column.editable decides which cells accept writes; column.editValidate rejects invalid values.
| 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 returns { applied, skipped } — handy for a “write 12 cells, skipped 3” toast.
Undo / redo history
history-enabled records every change to sort / filter / search / pagination / columnsState into an in-memory stack. Ctrl/⌘ + Z undoes, Ctrl/⌘ + Shift + Z / Ctrl + Y redoes. history-depth caps the stack size (default 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 fires { canUndo, canRedo } for toolbar buttons. Component instance also exposes imperative undo() / redo().
Variable row heights
virtual defaults to a single rowHeight. For rows of varying height (markdown body, image gallery, etc.), pass :get-row-height="(idx) => 42" and the table builds cumulative offsets, then binary-searches for the visible window on each scroll.
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 should be a pure function — the component recomputes offsets when row count changes. If real heights are only known after rendering, return an estimate first and update once measured. A future auto-row-height mode will wire ResizeObserver in for you.
Row-level edit mode
Set edit-mode="row" and double-click any row to enter row-edit mode: every editable column becomes an input simultaneously, and a Save / Cancel row appears below. Enter commits, Esc cancels. Validation failure keeps the row in edit mode.
| 姓名 | 邮箱 | 角色 | 配额 |
|---|---|---|---|
| Jane Liu | [email protected] | admin | 5 |
| Bob Wang | [email protected] | editor | 3 |
| Alice Chen | [email protected] | viewer | 1 |
双击任意行进入整行编辑:所有可编辑列同时变成 input。底部出现 Save / Cancel;Enter 也可提交,Esc 取消。
Each cell change still emits @cell-edit (same as cell mode); the parent decides whether to commit individually or batch them.
Batch action toolbar
batch-actions + selectable="multiple": when at least one row is selected, a sticky bar appears above the table — built-in count / Export selected / Clear, plus a #batch-actions slot for custom buttons (which receives 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 rows
pinned-row-keys lifts those rows to the top and makes them sticky inside the scroll container. Useful for “watchlist” / “high priority” rows that should always stay visible.
| 币对 | 价格 | 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 defaults to a fixed rowHeight. When row content really needs to render before height is known (rich text, images, nested layout), enable auto-row-height: the component runs ResizeObserver on every visible row and writes measured heights back into the offset table.
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. |
The first paint uses rowHeight as an estimate; once measurements arrive, padTop / padBottom are recomputed. Less code than hand-writing getRowHeight, but the very first scroll may see a tiny scrollbar jitter while the system calibrates.
Server-side debounce
Typing in the search box or filter dropdown shouldn’t pound your backend on every keystroke. Pass :server-debounce="300" (ms) to delay update:globalSearch and update:filters until the input is idle. Other signals (sort click, page change) are explicit clicks and remain synchronous.
<CfTable
:columns="columns"
:rows="rows"
:pagination="{ page, pageSize, total }"
:server-debounce="300"
@update:globalSearch="(q) => loadData({ search: q })"
@update:filters="(f) => loadData({ filters: f })"
/>
Server-side mode
Once pagination.total is a number, the component stops doing client-side slicing. sort / filter / pagination changes are emitted as events and you fetch from the backend:
<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 | Type | Default | Description |
|---|---|---|---|
columns | TableColumn[] | — | Column defs; nest children for grouped headers |
rows | T[] | — | Data rows; row[childrenField] is children |
rowKey | string | (row, i) => string | i | Unique row id for selection / expansion |
size | 'sm' | 'md' | 'lg' | 'md' | Density |
variant | 'default' | 'bordered' | 'striped' | 'default' | Visual variant |
hoverable | boolean | true | Row hover highlight |
stickyHeader | boolean | false | Sticky header (also pass height) |
height | number | string | — | Body max height; scrolls when exceeded |
loading | boolean | false | Show loading overlay |
emptyText | string | 'No data' | Empty state text |
sort | TableSort | TableSort[] | null | — | Current sort (controlled) |
defaultSort | same | null | Initial sort (uncontrolled) |
multiSort | boolean | false | Enable shift-click multi-sort |
filters | Record<string, unknown> | — | Filter values (controlled) |
defaultFilters | same | {} | Initial filters (uncontrolled) |
globalSearch | string | — | Global search (controlled) |
defaultGlobalSearch | string | '' | Initial search (uncontrolled) |
pagination | TablePagination | false | — | Pagination; false disables |
defaultPagination | TablePagination | — | Initial pagination (uncontrolled) |
selectable | 'single' | 'multiple' | — | Row selection mode |
modelValue | string | string[] | null | null | Selected row keys |
expandable | boolean | false | Show expand toggle |
expandRender | (row, i) => any | — | Custom expanded content |
expandedRowKeys | string[] | — | Expanded keys (controlled) |
defaultExpandedRowKeys | string[] | [] | Initial expanded (uncontrolled) |
childrenField | string | 'children' | Tree data field |
treeIndent | number | 16 | Per-level indent (px) |
columnsState | TableColumnsState | — | Hidden / order / widths (controlled) |
defaultColumnsState | same | — | Initial columns state (uncontrolled) |
resizable | boolean | false | Enable resize globally |
reorderable | boolean | false | Enable reorder globally |
showSummary | boolean | false | Auto-render a summary row from column.summary |
summary | TableSummaryRow[] | — | Extra manual summary rows |
toolbar | 'auto' | 'none' | 'none' | Toolbar (search + column menu + export) |
virtual | boolean | false | Enable virtual scroll (requires height + fixed rowHeight) |
rowHeight | number | 36 | Per-row height in virtual mode (px) |
overscan | number | 6 | Extra rows above/below the viewport |
rowReorderable | boolean | false | Enable row drag-and-drop reorder |
exportable | boolean | false | Show CSV export button in toolbar |
exportFileName | string | 'table' | Export file name (without extension) |
persistKey | string | — | Persist columnsState in localStorage under this key |
serverDebounce | number | 0 | Debounce (ms) for update:globalSearch / update:filters |
cellSelectable | boolean | false | Excel-style cell selection + Ctrl/⌘+C copy as TSV |
colVirtual | boolean | false | Enable column virtualization (recommended for 100+ columns) |
colWidth | number | 120 | Default column width when virtualizing (px) |
colOverscan | number | 4 | Extra columns rendered on each side |
treeReorderable | boolean | false | Combined with rowReorderable, enable above/below/inside drop zones |
cellPastable | boolean | false | Accept Ctrl/⌘+V to paste TSV / CSV back into cells |
historyEnabled | boolean | false | Snapshot stack for undo / redo across sort/filter/search/page/columnsState |
historyDepth | number | 50 | Max snapshots kept |
getRowHeight | (index) => number | — | Per-row height in virtual mode for variable-height rows |
editMode | 'cell' | 'row' | 'cell' | 'row' enters whole-row edit on double-click |
batchActions | boolean | false | Sticky batch action bar when rows are selected |
pinnedRowKeys | string[] | — | Row keys pinned to the top via sticky |
autoRowHeight | boolean | false | ResizeObserver auto-measures variable-height rows |
TableColumn
| Field | Type | Description |
|---|---|---|
key | string | Required unique id |
title | string | Header text |
dataIndex | string | Data field; falls back to key |
width / minWidth / maxWidth | number | string | Geometry |
align | 'left' | 'center' | 'right' | Alignment |
ellipsis | boolean | Truncate overflow |
sortable | boolean | Enable sort on this column |
sortFn | (a, b) => number | Custom sort comparator |
filterable | boolean | Enable filter on this column |
filterType | 'text' | 'select' | 'number-range' | 'date-range' | Filter UI type |
filterOptions | {label,value}[] | Options for select |
filterFn | (filterValue, row, i) => boolean | Custom filter |
fixed | 'left' | 'right' | Pin column |
resizable / reorderable / hideable | boolean | Per-column overrides |
hidden | boolean | Hidden by default |
render | (value, row, i) => any | Custom cell render |
format | (value, row, i) => string | String formatter |
headerRender | () => any | Custom header |
children | TableColumn[] | Grouped headers |
summary | 'sum' | 'avg' | 'count' | (rows) => any | Aggregation |
summaryRender | (value) => any | Custom summary cell |
mergeRows | boolean | 'consecutive' | (cur, prev) => boolean | Auto-merge rowSpan for consecutive equal cells |
editable | boolean | (row, i) => boolean | Whether this column is double-click editable |
editType | 'text' | 'number' | 'select' | Editor type |
editOptions | {label,value}[] | Options for select editor |
editValidate | (value, row, i) => boolean | Reject commit if it returns false |
exportable | boolean | When false, column is skipped in CSV export |
exportRender | (value, row, i) => string | Cell stringification on export |
cellClass | string | (row, i) => string | Cell class |
Events / Slots
| Event / Slot | Payload | Description |
|---|---|---|
update:sort / sort-change | TableSort | TableSort[] | null | Sort changed |
update:filters | Record<string, unknown> | Filter values changed |
update:globalSearch | string | Search box changed |
update:pagination | TablePagination | Page / page size changed |
update:modelValue | string | string[] | null | Selected rows changed |
update:columnsState | TableColumnsState | Reorder / hide / resize |
update:expandedRowKeys | string[] | Expansion changed |
update:rows / row-reorder | T[] / {from,to,rows} | Row drag reorder |
tree-reorder | {fromKey, toKey, pos, rows} | Tree-aware drag (above/below/inside) |
cell-edit | {row, column, oldValue, newValue, index} | Inline cell commit |
cell-paste | {applied, skipped} | TSV paste finished |
export | string | CSV export hook (receives the CSV string) |
history-change | {canUndo, canRedo} | History stack changed |
row-click | (row, i) | Row click (when not selectable) |
#header:<key> | — | Custom header node |
#cell:<key> | { row, index, value } | Custom cell |
#empty | — | Custom empty state |
#toolbar-left / #toolbar-right | — | Toolbar extension slots |
Performance notes
- All derivations (filtered → sorted → paginated → flat tree) run via
computed/useMemoand only re-run on dependency change. - Column resize / reorder updates the column state object, not the entire table — only the affected column re-evaluates its style.
- For 1k+ rows, combine
pagination+sticky-header+height. For 10k+, useexpandable+childrenas a “lazy tree” or wait for the upcoming<CfVirtualTable>(real virtual scroll). - React side keeps
Rowextracted; you can wrap it withmemoexternally. Vue side uses v-for diffing for cells.
反馈与讨论
Table · Discussion