logoAnt Design

⌘ K
  • 设计
  • 研发
  • 组件
  • 博客
  • 资源
5.3.3
  • 组件总览
  • 通用
    • Button按钮
    • Icon图标
    • Typography排版
  • 布局
    • Divider分割线
    • Grid栅格
    • Layout布局
    • Space间距
  • 导航
    • Anchor锚点
    • Breadcrumb面包屑
    • Dropdown下拉菜单
    • Menu导航菜单
    • Pagination分页
    • Steps步骤条
  • 数据录入
    • AutoComplete自动完成
    • Cascader级联选择
    • Checkbox多选框
    • DatePicker日期选择框
    • Form表单
    • Input输入框
    • InputNumber数字输入框
    • Mentions提及
    • Radio单选框
    • Rate评分
    • Select选择器
    • Slider滑动输入条
    • Switch开关
    • TimePicker时间选择框
    • Transfer穿梭框
    • TreeSelect树选择
    • Upload上传
  • 数据展示
    • Avatar头像
    • Badge徽标数
    • Calendar日历
    • Card卡片
    • Carousel走马灯
    • Collapse折叠面板
    • Descriptions描述列表
    • Empty空状态
    • Image图片
    • List列表
    • Popover气泡卡片
    • QRCode二维码
    • Segmented分段控制器
    • Statistic统计数值
    • Table表格
    • Tabs标签页
    • Tag标签
    • Timeline时间轴
    • Tooltip文字提示
    • Tour漫游式引导
    • Tree树形控件
  • 反馈
    • Alert警告提示
    • Drawer抽屉
    • Message全局提示
    • Modal对话框
    • Notification通知提醒框
    • Popconfirm气泡确认框
    • Progress进度条
    • Result结果
    • Skeleton骨架屏
    • Spin加载中
  • 其他
    • Affix固钉
    • App包裹组件
    • ConfigProvider全局化配置
    • FloatButton悬浮按钮
    • Watermark水印
何时使用
如何使用
相关推荐
代码演示
基本用法
JSX 风格的 API
可选择
选择和操作
自定义选择项
筛选和排序
树型筛选菜单
自定义筛选的搜索
多列排序
可控的筛选和排序
自定义筛选菜单
远程加载数据
紧凑型
带边框
可展开
特殊列排序
表格行/列合并
树形数据展示
固定表头
固定列
固定头和列
表头分组
可编辑单元格
可编辑行
嵌套子表格
拖拽排序
拖拽手柄列
单元格自动省略
自定义单元格省略提示
总结栏
虚拟列表
响应式
分页设置
随页面滚动的固定表头和滚动条
动态控制表格属性
API
Table
Column
ColumnGroup
pagination
expandable
rowSelection
scroll
selection
在 TypeScript 中使用
注意
FAQ
如何在没有数据或只有一页数据时隐藏分页栏
表格过滤时会回到第一页?
表格分页为何会出现 size 切换器?
为什么 更新 state 会导致全表渲染?
固定列穿透到最上层该怎么办?
如何自定义渲染可选列的勾选框(比如增加 Tooltip)?

Table表格

Statistic统计数值Tabs标签页

相关资源

Ant Design Charts
Ant Design Pro
Ant Design Pro Components
Ant Design Mobile
Ant Design Mini
Ant Design Landing-首页模板集
Scaffolds-脚手架市场
Umi-React 应用开发框架
dumi-组件/文档研发工具
qiankun-微前端框架
ahooks-React Hooks 库
Ant Motion-设计动效
国内镜像站点 🇨🇳

社区

Awesome Ant Design
Medium
Twitter
yuqueAnt Design 语雀专栏
Ant Design 知乎专栏
体验科技专栏
seeconfSEE Conf-蚂蚁体验科技大会
加入我们

帮助

GitHub
更新日志
常见问题
报告 Bug
议题
讨论区
StackOverflow
SegmentFault

Ant XTech更多产品

yuque语雀-构建你的数字花园
AntVAntV-数据可视化解决方案
EggEgg-企业级 Node.js 框架
kitchenKitchen-Sketch 工具集
xtech蚂蚁体验科技
主题编辑器
Made with ❤ by
蚂蚁集团和 Ant Design 开源社区

展示行列数据。

何时使用

  • 当有大量结构化的数据需要展现时;
  • 当需要对数据进行排序、搜索、分页、自定义操作等复杂行为时。

如何使用

指定表格的数据源 dataSource 为一个数组。

const dataSource = [
{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号',
},
{
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号',
},
];
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
];
<Table dataSource={dataSource} columns={columns} />;

相关推荐

  • Kitchen Sketch 插件 💎:设计师神器,两步自动生成 Ant Design 表格组件。
  • ProTable 高级表格:在 antd Table 之上扩展了更多便捷易用的功能,内置搜索、筛选、刷新等常用表格行为,并为多种类型数据展示提供了内置格式化。
  • S2 多维交叉分析表格:AntV S2 和 Antd Table 有什么区别?

代码演示

NameAgeAddressTagsAction
John Brown32New York No. 1 Lake ParkNICEDEVELOPER
Invite John Brown
Delete
Jim Green42London No. 1 Lake ParkLOSER
Invite Jim Green
Delete
Joe Black32Sydney No. 1 Lake ParkCOOLTEACHER
Invite Joe Black
Delete
  • 1
基本用法

简单的表格,最后一列是各种操作。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Space, Table, Tag } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: string;
  name: string;
  age: number;
  address: string;
  tags: string[];
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    render: (text) => <a>{text}</a>,
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
    key: 'address',
  },
  {
    title: 'Tags',
    key: 'tags',
    dataIndex: 'tags',
    render: (_, { tags }) => (
      <>
        {tags.map((tag) => {
          let color = tag.length > 5 ? 'geekblue' : 'green';
          if (tag === 'loser') {
            color = 'volcano';
          }
          return (
            <Tag color={color} key={tag}>
              {tag.toUpperCase()}
            </Tag>
          );
        })}
      </>
    ),
  },
  {
    title: 'Action',
    key: 'action',
    render: (_, record) => (
      <Space size="middle">
        <a>Invite {record.name}</a>
        <a>Delete</a>
      </Space>
    ),
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
    tags: ['nice', 'developer'],
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
    tags: ['loser'],
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
    tags: ['cool', 'teacher'],
  },
];

const App: React.FC = () => <Table columns={columns} dataSource={data} />;

export default App;
NameAgeAddressTagsAction
First NameLast Name
JohnBrown32New York No. 1 Lake Parknicedeveloper
Invite Brown
Delete
JimGreen42London No. 1 Lake Parkloser
Invite Green
Delete
JoeBlack32Sydney No. 1 Lake Parkcoolteacher
Invite Black
Delete
  • 1
JSX 风格的 API

使用 JSX 风格的 API(2.5.0 以后引入)

这个只是一个描述 columns 的语法糖,所以你不能用其他组件去包裹 Column 和 ColumnGroup。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Space, Table, Tag } from 'antd';

const { Column, ColumnGroup } = Table;

interface DataType {
  key: React.Key;
  firstName: string;
  lastName: string;
  age: number;
  address: string;
  tags: string[];
}

const data: DataType[] = [
  {
    key: '1',
    firstName: 'John',
    lastName: 'Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
    tags: ['nice', 'developer'],
  },
  {
    key: '2',
    firstName: 'Jim',
    lastName: 'Green',
    age: 42,
    address: 'London No. 1 Lake Park',
    tags: ['loser'],
  },
  {
    key: '3',
    firstName: 'Joe',
    lastName: 'Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
    tags: ['cool', 'teacher'],
  },
];

const App: React.FC = () => (
  <Table dataSource={data}>
    <ColumnGroup title="Name">
      <Column title="First Name" dataIndex="firstName" key="firstName" />
      <Column title="Last Name" dataIndex="lastName" key="lastName" />
    </ColumnGroup>
    <Column title="Age" dataIndex="age" key="age" />
    <Column title="Address" dataIndex="address" key="address" />
    <Column
      title="Tags"
      dataIndex="tags"
      key="tags"
      render={(tags: string[]) => (
        <>
          {tags.map((tag) => (
            <Tag color="blue" key={tag}>
              {tag}
            </Tag>
          ))}
        </>
      )}
    />
    <Column
      title="Action"
      key="action"
      render={(_: any, record: DataType) => (
        <Space size="middle">
          <a>Invite {record.lastName}</a>
          <a>Delete</a>
        </Space>
      )}
    />
  </Table>
);

export default App;
NameAgeAddress
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
Disabled User99Sydney No. 1 Lake Park
  • 1
可选择

第一列是联动的选择框。可以通过 rowSelection.type 属性指定选择类型,默认为 checkbox。

默认点击 checkbox 触发选择行为,需要点击行触发可以参考例子:https://codesandbox.io/s/000vqw38rl

expand codeexpand code
TypeScript
JavaScript
import React, { useState } from 'react';
import { Divider, Radio, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    render: (text: string) => <a>{text}</a>,
  },
  {
    title: 'Age',
    dataIndex: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Disabled User',
    age: 99,
    address: 'Sydney No. 1 Lake Park',
  },
];

// rowSelection object indicates the need for row selection
const rowSelection = {
  onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
    console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  },
  getCheckboxProps: (record: DataType) => ({
    disabled: record.name === 'Disabled User', // Column configuration not to be checked
    name: record.name,
  }),
};

const App: React.FC = () => {
  const [selectionType, setSelectionType] = useState<'checkbox' | 'radio'>('checkbox');

  return (
    <div>
      <Radio.Group
        onChange={({ target: { value } }) => {
          setSelectionType(value);
        }}
        value={selectionType}
      >
        <Radio value="checkbox">Checkbox</Radio>
        <Radio value="radio">radio</Radio>
      </Radio.Group>

      <Divider />

      <Table
        rowSelection={{
          type: selectionType,
          ...rowSelection,
        }}
        columns={columns}
        dataSource={data}
      />
    </div>
  );
};

export default App;
NameAgeAddress
Edward King 032London, Park Lane no. 0
Edward King 132London, Park Lane no. 1
Edward King 232London, Park Lane no. 2
Edward King 332London, Park Lane no. 3
Edward King 432London, Park Lane no. 4
Edward King 532London, Park Lane no. 5
Edward King 632London, Park Lane no. 6
Edward King 732London, Park Lane no. 7
Edward King 832London, Park Lane no. 8
Edward King 932London, Park Lane no. 9
  • 1
  • 2
  • 3
  • 4
  • 5
选择和操作

选择后进行操作,完成后清空选择,通过 rowSelection.selectedRowKeys 来控制选中项。

expand codeexpand code
TypeScript
JavaScript
import React, { useState } from 'react';
import { Button, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data: DataType[] = [];
for (let i = 0; i < 46; i++) {
  data.push({
    key: i,
    name: `Edward King ${i}`,
    age: 32,
    address: `London, Park Lane no. ${i}`,
  });
}

const App: React.FC = () => {
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  const [loading, setLoading] = useState(false);

  const start = () => {
    setLoading(true);
    // ajax request after empty completing
    setTimeout(() => {
      setSelectedRowKeys([]);
      setLoading(false);
    }, 1000);
  };

  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
    console.log('selectedRowKeys changed: ', newSelectedRowKeys);
    setSelectedRowKeys(newSelectedRowKeys);
  };

  const rowSelection = {
    selectedRowKeys,
    onChange: onSelectChange,
  };
  const hasSelected = selectedRowKeys.length > 0;

  return (
    <div>
      <div style={{ marginBottom: 16 }}>
        <Button type="primary" onClick={start} disabled={!hasSelected} loading={loading}>
          Reload
        </Button>
        <span style={{ marginLeft: 8 }}>
          {hasSelected ? `Selected ${selectedRowKeys.length} items` : ''}
        </span>
      </div>
      <Table rowSelection={rowSelection} columns={columns} dataSource={data} />
    </div>
  );
};

export default App;
NameAgeAddress
Edward King 032London, Park Lane no. 0
Edward King 132London, Park Lane no. 1
Edward King 232London, Park Lane no. 2
Edward King 332London, Park Lane no. 3
Edward King 432London, Park Lane no. 4
Edward King 532London, Park Lane no. 5
Edward King 632London, Park Lane no. 6
Edward King 732London, Park Lane no. 7
Edward King 832London, Park Lane no. 8
Edward King 932London, Park Lane no. 9
  • 1
  • 2
  • 3
  • 4
  • 5
自定义选择项

通过 rowSelection.selections 自定义选择项,默认不显示下拉选项,设为 true 时显示默认选择项。

expand codeexpand code
TypeScript
JavaScript
import React, { useState } from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { TableRowSelection } from 'antd/es/table/interface';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data: DataType[] = [];
for (let i = 0; i < 46; i++) {
  data.push({
    key: i,
    name: `Edward King ${i}`,
    age: 32,
    address: `London, Park Lane no. ${i}`,
  });
}

const App: React.FC = () => {
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);

  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
    console.log('selectedRowKeys changed: ', newSelectedRowKeys);
    setSelectedRowKeys(newSelectedRowKeys);
  };

  const rowSelection: TableRowSelection<DataType> = {
    selectedRowKeys,
    onChange: onSelectChange,
    selections: [
      Table.SELECTION_ALL,
      Table.SELECTION_INVERT,
      Table.SELECTION_NONE,
      {
        key: 'odd',
        text: 'Select Odd Row',
        onSelect: (changeableRowKeys) => {
          let newSelectedRowKeys = [];
          newSelectedRowKeys = changeableRowKeys.filter((_, index) => {
            if (index % 2 !== 0) {
              return false;
            }
            return true;
          });
          setSelectedRowKeys(newSelectedRowKeys);
        },
      },
      {
        key: 'even',
        text: 'Select Even Row',
        onSelect: (changeableRowKeys) => {
          let newSelectedRowKeys = [];
          newSelectedRowKeys = changeableRowKeys.filter((_, index) => {
            if (index % 2 !== 0) {
              return true;
            }
            return false;
          });
          setSelectedRowKeys(newSelectedRowKeys);
        },
      },
    ],
  };

  return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
};

export default App;
Name
Age
Address
Jim Green42London No. 1 Lake Park
John Brown32New York No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
Jim Red32London No. 2 Lake Park
  • 1
筛选和排序

对某一列数据进行筛选,使用列的 filters 属性来指定需要筛选菜单的列,onFilter 用于筛选当前数据,filterMultiple 用于指定多选和单选。

对某一列数据进行排序,通过指定列的 sorter 函数即可启动排序按钮。sorter: function(rowA, rowB) { ... }, rowA、rowB 为比较的两个行数据。

sortDirections: ['ascend' | 'descend']改变每列可用的排序方式,切换排序时按数组内容依次切换,设置在 table props 上时对所有列生效。你可以通过设置 ['ascend', 'descend', 'ascend'] 禁止排序恢复到默认状态。

使用 defaultSortOrder 属性,设置列的默认排序顺序。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    filters: [
      {
        text: 'Joe',
        value: 'Joe',
      },
      {
        text: 'Jim',
        value: 'Jim',
      },
      {
        text: 'Submenu',
        value: 'Submenu',
        children: [
          {
            text: 'Green',
            value: 'Green',
          },
          {
            text: 'Black',
            value: 'Black',
          },
        ],
      },
    ],
    // specify the condition of filtering result
    // here is that finding the name started with `value`
    onFilter: (value: string, record) => record.name.indexOf(value) === 0,
    sorter: (a, b) => a.name.length - b.name.length,
    sortDirections: ['descend'],
  },
  {
    title: 'Age',
    dataIndex: 'age',
    defaultSortOrder: 'descend',
    sorter: (a, b) => a.age - b.age,
  },
  {
    title: 'Address',
    dataIndex: 'address',
    filters: [
      {
        text: 'London',
        value: 'London',
      },
      {
        text: 'New York',
        value: 'New York',
      },
    ],
    onFilter: (value: string, record) => record.address.indexOf(value) === 0,
  },
];

const data = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 32,
    address: 'London No. 2 Lake Park',
  },
];

const onChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter, extra) => {
  console.log('params', pagination, filters, sorter, extra);
};

const App: React.FC = () => <Table columns={columns} dataSource={data} onChange={onChange} />;

export default App;
Name
Age
Address
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
Jim Red32London No. 2 Lake Park
  • 1
树型筛选菜单

可以使用 filterMode 来修改筛选菜单的 UI,可选值有 menu(默认)和 tree。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    filters: [
      {
        text: 'Joe',
        value: 'Joe',
      },
      {
        text: 'Category 1',
        value: 'Category 1',
        children: [
          {
            text: 'Yellow',
            value: 'Yellow',
          },
          {
            text: 'Pink',
            value: 'Pink',
          },
        ],
      },
      {
        text: 'Category 2',
        value: 'Category 2',
        children: [
          {
            text: 'Green',
            value: 'Green',
          },
          {
            text: 'Black',
            value: 'Black',
          },
        ],
      },
    ],
    filterMode: 'tree',
    filterSearch: true,
    onFilter: (value: string, record) => record.name.includes(value),
    width: '30%',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    sorter: (a, b) => a.age - b.age,
  },
  {
    title: 'Address',
    dataIndex: 'address',
    filters: [
      {
        text: 'London',
        value: 'London',
      },
      {
        text: 'New York',
        value: 'New York',
      },
    ],
    onFilter: (value: string, record) => record.address.startsWith(value),
    filterSearch: true,
    width: '40%',
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 32,
    address: 'London No. 2 Lake Park',
  },
];

const onChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter, extra) => {
  console.log('params', pagination, filters, sorter, extra);
};

const App: React.FC = () => <Table columns={columns} dataSource={data} onChange={onChange} />;

export default App;
Name
Age
Address
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
Jim Red32London No. 2 Lake Park
  • 1
自定义筛选的搜索

filterSearch 用于开启筛选项的搜索,通过 filterSearch:(input, record) => boolean 设置自定义筛选方法

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    filters: [
      {
        text: 'Joe',
        value: 'Joe',
      },
      {
        text: 'Category 1',
        value: 'Category 1',
      },
      {
        text: 'Category 2',
        value: 'Category 2',
      },
    ],
    filterMode: 'tree',
    filterSearch: true,
    onFilter: (value: string, record) => record.name.startsWith(value),
    width: '30%',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    sorter: (a, b) => a.age - b.age,
  },
  {
    title: 'Address',
    dataIndex: 'address',
    filters: [
      {
        text: 'London',
        value: 'London',
      },
      {
        text: 'New York',
        value: 'New York',
      },
    ],
    onFilter: (value: string, record) => record.address.startsWith(value),
    filterSearch: true,
    width: '40%',
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 32,
    address: 'London No. 2 Lake Park',
  },
];

const onChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter, extra) => {
  console.log('params', pagination, filters, sorter, extra);
};

const App: React.FC = () => <Table columns={columns} dataSource={data} onChange={onChange} />;

export default App;
Name
Chinese Score
Math Score
English Score
John Brown986070
Jim Green986689
Joe Black989070
Jim Red889989
  • 1
多列排序

column.sorter 支持 multiple 字段以配置多列排序优先级。通过 sorter.compare 配置排序逻辑,你可以通过不设置该函数只启动多列排序的交互形式。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  chinese: number;
  math: number;
  english: number;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
  },
  {
    title: 'Chinese Score',
    dataIndex: 'chinese',
    sorter: {
      compare: (a, b) => a.chinese - b.chinese,
      multiple: 3,
    },
  },
  {
    title: 'Math Score',
    dataIndex: 'math',
    sorter: {
      compare: (a, b) => a.math - b.math,
      multiple: 2,
    },
  },
  {
    title: 'English Score',
    dataIndex: 'english',
    sorter: {
      compare: (a, b) => a.english - b.english,
      multiple: 1,
    },
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    chinese: 98,
    math: 60,
    english: 70,
  },
  {
    key: '2',
    name: 'Jim Green',
    chinese: 98,
    math: 66,
    english: 89,
  },
  {
    key: '3',
    name: 'Joe Black',
    chinese: 98,
    math: 90,
    english: 70,
  },
  {
    key: '4',
    name: 'Jim Red',
    chinese: 88,
    math: 99,
    english: 89,
  },
];

const onChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter, extra) => {
  console.log('params', pagination, filters, sorter, extra);
};

const App: React.FC = () => <Table columns={columns} dataSource={data} onChange={onChange} />;

export default App;
Name
Age
Address
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
Jim Red32London No. 2 Lake Park
  • 1
可控的筛选和排序

使用受控属性对筛选和排序状态进行控制。

  1. columns 中定义了 filteredValue 和 sortOrder 属性即视为受控模式。
  2. 只支持同时对一列进行排序,请保证只有一列的 sortOrder 属性是生效的。
  3. 务必指定 column.key。
expand codeexpand code
TypeScript
JavaScript
import React, { useState } from 'react';
import type { TableProps } from 'antd';
import { Button, Space, Table } from 'antd';
import type { ColumnsType, FilterValue, SorterResult } from 'antd/es/table/interface';

interface DataType {
  key: string;
  name: string;
  age: number;
  address: string;
}

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 32,
    address: 'London No. 2 Lake Park',
  },
];

const App: React.FC = () => {
  const [filteredInfo, setFilteredInfo] = useState<Record<string, FilterValue | null>>({});
  const [sortedInfo, setSortedInfo] = useState<SorterResult<DataType>>({});

  const handleChange: TableProps<DataType>['onChange'] = (pagination, filters, sorter) => {
    console.log('Various parameters', pagination, filters, sorter);
    setFilteredInfo(filters);
    setSortedInfo(sorter as SorterResult<DataType>);
  };

  const clearFilters = () => {
    setFilteredInfo({});
  };

  const clearAll = () => {
    setFilteredInfo({});
    setSortedInfo({});
  };

  const setAgeSort = () => {
    setSortedInfo({
      order: 'descend',
      columnKey: 'age',
    });
  };

  const columns: ColumnsType<DataType> = [
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
      filters: [
        { text: 'Joe', value: 'Joe' },
        { text: 'Jim', value: 'Jim' },
      ],
      filteredValue: filteredInfo.name || null,
      onFilter: (value: string, record) => record.name.includes(value),
      sorter: (a, b) => a.name.length - b.name.length,
      sortOrder: sortedInfo.columnKey === 'name' ? sortedInfo.order : null,
      ellipsis: true,
    },
    {
      title: 'Age',
      dataIndex: 'age',
      key: 'age',
      sorter: (a, b) => a.age - b.age,
      sortOrder: sortedInfo.columnKey === 'age' ? sortedInfo.order : null,
      ellipsis: true,
    },
    {
      title: 'Address',
      dataIndex: 'address',
      key: 'address',
      filters: [
        { text: 'London', value: 'London' },
        { text: 'New York', value: 'New York' },
      ],
      filteredValue: filteredInfo.address || null,
      onFilter: (value: string, record) => record.address.includes(value),
      sorter: (a, b) => a.address.length - b.address.length,
      sortOrder: sortedInfo.columnKey === 'address' ? sortedInfo.order : null,
      ellipsis: true,
    },
  ];

  return (
    <>
      <Space style={{ marginBottom: 16 }}>
        <Button onClick={setAgeSort}>Sort age</Button>
        <Button onClick={clearFilters}>Clear filters</Button>
        <Button onClick={clearAll}>Clear filters and sorters</Button>
      </Space>
      <Table columns={columns} dataSource={data} onChange={handleChange} />
    </>
  );
};

export default App;
Name
Age
Address
John Brown32New York No. 1 Lake Park
Joe Black42London No. 1 Lake Park
Jim Green32Sydney No. 1 Lake Park
Jim Red32London No. 2 Lake Park
  • 1
自定义筛选菜单

通过 filterDropdown 自定义的列筛选功能,并实现一个搜索列的示例。

给函数 clearFilters 添加 boolean 类型参数 closeDropdown,是否关闭筛选菜单,默认为 true。添加 boolean 类型参数 confirm,清除筛选时是否提交已选项,默认 true。

expand codeexpand code
TypeScript
JavaScript
import React, { useRef, useState } from 'react';
import { SearchOutlined } from '@ant-design/icons';
import type { InputRef } from 'antd';
import { Button, Input, Space, Table } from 'antd';
import type { ColumnsType, ColumnType } from 'antd/es/table';
import type { FilterConfirmProps } from 'antd/es/table/interface';
import Highlighter from 'react-highlight-words';

interface DataType {
  key: string;
  name: string;
  age: number;
  address: string;
}

type DataIndex = keyof DataType;

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Joe Black',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Jim Green',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 32,
    address: 'London No. 2 Lake Park',
  },
];

const App: React.FC = () => {
  const [searchText, setSearchText] = useState('');
  const [searchedColumn, setSearchedColumn] = useState('');
  const searchInput = useRef<InputRef>(null);

  const handleSearch = (
    selectedKeys: string[],
    confirm: (param?: FilterConfirmProps) => void,
    dataIndex: DataIndex,
  ) => {
    confirm();
    setSearchText(selectedKeys[0]);
    setSearchedColumn(dataIndex);
  };

  const handleReset = (clearFilters: () => void) => {
    clearFilters();
    setSearchText('');
  };

  const getColumnSearchProps = (dataIndex: DataIndex): ColumnType<DataType> => ({
    filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, close }) => (
      <div style={{ padding: 8 }} onKeyDown={(e) => e.stopPropagation()}>
        <Input
          ref={searchInput}
          placeholder={`Search ${dataIndex}`}
          value={selectedKeys[0]}
          onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])}
          onPressEnter={() => handleSearch(selectedKeys as string[], confirm, dataIndex)}
          style={{ marginBottom: 8, display: 'block' }}
        />
        <Space>
          <Button
            type="primary"
            onClick={() => handleSearch(selectedKeys as string[], confirm, dataIndex)}
            icon={<SearchOutlined />}
            size="small"
            style={{ width: 90 }}
          >
            Search
          </Button>
          <Button
            onClick={() => clearFilters && handleReset(clearFilters)}
            size="small"
            style={{ width: 90 }}
          >
            Reset
          </Button>
          <Button
            type="link"
            size="small"
            onClick={() => {
              confirm({ closeDropdown: false });
              setSearchText((selectedKeys as string[])[0]);
              setSearchedColumn(dataIndex);
            }}
          >
            Filter
          </Button>
          <Button
            type="link"
            size="small"
            onClick={() => {
              close();
            }}
          >
            close
          </Button>
        </Space>
      </div>
    ),
    filterIcon: (filtered: boolean) => (
      <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
    ),
    onFilter: (value, record) =>
      record[dataIndex]
        .toString()
        .toLowerCase()
        .includes((value as string).toLowerCase()),
    onFilterDropdownOpenChange: (visible) => {
      if (visible) {
        setTimeout(() => searchInput.current?.select(), 100);
      }
    },
    render: (text) =>
      searchedColumn === dataIndex ? (
        <Highlighter
          highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
          searchWords={[searchText]}
          autoEscape
          textToHighlight={text ? text.toString() : ''}
        />
      ) : (
        text
      ),
  });

  const columns: ColumnsType<DataType> = [
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
      width: '30%',
      ...getColumnSearchProps('name'),
    },
    {
      title: 'Age',
      dataIndex: 'age',
      key: 'age',
      width: '20%',
      ...getColumnSearchProps('age'),
    },
    {
      title: 'Address',
      dataIndex: 'address',
      key: 'address',
      ...getColumnSearchProps('address'),
      sorter: (a, b) => a.address.length - b.address.length,
      sortDirections: ['descend', 'ascend'],
    },
  ];

  return <Table columns={columns} dataSource={data} />;
};

export default App;
Name
Gender
Email
暂无数据
远程加载数据

这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。

另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 onFilter 和 sorter 函数,而是在把筛选和排序的参数发到服务端来处理。

当使用 rowSelection 时,请设置 rowSelection.preserveSelectedRowKeys 属性以保留 key。

注意,此示例使用 模拟接口,展示数据可能不准确,请打开网络面板查看请求。

🛎️ 想要 3 分钟实现?试试 ProTable!

expand codeexpand code
TypeScript
JavaScript
import React, { useEffect, useState } from 'react';
import { Table } from 'antd';
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
import type { FilterValue, SorterResult } from 'antd/es/table/interface';
import qs from 'qs';

interface DataType {
  name: {
    first: string;
    last: string;
  };
  gender: string;
  email: string;
  login: {
    uuid: string;
  };
}

interface TableParams {
  pagination?: TablePaginationConfig;
  sortField?: string;
  sortOrder?: string;
  filters?: Record<string, FilterValue>;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    sorter: true,
    render: (name) => `${name.first} ${name.last}`,
    width: '20%',
  },
  {
    title: 'Gender',
    dataIndex: 'gender',
    filters: [
      { text: 'Male', value: 'male' },
      { text: 'Female', value: 'female' },
    ],
    width: '20%',
  },
  {
    title: 'Email',
    dataIndex: 'email',
  },
];

const getRandomuserParams = (params: TableParams) => ({
  results: params.pagination?.pageSize,
  page: params.pagination?.current,
  ...params,
});

const App: React.FC = () => {
  const [data, setData] = useState<DataType[]>();
  const [loading, setLoading] = useState(false);
  const [tableParams, setTableParams] = useState<TableParams>({
    pagination: {
      current: 1,
      pageSize: 10,
    },
  });

  const fetchData = () => {
    setLoading(true);
    fetch(`https://randomuser.me/api?${qs.stringify(getRandomuserParams(tableParams))}`)
      .then((res) => res.json())
      .then(({ results }) => {
        setData(results);
        setLoading(false);
        setTableParams({
          ...tableParams,
          pagination: {
            ...tableParams.pagination,
            total: 200,
            // 200 is mock data, you should read it from server
            // total: data.totalCount,
          },
        });
      });
  };

  useEffect(() => {
    fetchData();
  }, [JSON.stringify(tableParams)]);

  const handleTableChange = (
    pagination: TablePaginationConfig,
    filters: Record<string, FilterValue>,
    sorter: SorterResult<DataType>,
  ) => {
    setTableParams({
      pagination,
      filters,
      ...sorter,
    });

    // `dataSource` is useless since `pageSize` changed
    if (pagination.pageSize !== tableParams.pagination?.pageSize) {
      setData([]);
    }
  };

  return (
    <Table
      columns={columns}
      rowKey={(record) => record.login.uuid}
      dataSource={data}
      pagination={tableParams.pagination}
      loading={loading}
      onChange={handleTableChange}
    />
  );
};

export default App;
Middle size table
NameAgeAddress
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
  • 1
Small size table
NameAgeAddress
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
  • 1
紧凑型

两种紧凑型的列表,小型列表只用于对话框内。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table, Divider } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
];

const App: React.FC = () => (
  <>
    <Divider>Middle size table</Divider>
    <Table columns={columns} dataSource={data} size="middle" />
    <Divider>Small size table</Divider>
    <Table columns={columns} dataSource={data} size="small" />
  </>
);

export default App;
Header
NameCash AssetsAddress
John Brown¥300,000.00New York No. 1 Lake Park
Jim Green¥1,256,000.00London No. 1 Lake Park
Joe Black¥120,000.00Sydney No. 1 Lake Park
Footer
  • 1
带边框

添加表格边框线,页头和页脚。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: string;
  name: string;
  money: string;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    render: (text) => <a>{text}</a>,
  },
  {
    title: 'Cash Assets',
    className: 'column-money',
    dataIndex: 'money',
    align: 'right',
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    money: '¥300,000.00',
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    money: '¥1,256,000.00',
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    money: '¥120,000.00',
    address: 'Sydney No. 1 Lake Park',
  },
];

const App: React.FC = () => (
  <Table
    columns={columns}
    dataSource={data}
    bordered
    title={() => 'Header'}
    footer={() => 'Footer'}
  />
);

export default App;
NameAgeAddressAction
John Brown32New York No. 1 Lake ParkDelete
Jim Green42London No. 1 Lake ParkDelete
Not Expandable29Jiangsu No. 1 Lake ParkDelete
Joe Black32Sydney No. 1 Lake ParkDelete
  • 1
可展开

当表格内容较多不能一次性完全展示时。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
  description: string;
}

const columns: ColumnsType<DataType> = [
  { title: 'Name', dataIndex: 'name', key: 'name' },
  { title: 'Age', dataIndex: 'age', key: 'age' },
  { title: 'Address', dataIndex: 'address', key: 'address' },
  {
    title: 'Action',
    dataIndex: '',
    key: 'x',
    render: () => <a>Delete</a>,
  },
];

const data: DataType[] = [
  {
    key: 1,
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
    description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
  },
  {
    key: 2,
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
    description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
  },
  {
    key: 3,
    name: 'Not Expandable',
    age: 29,
    address: 'Jiangsu No. 1 Lake Park',
    description: 'This not expandable',
  },
  {
    key: 4,
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
    description: 'My name is Joe Black, I am 32 years old, living in Sydney No. 1 Lake Park.',
  },
];

const App: React.FC = () => (
  <Table
    columns={columns}
    expandable={{
      expandedRowRender: (record) => <p style={{ margin: 0 }}>{record.description}</p>,
      rowExpandable: (record) => record.name !== 'Not Expandable',
    }}
    dataSource={data}
  />
);

export default App;
NameAge
Address
John Brown32New York No. 1 Lake Park
Jim Green42London No. 1 Lake Park
Not Expandable29Jiangsu No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
  • 1
特殊列排序

你可以通过 Table.EXPAND_COLUMN 和 Table.SELECT_COLUMN 来控制选择和展开列的顺序。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
  description: string;
}

const columns: ColumnsType<DataType> = [
  { title: 'Name', dataIndex: 'name', key: 'name' },
  Table.EXPAND_COLUMN,
  { title: 'Age', dataIndex: 'age', key: 'age' },
  Table.SELECTION_COLUMN,
  { title: 'Address', dataIndex: 'address', key: 'address' },
];

const data: DataType[] = [
  {
    key: 1,
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
    description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
  },
  {
    key: 2,
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
    description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
  },
  {
    key: 3,
    name: 'Not Expandable',
    age: 29,
    address: 'Jiangsu No. 1 Lake Park',
    description: 'This not expandable',
  },
  {
    key: 4,
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
    description: 'My name is Joe Black, I am 32 years old, living in Sydney No. 1 Lake Park.',
  },
];

const App: React.FC = () => (
  <Table
    columns={columns}
    rowSelection={{}}
    expandable={{
      expandedRowRender: (record) => <p style={{ margin: 0 }}>{record.description}</p>,
    }}
    dataSource={data}
  />
);

export default App;
RowHeadNameAgeHome phoneAddress
1John Brown320571-2209890918889898989New York No. 1 Lake Park
2Jim Green
3Joe Black320575-2209890918900010002Sydney No. 1 Lake Park
4Jim Red180575-2209890918900010002London No. 2 Lake Park
5Jake White1818900010002Dublin No. 2 Lake Park
  • 1
表格行/列合并

表头只支持列合并,使用 column 里的 colSpan 进行设置。

表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: string;
  name: string;
  age: number;
  tel: string;
  phone: number;
  address: string;
}

// In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0
const sharedOnCell = (_: DataType, index: number) => {
  if (index === 1) {
    return { colSpan: 0 };
  }

  return {};
};

const columns: ColumnsType<DataType> = [
  {
    title: 'RowHead',
    dataIndex: 'key',
    rowScope: 'row',
  },
  {
    title: 'Name',
    dataIndex: 'name',
    render: (text) => <a>{text}</a>,
    onCell: (_, index) => ({
      colSpan: index === 1 ? 5 : 1,
    }),
  },
  {
    title: 'Age',
    dataIndex: 'age',
    onCell: sharedOnCell,
  },
  {
    title: 'Home phone',
    colSpan: 2,
    dataIndex: 'tel',
    onCell: (_, index) => {
      if (index === 3) {
        return { rowSpan: 2 };
      }
      // These two are merged into above cell
      if (index === 4) {
        return { rowSpan: 0 };
      }
      if (index === 1) {
        return { colSpan: 0 };
      }

      return {};
    },
  },
  {
    title: 'Phone',
    colSpan: 0,
    dataIndex: 'phone',
    onCell: sharedOnCell,
  },
  {
    title: 'Address',
    dataIndex: 'address',
    onCell: sharedOnCell,
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    tel: '0571-22098909',
    phone: 18889898989,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    tel: '0571-22098333',
    phone: 18889898888,
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    tel: '0575-22098909',
    phone: 18900010002,
    address: 'Sydney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 18,
    tel: '0575-22098909',
    phone: 18900010002,
    address: 'London No. 2 Lake Park',
  },
  {
    key: '5',
    name: 'Jake White',
    age: 18,
    tel: '0575-22098909',
    phone: 18900010002,
    address: 'Dublin No. 2 Lake Park',
  },
];

const App: React.FC = () => <Table columns={columns} dataSource={data} bordered />;

export default App;
CheckStrictly:
NameAgeAddress
John Brown sr.60New York No. 1 Lake Park
Joe Black32Sydney No. 1 Lake Park
  • 1
树形数据展示

表格支持树形数据的展示,当数据中有 children 字段时会自动展示为树形表格,如果不需要或配置为其他字段可以用 childrenColumnName 进行配置。

可以通过设置 indentSize 以控制每一层的缩进宽度。

expand codeexpand code
TypeScript
JavaScript
import React, { useState } from 'react';
import { Space, Switch, Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { TableRowSelection } from 'antd/es/table/interface';

interface DataType {
  key: React.ReactNode;
  name: string;
  age: number;
  address: string;
  children?: DataType[];
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
    width: '12%',
  },
  {
    title: 'Address',
    dataIndex: 'address',
    width: '30%',
    key: 'address',
  },
];

const data: DataType[] = [
  {
    key: 1,
    name: 'John Brown sr.',
    age: 60,
    address: 'New York No. 1 Lake Park',
    children: [
      {
        key: 11,
        name: 'John Brown',
        age: 42,
        address: 'New York No. 2 Lake Park',
      },
      {
        key: 12,
        name: 'John Brown jr.',
        age: 30,
        address: 'New York No. 3 Lake Park',
        children: [
          {
            key: 121,
            name: 'Jimmy Brown',
            age: 16,
            address: 'New York No. 3 Lake Park',
          },
        ],
      },
      {
        key: 13,
        name: 'Jim Green sr.',
        age: 72,
        address: 'London No. 1 Lake Park',
        children: [
          {
            key: 131,
            name: 'Jim Green',
            age: 42,
            address: 'London No. 2 Lake Park',
            children: [
              {
                key: 1311,
                name: 'Jim Green jr.',
                age: 25,
                address: 'London No. 3 Lake Park',
              },
              {
                key: 1312,
                name: 'Jimmy Green sr.',
                age: 18,
                address: 'London No. 4 Lake Park',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    key: 2,
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
];

// rowSelection objects indicates the need for row selection
const rowSelection: TableRowSelection<DataType> = {
  onChange: (selectedRowKeys, selectedRows) => {
    console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  },
  onSelect: (record, selected, selectedRows) => {
    console.log(record, selected, selectedRows);
  },
  onSelectAll: (selected, selectedRows, changeRows) => {
    console.log(selected, selectedRows, changeRows);
  },
};

const App: React.FC = () => {
  const [checkStrictly, setCheckStrictly] = useState(false);

  return (
    <>
      <Space align="center" style={{ marginBottom: 16 }}>
        CheckStrictly: <Switch checked={checkStrictly} onChange={setCheckStrictly} />
      </Space>
      <Table
        columns={columns}
        rowSelection={{ ...rowSelection, checkStrictly }}
        dataSource={data}
      />
    </>
  );
};

export default App;
NameAgeAddress
 
 
 
Edward King 032London, Park Lane no. 0
Edward King 132London, Park Lane no. 1
Edward King 232London, Park Lane no. 2
Edward King 332London, Park Lane no. 3
Edward King 432London, Park Lane no. 4
Edward King 532London, Park Lane no. 5
Edward King 632London, Park Lane no. 6
Edward King 732London, Park Lane no. 7
Edward King 832London, Park Lane no. 8
Edward King 932London, Park Lane no. 9
Edward King 1032London, Park Lane no. 10
Edward King 1132London, Park Lane no. 11
Edward King 1232London, Park Lane no. 12
Edward King 1332London, Park Lane no. 13
Edward King 1432London, Park Lane no. 14
Edward King 1532London, Park Lane no. 15
Edward King 1632London, Park Lane no. 16
Edward King 1732London, Park Lane no. 17
Edward King 1832London, Park Lane no. 18
Edward King 1932London, Park Lane no. 19
Edward King 2032London, Park Lane no. 20
Edward King 2132London, Park Lane no. 21
Edward King 2232London, Park Lane no. 22
Edward King 2332London, Park Lane no. 23
Edward King 2432London, Park Lane no. 24
Edward King 2532London, Park Lane no. 25
Edward King 2632London, Park Lane no. 26
Edward King 2732London, Park Lane no. 27
Edward King 2832London, Park Lane no. 28
Edward King 2932London, Park Lane no. 29
Edward King 3032London, Park Lane no. 30
Edward King 3132London, Park Lane no. 31
Edward King 3232London, Park Lane no. 32
Edward King 3332London, Park Lane no. 33
Edward King 3432London, Park Lane no. 34
Edward King 3532London, Park Lane no. 35
Edward King 3632London, Park Lane no. 36
Edward King 3732London, Park Lane no. 37
Edward King 3832London, Park Lane no. 38
Edward King 3932London, Park Lane no. 39
Edward King 4032London, Park Lane no. 40
Edward King 4132London, Park Lane no. 41
Edward King 4232London, Park Lane no. 42
Edward King 4332London, Park Lane no. 43
Edward King 4432London, Park Lane no. 44
Edward King 4532London, Park Lane no. 45
Edward King 4632London, Park Lane no. 46
Edward King 4732London, Park Lane no. 47
Edward King 4832London, Park Lane no. 48
Edward King 4932London, Park Lane no. 49
  • 1
  • 2
  • 50 条/页
固定表头

方便一页内展示大量数据。

需要指定 column 的 width 属性,否则列头和内容可能不对齐。如果指定 width 不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    width: 150,
  },
  {
    title: 'Age',
    dataIndex: 'age',
    width: 150,
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data: DataType[] = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i,
    name: `Edward King ${i}`,
    age: 32,
    address: `London, Park Lane no. ${i}`,
  });
}

const App: React.FC = () => (
  <Table columns={columns} dataSource={data} pagination={{ pageSize: 50 }} scroll={{ y: 240 }} />
);

export default App;
Full Name
Age
Column 1Column 2Column 3Column 4Column 5Column 6Column 7Column 8Action
 
 
 
 
 
 
 
 
 
 
 
John Brown32New York ParkNew York ParkNew York ParkNew York ParkNew York ParkNew York ParkNew York ParkNew York Parkaction
Jim Green40London ParkLondon ParkLondon ParkLondon ParkLondon ParkLondon ParkLondon ParkLondon Parkaction
  • 1
固定列

对于列数很多的数据,可以固定前后的列,横向滚动查看其它数据,需要和 scroll.x 配合使用。

若列头与内容不对齐或出现列重复,请指定固定列的宽度 width。如果指定 width 不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。

建议指定 scroll.x 为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过 scroll.x。

注意:v4 版本固定列通过 sticky 实现,IE 11 会降级成横向滚动。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Full Name',
    width: 100,
    dataIndex: 'name',
    key: 'name',
    fixed: 'left',
  },
  {
    title: 'Age',
    width: 100,
    dataIndex: 'age',
    key: 'age',
    fixed: 'left',
    sorter: true,
  },
  { title: 'Column 1', dataIndex: 'address', key: '1' },
  { title: 'Column 2', dataIndex: 'address', key: '2' },
  { title: 'Column 3', dataIndex: 'address', key: '3' },
  { title: 'Column 4', dataIndex: 'address', key: '4' },
  { title: 'Column 5', dataIndex: 'address', key: '5' },
  { title: 'Column 6', dataIndex: 'address', key: '6' },
  { title: 'Column 7', dataIndex: 'address', key: '7' },
  { title: 'Column 8', dataIndex: 'address', key: '8' },
  {
    title: 'Action',
    key: 'operation',
    fixed: 'right',
    width: 100,
    render: () => <a>action</a>,
  },
];

const data: DataType[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 40,
    address: 'London Park',
  },
];

const App: React.FC = () => <Table columns={columns} dataSource={data} scroll={{ x: 1300 }} />;

export default App;
Full NameAgeColumn 1Column 2Column 3Column 4Column 5Column 6Column 7Column 8Action
 
 
 
 
 
 
 
 
 
 
 
Edward 032London Park no. 0London Park no. 0London Park no. 0London Park no. 0London Park no. 0London Park no. 0London Park no. 0London Park no. 0action
Edward 132London Park no. 1London Park no. 1London Park no. 1London Park no. 1London Park no. 1London Park no. 1London Park no. 1London Park no. 1action
Edward 232London Park no. 2London Park no. 2London Park no. 2London Park no. 2London Park no. 2London Park no. 2London Park no. 2London Park no. 2action
Edward 332London Park no. 3London Park no. 3London Park no. 3London Park no. 3London Park no. 3London Park no. 3London Park no. 3London Park no. 3action
Edward 432London Park no. 4London Park no. 4London Park no. 4London Park no. 4London Park no. 4London Park no. 4London Park no. 4London Park no. 4action
Edward 532London Park no. 5London Park no. 5London Park no. 5London Park no. 5London Park no. 5London Park no. 5London Park no. 5London Park no. 5action
Edward 632London Park no. 6London Park no. 6London Park no. 6London Park no. 6London Park no. 6London Park no. 6London Park no. 6London Park no. 6action
Edward 732London Park no. 7London Park no. 7London Park no. 7London Park no. 7London Park no. 7London Park no. 7London Park no. 7London Park no. 7action
Edward 832London Park no. 8London Park no. 8London Park no. 8London Park no. 8London Park no. 8London Park no. 8London Park no. 8London Park no. 8action
Edward 932London Park no. 9London Park no. 9London Park no. 9London Park no. 9London Park no. 9London Park no. 9London Park no. 9London Park no. 9action
  • 1
  • 2
  • 3
  • 4
  • 5
  • •••
  • 10
  • 10 条/页
固定头和列

适合同时展示有大量数据和数据列。

若列头与内容不对齐或出现列重复,请指定固定列的宽度 width。如果指定 width 不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。

建议指定 scroll.x 为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过 scroll.x。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  address: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Full Name',
    width: 100,
    dataIndex: 'name',
    key: 'name',
    fixed: 'left',
  },
  {
    title: 'Age',
    width: 100,
    dataIndex: 'age',
    key: 'age',
    fixed: 'left',
  },
  {
    title: 'Column 1',
    dataIndex: 'address',
    key: '1',
    width: 150,
  },
  {
    title: 'Column 2',
    dataIndex: 'address',
    key: '2',
    width: 150,
  },
  {
    title: 'Column 3',
    dataIndex: 'address',
    key: '3',
    width: 150,
  },
  {
    title: 'Column 4',
    dataIndex: 'address',
    key: '4',
    width: 150,
  },
  {
    title: 'Column 5',
    dataIndex: 'address',
    key: '5',
    width: 150,
  },
  {
    title: 'Column 6',
    dataIndex: 'address',
    key: '6',
    width: 150,
  },
  {
    title: 'Column 7',
    dataIndex: 'address',
    key: '7',
    width: 150,
  },
  { title: 'Column 8', dataIndex: 'address', key: '8' },
  {
    title: 'Action',
    key: 'operation',
    fixed: 'right',
    width: 100,
    render: () => <a>action</a>,
  },
];

const data: DataType[] = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i,
    name: `Edward ${i}`,
    age: 32,
    address: `London Park no. ${i}`,
  });
}

const App: React.FC = () => (
  <Table columns={columns} dataSource={data} scroll={{ x: 1500, y: 300 }} />
);

export default App;
Name
OtherCompanyGender
Age
AddressCompany AddressCompany Name
StreetBlock
BuildingDoor No.
 
 
 
 
 
 
 
 
John Brown1Lake ParkC2035Lake Street 42SoftLake CoM
John Brown2Lake ParkC2035Lake Street 42SoftLake CoM
John Brown3Lake ParkC2035Lake Street 42SoftLake CoM
John Brown4Lake ParkC2035Lake Street 42SoftLake CoM
John Brown5Lake ParkC2035Lake Street 42SoftLake CoM
John Brown6Lake ParkC2035Lake Street 42SoftLake CoM
John Brown7Lake ParkC2035Lake Street 42SoftLake CoM
John Brown8Lake ParkC2035Lake Street 42SoftLake CoM
John Brown9Lake ParkC2035Lake Street 42SoftLake CoM
John Brown10Lake ParkC2035Lake Street 42SoftLake CoM
  • 1
  • 2
  • 3
  • 4
  • 5
  • •••
  • 10
  • 10 条/页
表头分组

columns[n] 可以内嵌 children,以渲染分组表头。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';

interface DataType {
  key: React.Key;
  name: string;
  age: number;
  street: string;
  building: string;
  number: number;
  companyAddress: string;
  companyName: string;
  gender: string;
}

const columns: ColumnsType<DataType> = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    width: 100,
    fixed: 'left',
    filters: [
      {
        text: 'Joe',
        value: 'Joe',
      },
      {
        text: 'John',
        value: 'John',
      },
    ],
    onFilter: (value: string, record) => record.name.indexOf(value) === 0,
  },
  {
    title: 'Other',
    children: [
      {
        title: 'Age',
        dataIndex: 'age',
        key: 'age',
        width: 150,
        sorter: (a, b) => a.age - b.age,
      },
      {
        title: 'Address',
        children: [
          {
            title: 'Street',
            dataIndex: 'street',
            key: 'street',
            width: 150,
          },
          {
            title: 'Block',
            children: [
              {
                title: 'Building',
                dataIndex: 'building',
                key: 'building',
                width: 100,
              },
              {
                title: 'Door No.',
                dataIndex: 'number',
                key: 'number',
                width: 100,
              },
            ],
          },
        ],
      },
    ],
  },
  {
    title: 'Company',
    children: [
      {
        title: 'Company Address',
        dataIndex: 'companyAddress',
        key: 'companyAddress',
        width: 200,
      },
      {
        title: 'Company Name',
        dataIndex: 'companyName',
        key: 'companyName',
      },
    ],
  },
  {
    title: 'Gender',
    dataIndex: 'gender',
    key: 'gender',
    width: 80,
    fixed: 'right',
  },
];

const data: DataType[] = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i,
    name: 'John Brown',
    age: i + 1,
    street: 'Lake Park',
    building: 'C',
    number: 2035,
    companyAddress: 'Lake Street 42',
    companyName: 'SoftLake Co',
    gender: 'M',
  });
}

const App: React.FC = () => (
  <Table
    columns={columns}
    dataSource={data}
    bordered
    size="middle"
    scroll={{ x: 'calc(700px + 50%)', y: 240 }}
  />
);

export default App;
nameageaddressoperation
Edward King 0
32London, Park Lane no. 0Delete
Edward King 1
32London, Park Lane no. 1Delete
  • 1
可编辑单元格

带单元格编辑功能的表格。当配合 shouldCellUpdate 使用时请注意闭包问题。

expand codeexpand code
TypeScript
JavaScript
import React, { useContext, useEffect, useRef, useState } from 'react';
import type { InputRef } from 'antd';
import { Button, Form, Input, Popconfirm, Table } from 'antd';
import type { FormInstance } from 'antd/es/form';

const EditableContext = React.createContext<FormInstance<any> | null>(null);

interface Item {
  key: string;
  name: string;
  age: string;
  address: string;
}

interface EditableRowProps {
  index: number;
}

const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
  const [form] = Form.useForm();
  return (
    <Form form={form} component={false}>
      <EditableContext.Provider value={form}>
        <tr {...props} />
      </EditableContext.Provider>
    </Form>
  );
};

interface EditableCellProps {
  title: React.ReactNode;
  editable: boolean;
  children: React.ReactNode;
  dataIndex: keyof Item;
  record: Item;
  handleSave: (record: Item) => void;
}

const EditableCell: React.FC<EditableCellProps> = ({
  title,
  editable,
  children,
  dataIndex,
  record,
  handleSave,
  ...restProps
}) => {
  const [editing, setEditing] = useState(false);
  const inputRef = useRef<InputRef>(null);
  const form = useContext(EditableContext)!;

  useEffect(() => {
    if (editing) {
      inputRef.current!.focus();
    }
  }, [editing]);

  const toggleEdit = () => {
    setEditing(!editing);
    form.setFieldsValue({ [dataIndex]: record[dataIndex] });
  };

  const save = async () => {
    try {
      const values = await form.validateFields();

      toggleEdit();
      handleSave({ ...record, ...values });
    } catch (errInfo) {
      console.log('Save failed:', errInfo);
    }
  };

  let childNode = children;

  if (editable) {
    childNode = editing ? (
      <Form.Item
        style={{ margin: 0 }}
        name={dataIndex}
        rules={[
          {
            required: true,
            message: `${title} is required.`,
          },
        ]}
      >
        <Input ref={inputRef} onPressEnter={save} onBlur={save} />
      </Form.Item>
    ) : (
      <div className="editable-cell-value-wrap" style={{ paddingRight: 24 }} onClick={toggleEdit}>
        {children}
      </div>
    );
  }

  return <td {...restProps}>{childNode}</td>;
};

type EditableTableProps = Parameters<typeof Table>[0];

interface DataType {
  key: React.Key;
  name: string;
  age: string;
  address: string;
}

type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;

const App: React.FC = () => {
  const [dataSource, setDataSource] = useState<DataType[]>([
    {
      key: '0',
      name: 'Edward King 0',
      age: '32',
      address: 'London, Park Lane no. 0',
    },
    {
      key: '1',
      name: 'Edward King 1',
      age: '32',
      address: 'London, Park Lane no. 1',
    },
  ]);

  const [count, setCount] = useState(2);

  const handleDelete = (key: React.Key) => {
    const newData = dataSource.filter((item) => item.key !== key);
    setDataSource(newData);
  };

  const defaultColumns: (ColumnTypes[number] & { editable?: boolean; dataIndex: string })[] = [
    {
      title: 'name',
      dataIndex: 'name',
      width: '30%',
      editable: true,
    },
    {
      title: 'age',
      dataIndex: 'age',
    },
    {
      title: 'address',
      dataIndex: 'address',
    },
    {
      title: 'operation',
      dataIndex: 'operation',
      render: (_, record: { key: React.Key }) =>
        dataSource.length >= 1 ? (
          <Popconfirm title="Sure to delete?" onConfirm={() => handleDelete(record.key)}>
            <a>Delete</a>
          </Popconfirm>
        ) : null,
    },
  ];

  const handleAdd = () => {
    const newData: DataType = {
      key: count,
      name: `Edward King ${count}`,
      age: '32',
      address: `London, Park Lane no. ${count}`,
    };
    setDataSource([...dataSource, newData]);
    setCount(count + 1);
  };

  const handleSave = (row: DataType) => {
    const newData = [...dataSource];
    const index = newData.findIndex((item) => row.key === item.key);
    const item = newData[index];
    newData.splice(index, 1, {
      ...item,
      ...row,
    });
    setDataSource(newData);
  };

  const components = {
    body: {
      row: EditableRow,
      cell: EditableCell,
    },
  };

  const columns = defaultColumns.map((col) => {
    if (!col.editable) {
      return col;
    }
    return {
      ...col,
      onCell: (record: DataType) => ({
        record,
        editable: col.editable,
        dataIndex: col.dataIndex,
        title: col.title,
        handleSave,
      }),
    };
  });

  return (
    <div>
      <Button onClick={handleAdd} type="primary" style={{ marginBottom: 16 }}>
        Add a row
      </Button>
      <Table
        components={components}
        rowClassName={() => 'editable-row'}
        bordered
        dataSource={dataSource}
        columns={columns as ColumnTypes}
      />
    </div>
  );
};

export default App;
.editable-cell {
  position: relative;
}

.editable-cell-value-wrap {
  padding: 5px 12px;
  cursor: pointer;
}

.editable-row:hover .editable-cell-value-wrap {
  padding: 4px 11px;
  border: 1px solid #d9d9d9;
  border-radius: 2px;
}
nameageaddressoperation
Edward 032London Park no. 0Edit
Edward 132London Park no. 1Edit
Edward 232London Park no. 2Edit
Edward 332London Park no. 3Edit
Edward 432London Park no. 4Edit
Edward 532London Park no. 5Edit
Edward 632London Park no. 6Edit
Edward 732London Park no. 7Edit
Edward 832London Park no. 8Edit
Edward 932London Park no. 9Edit
  • 1
  • 2
  • 3
  • 4
  • 5
  • •••
  • 10
  • 10 条/页
可编辑行

带行编辑功能的表格。

🛎️ 想要 3 分钟实现?试试 ProTable 的可编辑表格!

expand codeexpand code
TypeScript
JavaScript
import React, { useState } from 'react';
import { Form, Input, InputNumber, Popconfirm, Table, Typography } from 'antd';

interface Item {
  key: string;
  name: string;
  age: number;
  address: string;
}

const originData: Item[] = [];
for (let i = 0; i < 100; i++) {
  originData.push({
    key: i.toString(),
    name: `Edward ${i}`,
    age: 32,
    address: `London Park no. ${i}`,
  });
}
interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
  editing: boolean;
  dataIndex: string;
  title: any;
  inputType: 'number' | 'text';
  record: Item;
  index: number;
  children: React.ReactNode;
}

const EditableCell: React.FC<EditableCellProps> = ({
  editing,
  dataIndex,
  title,
  inputType,
  record,
  index,
  children,
  ...restProps
}) => {
  const inputNode = inputType === 'number' ? <InputNumber /> : <Input />;

  return (
    <td {...restProps}>
      {editing ? (
        <Form.Item
          name={dataIndex}
          style={{ margin: 0 }}
          rules={[
            {
              required: true,
              message: `Please Input ${title}!`,
            },
          ]}
        >
          {inputNode}
        </Form.Item>
      ) : (
        children
      )}
    </td>
  );
};

const App: React.FC = () => {
  const [form] = Form.useForm();
  const [data, setData] = useState(originData);
  const [editingKey, setEditingKey] = useState('');

  const isEditing = (record: Item) => record.key === editingKey;

  const edit = (record: Partial<Item> & { key: React.Key }) => {
    form.setFieldsValue({ name: '', age: '', address: '', ...record });
    setEditingKey(record.key);
  };

  const cancel = () => {
    setEditingKey('');
  };

  const save = async (key: React.Key) => {
    try {
      const row = (await form.validateFields()) as Item;

      const newData = [...data];
      const index = newData.findIndex((item) => key === item.key);
      if (index > -1) {
        const item = newData[index];
        newData.splice(index, 1, {
          ...item,
          ...row,
        });
        setData(newData);
        setEditingKey('');
      } else {
        newData.push(row);
        setData(newData);
        setEditingKey('');
      }
    } catch (errInfo) {
      console.log('Validate Failed:', errInfo);
    }
  };

  const columns = [
    {
      title: 'name',
      dataIndex: 'name',
      width: '25%',
      editable: true,
    },
    {
      title: 'age',
      dataIndex: 'age',
      width: '15%',
      editable: true,
    },
    {
      title: 'address',
      dataIndex: 'address',
      width: '40%',
      editable: true,
    },
    {
      title: 'operation',
      dataIndex: 'operation',
      render: (_: any, record: Item) => {
        const editable = isEditing(record);
        return editable ? (
          <span>
            <Typography.Link onClick={() => save(record.key)} style={{ marginRight: 8 }}>
              Save
            </Typography.Link>
            <Popconfirm title="Sure to cancel?" onConfirm={cancel}>
              <a>Cancel</a>
            </Popconfirm>
          </span>
        ) : (
          <Typography.Link disabled={editingKey !== ''} onClick={() => edit(record)}>
            Edit
          </Typography.Link>
        );
      },
    },
  ];

  const mergedColumns = columns.map((col) => {
    if (!col.editable) {
      return col;
    }
    return {
      ...col,
      onCell: (record: Item) => ({
        record,
        inputType: col.dataIndex === 'age' ? 'number' : 'text',
        dataIndex: col.dataIndex,
        title: col.title,
        editing: isEditing(record),
      }),
    };
  });

  return (
    <Form form={form} component={false}>
      <Table
        components={{
          body: {
            cell: EditableCell,
          },
        }}
        bordered
        dataSource={data}
        columns={mergedColumns}
        rowClassName="editable-row"
        pagination={{
          onChange: cancel,
        }}
      />
    </Form>
  );
};

export default App;
.editable-row .ant-form-item-explain {
  position: absolute;
  top: 100%;
  font-size: 12px;
}
NamePlatformVersionUpgradedCreatorDateAction
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
DateNameStatusUpgrade StatusAction
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
  • 1
NamePlatformVersionUpgradedCreatorDateAction
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
DateNameStatusUpgrade StatusAction
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
  • 1
NamePlatformVersionUpgradedCreatorDateAction
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
DateNameStatusUpgrade StatusAction
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
2014-12-24 23:12:00This is production nameFinishedUpgraded: 56
Pause
Stop
More
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
ScreeniOS10.3.4.5654500Jack2014-12-24 23:12:00Publish
  • 1
嵌套子表格

展示每行数据更详细的信息。

expand codeexpand code
TypeScript
JavaScript
import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { TableColumnsType } from 'antd';
import { Badge, Dropdown, Space, Table } from 'antd';

interface DataType {
  key: React.Key;
  name: string;
  platform: string;
  version: string;
  upgradeNum: number;
  creator: string;
  createdAt: string;
}

interface ExpandedDataType {
  key: React.Key;
  date: string;
  name: string;
  upgradeNum: string;
}

const items = [
  { key: '1', label: 'Action 1' },
  { key: '2', label: 'Action 2' },
];

const App: React.FC = () => {
  const expandedRowRender = () => {
    const columns: TableColumnsType<ExpandedDataType> = [
      { title: 'Date', dataIndex: 'date', key: 'date' },
      { title: 'Name', dataIndex: 'name', key: 'name' },
      {
        title: 'Status',
        key: 'state',
        render: () => <Badge status="success" text="Finished" />,
      },
      { title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
      {
        title: 'Action',
        dataIndex: 'operation',
        key: 'operation',
        render: () => (
          <Space size="middle">
            <a>Pause</a>
            <a>Stop</a>
            <Dropdown menu={{ items }}>
              <a>
                More <DownOutlined />
              </a>
            </Dropdown>
          </Space>
        ),
      },
    ];

    const data = [];
    for (let i = 0; i < 3; ++i) {
      data.push({
        key: i.toString(),
        date: '2014-12-24 23:12:00',
        name: 'This is production name',
        upgradeNum: 'Upgraded: 56',
      });
    }
    return <Table columns={columns} dataSource={data} pagination={false} />;
  };

  const columns: TableColumnsType<DataType> = [
    { title: 'Name', dataIndex: 'name', key: 'name' },
    { title: 'Platform', dataIndex: 'platform', key: 'platform' },
    { title: 'Version', dataIndex: 'version', key: 'version' },
    { title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
    { title: 'Creator', dataIndex: 'creator', key: 'creator' },
    { title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
    { title: 'Action', key: 'operation', render: () => <a>Publish</a> },
  ];

  const data: DataType[] = [];
  for (let i = 0; i < 3; ++i) {
    data.push({
      key: i.toString(),
      name: 'Screen',
      platform: 'iOS',
      version: '10.3.4.5654',
      upgradeNum: 500,
      creator: 'Jack',
      createdAt: '2014-12-24 23:12:00',
    });
  }

  return (
    <>
      <Table
        columns={columns}
        expandable={{ expandedRowRender, defaultExpandedRowKeys: ['0'] }}
        dataSource={data}
      />
      <Table
        columns={columns}
        expandable={{ expandedRowRender, defaultExpandedRowKeys: ['0'] }}
        dataSource={data}
        size="middle"
      />
      <Table
        columns={columns}
        expandable={{ expandedRowRender, defaultExpandedRowKeys: ['0'] }}
        dataSource={data}
        size="small"
      />
    </>
  );
};

export default App;
NameAgeAddress
John Brown32Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text Long text
Jim Green42London No. 1 Lake Park
Joe Black32Sidney No. 1 Lake Park
  • 1
拖拽排序

使用自定义元素,我们可以集成 dnd-kit 来实现拖拽排序。