跳至正文
区块

Data Table数据表格

支持排序、筛选、选择、编辑与虚拟滚动的原生数据表格。

正在加载…

功能说明

管理数据集列表,支持当前页选择、固定名称列、列宽调整、三档密度、搜索与状态筛选。示例数据均为虚构。

用到的组件

组件用途
grid-layout页面外框:竖线容器、全屏横线与点阵分隔带
checkbox / button / input选择、操作与编辑
grid-toolbar顶部工具栏:搜索、筛选、列设置、密度各占一格
bar-field搜索输入与快捷键提示
bar-select状态筛选与负责人多选筛选
grid-pagination底部翻页条:范围、每页条数、首尾页和跳页,与表格之间隔一条点阵小带
segmented-control密度选择(只显示行数图标,在工具栏里是三个方格)
alert-dialog单行与批量删除确认
dropdown-menu列设置与窄屏排序菜单
empty / skeleton空状态与加载
sheet桌面右侧详情、手机底部详情与编辑面板

安装

pnpm dlx shadcn@latest add https://yiui.dev/r/data-table-01.json
会连带安装 shadcn 组件 button、input、checkbox、skeleton、empty、sheet、dropdown-menu、tooltip、pagination、select、alert-dialog、combobox、input-group, 本站条目 grid-layout、bar-field、grid-pagination、grid-toolbar、flow-tone、segmented-control、bar-select。已存在的文件会询问是否覆盖。

安装入口是 app/data-table/page.tsx,替换其中示例数据与列定义即可。

用法

"use client"
import { DataTable } from "@/components/data-table-01/data-table"
const columns = [{ accessorKey: "name", header: "名称" }]
const data = [{ id: "1", name: "城市声景" }]
export default function Page() {
  return <DataTable columns={columns} data={data} getRowId={(row) => row.id} />
}

客户端与服务端模式

默认 mode="client" 在本地排序、筛选和分页。mode="manual" 时由使用方响应 onSortingChange、onGlobalFilterChange、onColumnFiltersChange 和 onPaginationChange,请求后更新 data 与 rowCount。请求期间传 loading,失败传 error 和 onRetry。

编辑与失败回滚

列 meta.editable 支持 text 与 number。编辑框就地替换内容,整格描主色实线框,下方贴一条提示条显示快捷键;meta.validate(value, row) 返回错误文字时提示条变红,编辑态保留。

onCellCommit({ rowId, columnId, value, row }) 可以返回 Promise。保存是乐观的:编辑框立刻关闭,格子右上角显示「保存中」,成功后显示「已保存」并淡出;Promise 拒绝时值回滚,格子标红,悬停或聚焦可看到错误,按 Enter 重新编辑会带回草稿。值没有变化时不会调用 onCellCommit。父层应在保存成功后同步数据;编辑使用 accessorKey 对应的顶层字段。示例模拟 600ms 请求,改成已有的名称会被拒绝。

行操作与主动编辑

通过 apiRef 获取 DataTableHandle,调用 startEditing(rowId, columnId) 开始单元格编辑,调用 openDetails(rowId) 打开详情。桌面详情从右侧展开,手机从底部展开。

import { DataTableRowActions } from "@/components/data-table-01/data-table-row-actions"

;<DataTableRowActions
  label={row.name}
  actions={[
    { label: "查看详情", onSelect: () => apiRef.current?.openDetails(row.id) },
    {
      label: "编辑名称",
      onSelect: () => apiRef.current?.startEditing(row.id, "name"),
    },
    "separator",
    { label: "删除…", destructive: true, onSelect: () => setDeleting([row]) },
  ]}
/>

每项可传 icon、shortcut;"separator" 插入分隔线。示例提供查看、编辑、复制编号、导出 CSV、归档与删除。删除和批量删除先确认,复制结果由读屏提示。选中行内控件后按 Shift+F10 或 ContextMenu 键打开该行菜单。

窄于 40rem 时状态筛选独占一行,隐藏色点和数量以保留完整状态文字;隐藏密度选择,给搜索与筛选留出空间。虚拟滚动仅显示总行数。

负责人筛选

示例通过 toolbar={(table) => ...} 插槽接入方条多选:插槽里的控件包一层 GridToolbarItem 成为工具栏的一格,用 owner 列的 filterFn 匹配选中负责人,更新筛选后回到第一页。每位负责人的计数来自完整数据集;窄容器隐藏此筛选。

示例

服务端分页

首次进入第 3 页固定失败,重试后成功。

正在加载…

一万行虚拟滚动

传 virtualization={{ height: 480, estimateRowHeight: 40 }},关闭分页,仅渲染视口附近的行。数据使用确定性公式生成。

正在加载…

手机

小于 40rem 的容器显示两行账目列表。通过行菜单打开全部字段详情,点击可编辑字段保存;长按编号栏进入多选,排序收进工具栏菜单。

正在加载…

状态与保存失败

切换加载、两种空状态及错误;名称输入「失败」后保存可观察回滚。

正在加载…

API

columns、data、getRowId 必填。排序、分页、全局筛选、列筛选、行选择均支持受控值、对应 default* 初值与 on*Change 回调。分页默认每页 10 条。

搜索框默认只显示图标,读屏名称取 labels.searchField;传 searchLabel="搜索" 可在图标前显示条内标签。statusFilter={{ columnId, options }} 在搜索框右侧显示状态下拉(方条下拉,选项带色点和数量,清除即全部),空字符串表示全部,它的 label 作为未选时的占位(如「全部状态」,不传时取 labels.all),tone 设置色点颜色(如 "var(--flow-green)")。toolbar、bulkActions(selected)、rowActions(row) 提供操作插槽;bulkActions 里的按钮文字包在 <span data-label> 中,窄容器只显示图标,header 可传入头部内容。manual 模式使用 counts 传入状态计数。onSelectAll 由使用方处理跨页全选,表头只选择当前页。labels 覆盖文案。

列 meta 支持 align、editable、validate、hideOnMobile、pinned、bar(数字比例条)、max(服务端模式的列最大值),列宽使用原生 size。detailCell(value, row) 定制详情字段的展示,例如完整时间。首个标记 pinned 的可见数据列固定。

文件职责:data-table.tsx 组装,data-table-toolbar.tsx 工具栏,data-table-pagination.tsx 分页,data-table-cell.tsx 编辑,types.ts 类型,config.ts 文案。根节点为 data-slot="data-table",滚动容器为 data-table-scroll,编辑格为 data-table-cell。

可访问性

按键操作
Tab聚焦排序、选择、编辑格与列宽手柄
Enter / F2 / 双击开始编辑
Enter保存并聚焦下一行同列
点击别处保存
Tab / Shift+Tab保存并聚焦下一个 / 上一个可编辑格
Esc放弃修改
← / →(列宽手柄)调整 16px

表头提供 aria-sort,虚拟行提供 aria-rowindex,编辑错误通过 aria-describedby 关联。

注意事项

Table 固定使用 v8 API,调用函数添加 "use no memo",避免 React Compiler 缓存稳定可变对象读取。虚拟滚动建议固定行高。不提供多格粘贴、撤销与方向键网格导航。

源码