Table表格

展示行列数据。

设计师专属

安装 Kitchen Sketch 插件 💎,两步就可以自动生成 Ant Design 表格组件。

何时使用

  • 当有大量结构化的数据需要展现时;

  • 当需要对数据进行排序、搜索、分页、自定义操作等复杂行为时。

如何使用

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

  1. const dataSource = [
  2. {
  3. key: '1',
  4. name: '胡彦斌',
  5. age: 32,
  6. address: '西湖区湖底公园1号',
  7. },
  8. {
  9. key: '2',
  10. name: '胡彦祖',
  11. age: 42,
  12. address: '西湖区湖底公园1号',
  13. },
  14. ];
  15. const columns = [
  16. {
  17. title: '姓名',
  18. dataIndex: 'name',
  19. key: 'name',
  20. },
  21. {
  22. title: '年龄',
  23. dataIndex: 'age',
  24. key: 'age',
  25. },
  26. {
  27. title: '住址',
  28. dataIndex: 'address',
  29. key: 'address',
  30. },
  31. ];
  32. <Table dataSource={dataSource} columns={columns} />;

代码演示

Table表格 - 图1

基本用法

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

  1. import { Table, Tag } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. key: 'name',
  7. render: text => <a>{text}</a>,
  8. },
  9. {
  10. title: 'Age',
  11. dataIndex: 'age',
  12. key: 'age',
  13. },
  14. {
  15. title: 'Address',
  16. dataIndex: 'address',
  17. key: 'address',
  18. },
  19. {
  20. title: 'Tags',
  21. key: 'tags',
  22. dataIndex: 'tags',
  23. render: tags => (
  24. <span>
  25. {tags.map(tag => {
  26. let color = tag.length > 5 ? 'geekblue' : 'green';
  27. if (tag === 'loser') {
  28. color = 'volcano';
  29. }
  30. return (
  31. <Tag color={color} key={tag}>
  32. {tag.toUpperCase()}
  33. </Tag>
  34. );
  35. })}
  36. </span>
  37. ),
  38. },
  39. {
  40. title: 'Action',
  41. key: 'action',
  42. render: (text, record) => (
  43. <span>
  44. <a style={{ marginRight: 16 }}>Invite {record.name}</a>
  45. <a>Delete</a>
  46. </span>
  47. ),
  48. },
  49. ];
  50. const data = [
  51. {
  52. key: '1',
  53. name: 'John Brown',
  54. age: 32,
  55. address: 'New York No. 1 Lake Park',
  56. tags: ['nice', 'developer'],
  57. },
  58. {
  59. key: '2',
  60. name: 'Jim Green',
  61. age: 42,
  62. address: 'London No. 1 Lake Park',
  63. tags: ['loser'],
  64. },
  65. {
  66. key: '3',
  67. name: 'Joe Black',
  68. age: 32,
  69. address: 'Sidney No. 1 Lake Park',
  70. tags: ['cool', 'teacher'],
  71. },
  72. ];
  73. ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);

Table表格 - 图2

JSX 风格的 API

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

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

  1. import { Table, Tag } from 'antd';
  2. const { Column, ColumnGroup } = Table;
  3. const data = [
  4. {
  5. key: '1',
  6. firstName: 'John',
  7. lastName: 'Brown',
  8. age: 32,
  9. address: 'New York No. 1 Lake Park',
  10. tags: ['nice', 'developer'],
  11. },
  12. {
  13. key: '2',
  14. firstName: 'Jim',
  15. lastName: 'Green',
  16. age: 42,
  17. address: 'London No. 1 Lake Park',
  18. tags: ['loser'],
  19. },
  20. {
  21. key: '3',
  22. firstName: 'Joe',
  23. lastName: 'Black',
  24. age: 32,
  25. address: 'Sidney No. 1 Lake Park',
  26. tags: ['cool', 'teacher'],
  27. },
  28. ];
  29. ReactDOM.render(
  30. <Table dataSource={data}>
  31. <ColumnGroup title="Name">
  32. <Column title="First Name" dataIndex="firstName" key="firstName" />
  33. <Column title="Last Name" dataIndex="lastName" key="lastName" />
  34. </ColumnGroup>
  35. <Column title="Age" dataIndex="age" key="age" />
  36. <Column title="Address" dataIndex="address" key="address" />
  37. <Column
  38. title="Tags"
  39. dataIndex="tags"
  40. key="tags"
  41. render={tags => (
  42. <span>
  43. {tags.map(tag => (
  44. <Tag color="blue" key={tag}>
  45. {tag}
  46. </Tag>
  47. ))}
  48. </span>
  49. )}
  50. />
  51. <Column
  52. title="Action"
  53. key="action"
  54. render={(text, record) => (
  55. <span>
  56. <a style={{ marginRight: 16 }}>Invite {record.lastName}</a>
  57. <a>Delete</a>
  58. </span>
  59. )}
  60. />
  61. </Table>,
  62. mountNode,
  63. );

Table表格 - 图3

可选择

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

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

TypeScript

JavaScript

  1. import React, { useState } from 'react';
  2. import { Table, Radio, Divider } from 'antd';
  3. const columns = [
  4. {
  5. title: 'Name',
  6. dataIndex: 'name',
  7. render: text => <a>{text}</a>,
  8. },
  9. {
  10. title: 'Age',
  11. dataIndex: 'age',
  12. },
  13. {
  14. title: 'Address',
  15. dataIndex: 'address',
  16. },
  17. ];
  18. const data = [
  19. {
  20. key: '1',
  21. name: 'John Brown',
  22. age: 32,
  23. address: 'New York No. 1 Lake Park',
  24. },
  25. {
  26. key: '2',
  27. name: 'Jim Green',
  28. age: 42,
  29. address: 'London No. 1 Lake Park',
  30. },
  31. {
  32. key: '3',
  33. name: 'Joe Black',
  34. age: 32,
  35. address: 'Sidney No. 1 Lake Park',
  36. },
  37. {
  38. key: '4',
  39. name: 'Disabled User',
  40. age: 99,
  41. address: 'Sidney No. 1 Lake Park',
  42. },
  43. ];
  44. // rowSelection object indicates the need for row selection
  45. const rowSelection = {
  46. onChange: (selectedRowKeys, selectedRows) => {
  47. console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  48. },
  49. getCheckboxProps: record => ({
  50. disabled: record.name === 'Disabled User', // Column configuration not to be checked
  51. name: record.name,
  52. }),
  53. };
  54. const Demo = () => {
  55. const [selectionType, setSelectionType] = useState('checkbox');
  56. return (
  57. <div>
  58. <Radio.Group
  59. onChange={({ target: { value } }) => {
  60. setSelectionType(value);
  61. }}
  62. value={selectionType}
  63. >
  64. <Radio value="checkbox">Checkbox</Radio>
  65. <Radio value="radio">radio</Radio>
  66. </Radio.Group>
  67. <Divider />
  68. <Table
  69. rowSelection={{
  70. type: selectionType,
  71. ...rowSelection,
  72. }}
  73. columns={columns}
  74. dataSource={data}
  75. />
  76. </div>
  77. );
  78. };
  79. ReactDOM.render(<Demo />, mountNode);

Table表格 - 图4

选择和操作

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

  1. import { Table, Button } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. },
  7. {
  8. title: 'Age',
  9. dataIndex: 'age',
  10. },
  11. {
  12. title: 'Address',
  13. dataIndex: 'address',
  14. },
  15. ];
  16. const data = [];
  17. for (let i = 0; i < 46; i++) {
  18. data.push({
  19. key: i,
  20. name: `Edward King ${i}`,
  21. age: 32,
  22. address: `London, Park Lane no. ${i}`,
  23. });
  24. }
  25. class App extends React.Component {
  26. state = {
  27. selectedRowKeys: [], // Check here to configure the default column
  28. loading: false,
  29. };
  30. start = () => {
  31. this.setState({ loading: true });
  32. // ajax request after empty completing
  33. setTimeout(() => {
  34. this.setState({
  35. selectedRowKeys: [],
  36. loading: false,
  37. });
  38. }, 1000);
  39. };
  40. onSelectChange = selectedRowKeys => {
  41. console.log('selectedRowKeys changed: ', selectedRowKeys);
  42. this.setState({ selectedRowKeys });
  43. };
  44. render() {
  45. const { loading, selectedRowKeys } = this.state;
  46. const rowSelection = {
  47. selectedRowKeys,
  48. onChange: this.onSelectChange,
  49. };
  50. const hasSelected = selectedRowKeys.length > 0;
  51. return (
  52. <div>
  53. <div style={{ marginBottom: 16 }}>
  54. <Button type="primary" onClick={this.start} disabled={!hasSelected} loading={loading}>
  55. Reload
  56. </Button>
  57. <span style={{ marginLeft: 8 }}>
  58. {hasSelected ? `Selected ${selectedRowKeys.length} items` : ''}
  59. </span>
  60. </div>
  61. <Table rowSelection={rowSelection} columns={columns} dataSource={data} />
  62. </div>
  63. );
  64. }
  65. }
  66. ReactDOM.render(<App />, mountNode);

Table表格 - 图5

自定义选择项

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. },
  7. {
  8. title: 'Age',
  9. dataIndex: 'age',
  10. },
  11. {
  12. title: 'Address',
  13. dataIndex: 'address',
  14. },
  15. ];
  16. const data = [];
  17. for (let i = 0; i < 46; i++) {
  18. data.push({
  19. key: i,
  20. name: `Edward King ${i}`,
  21. age: 32,
  22. address: `London, Park Lane no. ${i}`,
  23. });
  24. }
  25. class App extends React.Component {
  26. state = {
  27. selectedRowKeys: [], // Check here to configure the default column
  28. };
  29. onSelectChange = selectedRowKeys => {
  30. console.log('selectedRowKeys changed: ', selectedRowKeys);
  31. this.setState({ selectedRowKeys });
  32. };
  33. render() {
  34. const { selectedRowKeys } = this.state;
  35. const rowSelection = {
  36. selectedRowKeys,
  37. onChange: this.onSelectChange,
  38. hideDefaultSelections: true,
  39. selections: [
  40. Table.SELECTION_ALL,
  41. Table.SELECTION_INVERT,
  42. {
  43. key: 'odd',
  44. text: 'Select Odd Row',
  45. onSelect: changableRowKeys => {
  46. let newSelectedRowKeys = [];
  47. newSelectedRowKeys = changableRowKeys.filter((key, index) => {
  48. if (index % 2 !== 0) {
  49. return false;
  50. }
  51. return true;
  52. });
  53. this.setState({ selectedRowKeys: newSelectedRowKeys });
  54. },
  55. },
  56. {
  57. key: 'even',
  58. text: 'Select Even Row',
  59. onSelect: changableRowKeys => {
  60. let newSelectedRowKeys = [];
  61. newSelectedRowKeys = changableRowKeys.filter((key, index) => {
  62. if (index % 2 !== 0) {
  63. return true;
  64. }
  65. return false;
  66. });
  67. this.setState({ selectedRowKeys: newSelectedRowKeys });
  68. },
  69. },
  70. ],
  71. };
  72. return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
  73. }
  74. }
  75. ReactDOM.render(<App />, mountNode);

Table表格 - 图6

筛选和排序

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

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

sortDirections: ['ascend' | 'descend']改变每列可用的排序方式,切换排序时按数组内容依次切换,设置在 table props 上时对所有列生效。

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. filters: [
  7. {
  8. text: 'Joe',
  9. value: 'Joe',
  10. },
  11. {
  12. text: 'Jim',
  13. value: 'Jim',
  14. },
  15. {
  16. text: 'Submenu',
  17. value: 'Submenu',
  18. children: [
  19. {
  20. text: 'Green',
  21. value: 'Green',
  22. },
  23. {
  24. text: 'Black',
  25. value: 'Black',
  26. },
  27. ],
  28. },
  29. ],
  30. // specify the condition of filtering result
  31. // here is that finding the name started with `value`
  32. onFilter: (value, record) => record.name.indexOf(value) === 0,
  33. sorter: (a, b) => a.name.length - b.name.length,
  34. sortDirections: ['descend'],
  35. },
  36. {
  37. title: 'Age',
  38. dataIndex: 'age',
  39. defaultSortOrder: 'descend',
  40. sorter: (a, b) => a.age - b.age,
  41. },
  42. {
  43. title: 'Address',
  44. dataIndex: 'address',
  45. filters: [
  46. {
  47. text: 'London',
  48. value: 'London',
  49. },
  50. {
  51. text: 'New York',
  52. value: 'New York',
  53. },
  54. ],
  55. filterMultiple: false,
  56. onFilter: (value, record) => record.address.indexOf(value) === 0,
  57. sorter: (a, b) => a.address.length - b.address.length,
  58. sortDirections: ['descend', 'ascend'],
  59. },
  60. ];
  61. const data = [
  62. {
  63. key: '1',
  64. name: 'John Brown',
  65. age: 32,
  66. address: 'New York No. 1 Lake Park',
  67. },
  68. {
  69. key: '2',
  70. name: 'Jim Green',
  71. age: 42,
  72. address: 'London No. 1 Lake Park',
  73. },
  74. {
  75. key: '3',
  76. name: 'Joe Black',
  77. age: 32,
  78. address: 'Sidney No. 1 Lake Park',
  79. },
  80. {
  81. key: '4',
  82. name: 'Jim Red',
  83. age: 32,
  84. address: 'London No. 2 Lake Park',
  85. },
  86. ];
  87. function onChange(pagination, filters, sorter, extra) {
  88. console.log('params', pagination, filters, sorter, extra);
  89. }
  90. ReactDOM.render(<Table columns={columns} dataSource={data} onChange={onChange} />, mountNode);

Table表格 - 图7

多列排序

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. },
  7. {
  8. title: 'Chinese Score',
  9. dataIndex: 'chinese',
  10. sorter: {
  11. compare: (a, b) => a.chinese - b.chinese,
  12. multiple: 3,
  13. },
  14. },
  15. {
  16. title: 'Math Score',
  17. dataIndex: 'math',
  18. sorter: {
  19. compare: (a, b) => a.math - b.math,
  20. multiple: 2,
  21. },
  22. },
  23. {
  24. title: 'English Score',
  25. dataIndex: 'english',
  26. sorter: {
  27. compare: (a, b) => a.english - b.english,
  28. multiple: 1,
  29. },
  30. },
  31. ];
  32. const data = [
  33. {
  34. key: '1',
  35. name: 'John Brown',
  36. chinese: 98,
  37. math: 60,
  38. english: 70,
  39. },
  40. {
  41. key: '2',
  42. name: 'Jim Green',
  43. chinese: 98,
  44. math: 66,
  45. english: 89,
  46. },
  47. {
  48. key: '3',
  49. name: 'Joe Black',
  50. chinese: 98,
  51. math: 90,
  52. english: 70,
  53. },
  54. {
  55. key: '4',
  56. name: 'Jim Red',
  57. chinese: 88,
  58. math: 99,
  59. english: 89,
  60. },
  61. ];
  62. function onChange(pagination, filters, sorter, extra) {
  63. console.log('params', pagination, filters, sorter, extra);
  64. }
  65. ReactDOM.render(<Table columns={columns} dataSource={data} onChange={onChange} />, mountNode);

Table表格 - 图8

可控的筛选和排序

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

  1. columns 中定义了 filteredValue 和 sortOrder 属性即视为受控模式。

  2. 只支持同时对一列进行排序,请保证只有一列的 sortOrder 属性是生效的。

  3. 务必指定 column.key

  1. import { Table, Button } from 'antd';
  2. const data = [
  3. {
  4. key: '1',
  5. name: 'John Brown',
  6. age: 32,
  7. address: 'New York No. 1 Lake Park',
  8. },
  9. {
  10. key: '2',
  11. name: 'Jim Green',
  12. age: 42,
  13. address: 'London No. 1 Lake Park',
  14. },
  15. {
  16. key: '3',
  17. name: 'Joe Black',
  18. age: 32,
  19. address: 'Sidney No. 1 Lake Park',
  20. },
  21. {
  22. key: '4',
  23. name: 'Jim Red',
  24. age: 32,
  25. address: 'London No. 2 Lake Park',
  26. },
  27. ];
  28. class App extends React.Component {
  29. state = {
  30. filteredInfo: null,
  31. sortedInfo: null,
  32. };
  33. handleChange = (pagination, filters, sorter) => {
  34. console.log('Various parameters', pagination, filters, sorter);
  35. this.setState({
  36. filteredInfo: filters,
  37. sortedInfo: sorter,
  38. });
  39. };
  40. clearFilters = () => {
  41. this.setState({ filteredInfo: null });
  42. };
  43. clearAll = () => {
  44. this.setState({
  45. filteredInfo: null,
  46. sortedInfo: null,
  47. });
  48. };
  49. setAgeSort = () => {
  50. this.setState({
  51. sortedInfo: {
  52. order: 'descend',
  53. columnKey: 'age',
  54. },
  55. });
  56. };
  57. render() {
  58. let { sortedInfo, filteredInfo } = this.state;
  59. sortedInfo = sortedInfo || {};
  60. filteredInfo = filteredInfo || {};
  61. const columns = [
  62. {
  63. title: 'Name',
  64. dataIndex: 'name',
  65. key: 'name',
  66. filters: [
  67. { text: 'Joe', value: 'Joe' },
  68. { text: 'Jim', value: 'Jim' },
  69. ],
  70. filteredValue: filteredInfo.name || null,
  71. onFilter: (value, record) => record.name.includes(value),
  72. sorter: (a, b) => a.name.length - b.name.length,
  73. sortOrder: sortedInfo.columnKey === 'name' && sortedInfo.order,
  74. ellipsis: true,
  75. },
  76. {
  77. title: 'Age',
  78. dataIndex: 'age',
  79. key: 'age',
  80. sorter: (a, b) => a.age - b.age,
  81. sortOrder: sortedInfo.columnKey === 'age' && sortedInfo.order,
  82. ellipsis: true,
  83. },
  84. {
  85. title: 'Address',
  86. dataIndex: 'address',
  87. key: 'address',
  88. filters: [
  89. { text: 'London', value: 'London' },
  90. { text: 'New York', value: 'New York' },
  91. ],
  92. filteredValue: filteredInfo.address || null,
  93. onFilter: (value, record) => record.address.includes(value),
  94. sorter: (a, b) => a.address.length - b.address.length,
  95. sortOrder: sortedInfo.columnKey === 'address' && sortedInfo.order,
  96. ellipsis: true,
  97. },
  98. ];
  99. return (
  100. <div>
  101. <div className="table-operations">
  102. <Button onClick={this.setAgeSort}>Sort age</Button>
  103. <Button onClick={this.clearFilters}>Clear filters</Button>
  104. <Button onClick={this.clearAll}>Clear filters and sorters</Button>
  105. </div>
  106. <Table columns={columns} dataSource={data} onChange={this.handleChange} />
  107. </div>
  108. );
  109. }
  110. }
  111. ReactDOM.render(<App />, mountNode);
  1. .table-operations {
  2. margin-bottom: 16px;
  3. }
  4. .table-operations > button {
  5. margin-right: 8px;
  6. }

Table表格 - 图9

自定义筛选菜单

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

  1. import { Table, Input, Button } from 'antd';
  2. import Highlighter from 'react-highlight-words';
  3. import { SearchOutlined } from '@ant-design/icons';
  4. const data = [
  5. {
  6. key: '1',
  7. name: 'John Brown',
  8. age: 32,
  9. address: 'New York No. 1 Lake Park',
  10. },
  11. {
  12. key: '2',
  13. name: 'Joe Black',
  14. age: 42,
  15. address: 'London No. 1 Lake Park',
  16. },
  17. {
  18. key: '3',
  19. name: 'Jim Green',
  20. age: 32,
  21. address: 'Sidney No. 1 Lake Park',
  22. },
  23. {
  24. key: '4',
  25. name: 'Jim Red',
  26. age: 32,
  27. address: 'London No. 2 Lake Park',
  28. },
  29. ];
  30. class App extends React.Component {
  31. state = {
  32. searchText: '',
  33. searchedColumn: '',
  34. };
  35. getColumnSearchProps = dataIndex => ({
  36. filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
  37. <div style={{ padding: 8 }}>
  38. <Input
  39. ref={node => {
  40. this.searchInput = node;
  41. }}
  42. placeholder={`Search ${dataIndex}`}
  43. value={selectedKeys[0]}
  44. onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
  45. onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
  46. style={{ width: 188, marginBottom: 8, display: 'block' }}
  47. />
  48. <Button
  49. type="primary"
  50. onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
  51. icon={<SearchOutlined />}
  52. size="small"
  53. style={{ width: 90, marginRight: 8 }}
  54. >
  55. Search
  56. </Button>
  57. <Button onClick={() => this.handleReset(clearFilters)} size="small" style={{ width: 90 }}>
  58. Reset
  59. </Button>
  60. </div>
  61. ),
  62. filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
  63. onFilter: (value, record) =>
  64. record[dataIndex]
  65. .toString()
  66. .toLowerCase()
  67. .includes(value.toLowerCase()),
  68. onFilterDropdownVisibleChange: visible => {
  69. if (visible) {
  70. setTimeout(() => this.searchInput.select());
  71. }
  72. },
  73. render: text =>
  74. this.state.searchedColumn === dataIndex ? (
  75. <Highlighter
  76. highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
  77. searchWords={[this.state.searchText]}
  78. autoEscape
  79. textToHighlight={text.toString()}
  80. />
  81. ) : (
  82. text
  83. ),
  84. });
  85. handleSearch = (selectedKeys, confirm, dataIndex) => {
  86. confirm();
  87. this.setState({
  88. searchText: selectedKeys[0],
  89. searchedColumn: dataIndex,
  90. });
  91. };
  92. handleReset = clearFilters => {
  93. clearFilters();
  94. this.setState({ searchText: '' });
  95. };
  96. render() {
  97. const columns = [
  98. {
  99. title: 'Name',
  100. dataIndex: 'name',
  101. key: 'name',
  102. width: '30%',
  103. ...this.getColumnSearchProps('name'),
  104. },
  105. {
  106. title: 'Age',
  107. dataIndex: 'age',
  108. key: 'age',
  109. width: '20%',
  110. ...this.getColumnSearchProps('age'),
  111. },
  112. {
  113. title: 'Address',
  114. dataIndex: 'address',
  115. key: 'address',
  116. ...this.getColumnSearchProps('address'),
  117. },
  118. ];
  119. return <Table columns={columns} dataSource={data} />;
  120. }
  121. }
  122. ReactDOM.render(<App />, mountNode);

Table表格 - 图10

远程加载数据

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

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

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

  1. import { Table } from 'antd';
  2. import reqwest from 'reqwest';
  3. const columns = [
  4. {
  5. title: 'Name',
  6. dataIndex: 'name',
  7. sorter: true,
  8. render: name => `${name.first} ${name.last}`,
  9. width: '20%',
  10. },
  11. {
  12. title: 'Gender',
  13. dataIndex: 'gender',
  14. filters: [
  15. { text: 'Male', value: 'male' },
  16. { text: 'Female', value: 'female' },
  17. ],
  18. width: '20%',
  19. },
  20. {
  21. title: 'Email',
  22. dataIndex: 'email',
  23. },
  24. ];
  25. class App extends React.Component {
  26. state = {
  27. data: [],
  28. pagination: {},
  29. loading: false,
  30. };
  31. componentDidMount() {
  32. this.fetch();
  33. }
  34. handleTableChange = (pagination, filters, sorter) => {
  35. const pager = { ...this.state.pagination };
  36. pager.current = pagination.current;
  37. this.setState({
  38. pagination: pager,
  39. });
  40. this.fetch({
  41. results: pagination.pageSize,
  42. page: pagination.current,
  43. sortField: sorter.field,
  44. sortOrder: sorter.order,
  45. ...filters,
  46. });
  47. };
  48. fetch = (params = {}) => {
  49. console.log('params:', params);
  50. this.setState({ loading: true });
  51. reqwest({
  52. url: 'https://randomuser.me/api',
  53. method: 'get',
  54. data: {
  55. results: 10,
  56. ...params,
  57. },
  58. type: 'json',
  59. }).then(data => {
  60. const pagination = { ...this.state.pagination };
  61. // Read total count from server
  62. // pagination.total = data.totalCount;
  63. pagination.total = 200;
  64. this.setState({
  65. loading: false,
  66. data: data.results,
  67. pagination,
  68. });
  69. });
  70. };
  71. render() {
  72. return (
  73. <Table
  74. columns={columns}
  75. rowKey={record => record.login.uuid}
  76. dataSource={this.state.data}
  77. pagination={this.state.pagination}
  78. loading={this.state.loading}
  79. onChange={this.handleTableChange}
  80. />
  81. );
  82. }
  83. }
  84. ReactDOM.render(<App />, mountNode);

Table表格 - 图11

紧凑型

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. },
  7. {
  8. title: 'Age',
  9. dataIndex: 'age',
  10. },
  11. {
  12. title: 'Address',
  13. dataIndex: 'address',
  14. },
  15. ];
  16. const data = [
  17. {
  18. key: '1',
  19. name: 'John Brown',
  20. age: 32,
  21. address: 'New York No. 1 Lake Park',
  22. },
  23. {
  24. key: '2',
  25. name: 'Jim Green',
  26. age: 42,
  27. address: 'London No. 1 Lake Park',
  28. },
  29. {
  30. key: '3',
  31. name: 'Joe Black',
  32. age: 32,
  33. address: 'Sidney No. 1 Lake Park',
  34. },
  35. ];
  36. ReactDOM.render(
  37. <div>
  38. <h4>Middle size table</h4>
  39. <Table columns={columns} dataSource={data} size="middle" />
  40. <h4>Small size table</h4>
  41. <Table columns={columns} dataSource={data} size="small" />
  42. </div>,
  43. mountNode,
  44. );

Table表格 - 图12

带边框

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. render: text => <a>{text}</a>,
  7. },
  8. {
  9. title: 'Cash Assets',
  10. className: 'column-money',
  11. dataIndex: 'money',
  12. },
  13. {
  14. title: 'Address',
  15. dataIndex: 'address',
  16. },
  17. ];
  18. const data = [
  19. {
  20. key: '1',
  21. name: 'John Brown',
  22. money: '¥300,000.00',
  23. address: 'New York No. 1 Lake Park',
  24. },
  25. {
  26. key: '2',
  27. name: 'Jim Green',
  28. money: '¥1,256,000.00',
  29. address: 'London No. 1 Lake Park',
  30. },
  31. {
  32. key: '3',
  33. name: 'Joe Black',
  34. money: '¥120,000.00',
  35. address: 'Sidney No. 1 Lake Park',
  36. },
  37. ];
  38. ReactDOM.render(
  39. <Table
  40. columns={columns}
  41. dataSource={data}
  42. bordered
  43. title={() => 'Header'}
  44. footer={() => 'Footer'}
  45. />,
  46. mountNode,
  47. );
  1. th.column-money,
  2. td.column-money {
  3. text-align: right !important;
  4. }

Table表格 - 图13

可展开

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. { title: 'Name', dataIndex: 'name', key: 'name' },
  4. { title: 'Age', dataIndex: 'age', key: 'age' },
  5. { title: 'Address', dataIndex: 'address', key: 'address' },
  6. {
  7. title: 'Action',
  8. dataIndex: '',
  9. key: 'x',
  10. render: () => <a>Delete</a>,
  11. },
  12. ];
  13. const data = [
  14. {
  15. key: 1,
  16. name: 'John Brown',
  17. age: 32,
  18. address: 'New York No. 1 Lake Park',
  19. description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
  20. },
  21. {
  22. key: 2,
  23. name: 'Jim Green',
  24. age: 42,
  25. address: 'London No. 1 Lake Park',
  26. description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
  27. },
  28. {
  29. key: 3,
  30. name: 'Not Expandable',
  31. age: 29,
  32. address: 'Jiangsu No. 1 Lake Park',
  33. description: 'This not expandable',
  34. },
  35. {
  36. key: 4,
  37. name: 'Joe Black',
  38. age: 32,
  39. address: 'Sidney No. 1 Lake Park',
  40. description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
  41. },
  42. ];
  43. ReactDOM.render(
  44. <Table
  45. columns={columns}
  46. expandable={{
  47. expandedRowRender: record => <p style={{ margin: 0 }}>{record.description}</p>,
  48. rowExpandable: record => record.name !== 'Not Expandable',
  49. }}
  50. dataSource={data}
  51. />,
  52. mountNode,
  53. );

Table表格 - 图14

表格行/列合并

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

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

  1. import { Table } from 'antd';
  2. // In the fifth row, other columns are merged into first column
  3. // by setting it's colSpan to be 0
  4. const renderContent = (value, row, index) => {
  5. const obj = {
  6. children: value,
  7. props: {},
  8. };
  9. if (index === 4) {
  10. obj.props.colSpan = 0;
  11. }
  12. return obj;
  13. };
  14. const columns = [
  15. {
  16. title: 'Name',
  17. dataIndex: 'name',
  18. render: (text, row, index) => {
  19. if (index < 4) {
  20. return <a>{text}</a>;
  21. }
  22. return {
  23. children: <a>{text}</a>,
  24. props: {
  25. colSpan: 5,
  26. },
  27. };
  28. },
  29. },
  30. {
  31. title: 'Age',
  32. dataIndex: 'age',
  33. render: renderContent,
  34. },
  35. {
  36. title: 'Home phone',
  37. colSpan: 2,
  38. dataIndex: 'tel',
  39. render: (value, row, index) => {
  40. const obj = {
  41. children: value,
  42. props: {},
  43. };
  44. if (index === 2) {
  45. obj.props.rowSpan = 2;
  46. }
  47. // These two are merged into above cell
  48. if (index === 3) {
  49. obj.props.rowSpan = 0;
  50. }
  51. if (index === 4) {
  52. obj.props.colSpan = 0;
  53. }
  54. return obj;
  55. },
  56. },
  57. {
  58. title: 'Phone',
  59. colSpan: 0,
  60. dataIndex: 'phone',
  61. render: renderContent,
  62. },
  63. {
  64. title: 'Address',
  65. dataIndex: 'address',
  66. render: renderContent,
  67. },
  68. ];
  69. const data = [
  70. {
  71. key: '1',
  72. name: 'John Brown',
  73. age: 32,
  74. tel: '0571-22098909',
  75. phone: 18889898989,
  76. address: 'New York No. 1 Lake Park',
  77. },
  78. {
  79. key: '2',
  80. name: 'Jim Green',
  81. tel: '0571-22098333',
  82. phone: 18889898888,
  83. age: 42,
  84. address: 'London No. 1 Lake Park',
  85. },
  86. {
  87. key: '3',
  88. name: 'Joe Black',
  89. age: 32,
  90. tel: '0575-22098909',
  91. phone: 18900010002,
  92. address: 'Sidney No. 1 Lake Park',
  93. },
  94. {
  95. key: '4',
  96. name: 'Jim Red',
  97. age: 18,
  98. tel: '0575-22098909',
  99. phone: 18900010002,
  100. address: 'London No. 2 Lake Park',
  101. },
  102. {
  103. key: '5',
  104. name: 'Jake White',
  105. age: 18,
  106. tel: '0575-22098909',
  107. phone: 18900010002,
  108. address: 'Dublin No. 2 Lake Park',
  109. },
  110. ];
  111. ReactDOM.render(<Table columns={columns} dataSource={data} bordered />, mountNode);

Table表格 - 图15

树形数据展示

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

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

注:暂不支持父子数据递归关联选择。

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. key: 'name',
  7. },
  8. {
  9. title: 'Age',
  10. dataIndex: 'age',
  11. key: 'age',
  12. width: '12%',
  13. },
  14. {
  15. title: 'Address',
  16. dataIndex: 'address',
  17. width: '30%',
  18. key: 'address',
  19. },
  20. ];
  21. const data = [
  22. {
  23. key: 1,
  24. name: 'John Brown sr.',
  25. age: 60,
  26. address: 'New York No. 1 Lake Park',
  27. children: [
  28. {
  29. key: 11,
  30. name: 'John Brown',
  31. age: 42,
  32. address: 'New York No. 2 Lake Park',
  33. },
  34. {
  35. key: 12,
  36. name: 'John Brown jr.',
  37. age: 30,
  38. address: 'New York No. 3 Lake Park',
  39. children: [
  40. {
  41. key: 121,
  42. name: 'Jimmy Brown',
  43. age: 16,
  44. address: 'New York No. 3 Lake Park',
  45. },
  46. ],
  47. },
  48. {
  49. key: 13,
  50. name: 'Jim Green sr.',
  51. age: 72,
  52. address: 'London No. 1 Lake Park',
  53. children: [
  54. {
  55. key: 131,
  56. name: 'Jim Green',
  57. age: 42,
  58. address: 'London No. 2 Lake Park',
  59. children: [
  60. {
  61. key: 1311,
  62. name: 'Jim Green jr.',
  63. age: 25,
  64. address: 'London No. 3 Lake Park',
  65. },
  66. {
  67. key: 1312,
  68. name: 'Jimmy Green sr.',
  69. age: 18,
  70. address: 'London No. 4 Lake Park',
  71. },
  72. ],
  73. },
  74. ],
  75. },
  76. ],
  77. },
  78. {
  79. key: 2,
  80. name: 'Joe Black',
  81. age: 32,
  82. address: 'Sidney No. 1 Lake Park',
  83. },
  84. ];
  85. // rowSelection objects indicates the need for row selection
  86. const rowSelection = {
  87. onChange: (selectedRowKeys, selectedRows) => {
  88. console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  89. },
  90. onSelect: (record, selected, selectedRows) => {
  91. console.log(record, selected, selectedRows);
  92. },
  93. onSelectAll: (selected, selectedRows, changeRows) => {
  94. console.log(selected, selectedRows, changeRows);
  95. },
  96. };
  97. ReactDOM.render(
  98. <Table columns={columns} rowSelection={rowSelection} dataSource={data} />,
  99. mountNode,
  100. );

Table表格 - 图16

固定表头

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

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. width: 150,
  7. },
  8. {
  9. title: 'Age',
  10. dataIndex: 'age',
  11. width: 150,
  12. },
  13. {
  14. title: 'Address',
  15. dataIndex: 'address',
  16. },
  17. ];
  18. const data = [];
  19. for (let i = 0; i < 100; i++) {
  20. data.push({
  21. key: i,
  22. name: `Edward King ${i}`,
  23. age: 32,
  24. address: `London, Park Lane no. ${i}`,
  25. });
  26. }
  27. ReactDOM.render(
  28. <Table columns={columns} dataSource={data} pagination={{ pageSize: 50 }} scroll={{ y: 240 }} />,
  29. mountNode,
  30. );

Table表格 - 图17

固定列

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

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

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

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Full Name',
  5. width: 100,
  6. dataIndex: 'name',
  7. key: 'name',
  8. fixed: 'left',
  9. },
  10. {
  11. title: 'Age',
  12. width: 100,
  13. dataIndex: 'age',
  14. key: 'age',
  15. fixed: 'left',
  16. },
  17. { title: 'Column 1', dataIndex: 'address', key: '1' },
  18. { title: 'Column 2', dataIndex: 'address', key: '2' },
  19. { title: 'Column 3', dataIndex: 'address', key: '3' },
  20. { title: 'Column 4', dataIndex: 'address', key: '4' },
  21. { title: 'Column 5', dataIndex: 'address', key: '5' },
  22. { title: 'Column 6', dataIndex: 'address', key: '6' },
  23. { title: 'Column 7', dataIndex: 'address', key: '7' },
  24. { title: 'Column 8', dataIndex: 'address', key: '8' },
  25. {
  26. title: 'Action',
  27. key: 'operation',
  28. fixed: 'right',
  29. width: 100,
  30. render: () => <a>action</a>,
  31. },
  32. ];
  33. const data = [
  34. {
  35. key: '1',
  36. name: 'John Brown',
  37. age: 32,
  38. address: 'New York Park',
  39. },
  40. {
  41. key: '2',
  42. name: 'Jim Green',
  43. age: 40,
  44. address: 'London Park',
  45. },
  46. ];
  47. ReactDOM.render(<Table columns={columns} dataSource={data} scroll={{ x: 1300 }} />, mountNode);

Table表格 - 图18

固定头和列

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

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

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Full Name',
  5. width: 100,
  6. dataIndex: 'name',
  7. key: 'name',
  8. fixed: 'left',
  9. },
  10. {
  11. title: 'Age',
  12. width: 100,
  13. dataIndex: 'age',
  14. key: 'age',
  15. fixed: 'left',
  16. },
  17. {
  18. title: 'Column 1',
  19. dataIndex: 'address',
  20. key: '1',
  21. width: 150,
  22. },
  23. {
  24. title: 'Column 2',
  25. dataIndex: 'address',
  26. key: '2',
  27. width: 150,
  28. },
  29. {
  30. title: 'Column 3',
  31. dataIndex: 'address',
  32. key: '3',
  33. width: 150,
  34. },
  35. {
  36. title: 'Column 4',
  37. dataIndex: 'address',
  38. key: '4',
  39. width: 150,
  40. },
  41. {
  42. title: 'Column 5',
  43. dataIndex: 'address',
  44. key: '5',
  45. width: 150,
  46. },
  47. {
  48. title: 'Column 6',
  49. dataIndex: 'address',
  50. key: '6',
  51. width: 150,
  52. },
  53. {
  54. title: 'Column 7',
  55. dataIndex: 'address',
  56. key: '7',
  57. width: 150,
  58. },
  59. { title: 'Column 8', dataIndex: 'address', key: '8' },
  60. {
  61. title: 'Action',
  62. key: 'operation',
  63. fixed: 'right',
  64. width: 100,
  65. render: () => <a>action</a>,
  66. },
  67. ];
  68. const data = [];
  69. for (let i = 0; i < 100; i++) {
  70. data.push({
  71. key: i,
  72. name: `Edrward ${i}`,
  73. age: 32,
  74. address: `London Park no. ${i}`,
  75. });
  76. }
  77. ReactDOM.render(
  78. <Table columns={columns} dataSource={data} scroll={{ x: 1500, y: 300 }} />,
  79. mountNode,
  80. );

Table表格 - 图19

表头分组

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

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. key: 'name',
  7. width: 100,
  8. fixed: 'left',
  9. filters: [
  10. {
  11. text: 'Joe',
  12. value: 'Joe',
  13. },
  14. {
  15. text: 'John',
  16. value: 'John',
  17. },
  18. ],
  19. onFilter: (value, record) => record.name.indexOf(value) === 0,
  20. },
  21. {
  22. title: 'Other',
  23. children: [
  24. {
  25. title: 'Age',
  26. dataIndex: 'age',
  27. key: 'age',
  28. width: 150,
  29. sorter: (a, b) => a.age - b.age,
  30. },
  31. {
  32. title: 'Address',
  33. children: [
  34. {
  35. title: 'Street',
  36. dataIndex: 'street',
  37. key: 'street',
  38. width: 150,
  39. },
  40. {
  41. title: 'Block',
  42. children: [
  43. {
  44. title: 'Building',
  45. dataIndex: 'building',
  46. key: 'building',
  47. width: 100,
  48. },
  49. {
  50. title: 'Door No.',
  51. dataIndex: 'number',
  52. key: 'number',
  53. width: 100,
  54. },
  55. ],
  56. },
  57. ],
  58. },
  59. ],
  60. },
  61. {
  62. title: 'Company',
  63. children: [
  64. {
  65. title: 'Company Address',
  66. dataIndex: 'companyAddress',
  67. key: 'companyAddress',
  68. width: 200,
  69. },
  70. {
  71. title: 'Company Name',
  72. dataIndex: 'companyName',
  73. key: 'companyName',
  74. },
  75. ],
  76. },
  77. {
  78. title: 'Gender',
  79. dataIndex: 'gender',
  80. key: 'gender',
  81. width: 80,
  82. fixed: 'right',
  83. },
  84. ];
  85. const data = [];
  86. for (let i = 0; i < 100; i++) {
  87. data.push({
  88. key: i,
  89. name: 'John Brown',
  90. age: i + 1,
  91. street: 'Lake Park',
  92. building: 'C',
  93. number: 2035,
  94. companyAddress: 'Lake Street 42',
  95. companyName: 'SoftLake Co',
  96. gender: 'M',
  97. });
  98. }
  99. ReactDOM.render(
  100. <Table
  101. columns={columns}
  102. dataSource={data}
  103. bordered
  104. size="middle"
  105. scroll={{ x: 'calc(700px + 50%)', y: 240 }}
  106. />,
  107. mountNode,
  108. );

Table表格 - 图20

可编辑单元格

带单元格编辑功能的表格。

TypeScript

JavaScript

  1. import React, { useContext, useState, useEffect, useRef } from 'react';
  2. import { Table, Input, Button, Popconfirm, Form } from 'antd';
  3. const EditableContext = React.createContext<any>();
  4. interface Item {
  5. key: string;
  6. name: string;
  7. age: string;
  8. address: string;
  9. }
  10. interface EditableRowProps {
  11. index: number;
  12. }
  13. const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
  14. const [form] = Form.useForm();
  15. return (
  16. <Form form={form} component={false}>
  17. <EditableContext.Provider value={form}>
  18. <tr {...props} />
  19. </EditableContext.Provider>
  20. </Form>
  21. );
  22. };
  23. interface EditableCellProps {
  24. title: React.ReactNode;
  25. editable: boolean;
  26. children: React.ReactNode;
  27. dataIndex: string;
  28. record: Item;
  29. handleSave: (record: Item) => void;
  30. }
  31. const EditableCell: React.FC<EditableCellProps> = ({
  32. title,
  33. editable,
  34. children,
  35. dataIndex,
  36. record,
  37. handleSave,
  38. ...restProps
  39. }) => {
  40. const [editing, setEditing] = useState(false);
  41. const inputRef = useRef();
  42. const form = useContext(EditableContext);
  43. useEffect(() => {
  44. if (editing) {
  45. inputRef.current.focus();
  46. }
  47. }, [editing]);
  48. const toggleEdit = () => {
  49. setEditing(!editing);
  50. form.setFieldsValue({ [dataIndex]: record[dataIndex] });
  51. };
  52. const save = async e => {
  53. try {
  54. const values = await form.validateFields();
  55. toggleEdit();
  56. handleSave({ ...record, ...values });
  57. } catch (errInfo) {
  58. console.log('Save failed:', errInfo);
  59. }
  60. };
  61. let childNode = children;
  62. if (editable) {
  63. childNode = editing ? (
  64. <Form.Item
  65. style={{ margin: 0 }}
  66. name={dataIndex}
  67. rules={[
  68. {
  69. required: true,
  70. message: `${title} is required.`,
  71. },
  72. ]}
  73. >
  74. <Input ref={inputRef} onPressEnter={save} onBlur={save} />
  75. </Form.Item>
  76. ) : (
  77. <div className="editable-cell-value-wrap" style={{ paddingRight: 24 }} onClick={toggleEdit}>
  78. {children}
  79. </div>
  80. );
  81. }
  82. return <td {...restProps}>{childNode}</td>;
  83. };
  84. class EditableTable extends React.Component {
  85. constructor(props) {
  86. super(props);
  87. this.columns = [
  88. {
  89. title: 'name',
  90. dataIndex: 'name',
  91. width: '30%',
  92. editable: true,
  93. },
  94. {
  95. title: 'age',
  96. dataIndex: 'age',
  97. },
  98. {
  99. title: 'address',
  100. dataIndex: 'address',
  101. },
  102. {
  103. title: 'operation',
  104. dataIndex: 'operation',
  105. render: (text, record) =>
  106. this.state.dataSource.length >= 1 ? (
  107. <Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
  108. <a>Delete</a>
  109. </Popconfirm>
  110. ) : null,
  111. },
  112. ];
  113. this.state = {
  114. dataSource: [
  115. {
  116. key: '0',
  117. name: 'Edward King 0',
  118. age: '32',
  119. address: 'London, Park Lane no. 0',
  120. },
  121. {
  122. key: '1',
  123. name: 'Edward King 1',
  124. age: '32',
  125. address: 'London, Park Lane no. 1',
  126. },
  127. ],
  128. count: 2,
  129. };
  130. }
  131. handleDelete = key => {
  132. const dataSource = [...this.state.dataSource];
  133. this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
  134. };
  135. handleAdd = () => {
  136. const { count, dataSource } = this.state;
  137. const newData = {
  138. key: count,
  139. name: `Edward King ${count}`,
  140. age: 32,
  141. address: `London, Park Lane no. ${count}`,
  142. };
  143. this.setState({
  144. dataSource: [...dataSource, newData],
  145. count: count + 1,
  146. });
  147. };
  148. handleSave = row => {
  149. const newData = [...this.state.dataSource];
  150. const index = newData.findIndex(item => row.key === item.key);
  151. const item = newData[index];
  152. newData.splice(index, 1, {
  153. ...item,
  154. ...row,
  155. });
  156. this.setState({ dataSource: newData });
  157. };
  158. render() {
  159. const { dataSource } = this.state;
  160. const components = {
  161. body: {
  162. row: EditableRow,
  163. cell: EditableCell,
  164. },
  165. };
  166. const columns = this.columns.map(col => {
  167. if (!col.editable) {
  168. return col;
  169. }
  170. return {
  171. ...col,
  172. onCell: record => ({
  173. record,
  174. editable: col.editable,
  175. dataIndex: col.dataIndex,
  176. title: col.title,
  177. handleSave: this.handleSave,
  178. }),
  179. };
  180. });
  181. return (
  182. <div>
  183. <Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
  184. Add a row
  185. </Button>
  186. <Table
  187. components={components}
  188. rowClassName={() => 'editable-row'}
  189. bordered
  190. dataSource={dataSource}
  191. columns={columns}
  192. />
  193. </div>
  194. );
  195. }
  196. }
  197. ReactDOM.render(<EditableTable />, mountNode);
  1. .editable-cell {
  2. position: relative;
  3. }
  4. .editable-cell-value-wrap {
  5. padding: 5px 12px;
  6. cursor: pointer;
  7. }
  8. .editable-row:hover .editable-cell-value-wrap {
  9. border: 1px solid #d9d9d9;
  10. border-radius: 4px;
  11. padding: 4px 11px;
  12. }
  13. [data-theme='dark'] .editable-row:hover .editable-cell-value-wrap {
  14. border: 1px solid #434343;
  15. }

Table表格 - 图21

可编辑行

带行编辑功能的表格。

TypeScript

JavaScript

  1. import React, { useState } from 'react';
  2. import { Table, Input, InputNumber, Popconfirm, Form } from 'antd';
  3. interface Item {
  4. key: string;
  5. name: string;
  6. age: number;
  7. address: string;
  8. }
  9. const originData: Item[] = [];
  10. for (let i = 0; i < 100; i++) {
  11. originData.push({
  12. key: i.toString(),
  13. name: `Edrward ${i}`,
  14. age: 32,
  15. address: `London Park no. ${i}`,
  16. });
  17. }
  18. interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
  19. editing: boolean;
  20. dataIndex: string;
  21. title: any;
  22. inputType: 'number' | 'text';
  23. record: Item;
  24. index: number;
  25. children: React.ReactNode;
  26. }
  27. const EditableCell: React.FC<EditableCellProps> = ({
  28. editing,
  29. dataIndex,
  30. title,
  31. inputType,
  32. record,
  33. index,
  34. children,
  35. ...restProps
  36. }) => {
  37. const inputNode = inputType === 'number' ? <InputNumber /> : <Input />;
  38. return (
  39. <td {...restProps}>
  40. {editing ? (
  41. <Form.Item
  42. name={dataIndex}
  43. style={{ margin: 0 }}
  44. rules={[
  45. {
  46. required: true,
  47. message: `Please Input ${title}!`,
  48. },
  49. ]}
  50. >
  51. {inputNode}
  52. </Form.Item>
  53. ) : (
  54. children
  55. )}
  56. </td>
  57. );
  58. };
  59. const EditableTable = () => {
  60. const [form] = Form.useForm();
  61. const [data, setData] = useState(originData);
  62. const [editingKey, setEditingKey] = useState('');
  63. const isEditing = (record: Item) => record.key === editingKey;
  64. const edit = (record: Item) => {
  65. form.setFieldsValue({ ...record });
  66. setEditingKey(record.key);
  67. };
  68. const cancel = () => {
  69. setEditingKey('');
  70. };
  71. const save = async (key: React.Key) => {
  72. try {
  73. const row = (await form.validateFields()) as Item;
  74. const newData = [...data];
  75. const index = newData.findIndex(item => key === item.key);
  76. if (index > -1) {
  77. const item = newData[index];
  78. newData.splice(index, 1, {
  79. ...item,
  80. ...row,
  81. });
  82. setData(newData);
  83. setEditingKey('');
  84. } else {
  85. newData.push(row);
  86. setData(newData);
  87. setEditingKey('');
  88. }
  89. } catch (errInfo) {
  90. console.log('Validate Failed:', errInfo);
  91. }
  92. };
  93. const columns = [
  94. {
  95. title: 'name',
  96. dataIndex: 'name',
  97. width: '25%',
  98. editable: true,
  99. },
  100. {
  101. title: 'age',
  102. dataIndex: 'age',
  103. width: '15%',
  104. editable: true,
  105. },
  106. {
  107. title: 'address',
  108. dataIndex: 'address',
  109. width: '40%',
  110. editable: true,
  111. },
  112. {
  113. title: 'operation',
  114. dataIndex: 'operation',
  115. render: (_: any, record: Item) => {
  116. const editable = isEditing(record);
  117. return editable ? (
  118. <span>
  119. <a href="javascript:;" onClick={() => save(record.key)} style={{ marginRight: 8 }}>
  120. Save
  121. </a>
  122. <Popconfirm title="Sure to cancel?" onConfirm={cancel}>
  123. <a>Cancel</a>
  124. </Popconfirm>
  125. </span>
  126. ) : (
  127. <a disabled={editingKey !== ''} onClick={() => edit(record)}>
  128. Edit
  129. </a>
  130. );
  131. },
  132. },
  133. ];
  134. const mergedColumns = columns.map(col => {
  135. if (!col.editable) {
  136. return col;
  137. }
  138. return {
  139. ...col,
  140. onCell: (record: Item) => ({
  141. record,
  142. inputType: col.dataIndex === 'age' ? 'number' : 'text',
  143. dataIndex: col.dataIndex,
  144. title: col.title,
  145. editing: isEditing(record),
  146. }),
  147. };
  148. });
  149. return (
  150. <Form form={form} component={false}>
  151. <Table
  152. components={{
  153. body: {
  154. cell: EditableCell,
  155. },
  156. }}
  157. bordered
  158. dataSource={data}
  159. columns={mergedColumns}
  160. rowClassName="editable-row"
  161. pagination={{
  162. onChange: cancel,
  163. }}
  164. />
  165. </Form>
  166. );
  167. };
  168. ReactDOM.render(<EditableTable />, mountNode);
  1. .editable-row .ant-form-explain {
  2. position: absolute;
  3. font-size: 12px;
  4. margin-top: -4px;
  5. }

Table表格 - 图22

嵌套子表格

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

  1. import { Table, Badge, Menu, Dropdown } from 'antd';
  2. import { DownOutlined } from '@ant-design/icons';
  3. const menu = (
  4. <Menu>
  5. <Menu.Item>Action 1</Menu.Item>
  6. <Menu.Item>Action 2</Menu.Item>
  7. </Menu>
  8. );
  9. function NestedTable() {
  10. const expandedRowRender = () => {
  11. const columns = [
  12. { title: 'Date', dataIndex: 'date', key: 'date' },
  13. { title: 'Name', dataIndex: 'name', key: 'name' },
  14. {
  15. title: 'Status',
  16. key: 'state',
  17. render: () => (
  18. <span>
  19. <Badge status="success" />
  20. Finished
  21. </span>
  22. ),
  23. },
  24. { title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
  25. {
  26. title: 'Action',
  27. dataIndex: 'operation',
  28. key: 'operation',
  29. render: () => (
  30. <span className="table-operation">
  31. <a>Pause</a>
  32. <a>Stop</a>
  33. <Dropdown overlay={menu}>
  34. <a>
  35. More <DownOutlined />
  36. </a>
  37. </Dropdown>
  38. </span>
  39. ),
  40. },
  41. ];
  42. const data = [];
  43. for (let i = 0; i < 3; ++i) {
  44. data.push({
  45. key: i,
  46. date: '2014-12-24 23:12:00',
  47. name: 'This is production name',
  48. upgradeNum: 'Upgraded: 56',
  49. });
  50. }
  51. return <Table columns={columns} dataSource={data} pagination={false} />;
  52. };
  53. const columns = [
  54. { title: 'Name', dataIndex: 'name', key: 'name' },
  55. { title: 'Platform', dataIndex: 'platform', key: 'platform' },
  56. { title: 'Version', dataIndex: 'version', key: 'version' },
  57. { title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
  58. { title: 'Creator', dataIndex: 'creator', key: 'creator' },
  59. { title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
  60. { title: 'Action', key: 'operation', render: () => <a>Publish</a> },
  61. ];
  62. const data = [];
  63. for (let i = 0; i < 3; ++i) {
  64. data.push({
  65. key: i,
  66. name: 'Screem',
  67. platform: 'iOS',
  68. version: '10.3.4.5654',
  69. upgradeNum: 500,
  70. creator: 'Jack',
  71. createdAt: '2014-12-24 23:12:00',
  72. });
  73. }
  74. return (
  75. <Table
  76. className="components-table-demo-nested"
  77. columns={columns}
  78. expandable={{ expandedRowRender }}
  79. dataSource={data}
  80. />
  81. );
  82. }
  83. ReactDOM.render(<NestedTable />, mountNode);

Table表格 - 图23

拖拽排序

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

  1. import { Table } from 'antd';
  2. import { DndProvider, useDrag, useDrop } from 'react-dnd';
  3. import HTML5Backend from 'react-dnd-html5-backend';
  4. import update from 'immutability-helper';
  5. const type = 'DragbleBodyRow';
  6. const DragableBodyRow = ({ index, moveRow, className, style, ...restProps }) => {
  7. const ref = React.useRef();
  8. const [{ isOver, dropClassName }, drop] = useDrop({
  9. accept: type,
  10. collect: monitor => {
  11. const { index: dragIndex } = monitor.getItem() || {};
  12. if (dragIndex === index) {
  13. return {};
  14. }
  15. return {
  16. isOver: monitor.isOver(),
  17. dropClassName: dragIndex < index ? ' drop-over-downward' : ' drop-over-upward',
  18. };
  19. },
  20. drop: item => {
  21. moveRow(item.index, index);
  22. },
  23. });
  24. const [, drag] = useDrag({
  25. item: { type, index },
  26. collect: monitor => ({
  27. isDragging: monitor.isDragging(),
  28. }),
  29. });
  30. drop(drag(ref));
  31. return (
  32. <tr
  33. ref={ref}
  34. className={`${className}${isOver ? dropClassName : ''}`}
  35. style={{ cursor: 'move', ...style }}
  36. {...restProps}
  37. />
  38. );
  39. };
  40. const columns = [
  41. {
  42. title: 'Name',
  43. dataIndex: 'name',
  44. key: 'name',
  45. },
  46. {
  47. title: 'Age',
  48. dataIndex: 'age',
  49. key: 'age',
  50. },
  51. {
  52. title: 'Address',
  53. dataIndex: 'address',
  54. key: 'address',
  55. },
  56. ];
  57. class DragSortingTable extends React.Component {
  58. state = {
  59. data: [
  60. {
  61. key: '1',
  62. name: 'John Brown',
  63. age: 32,
  64. address: 'New York No. 1 Lake Park',
  65. },
  66. {
  67. key: '2',
  68. name: 'Jim Green',
  69. age: 42,
  70. address: 'London No. 1 Lake Park',
  71. },
  72. {
  73. key: '3',
  74. name: 'Joe Black',
  75. age: 32,
  76. address: 'Sidney No. 1 Lake Park',
  77. },
  78. ],
  79. };
  80. components = {
  81. body: {
  82. row: DragableBodyRow,
  83. },
  84. };
  85. moveRow = (dragIndex, hoverIndex) => {
  86. const { data } = this.state;
  87. const dragRow = data[dragIndex];
  88. this.setState(
  89. update(this.state, {
  90. data: {
  91. $splice: [
  92. [dragIndex, 1],
  93. [hoverIndex, 0, dragRow],
  94. ],
  95. },
  96. }),
  97. );
  98. };
  99. render() {
  100. return (
  101. <DndProvider backend={HTML5Backend}>
  102. <Table
  103. columns={columns}
  104. dataSource={this.state.data}
  105. components={this.components}
  106. onRow={(record, index) => ({
  107. index,
  108. moveRow: this.moveRow,
  109. })}
  110. />
  111. </DndProvider>
  112. );
  113. }
  114. }
  115. ReactDOM.render(<DragSortingTable />, mountNode);
  1. #components-table-demo-drag-sorting tr.drop-over-downward td {
  2. border-bottom: 2px dashed #1890ff;
  3. }
  4. #components-table-demo-drag-sorting tr.drop-over-upward td {
  5. border-top: 2px dashed #1890ff;
  6. }

Table表格 - 图24

可伸缩列

集成 react-resizable 来实现可伸缩列。

  1. import { Table } from 'antd';
  2. import { Resizable } from 'react-resizable';
  3. const ResizeableTitle = props => {
  4. const { onResize, width, ...restProps } = props;
  5. if (!width) {
  6. return <th {...restProps} />;
  7. }
  8. return (
  9. <Resizable
  10. width={width}
  11. height={0}
  12. handle={resizeHandle => (
  13. <span
  14. className={`react-resizable-handle react-resizable-handle-${resizeHandle}`}
  15. onClick={e => {
  16. e.stopPropagation();
  17. }}
  18. />
  19. )}
  20. onResize={onResize}
  21. draggableOpts={{ enableUserSelectHack: false }}
  22. >
  23. <th {...restProps} />
  24. </Resizable>
  25. );
  26. };
  27. class Demo extends React.Component {
  28. state = {
  29. columns: [
  30. {
  31. title: 'Date',
  32. dataIndex: 'date',
  33. width: 200,
  34. },
  35. {
  36. title: 'Amount',
  37. dataIndex: 'amount',
  38. width: 100,
  39. sorter: (a, b) => a.amount - b.amount,
  40. },
  41. {
  42. title: 'Type',
  43. dataIndex: 'type',
  44. width: 100,
  45. },
  46. {
  47. title: 'Note',
  48. dataIndex: 'note',
  49. width: 100,
  50. },
  51. {
  52. title: 'Action',
  53. key: 'action',
  54. render: () => <a>Delete</a>,
  55. },
  56. ],
  57. };
  58. components = {
  59. header: {
  60. cell: ResizeableTitle,
  61. },
  62. };
  63. data = [
  64. {
  65. key: 0,
  66. date: '2018-02-11',
  67. amount: 120,
  68. type: 'income',
  69. note: 'transfer',
  70. },
  71. {
  72. key: 1,
  73. date: '2018-03-11',
  74. amount: 243,
  75. type: 'income',
  76. note: 'transfer',
  77. },
  78. {
  79. key: 2,
  80. date: '2018-04-11',
  81. amount: 98,
  82. type: 'income',
  83. note: 'transfer',
  84. },
  85. ];
  86. handleResize = index => (e, { size }) => {
  87. this.setState(({ columns }) => {
  88. const nextColumns = [...columns];
  89. nextColumns[index] = {
  90. ...nextColumns[index],
  91. width: size.width,
  92. };
  93. return { columns: nextColumns };
  94. });
  95. };
  96. render() {
  97. const columns = this.state.columns.map((col, index) => ({
  98. ...col,
  99. onHeaderCell: column => ({
  100. width: column.width,
  101. onResize: this.handleResize(index),
  102. }),
  103. }));
  104. return <Table bordered components={this.components} columns={columns} dataSource={this.data} />;
  105. }
  106. }
  107. ReactDOM.render(<Demo />, mountNode);
  1. #components-table-demo-resizable-column .react-resizable {
  2. position: relative;
  3. background-clip: padding-box;
  4. }
  5. #components-table-demo-resizable-column .react-resizable-handle {
  6. position: absolute;
  7. width: 10px;
  8. height: 100%;
  9. bottom: 0;
  10. right: -5px;
  11. cursor: col-resize;
  12. z-index: 1;
  13. }

Table表格 - 图25

单元格自动省略

设置 column.ellipsis 可以让单元格内容根据宽度自动省略。

列头缩略暂不支持和排序筛选一起使用。

  1. import { Table } from 'antd';
  2. const columns = [
  3. {
  4. title: 'Name',
  5. dataIndex: 'name',
  6. key: 'name',
  7. render: text => <a>{text}</a>,
  8. width: 150,
  9. },
  10. {
  11. title: 'Age',
  12. dataIndex: 'age',
  13. key: 'age',
  14. width: 80,
  15. },
  16. {
  17. title: 'Address',
  18. dataIndex: 'address',
  19. key: 'address 1',
  20. ellipsis: true,
  21. },
  22. {
  23. title: 'Long Column Long Column Long Column',
  24. dataIndex: 'address',
  25. key: 'address 2',
  26. ellipsis: true,
  27. },
  28. {
  29. title: 'Long Column Long Column',
  30. dataIndex: 'address',
  31. key: 'address 3',
  32. ellipsis: true,
  33. },
  34. {
  35. title: 'Long Column',
  36. dataIndex: 'address',
  37. key: 'address 4',
  38. ellipsis: true,
  39. },
  40. ];
  41. const data = [
  42. {
  43. key: '1',
  44. name: 'John Brown',
  45. age: 32,
  46. address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
  47. tags: ['nice', 'developer'],
  48. },
  49. {
  50. key: '2',
  51. name: 'Jim Green',
  52. age: 42,
  53. address: 'London No. 2 Lake Park, London No. 2 Lake Park',
  54. tags: ['loser'],
  55. },
  56. {
  57. key: '3',
  58. name: 'Joe Black',
  59. age: 32,
  60. address: 'Sidney No. 1 Lake Park, Sidney No. 1 Lake Park',
  61. tags: ['cool', 'teacher'],
  62. },
  63. ];
  64. ReactDOM.render(<Table columns={columns} dataSource={data} />, mountNode);

Table表格 - 图26

总结栏

通过 summary 设置总结栏。

  1. import { Table, Typography } from 'antd';
  2. const { Text } = Typography;
  3. const columns = [
  4. {
  5. title: 'Name',
  6. dataIndex: 'name',
  7. },
  8. {
  9. title: 'Borrow',
  10. dataIndex: 'borrow',
  11. },
  12. {
  13. title: 'Repayment',
  14. dataIndex: 'repayment',
  15. },
  16. ];
  17. const data = [
  18. {
  19. key: '1',
  20. name: 'John Brown',
  21. borrow: 10,
  22. repayment: 33,
  23. },
  24. {
  25. key: '2',
  26. name: 'Jim Green',
  27. borrow: 100,
  28. repayment: 0,
  29. },
  30. {
  31. key: '3',
  32. name: 'Joe Black',
  33. borrow: 10,
  34. repayment: 10,
  35. },
  36. {
  37. key: '4',
  38. name: 'Jim Red',
  39. borrow: 75,
  40. repayment: 45,
  41. },
  42. ];
  43. ReactDOM.render(
  44. <Table
  45. columns={columns}
  46. dataSource={data}
  47. pagination={false}
  48. bordered
  49. summary={pageData => {
  50. let totalBorrow = 0;
  51. let totalRepayment = 0;
  52. pageData.forEach(({ borrow, repayment }) => {
  53. totalBorrow += borrow;
  54. totalRepayment += repayment;
  55. });
  56. return (
  57. <>
  58. <tr>
  59. <th>Total</th>
  60. <td>
  61. <Text type="danger">{totalBorrow}</Text>
  62. </td>
  63. <td>
  64. <Text>{totalRepayment}</Text>
  65. </td>
  66. </tr>
  67. <tr>
  68. <th>Balance</th>
  69. <td colSpan={2}>
  70. <Text type="danger">{totalBorrow - totalRepayment}</Text>
  71. </td>
  72. </tr>
  73. </>
  74. );
  75. }}
  76. />,
  77. mountNode,
  78. );

Table表格 - 图27

虚拟列表

通过 react-window 引入虚拟滚动方案,实现 100000 条数据的高性能表格。

TypeScript

JavaScript

  1. import React, { useState, useEffect, useRef } from 'react';
  2. import { VariableSizeGrid as Grid } from 'react-window';
  3. import ResizeObserver from 'rc-resize-observer';
  4. import classNames from 'classnames';
  5. import { Table } from 'antd';
  6. function VirtualTable(props) {
  7. const { columns, scroll, className } = props;
  8. const [tableWidth, setTableWidth] = useState(0);
  9. const widthColumnCount = columns.filter(({ width }) => !width).length;
  10. const mergedColumns = columns.map(column => {
  11. if (column.width) {
  12. return column;
  13. }
  14. return {
  15. ...column,
  16. width: Math.floor(tableWidth / widthColumnCount),
  17. };
  18. });
  19. const gridRef = useRef<any>();
  20. const [connectObject] = useState<any>(() => {
  21. const obj = {};
  22. Object.defineProperty(obj, 'scrollLeft', {
  23. get: () => null,
  24. set: (scrollLeft: number) => {
  25. if (gridRef.current) {
  26. gridRef.current.scrollTo({ scrollLeft });
  27. }
  28. },
  29. });
  30. return obj;
  31. });
  32. const resetVirtualGrid = () => {
  33. gridRef.current.resetAfterIndices({
  34. columnIndex: 0,
  35. shouldForceUpdate: false,
  36. });
  37. };
  38. useEffect(() => resetVirtualGrid, []);
  39. useEffect(() => resetVirtualGrid, [tableWidth]);
  40. const renderVirtualList = (rawData: object[], { scrollbarSize, ref, onScroll }: any) => {
  41. ref.current = connectObject;
  42. return (
  43. <Grid
  44. ref={gridRef}
  45. className="virtual-grid"
  46. columnCount={mergedColumns.length}
  47. columnWidth={index => {
  48. const { width } = mergedColumns[index];
  49. return index === mergedColumns.length - 1 ? width - scrollbarSize - 1 : width;
  50. }}
  51. height={scroll.y}
  52. rowCount={rawData.length}
  53. rowHeight={() => 54}
  54. width={tableWidth}
  55. onScroll={({ scrollLeft }) => {
  56. onScroll({ scrollLeft });
  57. }}
  58. >
  59. {({ columnIndex, rowIndex, style }) => (
  60. <div
  61. className={classNames('virtual-table-cell', {
  62. 'virtual-table-cell-last': columnIndex === mergedColumns.length - 1,
  63. })}
  64. style={style}
  65. >
  66. {rawData[rowIndex][mergedColumns[columnIndex].dataIndex]}
  67. </div>
  68. )}
  69. </Grid>
  70. );
  71. };
  72. return (
  73. <ResizeObserver
  74. onResize={({ width }) => {
  75. setTableWidth(width);
  76. }}
  77. >
  78. <Table
  79. {...props}
  80. className={classNames(className, 'virtual-table')}
  81. columns={mergedColumns}
  82. pagination={false}
  83. components={{
  84. body: renderVirtualList,
  85. }}
  86. />
  87. </ResizeObserver>
  88. );
  89. }
  90. // Usage
  91. const columns = [
  92. { title: 'A', dataIndex: 'key', width: 150 },
  93. { title: 'B', dataIndex: 'key' },
  94. { title: 'C', dataIndex: 'key' },
  95. { title: 'D', dataIndex: 'key' },
  96. { title: 'E', dataIndex: 'key', width: 200 },
  97. { title: 'F', dataIndex: 'key', width: 100 },
  98. ];
  99. const data = [];
  100. for (let i = 0; i < 100000; i += 1) {
  101. data.push({
  102. key: i,
  103. });
  104. }
  105. ReactDOM.render(
  106. <VirtualTable columns={columns} dataSource={data} scroll={{ y: 300, x: '100vw' }} />,
  107. mountNode,
  108. );

Table表格 - 图28

分页设置

表格的分页设置。

  1. import { Table, Tag, Radio } from 'antd';
  2. const topOptions = [
  3. { label: 'topLeft', value: 'topLeft' },
  4. { label: 'topCenter', value: 'topCenter' },
  5. { label: 'topRight', value: 'topRight' },
  6. { label: 'none', value: 'none' },
  7. ];
  8. const bottomOptions = [
  9. { label: 'bottomLeft', value: 'bottomLeft' },
  10. { label: 'bottomCenter', value: 'bottomCenter' },
  11. { label: 'bottomRight', value: 'bottomRight' },
  12. { label: 'none', value: 'none' },
  13. ];
  14. const columns = [
  15. {
  16. title: 'Name',
  17. dataIndex: 'name',
  18. key: 'name',
  19. render: (text) => <a>{text}</a>,
  20. },
  21. {
  22. title: 'Age',
  23. dataIndex: 'age',
  24. key: 'age',
  25. },
  26. {
  27. title: 'Address',
  28. dataIndex: 'address',
  29. key: 'address',
  30. },
  31. {
  32. title: 'Tags',
  33. key: 'tags',
  34. dataIndex: 'tags',
  35. render: (tags) => (
  36. <span>
  37. {tags.map((tag) => {
  38. let color = tag.length > 5 ? 'geekblue' : 'green';
  39. if (tag === 'loser') {
  40. color = 'volcano';
  41. }
  42. return (
  43. <Tag color={color} key={tag}>
  44. {tag.toUpperCase()}
  45. </Tag>
  46. );
  47. })}
  48. </span>
  49. ),
  50. },
  51. {
  52. title: 'Action',
  53. key: 'action',
  54. render: (text, record) => (
  55. <span>
  56. <a style={{ marginRight: 16 }}>Invite {record.name}</a>
  57. <a>Delete</a>
  58. </span>
  59. ),
  60. },
  61. ];
  62. const data = [
  63. {
  64. key: '1',
  65. name: 'John Brown',
  66. age: 32,
  67. address: 'New York No. 1 Lake Park',
  68. tags: ['nice', 'developer'],
  69. },
  70. {
  71. key: '2',
  72. name: 'Jim Green',
  73. age: 42,
  74. address: 'London No. 1 Lake Park',
  75. tags: ['loser'],
  76. },
  77. {
  78. key: '3',
  79. name: 'Joe Black',
  80. age: 32,
  81. address: 'Sidney No. 1 Lake Park',
  82. tags: ['cool', 'teacher'],
  83. },
  84. ];
  85. class Demo extends React.Component {
  86. state = {
  87. top: 'topLeft',
  88. bottom: 'bottomRight',
  89. };
  90. render() {
  91. return (
  92. <div>
  93. <div>
  94. <Radio.Group
  95. style={{ marginBottom: 10 }}
  96. options={topOptions}
  97. value={this.state.top}
  98. onChange={(e) => {
  99. this.setState({ top: e.target.value });
  100. }}
  101. />
  102. </div>
  103. <Radio.Group
  104. style={{ marginBottom: 10 }}
  105. options={bottomOptions}
  106. value={this.state.bottom}
  107. onChange={(e) => {
  108. this.setState({ bottom: e.target.value });
  109. }}
  110. />
  111. <Table
  112. columns={columns}
  113. pagination={{ position: [this.state.top, this.state.bottom] }}
  114. dataSource={data}
  115. />
  116. </div>
  117. );
  118. }
  119. }
  120. ReactDOM.render(<Demo />, mountNode);

Table表格 - 图29

动态控制表格属性

选择不同配置组合查看效果。

  1. import { Table, Switch, Radio, Form } from 'antd';
  2. import { DownOutlined } from '@ant-design/icons';
  3. const columns = [
  4. {
  5. title: 'Name',
  6. dataIndex: 'name',
  7. key: 'name',
  8. render: text => <a>{text}</a>,
  9. },
  10. {
  11. title: 'Age',
  12. dataIndex: 'age',
  13. key: 'age',
  14. },
  15. {
  16. title: 'Address',
  17. dataIndex: 'address',
  18. key: 'address',
  19. },
  20. {
  21. title: 'Action',
  22. key: 'action',
  23. render: () => (
  24. <span>
  25. <a style={{ marginRight: 16 }}>Delete</a>
  26. <a className="ant-dropdown-link">
  27. More actions <DownOutlined />
  28. </a>
  29. </span>
  30. ),
  31. },
  32. ];
  33. const data = [];
  34. for (let i = 1; i <= 10; i++) {
  35. data.push({
  36. key: i,
  37. name: 'John Brown',
  38. age: `${i}2`,
  39. address: `New York No. ${i} Lake Park`,
  40. description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`,
  41. });
  42. }
  43. const expandable = { expandedRowRender: record => <p>{record.description}</p> };
  44. const title = () => 'Here is title';
  45. const showHeader = true;
  46. const footer = () => 'Here is footer';
  47. const pagination = { position: 'bottom' };
  48. class Demo extends React.Component {
  49. state = {
  50. bordered: false,
  51. loading: false,
  52. pagination,
  53. size: 'default',
  54. expandable,
  55. title: undefined,
  56. showHeader,
  57. footer,
  58. rowSelection: {},
  59. scroll: undefined,
  60. hasData: true,
  61. tableLayout: undefined,
  62. };
  63. handleToggle = prop => enable => {
  64. this.setState({ [prop]: enable });
  65. };
  66. handleSizeChange = e => {
  67. this.setState({ size: e.target.value });
  68. };
  69. handleTableLayoutChange = e => {
  70. this.setState({ tableLayout: e.target.value });
  71. };
  72. handleExpandChange = enable => {
  73. this.setState({ expandable: enable ? expandable : undefined });
  74. };
  75. handleEllipsisChange = enable => {
  76. this.setState({ ellipsis: enable });
  77. };
  78. handleTitleChange = enable => {
  79. this.setState({ title: enable ? title : undefined });
  80. };
  81. handleHeaderChange = enable => {
  82. this.setState({ showHeader: enable ? showHeader : false });
  83. };
  84. handleFooterChange = enable => {
  85. this.setState({ footer: enable ? footer : undefined });
  86. };
  87. handleRowSelectionChange = enable => {
  88. this.setState({ rowSelection: enable ? {} : undefined });
  89. };
  90. handleYScrollChange = enable => {
  91. this.setState({ yScroll: enable });
  92. };
  93. handleXScrollChange = e => {
  94. this.setState({ xScroll: e.target.value });
  95. };
  96. handleDataChange = hasData => {
  97. this.setState({ hasData });
  98. };
  99. handlePaginationChange = e => {
  100. const { value } = e.target;
  101. this.setState({
  102. pagination: value === 'none' ? false : { position: value },
  103. });
  104. };
  105. render() {
  106. const { xScroll, yScroll, ...state } = this.state;
  107. const scroll = {};
  108. if (yScroll) {
  109. scroll.y = 240;
  110. }
  111. if (xScroll) {
  112. scroll.x = '100vw';
  113. }
  114. const tableColumns = columns.map(item => ({ ...item, ellipsis: state.ellipsis }));
  115. if (xScroll === 'fixed') {
  116. tableColumns[0].fixed = true;
  117. tableColumns[tableColumns.length - 1].fixed = 'right';
  118. }
  119. return (
  120. <div>
  121. <Form
  122. layout="inline"
  123. className="components-table-demo-control-bar"
  124. style={{ marginBottom: 16 }}
  125. >
  126. <Form.Item label="Bordered">
  127. <Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
  128. </Form.Item>
  129. <Form.Item label="loading">
  130. <Switch checked={state.loading} onChange={this.handleToggle('loading')} />
  131. </Form.Item>
  132. <Form.Item label="Title">
  133. <Switch checked={!!state.title} onChange={this.handleTitleChange} />
  134. </Form.Item>
  135. <Form.Item label="Column Header">
  136. <Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
  137. </Form.Item>
  138. <Form.Item label="Footer">
  139. <Switch checked={!!state.footer} onChange={this.handleFooterChange} />
  140. </Form.Item>
  141. <Form.Item label="Expandable">
  142. <Switch checked={!!state.expandable} onChange={this.handleExpandChange} />
  143. </Form.Item>
  144. <Form.Item label="Checkbox">
  145. <Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
  146. </Form.Item>
  147. <Form.Item label="Fixed Header">
  148. <Switch checked={!!yScroll} onChange={this.handleYScrollChange} />
  149. </Form.Item>
  150. <Form.Item label="Has Data">
  151. <Switch checked={!!state.hasData} onChange={this.handleDataChange} />
  152. </Form.Item>
  153. <Form.Item label="Ellipsis">
  154. <Switch checked={!!state.ellipsis} onChange={this.handleEllipsisChange} />
  155. </Form.Item>
  156. <Form.Item label="Size">
  157. <Radio.Group value={state.size} onChange={this.handleSizeChange}>
  158. <Radio.Button value="default">Default</Radio.Button>
  159. <Radio.Button value="middle">Middle</Radio.Button>
  160. <Radio.Button value="small">Small</Radio.Button>
  161. </Radio.Group>
  162. </Form.Item>
  163. <Form.Item label="Table Scroll">
  164. <Radio.Group value={xScroll} onChange={this.handleXScrollChange}>
  165. <Radio.Button value={undefined}>Unset</Radio.Button>
  166. <Radio.Button value="scroll">Scroll</Radio.Button>
  167. <Radio.Button value="fixed">Fixed Columns</Radio.Button>
  168. </Radio.Group>
  169. </Form.Item>
  170. <Form.Item label="Table Layout">
  171. <Radio.Group value={state.tableLayout} onChange={this.handleTableLayoutChange}>
  172. <Radio.Button value={undefined}>Unset</Radio.Button>
  173. <Radio.Button value="fixed">Fixed</Radio.Button>
  174. </Radio.Group>
  175. </Form.Item>
  176. <Form.Item label="Pagination">
  177. <Radio.Group
  178. value={state.pagination ? state.pagination.position : 'none'}
  179. onChange={this.handlePaginationChange}
  180. >
  181. <Radio.Button value="top">Top</Radio.Button>
  182. <Radio.Button value="bottom">Bottom</Radio.Button>
  183. <Radio.Button value="both">Both</Radio.Button>
  184. <Radio.Button value="none">None</Radio.Button>
  185. </Radio.Group>
  186. </Form.Item>
  187. </Form>
  188. <Table
  189. {...this.state}
  190. columns={tableColumns}
  191. dataSource={state.hasData ? data : null}
  192. scroll={scroll}
  193. />
  194. </div>
  195. );
  196. }
  197. }
  198. ReactDOM.render(<Demo />, mountNode);

API

Table

参数说明类型默认值
tableLayout表格元素的 table-layout 属性,设为 fixed 表示内容不会影响列的布局- | auto | fixed
- - -
固定表头/列或使用了 column.ellipsis 时,默认值为 fixed
bordered是否展示外边框和列边框booleanfalse
columns表格列的配置描述,具体项见下表ColumnProps[]-
components覆盖默认的 table 元素TableComponents-
dataSource数据数组any[]-
expandable配置展开属性expandable-
footer表格尾部Function(currentPageData)-
loading页面是否加载中boolean|object (更多)false
locale默认文案设置,目前包括排序、过滤、空数据文案objectfilterConfirm: '确定' filterReset: '重置' emptyText: '暂无数据' 默认值
pagination分页器,参考配置项pagination 文档,设为 false 时不展示和进行分页object-
rowClassName表格行的类名Function(record, index):string-
rowKey表格行 key 的取值,可以是字符串或一个函数string|Function(record):string'key'
rowSelection表格行是否可选择,配置项object-
scroll表格是否可滚动,也可以指定滚动区域的宽、高,配置项object-
showHeader是否显示表头booleantrue
size表格大小default | middle | smalldefault
summary总结栏(currentData) => ReactNode-
title表格标题Function(currentPageData)-
onChange分页、排序、筛选变化时触发Function(pagination, filters, sorter, extra: { currentDataSource: [] })-
onHeaderRow设置头部行属性Function(column, index)-
onRow设置行属性Function(record, index)-
getPopupContainer设置表格内各类浮层的渲染节点,如筛选菜单(triggerNode) => HTMLElement() => TableHtmlElement
sortDirections支持的排序方式,取值为 'ascend' 'descend'Array['ascend', 'descend']
showSorterTooltip表头是否显示下一次排序的 tooltip 提示booleantrue

onRow 用法

适用于 onRow onHeaderRow onCell onHeaderCell

  1. <Table
  2. onRow={record => {
  3. return {
  4. onClick: event => {}, // 点击行
  5. onDoubleClick: event => {},
  6. onContextMenu: event => {},
  7. onMouseEnter: event => {}, // 鼠标移入行
  8. onMouseLeave: event => {},
  9. };
  10. }}
  11. onHeaderRow={column => {
  12. return {
  13. onClick: () => {}, // 点击表头行
  14. };
  15. }}
  16. />

Column

列描述数据对象,是 columns 中的一项,Column 使用相同的 API。

参数说明类型默认值
align设置列的对齐方式left | right | centerleft
ellipsis超过宽度将自动省略,暂不支持和排序筛选一起使用。设置为 true 时,表格布局将变成 tableLayout="fixed"booleanfalse
className列样式类名string-
colSpan表头列合并,设置为 0 时,不渲染number-
dataIndex列数据在数据项中对应的路径,支持通过数组查询嵌套路径string | string[]-
defaultFilteredValue默认筛选值string[]-
defaultSortOrder默认排序顺序ascend | descend-
filterDropdown可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互React.ReactNode | (props: FilterDropdownProps) => React.ReactNode-
filterDropdownVisible用于控制自定义筛选菜单是否可见boolean-
filtered标识数据是否经过过滤,筛选图标会高亮booleanfalse
filteredValue筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组string[]-
filterIcon自定义 filter 图标。ReactNode|(filtered: boolean) => ReactNodefalse
filterMultiple是否多选booleantrue
filters表头的筛选菜单项object[]-
fixed(IE 下无效)列是否固定,可选 true(等效于 left) 'left' 'right'boolean|stringfalse
keyReact 需要的 key,如果已经设置了唯一的 dataIndex,可以忽略这个属性string-
render生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格行/列合并Function(text, record, index) {}-
sorter排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 trueFunction|boolean-
sortOrder排序的受控属性,外界可用此控制列的排序,可设置为 'ascend' 'descend' falseboolean|string-
sortDirections支持的排序方式,覆盖TablesortDirections, 取值为 'ascend' 'descend'Array['ascend', 'descend']
title列头显示文字(函数用法 3.10.0 后支持)ReactNode|({ sortOrder, sortColumn, filters }) => ReactNode-
width列宽度(指定了也不生效?string|number-
onCell设置单元格属性Function(record, rowIndex)-
onFilter本地模式下,确定筛选的运行函数Function-
onFilterDropdownVisibleChange自定义筛选菜单可见变化时调用function(visible) {}-
onHeaderCell设置头部单元格属性Function(column)-
showSorterTooltip表头显示下一次排序的 tooltip 提示, 覆盖 table 中showSorterTooltipbooleantrue

ColumnGroup

参数说明类型默认值
title列头显示文字string|ReactNode-

pagination

分页的配置项。

参数说明类型默认值
position指定分页显示的位置, 取值为topLeft | topCenter | topRight |bottomLeft | bottomCenter | bottomRightArray['bottomRight']

更多配置项,请查看 Pagination

expandable

展开功能的配置。

参数说明类型默认值
childrenColumnName指定树形结构的列名string[]children
defaultExpandAllRows初始时,是否展开所有行booleanfalse
defaultExpandedRowKeys默认展开的行string[]-
expandIcon自定义展开图标,参考示例Function(props):ReactNode-
expandIconColumnIndex自定义展开按钮的列顺序number-
expandedRowKeys展开的行,控制属性string[]-
expandedRowRender额外的展开行Function(record, index, indent, expanded):ReactNode-
expandRowByClick通过点击行来展开子行booleanfalse
indentSize展示树形数据时,每层缩进的宽度,以 px 为单位number15
rowExpandable设置是否允许行展开(record) => boolean-
onExpand点击展开图标时触发Function(expanded, record)-
onExpandedRowsChange展开的行变化时触发Function(expandedRows)-

rowSelection

选择功能的配置。

参数说明类型默认值版本
columnWidth自定义列表选择框宽度string|number60px4.0
columnTitle自定义列表选择框标题string|React.ReactNode-4.0
fixed把选择框列固定在左边boolean-4.0
getCheckboxProps选择框的默认属性配置Function(record)-4.0
hideDefaultSelections自定义选择项时去掉『全选』『反选』两个默认选项booleanfalse4.0
renderCell渲染勾选框,用法与 Column 的 render 相同Function(checked, record, index, originNode) {}-4.1
selectedRowKeys指定选中项的 key 数组,需要和 onChange 进行配合string[]|number[][]4.0
selections自定义选择项 配置项, 设为 true 时使用默认选择项object[]|booleantrue4.0
type多选/单选,checkbox or radiostringcheckbox4.0
onChange选中项发生变化时的回调Function(selectedRowKeys, selectedRows)-4.0
onSelect用户手动选择/取消选择某行的回调Function(record, selected, selectedRows, nativeEvent)-4.0
onSelectAll用户手动选择/取消选择所有行的回调Function(selected, selectedRows, changeRows)-4.0
onSelectInvert用户手动选择反选的回调Function(selectedRowKeys)-4.0

scroll

参数说明类型默认值
x设置横向滚动,也可用于指定滚动区域的宽,可以设置为像素值,百分比,true 和 'max-content'number | true-
y设置纵向滚动,也可用于指定滚动区域的高,可以设置为像素值number-
scrollToFirstRowOnChange当分页、排序、筛选变化后是否滚动到表格顶部boolean-

selection

参数说明类型默认值
keyReact 需要的 key,建议设置string-
text选择项显示的文字string|React.ReactNode-
onSelect选择项点击回调Function(changeableRowKeys)-

在 TypeScript 中使用

import { Table } from 'antd';
import { ColumnProps } from 'antd/es/table';

interface User {
  key: number;
  name: string;
}

const columns: ColumnProps<User>[] = [{
  key: 'name',
  title: 'Name',
  dataIndex: 'name',
}];

const data: User[] = [{
  key: 0,
  name: 'Jack',
}];

class UserTable extends Table<User> {}
<UserTable columns={columns} dataSource={data} />

// 使用 JSX 风格的 API
class NameColumn extends Table.Column<User> {}

<UserTable dataSource={data}>
  <NameColumn key="name" title="Name" dataIndex="name" />
</UserTable>

// TypeScript 2.9 之后也可以这样写
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements
<Table<User> columns={columns} dataSource={data} />
<Table<User> dataSource={data}>
  <Table.Column<User> key="name" title="Name" dataIndex="name" />
</Table>

注意

按照 React 的规范,所有的数组组件必须绑定 key。在 Table 中,dataSourcecolumns 里的数据值都需要指定 key 值。对于 dataSource 默认将每列数据的 key 属性作为唯一的标识。

控制台警告

如果 dataSource[i].key 没有提供,你应该使用 rowKey 来指定 dataSource 的主键,如下所示。若没有指定,控制台会出现以上的提示,表格组件也会出现各类奇怪的错误。

// 比如你的数据主键是 uid
return <Table rowKey="uid" />;
// 或
return <Table rowKey={record => record.uid} />;

从 v3 升级到 v4

Table 移除了在 v3 中废弃的 onRowClickonRowDoubleClickonRowMouseEnteronRowMouseLeave 等方法。如果你使用的 api 为文档中列举的 api,那你不用担心会丢失功能。

此外,比较重大的改动为 dataIndex 从支持路径嵌套如 user.age 改成了数组路径如 ['user', 'age']。以解决过去属性名带 . 需要额外的数据转化问题。

FAQ

如何在没有数据或只有一页数据时隐藏分页栏

你可以设置 paginationhideOnSinglePage 属性为 true

表格过滤时会回到第一页?

 前端过滤时通常条目总数会减少,从而导致总页数小于筛选前的当前页数,为了防止当前页面没有数据,我们默认会返回第一页。

如果你在使用远程分页,很可能需要保持当前页面,你可以参照这个 受控例子 控制当前页面不变。