组件规范 前端代码

Table 表格

Guide#

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

基本使用#

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

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

const dataSource = [{id: 1, time: '2016'}];
ReactDOM.render(<Table dataSource={dataSource}>
    <Table.Column title="Id" dataIndex="id"/>
    <Table.Column title="Time" dataIndex="time"/>
</Table>, mountNode)

列配置#

Table.Column提供了非常多的配置属性用于自定义列,最常见的就是使用cell自定义单元格的渲染逻辑. 其他的配置选项可以参考下面的Table.Column的API

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

const dataSource = [{id: 1, time: '2016'}];
const renderTime = value => {
    if (value == '2016') {
        return '今年';
    }
    return value;
}
ReactDOM.render(<Table dataSource={dataSource}>
    <Table.Column title="Id" dataIndex="id"/>
    <Table.Column title="Time" dataIndex="time" cell={renderTime}/>
</Table>, mountNode)

多表头#

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

const dataSource = [{id: 1, time: '2016'}];
ReactDOM.render(<Table dataSource={dataSource}>
    <Table.ColumnGroup>
        <Table.Column title="Id" dataIndex="id"/>
        <Table.Column title="Time" dataIndex="time"/>
    </Table.ColumnGroup>
    <Table.ColumnGroup>
        <Table.Column title="Id" dataIndex="id"/>
    </Table.ColumnGroup>
</Table>, mountNode)

性能问题#

由于React的机制问题,在做与Table无关的更新的时候,可能会导致diff计算花费大量的时间,

在你确认只有props和state才能影响Table渲染的情况下,可以设置optimizationtrue来开启, 原理就是通过

shouldComponentUpdate的生命周期来对比props和state的变更,开启了该选项后可能导致下面的副作用。

class App extends React.Component{
    state = {
        extra: 'abc'
    }
    cellRender = (value) => {
        return value + this.state.extra;
    }
    render(){
        return <Table dataSource={[{id: 1}]}>
            <Table.Column cell={this.cellRender} dataIndex="id"/>
        </Table>
    }
    componentDidMount(){
        setTimeout(() => {
            this.setState({
                extra: 'bcd'
            })
        },1000)
    }
}

上面的代码在componentDidMount之后的setState虽然更新了extra, 但是并不会触发Table的重新渲染。

解决方式如下:

  1. 将cellRender访问的state通过props的方式传入。

    class App extends React.Component{
        state = {
            extra: 'abc'
        }
        cellRender = (value, index, record, context) => {
            return value + context.props.extra;
        }
        render(){
            return <Table dataSource={[{id: 1}]} extra={this.state.extra}>
                <Table.Column cell={this.cellRender} dataIndex="id"/>
            </Table>
        }
        componentDidMount(){
            setTimeout(() => {
                this.setState({
                    extra: 'bcd'
                })
            },1000)
        }
    }
  2. 通过设置optimizationfalse来关闭Table的shouldComponentUpdate配置。

    class App extends React.Component{
        state = {
            extra: 'abc'
        }
        cellRender = (value, index, record, context) => {
            return value + this.state.extra;
        }
        render(){
            return <Table dataSource={[{id: 1}]} optimization={false}>
                <Table.Column cell={this.cellRender} dataIndex="id"/>
            </Table>
        }
        componentDidMount(){
            setTimeout(() => {
                this.setState({
                    extra: 'bcd'
                })
            },1000)
        }
    }

API#

Table#

Table的子元素必须是下面三个元素中的一种

属性说明类型默认值
dataSource表格展示的数据源Array[]
rowSelection是否启用Checkbox多选,详见rowSelectionObject{}
getRowClassName获取每一行的样式名Function(record, index):Stringnoop
getCellProps获取该单元格的属性Function(rowIndex, colIndex, record):Objectnoop
fixedHeader是否固定表头Booleanfalse
maxBodyHeight最大内容区域的高度,在fixedHeadertrue的时候,超过这个高度会出现滚动条Number200
hasBorder表格是否具有边框Booleantrue
hasHeader表格是否具有头部Booleantrue
isZebra表格是否是斑马线Booleanfalse
isLoading表格是否在加载中Booleanfalse
primaryKeydataSource当中数据的主键Stringid
filterParams当前过滤的的keys,使用此属性可以控制表格的头部的过滤选项中哪个菜单被选中,格式为 {dataIndex: {selectedKeys:[]}}Objectnull
sort当前排序的字段,使用此属性可以控制表格的字段的排序,格式为{dataIndex: 'asc'}Objectnull
expandedRowRender额外渲染的行的函数Function(record, index)noop
expandedRowKeys默认展开的额外的行, 传入此属性为受控状态Arraynull
expandedRowIndent配置默认展开行的缩进Array[left,right][1,0]
hasExpandedRowCtrl是否显示+号按钮Booleantrue
onExpandedChange受控的时候触发的事件Function(expandedRowKeys)noop
onExpandedRowClick额外行点击触发的事件Function(record, index, e)noop
onRowClick每一行点击触发的事件Function(record, index, e)noop
onRowMouseEnter每一行鼠标悬浮的时候出发的事件Function(record, index, e)noop
onRowMouseLeave每一行鼠标离开的时候出发的事件Function(record, index, e)noop
onSort点击列排序触发的事件Function(dataIndex, order, sort)noop
onFilter点击过滤触发的事件Function(filterKeys)noop
isTree开启Table的tree模式,接收的数据格式中包含children则渲染成tree tableBooleanfalse
indentSize在tree模式下的缩进尺寸Number12
openRowKeys传入了此属性代表tree的展开为受控操作Arraynull
onRowOpen点击tree展开或者关闭的时候触发的事件Function(openRowKeys:Array)noop
optimization是否开启性能优化Booleantrue

rowSelection#

属性说明类型默认值
getProps获取selection的默认属性Function(record):Objectnoop
onChange选择改变的时候触发的事件Function(selectedRowKeys:Array, records:Array)null
onSelect用户手动选择/取消选择某列的回调Function(selected:Boolean, record:Object, records:Array)null
onSelectAll用户手动选择/取消选择所有列的回调Function(selected:Boolean, records:Array)null
selectedRowKeys设置了此属性,将rowSelection变为受控状态,接收值为该行数据的primaryKey的值Arraynull
mode选择selection的模式, 可选值为single,multipleStringmultiple

Table.Column#

属性说明类型默认值
dataIndex指定单元格渲染的keyStringnull
cell行渲染的逻辑Function(value, index, record, context), ReactElementnull
title表头显示的内容String, ReactElement''
sortable是否支持排序Booleanfalse
filters生成标题过滤的菜单,格式为[{label:'xxx', value:'xxx'}]Arrayfalse
filterMode过滤的模式是单选还是多选,可选值为single,multipleStringmultiple
lock是否支持锁列,可选值为left,right, trueBoolean, Stringfalse
width在锁列的情况下需要配置的宽度Numbernull

Table.ColumnGroup#

属性说明类型默认值
title表头显示的内容String, ReactElement''

Table.GroupHeader#

如果Table的子元素包含GroupHeader,则开启GroupList模式

属性说明类型默认值
cell行渲染的逻辑Function(value, index, record), React Elementnull

代码演示

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onRowClick = function(record, index, e){
    console.log(record, index, e);
  },
  getData = () =>{
    let result = [];
    for(let i = 0; i< 5; i++){
      result.push({
          title:{
            name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
          id:100306660940+i,
          time: 2000 + i
        })
    }
    return result;
},
render= (value, index, record) => {
    return <a>Remove({record.id})</a>;
}

ReactDOM.render(<Table dataSource={getData()} onRowClick={onRowClick}>
    <Table.Column title="Id" dataIndex="id"/>
    <Table.Column title="Title" dataIndex="title.name" />
    <Table.Column title="Time" dataIndex="time"/>
    <Table.Column cell={render} width="40%"/>
</Table>, mountNode);

简单的表格渲染

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onChange = function(...args){
    console.log(...args);
},
getData = () =>{
    let result = [];
    for(let i = 0; i< 5; i++){
        result.push({
            title:{
                name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
            id:100306660940+i,
            time: 2000 + i
        })
    }
    return result;
},
render= (value, index, record) => {
    return <a>Remove({record.id})</a>;
},
rowSelection = {
    onChange: onChange,
    getProps: (record) =>{
        return {
            disabled: record.id == 100306660942
        }
    }
}

ReactDOM.render(<Table dataSource={getData()}
                       rowSelection={rowSelection}>
    <Table.Column title="Id" dataIndex="id"/>
    <Table.Column title="Title" dataIndex="title.name"/>
    <Table.Column title="Time" dataIndex="time"/>
    <Table.Column cell={render} width={200}/>
</Table>, mountNode);

通过getProps来控制选择框属性

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';
import Button from 'blue/lib/button';

const onRowClick = function(record, index, e){
    console.log(record, index, e);
  },
  getData = () => { 
    let result = [];
    for(let i = 0; i< 5; i++){
      result.push({
          title:{
            name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
          id:100306660940+i,
          time: 2000 + i
        })
    }
    return result;
};


class App extends React.Component {
    state = {
        dataSource: getData()
    }
    render() {
        const renderOper = (value, index, record) => {
            return <a onClick={this.onRemove.bind(this, record.id)}>Remove({record.id})</a>;
        }
        return <div>
                <p><Button onClick={this.onAdd}>Add Item</Button></p>
                <Table dataSource={this.state.dataSource} onRowClick={onRowClick}>
                    <Table.Column title="Id" dataIndex="id"/>
                    <Table.Column title="Title" dataIndex="title.name" />
                    <Table.Column title="Time" dataIndex="time"/>
                    <Table.Column cell={renderOper} width="20%"/>
                </Table>
            </div>
    }
    onAdd = () => {
        // 注意在没有通过shouldComponentUpdate判断的时候可以使用此写法
        // 否则注意数组和对象的引用关系
        const {dataSource} = this.state;
        dataSource.push({
            title:{
                name: `Quotation for 1PCS Nano controller compatible`,
            },
            id: Date.now(),
            time: 2000 
        })
        this.setState({
            dataSource
        });
    }

    onRemove = (id) => {
        const {dataSource} = this.state;
        let index = -1;
        dataSource.forEach((item, i) => {
            if (item.id == id) {
                index = i;
            }
        })
        if (index != -1) {
            dataSource.splice(index, 1);
            this.setState({
                dataSource
            })
        }
    }
}

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

演示对表格的增删改查

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

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

const tableMixTree = <Table dataSource={data} primaryKey="key" isTree rowSelection={{onChange: () => {}}}>
    <Table.Column title="Key" dataIndex="key"/>
    <Table.Column title="Name" dataIndex="name"/>
    <Table.Column title="Age" dataIndex="age" />
    <Table.Column title="Address" dataIndex="address"/>
</Table>

const tableMixExpanded = <Table dataSource={data} 
                        primaryKey="key" 
                        expandedRowRender = {(record)=> record.address} 
                        rowSelection={{onChange: () => {}}}>
    <Table.Column title="Key" dataIndex="key"/>
    <Table.Column title="Name" dataIndex="name"/>
    <Table.Column title="Age" dataIndex="age" />
    <Table.Column title="Address" dataIndex="address"/>
</Table>

const tableMixTreeLock = <div style={{width:'500px'}}>
<Table dataSource={data} primaryKey="key" rowSelection={{onChange: () => {}}} isTree>
    <Table.Column title="Key" dataIndex="key" width={100}/>
    <Table.Column title="Name" dataIndex="name" lock width={100}/>
    <Table.Column title="Age" dataIndex="age" width={200} lock="right"/>
    <Table.Column title="Address" dataIndex="address" width={200}/>
</Table>
</div>

const tableMixLock = <div style={{width:'500px'}}>
<Table dataSource={data} primaryKey="key" rowSelection={{onChange: () => {}}}>
    <Table.Column title="Key" dataIndex="key" width={100}/>
    <Table.Column title="Name" dataIndex="name" lock width={100}/>
    <Table.Column title="Age" dataIndex="age" width={200} lock="right"/>
    <Table.Column title="Address" dataIndex="address" width={200}/>
</Table>
</div>

ReactDOM.render(<div className="mix-demo">
    <div className="row">{tableMixTree}</div>
    <div className="row">{tableMixExpanded}</div>
    <div className="row">{tableMixTreeLock}</div>
    {tableMixLock}
    </div>, mountNode);
.mix-demo .row {
  margin-top:10px;
}

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

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onRowClick = function(record, index, e){
    console.log(record, index, e);
  },
  getData = () =>{
    let result = [];
    for(let i = 0; i< 50; i++){
      result.push({
          title:{
            name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
          id:100306660940+i,
          time: 2000 + i
        })
    }
    return result;
},
render= (value, index, record) => {
    return <a>Remove({record.id})</a>;
}

class App extends React.Component{
    state = {
        dataSource: []
    }
    render(){
        return <Table dataSource={this.state.dataSource} onRowClick={onRowClick} fixedHeader>
                <Table.Column title="Id-Id-Id-Id-Id-Id-Id-Id-Id-Id-Id-Id" dataIndex="id" lock width={140}/>
                <Table.Column title="Title" dataIndex="title.name" width={400}/>
                <Table.Column title="Title" dataIndex="title.name" width={100}/>
                <Table.Column title="Title" dataIndex="title.name" width={400}/>
                <Table.Column title="Time" dataIndex="time" width={500}/>
                <Table.Column cell={render} width={200} lock="right"/>
            </Table>
    }
    componentDidMount(){
        setTimeout(()=>{
            this.setState({
                dataSource: getData()
            })
        }, 200)
    }
}

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

演示表格锁列的功能

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onRowClick = function(record, index, e){
    console.log(record, index, e);
  },
  getData = () =>{
    let result = [];
    for(let i = 0; i< 5; i++){
      result.push({
          title:{
            name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
          id:100306660940+i,
          time: 2000 + i
        })
    }
    return result;
},
render= (value, index, record) => {
    return <a>Remove({record.id})</a>;
},
getCellProps = (rowIndex, colIndex) => {
  if(rowIndex == 2 && colIndex == 1){
    return {
      colSpan: 2,
      rowSpan: 3
    }
  }
  if(rowIndex == 1 && colIndex == 2) {
     return {
       colSpan: 2,
       rowSpan: 1
     }
  }
}

ReactDOM.render(<Table dataSource={getData()} onRowClick={onRowClick} getCellProps={getCellProps}>
    <Table.Column title="Id" dataIndex="id"/>
    <Table.Column title="Title" dataIndex="title.name" />
    <Table.Column title="Time" dataIndex="time"/>
    <Table.Column cell={render} width={200}/>
</Table>, mountNode);

通过getCellProps进行列合并。

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onChange = function(...args){
          console.log(...args);
      },
      getData = () =>{
        let result = [];
        for(let i = 0; i< 5; i++){
          result.push({
              title:{
                name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
                },
              id:100306660940+i,
              time: 2000 + i
            })
        }
        return result;
    },
    render= (value, index, record) => {
        return <a>Remove({record.id})</a>;
    };

ReactDOM.render(<Table dataSource={getData()}
                       rowSelection={{onChange: onChange}}>
    <Table.Column title="Id" dataIndex="id"/>
    <Table.Column title="Title" dataIndex="title.name"/>
    <Table.Column title="Time" dataIndex="time"/>
    <Table.Column cell={render} width={200}/>
</Table>, mountNode);

表格可选择功能

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';
import Button from 'blue/lib/button';

const getData = (i,j) =>{
        let result = [];
        for (let k =i; k < j; k++) {
          result.push({
              title:{
                name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
                },
              id:100306660940+k,
              time: 2000 + k
            })
        }
        return result;
    },
    render= (value, index, record) => {
        return <a>Remove({record.id})</a>;
    };

class App extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      rowSelection: {
        onChange: this.onChange.bind(this),
        onSelect: function(selected, record, records){ console.log('onSelect',selected, record, records) },
        onSelectAll: function(selected, records){ console.log('onSelectAll', selected, records) },
        selectedRowKeys: []
      },
      dataSource: getData(0, 5)
    }
  }
  render(){
    return <span>
        <p><Button onClick={this.clear.bind(this)}>Clear Selection</Button>
        <Button onClick={this.changeMode.bind(this)}>Switch single mode</Button>
        <Button onClick={this.toggleLoading.bind(this)}>Toggle loading</Button>
        <Button onClick={this.modifyDataSource.bind(this)}>Modify dataSource</Button>
        </p>
        <Table dataSource={this.state.dataSource}
                           isLoading = {this.state.isLoading}
                           rowSelection={this.state.rowSelection}>
            <Table.Column title="Id" dataIndex="id"/>
            <Table.Column title="Title" dataIndex="title.name" />
            <Table.Column title="Time" dataIndex="time"/>
            <Table.Column cell={render} width={200}/>
        </Table>
    </span>
  }
  onChange(ids, records){
    let {rowSelection} = this.state;
    rowSelection.selectedRowKeys = ids;
    console.log('onChange', ids, records);
    this.setState({ rowSelection });
  }
  clear(){
    let {rowSelection} = this.state;
    rowSelection.selectedRowKeys = [];
    this.setState({ rowSelection });
  }
  toggleLoading(){
    this.setState({isLoading: !this.state.isLoading});
  }
  changeMode(){
    let {rowSelection} = this.state;
    rowSelection.mode = 'single';
    this.setState({ rowSelection });
  }
  modifyDataSource(){
    this.setState({
      dataSource: getData(9, 14)
    })
  }
}

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

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

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onChange = function(...args){
          console.log(...args);
      },
      getData = () =>{
        let result = [];
        for(let i = 0; i< 5; i++){
          result.push({
              title:`Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
              id:100306660940+i,
              time: 2000 + i
            })
        }
        return result;
    },
    render= (value, index, record) => {
        return <a>Remove({record.id})</a>;
    };

class App extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      dataSource: getData(),
      filterMode: 'multiple'
    }
  }
  render(){
    let filters = [{
        label: 'Nano 包含3',
        value: 3
    },{
        label: 'Nano 包含2',
        value: 2,
        children: [{
            label: 'Nano 包含12',
            value: 22
        },{
            label: 'Nano 包含23',
            value: 23
        }]
    },{
        label:'其他',
        children: [{
            label: 'Nano 包含4',
            value: 4
        },{
            label: 'Nano 包含5',
            value: 5
        }]
    }]
    return <span>
        <p><button onClick={this.changeMode.bind(this)}>切换过滤为单选模式</button></p>
        <Table dataSource={this.state.dataSource}
                           onSort = {this.onSort.bind(this)}
                           onFilter = {this.onFilter.bind(this)}>
            <Table.Column title="Id" dataIndex="id" sortable/>
            <Table.Column title="Title" dataIndex="title" filters={filters} filterMode={this.state.filterMode}/>
            <Table.Column title="Time" dataIndex="time"/>
            <Table.Column cell={render} width={200}/>
        </Table>
    </span>
  }
  onSort(dataIndex, order, sort){
    let dataSource = this.state.dataSource.sort(function(a, b){
      let result = a[dataIndex] - b[dataIndex];
      return  (order == 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
    });
    this.setState({
        dataSource
    });
  }
  onFilter(filterParams){
    let  dataSource = getData(),
         result = [];

    for(let key in filterParams){
        let selectedKeys = filterParams[key].selectedKeys;
        if(!selectedKeys.length){

        }else{
            dataSource = dataSource.filter(record=>{
                return selectedKeys.some(value=>{
                    return record[key].indexOf(value) > -1
                });
            })
        }
    }
    this.setState({dataSource});
  }
  changeMode(){
        this.setState({
            filterMode: 'single'
        })
  }
}

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

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

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onChange = function(...args){
          console.log(...args);
      },
      getData = () =>{
        let result = [];
        for(let i = 0; i< 5; i++){
          result.push({
              title:`Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
              id:100306660940+i,
              time: 2000 + i
            })
        }
        return result;
    },
    render= (value, index, record) => {
        return <a>Remove({record.id})</a>;
    };

class App extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      dataSource: getData()
    }
  }
  render(){
    return <span>
        <p>
            <button onClick={this.toggleIndent.bind(this)}> 设置缩进为左二右一 </button>
        </p>
        <Table dataSource={this.state.dataSource}
               isZebra = {this.state.isZebra}
               hasBorder = {false}
               onSort = {this.onSort.bind(this)}
               expandedRowRender = {(record)=> record.title}
               onRowClick = {()=>console.log('rowClick')}
               onExpandedRowClick = {()=>console.log('expandedRowClick')}
               expandedRowIndent = {this.state.expandedRowIndent}
               >
            <Table.Column title="Id" dataIndex="id" sortable/>
            <Table.Column title="Title" dataIndex="title"/>
            <Table.Column title="Time" dataIndex="time"/>
            <Table.Column cell={render} width={200}/>
        </Table>
    </span>
  }
  onSort(dataIndex, order, sort){
    let dataSource = this.state.dataSource.sort(function(a, b){
      let result = a[dataIndex] - b[dataIndex];
      return  (order == 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
    });
    this.setState({
        dataSource
    });
  }
  toggleIndent(){
    this.setState({
      expandedRowIndent:[2, 1]
    });
  }
  toggleCol(){
    this.setState({
      hasExpandedRowCtrl: false
    })
  }
}

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

可以通过 expandedRowRender 额外渲染行

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

class ExpandedApp extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            dataSource : this.props.dataSource
        }
    }
    render(){
        return <div>

          <Table dataSource={this.state.dataSource} hasHeader={false} hasBorder={false}>
            <Table.Column title="Title" dataIndex="title"/>
                <Table.Column title="Time" dataIndex="time" width={200}/>
          </Table>
            <p style={{borderTop:"1px solid #eee",textAlign:"center",background:"#f8f8f8",lineHeight:"28px"}}
                onClick={this.load.bind(this)}>Load more data.</p>
        </div>
    }
    load(){
        let {dataSource} = this.state;
        dataSource = dataSource.concat(dataSource);
        this.setState({dataSource});
    }
}

const onChange = function(...args){
          console.log(...args);
      },
      getData = () =>{
        let result = [];
        for(let i = 0; i< 5; i++){
          result.push({
              title:`Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
              id:100306660940+i,
              time: 2000 + i,
              children: [{
                  title: `Sub title for Quotation ${3+i}`,
                  time: 2000 + i,
              },{
                title: `Sub2 title for Quotation ${3+i}`,
                time: 2000 + i,
              }]
            })
        }
        return result;
    },
    render = (value, index, record) => {
        return <a>Remove({record.id})</a>;
    },
    expandedRowRender = (record, index) => {
      let children = record.children;
      return <ExpandedApp dataSource={children}/>
    }

class App extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      dataSource: getData(),
      hasBorder: false,
      expandedRowKeys: []
    }
  }
  render(){
    const renderTitle = (value, index, record) =>{
        return <div>{value}<span onClick={this.toggleExpand.bind(this, record)}>+++++</span></div>
    }
    return <span>
        <p> <button onClick={this.disabledExpandedCol.bind(this)}> 设置第4行禁用 </button>
                <button onClick={this.toggleCol.bind(this)}> 隐藏+号 </button></p>
        <Table dataSource={this.state.dataSource}
               isZebra = {this.state.isZebra}
               hasBorder = {this.state.hasBorder}
               onSort = {this.onSort.bind(this)}
               expandedRowRender = {expandedRowRender}
               expandedRowIndent = {[1,1]}
               expandedRowKeys = {this.state.expandedRowKeys}
               getExpandedColProps = {this.state.getExpandedColProps}
               hasExpandedRowCtrl = {this.state.hasExpandedRowCtrl}
               onExpandedChange = {this.onExpandedChange.bind(this)}
               >
            <Table.Column title="Id" dataIndex="id" sortable/>
            <Table.Column title="Title" dataIndex="title" cell={renderTitle}/>
            <Table.Column title="Time" dataIndex="time" width={200}/>
            <Table.Column cell={render} width={200}/>
        </Table>
    </span>
  }
  onSort(dataIndex, order, sort){
    let dataSource = this.state.dataSource.sort(function(a, b){
      let result = a[dataIndex] - b[dataIndex];
      return  (order == 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
    });
    this.setState({
        dataSource
    });
  }
  disabledExpandedCol(){
    this.setState({
        getExpandedColProps: (record, index)=>{
          if(index == 3){
            return {
              disabled: true
            }
          }
        }
      })
  }
    toggleCol(){
        this.setState({
            hasExpandedRowCtrl: false
        });
    }
    onExpandedChange(expandedRowKeys){
        this.setState({ expandedRowKeys});
    }
    toggleExpand(record){
        let key = record.id,
            { expandedRowKeys } = this.state,
            index = expandedRowKeys.indexOf(key);
        if( index > -1){
            expandedRowKeys.splice(index, 1);
        }else{
            expandedRowKeys.push(key);
        }
        this.setState({
            expandedRowKeys: expandedRowKeys
        })
    }
}

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

可以通过 expandedRowRender 额外渲染行,但是会包含复杂的组件

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

const onRowClick = function(record, index, e){
    console.log(record, index, e);
  },
  getData = (length) =>{
    let result = [];
    for(let i = 0; i < length; i++){
      result.push({
          title:{
            name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
          id:100306660940+i,
          time: 2000 + i
        })
    }
    return result;
},
render= (value, index, record) => {
    return <a>Remove({record.id})</a>;
}

class App extends React.Component {
    state = {
        dataSource: getData(10)
    }
    render() {
        return (<div>
            <button onClick={this.reduceContent.bind(this)}>切换到2条数据</button>
            <Table dataSource={this.state.dataSource} onRowClick={onRowClick} fixedHeader={true}>
                <Table.Column title="Id" dataIndex="id"/>
                <Table.Column title="Title" dataIndex="title.name"/>
                <Table.Column title="Time" dataIndex="time"/>
                <Table.Column cell={render} width={200}/>
            </Table>
        </div>)
    }

    reduceContent() {
        this.setState({
            dataSource: getData(2)
        })
    }
}

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

表格可以固定表头

import Table from 'blue/lib/table';
import 'blue/lib/table/index.scss';

let dataSource = [{
      price:'US $2.45',
      status: 0,
      id: 1,
      product:[{
        title:'2014 New Fashion Novelty Tank Slim Women\'s Fashion Dresses With Lace',
        avatar: 'https://sc01.alicdn.com/kf/HTB1ravHKXXXXXccXVXXq6xXFXXXJ/Chinese-Style-Fashion-Custom-Digital-Print-Silk.jpg_220x220.jpg'
      }]
    },{
      price:'US $2.5',
      status: 1,
      id: 2,
      product:[{
        title:'Free shipping women Casual dresses lady dress plus size 2014',
        avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
      }]
    },{
      price:'US $2.5',
      status: 1,
      id: 3,
      product:[{
        title:'Free shipping women Casual dresses lady dress plus size 2014',
        avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
      }]
    },{
      price:'US $2.5',
      status: 1,
      id: 4,
      product:[{
        title:'Free shipping women Casual dresses lady dress plus size 2014',
        avatar: 'https://sc02.alicdn.com/kf/HTB1efnNLVXXXXbtXpXXq6xXFXXXN/Light-100-acrylic-fashionabe-snood-shawl-weight.jpg_220x220.jpg'
      }]
    }],
    productRender = function(product){
      return <div className="media">
        <img src={product[0].avatar} className="media-side"/>
        <div className="media-content">{product[0].title}</div>
      </div>
    },
    priceRender = function(price){
      return <b>{price}</b>
    },
    statusRender = function(status){
      if(status){
        return 'Already Priced'
      }else{
        return 'No Priced'
      }
    },
    operRender = function(){
      return <a href="javascript:;">View</a>
    },
    groupHeaderRender = function(record){
      return <div>{record.product[0].title}</div>
    },
    getRowClassName = function(record){
      if(record.status == 0){
        return 'highlight-row';
      }
    },
    rowSelection={onChange: function(){}};

ReactDOM.render(<Table dataSource={dataSource} getRowClassName={getRowClassName} rowSelection={rowSelection}>
        <Table.GroupHeader cell={groupHeaderRender}/>
        <Table.Column cell={productRender} title="Product Details" dataIndex="product"/>
        <Table.Column cell={priceRender} title="Price" dataIndex="price"  width={120}/>
        <Table.Column cell={statusRender} title="Status" dataIndex="status" width={100}/>
        <Table.Column cell={operRender} title="" width={100}/>
</Table>, mountNode);
.media-side{
    width:48px;
    height:48px;
    float:left;
    margin-right:10px;
}
.media-content{
    overflow: hidden;
    vertical-align: top;
}
.media{
    overflow: hidden;
}
.next-table .highlight-row .next-table-group-header td{
    background: #E8F6FF;
}
.next-table .highlight-row td{
    border-color: #D3E9F7;
}

分组列表展现

import Table from 'blue/lib/table';
import Button from 'blue/lib/button';
import 'blue/lib/table/index.scss';

const onRowClick = function(record, index, e){
    console.log(record, index, e);
  },
  getData = (j) =>{
    let result = [];
    for(let i = 0; i< j; i++){
      result.push({
          title:{
            name: `Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
            },
          id:100306660940+i,
          time: 2000 + i
        })
    }
    return result;
},
render= (value, index, record) => {
    return <a>Remove({record.id})</a>;
}

class App extends React.Component {

    state = {
        dataSource: getData(15)
    }

    render() {
        return <div><p><Button onClick={this.onClick}>Reduce count</Button></p>
        <Table dataSource={this.state.dataSource} onRowClick={onRowClick} fixedHeader maxBodyHeight={400}>
    <Table.Column title="Id1" dataIndex="id" width={140}/>
    <Table.ColumnGroup>
        <Table.Column title="Id2" dataIndex="id" lock width={140}/>
        <Table.Column title="Title" dataIndex="title.name" width={400}/>
            <Table.Column title="Title" dataIndex="title.name" width={200}/>
    </Table.ColumnGroup>
    <Table.ColumnGroup>
        <Table.Column title="Time" dataIndex="time" width={500}/>
        <Table.Column cell={render} width={200} lock="right"/>
    </Table.ColumnGroup>
</Table></div>
    }

    onClick = () => {
        this.setState({
            dataSource: getData(4)
        });
    }
}

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

多个表头

import Table from 'blue/lib/table';
import Button from 'blue/lib/button';
import 'blue/lib/table/index.scss';

const onChange = function(...args){
          console.log(...args);
      },
      getData = () =>{
        let result = [];
        for(let i = 0; i< 5; i++){
          result.push({
              title:`Quotation for 1PCS Nano ${3+i}.0 controller compatible`,
              id:100306660940+i,
              time: 2000 + i
            })
        }
        return result;
    },
    render= (value, index, record) => {
        return <a>Remove({record.id})</a>;
    };

class App extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      dataSource: getData(),
      className: ''
    }
  }
  render(){
    return <span>
        <p>
            <Button onClick={this.toggleZebra.bind(this)}> Toggle zebra </Button>
            <Button onClick={this.toggleBorder.bind(this)}> Toggle border</Button>
            <Button onClick={this.makeBeauty.bind(this)}> Make second column beauty </Button>
        </p>
        <Table dataSource={this.state.dataSource}
               isZebra = {this.state.isZebra}
               hasBorder = {this.state.hasBorder}
               onSort = {this.onSort.bind(this)}>
            <Table.Column title="Id" dataIndex="id" sortable />
            <Table.Column title="Title" dataIndex="title" className={this.state.className}/>
            <Table.Column title="Time" dataIndex="time"/>
            <Table.Column cell={render} width={200}/>
        </Table>
    </span>
  }
  onSort(dataIndex, order, sort){
    let dataSource = this.state.dataSource.sort(function(a, b){
      let result = a[dataIndex] - b[dataIndex];
      return  (order == 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
    });
    this.setState({
        dataSource
    });
  }
  toggleZebra(){
    this.setState({
      isZebra: !this.state.isZebra
    })
  }
  toggleBorder(){
    this.setState({
      hasBorder: !this.state.hasBorder
    })
  }
  makeBeauty() {
    this.setState({
      className: 'beauty'
    })
  }
}

ReactDOM.render(<App/>, mountNode);
.beauty{
  background: #f7f7f7;
}

自定义表格边框