Table 表格

展示行列数据。

何时使用#

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

如何使用#

指定表格的数据源 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} />

注:dataSource0.11.0 版本后不再支持远程模式。

API#

Table#

参数 说明 类型 可选值 默认值
rowSelection 列表项是否可选择 Object 配置项 null
pagination 分页器 Object 配置项参考 pagination,设为 false 时不显示分页
size 正常或迷你类型 String default or small default
dataSource 数据数组 Array
columns 表格列的配置描述,具体项见下表 Array
rowKey 表格列 key 的取值 Function(recode, index):string record.key
expandedRowRender 额外的列展开元素 Function -
defaultExpandedRowKeys 默认展开的列 Array -
onChange 分页、排序、筛选变化时触发 Function(pagination, filters, sorter)
loading 页面是否加载中 Boolean false
locale 设置排序、过滤按钮的文字或 title Object 默认值

Column#

列描述数据对象,是 columns 中的一项。

参数 说明 类型 可选值 默认值
title 列头显示文字 String or React.Element
key React 需要的 key,建议设置 String
dataIndex 列数据在数据项中对应的 key String
render 生成复杂数据的渲染函数,参数分别为当前列的值,当前列数据,列索引,@return里面可以设置表格行/列合并 Function(text, record, index) {}
filters 表头的筛选菜单项 Array
onFilter 本地模式下,确定筛选的运行函数 Function
filterMultiple 是否多选 Boolean true
sorter 排序函数,本地排序使用一个函数,需要服务端排序可设为 true Function or Boolean
colSpan 表头列合并,设置为 0 时,不渲染 Number
indentSize 展示树形数据时,每层缩进的宽度,以 px 为单位 Number 15
width 列宽度 String or Number
className 列的 className String

rowSelection#

选择功能的配置。

参数 说明 类型 默认值
type 多选/单选,checkbox or radio String checkbox
selectedRowKeys 指定选中项的 key 数组,需要和 onChange 进行配合 Array []
onChange 选中项发生变化的时的回调,用户手动点选、换页、更新数据均会触发 Function(selectedRowKeys) -
getCheckboxProps 选择框的默认属性配置 Function(record) -
onSelect 用户手动选择/取消选择某列的回调 Function(record, selected, selectedRows) -
onSelectAll 用户手动选择/取消选择所有列的回调 Function(record, selected, selectedRows) -

注意#

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

如果你的数据没有这个属性,务必使用 rowKey 来指定数据列的主键。若没有指定,控制台会出现以下的提示,表格组件也会出现各类奇怪的错误。

const rowKey = function(record) {
  return record.uid;  // 比如你的数据主键是 uid
};

return <Table rowKey={rowKey} />;

代码演示

import { Table, Icon } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  key: 'name',
  render: function(text) {
    return <a href="#">{text}</a>;
  }
}, {
  title: '年龄',
  dataIndex: 'age',
  key: 'age',
}, {
  title: '住址',
  dataIndex: 'address',
  key: 'address',
}, {
  title: '操作',
  key: 'operation',
  render: function(text, record) {
    return <span>
      <a href="#">操作一{record.name}</a>
      <span className="ant-divider"></span>
      <a href="#">操作二</a>
      <span className="ant-divider"></span>
      <a href="#" className="ant-dropdown-link">
        更多 <Icon type="down" />
      </a>
    </span>;
  }
}];
const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '西湖区湖底公园1号'
}];

ReactDOM.render(<Table columns={columns} dataSource={data} />
, mountNode);

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

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  render: function(text) {
    return <a href="#">{text}</a>;
  }
}, {
  title: '年龄',
  dataIndex: 'age'
}, {
  title: '住址',
  dataIndex: 'address'
}];
const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '西湖区湖底公园1号'
}];

// 通过 rowSelection 对象表明需要行选择
const rowSelection = {
  onChange(selectedRowKeys) {
    console.log('selectedRowKeys changed: ' + selectedRowKeys);
  },
  onSelect: function(record, selected, selectedRows) {
    console.log(record, selected, selectedRows);
  },
  onSelectAll: function(selected, selectedRows) {
    console.log(selected, selectedRows);
  }
};

ReactDOM.render(<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
, mountNode);

第一列是联动的选择框。

import { Table, Button } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
}, {
  title: '年龄',
  dataIndex: 'age',
}, {
  title: '住址',
  dataIndex: 'address',
}];

const data = [];
for (let i = 0; i < 46; i++) {
  data.push({
    key: i,
    name: '李大嘴' + i,
    age: 32,
    address: '西湖区湖底公园' + i + '号'
  });
}

const App = React.createClass({
  getInitialState() {
    return {
      selectedRowKeys: [],
      loading: false,
    };
  },
  start() {
    this.setState({ loading: true });
    // 模拟 ajax 请求,完成后清空
    setTimeout(() => {
      this.setState({
        selectedRowKeys: [],
        loading: false,
      });
    }, 1000);
  },
  onSelectChange(selectedRowKeys) {
    console.log('selectedRowKeys changed: ', selectedRowKeys);
    this.setState({ selectedRowKeys });
  },
  render() {
    const { loading, selectedRowKeys } = this.state;
    const rowSelection = {
      selectedRowKeys,
      onChange: this.onSelectChange,
    };
    const hasSelected = selectedRowKeys.length > 0;
    return <div>
      <div style={{marginBottom: 16}}>
         <Button type="primary" onClick={this.start}
           disabled={!hasSelected} loading={loading}>操作</Button>
         <span style={{marginLeft: 8}}>{hasSelected ? `选择了 ${selectedRowKeys.length} 个对象` : ''}</span>
      </div>
      <Table rowSelection={rowSelection} columns={columns} dataSource={data} />
    </div>;
  }
});

ReactDOM.render(<App />, mountNode);

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

此版本换页后将会清空选中。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  render: function(text) {
    return <a href="#">{text}</a>;
  }
}, {
  title: '年龄',
  dataIndex: 'age'
}, {
  title: '住址',
  dataIndex: 'address'
}];
const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '西湖区湖底公园1号'
}];

// 通过 rowSelection 对象表明需要行选择
const rowSelection = {
  getCheckboxProps: function(record) {
    return {
      defaultChecked: record.name === '李大嘴', // 配置默认勾选的列
      disabled: record.name === '胡彦祖'    // 配置无法勾选的列
    };
  },
  onChange(selectedRowKeys) {
    console.log('selectedRowKeys changed: ' + selectedRowKeys);
  },
  onSelect: function(record, selected, selectedRows) {
    console.log(record, selected, selectedRows);
  },
  onSelectAll: function(selected, selectedRows) {
    console.log(selected, selectedRows);
  },
};

ReactDOM.render(<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
, mountNode);

配置选择框的默认属性。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  render: function(text) {
    return <a href="#">{text}</a>;
  }
}, {
  title: '年龄',
  dataIndex: 'age'
}, {
  title: '住址',
  dataIndex: 'address'
}];

const data = [];
for (let i = 0; i < 46; i++) {
  data.push({
    key: i,
    name: '李大嘴' + i,
    age: 32,
    address: '西湖区湖底公园' + i + '号'
  });
}

const pagination = {
  total: data.length,
  current: 1,
  showSizeChanger: true,
  onShowSizeChange: function(current, pageSize) {
    console.log('Current: ', current, '; PageSize: ', pageSize);
  },
  onChange: function(current) {
    console.log('Current: ', current);
  }
};

ReactDOM.render(<Table columns={columns} dataSource={data} pagination={pagination} />
, mountNode);

数据项较多时显示分页。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  filters: [{
    text: '姓李的',
    value: '李'
  }, {
    text: '姓胡的',
    value: '胡'
  }],
  // 指定确定筛选的条件函数
  // 这里是名字中第一个字是 value
  onFilter: function(value, record) {
    return record.name.indexOf(value) === 0;
  },
  sorter: function(a, b) {
    return a.name.length - b.name.length;
  }
}, {
  title: '年龄',
  dataIndex: 'age',
  sorter: function(a, b) {
    return a.age - b.age;
  }
}, {
  title: '地址',
  dataIndex: 'address',
  filters: [{
    text: '南湖',
    value: '南湖'
  }, {
    text: '西湖',
    value: '西湖'
  }],
  filterMultiple: false,
  onFilter: function(value, record) {
    return record.address.indexOf(value) === 0;
  },
  sorter: function(a, b) {
    return a.address.length - b.address.length;
  }
}];

const data = [{
  key: '1',
  name: '胡斌',
  age: 32,
  address: '南湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园12号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '南湖区湖底公园123号'
}, {
  key: '4',
  name: '李秀莲大嘴哥',
  age: 32,
  address: '西湖区湖底公园123号'
}];

function onChange(pagination, filters, sorter) {
  // 点击分页、筛选、排序时触发
  console.log('各类参数是', pagination, filters, sorter);
}

ReactDOM.render(<Table columns={columns} dataSource={data} onChange={onChange} />
, mountNode);

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

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

import { Table } from 'antd';
import reqwest from 'reqwest';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  filters: [{
    text: '姓李的',
    value: '李'
  }, {
    text: '姓胡的',
    value: '胡'
  }]
}, {
  title: '年龄',
  dataIndex: 'age',
  sorter: true
}, {
  title: '住址',
  dataIndex: 'address'
}];

const Test = React.createClass({
  getInitialState() {
    return {
      data: [],
      pagination: {},
      loading: false,
    };
  },
  handleTableChange(pagination, filters, sorter) {
    const pager = this.state.pagination;
    pager.current = pagination.current;
    this.setState({
      pagination: pager
    });
    const params = {
      pageSize: pagination.pageSize,
      currentPage: pagination.current,
      sortField: sorter.field,
      sortOrder: sorter.order
    };
    for (let key in filters) {
      params[key] = filters[key];
    }
    this.fetch(params);
  },
  fetch(params = {}) {
    console.log('请求参数:', params);
    this.setState({ loading: true });
    reqwest({
      url: 'demo/data.json',
      method: 'get',
      data: params,
      type: 'json',
      success: (result) => {
        const pagination = this.state.pagination;
        pagination.total = result.totalCount;
        this.setState({
          loading: false,
          data: result.data,
          pagination,
        });
      }
    });
  },
  componentDidMount() {
    this.fetch();
  },
  render() {
    return (
      <Table columns={columns}
             dataSource={this.state.data}
             pagination={this.state.pagination}
             loading={this.state.loading}
             onChange={this.handleTableChange} />
    );
  }
});

ReactDOM.render(<Test />, mountNode);

0.11.0 以后,dataSource 远程模式被移除,用户可以自行实现数据读取方式。

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

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

注意,此示例是静态数据模拟,展示数据不会变化,请打开网络面板查看请求。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name'
}, {
  title: '年龄',
  dataIndex: 'age'
}, {
  title: '住址',
  dataIndex: 'address'
}];

const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '西湖区湖底公园1号'
}];

ReactDOM.render(<Table columns={columns} dataSource={data} pagination={false} />
, mountNode);

传入 pagination 为 false 即可。此时表格将完整显示 dataSource 内的数据,不进行任何分页。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name'
}, {
  title: '年龄',
  dataIndex: 'age'
}, {
  title: '住址',
  dataIndex: 'address'
}];
const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '西湖区湖底公园1号'
}];

ReactDOM.render(<div>
  <h4>中号表格(紧凑型)</h4>
  <Table columns={columns} dataSource={data} size="middle" />
  <h4>小号表格</h4>
  <Table columns={columns} dataSource={data} size="small" />
</div>, mountNode);

紧凑型的列表, 中型列表用于需要数据紧凑展示的情况,小型列表只用于对话框内。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  render: function(text) {
    return <a href="#">{text}</a>;
  }
}, {
  title: '资产',
  className: 'column-money',
  dataIndex: 'money'
}, {
  title: '住址',
  dataIndex: 'address'
}];

const data = [{
  key: '1',
  name: '胡彦斌',
  money: '¥300,000.00',
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  money: '¥1,256,000.00',
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  money: '¥120,000.00',
  address: '西湖区湖底公园1号'
}];

ReactDOM.render(<Table columns={columns} dataSource={data} bordered />
, mountNode);
.column-money {
  text-align: right;
}

添加表格边框线,bordered

import { Table } from 'antd';

function renderAction() {
  return <a href="#">删除</a>;
}

function expandedRowRender(record) {
  return <p>{record.description}</p>;
}

const columns = [
  {title: '姓名', dataIndex: 'name', key: 'name'},
  {title: '年龄', dataIndex: 'age', key: 'age'},
  {title: '住址', dataIndex: 'address', key: 'address'},
  {title: '操作', dataIndex: '', key: 'x', render: renderAction}
];

const data = [
  {key: 1, name: '胡彦斌', age: 32, address: '西湖区湖底公园1号', description: '我是胡彦斌,今年32岁,住在西湖区湖底公园1号。'},
  {key: 2, name: '吴彦祖', age: 42, address: '西湖区湖底公园2号', description: '我是吴彦祖,今年42岁,住在西湖区湖底公园2号。'},
  {key: 3, name: '李大嘴', age: 32, address: '西湖区湖底公园3号', description: '我是李大嘴,今年32岁,住在西湖区湖底公园3号。'}
];

ReactDOM.render(
<Table columns={columns}
  expandedRowRender={expandedRowRender}
  dataSource={data}
  className="table" />
, mountNode);

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

import { Table } from 'antd';

// 事例表中第四行合并了五列,除了第一列设置 colSpan = 5 外
// 其他列的第四行 colSpan = 0 (被合并掉,不会渲染)
const renderContent = function(value, row, index) {
  let obj = {
    children: value,
    props: {}
  };
  if (index === 4) {
    obj.props.colSpan = 0;
  }
  return obj;
};

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  render: function(text, row, index) {
    if (index < 4) {
      return <a href="#">{text}</a>;
    } else {
      return {
        children: <a href="#">{text}</a>,
        props: {
          colSpan: 5
        }
      };
    }
  }
}, {
  title: '年龄',
  dataIndex: 'age',
  render: renderContent
}, {
  title: '家庭电话',
  colSpan: 2,
  dataIndex: 'tel',
  render: function(value, row, index) {
    let obj = {
      children: value,
      props:{}
    };
    // 第三列的第三行行合并
    if (index === 2) {
      obj.props.rowSpan = 2;
    }

    // 第三列的第四行被合并没了,设置 rowSpan = 0 直接不用渲染
    if (index === 3) {
      obj.props.rowSpan = 0;
    }

    if (index === 4) {
      obj.props.colSpan = 0;
    }
    return obj;
  }
}, {
  title: '手机号',
  colSpan: 0,
  dataIndex: 'phone',
  render: renderContent
}, {
  title: '住址',
  dataIndex: 'address',
  render: renderContent
}];

const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  tel: '0571-22098909',
  phone: 18889898989,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  tel: '0571-22098333',
  phone: 18889898888,
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  tel: '0575-22098909',
  phone: 18900010002,
  address: '西湖区湖底公园1号'
}, {
  key: '4',
  name: '李夫人',
  age: 18,
  tel: '0575-22098909',
  phone: 18900010002,
  address: '西湖区湖底公园1号'
}, {
  key: '5',
  name: '习大大',
  age: 18,
  tel: '0575-22098909',
  phone: 18900010002,
  address: '西湖区湖底公园1号'
}];

ReactDOM.render(<Table columns={columns} dataSource={data} bordered />
, mountNode);

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

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

import { Table, Button } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name'
}, {
  title: '年龄',
  dataIndex: 'age'
}, {
  title: '住址',
  dataIndex: 'address'
}];
const data = [{
  key: '1',
  name: '胡彦斌',
  age: 32,
  address: '西湖区湖底公园1号'
}, {
  key: '2',
  name: '胡彦祖',
  age: 42,
  address: '西湖区湖底公园1号'
}, {
  key: '3',
  name: '李大嘴',
  age: 32,
  address: '西湖区湖底公园1号'
}];

const App = React.createClass({
  getInitialState() {
    return {
      loading: false
    };
  },
  toggleLoading() {
    this.setState({
      loading: !this.state.loading
    });
  },
  render() {
    return <div>
      <Table columns={columns} dataSource={data} loading={this.state.loading} />
      <Button type="primary" onClick={this.toggleLoading}>切换 loading 状态</Button>
    </div>;
  }
});

ReactDOM.render(<App />, mountNode);

用属性 loading 控制表格加载中状态。

import { Table } from 'antd';

const columns = [{
  title: '姓名',
  dataIndex: 'name',
  key: 'name',
  width: '40%',
}, {
  title: '年龄',
  dataIndex: 'age',
  key: 'age',
  width: '30%',
}, {
  title: '住址',
  dataIndex: 'address',
  key: 'address',
  width: '30%',
}];

const data = [{
  key: 1,
  name: 'a',
  age: 32,
  address: '我是a',
  children: [{
    key: 11,
    name: 'aa',
    age: 33,
    address: '我是aa',
  }, {
    key: 12,
    name: 'ab',
    age: 33,
    address: '我是ab',
    children: [{
      key: 121,
      name: 'aba',
      age: 33,
      address: '我是aba',
    }],
  }, {
    key: 13,
    name: 'ac',
    age: 33,
    address: '我是ac',
    children: [{
      key: 131,
      name: 'aca',
      age: 33,
      address: '我是aca',
      children: [{
        key: 1311,
        name: 'acaa',
        age: 33,
        address: '我是acaa',
      }, {
        key: 1312,
        name: 'acab',
        age: 33,
        address: '我是acab',
      }],
    }],
  }],
}, {
  key: 2,
  name: 'b',
  age: 32,
  address: '我是b',
}];

ReactDOM.render(
  <Table columns={columns} dataSource={data} indentSize={30} />,
  mountNode
);

表格支持树形数据的展示,可以通过设置 indentSize 以控制每一层的缩进宽度。