Table表格

展示行列数据。

何时使用

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

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

如何使用

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

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

代码演示

Table 表格 - 图1

基本用法

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

  1. import { Table, Icon, Divider } from 'choerodon-ui';
  2. const columns = [{
  3. title: 'Name',
  4. dataIndex: 'name',
  5. key: 'name',
  6. render: text => <a href="#">{text}</a>,
  7. }, {
  8. title: 'Age',
  9. dataIndex: 'age',
  10. key: 'age',
  11. }, {
  12. title: 'Address',
  13. dataIndex: 'address',
  14. key: 'address',
  15. }, {
  16. title: '',
  17. key: 'action',
  18. render: (text, record) => (
  19. <span>
  20. <a href="#">Action {record.name}</a>
  21. <Divider type="vertical" />
  22. <a href="#">Delete</a>
  23. <Divider type="vertical" />
  24. <a href="#" className="c7n-dropdown-link">
  25. More actions <Icon type="down" />
  26. </a>
  27. </span>
  28. ),
  29. }];
  30. const data = [{
  31. key: '1',
  32. name: 'John Brown',
  33. age: 32,
  34. address: 'New York No. 1 Lake Park',
  35. }, {
  36. key: '2',
  37. name: 'Jim Green',
  38. age: 42,
  39. address: 'London No. 1 Lake Park',
  40. }, {
  41. key: '3',
  42. name: 'Joe Black',
  43. age: 32,
  44. address: 'Sidney No. 1 Lake Park',
  45. }];
  46. ReactDOM.render(<Table columns={columns} dataSource={data} filterBarPlaceholder="过滤表" onColumnFilterChange={item => console.log(item)} />, mountNode);

Table 表格 - 图2

JSX 风格的 API

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

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

  1. import { Table, Icon, Divider } from 'choerodon-ui';
  2. const { Column, ColumnGroup } = Table;
  3. const data = [{
  4. key: '1',
  5. firstName: 'John',
  6. lastName: 'Brown',
  7. age: 32,
  8. address: 'New York No. 1 Lake Park',
  9. }, {
  10. key: '2',
  11. firstName: 'Jim',
  12. lastName: 'Green',
  13. age: 42,
  14. address: 'London No. 1 Lake Park',
  15. }, {
  16. key: '3',
  17. firstName: 'Joe',
  18. lastName: 'Black',
  19. age: 32,
  20. address: 'Sidney No. 1 Lake Park',
  21. }];
  22. ReactDOM.render(
  23. <Table dataSource={data}>
  24. <ColumnGroup title="Name">
  25. <Column
  26. title="First Name"
  27. dataIndex="firstName"
  28. key="firstName"
  29. />
  30. <Column
  31. title="Last Name"
  32. dataIndex="lastName"
  33. key="lastName"
  34. />
  35. </ColumnGroup>
  36. <Column
  37. title="Age"
  38. dataIndex="age"
  39. key="age"
  40. />
  41. <Column
  42. title="Address"
  43. dataIndex="address"
  44. key="address"
  45. />
  46. <Column
  47. title="Action"
  48. key="action"
  49. render={(text, record) => (
  50. <span>
  51. <a href="#">Action {record.name}</a>
  52. <Divider type="vertical" />
  53. <a href="#">Delete</a>
  54. <Divider type="vertical" />
  55. <a href="#" className="c7n-dropdown-link">
  56. More actions <Icon type="down" />
  57. </a>
  58. </span>
  59. )}
  60. />
  61. </Table>,
  62. mountNode);

Table 表格 - 图3

可选择

第一列是联动的选择框。

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

  1. import { Table } from 'choerodon-ui';
  2. const columns = [{
  3. title: 'Name',
  4. dataIndex: 'name',
  5. render: text => <a href="#">{text}</a>,
  6. }, {
  7. title: 'Age',
  8. dataIndex: 'age',
  9. }, {
  10. title: 'Address',
  11. dataIndex: 'address',
  12. }];
  13. const data = [{
  14. key: '1',
  15. name: 'John Brown',
  16. age: 32,
  17. address: 'New York No. 1 Lake Park',
  18. }, {
  19. key: '2',
  20. name: 'Jim Green',
  21. age: 42,
  22. address: 'London No. 1 Lake Park',
  23. }, {
  24. key: '3',
  25. name: 'Joe Black',
  26. age: 32,
  27. address: 'Sidney No. 1 Lake Park',
  28. }, {
  29. key: '4',
  30. name: 'Disabled User',
  31. age: 99,
  32. address: 'Sidney No. 1 Lake Park',
  33. }];
  34. // rowSelection object indicates the need for row selection
  35. const rowSelection = {
  36. onChange: (selectedRowKeys, selectedRows) => {
  37. console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  38. },
  39. getCheckboxProps: record => ({
  40. disabled: record.name === 'Disabled User', // Column configuration not to be checked
  41. name: record.name,
  42. }),
  43. selections: true,
  44. };
  45. ReactDOM.render(
  46. <Table rowSelection={rowSelection} columns={columns} dataSource={data} />,
  47. mountNode);

Table 表格 - 图4

选择和操作

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

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

Table 表格 - 图5

自定义选择项

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

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

Table 表格 - 图6

下拉菜单筛选

设置 filterBar 属性为false来显示下拉过滤菜单。

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

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

Table 表格 - 图7

筛选和排序

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

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

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

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

Table 表格 - 图8

可控的筛选和排序

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

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

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

  3. 务必指定 column.key

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

Table 表格 - 图9

自定义筛选菜单

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

  1. import { Table, Input, Button, Icon } from 'choerodon-ui';
  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: 'Joe Black',
  12. age: 42,
  13. address: 'London No. 1 Lake Park',
  14. },
  15. {
  16. key: '3',
  17. name: 'Jim Green',
  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. filterDropdownVisible: false,
  31. data,
  32. searchText: '',
  33. filtered: false,
  34. };
  35. onInputChange = e => {
  36. this.setState({ searchText: e.target.value });
  37. };
  38. onSearch = () => {
  39. const { searchText } = this.state;
  40. const reg = new RegExp(searchText, 'gi');
  41. this.setState({
  42. filterDropdownVisible: false,
  43. filtered: !!searchText,
  44. data: data
  45. .map(record => {
  46. const match = record.name.match(reg);
  47. if (!match) {
  48. return null;
  49. }
  50. return {
  51. ...record,
  52. name: (
  53. <span>
  54. {record.name.split(reg).map((text, i) =>
  55. i > 0
  56. ? [
  57. <span key={text} className="highlight">
  58. {match[0]}
  59. </span>,
  60. text,
  61. ]
  62. : text,
  63. )}
  64. </span>
  65. ),
  66. };
  67. })
  68. .filter(record => !!record),
  69. });
  70. };
  71. render() {
  72. const columns = [
  73. {
  74. title: 'Name',
  75. dataIndex: 'name',
  76. key: 'name',
  77. filterDropdown: (
  78. <div className="custom-filter-dropdown">
  79. <Input
  80. ref={ele => (this.searchInput = ele)}
  81. placeholder="Search name"
  82. value={this.state.searchText}
  83. onChange={this.onInputChange}
  84. onPressEnter={this.onSearch}
  85. />
  86. <Button type="primary" onClick={this.onSearch}>
  87. Search
  88. </Button>
  89. </div>
  90. ),
  91. filterIcon: (
  92. <Icon type="smile-o" style={{ color: this.state.filtered ? '#108ee9' : '#aaa' }} />
  93. ),
  94. filterDropdownVisible: this.state.filterDropdownVisible,
  95. onFilterDropdownVisibleChange: visible => {
  96. this.setState(
  97. {
  98. filterDropdownVisible: visible,
  99. },
  100. () => this.searchInput && this.searchInput.focus(),
  101. );
  102. },
  103. },
  104. {
  105. title: 'Age',
  106. dataIndex: 'age',
  107. key: 'age',
  108. },
  109. {
  110. title: 'Address',
  111. dataIndex: 'address',
  112. key: 'address',
  113. filters: [
  114. {
  115. text: 'London',
  116. value: 'London',
  117. },
  118. {
  119. text: 'New York',
  120. value: 'New York',
  121. },
  122. ],
  123. onFilter: (value, record) => record.address.indexOf(value) === 0,
  124. },
  125. ];
  126. return <Table columns={columns} dataSource={this.state.data} />;
  127. }
  128. }
  129. ReactDOM.render(<App />, mountNode);
  1. .custom-filter-dropdown {
  2. padding: 8px;
  3. border-radius: 6px;
  4. background: #fff;
  5. box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
  6. }
  7. .custom-filter-dropdown input {
  8. width: 130px;
  9. margin-right: 8px;
  10. }
  11. .highlight {
  12. color: #f50;
  13. }

Table 表格 - 图10

远程加载数据

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

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

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

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

Table 表格 - 图11

紧凑型

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

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

Table 表格 - 图12

带边框

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

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

Table 表格 - 图13

可展开

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

  1. import { Table } from 'choerodon-ui';
  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. { title: 'Action', dataIndex: '', key: 'x', render: () => <a href="#">Delete</a> },
  7. ];
  8. const data = [
  9. { key: 1, name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park', description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.' },
  10. { key: 2, name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' },
  11. { key: 3, name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.' },
  12. ];
  13. ReactDOM.render(
  14. <Table
  15. columns={columns}
  16. expandedRowRender={record => <p style={{ margin: 0 }}>{record.description}</p>}
  17. dataSource={data}
  18. />,
  19. mountNode);

Table 表格 - 图14

表格行/列合并

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

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

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

Table 表格 - 图15

树形数据展示

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

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

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

Table 表格 - 图16

固定表头

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

需要指定 column 的 width 属性,否则列头和内容可能不对齐。

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

Table 表格 - 图17

固定列

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

若列头与内容不对齐或出现列重复,请指定列的宽度 width

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

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

Table 表格 - 图18

固定头和列

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

若列头与内容不对齐或出现列重复,请指定列的宽度 width

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

  1. import { Table } from 'choerodon-ui';
  2. const columns = [
  3. { title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left' },
  4. { title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left' },
  5. { title: 'Column 1', dataIndex: 'address', key: '1', width: 150 },
  6. { title: 'Column 2', dataIndex: 'address', key: '2', width: 150 },
  7. { title: 'Column 3', dataIndex: 'address', key: '3', width: 150 },
  8. { title: 'Column 4', dataIndex: 'address', key: '4', width: 150 },
  9. { title: 'Column 5', dataIndex: 'address', key: '5', width: 150 },
  10. { title: 'Column 6', dataIndex: 'address', key: '6', width: 150 },
  11. { title: 'Column 7', dataIndex: 'address', key: '7', width: 150 },
  12. { title: 'Column 8', dataIndex: 'address', key: '8' },
  13. {
  14. title: 'Action',
  15. key: 'operation',
  16. fixed: 'right',
  17. width: 100,
  18. render: () => <a href="#">action</a>,
  19. },
  20. ];
  21. const data = [];
  22. for (let i = 0; i < 100; i++) {
  23. data.push({
  24. key: i,
  25. name: `Edrward ${i}`,
  26. age: 32,
  27. address: `London Park no. ${i}`,
  28. });
  29. }
  30. ReactDOM.render(<Table columns={columns} dataSource={data} scroll={{ x: 1500, y: 300 }} />, mountNode);

Table 表格 - 图19

表头分组

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

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

Table 表格 - 图20

可编辑单元格

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

  1. import { Table, Input, Icon, Button, Popconfirm } from 'choerodon-ui';
  2. class EditableCell extends React.Component {
  3. state = {
  4. value: this.props.value,
  5. editable: false,
  6. }
  7. handleChange = (e) => {
  8. const value = e.target.value;
  9. this.setState({ value });
  10. }
  11. check = () => {
  12. this.setState({ editable: false });
  13. if (this.props.onChange) {
  14. this.props.onChange(this.state.value);
  15. }
  16. }
  17. edit = () => {
  18. this.setState({ editable: true });
  19. }
  20. render() {
  21. const { value, editable } = this.state;
  22. return (
  23. <div className="editable-cell">
  24. {
  25. editable ? (
  26. <div className="editable-cell-input-wrapper">
  27. <Input
  28. value={value}
  29. onChange={this.handleChange}
  30. onPressEnter={this.check}
  31. />
  32. <Icon
  33. type="check"
  34. className="editable-cell-icon-check"
  35. onClick={this.check}
  36. />
  37. </div>
  38. ) : (
  39. <div className="editable-cell-text-wrapper">
  40. {value || ' '}
  41. <Icon
  42. type="mode_edit"
  43. className="editable-cell-icon"
  44. onClick={this.edit}
  45. />
  46. </div>
  47. )
  48. }
  49. </div>
  50. );
  51. }
  52. }
  53. class EditableTable extends React.Component {
  54. constructor(props) {
  55. super(props);
  56. this.columns = [{
  57. title: 'name',
  58. dataIndex: 'name',
  59. width: '30%',
  60. render: (text, record) => (
  61. <EditableCell
  62. value={text}
  63. onChange={this.onCellChange(record.key, 'name')}
  64. />
  65. ),
  66. }, {
  67. title: 'age',
  68. dataIndex: 'age',
  69. }, {
  70. title: 'address',
  71. dataIndex: 'address',
  72. }, {
  73. title: 'operation',
  74. dataIndex: 'operation',
  75. render: (text, record) => {
  76. return (
  77. this.state.dataSource.length > 1
  78. ? (
  79. <Popconfirm title="Sure to delete?" onConfirm={() => this.onDelete(record.key)}>
  80. <a href="#">Delete</a>
  81. </Popconfirm>
  82. ) : null
  83. );
  84. },
  85. }];
  86. this.state = {
  87. dataSource: [{
  88. key: '0',
  89. name: 'Edward King 0',
  90. age: '32',
  91. address: 'London, Park Lane no. 0',
  92. }, {
  93. key: '1',
  94. name: 'Edward King 1',
  95. age: '32',
  96. address: 'London, Park Lane no. 1',
  97. }],
  98. count: 2,
  99. };
  100. }
  101. onCellChange = (key, dataIndex) => {
  102. return (value) => {
  103. const dataSource = [...this.state.dataSource];
  104. const target = dataSource.find(item => item.key === key);
  105. if (target) {
  106. target[dataIndex] = value;
  107. this.setState({ dataSource });
  108. }
  109. };
  110. }
  111. onDelete = (key) => {
  112. const dataSource = [...this.state.dataSource];
  113. this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
  114. }
  115. handleAdd = () => {
  116. const { count, dataSource } = this.state;
  117. const newData = {
  118. key: count,
  119. name: `Edward King ${count}`,
  120. age: 32,
  121. address: `London, Park Lane no. ${count}`,
  122. };
  123. this.setState({
  124. dataSource: [...dataSource, newData],
  125. count: count + 1,
  126. });
  127. }
  128. render() {
  129. const { dataSource } = this.state;
  130. const columns = this.columns;
  131. return (
  132. <div>
  133. <Button className="editable-add-btn" onClick={this.handleAdd}>Add</Button>
  134. <Table bordered dataSource={dataSource} columns={columns} />
  135. </div>
  136. );
  137. }
  138. }
  139. ReactDOM.render(<EditableTable />, mountNode);
  1. .editable-cell {
  2. position: relative;
  3. }
  4. .editable-cell-input-wrapper,
  5. .editable-cell-text-wrapper {
  6. padding-right: 24px;
  7. }
  8. .editable-cell-text-wrapper {
  9. padding: 5px 24px 5px 5px;
  10. }
  11. .editable-cell-icon,
  12. .editable-cell-icon-check {
  13. position: absolute;
  14. right: 0;
  15. width: 20px;
  16. cursor: pointer;
  17. }
  18. .editable-cell-icon {
  19. line-height: 18px;
  20. display: none;
  21. }
  22. .editable-cell-icon-check {
  23. line-height: 28px;
  24. }
  25. .editable-cell:hover .editable-cell-icon {
  26. display: inline-block;
  27. }
  28. .editable-cell-icon:hover,
  29. .editable-cell-icon-check:hover {
  30. color: #108ee9;
  31. }
  32. .editable-add-btn {
  33. margin-bottom: 8px;
  34. }

Table 表格 - 图21

可编辑行

带行编辑功能的表格。

  1. import { Table, Input, Popconfirm } from 'choerodon-ui';
  2. const data = [];
  3. for (let i = 0; i < 100; i++) {
  4. data.push({
  5. key: i.toString(),
  6. name: `Edrward ${i}`,
  7. age: 32,
  8. address: `London Park no. ${i}`,
  9. });
  10. }
  11. const EditableCell = ({ editable, value, onChange }) => (
  12. <div>
  13. {editable
  14. ? <Input style={{ margin: '-5px 0' }} value={value} onChange={e => onChange(e.target.value)} />
  15. : value
  16. }
  17. </div>
  18. );
  19. class EditableTable extends React.Component {
  20. constructor(props) {
  21. super(props);
  22. this.columns = [{
  23. title: 'name',
  24. dataIndex: 'name',
  25. width: '25%',
  26. render: (text, record) => this.renderColumns(text, record, 'name'),
  27. }, {
  28. title: 'age',
  29. dataIndex: 'age',
  30. width: '15%',
  31. render: (text, record) => this.renderColumns(text, record, 'age'),
  32. }, {
  33. title: 'address',
  34. dataIndex: 'address',
  35. width: '40%',
  36. render: (text, record) => this.renderColumns(text, record, 'address'),
  37. }, {
  38. title: 'operation',
  39. dataIndex: 'operation',
  40. render: (text, record) => {
  41. const { editable } = record;
  42. return (
  43. <div className="editable-row-operations">
  44. {
  45. editable ? (
  46. <span>
  47. <a onClick={() => this.save(record.key)}>Save</a>
  48. <Popconfirm title="Sure to cancel?" onConfirm={() => this.cancel(record.key)}>
  49. <a>Cancel</a>
  50. </Popconfirm>
  51. </span>
  52. )
  53. : <a onClick={() => this.edit(record.key)}>Edit</a>
  54. }
  55. </div>
  56. );
  57. },
  58. }];
  59. this.state = { data };
  60. this.cacheData = data.map(item => ({ ...item }));
  61. }
  62. renderColumns(text, record, column) {
  63. return (
  64. <EditableCell
  65. editable={record.editable}
  66. value={text}
  67. onChange={value => this.handleChange(value, record.key, column)}
  68. />
  69. );
  70. }
  71. handleChange(value, key, column) {
  72. const newData = [...this.state.data];
  73. const target = newData.filter(item => key === item.key)[0];
  74. if (target) {
  75. target[column] = value;
  76. this.setState({ data: newData });
  77. }
  78. }
  79. edit(key) {
  80. const newData = [...this.state.data];
  81. const target = newData.filter(item => key === item.key)[0];
  82. if (target) {
  83. target.editable = true;
  84. this.setState({ data: newData });
  85. }
  86. }
  87. save(key) {
  88. const newData = [...this.state.data];
  89. const target = newData.filter(item => key === item.key)[0];
  90. if (target) {
  91. delete target.editable;
  92. this.setState({ data: newData });
  93. this.cacheData = newData.map(item => ({ ...item }));
  94. }
  95. }
  96. cancel(key) {
  97. const newData = [...this.state.data];
  98. const target = newData.filter(item => key === item.key)[0];
  99. if (target) {
  100. Object.assign(target, this.cacheData.filter(item => key === item.key)[0]);
  101. delete target.editable;
  102. this.setState({ data: newData });
  103. }
  104. }
  105. render() {
  106. return <Table bordered dataSource={this.state.data} columns={this.columns} />;
  107. }
  108. }
  109. ReactDOM.render(<EditableTable />, mountNode);
  1. .editable-row-operations a {
  2. margin-right: 8px;
  3. }

Table 表格 - 图22

嵌套子表格

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

  1. import { Table, Badge, Menu, Dropdown, Icon } from 'choerodon-ui';
  2. const menu = (
  3. <Menu>
  4. <Menu.Item>
  5. Action 1
  6. </Menu.Item>
  7. <Menu.Item>
  8. Action 2
  9. </Menu.Item>
  10. </Menu>
  11. );
  12. function NestedTable() {
  13. const expandedRowRender = () => {
  14. const columns = [
  15. { title: 'Date', dataIndex: 'date', key: 'date' },
  16. { title: 'Name', dataIndex: 'name', key: 'name' },
  17. { title: 'Status', key: 'state', render: () => <span><Badge status="success" />Finished</span> },
  18. { title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
  19. {
  20. title: 'Action',
  21. dataIndex: 'operation',
  22. key: 'operation',
  23. render: () => (
  24. <span className="table-operation">
  25. <a href="#">Pause</a>
  26. <a href="#">Stop</a>
  27. <Dropdown overlay={menu}>
  28. <a href="#">
  29. More <Icon type="down" />
  30. </a>
  31. </Dropdown>
  32. </span>
  33. ),
  34. },
  35. ];
  36. const data = [];
  37. for (let i = 0; i < 3; ++i) {
  38. data.push({
  39. key: i,
  40. date: '2014-12-24 23:12:00',
  41. name: 'This is production name',
  42. upgradeNum: 'Upgraded: 56',
  43. });
  44. }
  45. return (
  46. <Table
  47. columns={columns}
  48. dataSource={data}
  49. pagination={false}
  50. />
  51. );
  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 href="#">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. expandedRowRender={expandedRowRender}
  79. dataSource={data}
  80. />
  81. );
  82. }
  83. ReactDOM.render(<NestedTable />, mountNode);
  1. .components-table-demo-nested .c7n-table-expanded-row > td:last-child {
  2. padding: 0 48px 0 8px;
  3. }
  4. .components-table-demo-nested .c7n-table-expanded-row > td:last-child .c7n-table-thead th {
  5. border-bottom: 1px solid #e9e9e9;
  6. }
  7. .components-table-demo-nested .c7n-table-expanded-row > td:last-child .c7n-table-thead th:first-child {
  8. padding-left: 0;
  9. }
  10. .components-table-demo-nested .c7n-table-expanded-row > td:last-child .c7n-table-row td:first-child {
  11. padding-left: 0;
  12. }
  13. .components-table-demo-nested .c7n-table-expanded-row .c7n-table-row:last-child td {
  14. border: none;
  15. }
  16. .components-table-demo-nested .c7n-table-expanded-row .c7n-table-thead > tr > th {
  17. background: none;
  18. }
  19. .components-table-demo-nested .table-operation a:not(:last-child) {
  20. margin-right: 24px;
  21. }
  22. .components-table-demo-nested .c7n-table-expanded-row:hover > td {
  23. background: #fbfbfb;
  24. }

Table 表格 - 图23

拖拽排序

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

  1. import { Table } from 'choerodon-ui';
  2. import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
  3. import HTML5Backend from 'react-dnd-html5-backend';
  4. import update from 'immutability-helper';
  5. function dragDirection(
  6. dragIndex,
  7. hoverIndex,
  8. initialClientOffset,
  9. clientOffset,
  10. sourceClientOffset,
  11. ) {
  12. const hoverMiddleY = (initialClientOffset.y - sourceClientOffset.y) / 2;
  13. const hoverClientY = clientOffset.y - sourceClientOffset.y;
  14. if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) {
  15. return 'downward';
  16. }
  17. if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) {
  18. return 'upward';
  19. }
  20. }
  21. let BodyRow = props => {
  22. const {
  23. isOver,
  24. connectDragSource,
  25. connectDropTarget,
  26. moveRow,
  27. dragRow,
  28. clientOffset,
  29. sourceClientOffset,
  30. initialClientOffset,
  31. ...restProps
  32. } = props;
  33. const style = { ...restProps.style, cursor: 'move' };
  34. let className = restProps.className;
  35. if (isOver && initialClientOffset) {
  36. const direction = dragDirection(
  37. dragRow.index,
  38. restProps.index,
  39. initialClientOffset,
  40. clientOffset,
  41. sourceClientOffset,
  42. );
  43. if (direction === 'downward') {
  44. className += ' drop-over-downward';
  45. }
  46. if (direction === 'upward') {
  47. className += ' drop-over-upward';
  48. }
  49. }
  50. return connectDragSource(
  51. connectDropTarget(<tr {...restProps} className={className} style={style} />),
  52. );
  53. };
  54. const rowSource = {
  55. beginDrag(props) {
  56. return {
  57. index: props.index,
  58. };
  59. },
  60. };
  61. const rowTarget = {
  62. drop(props, monitor) {
  63. const dragIndex = monitor.getItem().index;
  64. const hoverIndex = props.index;
  65. // Don't replace items with themselves
  66. if (dragIndex === hoverIndex) {
  67. return;
  68. }
  69. // Time to actually perform the action
  70. props.moveRow(dragIndex, hoverIndex);
  71. // Note: we're mutating the monitor item here!
  72. // Generally it's better to avoid mutations,
  73. // but it's good here for the sake of performance
  74. // to avoid expensive index searches.
  75. monitor.getItem().index = hoverIndex;
  76. },
  77. };
  78. BodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
  79. connectDropTarget: connect.dropTarget(),
  80. isOver: monitor.isOver(),
  81. sourceClientOffset: monitor.getSourceClientOffset(),
  82. }))(
  83. DragSource('row', rowSource, (connect, monitor) => ({
  84. connectDragSource: connect.dragSource(),
  85. dragRow: monitor.getItem(),
  86. clientOffset: monitor.getClientOffset(),
  87. initialClientOffset: monitor.getInitialClientOffset(),
  88. }))(BodyRow),
  89. );
  90. const columns = [
  91. {
  92. title: 'Name',
  93. dataIndex: 'name',
  94. key: 'name',
  95. },
  96. {
  97. title: 'Age',
  98. dataIndex: 'age',
  99. key: 'age',
  100. },
  101. {
  102. title: 'Address',
  103. dataIndex: 'address',
  104. key: 'address',
  105. },
  106. ];
  107. class DragSortingTable extends React.Component {
  108. state = {
  109. data: [
  110. {
  111. key: '1',
  112. name: 'John Brown',
  113. age: 32,
  114. address: 'New York No. 1 Lake Park',
  115. },
  116. {
  117. key: '2',
  118. name: 'Jim Green',
  119. age: 42,
  120. address: 'London No. 1 Lake Park',
  121. },
  122. {
  123. key: '3',
  124. name: 'Joe Black',
  125. age: 32,
  126. address: 'Sidney No. 1 Lake Park',
  127. },
  128. ],
  129. };
  130. components = {
  131. body: {
  132. row: BodyRow,
  133. },
  134. };
  135. moveRow = (dragIndex, hoverIndex) => {
  136. const { data } = this.state;
  137. const dragRow = data[dragIndex];
  138. this.setState(
  139. update(this.state, {
  140. data: {
  141. $splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]],
  142. },
  143. }),
  144. );
  145. };
  146. render() {
  147. return (
  148. <Table
  149. columns={columns}
  150. dataSource={this.state.data}
  151. components={this.components}
  152. onRow={(record, index) => ({
  153. index,
  154. moveRow: this.moveRow,
  155. })}
  156. />
  157. );
  158. }
  159. }
  160. const Demo = DragDropContext(HTML5Backend)(DragSortingTable);
  161. ReactDOM.render(<Demo />, 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

动态控制表格属性

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

  1. import { Table, Icon, Switch, Radio, Form, Divider } from 'choerodon-ui';
  2. const FormItem = Form.Item;
  3. const columns = [
  4. {
  5. title: 'Name',
  6. dataIndex: 'name',
  7. key: 'name',
  8. width: 150,
  9. render: text => <a href="#">{text}</a>,
  10. },
  11. {
  12. title: 'Age',
  13. dataIndex: 'age',
  14. key: 'age',
  15. width: 70,
  16. },
  17. {
  18. title: 'Address',
  19. dataIndex: 'address',
  20. key: 'address',
  21. },
  22. {
  23. title: 'Action',
  24. key: 'action',
  25. width: 360,
  26. render: (text, record) => (
  27. <span>
  28. <a href="#">Action {record.name}</a>
  29. <Divider type="vertical" />
  30. <a href="#">Delete</a>
  31. <Divider type="vertical" />
  32. <a href="#" className="c7n-dropdown-link">
  33. More actions <Icon type="down" />
  34. </a>
  35. </span>
  36. ),
  37. },
  38. ];
  39. const data = [];
  40. for (let i = 1; i <= 10; i++) {
  41. data.push({
  42. key: i,
  43. name: 'John Brown',
  44. age: `${i}2`,
  45. address: `New York No. ${i} Lake Park`,
  46. description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`,
  47. });
  48. }
  49. const expandedRowRender = record => <p>{record.description}</p>;
  50. const title = () => 'Here is title';
  51. const showHeader = true;
  52. const footer = () => 'Here is footer';
  53. const scroll = { y: 240 };
  54. const pagination = { position: 'bottom' };
  55. class Demo extends React.Component {
  56. state = {
  57. bordered: false,
  58. loading: false,
  59. pagination,
  60. size: 'default',
  61. expandedRowRender,
  62. title: undefined,
  63. showHeader,
  64. footer,
  65. rowSelection: {},
  66. scroll: undefined,
  67. };
  68. handleToggle = prop => {
  69. return enable => {
  70. this.setState({ [prop]: enable });
  71. };
  72. };
  73. handleSizeChange = e => {
  74. this.setState({ size: e.target.value });
  75. };
  76. handleExpandChange = enable => {
  77. this.setState({ expandedRowRender: enable ? expandedRowRender : undefined });
  78. };
  79. handleTitleChange = enable => {
  80. this.setState({ title: enable ? title : undefined });
  81. };
  82. handleHeaderChange = enable => {
  83. this.setState({ showHeader: enable ? showHeader : false });
  84. };
  85. handleFooterChange = enable => {
  86. this.setState({ footer: enable ? footer : undefined });
  87. };
  88. handleRowSelectionChange = enable => {
  89. this.setState({ rowSelection: enable ? {} : undefined });
  90. };
  91. handleScollChange = enable => {
  92. this.setState({ scroll: enable ? scroll : undefined });
  93. };
  94. handlePaginationChange = e => {
  95. const { value } = e.target;
  96. this.setState({
  97. pagination: value === 'none' ? false : { position: value },
  98. });
  99. };
  100. render() {
  101. const state = this.state;
  102. return (
  103. <div>
  104. <div className="components-table-demo-control-bar">
  105. <Form layout="inline">
  106. <FormItem label="Bordered">
  107. <Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
  108. </FormItem>
  109. <FormItem label="loading">
  110. <Switch checked={state.loading} onChange={this.handleToggle('loading')} />
  111. </FormItem>
  112. <FormItem label="Title">
  113. <Switch checked={!!state.title} onChange={this.handleTitleChange} />
  114. </FormItem>
  115. <FormItem label="Column Header">
  116. <Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
  117. </FormItem>
  118. <FormItem label="Footer">
  119. <Switch checked={!!state.footer} onChange={this.handleFooterChange} />
  120. </FormItem>
  121. <FormItem label="Expandable">
  122. <Switch checked={!!state.expandedRowRender} onChange={this.handleExpandChange} />
  123. </FormItem>
  124. <FormItem label="Checkbox">
  125. <Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
  126. </FormItem>
  127. <FormItem label="Fixed Header">
  128. <Switch checked={!!state.scroll} onChange={this.handleScollChange} />
  129. </FormItem>
  130. <FormItem label="Size">
  131. <Radio.Group size="default" value={state.size} onChange={this.handleSizeChange}>
  132. <Radio.Button value="default">Default</Radio.Button>
  133. <Radio.Button value="large">Large</Radio.Button>
  134. <Radio.Button value="small">Small</Radio.Button>
  135. </Radio.Group>
  136. </FormItem>
  137. <FormItem label="Pagination">
  138. <Radio.Group
  139. value={state.pagination ? state.pagination.position : 'none'}
  140. onChange={this.handlePaginationChange}
  141. >
  142. <Radio.Button value="top">Top</Radio.Button>
  143. <Radio.Button value="bottom">Bottom</Radio.Button>
  144. <Radio.Button value="both">Both</Radio.Button>
  145. <Radio.Button value="none">None</Radio.Button>
  146. </Radio.Group>
  147. </FormItem>
  148. </Form>
  149. </div>
  150. <Table {...this.state} columns={columns} dataSource={data} />
  151. </div>
  152. );
  153. }
  154. }
  155. ReactDOM.render(<Demo />, mountNode);

API

Table

参数说明类型默认值
autoScroll是否在翻页时自动滚动到表格可视区域,并滚动到第一条booleantrue
bordered是否展示外边框和列边框booleanfalse
columns表格列的配置描述,具体项见下表ColumnProps[]-
components覆盖默认的 table 元素object-
dataSource数据数组any[]
defaultExpandAllRows初始时,是否展开所有行booleanfalse
defaultExpandedRowKeys默认展开的行string[]-
expandedRowKeys展开的行,控制属性string[]-
expandedRowRender额外的展开行Function(record):ReactNode-
expandRowByClick通过点击行来展开子行booleanfalse
footer表格尾部Function(currentPageData)
empty当数据源为空时显示的内容ReactNode-
indentSize展示树形数据时,每层缩进的宽度,以 px 为单位number15
loading页面是否加载中boolean|objectfalse
locale默认文案设置,目前包括排序、过滤、空数据文案objectfilterConfirm: '确定' filterReset: '重置' emptyText: '暂无数据'
pagination分页器,参考配置项pagination,设为 false 时不展示和进行分页object
rowClassName表格行的类名Function(record, index):string-
rowKey表格行 key 的取值,可以是字符串或一个函数string|Function(record):string'key'
rowSelection列表项是否可选择,配置项objectnull
scroll设置横向或纵向滚动,也可用于指定滚动区域的宽和高,建议为 x 设置一个数字,如果要设置为 true,需要配合样式 .c7n-table td { white-space: nowrap; }{ x: number | true, y: number }-
showHeader是否显示表头booleantrue
size正常或迷你类型,default or smallstringdefault
title表格标题Function(currentPageData)
onChange分页、排序、筛选变化时触发Function(pagination, filters, sorter)
onColumnFilterChange右上角行过滤按钮中选项变化时触发Function(item)
onExpand点击展开图标时触发Function(expanded, record)
onExpandedRowsChange展开的行变化时触发Function(expandedRows)
onHeaderRow设置头部行属性Function(column, index)-
onRow设置行属性Function(record, index)-
filterBar显示过滤条,设置为false时,在列头上会显示过滤菜单按钮booleantrue
filters<受控>过滤条中的过滤条件,例:[{ name: 'Jom' }, 'OR', { name: 'Jim' }]name 为列的 keydataIndexany[]-
noFilters去掉组件自带的模糊搜索booleanfalse
filterBarPlaceholder过滤条的占位文本string

onRow 用法

适用于 onRow onHeaderRow onCell onHeaderCell

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

Column

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

参数说明类型默认值
className列的 classNamestring-
colSpan表头列合并,设置为 0 时,不渲染number
dataIndex列数据在数据项中对应的 key,支持 a.b.c 的嵌套写法string-
disableClick禁用点击列表筛选项booleanfalse
filterDropdown可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互ReactNode-
filterDropdownVisible用于控制自定义筛选菜单是否可见boolean-
filtered标识数据是否经过过滤,筛选图标会高亮booleanfalse
filteredValue筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组string[]-
filterIcon自定义 fiter 图标。ReactNodefalse
filterMultiple是否多选booleanfalse
filters表头的筛选菜单项object[]-
fixed列是否固定,可选 true(等效于 left) 'left' 'right'boolean|stringfalse
keyReact 需要的 key,如果已经设置了唯一的 dataIndex,可以忽略这个属性string-
render生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return里面可以设置表格行/列合并Function(text, record, index) {}-
align设置列内容的对齐方式'left' | 'right' | 'center''left'
sorter排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 trueFunction|boolean-
sortOrder排序的受控属性,外界可用此控制列的排序,可设置为 'ascend' 'descend' falseboolean|string-
title列头显示文字string|ReactNode-
filterTitle过滤条可选则的列的名字,默认为属性title的值string|ReactNode-
width列宽度string|number-
onCell设置单元格属性Function(record)-
onFilter本地模式下,确定筛选的运行函数Function-
onFilterDropdownVisibleChange自定义筛选菜单可见变化时调用function(visible) {}-
onHeaderCell设置头部单元格属性Function(column)-

ColumnGroup

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

pagination

分页的配置项。

参数说明类型默认值
position指定分页显示的位置'top' | 'bottom' | 'both''bottom'

更多配置项,请查看 Pagination

rowSelection

选择功能的配置。

参数说明类型默认值
fixed把选择框列固定在左边boolean-
getCheckboxProps选择框的默认属性配置Function(record)-
hideDefaultSelections去掉『全选』『反选』两个默认选项booleanfalse
selectedRowKeys指定选中项的 key 数组,需要和 onChange 进行配合string[][]
columnWidth自定义列表选择框宽度string|number-
selections自定义选择项 配置项, 设为 true 时使用默认选择项object[]|booleantrue
type多选/单选,checkbox or radiostringcheckbox
onChange选中项发生变化的时的回调Function(selectedRowKeys, selectedRows)-
onSelect用户手动选择/取消选择某列的回调Function(record, selected, selectedRows, nativeEvent)-
onSelectAll用户手动选择/取消选择所有列的回调Function(selected, selectedRows, changeRows)-
onSelectInvert用户手动选择反选的回调Function(selectedRows)-

selection

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

在 TypeScript 中使用

import { Table } from 'choerodon-ui';
import { ColumnProps } from 'choerodon-ui/lib/table';

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

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

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

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

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

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

注意

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

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

Table 表格 - 图25

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