@canvas-components/list-table

中文文档
0.3.58发布于 2026/11/09MITTypeScriptESM + CJS近 30 天下载 6009

Framework-agnostic list table core engine.

原始使用文档
文档目录37 节
  1. 表头合并
  2. 双击表头边界自动列宽
  3. 内置图标
  4. 时间轴单元格
  5. 上传单元格
  6. 多列排序
  7. 多列拖拽排序
  8. 图片复制与导出
  9. 功能作用
  10. 展开详情自动高度
  11. 内嵌子表头
  12. 子表列宽与列顺序拖拽
  13. 分页尺寸缓存
  14. 本地配置缓存
  15. 按列条件格式
  16. 分区 CSS 样式
  17. 安装方法
  18. 使用方法
  19. 属性说明
  20. 实例配置 ListTableOptions
  21. 分批加载与服务端汇总
  22. 列配置 ListTableColumn
  23. 常见嵌套能力
  24. 方法说明
  25. XLSX Worker 导出
  26. 内建单元格内容
  27. 开关二次确认
  28. 金额单元格示例
  29. 行点击事件
  30. 推荐搭配
  31. 滚动条配置
  32. 级联选择编辑(Cascader)
  33. 树选择编辑(TreeSelect)
  34. 绘制到已有 Canvas 上下文
  35. 清空行选择
  36. 同一单元格编辑多个字段
  37. 紧凑图片与表头操作

@canvas-components/list-table

@canvas-components/list-table 是整个项目的纯原生 Canvas 表格核心包,不依赖 React,可直接在 DOM 容器中创建高性能表格实例,也可作为适配层的底层引擎使用。

表头合并

使用 children 自动生成多级表头,或通过 onHeaderCell(column) 返回 rowSpan、colSpan 自定义合并;跨度为 0 时隐藏该表头,表体列保持独立。原生表格和 React 适配层共用此配置,详见 表头合并配置。

双击表头边界自动列宽

双击表头可拖拽的右边界,按内容自动计算当前列宽;双击左边界则调整左侧相邻列,与拖拽边界归属一致。支持左右固定列及最后一列,沿用右键菜单的内容测量、最小/最大宽度限制与列宽缓存设置。禁用列宽调整的边界和空白选择列等特殊列不响应。

内置图标

{ type: "icon", icon: "EditOutlined", size: 20 } 可绘制原生图标,完整提供官方 848 个图标(线框、实心、双色),另有 58 个内置短名称图标,包含表头同款排序、过滤、固定图标。 图标路径统一维护于 src/rendering/icons/,供单元格、评分、时间轴和附件功能复用。 详见 图标目录与配置。

时间轴单元格

render 返回 { type: "timeline", items: [{ title: "09:00", content: "订单创建", color: "green" }] } 即可绘制原生纵向时间轴;支持自动换行、自动行高、交替布局和倒序。 详见 时间轴配置。

上传单元格

设置 formItemProps: { valueType: "upload", upload: { action: "/api/upload", multiple: true } } 即可使用原生上传编辑器。图片回显缩略图,其他文件显示类型图标和文件名,不依赖 React 组件。 详见 上传配置与接口说明。

多列排序

使用列配置,设置 sorter: { multiple: 数值 } 开启该列的多列排序,数值越大越优先;同优先级按加入排序的先后顺序执行。

const columns = [
  { title: "部门", dataIndex: "dept", sorter: { multiple: 3 } },
  { title: "绩效", dataIndex: "score", sorter: { multiple: 2 } },
  { title: "姓名", dataIndex: "name", sorter: { multiple: 1 } },
];
  • 点击排序图标依次切换升序、降序、取消,仅修改当前列;右键“取消排序”同样只清除当前列,“重置排序”清除全部条件。
  • multiple 相同时,先点击加入排序的列优先;切换升降序不改变次序,取消后重新加入排在同权重列的最后。缓存恢复保留此顺序,未点击的默认排序按列配置顺序。
  • sortValueGetter(record) 用于提取数字或字符串排序值,支持状态权重映射、数值计算等;sorter.compare(a, b) 则直接比较两条原始记录,优先于 sortValueGetter。按升序返回负数、零或正数,降序由表格自动反转;返回 0 或 NaN 时继续比较下一列,全部相等保留原始顺序。比较函数需保持纯函数并满足排序一致性。
  • 自定义 compare 不会序列化到 Worker,支持闭包,采用可取消的主线程分片排序;同一组排序中的普通列仍可使用 Worker。比较函数抛出异常时进入查询错误处理,不会静默改用默认规则。单次比较函数本身仍需快速返回。
  • 未配置 multiple 的列保持单列交互:开始排序时替换其他排序条件。sorter: true 开启单列排序,也可继续使用 allowSort 控制排序能力。
  • defaultSortOrder 可与 sorter.multiple 配合设置多列默认排序,父表和嵌套子表独立维护各自的排序条件。
  • 排序状态统一使用数组:[{ key, order, priority }],未排序为 [];query.onChange 第三个参数也是数组:[{ columnKey, field, order, priority }]。extra.sorter、extra.sorters 均返回排序状态数组。不转换旧单列缓存数据。
  • 本地同步排序、Worker 排序及无 Worker 分片回退遵循相同优先级;服务端模式由业务端按回调数组执行排序。
  • 基础示例中已配置部门、绩效、姓名三列,可连续点击它们验证组合排序。
const scoreColumn = {
  title: "绩效",
  dataIndex: "score",
  sorter: {
    multiple: 2,
    compare: (a, b) => a.score - b.score,
  },
};

多列拖拽排序

开启 draggable: true 后,可先使用 Shift 选中连续列,或使用 Ctrl / Command 选中不连续列,再从任意已选列的表头空白处拖动。同层级中允许拖拽的选中列会按原显示顺序整体移动;不连续选中的列在落点合并为连续的一组。高亮选区跟随列移动,浮层同步展示各列表头。

拖动未选中的列时仍只移动该列。固定列、draggable: false 的列不参与移动;多级表头沿用同级重排边界,不把子列移出所属分组。落回原位时不提交重排。

图片复制与导出

右键菜单「复制」支持「复制为图片」,「导出」支持「导出图片」,均提供三个范围:

  • 所有区域:重新绘制当前已加载、经过排序过滤的全部数据及可见列,包含表头和已开启的汇总行。服务端分页不会额外请求尚未加载的数据。
  • 选中区域:绘制当前单元格、拖拽矩形选区、整行或整列选区,不包含表头和汇总行;没有选区时不可用。
  • 视口区域:截取当前表格画布,保留当前滚动位置及可见效果,不包含分页器、工具栏或右键菜单。

图片格式为 PNG。复制需要浏览器支持图片剪贴板,且通过 HTTPS 或 localhost 访问;下载不依赖剪贴板权限。 图片包含尚未加载的图片内容时,最多等待 10 秒。跨域资源禁止读取、编码失败或尺寸过大会明确提示,不会静默生成不完整文件。 为防止内存溢出,输出图片单边不超过 16384 像素,总像素不超过 1600 万(计入缩放及设备像素比);超过时请缩小选区或使用视口范围。 「复制」和「导出」仍分别遵循 contextMenu.features 的 copy、export 开关。

功能作用

  • 原生 Canvas 表格渲染
  • 大数据量虚拟滚动
  • 多级表头、固定列、汇总行
  • 排序、过滤、自动合并单元格
  • 行选择、展开树表、右键菜单
  • 单元格编辑、校验、tooltip、图片预览
  • 列宽调整、列拖拽、行拖拽、轮播滚动
  • 内建二维码、条形码、进度、图表、评分、按钮等单元格内容

展开详情自动高度

expandable.expandedRowRender 返回标签、文本等富内容时,不传 expandedRowHeight 即按实际内容宽度自动换行、测高。容器宽度、列宽、字体或密度变化后会重新计算,并同步更新行位置和滚动范围。测量与绘制共用布局规则,业务侧无需自行测量标签或监听容器宽度。

显式设置 expandedRowHeight 数字或回调时仍优先使用该配置;嵌套子表继续使用其独立的内容高度计算。

展开详情中的 button、link 等交互片段与普通单元格共用点击、禁用及二次确认逻辑。onClick 收到所属数据行的 record、扁平行索引 rowIndex、片段索引 contentIndex 和原生事件;column.key 为 __expanded_row__,value 为 undefined。文档站「可展开行」的入库条码明细示例演示物料标签与按钮组合、权限切换及明细弹窗。

内嵌子表头

children 树形数据共用父表列和表头,叶子行会预留展开按钮位置,使文字按 indentSize 逐级缩进。展开详情返回 type: "table" 时,子表默认独立显示表头,即使父子列配置相同也不会自动隐藏。

内嵌表格内容支持 showHeader: false:

expandable: {
  expandedRowRender: (record) => ({
    type: "table",
    showHeader: false,
    columns: detailColumns,
    dataSource: record.details,
  }),
}

隐藏时不绘制子表头、排序/筛选等表头入口,也不预留表头高度;首行数据从子表顶部开始,编辑和选区位置随之调整。showHeader 默认 true,设为 false 时优先于 headerHeight。父子可见业务列的字段和显示顺序一致,且子表未显式设置 width 时,子表会跟随父表实际列宽及固定列位置,缩进只占首个业务列;显示或隐藏子表头均保持后续列边界对齐。字段或顺序不同、显式指定子表宽度时仍独立计算列宽。文档站「可展开行」示例可切换子表头显示。

子表列宽与列顺序拖拽

内嵌 type: "table" 子表默认允许拖动相邻列在表头或表体中的边界调整两列宽度,内部边界保持子表总宽不变;拖动最右列的右边界可改变子表总宽,其他列宽保持不变。展开子表超出视口时,共用父表底部横向滚动条,折叠或收窄后自动更新滚动范围;拖动表头超过 5px 后可调整同一固定区域内的列顺序,拖动时显示与父表一致的带蓝色高亮边框的半透明整列浮层、蓝色落点线和圆点,并显示与父表一致的列插入位置 tooltip,松手后以相同的 320ms 缓动完成列交换与浮层归位。父子边界重合时,子表可见区域内优先调整子表列宽。调整列宽时,从按下列边界开始全程显示贯穿子表高度的蓝色虚线指示线;边界进入父表可见业务列边界约 6px 范围内会自动吸附,指示线与父表边界重合。离开后恢复自由调整,松开或按 Escape 后隐藏指示线并保留当前宽度。吸附距离随表格缩放换算,并遵守相邻列的最小/最大宽度限制。每个子表独立保存当前实例中的布局,父表和其他子表不受影响;折叠后再展开仍保留,刷新页面不持久化。

  • 子表级 resizable: false / draggable: false 分别关闭列宽和列顺序拖拽。
  • 列级同名配置可禁止指定列参与调整;任一相邻列禁止调整时,该边界不可拖动。
  • 列级 minWidth(默认 40)/ maxWidth 限制调整范围,同时满足相邻列的限制。
  • showHeader: false 时不提供拖拽入口。
  • 同列子表首次手动调整后改用独立布局,不再自动跟随父表列宽和顺序;编辑、排序、过滤仍按原始列定位。
{
  type: "table",
  resizable: true,
  draggable: true,
  columns: [
    { title: "名称", dataIndex: "name", width: 220, minWidth: 100 },
    { title: "数量", dataIndex: "quantity", width: 120, minWidth: 60, maxWidth: 240 },
  ],
  dataSource: record.details,
}

分页尺寸缓存

设置表格 storageKey 后,用户选择或输入的每页条数会自动保存到 localStorage,下次创建表格时恢复。可通过 pagination.cachePageSize: true 明确开启;默认沿用开启行为,设置为 false 后禁止读取和写入已有缓存。不同表格应使用不同的键;不缓存页码,不影响滚动加载。无键、缓存无效、缓存超出当前 minPageSize / maxPageSize 范围或浏览器禁用存储时,继续使用默认配置。

显式传入 pagination.pageSize 时以受控值为准。外部负责请求的封装(如 WWISuperTable)应在首次请求前读取缓存,初始化自己的分页状态,避免只改变显示值而请求条数不一致:

import { getListTableStoredPageSize } from "@canvas-components/list-table";

const pagination = { cachePageSize: true, minPageSize: 5, maxPageSize: 500 };
const initialPageSize =
  explicitPageSize ?? getListTableStoredPageSize(storageKey, pagination) ?? 10;
// 将 initialPageSize 用于首次请求和受控 pagination.pageSize。

该读取方法也由 @canvas-components/react-list-table 导出。缓存只记录选中的尺寸,不保存自定义候选历史;React 分页器恢复到自定义尺寸后仍可删除,删除当前自定义项会将回退尺寸写入缓存。

本地配置缓存

汇总行右键“汇总方式”中提供“展示汇总方式前缀”,默认勾选。取消后仅显示结果值(如 173),勾选时显示 求和: 173 等前缀;主表、子表和复制文本使用同一展示规则。列配置可使用 summary: { type: "sum", showTypePrefix: false },该开关随汇总配置缓存,自定义汇总标题不受影响。

配置稳定且唯一的 storageKey 后,可在右键通用“缓存到本地”分组中分别设置:列设置、排序、筛选、汇总、对齐、格式(数字、日期、货币等)、表格密度、缩放,以及当前列条件格式,默认全部开启。“列显示”和“条件格式”子菜单不重复提供缓存开关。通用分组中的“列设置”与标题栏“列设置”标题右侧的 Checkbox 共用状态,控制列宽、显隐、固定和顺序;未配置 storageKey 时禁用并提示。

关闭某类缓存不会撤销当前页面的操作,只清除该类持久化内容,刷新后使用传入配置;其他类别不受影响。各缓存开关自身也会保存。冻结行、复制、导出等没有对应持久化数据,不提供缓存开关。

可用 cacheColumnSettings: false 设置列设置缓存默认关闭;其他类别通过 cachePreferences 配置,如 { sort: false, filter: false, summary: false, alignment: false, dataFormat: false, density: false, zoom: false }。未指定默认开启,已保存的用户选择优先。原生实例支持 setColumnSettingsCache(enabled);自定义右键功能列表可通过 cache 控制分类缓存分组。

按列条件格式

规则通过 backgroundColor 和 textColor 分别设置背景色与文本颜色,编辑弹窗使用现有颜色选择器并实时预览。文本颜色覆盖当前单元格文字片段的颜色,选中和悬停时仍生效;不修改其他列、表头或汇总行。不提供旧规则文本颜色迁移逻辑。

通用“缓存到本地”分组中的“当前列条件格式”按列控制规则是否持久化,默认开启。关闭后保留当前页面的规则效果,但保存时移除该列已缓存的规则;刷新后只使用代码中配置的初始规则。可通过列属性 cacheConditionalFormats: false 设置默认关闭。

单条规则支持新增、删除多个条件,相邻条件之间分别选择“且 / 或”,且优先于或。所有条件比较当前列原始值,命中后使用该规则的样式。例如 { id: "score", conditions: [{ operator: "gte", value: 80 }, { relation: "and", operator: "lt", value: 90 }], backgroundColor: "#fff1b8", textColor: "#874d00" }。每项 relation 表示与上一条件的连接关系,首项忽略;删除条件时保留剩余条件携带的关系。未指定时兼容旧的 conditionRelation,默认“且”。conditions 优先于旧的单条件字段;未配置时兼容原有 operator/value/endValue,空条件列表不命中。

右键业务叶子列表头 → 条件格式,二级菜单以“颜色块 + 规则名称”展示当前列规则,未命名时显示“规则 1”等默认名称,悬停可查看完整名称和条件摘要;并提供“新增规则”“重置条件格式”。点击规则打开编辑弹窗,可修改条件、背景色或删除规则;取消不会改动表格。

规则通过 scope 设置作用效果:"cell"(默认)只作用于当前列命中的数据单元格,"row" 则将命中规则的背景色和文字颜色应用于整条数据行。行级规则按可见列顺序检查,首条命中优先;单元格级规则在同一单元格命中时优先于行级样式。两种规则都不修改表头或汇总行。条件背景优先于选中和悬停背景,保留选区边框;校验错误提示仍优先于条件背景。只对绘制中的数据行和单元格计算,不预扫描全量数据。

const columns = [
  {
    key: "amount",
    dataIndex: "amount",
    title: "金额",
    conditionalFormats: [
      {
        id: "high",
        name: "高金额",
        operator: "gte",
        value: 1000,
        backgroundColor: "#fff1b8",
        textColor: "#874d00",
      },
      {
        id: "negative",
        name: "负数",
        operator: "lt",
        value: 0,
        backgroundColor: "#ffccc7",
        textColor: "#a8071a",
      },
      {
        id: "review",
        name: "复核整行",
        scope: "row",
        operator: "gte",
        value: 5000,
        backgroundColor: "#e6f4ff",
        textColor: "#0958d9",
      },
    ],
  },
];

支持 eq / neq(原始值转文本后比较)、gt / gte / lt / lte(数值比较)、between(包含上下界,结束值使用 endValue)、contains(区分大小写)、empty / notEmpty。空值及非数值不参与数值比较。

配置 storageKey 后规则随列设置保存;“重置条件格式”只清空当前列规则,不清除其他样式或其他列规则。自定义右键菜单功能列表时,可通过 conditionalFormat 控制该入口。核心包通过 HeaderContextMenuConfig.conditionalFormats 和 onChangeConditionalFormats 向 UI 适配层提供配置。

分区 CSS 样式

原生 ListTable 和 ReactListTable 均支持 styles。Canvas 区域通过 CSS 属性映射到绘制参数,不创建单元格 DOM,不注入全局样式。

styles: {
  cell: { fontSize: '14px', padding: '2px 12px', borderColor: '#d9e1ec' },
  header: { backgroundColor: '#f0f9ff', color: '#075985', fontWeight: 700, textAlign: 'center' },
  body: { color: '#334155' },
  summary: { backgroundColor: '#f0fdf4', color: '#166534', textAlign: 'right' },
  activeCell: { backgroundColor: '#fef3c7', borderColor: '#d97706', borderWidth: '2px' },
  activeRow: { backgroundColor: '#e0e7ff', color: '#3730a3' },
  scrollVertical: { width: '14px', color: '#94a3b8', backgroundColor: '#f1f5f9', borderRadius: 4 },
  scrollHorizontal: { height: '12px', color: '#64748b', backgroundColor: '#f8fafc' },
  pagination: {
    root: { backgroundColor: '#f8fafc', color: '#334155', fontSize: '14px' },
    total: { color: '#64748b' },
    pageItem: { borderRadius: 4 },
    activePageItem: { backgroundColor: '#1677ff', color: '#fff' },
    sizeChanger: { width: 140 },
    quickJumperInput: { width: 64 },
  },
}
  • cell 是表头、表体和汇总单元格的通用默认值;分区覆盖通用值,列级 headerStyle / cellStyle / summaryStyle 优先。
  • Canvas 分区支持 backgroundColor、color、fontSize、fontWeight、fontFamily、textAlign、verticalAlign、padding、paddingInline、paddingBlock、borderColor、borderWidth。长度只支持非负数字或 px,padding 支持一值或“上下 左右”两值;不支持百分比、em 或 DOM 布局属性。
  • activeCell 支持背景、文字和边框,activeRow 支持背景与文字;仅改变已有选中状态的绘制,不改变选择模式。校验错误提示仍优先。
  • 滚动条 color 对应滑块、backgroundColor 对应槽色;纵轴 width、横轴 height 对应厚度。显式 scrollbar.vertical/horizontal: false 仍禁止对应轴显示。
  • pagination 按节点直接应用 DOM CSS,使用 camelCase 属性名,数字长度按 React DOM 规则处理;仅影响当前实例。通过原生 UI 回调自行渲染分页器时,从 PaginationConfig.styles 读取并应用样式。当前分页占位高度仍由表格密度决定,避免设置超出该高度的高度或纵向间距。

分页器支持以下节点:

节点作用
root分页器根容器,颜色、字体等可向内部继承
total总条数文本
pages页码列表容器
pageItem普通页码按钮;简易模式下为页码文本
activePageItem当前页按钮,叠加并覆盖 pageItem
prevButton / nextButton上一页 / 下一页按钮
prevIcon / nextIcon上一页 / 下一页箭头,通过 borderColor 设置箭头颜色
ellipsis页码省略号
sizeChanger分页尺寸选择控件,不包含弹出选项面板
quickJumper快速跳转区域容器
quickJumperText“跳至”和“页”文本
quickJumperInput跳转输入控件外框,可设置宽度、背景与边框

节点样式优先于默认样式;未配置节点保持原样。独立使用 ReactPagination 时也支持同名 styles,其中 styles.root 优先于 style。

  • 更新或移除 styles 后即时生效;不额外持久化 styles 对象,已有列配置缓存中的对齐设置仍优先。嵌套子表格的独立内容样式仍通过其列/内容配置指定。

文档站“主题”示例增加了“启用分区 CSS 样式”开关,可切换验证。

安装方法

pnpm add @canvas-components/list-table

最低环境要求:

  • Node.js >= 18.12.0
  • pnpm >= 9.0.0

使用方法

import { ListTable } from "@canvas-components/list-table";

const container = document.querySelector("#table") as HTMLElement;

const table = new ListTable(container, {
  type: "list",
  width: "100%",
  height: 480,
  rowKey: "id",
  columns: [
    { key: "name", title: "姓名", dataIndex: "name", width: 180 },
    { key: "age", title: "年龄", dataIndex: "age", width: 120, align: "right" },
  ],
  dataSource: [
    { id: 1, name: "张三", age: 28 },
    { id: 2, name: "李四", age: 31 },
  ],
});

table.mount();

属性说明

实例配置 ListTableOptions

属性类型说明
type"list"表格类型,当前稳定支持列表表格
rowKeystring | number | Array<string | number> | ((record, index) => string | number)行主键解析规则
columnsListTableColumn[]列配置集合
widthnumber | string表格宽度
heightnumber | string表格高度
dataSourceRecordType[]表格数据源
emptyTextstring空状态文案
themePartial<ListTableTheme>主题覆盖
stripedboolean是否开启斑马纹
highlightMode"cell" | "row" | "column" | "cross"点击高亮模式
keyboardNavigation{ tabWrap?: boolean; verticalWrap?: boolean }键盘边界导航;两项默认均为 true
contextMenufalse | ListTableContextMenuOptions右键菜单配置
groupingListTableGroupingOptions分组栏、初始分组列与变化回调
rowHeightnumber | "auto"行高
headerRowHeightnumber表头单行高度
summaryRowHeightnumber汇总行高度
overscanRowCountnumber虚拟滚动预渲染行数
queryListTableQueryOptions排序、过滤、合并单元格配置
rowSelectorboolean | ListTableRowSelectorOptions最左侧空白整行选择器配置
rowSelectionListTableRowSelectionOptions行选择配置
expandableListTableExpandableOptions树表 / 展开行配置
paginationfalse | ListTablePaginationOptions分页或滚动加载配置
scrollbarListTableScrollbarOptions滚动条配置
carouselScrollListTableCarouselScrollOptions自动轮播滚动配置
stretchColumnsboolean列总宽不足时是否拉伸补齐
resizableboolean是否开启列宽拖拽
storageKeystring列配置、查询、缩放和密度持久化键
draggableboolean是否开启列拖拽排序
tableIdstring表格唯一标识
rowDragListTableRowDragOptions行拖拽配置
debugListTableDebugOptions调试配置

通过 stickToBottom: true 开启动态数据平滑吸底(默认关闭),React 使用 <ReactListTable stickToBottom />。鼠标滚轮、拖动滚动条和触摸滚动均可正常浏览历史;停止滚动后默认等待 3 秒,再平滑滚向最新底部。配置 stickToBottom={{ enabled: true, resumeDelay: 5000 }} 可将等待时间改为 5 秒(resumeDelay 单位毫秒,范围 0~600000)。右键“吸底”二级菜单可切换开关、选择常用等待时间或输入自定义秒数。菜单设置在相同配置的数据更新中保留,显式改变对应配置时同步新值;可用 contextMenu.disabledFeatures: ["stickToBottom"] 隐藏该菜单。

keyboardNavigation.tabWrap 控制 Tab / Shift+Tab 在行尾 / 行首是否进入相邻行;verticalWrap 控制 ↑ / ↓ 在列首 / 列尾是否进入相邻列。设为 false 时到对应边界即停止,不改变当前活动单元格。

分批加载与服务端汇总

数据仍在异步生成或分批加载时,设置表格顶层属性 loading: true。此时汇总行及标题正常显示,统计结果保持空白,也不会计算首批临时数据。完整数据就绪后,将完整 dataSource 与 loading: false 一起更新,表格自动计算汇总并在完成后统一显示结果。默认值为 false,正常的空数据仍可汇总为零。

使用服务端汇总时,通过 query.summaryValues 传入以列 key 为键的对象或 Map。若数据和汇总分别请求,可保持 loading: true,待两者都返回后再设为 false;此配置同样支持 React 适配包,外部无需控制汇总计算状态。

const options = {
  dataSource: rows,
  loading,
  query: {
    // 服务端汇总可选;不传则使用本地异步汇总。
    summaryValues: serverSummaryValues,
  },
};

列配置 ListTableColumn

属性类型说明
keystring列唯一标识
titlestring列标题
dataIndexstring | number | Array<string | number>取值路径
childrenListTableColumn[]子列,用于多级表头
widthnumber列宽
minWidth / maxWidthnumber允许调整的最小 / 最大列宽
align"left" | "center" | "right"内容对齐
headerAlign"left" | "center" | "right"表头水平对齐,未传时回退到 align
fixed"left" | "right"固定列
hiddenboolean是否隐藏
ellipsisboolean超出是否省略
editableboolean | (text, record, index) => boolean是否允许编辑
formItemPropsListTableFormItemPropsConfig编辑器配置
resizableboolean单列是否允许调整宽度
draggableboolean单列是否允许拖拽
allowSortboolean是否开启排序
sorterListTableSorter<RecordType>multiple 指定优先级,compare(a, b) 自定义比较
defaultSortOrder"ascend" | "descend"默认排序方向,可配合 sorter.multiple
allowFilterboolean是否开启过滤
filterOptionsListTableFilterOption[]精确过滤选项
cellTypeListTableCellContent["type"]内建单元格类型
moneyFormatListTableMoneyFormatOptions金额格式配置,仅在 cellType: "money" 时生效
dataFormat"default" | "text" | "number" | "thousands" | "decimal" | ...右键菜单设置的数据展示格式
cellStyle.verticalAlign"top" | "middle" | "bottom"表体垂直对齐
headerStyle.verticalAlign"top" | "middle" | "bottom"表头垂直对齐
render(value, record, rowIndex, column) => ...自定义渲染
onCell(value, record, rowIndex, column) => { rowSpan?: number; colSpan?: number }单元格跨度控制
summaryboolean | ListTableSummaryConfig汇总配置
summaryTitlestring汇总标题文本

常见嵌套能力

配置说明
query排序、筛选、远程查询、自动合并单元格
rowSelector空白整行选择器,单击选中一行、纵向拖拽选中连续多行
rowSelectioncheckbox / radio 行选择
expandable树形数据、详情展开、展开列
pagination按钮分页或滚动加载
rowDrag行拖拽、跨表拖拽、拖放回调
contextMenu复制、导出、排序、冻结、列显示、分组栏、对齐、数据格式、密度、缩放等菜单能力

分组功能默认关闭,只有显式传入 grouping: {} 才会启用右键菜单中的分组入口。grouping.visible 控制 Canvas 顶部分组栏初始显隐,默认 false;grouping.columnKeys 按顺序声明初始分组层级。显示分组栏后可将列头拖入栏内;分组列会从表头和表体隐藏,仅以 Canvas 标签展示,移除标签会立即恢复该列。右键分组栏可隐藏分组栏;grouping.onChange 会在标签增删时收到最新列 key 列表。点击分组标题可折叠或展开该分组,重新设置分组列后会清除折叠状态。

rowSelection.selectRowByClick 控制点击普通表体区域时是否同步切换 checkbox / radio 选择状态,默认关闭。该配置直接复用表格内部选择状态,无需通过外部 key 强制重新创建表格;查看详情等业务点击仍使用 onRow().onClick。

rowSelector 与 rowSelection 相互独立。默认在最左侧展示宽度为 12px 的空白选择器列;传入 { width: 32 } 可自定义宽度,传入 false 可隐藏。该列只建立 Canvas 整行选区,不修改 checkbox / radio 的选中数据。

方法说明

方法签名说明
constructornew ListTable(container, options)创建实例
mount() => void挂载表格
updateOptions(options) => void更新配置并刷新
getDebugSnapshot() => ListTableDebugSnapshot | null获取调试快照
validate() => Promise<RecordType[]>手动触发校验,失败时抛出 ListTableValidationFailure
exportXlsx(options?: ListTableExportXlsxOptions) => void异步导出全部原始数据为 XLSX,可配置文件名和汇总行
scrollTo(options: ListTableScrollToOptions) => void滚动到指定行
destroy() => void销毁实例

列配置可通过 settingTitle、hideInSetting、disableHide 约束列设置中的标题和隐藏权限。hidden 是业务动态隐藏约束,默认 false,不读写缓存;visible 是用户显隐偏好,默认 true,按 storageKey 保存。实际显示为 hidden !== true && visible !== false。旧缓存中的显隐 hidden 不再恢复,列宽、顺序和固定配置继续保留。详见 业务显隐与用户列偏好。

XLSX Worker 导出

导出默认先确认文件名称,输入框默认填入配置的名称,后缀单独显示且不可修改。表格选项 exportOptions: { fileName: "报表", promptFileName: false } 可跳过弹窗;单次 exportXlsx({ promptFileName: false }) 可覆盖默认行为。原生接入通过 ui.onExportNameRequest 自定义弹窗,未提供时使用浏览器输入框。

当前显示汇总行时,XLSX 默认在全部数据末尾追加汇总行(子表格独立判断);显式设置 includeSummary: false 可排除汇总行,true 可强制追加。汇总按导出的全量数据与当前列配置计算。

exportXlsx() 默认对 1 万行及以上数据尝试 Worker;现有调用无需修改。表格加载提示会显示已处理行数与文件生成阶段。取值和 render 回调仍在主线程执行,工作表构建、文件编码交给 Worker。

const controller = new AbortController();
table.exportXlsx({
  fileName: "表格数据",
  includeSummary: true,
  worker: "auto", // true:始终尝试;false:使用主线程
  maxRowsPerFile: 100_000, // 默认十万行;20 列以内无需降低每份行数
  signal: controller.signal,
  onError: (error) => console.error("导出失败", error), // 可替换为业务提示
  onProgress: ({ phase, completedRows, totalRows }) => {
    console.log(phase, completedRows, totalRows);
  },
});
// 需要取消时调用 controller.abort()。

大数据按行数和单元格预算自动拆成多个独立 XLSX,文件名追加 _001、_002。每份保留表头,汇总只追加在全部数据末尾。逐份生成、下载并释放,不同时持有全部工作簿。进度包含 fileIndex、fileCount;可通过 saveFile(blob, fileName, part) 接管保存,等待其返回后才生成下一份。

浏览器可能要求允许下载多个文件。取消或失败会停止后续文件,已经下载的文件保留。每份默认最多 10 万行(含汇总尾行、不含表头),20 列以内按此行数分文件;超过 20 列时受 2,000,020 个单元格预算限制,适当减小每份行数。长文本另有 16M UTF-16 码元安全预算,超限时需调低 maxRowsPerFile。Worker 不可用时回退;CSV、TXT、JSON 不受影响。千万行多列数据仍建议服务端导出。

内建单元格内容

render 支持直接返回结构化内容,内建类型包括:

  • text
  • money
  • image
  • qrcode
  • barcode
  • progress
  • chart
  • rate
  • button
  • tag
  • color
  • icon
  • checkbox
  • radio
  • switch
  • link

icon 当前内置 star、sun、moon、check(√)和 close(×)。

开关二次确认

switch 内容可配置 popconfirm,与危险按钮复用同一套确认浮层和文案字段。点击后先确认,确认时才触发 onClick;取消或禁用时不触发回调。未配置 popconfirm 时直接触发回调。开关仍由业务通过 checked 控制,组件不会自动修改记录。

render(value, record) {
  return {
    type: "switch",
    checked: Boolean(value),
    popconfirm: {
      title: value ? "确认停用?" : "确认启用?",
      description: "确认后将更新启用状态。",
      okText: "确定",
      cancelText: "取消",
    },
    onClick: () => updateActive(record.id, !value),
  };
}

原生包在未提供 ui.onDangerConfirmRequest 时回退使用浏览器 confirm;React 适配包自动展示确认浮层。

金额单元格示例

const columns = [
  {
    title: "金额",
    dataIndex: "amount",
    width: 140,
    align: "right",
    cellType: "money",
    moneyFormat: {
      symbol: "¥",
      thousandsSeparator: true,
      decimalPlaces: 2,
    },
  },
  {
    title: "结算金额",
    dataIndex: "settlementAmount",
    width: 160,
    align: "right",
    render(value) {
      return {
        type: "money",
        value: value as number,
        symbol: "$",
        thousandsSeparator: ",",
        decimalPlaces: 1,
      };
    },
  },
];

行点击事件

onRow 使用统一的回调结构。由于表格由 Canvas 渲染,支持整行点击事件及 style.backgroundColor、style.color。业务可按记录 key 控制持续高亮,初始渲染、冻结列及悬停时均生效;校验错误样式优先。

const table = new ListTable(container, {
  columns,
  dataSource,
  onRow: (record, rowIndex) => ({
    style:
      record.id === selectedId
        ? { backgroundColor: "#d6eaff", color: "#003a8c" }
        : undefined,
    onClick: (event) => {
      console.log(record, rowIndex, event);
    },
  }),
});

推荐搭配

  • 原生 DOM 场景:直接使用 ListTable
  • React 场景:优先使用 @canvas-components/react-list-table
  • 二维码 / 条形码 / 图表单元格:配合 @canvas-components/qrcode、@canvas-components/barcode、@canvas-components/chart

滚动条配置

scrollbar 同时适用于原生表格与 React 适配组件。通用配置由 horizontal、vertical 单独覆盖;不配置时保留原有外观。

scrollbar: {
  thickness: 10,
  thumbColor: '#8793a5',
  trackColor: '#f1f5f9',
  buttonColor: '#475569',
  thumbRadius: 4,
  horizontal: { thickness: 8, thumbColor: '#1677ff' },
  vertical: { thickness: 14, trackColor: '#e2e8f0' },
}
  • thickness:横向滚动条高度、纵向滚动条宽度,单位 px,默认 10,最小 6。
  • thumbColor / trackColor / buttonColor:滑块、滚动槽、两端箭头颜色。
  • thumbRadius:滑块圆角,0 为直角;不传时保持胶囊形状。
  • horizontal: false 或 vertical: false:单独隐藏某一轴,也可设置该轴的 visible: false。
  • visible: false:同时隐藏两轴,优先于单轴设置。隐藏后释放对应槽位,仍可通过滚轮或程序滚动。

配置仅作用于当前 Canvas 表格,不修改全局浏览器滚动条样式。

级联选择编辑(Cascader)

formItemProps.valueType: "cascader" 使用多列展开的单选/多选级联选择器,配置支持级联选择。父表和嵌套子表使用相同配置;React 适配包提供编辑界面,原生包仅提供配置与路径格式化,不引入 React。

{
  title: '所属地区', dataIndex: 'region', editable: true, width: 240,
  formItemProps: {
    valueType: 'cascader', showSearch: true, allowClear: true,
    expandTrigger: 'click', changeOnSelect: false,
    options: [{ label: '浙江', value: 'zj', children: [
      { label: '杭州', value: 0, children: [{ label: '西湖区', value: 'xh' }] },
      { label: '停用地区', value: 'disabled', disabled: true },
    ] }],
  },
}
// 行数据:{ region: ['zj', 0, 'xh'] }
  • 值为 (string | number)[],清空将编辑草稿更新为 [],保持编辑状态,可继续选择、失焦提交或按 Escape 取消。默认显示完整标签路径,以 / 分隔;可用 displayRender(labels, selectedOptions) 定制。显式 format 优先级更高。
  • showSearch 默认 false,开启后按完整路径搜索;expandTrigger 支持 click(默认)或 hover。
  • changeOnSelect 默认 false;开启后点击中间级更新编辑草稿,失焦或 Tab 提交,选择末级立即提交。
  • 方向键在级联菜单中导航,Enter 选择,Escape 取消本次编辑,Tab 切换编辑单元格。
  • 支持单选末级、单选任意级、多选父级回填和多选末级回填。使用标准 label/value/children 节点;暂不包含异步加载及自定义字段映射。
  • 文档站“单元格编辑”示例新增“所属地区(级联)”列。

多选配置如下:

formItemProps: {
  valueType: 'cascader',
  options: regionOptions,
  multiple: true,
  showCheckedStrategy: 'SHOW_PARENT', // 或 'SHOW_CHILD'
  showSearch: true,
}
// 多选值是二维路径数组,例如:[['zj', 'hz', 0], ['js', 'nj', 1]]
  • multiple 默认 false;开启后复选框切换当前节点及其可用后代,父级支持半选。点击分支文案仅展开,点击末级文案可勾选。
  • showCheckedStrategy 默认 SHOW_PARENT,全部可用子级选中时回填父级路径;SHOW_CHILD 回填全部已选末级路径。该策略同时作用于变更回调、输入框和单元格文本。
  • 多选路径以 ; 分隔回显,displayRender 逐条处理路径。禁用节点不参与父子联动,已有禁用值不会被其他分支操作误删。
  • 多选勾选及清空不会退出编辑;失焦或 Tab 提交,Escape 取消。方向键导航,Enter / 空格切换当前选项(搜索输入中的空格仍用于输入)。changeOnSelect 仅影响单选。
  • 示例新增“级联单选(任意级)”“级联多选(父级回填)”“级联多选(末级回填)”列。

树选择编辑(TreeSelect)

使用 formItemProps.valueType: 'tree-select',配置支持树形选择。父表和嵌套子表使用同一套编辑配置。

{
  title: '所属部门', dataIndex: 'departments', editable: true, width: 280,
  formItemProps: {
    valueType: 'tree-select',
    multiple: true, // 不传则单选
    showSearch: true,
    allowClear: true,
    treeDefaultExpandAll: true,
    treeData: [{ title: '组织架构', value: 'root', selectable: false, children: [
      { title: '生产中心', value: 'production', children: [
        { title: '织造车间', value: 0 },
        { title: '检验车间', value: 'inspection' },
      ] },
      { title: '停用部门', value: 'disabled', disabled: true },
    ] }],
  },
}
// 单选值:0;多选值:[0, 'inspection'],不是 Cascader 的路径数组。
  • treeData 使用 title/value/children,value 必须在树内唯一;数字 0 与字符串 '0' 严格区分。disabled 禁止选择当前节点,selectable: false 用于只能展开的分组。
  • multiple 默认 false;多选独立切换各个节点,不进行父子联动。单元格和输入框回显节点标题,多选使用 ; 分隔,显式 format 可覆盖单元格显示。
  • showSearch 单选默认关闭、多选默认开启,按标题搜索并保留祖先上下文;搜索不修改原展开状态。
  • treeDefaultExpandAll 默认 false;也可使用 treeDefaultExpandedKeys 指定默认展开节点,依据节点的 key(省略时使用 value)。已选节点的祖先自动展开。
  • 单选选择后提交;多选连续选择不提交,失焦或 Tab 提交。清空单选为 null、多选为 [],保持焦点和弹层,可继续选择;Escape 取消编辑。
  • 上下方向键导航,左右方向键展开/收起,Enter 或空格选择。搜索输入中的空格用于输入。
  • 本次实现基础单选与独立多选,不包含 treeCheckable、treeCheckStrictly、异步加载、labelInValue 和自定义字段映射。
  • “单元格编辑”示例新增“树选择(单选)”“树选择(多选)”列。组件内部索引和值处理放在组件目录,不增加公共工具包接口。

绘制到已有 Canvas 上下文

drawListTableToCanvas({ ctx, width, height, options, requestRender }) 使用现有列配置绘制静态表格,复用布局、单元格、表头与汇总规则,不创建表格 DOM 实例,也不重置目标 Canvas 的尺寸或变换。width、height 为逻辑像素,调用方可通过 ctx.translate/scale 定位和缩放。requestRender 用于图片加载后的重绘通知。内容按指定边界裁剪;不显示排序、筛选和滚动条等交互控件。汇总同步计算,适合打印和有限数据量的离屏绘制。

清空行选择

原生实例调用 table.clearRowSelection()。该方法清空通过选中 key 设置的行高亮、行勾选和 Shift 选择锚点,配置 rowSelection 且选择发生变化时触发 rowSelection.onChange([], [], { type: "none" })。受控模式请在回调中同步 selectedRowKeys。

过滤面板勾选或取消过滤项并即时应用时,会立即清空全部已选行,不保留仍在过滤结果中的勾选。

调用 table.setSelectedRowKeys(["1", "2", "3"]) 可以主动设置行高亮,无需配置 rowSelection,也不会新增 checkbox / radio 列。配置 rowSelection 时同时同步勾选并触发 onChange;单选模式只保留第一个 key。传入空数组等同清空,普通配置更新会保留未受控的选中 key。

普通按钮独立配置 popconfirm 即可在执行 onClick 前确认,无需设置 danger;禁用按钮不发起确认。汇总栏显示继续按现有缓存规则恢复用户选择;仅切换或恢复密度不会主动创建汇总栏,没有汇总配置时重置列设置也不会新增汇总栏。

表头文字区域优先选择并支持复制;拖动表头空白区域调整列顺序。完整交互见文档站“行选择”的业务状态与缓存回归示例。

展开行子表在手动调整列宽或列顺序后使用独立布局。父表配置 storageKey 且开启 cacheColumnSettings 时,子表的列宽与列顺序一同缓存;缓存按父行 rowKey、所在列和内容索引隔离,建议配置稳定的 rowKey。刷新或重新挂载后恢复,关闭列缓存后不再保存这些布局。展开行示例提供“重新挂载验证缓存”按钮。

禁用的原生按钮内容(type: "button", disabled: true)不会触发内容、单元格、行或列的点击回调,也不会改变活动单元格和鼠标选区;同一单元格内其他启用按钮仍可独立交互。

同一单元格编辑多个字段

formItemProps 兼容配置对象、配置数组,以及返回对象或数组的函数。数组每项直接配置 key、dataIndex、valueType、rules、onChange,不需要额外的 valueType。key 在当前单元格内唯一;dataIndex 相对行记录,嵌套属性使用 ['measurement', 'quantity'],写入保留未配置属性。label 提供无障碍名称,width 为固定像素或 fr 比例,默认 1fr;可设置 disabled、readOnly。

formItemProps: (_value, record, _index, context) => [
  {
    key: "quantity",
    dataIndex: "quantity",
    label: "数量",
    valueType: "number",
    width: "1fr",
    rules: [{ required: true, message: "请填写数量" }],
    onChange: (quantity, { draftRecord, record }) => ({
      totalPrice: Number(quantity ?? 0) * (draftRecord ?? record).unitPrice,
    }),
  },
  {
    key: "unit",
    dataIndex: "unitId",
    label: "单位",
    valueType: "select",
    width: 88,
    options:
      (context?.draftRecord ?? record).kind === "weight"
        ? weightUnits
        : lengthUnits,
    rules: [{ required: true, message: "请选择单位" }],
  },
];

函数的 record 保持会话基准快照,可选第四参数的只读 draftRecord 提供尚未提交的候选行。数组内回调接收当前项值,context 的 dataIndex 是该项路径,并增加 itemKey 与可选 draftRecord;同步补丁按操作顺序累计,整组提交时一次写入,不递归触发其他回调。回调返回 false、异常或非法补丁会保持编辑;普通校验失败仍保存整组记录并按项标错,validate() 包含没有独立列的字段。数字 0 与单位编码 0 均为有效值。

Tab/Shift+Tab 在组内遍历,越过首尾才提交并导航;Enter 提交整组,子选择弹层优先处理确认;Escape 先关闭子弹层,再取消整组。非编辑状态也按各项 width 分区绘制,空字段保留位置,长文本仅在本项内省略;进入/退出编辑时各字段位置保持一致。复杂展示使用列 render。列排序、筛选继续使用列自身 dataIndex。空数组不提供编辑,单项数组仍要求显式绑定。

首期数组支持 text、number、select、auto-complete、table-select、year、quarter、month、date、time、datetime、date-range、timerange、datetime-range、input-range、math-interval、number-unit。会话中保持项的顺序、key、路径及类型稳定,不支持嵌套数组、多行布局、动态增删项及 upload/color/tree-select/cascader;这些类型的原有对象配置不受影响。原生宿主须使用声明数组编辑能力的编辑工厂。中文示例位于文档站“单元格编辑 → 多字段组合编辑”。

数组编辑项在非编辑态按各自宽度保留分区,校验背景与 tooltip 仅作用于出错表单,提示锚定该表单区域中间;编辑态不展示tooltip,非编辑态鼠标移入出错表单区域时展示。日期、范围、数学区间及数字单位默认展示沿用编辑控件布局;单元格 padding 传递给表单,避免重复内边距。显式 format 与列 render 的自定义展示仍优先。

紧凑图片与表头操作

图片内容在绘制前按单元格扣除内边距后的可用宽高等比缩小,仅缩小不放大;不会为图片增高固定行高。图片原配置、自动行高测量和原图预览保持原尺寸。

表格和列均支持 showHeaderActions?: boolean(默认 true)。设为 false 时隐藏排序、筛选、固定操作图标及编辑标记,不预留图标空间。表格级关闭优先于列级开启;斜线表头仍需 headerDiagonal.showActions:true 才显示操作。普通表头的排序图标同时遵守 allowSort:false 和 sorter:false。此选项只控制图标显示,右键菜单、拖拽、缩放和编辑能力仍由各自配置决定。

运行时密度、滚动条、列顺序和列配置偏好按 storageKey 隔离。切换 key 时先使用新身份源配置,再读取新身份缓存;业务 hidden 仍不缓存。

版本与发布记录

@canvas-components/list-table 共发布 66 个版本,首次发布于 2026/08/19;过去 12 个月发布了 66 个版本。

版本发布日期距今
0.3.58latest2026/11/09今天
0.3.572026/11/09今天
0.3.562026/11/063 天前
0.3.552026/11/054 天前
0.3.542026/11/054 天前
0.3.532026/11/054 天前

关于 @canvas-components/list-table 的常见问题

@canvas-components/list-table 是什么?

Framework-agnostic list table core engine.

如何安装 @canvas-components/list-table?

在项目目录运行 npm install @canvas-components/list-table,或使用 pnpm add / yarn add / bun add。

@canvas-components/list-table 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@canvas-components/list-table 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@canvas-components/list-table 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@canvas-components/list-table 可以商用吗?

@canvas-components/list-table 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@canvas-components/list-table 的下载量有多少?

根据 npm 官方下载统计,@canvas-components/list-table 在 2026/10/09 – 2026/11/07 这 30 天内被下载 6,009 次,日均约 200 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@canvas-components/list-table 还在维护吗?

最新版本 0.3.58 发布于 2026/11/09(今天);至今共发布 66 个版本,其中过去 12 个月发布了 66 个;首次发布于 2026/08/19。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。