| 主题 | 发件人 | 日期 |
|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 |
| 新的工作机会 | Lark | 10 分钟前 |
| 每日邮件摘要 | Lark | 1 小时前 |
import { LayerCard, Table } from "@lark-ui/core";
export function TableBasicDemo() {
return (
<LayerCard className="p-0">
<Table>
<Table.Header>
<Table.Row>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{emailData.slice(0, 3).map((row) => (
<Table.Row key={row.id}>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}安装
统一导出
import { Table } from "@lark-ui/core";按需导出
import { Table } from "@lark-ui/core/components/table";用法
import { Table, LayerCard } from "@lark-ui/core";
export default function Example() {
return (
<LayerCard className="p-0">
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Name</Table.Head>
<Table.Head>Email</Table.Head>
<Table.Head>Role</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>John Doe</Table.Cell>
<Table.Cell>john@example.com</Table.Cell>
<Table.Cell>Admin</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
</LayerCard>
);
}示例
带复选框
使用 Table.CheckHead 和 Table.CheckCell 添加行选择。两者都接受
onCheckedChange,其签名与底层 Checkbox(复选框)组件一致,并通过第二个参数提供可选的事件详情。
较早的 onValueChange 属性仍然可用,但已被弃用——lint 规则会对其标记警告,并将在未来的主版本中移除。请通过重命名该属性完成迁移:
// Before (deprecated)
<Table.CheckCell onValueChange={(checked) => toggleRow(id)} />
// After
<Table.CheckCell onCheckedChange={(checked) => toggleRow(id)} />
// With event details
<Table.CheckCell
onCheckedChange={(checked, eventDetails) => {
toggleRow(id);
eventDetails?.event.stopPropagation();
}}
/>| 主题 | 发件人 | 日期 | |
|---|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 | |
| 新的工作机会 | Lark | 10 分钟前 | |
| 每日邮件摘要 | Lark | 1 小时前 |
import { useState } from "react";
import { LayerCard, Table } from "@lark-ui/core";
export function TableWithCheckboxDemo() {
const rows = emailData.slice(0, 3);
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
const toggleRow = (id: string) => {
setSelectedIds((prev) => {
const next = new Set(prev);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
return next;
});
};
const toggleAll = () => {
if (selectedIds.size === rows.length) {
setSelectedIds(new Set());
} else {
setSelectedIds(new Set(rows.map((r) => r.id)));
}
};
return (
<LayerCard className="p-0">
<Table>
<Table.Header>
<Table.Row>
<Table.CheckHead
checked={selectedIds.size === rows.length}
indeterminate={
selectedIds.size > 0 && selectedIds.size < rows.length
}
onCheckedChange={toggleAll}
aria-label="选择所有行"
/>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row key={row.id}>
<Table.CheckCell
checked={selectedIds.has(row.id)}
onCheckedChange={() => toggleRow(row.id)}
aria-label={`选择 ${row.subject}`}
/>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}紧凑表头
在 Table.Header 上使用 variant="compact" 可获得更紧凑的表头样式。
| 主题 | 发件人 | 日期 |
|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 |
| 新的工作机会 | Lark | 10 分钟前 |
| 每日邮件摘要 | Lark | 1 小时前 |
import { LayerCard, Table } from "@lark-ui/core";
export function TableWithCompactHeaderDemo() {
return (
<LayerCard className="p-0">
<Table>
<Table.Header variant="compact">
<Table.Row>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{emailData.slice(0, 3).map((row) => (
<Table.Row key={row.id}>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}选中行
在 Table.Row 上使用 variant="selected" 可高亮选中的行。
| 主题 | 发件人 | 日期 | |
|---|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 | |
| 新的工作机会 | Lark | 10 分钟前 | |
| 每日邮件摘要 | Lark | 1 小时前 |
import { useState } from "react";
import { LayerCard, Table } from "@lark-ui/core";
export function TableSelectedRowDemo() {
const rows = emailData.slice(0, 3);
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set(["2"]));
const toggleRow = (id: string) => {
setSelectedIds((prev) => {
const next = new Set(prev);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
return next;
});
};
const toggleAll = () => {
if (selectedIds.size === rows.length) {
setSelectedIds(new Set());
} else {
setSelectedIds(new Set(rows.map((r) => r.id)));
}
};
return (
<LayerCard className="p-0">
<Table>
<Table.Header>
<Table.Row>
<Table.CheckHead
checked={selectedIds.size === rows.length}
indeterminate={
selectedIds.size > 0 && selectedIds.size < rows.length
}
onCheckedChange={toggleAll}
aria-label="选择所有行"
/>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row) => (
<Table.Row
key={row.id}
variant={selectedIds.has(row.id) ? "selected" : "default"}
>
<Table.CheckCell
checked={selectedIds.has(row.id)}
onCheckedChange={() => toggleRow(row.id)}
aria-label={`选择 ${row.subject}`}
/>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}固定布局与列宽
如需精确控制列宽,请设置 layout="fixed" 并搭配 col 元素使用 colgroup。
| 主题 | 发件人 | 日期 |
|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 |
| 新的工作机会 | Lark | 10 分钟前 |
| 每日邮件摘要 | Lark | 1 小时前 |
| GitLab - 新评论 | Rob Knecht | 1 天前 |
| 不在办公室 | Johnnie Lappen | 3 天前 |
import { LayerCard, Table } from "@lark-ui/core";
export function TableFixedLayoutDemo() {
return (
<LayerCard className="p-0">
<Table layout="fixed">
<colgroup>
<col />
<col className="w-[150px]" />
<col className="w-[150px]" />
</colgroup>
<Table.Header>
<Table.Row>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{emailData.map((row) => (
<Table.Row key={row.id}>
<Table.Cell>{row.subject}</Table.Cell>
<Table.Cell>{row.from}</Table.Cell>
<Table.Cell>{row.date}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}粘性列
在 Table.Head 和 Table.Cell 上设置 sticky="left" 或 sticky="right",
即可将列固定在滚动容器的左侧或右侧边缘。组件会自动添加不透明背景和渐变淡出效果。
请用 overflow-x-auto 容器包裹表格。
| 主题 | 发件人 | 日期 | 标签 | 操作 |
|---|---|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 | — | |
| 新的工作机会 | Lark | 10 分钟前 | — | |
| 每日邮件摘要 | Lark | 1 小时前 | 促销 | |
| GitLab - 新评论 | Rob Knecht | 1 天前 | — | |
| 不在办公室 | Johnnie Lappen | 3 天前 | — |
import { Badge, Button, DropdownMenu, LayerCard, Table } from "@lark-ui/core";
import { DotsThree, Eye, PencilSimple, Trash } from "@phosphor-icons/react";
/**
* 演示固定在右侧的操作列,表格横向滚动时该列保持不动。
* 在 `Table.Head` 和 `Table.Cell` 上使用 `sticky="right"`——渐变淡出和不透明背景
* 会自动应用。
*/
export function TableStickyColumnDemo() {
return (
<LayerCard className="w-full max-w-md overflow-x-auto p-0">
<Table>
<Table.Header>
<Table.Row>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
<Table.Head>标签</Table.Head>
<Table.Head sticky="right">
<span className="sr-only">操作</span>
</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{emailData.map((row) => (
<Table.Row key={row.id}>
<Table.Cell className="whitespace-nowrap">
{row.subject}
</Table.Cell>
<Table.Cell className="whitespace-nowrap">{row.from}</Table.Cell>
<Table.Cell className="whitespace-nowrap">{row.date}</Table.Cell>
<Table.Cell className="whitespace-nowrap">
{row.tags ? (
<div className="inline-flex gap-1">
{row.tags.map((tag) => (
<Badge key={tag}>{tag}</Badge>
))}
</div>
) : (
"—"
)}
</Table.Cell>
<Table.Cell sticky="right" className="text-right">
<DropdownMenu>
<DropdownMenu.Trigger
render={
<Button
variant="ghost"
size="sm"
shape="square"
aria-label="更多选项"
>
<DotsThree weight="bold" size={16} />
</Button>
}
/>
<DropdownMenu.Content>
<DropdownMenu.Item icon={Eye}>查看</DropdownMenu.Item>
<DropdownMenu.Item icon={PencilSimple}>
编辑
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={Trash} variant="danger">
删除
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}紧凑表头与粘性列
将 Table.Header 上的 variant="compact" 与 sticky 列结合使用。
| 主题 | 发件人 | 日期 | 标签 | 操作 |
|---|---|---|---|---|
| Lark v1.0.0 已发布 | Visal In | 5 秒前 | — | |
| 新的工作机会 | Lark | 10 分钟前 | — | |
| 每日邮件摘要 | Lark | 1 小时前 | 促销 | |
| GitLab - 新评论 | Rob Knecht | 1 天前 | — | |
| 不在办公室 | Johnnie Lappen | 3 天前 | — |
import { Badge, Button, DropdownMenu, LayerCard, Table } from "@lark-ui/core";
import { DotsThree, Eye, PencilSimple, Trash } from "@phosphor-icons/react";
/**
* 演示紧凑表头与粘性列的组合。该组合可能导致紧凑表头背景(`bg-lark-elevated`)
* 与粘性单元格背景(`bg-lark-base`)之间出现视觉不一致。
*/
export function TableCompactStickyDemo() {
return (
<LayerCard className="w-full max-w-md overflow-x-auto p-0">
<Table>
<Table.Header variant="compact">
<Table.Row>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
<Table.Head>标签</Table.Head>
<Table.Head sticky="right">
<span className="sr-only">操作</span>
</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{emailData.map((row) => (
<Table.Row key={row.id}>
<Table.Cell className="whitespace-nowrap">
{row.subject}
</Table.Cell>
<Table.Cell className="whitespace-nowrap">{row.from}</Table.Cell>
<Table.Cell className="whitespace-nowrap">{row.date}</Table.Cell>
<Table.Cell className="whitespace-nowrap">
{row.tags ? (
<div className="inline-flex gap-1">
{row.tags.map((tag) => (
<Badge key={tag}>{tag}</Badge>
))}
</div>
) : (
"—"
)}
</Table.Cell>
<Table.Cell sticky="right" className="text-right">
<DropdownMenu>
<DropdownMenu.Trigger
render={
<Button
variant="ghost"
size="sm"
shape="square"
aria-label="更多选项"
>
<DotsThree weight="bold" size={16} />
</Button>
}
/>
<DropdownMenu.Content>
<DropdownMenu.Item icon={Eye}>查看</DropdownMenu.Item>
<DropdownMenu.Item icon={PencilSimple}>
编辑
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={Trash} variant="danger">
删除
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}完整示例
包含复选框、徽标(Badge)、操作按钮和固定列宽的完整表格。
| 主题 | 发件人 | 日期 | ||
|---|---|---|---|---|
Lark v1.0.0 已发布 | Visal In | 5 秒前 | ||
新的工作机会 | Lark | 10 分钟前 | ||
每日邮件摘要 促销 | Lark | 1 小时前 | ||
GitLab - 新评论 | Rob Knecht | 1 天前 | ||
不在办公室 | Johnnie Lappen | 3 天前 |
import { useState } from "react";
import { Badge, Button, DropdownMenu, LayerCard, Table } from "@lark-ui/core";
import { DotsThree, EnvelopeSimple, Eye, PencilSimple, Trash } from "@phosphor-icons/react";
export function TableFullDemo() {
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set(["2"]));
const toggleRow = (id: string) => {
setSelectedIds((prev) => {
const next = new Set(prev);
if (next.has(id)) {
next.delete(id);
} else {
next.add(id);
}
return next;
});
};
const toggleAll = () => {
if (selectedIds.size === emailData.length) {
setSelectedIds(new Set());
} else {
setSelectedIds(new Set(emailData.map((r) => r.id)));
}
};
return (
<LayerCard className="w-full overflow-x-auto p-0">
<Table layout="fixed">
<colgroup>
<col />{" "}
{/* Checkbox column - width handled by Table.CheckHead/CheckCell */}
<col />
<col style={{ width: "150px" }} />
<col style={{ width: "120px" }} />
<col style={{ width: "50px" }} />
</colgroup>
<Table.Header>
<Table.Row>
<Table.CheckHead
checked={selectedIds.size === emailData.length}
indeterminate={
selectedIds.size > 0 && selectedIds.size < emailData.length
}
onCheckedChange={toggleAll}
aria-label="选择所有行"
/>
<Table.Head>主题</Table.Head>
<Table.Head>发件人</Table.Head>
<Table.Head>日期</Table.Head>
<Table.Head></Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{emailData.map((row) => (
<Table.Row
key={row.id}
variant={selectedIds.has(row.id) ? "selected" : "default"}
>
<Table.CheckCell
checked={selectedIds.has(row.id)}
onCheckedChange={() => toggleRow(row.id)}
aria-label={`选择 ${row.subject}`}
/>
<Table.Cell>
<div className="flex items-center gap-2">
<EnvelopeSimple size={16} />
<span className="truncate">{row.subject}</span>
{row.tags && (
<div className="ml-2 inline-flex gap-1">
{row.tags.map((tag) => (
<Badge key={tag}>{tag}</Badge>
))}
</div>
)}
</div>
</Table.Cell>
<Table.Cell>
<span className="truncate">{row.from}</span>
</Table.Cell>
<Table.Cell>
<span className="truncate">{row.date}</span>
</Table.Cell>
<Table.Cell className="text-right">
<DropdownMenu>
<DropdownMenu.Trigger
render={
<Button
variant="ghost"
size="sm"
shape="square"
aria-label="更多选项"
>
<DotsThree weight="bold" size={16} />
</Button>
}
/>
<DropdownMenu.Content>
<DropdownMenu.Item icon={Eye}>查看</DropdownMenu.Item>
<DropdownMenu.Item icon={PencilSimple}>
编辑
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={Trash} variant="danger">
删除
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</LayerCard>
);
}API 参考
Table
根表格组件。渲染语义化的 <table> 元素。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| layout | "auto" | "fixed" | "auto" | - |
| variant | "default" | "selected" | "default" | - |
| sticky | "left" | "right" | - | - |
| className | string | - | 额外的 CSS 类名 |
| children | ReactNode | - | 子元素 |
Table.Header
表头区域。渲染 <thead>。设置 sticky 可将表头行固定在滚动容器顶部。
Table.Body
表体区域。渲染 <tbody>。
Table.Row
表格行。支持 variant="selected" 以高亮显示。
| 属性 | 类型 | 默认值 |
|---|
该组件没有专有属性,支持标准 HTML 属性。
Table.Head
表头单元格。渲染 <th>。接受 sticky="left" 或 sticky="right" 来固定该列。
Table.Cell
表体单元格。渲染 <td>。接受 sticky="left" 或 sticky="right" 来固定该列。
Table.CheckHead
带复选框的表头单元格,用于「全选」功能。
| 属性 | 类型 | 默认值 |
|---|
该组件没有专有属性,支持标准 HTML 属性。
Table.CheckCell
带复选框的表体单元格,用于行选择。
| 属性 | 类型 | 默认值 |
|---|
该组件没有专有属性,支持标准 HTML 属性。
Table.ResizeHandle
用于调整列宽的可拖拽手柄。可配合 TanStack Table 或自定义的调整逻辑使用。
TanStack Table 集成
如需排序、筛选和可调整列宽等高级功能,请集成 TanStack Table。表格(Table)组件经过专门设计,可与 TanStack 的无头 API 无缝协作。
import {
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Table } from "@lark-ui/core";
function DataTable({ data, columns }) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
columnResizeMode: "onChange",
});
return (
<Table layout="fixed">
<colgroup>
{table.getAllColumns().map((column) => (
<col key={column.id} style={{ width: column.getSize() }} />
))}
</colgroup>
<Table.Header>
{table.getHeaderGroups().map((headerGroup) => (
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<Table.Head key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
<Table.ResizeHandle
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
/>
</Table.Head>
))}
</Table.Row>
))}
</Table.Header>
<Table.Body>
{table.getRowModel().rows.map((row) => (
<Table.Row key={row.id}>
{row.getVisibleCells().map((cell) => (
<Table.Cell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Table.Cell>
))}
</Table.Row>
))}
</Table.Body>
</Table>
);
}无障碍
语义化 HTML
表格使用语义化的 <table>、<thead>、<tbody>、<th> 和 <td> 元素,便于屏幕阅读器正确导航。
复选框标签
始终为 Table.CheckHead 和 Table.CheckCell 提供 aria-label,以说明其用途。
键盘导航
Tab 可在可交互元素之间移动焦点。复选框响应 Space。