Table 表格
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
开发指南
Table 负责将数据呈现为高度可定制和具备可访问性的 HTML 表格,其核心功能为将结构化的数据使用表格的方式展现,然后可以使用各种参数来向表格中加入一些特性,比如排序,过滤,滚动,锁列等。
基本使用
基本的 Table 包含行和列,使用 Table.Column 来定义列的信息,使用传入的 dataSource 属性数据来创建行。
下面的代码将会创建一行两列的数据表:
import { Table } from '@alifd/next';
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);
常见问题
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
根据值渲染不同的视图:
import { Table } from '@alifd/next';
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 来创建有多个表头的表格。
import { Table } from '@alifd/next';
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);
已知问题
分组 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} 排序对应的顺序, 有desc 和asc 两种 | 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 | 表格是否具有边框 | Boolean | true |
hasHeader | 表格是否具有头部 | Boolean | true |
isZebra | 表格是否是斑马线 | Boolean | false |
loading | 表格是否在加载中 | Boolean | false |
loadingComponent | 自定义 Loading 组件请务必传递 props, 使用方式: loadingComponent={props => <Loading {…props}/>}签名:Function(props: Object) => void参数:props: {Object} 当前点击行的key | Function | - |
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 | - |
primaryKey | dataSource当中数据的主键,如果给定的数据源中的属性不包含该主键,会造成选择状态全部选中 | String | 'id' |
expandedRowRender | 额外渲染行的渲染函数签名:Function(record: Object, index: Number) => Element参数:record: {Object} 该行所对应的数据index: {Number} 该行所对应的序列返回值:{Element} 渲染内容 | Function | - |
expandedRowIndent | 额外渲染行的缩进 | Array | - |
openRowKeys | 默认情况下展开的渲染行或者Tree, 传入此属性为受控状态 | Array | - |
hasExpandedRowCtrl | 是否显示点击展开额外渲染行的+号按钮 | Boolean | - |
getExpandedColProps | 设置额外渲染行的属性签名:Function() => void | Function | - |
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 | 最大内容区域的高度,在fixedHeader 为true 的时候,超过这个高度会出现滚动条 | 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 ,默认为multiple columnProps: {Function} Function()=>Object 选择列 的props,例如锁列、对齐等,可使用Table.Column 的所有参数titleProps: {Function} Function()=>Object 选择列 表头的props,仅在 multiple 模式下生效titleAddons: {Function} Function()=>Node 选择列 表头添加的元素,在single multiple 下都生效 | Object | - |
stickyHeader | 表头是否是sticky | Boolean | - |
offsetTop | 距离窗口顶部达到指定偏移量后触发 | Number | - |
affixProps | affix组件的的属性 | Object | - |
indent | 在tree模式下的缩进尺寸, 仅在isTree为true时候有效 | Number | - |
isTree | 开启Table的tree模式, 接收的数据格式中包含children则渲染成tree table | Boolean | - |
useVirtual | 是否开启虚拟滚动 | Boolean | - |
rowHeight | 设置行高 | Number/Function | - |
onBodyScroll | 在内容区域滚动的时候触发的函数签名:Function() => void | Function | - |
expandedIndexSimulate | 开启时,getExpandedColProps() / rowProps() / expandedRowRender() 的第二个参数 index (该行所对应的序列) 将按照01,2,3,4…的顺序返回,否则返回真实index(0,2,4,6… / 1,3,5,7…) | Boolean | false |
Table.Column
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
dataIndex | 指定列对应的字段,支持a.b 形式的快速取值 | String | - |
cell | 行渲染的逻辑value, rowIndex, record, context四个属性只可读不可被更改Function(value, index, record) => Element | ReactElement/ReactNode/Function | value => 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' |
filterMenuProps | filter 模式下传递给 Menu 菜单的属性, 默认继承 Menu 组件的API属性:subMenuSelectable: {Boolean} 默认为false subMenu是否可选择isSelectIconRight: {Boolean} 默认为false 是否将选中图标居右。注意:SubMenu 上的选中图标一直居左,不受此API控制 | Object | { subMenuSelectable: false, } |
lock | 是否支持锁列,可选值为left ,right , true | Boolean/String | - |
resizable | 是否支持列宽调整, 当该值设为true,table的布局方式会修改为fixed. | Boolean | false |
Table.ColumnGroup
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
title | 表头显示的内容 | ReactElement/ReactNode/Function | 'column-group' |
Table.GroupHeader
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
cell | 行渲染的逻辑 | ReactElement/ReactNode/Function | () => '' |
hasChildrenSelection | 是否在Children上面渲染selection | Boolean | false |
hasSelection | 是否在GroupHeader上面渲染selection | Boolean | true |
useFirstLevelDataWhenNoChildren | 当 dataSouce 里没有 children 时,是否使用内容作为数据 | Boolean | false |
Table.GroupFooter
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
cell | 行渲染的逻辑 | ReactElement/ReactNode/Function | () => '' |
代码示例
简单的表格渲染
查看源码在线预览
import { Table } from '@alifd/next';
const dataSource = () => {
const 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;
};
const render = (value, index, record) => {
return <a href="javascript:;">Remove({record.id})</a>;
};
ReactDOM.render(<Table dataSource={dataSource()}>
<Table.Column title="Id" dataIndex="id"/>
<Table.Column title="Title" dataIndex="title.name" />
<Table.Column title="Time" dataIndex="time"/>
<Table.Column cell={render}/>
</Table>, mountNode);
表格可选择功能
查看源码在线预览
import { Table, Icon, MenuButton } from '@alifd/next';
const { Item } = MenuButton;
const dataSource = () => {
const 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;
};
const render = (value, index, record) => {
return <a href="javascript:;">Remove({record.id})</a>;
};
const onChange = (...args) => {
console.log(args);
};
const selectItem = id => {
console.log(id);
};
ReactDOM.render(<Table
dataSource={dataSource()}
rowSelection={{
onChange: onChange,
getProps: (record, index) => {
console.log(record, index);
return index === 2 ? {
disabled: true,
children: index
} : {
children: index
};
},
columnProps: () => {
return {
lock: 'left',
width: 90,
align: 'center'
};
},
titleAddons: () => {
return <div>请选择</div>;
},
titleProps: () => {
return {
// remove the select all button
// style: {display: 'none'},
disabled: true,
children:
<MenuButton text onItemClick={selectItem} menuProps={{
isSelectIconRight: true
}} >
<Item key="odd">odd</Item>
<Item key="even">even</Item>
</MenuButton>
};
}
}}>
<Table.Column title="Id" dataIndex="id" width={200}/>
<Table.Column title="Title" dataIndex="title.name" width={200}/>
<Table.Column title="Time" dataIndex="time" width={200}/>
<Table.Column cell={render} width={200}/>
</Table>, mountNode);
演示全选和单选受控的功能
查看源码在线预览
import { Table, Button } from '@alifd/next';
const dataSource = (i, j) => {
const result = [];
for (let a = i; a < j; a++) {
result.push({
title: {name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible`},
id: 100306660940 + a,
time: 2000 + a
});
}
return result;
},
render = (value, index, record) => {
return <a href="javascript:;">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: [100306660940, 100306660941],
getProps: (record) => {
return {
disabled: record.id === 100306660941
};
}
},
dataSource: dataSource(0, 5)
};
}
onChange(ids, records) {
const {rowSelection} = this.state;
rowSelection.selectedRowKeys = ids;
console.log('onChange', ids, records);
this.setState({ rowSelection });
}
clear() {
const {rowSelection} = this.state;
rowSelection.selectedRowKeys = [];
this.setState({ rowSelection });
}
toggleLoading() {
this.setState({loading: !this.state.loading});
}
changeMode() {
const {rowSelection} = this.state;
const mode = rowSelection.mode;
const selectedRowKeys = rowSelection.selectedRowKeys;
rowSelection.mode = mode === 'single' ? 'multiple' : 'single';
rowSelection.selectedRowKeys = selectedRowKeys.length === 1 ? selectedRowKeys : [];
this.setState({ rowSelection });
}
modifyDataSource() {
this.setState({
dataSource: dataSource(9, 14)
});
}
render () {
return (
<div>
<p>
<Button onClick={this.clear.bind(this)}>Clear Selection</Button>
<Button onClick={this.changeMode.bind(this)}>Change 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}
loading={this.state.loading}
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>
</div>
);
}
}
ReactDOM.render(<App/>, mountNode);
示例演示了排序和过滤的特性
查看源码在线预览
import { Table, Button, Icon } from '@alifd/next';
const dataSource = () => {
const 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 href="javascript:;">Remove({record.id})</a>;
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: dataSource(),
filterMode: 'multiple'
};
}
onSort(dataIndex, order) {
const dataSource = this.state.dataSource.sort(function(a, b) {
const result = a[dataIndex] - b[dataIndex];
return (order === 'asc') ? (result > 0 ? 1 : -1) : (result > 0 ? -1 : 1);
});
this.setState({
dataSource,
sort: {id: order}
});
}
onFilter(filterParams) {
let ds = dataSource();
Object.keys(filterParams).forEach(key => {
const selectedKeys = filterParams[key].selectedKeys;
if (selectedKeys.length) {
ds = ds.filter(record => {
return selectedKeys.some(value => {
return record[key].indexOf(value) > -1;
});
});
}
});
this.setState({dataSource: ds});
}
changeMode() {
this.setState({
filterMode: 'single'
});
}
render() {
const filters = [{
label: 'Nano 3',
value: 3
}, {
label: 'Nano 678',
value: 678,
children: [{
label: 'Nano 67',
value: 67,
children: [{
label: 'Nano 6',
value: 6
}, {
label: 'Nano 7',
value: 7
}]
}, {
label: 'Nano 8',
value: 8
}]
}, {
label: 'Other',
value: 'other',
children: [{
label: 'Nano 4',
value: 4
}, {
label: 'Nano 5',
value: 5
}]
}];
return (
<div>
<p><Button onClick={this.changeMode.bind(this)}>Change filter menu to single select</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>
<br />
Customize sortIcons:
<br />
<Table dataSource={[]}
onSort={() => {}}
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" />
}}>
<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>
</div>
);
}
}
ReactDOM.render(<App/>, mountNode);
通过 rowSelection.getProps 来控制选择框属性
查看源码在线预览
import { Table } from '@alifd/next';
const onChange = function(...args) {
console.log(...args);
},
dataSource = () => {
const 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={dataSource()}
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);
可以通过 expandedRowRender
额外渲染行
查看源码在线预览
import { Table, Button } from '@alifd/next';
const dataSource = () => {
const 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: dataSource()
};
}
onSort(dataIndex, order) {
const dataSource = this.state.dataSource.sort(function(a, b) {
const 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
});
}
render() {
return (<div>
<p>
<Button onClick={this.toggleIndent.bind(this)}> Update indent </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>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
可以通过 expandedRowRender
额外渲染行,但是会包含复杂的组件, 可通过 expandedIndexSimulate
设置 index 类型
查看源码在线预览
import { Table, Button } from '@alifd/next';
/*eslint-disable react/prop-types, react/no-multi-comp*/
class ExpandedApp extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: this.props.dataSource
};
}
load() {
let {dataSource} = this.state;
dataSource = dataSource.concat(dataSource);
this.setState({dataSource});
}
render() {
const style = {
borderTop: '1px solid #eee',
textAlign: 'center',
background: '#f8f8f8',
lineHeight: '28px'
};
return (
<div style={{marginTop: 10}}>
<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={style}
onClick={this.load.bind(this)}>{this.props.index}: Load more data. </p>
</div>
);
}
}
const dataSource = () => {
const 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) => {
const children = record.children;
return <ExpandedApp dataSource={children} index={index}/>;
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: dataSource(),
hasBorder: false,
openRowKeys: []
};
}
onSort(dataIndex, order) {
const dataSource = this.state.dataSource.sort(function(a, b) {
const 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) => {
console.log(index);
if (index === 3) {
return {
disabled: true
};
}
}
});
}
toggleCol() {
this.setState({
hasExpandedRowCtrl: false
});
}
onRowOpen(openRowKeys) {
this.setState({ openRowKeys});
}
toggleExpand(record) {
const key = record.id,
{ openRowKeys } = this.state,
index = openRowKeys.indexOf(key);
if (index > -1) {
openRowKeys.splice(index, 1);
} else {
openRowKeys.push(key);
}
this.setState({
openRowKeys: openRowKeys
});
}
rowProps(record, index) {
console.log('rowProps', record, index);
return {className: `next-myclass-${index}`};
}
onExpandedRowClick(record, index) {
console.log('onExpandedRowClick', record, index);
}
render() {
const renderTitle = (value, index, record) => {
return <div>{value}<span onClick={this.toggleExpand.bind(this, record)}>index:{index} +++++</span></div>;
};
return (
<span>
<p> <Button onClick={this.disabledExpandedCol.bind(this)}> disable fourth row </Button>
<Button onClick={this.toggleCol.bind(this)}> hide + </Button></p>
<Table dataSource={this.state.dataSource}
expandedIndexSimulate
isZebra={this.state.isZebra}
hasBorder={this.state.hasBorder}
onSort={this.onSort.bind(this)}
expandedRowRender={expandedRowRender}
expandedRowIndent={[1, 1]}
openRowKeys={this.state.openRowKeys}
getExpandedColProps={this.state.getExpandedColProps}
hasExpandedRowCtrl={this.state.hasExpandedRowCtrl}
onRowOpen={this.onRowOpen.bind(this)}
rowProps={this.rowProps.bind(this)}
onExpandedRowClick={this.onExpandedRowClick.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>
);
}
}
ReactDOM.render(<App/>, mountNode);
通过 cellProps 进行列合并。
查看源码在线预览
import { Table } from '@alifd/next';
const onRowClick = function (record, index, e) {
console.log(record, index, e);
},
dataSource = () => {
const 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>;
},
cellProps = (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={dataSource()} onRowClick={onRowClick} cellProps={cellProps}>
<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, Button } from '@alifd/next';
const onRowClick = function(record, index, e) {
console.log(record, index, e);
},
dataSource = () => {
const 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: dataSource()
}
onAdd = () => {
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
});
}
}
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>);
}
}
ReactDOM.render(<App/>, mountNode);
表格可以固定表头,支持sticky方式
查看源码在线预览
import { Table, Button } from '@alifd/next';
const dataSource = (length) => {
const 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;
};
class App extends React.Component {
state = {
sticky: false,
lock: false,
dataSource: dataSource(50),
}
onSwitch(tag) {
const props = {};
switch (tag) {
case 'sticky':
props.sticky = true;
break;
case 'lock':
props.lock = true;
break;
case 'dataSource':
props.dataSource = this.state.dataSource.length > 0 ? [] : dataSource(50);
break;
default:
break;
}
this.setState(props);
}
render() {
return (<div>
<p>
<Button onClick={this.onSwitch.bind(this, 'sticky')}>enable sticky</Button>
<Button onClick={this.onSwitch.bind(this, 'lock')}>enable lock</Button>
<Button onClick={this.onSwitch.bind(this, 'dataSource')}>toggle dataSource</Button>
</p>
<Table dataSource={this.state.dataSource} fixedHeader stickyHeader={this.state.sticky}>
<Table.Column title="Id" dataIndex="id" width={200} lock={this.state.lock}/>
<Table.Column title="Title" dataIndex="title.name" width={200}/>
<Table.Column title="Time" dataIndex="time" width={200}/>
</Table>
</div>
);
}
}
ReactDOM.render(<App/>, mountNode);
分组列表展现
查看源码在线预览
import { Table, Button } from '@alifd/next';
const dataSource = [{
price: 'US $1',
status: 1,
parent: 'root',
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'
}],
children: [{
price: 'US $1-1',
status: 11,
id: 2,
parent: 0,
index: 0,
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 $1-2',
status: 12,
id: 3,
parent: 0,
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 $1-3',
status: 13,
id: 7,
parent: 0,
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 $3',
status: 3,
parent: 'root',
id: 3,
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'
}],
children: [{
price: 'US $3-1',
status: 31,
id: 31,
parent: 1,
index: 0,
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 $3-2',
status: 32,
id: 32,
parent: 1,
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 $3-3',
status: 33,
id: 33,
parent: 1,
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 $3-4',
status: 34,
id: 34,
parent: 1,
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 $4',
status: 4,
parent: 'root',
id: 4,
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'
}],
children: [{
price: 'US $4-1',
status: 31,
id: 31,
parent: 2,
index: 0,
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 $4-2',
status: 32,
id: 32,
parent: 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 $4-3',
status: 33,
id: 33,
parent: 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'
}]
}]
}],
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>;
},
rowSelection = {
onChange: function(selectedKeys) {
console.log(selectedKeys);
}
};
const cellProps = (rowIndex, colIndex, dataIndex, record) => {
if (colIndex === 3 && record.index === 0 ) {
return {
rowSpan: dataSource[record.parent].children.length
};
}
if (colIndex === 4 && record.index === 0 ) {
return {
rowSpan: dataSource[record.parent].children.length
};
}
};
class App extends React.Component {
state = {
hasSelection: false
}
toggleGroupSelection = () => {
this.setState({
hasSelection: !this.state.hasSelection
});
}
render() {
return (
<div>
<p><Button onClick={this.toggleGroupSelection}>Toggle GroupHeader Selection</Button></p>
<Table dataSource={dataSource} rowSelection={rowSelection} cellProps={cellProps}>
<Table.GroupHeader cell={groupHeaderRender} hasChildrenSelection={this.state.hasSelection}/>
<Table.GroupFooter 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="Operation" width={100}/>
</Table>
</div>
);
}
}
ReactDOM.render(<App/>, mountNode);
.media-side{
width:48px;
height:48px;
float:left;
margin-right:10px;
}
.media-content{
overflow: hidden;
vertical-align: top;
}
.media{
overflow: hidden;
}
与分页结合
查看源码在线预览
import { Table, Pagination } from '@alifd/next';
const dataSource = (j) => {
const result = [];
for (let i = 0; i < 5; i++) {
result.push({
title: { name: `Quotation for 1PCS Nano ${3 + i}.0 controller compatible` },
id: 100306660940 + i + j,
time: 2000 + j
});
}
return result;
},
render = (value, index, record) => {
return <a href="javascript:;">Remove({record.id})</a>;
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: dataSource(5)
};
}
onChange = (currentPage) => {
this.setState({
loading: true
});
setTimeout(() => {
this.setState({
dataSource: dataSource(currentPage * 5),
loading: false
});
}, 200);
}
render() {
return (
<div>
<Table dataSource={this.state.dataSource}
loading={this.state.loading}>
<Table.Column title="Id1" dataIndex="id" width={140} />
<Table.Column title="Time" dataIndex="time" width={500} />
<Table.Column cell={render} width={200} />
</Table>
<Pagination onChange={this.onChange} className="page-demo" />
</div>);
}
}
ReactDOM.render(<App />, mountNode);
.page-demo {
margin-top:10px;
}
多个表头
查看源码在线预览
import { Table, Button } from '@alifd/next';
const onRowClick = function(record, index, e) {
console.log(record, index, e);
},
dataSource = (j) => {
const 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: dataSource(200)
}
onClick = () => {
this.setState({
dataSource: dataSource(4)
});
}
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="Title1" dataIndex="id" width={140}/>
<Table.ColumnGroup title="Group2-7">
<Table.Column title="Title2" dataIndex="id" lock width={140}/>
<Table.Column title="Title3" dataIndex="title.name" width={200}/>
<Table.ColumnGroup title="Group4-7">
<Table.Column title="Title4" dataIndex="title.name" width={400}/>
<Table.Column title="Title5" dataIndex="title.name" width={200}/>
<Table.ColumnGroup title="Group6-7">
<Table.Column title="Title6" dataIndex="title.name" width={400}/>
<Table.Column title="Title7" dataIndex="title.name" width={200}/>
</Table.ColumnGroup>
</Table.ColumnGroup>
</Table.ColumnGroup>
<Table.ColumnGroup>
<Table.Column title="Time" dataIndex="time" width={500}/>
<Table.Column cell={render} width={200} lock="right"/>
</Table.ColumnGroup>
</Table>
</div>);
}
}
ReactDOM.render(<App/>, mountNode);
使用 useVirtual
开启虚拟滚动,scrollToRow
滚动到指定列
查看源码在线预览
import { Table } from '@alifd/next';
const dataSource = (j) => {
const 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,
index: i
});
}
return result;
};
const render = (value, index, record) => {
return <a>Remove({record.id})</a>;
};
class App extends React.Component {
state = {
scrollToRow: 20
}
onBodyScroll = (start) => {
this.setState({
scrollToRow: start
});
}
render() {
return (<Table dataSource={dataSource(1000)} maxBodyHeight={400} useVirtual scrollToRow={this.state.scrollToRow} onBodyScroll={this.onBodyScroll}>
<Table.Column title="Id1" dataIndex="id" width={100}/>
<Table.Column title="Index" dataIndex="index" width={200}/>
<Table.Column title="Time" dataIndex="time" width={200}/>
<Table.Column title="Time" dataIndex="time" width={200}/>
<Table.Column title="Time" dataIndex="time" width={200} lock="right"/>
<Table.Column cell={render} width={200} lock/>
</Table>);
}
}
ReactDOM.render(<App/>, mountNode);
演示表格锁列的功能
查看源码在线预览
import { Table, Button } from '@alifd/next';
const onRowClick = function(record, index, e) {
console.log(record, index, e);
},
dataSource = () => {
const result = [];
for (let i = 0; i < 100; 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: [],
cols: [
<Table.Column title="Title" dataIndex="title.name" width={400} key="name1" lock/>,
<Table.ColumnGroup title="abc" key="name-group">
<Table.Column title="Title" dataIndex="title.name" width={100} key="name2"/>
<Table.Column title="Title" dataIndex="title.name" width={400} key="name3"/>
</Table.ColumnGroup>,
<Table.Column title="Time" dataIndex="time" width={500} key="time"/>
],
loading: true
}
componentDidMount() {
setTimeout(() => {
this.setState({
dataSource: dataSource(),
loading: false
});
}, 200);
}
reduceCol = () => {
this.setState({
cols: [<Table.Column title="Title" dataIndex="title.name" width={400} key="name1" lock/>,
<Table.Column title="Time" dataIndex="time" width={100} key="time"/>]
});
}
render() {
return (
<div>
<p><Button onClick={this.reduceCol}>Reduce Cols</Button></p>
<Table dataSource={this.state.dataSource} onRowClick={onRowClick} fixedHeader loading={this.state.loading}>
<Table.Column title="Id-Id-Id-Id-Id-Id-Id-Id-Id-Id-Id-Id" dataIndex="id" lock width={140}/>
{this.state.cols}
<Table.Column cell={render} width={200}/>
</Table>
</div>
);
}
}
ReactDOM.render(<App/>, mountNode);
定制显示的表格列数
查看源码在线预览
import { Table, Button, Dialog, Checkbox } from '@alifd/next';
const {Group} = Checkbox;
const dataSource = () => {
const 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;
},
cols = [{
title: 'id',
dataIndex: 'id'
}, {
title: 'Title',
dataIndex: 'title'
}, {
title: 'Time',
dataIndex: 'time'
}];
class App extends React.Component {
constructor (props) {
super(props);
this.state = {
dataSource: dataSource(),
cols: cols
};
}
openDialog = () => {
Dialog.alert({
content: this.renderControlContent(),
title: 'Select columns',
onOk: () => {
this.setState({
cols: this.changedCols || this.state.cols
});
return true;
}
});
}
renderControlContent() {
const groupSource = cols.map(col => {
return {
label: col.title,
value: col.dataIndex
};
}), defaultValue = this.state.cols.map(col => col.dataIndex);
return <Group dataSource={groupSource} onChange={this.onChange} defaultValue={defaultValue}/>;
}
onChange = (value) => {
this.changedCols = cols.filter(col => value.indexOf(col.dataIndex) > -1);
}
renderCols() {
const {cols} = this.state;
return cols.map(col => {
return <Table.Column title={col.title} dataIndex={col.dataIndex} key={col.dataIndex} />;
});
}
render() {
return (
<div>
<p><Button onClick={this.openDialog}> Select columns </Button></p>
<Table dataSource={this.state.dataSource}>
{this.renderCols()}
</Table>
</div>
);
}
}
ReactDOM.render(<App/>, mountNode);
自定义表格边框
查看源码在线预览
import { Table, Button } from '@alifd/next';
const dataSource = () => {
const 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: dataSource(),
className: '',
align: 'left'
};
}
toggleZebra() {
this.setState({
isZebra: !this.state.isZebra
});
}
toggleBorder() {
this.setState({
hasBorder: !this.state.hasBorder
});
}
makeBeauty() {
this.setState({
className: 'beauty'
});
}
makeAlign() {
this.setState({
align: 'right'
});
}
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>
<Button onClick={this.makeAlign.bind(this)}> Make first column align right </Button>
</p>
<Table dataSource={this.state.dataSource}
isZebra={this.state.isZebra}
hasBorder={this.state.hasBorder}>
<Table.Column title="Id" dataIndex="id" alignHeader="center"/>
<Table.Column title="Title" dataIndex="title" align={this.state.align} className={this.state.className}/>
<Table.Column title="Time" dataIndex="time"/>
<Table.Column cell={render} width={200}/>
</Table>
</span>);
}
}
ReactDOM.render(<App/>, mountNode);
.beauty{
background: #f7f7f7;
}
通过Table暴露的子组件进行扩展
查看源码在线预览
import { Table } from '@alifd/next';
import PropTypes from 'prop-types';
/* eslint-disable react/no-multi-comp,react/prop-types */
const {Header, Cell} = Table;
const dataSource = () => {
const 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;
};
const AppHeader = (props, context) => {
const {columns} = props;
const {onChange} = context;
const length = columns[columns.length - 1].length;
return (<Header {...props}>
<tr>
<Cell colSpan={length}>
<a onClick={() => onChange(true)} href="javascript:;">Select all</a>
<a onClick={() => onChange(false)} href="javascript:;">Unselect all</a>
</Cell>
</tr>
</Header>);
};
AppHeader.contextTypes = {
onChange: PropTypes.func
};
class App extends React.Component {
static childContextTypes = {
onChange: PropTypes.func
}
state = {
selectedKeys: []
}
getChildContext() {
return {
onChange: this.onChange
};
}
dataSource = dataSource()
onChange = (checked) => {
let selectedKeys = [];
if (checked) {
selectedKeys = this.dataSource.map(item => item.id);
}
this.onRowChange(selectedKeys);
}
onRowChange = (selectedKeys) => {
this.setState({
selectedKeys
});
}
render() {
return (<span>
<Table dataSource={this.dataSource}
components={{
Header: AppHeader
}}
rowSelection={{
selectedRowKeys: this.state.selectedKeys,
onChange: this.onRowChange
}}>
<Table.Column title="Id" dataIndex="id" />
<Table.Column title="Title" dataIndex="title"/>
<Table.Column title="Time" dataIndex="time"/>
</Table>
</span>);
}
}
ReactDOM.render(<App/>, mountNode);
通过onResizeChange来让列宽可以调整
查看源码在线预览
import { Table } from '@alifd/next';
const onChange = function(...args) {
console.log(...args);
},
dataSource = () => {
const 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
};
}
};
class App extends React.Component {
state = {
widths: {
id: 100
}
}
onResizeChange = (dataIndex, value) => {
const {widths} = this.state;
widths[dataIndex] = widths[dataIndex] + value;
this.setState({
widths
});
}
render() {
return (<Table dataSource={dataSource()}
rowSelection={rowSelection} onResizeChange={this.onResizeChange}>
<Table.Column title="Id" dataIndex="id" resizable width={this.state.widths.id}/>
<Table.Column title="Title" dataIndex="title.name" width={400}/>
<Table.Column title="Time" dataIndex="time" width={600}/>
<Table.Column cell={render} width={200}/>
</Table>);
}
}
ReactDOM.render(<App/>, mountNode);
演示了tree模式和rowSelection模式混合
查看源码在线预览
import { Table } from '@alifd/next';
const data = [{
key: 1,
name: 'a',
age: 32,
address: 'aa',
children: [{
key: 11,
name: 'b',
age: 33,
address: 'bb'
}, {
key: 12,
name: 'c',
age: 33,
address: 'cc',
children: [{
key: 121,
name: 'd',
age: 33,
address: 'dd'
}]
}, {
key: 13,
name: 'e',
age: 33,
address: 'ee',
children: [{
key: 131,
name: 'f',
age: 33,
address: 'ff',
children: [{
key: 1311,
name: 'g',
age: 33,
address: 'gg'
}, {
key: 1312,
name: 'h',
age: 33,
address: 'hh'
}]
}]
}]
}, {
key: 2,
name: 'i',
age: 32,
address: 'ii',
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 tableMixSelectionTreeLock = (<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>);
const tableMixExpandedLock = (<div style={{width: '500px'}}>
<Table dataSource={data} primaryKey="key" rowSelection={{onChange: () => {}}} expandedRowRender={(record) => record.address} expandedRowIndent={[3, 1]}>
<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 tableMixTreeLock = (<div style={{width: '500px'}}>
<Table dataSource={data} primaryKey="key" 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>);
ReactDOM.render(<div className="mix-demo">
<div className="row">
<h4>tree & select</h4>
{tableMixTree}
</div>
<div className="row">
<h4>extra & select</h4>
{tableMixExpanded}
</div>
<div className="row">
<h4>tree & lock column & select</h4>
{tableMixSelectionTreeLock}
</div>
<div className="row">
<h4>extra & lock column & select</h4>
{tableMixExpandedLock}
</div>
<div className="row">
<h4>lock column & select</h4>
{tableMixLock}
</div>
<div className="row">
<h4>tree & lock column</h4>
{tableMixTreeLock}
</div>
</div>, mountNode);
.mix-demo .row {
margin-top:10px;
}
查看源码在线预览
import { Table, Loading, Icon } from '@alifd/next';
const dataSource = () => {
const 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;
};
const render = (value, index, record) => {
return <a href="javascript:;">Remove({record.id})</a>;
};
const indicator = (
<div>
<Icon type="loading" />
</div>
);
const CustomLoading = (props) => (
<Loading
indicator={indicator}
{...props}
/>
);
ReactDOM.render(
<Table
dataSource={dataSource()}
loading
loadingComponent={CustomLoading}
>
<Table.Column title="Id" dataIndex="id"/>
<Table.Column title="Title" dataIndex="title.name" />
<Table.Column title="Time" dataIndex="time"/>
<Table.Column cell={render}/>
</Table>,
mountNode
);
可以重写部分原生属性,比如className style onDoubleClick等。
查看源码在线预览
import { Table } from '@alifd/next';
const dataSource = () => {
const 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;
};
const propsConf = {
className: 'next-myclass',
style: {background: 'black', color: 'white'},
onDoubleClick: () => {
console.log('doubleClicked');
}
};
const setRowProps = (record, index) => {
if (index === 2) {
return propsConf;
}
};
const setCellProps = (rowIndex, colIndex, dataIndex, record) => {
if (rowIndex === 0 && colIndex === 0) {
console.log(record);
return propsConf;
}
};
ReactDOM.render(<Table dataSource={dataSource()} rowProps={setRowProps} cellProps={setCellProps}>
<Table.Column title="Id" dataIndex="id"/>
<Table.Column title="Title" dataIndex="title.name" />
<Table.Column title="Time" dataIndex="time"/>
</Table>, mountNode);
单元格可编辑的表格
查看源码在线预览
import { Table, Input } from '@alifd/next';
const result = [{
id: '001',
time: 1951,
title: {name: 'The Old Man and the Sea'},
}, {
id: '002',
time: 1925,
title: {name: 'the great gatsby'},
}, {
id: '003',
time: 1719,
title: {name: 'The adventures of Robinson Crusoe'},
}];
class EditablePane extends React.Component {
constructor(props) {
super(props);
this.state = {
cellTitle: props.defaultTitle,
editable: false
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.defaultTitle !== this.state.cellTitle) {
this.setState({
cellTitle: nextProps.defaultTitle
});
}
}
onKeyDown = (e) => {
const { keyCode } = e;
// Stop bubble up the events of keyUp, keyDown, keyLeft, and keyRight
if (keyCode > 36 && keyCode < 41) {
e.stopPropagation();
}
}
onBlur = (e) => {
this.setState({
editable: false,
cellTitle: e.target.value
});
}
onDblClick = () => {
this.setState({
editable: true
});
}
render() {
const { cellTitle, editable } = this.state;
if (editable) {
return <Input autoFocus defaultValue={cellTitle} onKeyDown={this.onKeyDown} onBlur={this.onBlur} />;
}
return <span onDoubleClick={this.onDblClick}>{cellTitle}</span>;
}
}
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: result,
id:''
};
}
renderCell = (value, index, record) => {
return <EditablePane defaultTitle={value}/>;
}
render() {
return (<div>
<Table dataSource={this.state.dataSource} >
<Table.Column title="Id" dataIndex="id"/>
<Table.Column title="Title" dataIndex="title.name" cell={this.renderCell}/>
<Table.Column title="Time" dataIndex="time"/>
</Table>
</div>);
}
}
ReactDOM.render(<Demo />, mountNode);
通过键盘方向键浏览表格。
查看源码在线预览
import { Table } from '@alifd/next';
const result = [{
id: '001',
time: 1951,
title: {name: 'The Old Man and the Sea'},
}, {
id: '002',
time: 1925,
title: {name: 'the great gatsby'},
}, {
id: '003',
time: 1719,
title: {name: 'The adventures of Robinson Crusoe'},
}];
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: result,
};
}
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
});
}
}
renderOper = (value, index, record) => {
return <a onClick={this.onRemove.bind(this, record.id)}>Remove({record.id})</a>;
};
render() {
return (<div>
<Table dataSource={this.state.dataSource}>
<Table.Column title="Id" dataIndex="id"/>
<Table.Column title="Title" dataIndex="title.name" />
<Table.Column title="Time" dataIndex="time"/>
<Table.Column title="operate" cell={this.renderOper}/>
</Table>
</div>);
}
}
ReactDOM.render(<Demo />, mountNode);
可拖拽的表格。拖拽功能的实现依赖react-dnd@7.x 及react-dnd-html5-backend@7.x, 它要求react react-dom 版本高于16.3.x
查看源码在线预览
import { Table } from '@alifd/next';
import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import classnames from 'classnames';
const { SelectionRow } = Table;
let dragingIndex = -1;
function MyRow (props) {
const {
isDragging,
isOver,
connectDragSource,
connectDropTarget,
moveRow,
className,
...others
} = props;
const opacity = isDragging ? 0 : 1;
const style = { ...others.style, cursor: 'move' };
const cls = classnames({
[className]: className,
'drop-over-upward': isOver && others.index < dragingIndex,
'drop-over-downward': isOver && others.index > dragingIndex,
});
return (<SelectionRow {...others}
style={{ ...style, ...{ opacity } }}
className={cls}
wrapper={(row) => connectDragSource(connectDropTarget(row))} />);
}
const NewRow = DropTarget(
'row',
{
drop(props, monitor) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
if (dragIndex === hoverIndex) {
return;
}
props.moveRow(dragIndex, hoverIndex);
monitor.getItem().index = hoverIndex;
},
},
(connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
}),
)(
DragSource(
'row',
{
beginDrag: props => {
dragingIndex = props.index;
return {
id: props.record[props.primaryKey],
index: props.rowIndex,
};
},
},
(connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}),
)(MyRow),
);
class InnerTable extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: [...props.dataSource],
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.dataSource && JSON.stringify(nextProps.dataSource) !==
JSON.stringify(this.state.dataSource)) {
this.setState({ dataSource: [...nextProps.dataSource] });
}
}
moveRow = (dragIndex, hoverIndex) => {
const { onSort } = this.props;
const dragRow = this.state.dataSource[dragIndex];
const dataSource = [...this.state.dataSource];
dataSource.splice(dragIndex, 1);
dataSource.splice(hoverIndex, 0, dragRow);
this.setState({
dataSource,
});
onSort && onSort(this.state.dataSource);
};
render() {
const { excludeProvider, ...restProps } = this.props;
const tableProps = {
...restProps,
dataSource: this.state.dataSource,
rowProps: (props, index) => ({
index,
moveRow: this.moveRow,
}),
components: {
Row: NewRow
},
};
return <Table {...tableProps} />;
}
}
class SortableTable extends React.Component {
render() {
const ComponentName = DragDropContext(HTML5Backend)(InnerTable);
return <ComponentName {...this.props} />;
}
}
const result = [{
id: '001',
time: 1951,
title: {name: 'The Old Man and the Sea'},
}, {
id: '002',
time: 1925,
title: {name: 'the great gatsby'},
}, {
id: '003',
time: 1719,
title: {name: 'The adventures of Robinson Crusoe'},
}];
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: result,
};
}
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
});
}
}
renderOper = (value, index, record) => {
return <a onClick={this.onRemove.bind(this, record.id)}>Remove({record.id})</a>;
};
render() {
return (<div>
<SortableTable dataSource={this.state.dataSource}>
<Table.Column title="Id" dataIndex="id" width={100} lock/>
<Table.Column title="Title" dataIndex="title.name" width={400} />
<Table.Column title="Time" dataIndex="time" width={300}/>
<Table.Column title="operate" cell={this.renderOper} width={300} lock="right"/>
</SortableTable>
</div>);
}
}
ReactDOM.render(<Demo />, mountNode);
.drop-over-downward{
border-bottom: 2px dashed #3080fe;
}
.drop-over-upward{
border-top: 2px dashed #3080fe;
}