Table 表格

如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令npm install @alifd/next@latest -S

开发指南

Table 负责将数据呈现为高度可定制和具备可访问性的 HTML 表格,其核心功能为将结构化的数据使用表格的方式展现,然后可以使用各种参数来向表格中加入一些特性,比如排序,过滤,滚动,锁列等。

基本使用

基本的 Table 包含行和列,使用 Table.Column 来定义列的信息,使用传入的 dataSource 属性数据来创建行。

下面的代码将会创建一行两列的数据表:

  1. import { Table } from '@alifd/next';
  2. const dataSource = [{id: 1, time: '2016'}];
  3. ReactDOM.render(
  4. <Table dataSource={dataSource}>
  5. <Table.Column title="Id" dataIndex="id"/>
  6. <Table.Column title="Time" dataIndex="time"/>
  7. </Table>, mountNode);

常见问题

  • Q: 通过 rowSelection 开启了选择模式,为什么选择任意一个都是全选? A: 给定的数据源中的属性需要有一个唯一标示该条数据的主键,默认值为id,可通过 primaryKey 更改 e.g.<Table primaryKey='myId'></Table>

  • Q: 还是 rowSelection 选择模式,如何设置默认选中/禁用呢? A: 通过受控模式,设置 rowSelection.selectedRowKeys 可以默认选中选中;通过 rowSelection.getProps 可以自定义每一行checkbox的props,具体可搜索demo选择可控

  • Q: 又是 rowSelection 选择模式,如何屏蔽全选按钮/自定义全选按钮呢 A: 通过 rowSelection.titleProps 可以自定义选择列的表头的props,可通过 style: {display: 'none'} 屏蔽全选按钮;此外还有 rowSelection.titleAddons rowSelection.columnProps等属性,具体用法可搜索demo 可选择

  • Q: 能用什么样的方式支持行的双击事件/设置每一行的样式?处理整行点击呢? A: 通过 rowProps 属性,重写行支持的原生属性,比如className style onDoubleClick等;通过 onRowClick 处理整行点击

列配置

API。

下面的代码会让cell根据值渲染不同的视图:

  1. import { Table } from '@alifd/next';
  2. const dataSource = [{id: 1, time: '2016'}];
  3. const renderTime = value => {
  4. if (value === '2016') {
  5. return '今年';
  6. }
  7. return value;
  8. };
  9. ReactDOM.render(
  10. <Table dataSource={dataSource}>
  11. <Table.Column title="Id" dataIndex="id"/>
  12. <Table.Column title="Time" dataIndex="time" cell={renderTime}/>
  13. </Table>, mountNode);

多表头

使用 Table.ColumnGroup 包裹 Table.Column 来创建有多个表头的表格。

  1. import { Table } from '@alifd/next';
  2. const dataSource = [{id: 1, time: '2016'}];
  3. ReactDOM.render(
  4. <Table dataSource={dataSource}>
  5. <Table.ColumnGroup>
  6. <Table.Column title="Id" dataIndex="id"/>
  7. <Table.Column title="Time" dataIndex="time"/>
  8. </Table.ColumnGroup>
  9. <Table.ColumnGroup>
  10. <Table.Column title="Id" dataIndex="id"/>
  11. </Table.ColumnGroup>
  12. </Table>, mountNode);

已知问题

分组 Table 不支持在 Hover 状态和选中状态下显示背景色,无法合并单元格

Table 锁列特性下面无法使用合并单元格功能

API

Table

参数说明类型默认值
dataSource表格展示的数据源Array[]
onRowClick点击表格每一行触发的事件签名:Function(record: Object, index: Number, e: Event) => void参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列e: {Event} DOM事件对象Function() => {}
onRowMouseEnter悬浮在表格每一行的时候触发的事件签名:Function(record: Object, index: Number, e: Event) => void参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列e: {Event} DOM事件对象Function() => {}
onRowMouseLeave离开表格每一行的时候触发的事件签名:Function(record: Object, index: Number, e: Event) => void参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列e: {Event} DOM事件对象Function() => {}
onSort点击列排序触发的事件签名:Function(dataIndex: String, order: String) => void参数:dataIndex: {String} 指定的排序的字段order: {String} 排序对应的顺序, 有descasc两种Function() => {}
onFilter点击过滤确认按钮触发的事件签名:Function(filterParams: Object) => void参数:filterParams: {Object} 过滤的字段信息Function() => {}
onResizeChange重设列尺寸的时候触发的事件签名:Function(dataIndex: String, value: Number) => void参数:dataIndex: {String} 指定重设的字段value: {Number} 列宽变动的数值Function() => {}
rowProps设置每一行的属性,如果返回值和其他针对行操作的属性冲突则无效。签名:Function(record: Object, index: Number) => Object参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列返回值:{Object} 需要设置的行属性Function() => {}
cellProps设置单元格的属性,通过该属性可以进行合并单元格签名:Function(rowIndex: Number, colIndex: Number, dataIndex: String, record: Object) => Object参数:rowIndex: {Number} 该行所对应的序列colIndex: {Number} 该列所对应的序列dataIndex: {String} 该列所对应的字段名称record: {Object} 该行对应的记录返回值:{Object} 返回td元素的所支持的属性对象Function() => {}
hasBorder表格是否具有边框Booleantrue
hasHeader表格是否具有头部Booleantrue
isZebra表格是否是斑马线Booleanfalse
loading表格是否在加载中Booleanfalse
loadingComponent自定义 Loading 组件请务必传递 props, 使用方式: loadingComponent={props => <Loading {…props}/>}签名:Function(props: Object) => void参数:props: {Object} 当前点击行的keyFunction-
filterParams当前过滤的的keys,使用此属性可以控制表格的头部的过滤选项中哪个菜单被选中,格式为 {dataIndex: {selectedKeys:[]}}示例:假设要控制dataIndex为id的列的过滤菜单中key为one的菜单项选中<Table filterParams={{id: {selectedKeys: ['one']}}}/>Object-
sort当前排序的字段,使用此属性可以控制表格的字段的排序,格式为{dataIndex: 'asc'}Object-
sortIcons自定义排序按钮,例如上下排布的: {desc: <Icon style={{top: '6px', left: '4px'}} type={'arrow-down'} size="small" />, asc: <Icon style={{top: '-6px', left: '4px'}} type={'arrow-up'} size="small" />}Object-
emptyContent设置数据为空的时候的表格内容展现ReactNode-
primaryKeydataSource当中数据的主键,如果给定的数据源中的属性不包含该主键,会造成选择状态全部选中String'id'
expandedRowRender额外渲染行的渲染函数签名:Function(record: Object, index: Number) => Element参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列返回值:{Element} 渲染内容Function-
expandedRowIndent额外渲染行的缩进Array-
openRowKeys默认情况下展开的渲染行或者Tree, 传入此属性为受控状态Array-
hasExpandedRowCtrl是否显示点击展开额外渲染行的+号按钮Boolean-
getExpandedColProps设置额外渲染行的属性签名:Function() => voidFunction-
onRowOpen在额外渲染行或者Tree展开或者收起的时候触发的事件签名:Function(openRowKeys: Array, currentRowKey: String, expanded: Boolean, currentRecord: Object) => void参数:openRowKeys: {Array} 展开的渲染行的keycurrentRowKey: {String} 当前点击的渲染行的keyexpanded: {Boolean} 当前点击是展开还是收起currentRecord: {Object} 当前点击额外渲染行的记录Function-
onExpandedRowClick点击额外渲染行触发的事件签名:Function(record: Object, index: Number, e: Event) => void参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列e: {Event} DOM事件对象Function-
fixedHeader表头是否固定,该属性配合maxBodyHeight使用,当内容区域的高度超过maxBodyHeight的时候,在内容区域会出现滚动条Boolean-
maxBodyHeight最大内容区域的高度,在fixedHeadertrue的时候,超过这个高度会出现滚动条Number/String-
rowSelection是否启用选择模式属性:getProps: {Function} Function(record, index)=>Object 获取selection的默认属性onChange: {Function} Function(selectedRowKeys:Array, records:Array) 选择改变的时候触发的事件,注意: 其中records只会包含当前dataSource的数据,很可能会小于selectedRowKeys的长度。onSelect: {Function} Function(selected:Boolean, record:Object, records:Array) 用户手动选择/取消选择某行的回调onSelectAll: {Function} Function(selected:Boolean, records:Array) 用户手动选择/取消选择所有行的回调selectedRowKeys: {Array} 设置了此属性,将rowSelection变为受控状态,接收值为该行数据的primaryKey的值mode: {String} 选择selection的模式, 可选值为single, multiple,默认为multiplecolumnProps: {Function} Function()=>Object 选择列 的props,例如锁列、对齐等,可使用Table.Column 的所有参数titleProps: {Function} Function()=>Object 选择列 表头的props,仅在 multiple 模式下生效titleAddons: {Function} Function()=>Node 选择列 表头添加的元素,在single multiple 下都生效Object-
stickyHeader表头是否是stickyBoolean-
offsetTop距离窗口顶部达到指定偏移量后触发Number-
affixPropsaffix组件的的属性Object-
indent在tree模式下的缩进尺寸, 仅在isTree为true时候有效Number-
isTree开启Table的tree模式, 接收的数据格式中包含children则渲染成tree tableBoolean-
useVirtual是否开启虚拟滚动Boolean-
rowHeight设置行高Number/Function-
onBodyScroll在内容区域滚动的时候触发的函数签名:Function() => voidFunction-
expandedIndexSimulate开启时,getExpandedColProps() / rowProps() / expandedRowRender() 的第二个参数 index (该行所对应的序列) 将按照01,2,3,4…的顺序返回,否则返回真实index(0,2,4,6… / 1,3,5,7…)Booleanfalse

Table.Column

参数说明类型默认值
dataIndex指定列对应的字段,支持a.b形式的快速取值String-
cell行渲染的逻辑value, rowIndex, record, context四个属性只可读不可被更改Function(value, index, record) => ElementReactElement/ReactNode/Functionvalue => value
title表头显示的内容ReactElement/ReactNode/Function-
sortable是否支持排序Boolean-
width列宽,注意在锁列的情况下一定需要配置宽度Number/String-
align单元格的对齐方式可选值:'left', 'center', 'right'Enum-
alignHeader单元格标题的对齐方式, 不配置默认读取align值可选值:'left', 'center', 'right'Enum-
filters生成标题过滤的菜单, 格式为[{label:'xxx', value:'xxx'}]Array<Object>-
filterMode过滤的模式是单选还是多选可选值:'single', 'multiple'Enum'multiple'
filterMenuPropsfilter 模式下传递给 Menu 菜单的属性, 默认继承 Menu 组件的API属性:subMenuSelectable: {Boolean} 默认为false subMenu是否可选择isSelectIconRight: {Boolean} 默认为false 是否将选中图标居右。注意:SubMenu 上的选中图标一直居左,不受此API控制Object{ subMenuSelectable: false, }
lock是否支持锁列,可选值为left,right, trueBoolean/String-
resizable是否支持列宽调整, 当该值设为true,table的布局方式会修改为fixed.Booleanfalse

Table.ColumnGroup

参数说明类型默认值
title表头显示的内容ReactElement/ReactNode/Function'column-group'

Table.GroupHeader

参数说明类型默认值
cell行渲染的逻辑ReactElement/ReactNode/Function() => ''
hasChildrenSelection是否在Children上面渲染selectionBooleanfalse
hasSelection是否在GroupHeader上面渲染selectionBooleantrue
useFirstLevelDataWhenNoChildren当 dataSouce 里没有 children 时,是否使用内容作为数据Booleanfalse

Table.GroupFooter

参数说明类型默认值
cell行渲染的逻辑ReactElement/ReactNode/Function() => ''

代码示例

简单

简单的表格渲染

Table 表格 - 图1

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const dataSource = () => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. };
  13. const render = (value, index, record) => {
  14. return <a href="javascript:;">Remove({record.id})</a>;
  15. };
  16. ReactDOM.render(<Table dataSource={dataSource()}>
  17. <Table.Column title="Id" dataIndex="id"/>
  18. <Table.Column title="Title" dataIndex="title.name" />
  19. <Table.Column title="Time" dataIndex="time"/>
  20. <Table.Column cell={render}/>
  21. </Table>, mountNode);

可选择

表格可选择功能

Table 表格 - 图2

查看源码在线预览

  1. import { Table, Icon, MenuButton } from '@alifd/next';
  2. const { Item } = MenuButton;
  3. const dataSource = () => {
  4. const result = [];
  5. for (let i = 0; i < 5; i++) {
  6. result.push({
  7. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  8. id: 100306660940 + i,
  9. time: 2000 + i
  10. });
  11. }
  12. return result;
  13. };
  14. const render = (value, index, record) => {
  15. return <a href="javascript:;">Remove({record.id})</a>;
  16. };
  17. const onChange = (...args) => {
  18. console.log(args);
  19. };
  20. const selectItem = id => {
  21. console.log(id);
  22. };
  23. ReactDOM.render(<Table
  24. dataSource={dataSource()}
  25. rowSelection={{
  26. onChange: onChange,
  27. getProps: (record, index) => {
  28. console.log(record, index);
  29. return index === 2 ? {
  30. disabled: true,
  31. children: index
  32. } : {
  33. children: index
  34. };
  35. },
  36. columnProps: () => {
  37. return {
  38. lock: 'left',
  39. width: 90,
  40. align: 'center'
  41. };
  42. },
  43. titleAddons: () => {
  44. return <div>请选择</div>;
  45. },
  46. titleProps: () => {
  47. return {
  48. // remove the select all button
  49. // style: {display: 'none'},
  50. disabled: true,
  51. children:
  52. <MenuButton text onItemClick={selectItem} menuProps={{
  53. isSelectIconRight: true
  54. }} >
  55. <Item key="odd">odd</Item>
  56. <Item key="even">even</Item>
  57. </MenuButton>
  58. };
  59. }
  60. }}>
  61. <Table.Column title="Id" dataIndex="id" width={200}/>
  62. <Table.Column title="Title" dataIndex="title.name" width={200}/>
  63. <Table.Column title="Time" dataIndex="time" width={200}/>
  64. <Table.Column cell={render} width={200}/>
  65. </Table>, mountNode);

选择可控

演示全选和单选受控的功能

Table 表格 - 图3

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const dataSource = (i, j) => {
  3. const result = [];
  4. for (let a = i; a < j; a++) {
  5. result.push({
  6. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  7. id: 100306660940 + a,
  8. time: 2000 + a
  9. });
  10. }
  11. return result;
  12. },
  13. render = (value, index, record) => {
  14. return <a href="javascript:;">Remove({record.id})</a>;
  15. };
  16. class App extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. rowSelection: {
  21. onChange: this.onChange.bind(this),
  22. onSelect: function(selected, record, records) {
  23. console.log('onSelect', selected, record, records);
  24. },
  25. onSelectAll: function(selected, records) {
  26. console.log('onSelectAll', selected, records);
  27. },
  28. selectedRowKeys: [100306660940, 100306660941],
  29. getProps: (record) => {
  30. return {
  31. disabled: record.id === 100306660941
  32. };
  33. }
  34. },
  35. dataSource: dataSource(0, 5)
  36. };
  37. }
  38. onChange(ids, records) {
  39. const {rowSelection} = this.state;
  40. rowSelection.selectedRowKeys = ids;
  41. console.log('onChange', ids, records);
  42. this.setState({ rowSelection });
  43. }
  44. clear() {
  45. const {rowSelection} = this.state;
  46. rowSelection.selectedRowKeys = [];
  47. this.setState({ rowSelection });
  48. }
  49. toggleLoading() {
  50. this.setState({loading: !this.state.loading});
  51. }
  52. changeMode() {
  53. const {rowSelection} = this.state;
  54. const mode = rowSelection.mode;
  55. const selectedRowKeys = rowSelection.selectedRowKeys;
  56. rowSelection.mode = mode === 'single' ? 'multiple' : 'single';
  57. rowSelection.selectedRowKeys = selectedRowKeys.length === 1 ? selectedRowKeys : [];
  58. this.setState({ rowSelection });
  59. }
  60. modifyDataSource() {
  61. this.setState({
  62. dataSource: dataSource(9, 14)
  63. });
  64. }
  65. render () {
  66. return (
  67. <div>
  68. <p>
  69. <Button onClick={this.clear.bind(this)}>Clear Selection</Button>&nbsp;
  70. <Button onClick={this.changeMode.bind(this)}>Change mode</Button>&nbsp;
  71. <Button onClick={this.toggleLoading.bind(this)}>Toggle loading</Button>&nbsp;
  72. <Button onClick={this.modifyDataSource.bind(this)}>Modify dataSource</Button>
  73. </p>
  74. <Table dataSource={this.state.dataSource}
  75. loading={this.state.loading}
  76. rowSelection={this.state.rowSelection}>
  77. <Table.Column title="Id" dataIndex="id"/>
  78. <Table.Column title="Title" dataIndex="title.name" />
  79. <Table.Column title="Time" dataIndex="time"/>
  80. <Table.Column cell={render} width={200}/>
  81. </Table>
  82. </div>
  83. );
  84. }
  85. }
  86. ReactDOM.render(<App/>, mountNode);

排序与过滤

示例演示了排序和过滤的特性

Table 表格 - 图4

查看源码在线预览

  1. import { Table, Button, Icon } from '@alifd/next';
  2. const dataSource = () => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`,
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. },
  13. render = (value, index, record) => {
  14. return <a href="javascript:;">Remove({record.id})</a>;
  15. };
  16. class App extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. dataSource: dataSource(),
  21. filterMode: 'multiple'
  22. };
  23. }
  24. onSort(dataIndex, order) {
  25. const dataSource = this.state.dataSource.sort(function(a, b) {
  26. const result = a[dataIndex] - b[dataIndex];
  27. return (order === 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
  28. });
  29. this.setState({
  30. dataSource,
  31. sort: {id: order}
  32. });
  33. }
  34. onFilter(filterParams) {
  35. let ds = dataSource();
  36. Object.keys(filterParams).forEach(key => {
  37. const selectedKeys = filterParams[key].selectedKeys;
  38. if (selectedKeys.length) {
  39. ds = ds.filter(record => {
  40. return selectedKeys.some(value => {
  41. return record[key].indexOf(value) > -1;
  42. });
  43. });
  44. }
  45. });
  46. this.setState({dataSource: ds});
  47. }
  48. changeMode() {
  49. this.setState({
  50. filterMode: 'single'
  51. });
  52. }
  53. render() {
  54. const filters = [{
  55. label: 'Nano 3',
  56. value: 3
  57. }, {
  58. label: 'Nano 678',
  59. value: 678,
  60. children: [{
  61. label: 'Nano 67',
  62. value: 67,
  63. children: [{
  64. label: 'Nano 6',
  65. value: 6
  66. }, {
  67. label: 'Nano 7',
  68. value: 7
  69. }]
  70. }, {
  71. label: 'Nano 8',
  72. value: 8
  73. }]
  74. }, {
  75. label: 'Other',
  76. value: 'other',
  77. children: [{
  78. label: 'Nano 4',
  79. value: 4
  80. }, {
  81. label: 'Nano 5',
  82. value: 5
  83. }]
  84. }];
  85. return (
  86. <div>
  87. <p><Button onClick={this.changeMode.bind(this)}>Change filter menu to single select</Button></p>
  88. <Table dataSource={this.state.dataSource}
  89. onSort={this.onSort.bind(this)}
  90. onFilter={this.onFilter.bind(this)}>
  91. <Table.Column title="Id" dataIndex="id" sortable/>
  92. <Table.Column title="Title" dataIndex="title" filters={filters} filterMode={this.state.filterMode}/>
  93. <Table.Column title="Time" dataIndex="time"/>
  94. <Table.Column cell={render} width={200}/>
  95. </Table>
  96. <br />
  97. Customize sortIcons:
  98. <br />
  99. <Table dataSource={[]}
  100. onSort={() => {}}
  101. sortIcons={{
  102. desc: <Icon style={{top: '6px', left: '4px'}} type={'arrow-down'} size="small" />,
  103. asc: <Icon style={{top: '-6px', left: '4px'}} type={'arrow-up'} size="small" />
  104. }}>
  105. <Table.Column title="Id" dataIndex="id" sortable/>
  106. <Table.Column title="Title" dataIndex="title" filters={filters} filterMode={this.state.filterMode}/>
  107. <Table.Column title="Time" dataIndex="time"/>
  108. </Table>
  109. </div>
  110. );
  111. }
  112. }
  113. ReactDOM.render(<App/>, mountNode);

选择框属性

通过 rowSelection.getProps 来控制选择框属性

Table 表格 - 图5

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const onChange = function(...args) {
  3. console.log(...args);
  4. },
  5. dataSource = () => {
  6. const result = [];
  7. for (let i = 0; i < 5; i++) {
  8. result.push({
  9. title: {
  10. name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`
  11. },
  12. id: 100306660940 + i,
  13. time: 2000 + i
  14. });
  15. }
  16. return result;
  17. },
  18. render = (value, index, record) => {
  19. return <a>Remove({record.id})</a>;
  20. },
  21. rowSelection = {
  22. onChange: onChange,
  23. getProps: (record) => {
  24. return {
  25. disabled: record.id === 100306660942
  26. };
  27. }
  28. };
  29. ReactDOM.render(<Table dataSource={dataSource()}
  30. rowSelection={rowSelection}>
  31. <Table.Column title="Id" dataIndex="id"/>
  32. <Table.Column title="Title" dataIndex="title.name"/>
  33. <Table.Column title="Time" dataIndex="time"/>
  34. <Table.Column cell={render} width={200}/>
  35. </Table>, mountNode);

可展开

可以通过 expandedRowRender 额外渲染行

Table 表格 - 图6

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const dataSource = () => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`,
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. },
  13. render = (value, index, record) => {
  14. return <a>Remove({record.id})</a>;
  15. };
  16. class App extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. dataSource: dataSource()
  21. };
  22. }
  23. onSort(dataIndex, order) {
  24. const dataSource = this.state.dataSource.sort(function(a, b) {
  25. const result = a[dataIndex] - b[dataIndex];
  26. return (order === 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
  27. });
  28. this.setState({
  29. dataSource
  30. });
  31. }
  32. toggleIndent() {
  33. this.setState({
  34. expandedRowIndent: [2, 1]
  35. });
  36. }
  37. toggleCol() {
  38. this.setState({
  39. hasExpandedRowCtrl: false
  40. });
  41. }
  42. render() {
  43. return (<div>
  44. <p>
  45. <Button onClick={this.toggleIndent.bind(this)}> Update indent </Button>
  46. </p>
  47. <Table dataSource={this.state.dataSource}
  48. isZebra={this.state.isZebra}
  49. hasBorder={false}
  50. onSort={this.onSort.bind(this)}
  51. expandedRowRender={(record) => record.title}
  52. onRowClick={() => console.log('rowClick')}
  53. onExpandedRowClick={() => console.log('expandedRowClick')}
  54. expandedRowIndent={this.state.expandedRowIndent}
  55. >
  56. <Table.Column title="Id" dataIndex="id" sortable/>
  57. <Table.Column title="Title" dataIndex="title"/>
  58. <Table.Column title="Time" dataIndex="time"/>
  59. <Table.Column cell={render} width={200}/>
  60. </Table>
  61. </div>);
  62. }
  63. }
  64. ReactDOM.render(<App/>, mountNode);

可展开-复杂

可以通过 expandedRowRender 额外渲染行,但是会包含复杂的组件, 可通过 expandedIndexSimulate 设置 index 类型

Table 表格 - 图7

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. /*eslint-disable react/prop-types, react/no-multi-comp*/
  3. class ExpandedApp extends React.Component {
  4. constructor(props) {
  5. super(props);
  6. this.state = {
  7. dataSource: this.props.dataSource
  8. };
  9. }
  10. load() {
  11. let {dataSource} = this.state;
  12. dataSource = dataSource.concat(dataSource);
  13. this.setState({dataSource});
  14. }
  15. render() {
  16. const style = {
  17. borderTop: '1px solid #eee',
  18. textAlign: 'center',
  19. background: '#f8f8f8',
  20. lineHeight: '28px'
  21. };
  22. return (
  23. <div style={{marginTop: 10}}>
  24. <Table dataSource={this.state.dataSource} hasHeader={false} hasBorder={false}>
  25. <Table.Column title="Title" dataIndex="title"/>
  26. <Table.Column title="Time" dataIndex="time" width={200}/>
  27. </Table>
  28. <p style={style}
  29. onClick={this.load.bind(this)}>{this.props.index}: Load more data. </p>
  30. </div>
  31. );
  32. }
  33. }
  34. const dataSource = () => {
  35. const result = [];
  36. for (let i = 0; i < 5; i++) {
  37. result.push({
  38. title: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`,
  39. id: 100306660940 + i,
  40. time: 2000 + i,
  41. children: [{
  42. title: `Sub title for Quotation ${3 + i}`,
  43. time: 2000 + i
  44. }, {
  45. title: `Sub2 title for Quotation ${3 + i}`,
  46. time: 2000 + i
  47. }]
  48. });
  49. }
  50. return result;
  51. },
  52. render = (value, index, record) => {
  53. return <a>Remove({record.id})</a>;
  54. },
  55. expandedRowRender = (record, index) => {
  56. const children = record.children;
  57. return <ExpandedApp dataSource={children} index={index}/>;
  58. };
  59. class App extends React.Component {
  60. constructor(props) {
  61. super(props);
  62. this.state = {
  63. dataSource: dataSource(),
  64. hasBorder: false,
  65. openRowKeys: []
  66. };
  67. }
  68. onSort(dataIndex, order) {
  69. const dataSource = this.state.dataSource.sort(function(a, b) {
  70. const result = a[dataIndex] - b[dataIndex];
  71. return (order === 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
  72. });
  73. this.setState({
  74. dataSource
  75. });
  76. }
  77. disabledExpandedCol() {
  78. this.setState({
  79. getExpandedColProps: (record, index) => {
  80. console.log(index);
  81. if (index === 3) {
  82. return {
  83. disabled: true
  84. };
  85. }
  86. }
  87. });
  88. }
  89. toggleCol() {
  90. this.setState({
  91. hasExpandedRowCtrl: false
  92. });
  93. }
  94. onRowOpen(openRowKeys) {
  95. this.setState({ openRowKeys});
  96. }
  97. toggleExpand(record) {
  98. const key = record.id,
  99. { openRowKeys } = this.state,
  100. index = openRowKeys.indexOf(key);
  101. if (index > -1) {
  102. openRowKeys.splice(index, 1);
  103. } else {
  104. openRowKeys.push(key);
  105. }
  106. this.setState({
  107. openRowKeys: openRowKeys
  108. });
  109. }
  110. rowProps(record, index) {
  111. console.log('rowProps', record, index);
  112. return {className: `next-myclass-${index}`};
  113. }
  114. onExpandedRowClick(record, index) {
  115. console.log('onExpandedRowClick', record, index);
  116. }
  117. render() {
  118. const renderTitle = (value, index, record) => {
  119. return <div>{value}<span onClick={this.toggleExpand.bind(this, record)}>index:{index} +++++</span></div>;
  120. };
  121. return (
  122. <span>
  123. <p> <Button onClick={this.disabledExpandedCol.bind(this)}> disable fourth row </Button> &nbsp;
  124. <Button onClick={this.toggleCol.bind(this)}> hide + </Button></p>
  125. <Table dataSource={this.state.dataSource}
  126. expandedIndexSimulate
  127. isZebra={this.state.isZebra}
  128. hasBorder={this.state.hasBorder}
  129. onSort={this.onSort.bind(this)}
  130. expandedRowRender={expandedRowRender}
  131. expandedRowIndent={[1, 1]}
  132. openRowKeys={this.state.openRowKeys}
  133. getExpandedColProps={this.state.getExpandedColProps}
  134. hasExpandedRowCtrl={this.state.hasExpandedRowCtrl}
  135. onRowOpen={this.onRowOpen.bind(this)}
  136. rowProps={this.rowProps.bind(this)}
  137. onExpandedRowClick={this.onExpandedRowClick.bind(this)}
  138. >
  139. <Table.Column title="Id" dataIndex="id" sortable/>
  140. <Table.Column title="Title" dataIndex="title" cell={renderTitle}/>
  141. <Table.Column title="Time" dataIndex="time" width={200}/>
  142. <Table.Column cell={render} width={200}/>
  143. </Table>
  144. </span>
  145. );
  146. }
  147. }
  148. ReactDOM.render(<App/>, mountNode);

行列合并

通过 cellProps 进行列合并。

Table 表格 - 图8

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const onRowClick = function (record, index, e) {
  3. console.log(record, index, e);
  4. },
  5. dataSource = () => {
  6. const result = [];
  7. for (let i = 0; i < 5; i++) {
  8. result.push({
  9. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  10. id: 100306660940 + i,
  11. time: 2000 + i
  12. });
  13. }
  14. return result;
  15. },
  16. render = (value, index, record) => {
  17. return <a>Remove({record.id})</a>;
  18. },
  19. cellProps = (rowIndex, colIndex) => {
  20. if (rowIndex === 2 && colIndex === 1) {
  21. return {
  22. colSpan: 2,
  23. rowSpan: 3
  24. };
  25. }
  26. if (rowIndex === 1 && colIndex === 2) {
  27. return {
  28. colSpan: 2,
  29. rowSpan: 1
  30. };
  31. }
  32. };
  33. ReactDOM.render(<Table dataSource={dataSource()} onRowClick={onRowClick} cellProps={cellProps}>
  34. <Table.Column title="Id" dataIndex="id"/>
  35. <Table.Column title="Title" dataIndex="title.name" />
  36. <Table.Column title="Time" dataIndex="time"/>
  37. <Table.Column cell={render} width={200}/>
  38. </Table>, mountNode);

增删改查

演示对表格的增删改查

Table 表格 - 图9

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const onRowClick = function(record, index, e) {
  3. console.log(record, index, e);
  4. },
  5. dataSource = () => {
  6. const result = [];
  7. for (let i = 0; i < 5; i++) {
  8. result.push({
  9. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  10. id: 100306660940 + i,
  11. time: 2000 + i
  12. });
  13. }
  14. return result;
  15. };
  16. class App extends React.Component {
  17. state = {
  18. dataSource: dataSource()
  19. }
  20. onAdd = () => {
  21. const {dataSource} = this.state;
  22. dataSource.push({
  23. title: {
  24. name: 'Quotation for 1PCS Nano controller compatible'
  25. },
  26. id: Date.now(),
  27. time: 2000
  28. });
  29. this.setState({
  30. dataSource
  31. });
  32. }
  33. onRemove = (id) => {
  34. const {dataSource} = this.state;
  35. let index = -1;
  36. dataSource.forEach((item, i) => {
  37. if (item.id === id) {
  38. index = i;
  39. }
  40. });
  41. if (index !== -1) {
  42. dataSource.splice(index, 1);
  43. this.setState({
  44. dataSource
  45. });
  46. }
  47. }
  48. render() {
  49. const renderOper = (value, index, record) => {
  50. return <a onClick={this.onRemove.bind(this, record.id)}>Remove({record.id})</a>;
  51. };
  52. return (<div>
  53. <p><Button onClick={this.onAdd}>Add Item</Button></p>
  54. <Table dataSource={this.state.dataSource} onRowClick={onRowClick}>
  55. <Table.Column title="Id" dataIndex="id"/>
  56. <Table.Column title="Title" dataIndex="title.name" />
  57. <Table.Column title="Time" dataIndex="time"/>
  58. <Table.Column cell={renderOper} width="20%"/>
  59. </Table>
  60. </div>);
  61. }
  62. }
  63. ReactDOM.render(<App/>, mountNode);

固定表头

表格可以固定表头,支持sticky方式

Table 表格 - 图10

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const dataSource = (length) => {
  3. const result = [];
  4. for (let i = 0; i < length; i++) {
  5. result.push({
  6. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. };
  13. class App extends React.Component {
  14. state = {
  15. sticky: false,
  16. lock: false,
  17. dataSource: dataSource(50),
  18. }
  19. onSwitch(tag) {
  20. const props = {};
  21. switch (tag) {
  22. case 'sticky':
  23. props.sticky = true;
  24. break;
  25. case 'lock':
  26. props.lock = true;
  27. break;
  28. case 'dataSource':
  29. props.dataSource = this.state.dataSource.length > 0 ? [] : dataSource(50);
  30. break;
  31. default:
  32. break;
  33. }
  34. this.setState(props);
  35. }
  36. render() {
  37. return (<div>
  38. <p>
  39. <Button onClick={this.onSwitch.bind(this, 'sticky')}>enable sticky</Button> &nbsp;
  40. <Button onClick={this.onSwitch.bind(this, 'lock')}>enable lock</Button> &nbsp;
  41. <Button onClick={this.onSwitch.bind(this, 'dataSource')}>toggle dataSource</Button>
  42. </p>
  43. <Table dataSource={this.state.dataSource} fixedHeader stickyHeader={this.state.sticky}>
  44. <Table.Column title="Id" dataIndex="id" width={200} lock={this.state.lock}/>
  45. <Table.Column title="Title" dataIndex="title.name" width={200}/>
  46. <Table.Column title="Time" dataIndex="time" width={200}/>
  47. </Table>
  48. </div>
  49. );
  50. }
  51. }
  52. ReactDOM.render(<App/>, mountNode);

分组列表

分组列表展现

Table 表格 - 图11

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const dataSource = [{
  3. price: 'US $1',
  4. status: 1,
  5. parent: 'root',
  6. id: 1,
  7. product: [{
  8. title: '2014 New Fashion Novelty Tank Slim Women\'s Fashion Dresses With Lace',
  9. avatar: 'https://sc01.alicdn.com/kf/HTB1ravHKXXXXXccXVXXq6xXFXXXJ/Chinese-Style-Fashion-Custom-Digital-Print-Silk.jpg_220x220.jpg'
  10. }],
  11. children: [{
  12. price: 'US $1-1',
  13. status: 11,
  14. id: 2,
  15. parent: 0,
  16. index: 0,
  17. product: [{
  18. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  19. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  20. }]
  21. }, {
  22. price: 'US $1-2',
  23. status: 12,
  24. id: 3,
  25. parent: 0,
  26. product: [{
  27. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  28. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  29. }]
  30. }, {
  31. price: 'US $1-3',
  32. status: 13,
  33. id: 7,
  34. parent: 0,
  35. product: [{
  36. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  37. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  38. }]
  39. }]
  40. }, {
  41. price: 'US $3',
  42. status: 3,
  43. parent: 'root',
  44. id: 3,
  45. product: [{
  46. title: '2014 New Fashion Novelty Tank Slim Women\'s Fashion Dresses With Lace',
  47. avatar: 'https://sc01.alicdn.com/kf/HTB1ravHKXXXXXccXVXXq6xXFXXXJ/Chinese-Style-Fashion-Custom-Digital-Print-Silk.jpg_220x220.jpg'
  48. }],
  49. children: [{
  50. price: 'US $3-1',
  51. status: 31,
  52. id: 31,
  53. parent: 1,
  54. index: 0,
  55. product: [{
  56. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  57. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  58. }]
  59. }, {
  60. price: 'US $3-2',
  61. status: 32,
  62. id: 32,
  63. parent: 1,
  64. product: [{
  65. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  66. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  67. }]
  68. }, {
  69. price: 'US $3-3',
  70. status: 33,
  71. id: 33,
  72. parent: 1,
  73. product: [{
  74. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  75. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  76. }]
  77. }, {
  78. price: 'US $3-4',
  79. status: 34,
  80. id: 34,
  81. parent: 1,
  82. product: [{
  83. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  84. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  85. }]
  86. }]
  87. }, {
  88. price: 'US $4',
  89. status: 4,
  90. parent: 'root',
  91. id: 4,
  92. product: [{
  93. title: '2014 New Fashion Novelty Tank Slim Women\'s Fashion Dresses With Lace',
  94. avatar: 'https://sc01.alicdn.com/kf/HTB1ravHKXXXXXccXVXXq6xXFXXXJ/Chinese-Style-Fashion-Custom-Digital-Print-Silk.jpg_220x220.jpg'
  95. }],
  96. children: [{
  97. price: 'US $4-1',
  98. status: 31,
  99. id: 31,
  100. parent: 2,
  101. index: 0,
  102. product: [{
  103. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  104. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  105. }]
  106. }, {
  107. price: 'US $4-2',
  108. status: 32,
  109. id: 32,
  110. parent: 2,
  111. product: [{
  112. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  113. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  114. }]
  115. }, {
  116. price: 'US $4-3',
  117. status: 33,
  118. id: 33,
  119. parent: 2,
  120. product: [{
  121. title: 'Free shipping women Casual dresses lady dress plus size 2014',
  122. avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
  123. }]
  124. }]
  125. }],
  126. productRender = function(product) {
  127. return (<div className="media">
  128. <img src={product[0].avatar} className="media-side"/>
  129. <div className="media-content">{product[0].title}</div>
  130. </div>);
  131. },
  132. priceRender = function(price) {
  133. return <b>{price}</b>;
  134. },
  135. statusRender = function(status) {
  136. if (status) {
  137. return 'Already Priced';
  138. } else {
  139. return 'No Priced';
  140. }
  141. },
  142. operRender = function() {
  143. return <a href="javascript:;">View</a>;
  144. },
  145. groupHeaderRender = function(record) {
  146. return <div>{record.product[0].title}</div>;
  147. },
  148. rowSelection = {
  149. onChange: function(selectedKeys) {
  150. console.log(selectedKeys);
  151. }
  152. };
  153. const cellProps = (rowIndex, colIndex, dataIndex, record) => {
  154. if (colIndex === 3 && record.index === 0 ) {
  155. return {
  156. rowSpan: dataSource[record.parent].children.length
  157. };
  158. }
  159. if (colIndex === 4 && record.index === 0 ) {
  160. return {
  161. rowSpan: dataSource[record.parent].children.length
  162. };
  163. }
  164. };
  165. class App extends React.Component {
  166. state = {
  167. hasSelection: false
  168. }
  169. toggleGroupSelection = () => {
  170. this.setState({
  171. hasSelection: !this.state.hasSelection
  172. });
  173. }
  174. render() {
  175. return (
  176. <div>
  177. <p><Button onClick={this.toggleGroupSelection}>Toggle GroupHeader Selection</Button></p>
  178. <Table dataSource={dataSource} rowSelection={rowSelection} cellProps={cellProps}>
  179. <Table.GroupHeader cell={groupHeaderRender} hasChildrenSelection={this.state.hasSelection}/>
  180. <Table.GroupFooter cell={groupHeaderRender}/>
  181. <Table.Column cell={productRender} title="Product Details" dataIndex="product"/>
  182. <Table.Column cell={priceRender} title="Price" dataIndex="price" width={120}/>
  183. <Table.Column cell={statusRender} title="Status" dataIndex="status" width={100}/>
  184. <Table.Column cell={operRender} title="Operation" width={100}/>
  185. </Table>
  186. </div>
  187. );
  188. }
  189. }
  190. ReactDOM.render(<App/>, mountNode);
  1. .media-side{
  2. width:48px;
  3. height:48px;
  4. float:left;
  5. margin-right:10px;
  6. }
  7. .media-content{
  8. overflow: hidden;
  9. vertical-align: top;
  10. }
  11. .media{
  12. overflow: hidden;
  13. }

分页

与分页结合

Table 表格 - 图12

查看源码在线预览

  1. import { Table, Pagination } from '@alifd/next';
  2. const dataSource = (j) => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: { name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible` },
  7. id: 100306660940 + i + j,
  8. time: 2000 + j
  9. });
  10. }
  11. return result;
  12. },
  13. render = (value, index, record) => {
  14. return <a href="javascript:;">Remove({record.id})</a>;
  15. };
  16. class App extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. dataSource: dataSource(5)
  21. };
  22. }
  23. onChange = (currentPage) => {
  24. this.setState({
  25. loading: true
  26. });
  27. setTimeout(() => {
  28. this.setState({
  29. dataSource: dataSource(currentPage * 5),
  30. loading: false
  31. });
  32. }, 200);
  33. }
  34. render() {
  35. return (
  36. <div>
  37. <Table dataSource={this.state.dataSource}
  38. loading={this.state.loading}>
  39. <Table.Column title="Id1" dataIndex="id" width={140} />
  40. <Table.Column title="Time" dataIndex="time" width={500} />
  41. <Table.Column cell={render} width={200} />
  42. </Table>
  43. <Pagination onChange={this.onChange} className="page-demo" />
  44. </div>);
  45. }
  46. }
  47. ReactDOM.render(<App />, mountNode);
  1. .page-demo {
  2. margin-top:10px;
  3. }

多表头

多个表头

Table 表格 - 图13

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const onRowClick = function(record, index, e) {
  3. console.log(record, index, e);
  4. },
  5. dataSource = (j) => {
  6. const result = [];
  7. for (let i = 0; i < j; i++) {
  8. result.push({
  9. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  10. id: 100306660940 + i,
  11. time: 2000 + i
  12. });
  13. }
  14. return result;
  15. },
  16. render = (value, index, record) => {
  17. return <a>Remove({record.id})</a>;
  18. };
  19. class App extends React.Component {
  20. state = {
  21. dataSource: dataSource(200)
  22. }
  23. onClick = () => {
  24. this.setState({
  25. dataSource: dataSource(4)
  26. });
  27. }
  28. render() {
  29. return (
  30. <div>
  31. <p><Button onClick={this.onClick}>Reduce count</Button></p>
  32. <Table dataSource={this.state.dataSource} onRowClick={onRowClick} fixedHeader maxBodyHeight={400}>
  33. <Table.Column title="Title1" dataIndex="id" width={140}/>
  34. <Table.ColumnGroup title="Group2-7">
  35. <Table.Column title="Title2" dataIndex="id" lock width={140}/>
  36. <Table.Column title="Title3" dataIndex="title.name" width={200}/>
  37. <Table.ColumnGroup title="Group4-7">
  38. <Table.Column title="Title4" dataIndex="title.name" width={400}/>
  39. <Table.Column title="Title5" dataIndex="title.name" width={200}/>
  40. <Table.ColumnGroup title="Group6-7">
  41. <Table.Column title="Title6" dataIndex="title.name" width={400}/>
  42. <Table.Column title="Title7" dataIndex="title.name" width={200}/>
  43. </Table.ColumnGroup>
  44. </Table.ColumnGroup>
  45. </Table.ColumnGroup>
  46. <Table.ColumnGroup>
  47. <Table.Column title="Time" dataIndex="time" width={500}/>
  48. <Table.Column cell={render} width={200} lock="right"/>
  49. </Table.ColumnGroup>
  50. </Table>
  51. </div>);
  52. }
  53. }
  54. ReactDOM.render(<App/>, mountNode);

虚拟滚动

使用 useVirtual 开启虚拟滚动,scrollToRow 滚动到指定列

Table 表格 - 图14

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const dataSource = (j) => {
  3. const result = [];
  4. for (let i = 0; i < j; i++) {
  5. result.push({
  6. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  7. id: `100306660940${i}`,
  8. time: 2000 + i,
  9. index: i
  10. });
  11. }
  12. return result;
  13. };
  14. const render = (value, index, record) => {
  15. return <a>Remove({record.id})</a>;
  16. };
  17. class App extends React.Component {
  18. state = {
  19. scrollToRow: 20
  20. }
  21. onBodyScroll = (start) => {
  22. this.setState({
  23. scrollToRow: start
  24. });
  25. }
  26. render() {
  27. return (<Table dataSource={dataSource(1000)} maxBodyHeight={400} useVirtual scrollToRow={this.state.scrollToRow} onBodyScroll={this.onBodyScroll}>
  28. <Table.Column title="Id1" dataIndex="id" width={100}/>
  29. <Table.Column title="Index" dataIndex="index" width={200}/>
  30. <Table.Column title="Time" dataIndex="time" width={200}/>
  31. <Table.Column title="Time" dataIndex="time" width={200}/>
  32. <Table.Column title="Time" dataIndex="time" width={200} lock="right"/>
  33. <Table.Column cell={render} width={200} lock/>
  34. </Table>);
  35. }
  36. }
  37. ReactDOM.render(<App/>, mountNode);

锁列

演示表格锁列的功能

Table 表格 - 图15

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const onRowClick = function(record, index, e) {
  3. console.log(record, index, e);
  4. },
  5. dataSource = () => {
  6. const result = [];
  7. for (let i = 0; i < 100; i++) {
  8. result.push({
  9. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  10. id: 100306660940 + i,
  11. time: 2000 + i
  12. });
  13. }
  14. return result;
  15. },
  16. render = (value, index, record) => {
  17. return <a>Remove({record.id})</a>;
  18. };
  19. class App extends React.Component {
  20. state = {
  21. dataSource: [],
  22. cols: [
  23. <Table.Column title="Title" dataIndex="title.name" width={400} key="name1" lock/>,
  24. <Table.ColumnGroup title="abc" key="name-group">
  25. <Table.Column title="Title" dataIndex="title.name" width={100} key="name2"/>
  26. <Table.Column title="Title" dataIndex="title.name" width={400} key="name3"/>
  27. </Table.ColumnGroup>,
  28. <Table.Column title="Time" dataIndex="time" width={500} key="time"/>
  29. ],
  30. loading: true
  31. }
  32. componentDidMount() {
  33. setTimeout(() => {
  34. this.setState({
  35. dataSource: dataSource(),
  36. loading: false
  37. });
  38. }, 200);
  39. }
  40. reduceCol = () => {
  41. this.setState({
  42. cols: [<Table.Column title="Title" dataIndex="title.name" width={400} key="name1" lock/>,
  43. <Table.Column title="Time" dataIndex="time" width={100} key="time"/>]
  44. });
  45. }
  46. render() {
  47. return (
  48. <div>
  49. <p><Button onClick={this.reduceCol}>Reduce Cols</Button></p>
  50. <Table dataSource={this.state.dataSource} onRowClick={onRowClick} fixedHeader loading={this.state.loading}>
  51. <Table.Column title="Id-Id-Id-Id-Id-Id-Id-Id-Id-Id-Id-Id" dataIndex="id" lock width={140}/>
  52. {this.state.cols}
  53. <Table.Column cell={render} width={200}/>
  54. </Table>
  55. </div>
  56. );
  57. }
  58. }
  59. ReactDOM.render(<App/>, mountNode);

定制列

定制显示的表格列数

Table 表格 - 图16

查看源码在线预览

  1. import { Table, Button, Dialog, Checkbox } from '@alifd/next';
  2. const {Group} = Checkbox;
  3. const dataSource = () => {
  4. const result = [];
  5. for (let i = 0; i < 5; i++) {
  6. result.push({
  7. title: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`,
  8. id: 100306660940 + i,
  9. time: 2000 + i
  10. });
  11. }
  12. return result;
  13. },
  14. cols = [{
  15. title: 'id',
  16. dataIndex: 'id'
  17. }, {
  18. title: 'Title',
  19. dataIndex: 'title'
  20. }, {
  21. title: 'Time',
  22. dataIndex: 'time'
  23. }];
  24. class App extends React.Component {
  25. constructor (props) {
  26. super(props);
  27. this.state = {
  28. dataSource: dataSource(),
  29. cols: cols
  30. };
  31. }
  32. openDialog = () => {
  33. Dialog.alert({
  34. content: this.renderControlContent(),
  35. title: 'Select columns',
  36. onOk: () => {
  37. this.setState({
  38. cols: this.changedCols || this.state.cols
  39. });
  40. return true;
  41. }
  42. });
  43. }
  44. renderControlContent() {
  45. const groupSource = cols.map(col => {
  46. return {
  47. label: col.title,
  48. value: col.dataIndex
  49. };
  50. }), defaultValue = this.state.cols.map(col => col.dataIndex);
  51. return <Group dataSource={groupSource} onChange={this.onChange} defaultValue={defaultValue}/>;
  52. }
  53. onChange = (value) => {
  54. this.changedCols = cols.filter(col => value.indexOf(col.dataIndex) > -1);
  55. }
  56. renderCols() {
  57. const {cols} = this.state;
  58. return cols.map(col => {
  59. return <Table.Column title={col.title} dataIndex={col.dataIndex} key={col.dataIndex} />;
  60. });
  61. }
  62. render() {
  63. return (
  64. <div>
  65. <p><Button onClick={this.openDialog}> Select columns </Button></p>
  66. <Table dataSource={this.state.dataSource}>
  67. {this.renderCols()}
  68. </Table>
  69. </div>
  70. );
  71. }
  72. }
  73. ReactDOM.render(<App/>, mountNode);

样式

自定义表格边框

Table 表格 - 图17

查看源码在线预览

  1. import { Table, Button } from '@alifd/next';
  2. const dataSource = () => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`,
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. },
  13. render = (value, index, record) => {
  14. return <a>Remove({record.id})</a>;
  15. };
  16. class App extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. dataSource: dataSource(),
  21. className: '',
  22. align: 'left'
  23. };
  24. }
  25. toggleZebra() {
  26. this.setState({
  27. isZebra: !this.state.isZebra
  28. });
  29. }
  30. toggleBorder() {
  31. this.setState({
  32. hasBorder: !this.state.hasBorder
  33. });
  34. }
  35. makeBeauty() {
  36. this.setState({
  37. className: 'beauty'
  38. });
  39. }
  40. makeAlign() {
  41. this.setState({
  42. align: 'right'
  43. });
  44. }
  45. render() {
  46. return (<span>
  47. <p>
  48. <Button onClick={this.toggleZebra.bind(this)}> Toggle zebra </Button> &nbsp;
  49. <Button onClick={this.toggleBorder.bind(this)}> Toggle border</Button> &nbsp;
  50. <Button onClick={this.makeBeauty.bind(this)}> Make second column beauty </Button> &nbsp;
  51. <Button onClick={this.makeAlign.bind(this)}> Make first column align right </Button> &nbsp;
  52. </p>
  53. <Table dataSource={this.state.dataSource}
  54. isZebra={this.state.isZebra}
  55. hasBorder={this.state.hasBorder}>
  56. <Table.Column title="Id" dataIndex="id" alignHeader="center"/>
  57. <Table.Column title="Title" dataIndex="title" align={this.state.align} className={this.state.className}/>
  58. <Table.Column title="Time" dataIndex="time"/>
  59. <Table.Column cell={render} width={200}/>
  60. </Table>
  61. </span>);
  62. }
  63. }
  64. ReactDOM.render(<App/>, mountNode);
  1. .beauty{
  2. background: #f7f7f7;
  3. }

扩展

通过Table暴露的子组件进行扩展

Table 表格 - 图18

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. import PropTypes from 'prop-types';
  3. /* eslint-disable react/no-multi-comp,react/prop-types */
  4. const {Header, Cell} = Table;
  5. const dataSource = () => {
  6. const result = [];
  7. for (let i = 0; i < 5; i++) {
  8. result.push({
  9. title: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`,
  10. id: 100306660940 + i,
  11. time: 2000 + i
  12. });
  13. }
  14. return result;
  15. };
  16. const AppHeader = (props, context) => {
  17. const {columns} = props;
  18. const {onChange} = context;
  19. const length = columns[columns.length - 1].length;
  20. return (<Header {...props}>
  21. <tr>
  22. <Cell colSpan={length}>
  23. <a onClick={() => onChange(true)} href="javascript:;">Select all</a>&nbsp;
  24. <a onClick={() => onChange(false)} href="javascript:;">Unselect all</a>
  25. </Cell>
  26. </tr>
  27. </Header>);
  28. };
  29. AppHeader.contextTypes = {
  30. onChange: PropTypes.func
  31. };
  32. class App extends React.Component {
  33. static childContextTypes = {
  34. onChange: PropTypes.func
  35. }
  36. state = {
  37. selectedKeys: []
  38. }
  39. getChildContext() {
  40. return {
  41. onChange: this.onChange
  42. };
  43. }
  44. dataSource = dataSource()
  45. onChange = (checked) => {
  46. let selectedKeys = [];
  47. if (checked) {
  48. selectedKeys = this.dataSource.map(item => item.id);
  49. }
  50. this.onRowChange(selectedKeys);
  51. }
  52. onRowChange = (selectedKeys) => {
  53. this.setState({
  54. selectedKeys
  55. });
  56. }
  57. render() {
  58. return (<span>
  59. <Table dataSource={this.dataSource}
  60. components={{
  61. Header: AppHeader
  62. }}
  63. rowSelection={{
  64. selectedRowKeys: this.state.selectedKeys,
  65. onChange: this.onRowChange
  66. }}>
  67. <Table.Column title="Id" dataIndex="id" />
  68. <Table.Column title="Title" dataIndex="title"/>
  69. <Table.Column title="Time" dataIndex="time"/>
  70. </Table>
  71. </span>);
  72. }
  73. }
  74. ReactDOM.render(<App/>, mountNode);

重设列的尺寸

通过onResizeChange来让列宽可以调整

Table 表格 - 图19

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const onChange = function(...args) {
  3. console.log(...args);
  4. },
  5. dataSource = () => {
  6. const result = [];
  7. for (let i = 0; i < 5; i++) {
  8. result.push({
  9. title: {
  10. name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`
  11. },
  12. id: 100306660940 + i,
  13. time: 2000 + i
  14. });
  15. }
  16. return result;
  17. },
  18. render = (value, index, record) => {
  19. return <a>Remove({record.id})</a>;
  20. },
  21. rowSelection = {
  22. onChange: onChange,
  23. getProps: (record) => {
  24. return {
  25. disabled: record.id === 100306660942
  26. };
  27. }
  28. };
  29. class App extends React.Component {
  30. state = {
  31. widths: {
  32. id: 100
  33. }
  34. }
  35. onResizeChange = (dataIndex, value) => {
  36. const {widths} = this.state;
  37. widths[dataIndex] = widths[dataIndex] + value;
  38. this.setState({
  39. widths
  40. });
  41. }
  42. render() {
  43. return (<Table dataSource={dataSource()}
  44. rowSelection={rowSelection} onResizeChange={this.onResizeChange}>
  45. <Table.Column title="Id" dataIndex="id" resizable width={this.state.widths.id}/>
  46. <Table.Column title="Title" dataIndex="title.name" width={400}/>
  47. <Table.Column title="Time" dataIndex="time" width={600}/>
  48. <Table.Column cell={render} width={200}/>
  49. </Table>);
  50. }
  51. }
  52. ReactDOM.render(<App/>, mountNode);

混合模式

演示了tree模式和rowSelection模式混合

Table 表格 - 图20

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const data = [{
  3. key: 1,
  4. name: 'a',
  5. age: 32,
  6. address: 'aa',
  7. children: [{
  8. key: 11,
  9. name: 'b',
  10. age: 33,
  11. address: 'bb'
  12. }, {
  13. key: 12,
  14. name: 'c',
  15. age: 33,
  16. address: 'cc',
  17. children: [{
  18. key: 121,
  19. name: 'd',
  20. age: 33,
  21. address: 'dd'
  22. }]
  23. }, {
  24. key: 13,
  25. name: 'e',
  26. age: 33,
  27. address: 'ee',
  28. children: [{
  29. key: 131,
  30. name: 'f',
  31. age: 33,
  32. address: 'ff',
  33. children: [{
  34. key: 1311,
  35. name: 'g',
  36. age: 33,
  37. address: 'gg'
  38. }, {
  39. key: 1312,
  40. name: 'h',
  41. age: 33,
  42. address: 'hh'
  43. }]
  44. }]
  45. }]
  46. }, {
  47. key: 2,
  48. name: 'i',
  49. age: 32,
  50. address: 'ii',
  51. children: []
  52. }];
  53. const tableMixTree = (<Table dataSource={data} primaryKey="key" isTree rowSelection={{onChange: () => {}}}>
  54. <Table.Column title="Key" dataIndex="key"/>
  55. <Table.Column title="Name" dataIndex="name"/>
  56. <Table.Column title="Age" dataIndex="age" />
  57. <Table.Column title="Address" dataIndex="address"/>
  58. </Table>);
  59. const tableMixExpanded = (<Table dataSource={data}
  60. primaryKey="key"
  61. expandedRowRender={(record) => record.address}
  62. rowSelection={{onChange: () => {}}}>
  63. <Table.Column title="Key" dataIndex="key"/>
  64. <Table.Column title="Name" dataIndex="name"/>
  65. <Table.Column title="Age" dataIndex="age" />
  66. <Table.Column title="Address" dataIndex="address"/>
  67. </Table>);
  68. const tableMixSelectionTreeLock = (<div style={{width: '500px'}}>
  69. <Table dataSource={data} primaryKey="key" rowSelection={{onChange: () => {}}} isTree>
  70. <Table.Column title="Key" dataIndex="key" width={100}/>
  71. <Table.Column title="Name" dataIndex="name" lock width={100}/>
  72. <Table.Column title="Age" dataIndex="age" width={200} lock="right"/>
  73. <Table.Column title="Address" dataIndex="address" width={200}/>
  74. </Table>
  75. </div>);
  76. const tableMixLock = (<div style={{width: '500px'}}>
  77. <Table dataSource={data} primaryKey="key" rowSelection={{onChange: () => {}}}>
  78. <Table.Column title="Key" dataIndex="key" width={100}/>
  79. <Table.Column title="Name" dataIndex="name" lock width={100}/>
  80. <Table.Column title="Age" dataIndex="age" width={200} lock="right"/>
  81. <Table.Column title="Address" dataIndex="address" width={200}/>
  82. </Table>
  83. </div>);
  84. const tableMixExpandedLock = (<div style={{width: '500px'}}>
  85. <Table dataSource={data} primaryKey="key" rowSelection={{onChange: () => {}}} expandedRowRender={(record) => record.address} expandedRowIndent={[3, 1]}>
  86. <Table.Column title="Key" dataIndex="key" width={100}/>
  87. <Table.Column title="Name" dataIndex="name" lock width={100}/>
  88. <Table.Column title="Age" dataIndex="age" width={200} lock="right"/>
  89. <Table.Column title="Address" dataIndex="address" width={200}/>
  90. </Table>
  91. </div>);
  92. const tableMixTreeLock = (<div style={{width: '500px'}}>
  93. <Table dataSource={data} primaryKey="key" isTree>
  94. <Table.Column title="Key" dataIndex="key" width={100}/>
  95. <Table.Column title="Name" dataIndex="name" lock width={100}/>
  96. <Table.Column title="Age" dataIndex="age" width={200} lock="right"/>
  97. <Table.Column title="Address" dataIndex="address" width={200}/>
  98. </Table>
  99. </div>);
  100. ReactDOM.render(<div className="mix-demo">
  101. <div className="row">
  102. <h4>tree & select</h4>
  103. {tableMixTree}
  104. </div>
  105. <div className="row">
  106. <h4>extra & select</h4>
  107. {tableMixExpanded}
  108. </div>
  109. <div className="row">
  110. <h4>tree & lock column & select</h4>
  111. {tableMixSelectionTreeLock}
  112. </div>
  113. <div className="row">
  114. <h4>extra & lock column & select</h4>
  115. {tableMixExpandedLock}
  116. </div>
  117. <div className="row">
  118. <h4>lock column & select</h4>
  119. {tableMixLock}
  120. </div>
  121. <div className="row">
  122. <h4>tree & lock column</h4>
  123. {tableMixTreeLock}
  124. </div>
  125. </div>, mountNode);
  1. .mix-demo .row {
  2. margin-top:10px;
  3. }

自定义 Loading 组件

Table 表格 - 图21

查看源码在线预览

  1. import { Table, Loading, Icon } from '@alifd/next';
  2. const dataSource = () => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. };
  13. const render = (value, index, record) => {
  14. return <a href="javascript:;">Remove({record.id})</a>;
  15. };
  16. const indicator = (
  17. <div>
  18. <Icon type="loading" />
  19. </div>
  20. );
  21. const CustomLoading = (props) => (
  22. <Loading
  23. indicator={indicator}
  24. {...props}
  25. />
  26. );
  27. ReactDOM.render(
  28. <Table
  29. dataSource={dataSource()}
  30. loading
  31. loadingComponent={CustomLoading}
  32. >
  33. <Table.Column title="Id" dataIndex="id"/>
  34. <Table.Column title="Title" dataIndex="title.name" />
  35. <Table.Column title="Time" dataIndex="time"/>
  36. <Table.Column cell={render}/>
  37. </Table>,
  38. mountNode
  39. );

自定义Row/Cell

可以重写部分原生属性,比如className style onDoubleClick等。

Table 表格 - 图22

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const dataSource = () => {
  3. const result = [];
  4. for (let i = 0; i < 5; i++) {
  5. result.push({
  6. title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
  7. id: 100306660940 + i,
  8. time: 2000 + i
  9. });
  10. }
  11. return result;
  12. };
  13. const propsConf = {
  14. className: 'next-myclass',
  15. style: {background: 'black', color: 'white'},
  16. onDoubleClick: () => {
  17. console.log('doubleClicked');
  18. }
  19. };
  20. const setRowProps = (record, index) => {
  21. if (index === 2) {
  22. return propsConf;
  23. }
  24. };
  25. const setCellProps = (rowIndex, colIndex, dataIndex, record) => {
  26. if (rowIndex === 0 && colIndex === 0) {
  27. console.log(record);
  28. return propsConf;
  29. }
  30. };
  31. ReactDOM.render(<Table dataSource={dataSource()} rowProps={setRowProps} cellProps={setCellProps}>
  32. <Table.Column title="Id" dataIndex="id"/>
  33. <Table.Column title="Title" dataIndex="title.name" />
  34. <Table.Column title="Time" dataIndex="time"/>
  35. </Table>, mountNode);

可编辑的表格

单元格可编辑的表格

Table 表格 - 图23

查看源码在线预览

  1. import { Table, Input } from '@alifd/next';
  2. const result = [{
  3. id: '001',
  4. time: 1951,
  5. title: {name: 'The Old Man and the Sea'},
  6. }, {
  7. id: '002',
  8. time: 1925,
  9. title: {name: 'the great gatsby'},
  10. }, {
  11. id: '003',
  12. time: 1719,
  13. title: {name: 'The adventures of Robinson Crusoe'},
  14. }];
  15. class EditablePane extends React.Component {
  16. constructor(props) {
  17. super(props);
  18. this.state = {
  19. cellTitle: props.defaultTitle,
  20. editable: false
  21. };
  22. }
  23. componentWillReceiveProps(nextProps) {
  24. if (nextProps.defaultTitle !== this.state.cellTitle) {
  25. this.setState({
  26. cellTitle: nextProps.defaultTitle
  27. });
  28. }
  29. }
  30. onKeyDown = (e) => {
  31. const { keyCode } = e;
  32. // Stop bubble up the events of keyUp, keyDown, keyLeft, and keyRight
  33. if (keyCode > 36 && keyCode < 41) {
  34. e.stopPropagation();
  35. }
  36. }
  37. onBlur = (e) => {
  38. this.setState({
  39. editable: false,
  40. cellTitle: e.target.value
  41. });
  42. }
  43. onDblClick = () => {
  44. this.setState({
  45. editable: true
  46. });
  47. }
  48. render() {
  49. const { cellTitle, editable } = this.state;
  50. if (editable) {
  51. return <Input autoFocus defaultValue={cellTitle} onKeyDown={this.onKeyDown} onBlur={this.onBlur} />;
  52. }
  53. return <span onDoubleClick={this.onDblClick}>{cellTitle}</span>;
  54. }
  55. }
  56. class Demo extends React.Component {
  57. constructor(props) {
  58. super(props);
  59. this.state = {
  60. dataSource: result,
  61. id:''
  62. };
  63. }
  64. renderCell = (value, index, record) => {
  65. return <EditablePane defaultTitle={value}/>;
  66. }
  67. render() {
  68. return (<div>
  69. <Table dataSource={this.state.dataSource} >
  70. <Table.Column title="Id" dataIndex="id"/>
  71. <Table.Column title="Title" dataIndex="title.name" cell={this.renderCell}/>
  72. <Table.Column title="Time" dataIndex="time"/>
  73. </Table>
  74. </div>);
  75. }
  76. }
  77. ReactDOM.render(<Demo />, mountNode);

无障碍

通过键盘方向键浏览表格。

Table 表格 - 图24

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. const result = [{
  3. id: '001',
  4. time: 1951,
  5. title: {name: 'The Old Man and the Sea'},
  6. }, {
  7. id: '002',
  8. time: 1925,
  9. title: {name: 'the great gatsby'},
  10. }, {
  11. id: '003',
  12. time: 1719,
  13. title: {name: 'The adventures of Robinson Crusoe'},
  14. }];
  15. class Demo extends React.Component {
  16. constructor(props) {
  17. super(props);
  18. this.state = {
  19. dataSource: result,
  20. };
  21. }
  22. onRemove = (id) => {
  23. const {dataSource} = this.state;
  24. let index = -1;
  25. dataSource.forEach((item, i) => {
  26. if (item.id === id) {
  27. index = i;
  28. }
  29. });
  30. if (index !== -1) {
  31. dataSource.splice(index, 1);
  32. this.setState({
  33. dataSource
  34. });
  35. }
  36. }
  37. renderOper = (value, index, record) => {
  38. return <a onClick={this.onRemove.bind(this, record.id)}>Remove({record.id})</a>;
  39. };
  40. render() {
  41. return (<div>
  42. <Table dataSource={this.state.dataSource}>
  43. <Table.Column title="Id" dataIndex="id"/>
  44. <Table.Column title="Title" dataIndex="title.name" />
  45. <Table.Column title="Time" dataIndex="time"/>
  46. <Table.Column title="operate" cell={this.renderOper}/>
  47. </Table>
  48. </div>);
  49. }
  50. }
  51. ReactDOM.render(<Demo />, mountNode);

拖拽排序

可拖拽的表格。拖拽功能的实现依赖react-dnd@7.x 及react-dnd-html5-backend@7.x, 它要求react react-dom 版本高于16.3.x

Table 表格 - 图25

查看源码在线预览

  1. import { Table } from '@alifd/next';
  2. import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
  3. import HTML5Backend from 'react-dnd-html5-backend';
  4. import classnames from 'classnames';
  5. const { SelectionRow } = Table;
  6. let dragingIndex = -1;
  7. function MyRow (props) {
  8. const {
  9. isDragging,
  10. isOver,
  11. connectDragSource,
  12. connectDropTarget,
  13. moveRow,
  14. className,
  15. ...others
  16. } = props;
  17. const opacity = isDragging ? 0 : 1;
  18. const style = { ...others.style, cursor: 'move' };
  19. const cls = classnames({
  20. [className]: className,
  21. 'drop-over-upward': isOver && others.index < dragingIndex,
  22. 'drop-over-downward': isOver && others.index > dragingIndex,
  23. });
  24. return (<SelectionRow {...others}
  25. style={{ ...style, ...{ opacity } }}
  26. className={cls}
  27. wrapper={(row) => connectDragSource(connectDropTarget(row))} />);
  28. }
  29. const NewRow = DropTarget(
  30. 'row',
  31. {
  32. drop(props, monitor) {
  33. const dragIndex = monitor.getItem().index;
  34. const hoverIndex = props.index;
  35. if (dragIndex === hoverIndex) {
  36. return;
  37. }
  38. props.moveRow(dragIndex, hoverIndex);
  39. monitor.getItem().index = hoverIndex;
  40. },
  41. },
  42. (connect, monitor) => ({
  43. connectDropTarget: connect.dropTarget(),
  44. isOver: monitor.isOver(),
  45. }),
  46. )(
  47. DragSource(
  48. 'row',
  49. {
  50. beginDrag: props => {
  51. dragingIndex = props.index;
  52. return {
  53. id: props.record[props.primaryKey],
  54. index: props.rowIndex,
  55. };
  56. },
  57. },
  58. (connect, monitor) => ({
  59. connectDragSource: connect.dragSource(),
  60. isDragging: monitor.isDragging(),
  61. }),
  62. )(MyRow),
  63. );
  64. class InnerTable extends React.Component {
  65. constructor(props) {
  66. super(props);
  67. this.state = {
  68. dataSource: [...props.dataSource],
  69. };
  70. }
  71. componentWillReceiveProps(nextProps) {
  72. if (nextProps.dataSource && JSON.stringify(nextProps.dataSource) !==
  73. JSON.stringify(this.state.dataSource)) {
  74. this.setState({ dataSource: [...nextProps.dataSource] });
  75. }
  76. }
  77. moveRow = (dragIndex, hoverIndex) => {
  78. const { onSort } = this.props;
  79. const dragRow = this.state.dataSource[dragIndex];
  80. const dataSource = [...this.state.dataSource];
  81. dataSource.splice(dragIndex, 1);
  82. dataSource.splice(hoverIndex, 0, dragRow);
  83. this.setState({
  84. dataSource,
  85. });
  86. onSort && onSort(this.state.dataSource);
  87. };
  88. render() {
  89. const { excludeProvider, ...restProps } = this.props;
  90. const tableProps = {
  91. ...restProps,
  92. dataSource: this.state.dataSource,
  93. rowProps: (props, index) => ({
  94. index,
  95. moveRow: this.moveRow,
  96. }),
  97. components: {
  98. Row: NewRow
  99. },
  100. };
  101. return <Table {...tableProps} />;
  102. }
  103. }
  104. class SortableTable extends React.Component {
  105. render() {
  106. const ComponentName = DragDropContext(HTML5Backend)(InnerTable);
  107. return <ComponentName {...this.props} />;
  108. }
  109. }
  110. const result = [{
  111. id: '001',
  112. time: 1951,
  113. title: {name: 'The Old Man and the Sea'},
  114. }, {
  115. id: '002',
  116. time: 1925,
  117. title: {name: 'the great gatsby'},
  118. }, {
  119. id: '003',
  120. time: 1719,
  121. title: {name: 'The adventures of Robinson Crusoe'},
  122. }];
  123. class Demo extends React.Component {
  124. constructor(props) {
  125. super(props);
  126. this.state = {
  127. dataSource: result,
  128. };
  129. }
  130. onRemove = (id) => {
  131. const {dataSource} = this.state;
  132. let index = -1;
  133. dataSource.forEach((item, i) => {
  134. if (item.id === id) {
  135. index = i;
  136. }
  137. });
  138. if (index !== -1) {
  139. dataSource.splice(index, 1);
  140. this.setState({
  141. dataSource
  142. });
  143. }
  144. }
  145. renderOper = (value, index, record) => {
  146. return <a onClick={this.onRemove.bind(this, record.id)}>Remove({record.id})</a>;
  147. };
  148. render() {
  149. return (<div>
  150. <SortableTable dataSource={this.state.dataSource}>
  151. <Table.Column title="Id" dataIndex="id" width={100} lock/>
  152. <Table.Column title="Title" dataIndex="title.name" width={400} />
  153. <Table.Column title="Time" dataIndex="time" width={300}/>
  154. <Table.Column title="operate" cell={this.renderOper} width={300} lock="right"/>
  155. </SortableTable>
  156. </div>);
  157. }
  158. }
  159. ReactDOM.render(<Demo />, mountNode);
  1. .drop-over-downward{
  2. border-bottom: 2px dashed #3080fe;
  3. }
  4. .drop-over-upward{
  5. border-top: 2px dashed #3080fe;
  6. }

相关区块

Table 表格 - 图26

暂无相关区块