PerformanceTable 大数据表格
展示行列数据。
何时使用
- 当有大数据需要高性能展现时;
- 千级数据量,简单需求,高流畅要求;
代码演示
基本
最简单的用法。
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable, Button } from 'choerodon-ui/pro';
const Table = () => {
const tableRef = React.createRef();
const [fakeLargeData, setFakeLargeData] = useState([]);
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓',
dataIndex: 'lastName',
key: 'lastName',
width: 150,
},
{
title: '名',
dataIndex: 'firstName',
key: 'firstName',
width: 150,
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 300,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 300,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
width: 300,
},
];
useEffect(() => {
fetch('../data/fakeLargeData.json')
.then((response) => response.json())
.then((data) => {
setFakeLargeData(data);
调整列宽
把鼠标移动到列分割线的时候,会显示出一个蓝色的移动手柄,点击不松开并左右拖动就可以调整列的宽度。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class ResizableColumnTable extends React.Component {
constructor(props) {
super(props);
const data = fakeData.filter((v, i) => i < 8);
this.state = {
data,
};
}
render() {
const { data } = this.state;
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 50,
resizable: true,
fixed: true,
},
{
title: '姓名',
dataIndex: 'firstName',
key: 'firstName',
width: 100,
resizable: true,
onResize: (columnWidth, dataKey) => {
console.log(columnWidth, dataKey);
},
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 400,
resizable: true,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 400,
resizable: true,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
流体列宽
如果需要把某列设置为自动宽度,需要配置 flexGrow 属性。 flexGrow 是 number 类型。会按照所有 flexGrow 总和比例撑满剩下的宽度。
注意: 设置 flexGrow 以后,就不能设置 width 和 resizable 属性。 可以通过 minWidth 设置一个最小宽度。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class FluidColumnTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
};
}
render() {
const { data } = this.state;
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓',
dataIndex: 'lastName',
key: 'lastName',
width: 100,
fixed: true,
},
{
title: '名',
dataIndex: 'firstName',
key: 'firstName',
width: 130,
resizable: true,
sortable: true,
},
{
title: (
<span>
城市 <code>flexGrow: 1 </code>
</span>
),
dataIndex: 'city',
key: 'city',
flexGrow: 1,
sortable: true,
},
{
title: (
<span>
公司 <code>flexGrow: 2</code>
锁定列
Fixed 锁定列。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class FixedColumnTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
};
}
render() {
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓',
dataIndex: 'lastName',
key: 'lastName',
width: 130,
fixed: true,
},
{
title: '名',
dataIndex: 'firstName',
key: 'firstName',
width: 130,
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 200,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 200,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
width: 300,
},
{
自动换行
如果想让单元格自动换行,需要设置 wordWrap。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class WordWrapTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
};
}
render() {
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓名',
dataIndex: 'firstName',
key: 'firstName',
width: 150,
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 200,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 300,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
width: 300,
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
width: 300,
},
];
return (
自定义单元格
根据不同的业务场景,单元格中可以自己定义显示的内容。如果有相关单元格需要key唯一,获取rowIndex处理。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable, CheckBox } from 'choerodon-ui/pro';
import { Popover } from 'choerodon-ui';
const NameCell = ({ rowData, dataIndex }) => {
return (
<Popover
title="Description"
content={
<>
<p>
<b>Name:</b> {`${rowData.firstName} ${rowData.lastName}`}{' '}
</p>
<p>
<b>Email:</b> {rowData.email}{' '}
</p>
<p>
<b>Company:</b> {rowData.companyName}{' '}
</p>
<p>
<b>Sentence:</b> {rowData.sentence}{' '}
</p>
</>
}
>
{rowData[dataIndex].toLocaleString()}
</Popover>
);
};
const ActionCell = ({ rowData, dataIndex }) => {
function handleAction() {
alert(`id:${rowData[dataIndex]}`);
console.log(rowData, dataIndex);
}
return (
<span>
<a onClick={handleAction}> Edit </a>|
<a onClick={handleAction}> Remove </a>
</span>
);
};
class CustomColumnTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
checkValues: [],
};
排序
设置 sortable,同时在 performanceTable 定义一个 onSortColumn 回调函数,点击列头排序图标的时候,会触发该方法,并返回 sortColumn 和 sortType。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class SortTable extends React.Component {
constructor(props) {
super(props);
this.state = {
sortColumn: 'id',
data: fakeData,
};
this.handleSortColumn = this.handleSortColumn.bind(this);
}
getData() {
const { data, sortColumn, sortType } = this.state;
if (sortColumn && sortType) {
return data.sort((a, b) => {
let x = a[sortColumn];
let y = b[sortColumn];
if (typeof x === 'string') {
x = x.charCodeAt();
}
if (typeof y === 'string') {
y = y.charCodeAt();
}
if (sortType === 'asc') {
return x - y;
} else {
return y - x;
}
});
}
return data;
}
handleSortColumn(sortColumn, sortType) {
this.setState({
loading: true,
});
setTimeout(() => {
console.log(sortColumn);
this.setState({
树形
树形表格,主要为了展示有结构关系的数据,需要在 Table 组件上设置一个 isTree 属性,同时 data 中的数据需要通过 children 来定义关系结构。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class TreeTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeTreeData
};
}
render() {
const { data } = this.state;
const columns = [
{
title: 'Key',
dataIndex: 'key',
width: 100,
},
{
title: 'Label (Tree Col)',
dataIndex: 'labelName',
flexGrow: 1,
treeCol: true,
},
{
title: 'Status',
width: 100,
dataIndex: 'status',
},
{
title: 'Count',
width: 100,
dataIndex: 'count',
},
];
return (
<div>
<PerformanceTable
isTree
virtualized
minHeight={260}
height={400}
data={data}
columns={columns}
defaultExpandedRowKeys={[0]}
onExpandChange={(expanded, rowData) => {
console.log(expanded, rowData);
}}
renderTreeToggle={(icon, rowData, expanded) => {
if (rowData.labelName === '手机') {
return <i className="icon icon-spin icon-spinner" />;
可展开
实现一个可以展开的 Table ,需要以下几个属性的组合完成。
第一步:给 Table 设置属性 renderRowExpanded(rowData) => React.Node 用来返回需要在展开面板中渲染的内容 rowExpandedHeight 设置可展开区域的高度, 默认是 100 expandedRowKeys(受控) 和 defaultExpandedRowKeys 用来配置需要展开的行。
需要注意的是这两个属性接收的参数是一个的数组,数组中是 rowKey。 rowKey 给每一行数据对一个唯一 key , 对应 data 中的一个唯一值的 key。
第二步:自定义 Cell 自定义一个 Cell, 在内部放一个可以操作按钮,用于操作 expandedRowKeys 中的。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable, Button } from 'choerodon-ui/pro';
const rowKey = 'id';
const ExpandCell = ({ rowData, dataIndex, expandedRowKeys, onChange }) => (
<Button
onClick={() => {
onChange(rowData);
}}
funcType="flat"
size="small"
>
{expandedRowKeys.some((key) => key === rowData[rowKey]) ? '-' : '+'}
</Button>
);
class ExpandedTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
expandedRowKeys: [0],
};
this.handleExpanded = this.handleExpanded.bind(this);
}
handleExpanded(rowData, dataKey) {
const { expandedRowKeys } = this.state;
let open = false;
const nextExpandedRowKeys = [];
expandedRowKeys.forEach((key) => {
if (key === rowData[rowKey]) {
open = true;
} else {
nextExpandedRowKeys.push(key);
}
});
if (!open) {
nextExpandedRowKeys.push(rowData[rowKey]);
}
this.setState({
expandedRowKeys: nextExpandedRowKeys,
});
}
render() {
const { expandedRowKeys, data } = this.state;
const columns = [
{
title: '#',
可编辑
可编辑的表格。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
import _ from 'lodash';
export const EditCell = ({ rowData, dataIndex, onChange }) => {
return rowData.status === 'EDIT' ? (
<input
className="input"
style={{ height: 26 }}
defaultValue={rowData[dataIndex]}
onChange={(event) => {
onChange && onChange(rowData.id, dataIndex, event.target.value);
}}
/>
) : (
rowData[dataIndex]
);
};
const ActionCell = ({ rowData, onClick }) => {
return (
<a
onClick={() => {
onClick && onClick(rowData.id);
}}
>
{rowData.status === 'EDIT' ? 'Save' : 'Edit'}
</a>
);
};
class EditTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData.filter((item, index) => index < 20),
};
this.handleChange = this.handleChange.bind(this);
this.handleEditState = this.handleEditState.bind(this);
}
handleChange(id, key, value) {
const { data } = this.state;
// 可使用 _.clone
const nextData = [...data];
nextData.find((item) => item.id === id)[key] = value;
this.setState({
data: nextData,
});
}
handleEditState(id) {
const { data } = this.state;
组合列
在某些情况下,需要合并列来组织数据之间的关系,设置 colSpan 属性,同时通过 children 设置表头分组。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class FixedColumnTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeDataForColSpan,
};
}
render() {
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
verticalAlign: 'middle',
fixed: true,
},
{
header: '基本信息',
type: 'ColumnGroup',
align: 'center',
verticalAlign: 'middle',
fixed: true,
children: [
{
title: '姓',
dataIndex: 'lastName',
key: 'lastName',
width: 150,
resizable: true,
},
{
title: '名',
dataIndex: 'firstName',
key: 'firstName',
width: 150,
resizable: true,
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
width: 200,
resizable: true,
},
],
},
自动高度
自适应高度。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class AutoHeightTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
};
}
render() {
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓名',
dataIndex: 'firstName',
key: 'firstName',
width: 130,
resizable: true,
onResize: (columnWidth, dataKey) => {
console.log(columnWidth, dataKey);
},
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 200,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 300,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
width: 300,
},
{
title: '邮箱',
dataIndex: 'email',
虚拟滚动
虚拟滚动,大数据表格。
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable, Button } from 'choerodon-ui/pro';
const LargeListsTable = () => {
const tableRef = React.createRef();
const [fakeLargeData, setFakeLargeData] = useState([]);
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓',
dataIndex: 'lastName',
key: 'lastName',
width: 150,
},
{
title: '名',
dataIndex: 'firstName',
key: 'firstName',
width: 150,
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 300,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 300,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
width: 300,
},
];
useEffect(() => {
fetch('../data/fakeLargeData.json')
.then((response) => response.json())
.then((data) => {
setFakeLargeData(data);
固定横向滚动条页面底部
固定横向滚动条页面底部。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class AffixHorizontalScrollbarTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeData,
};
}
render() {
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓名',
dataIndex: 'firstName',
key: 'firstName',
width: 130,
resizable: true,
onResize: (columnWidth, dataKey) => {
console.log(columnWidth, dataKey);
},
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 200,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 300,
},
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
width: 300,
},
{
title: '邮箱',
dataIndex: 'email',
合并单元格
合并单元格。
import React from 'react';
import ReactDOM from 'react-dom';
import { PerformanceTable } from 'choerodon-ui/pro';
class FixedColumnTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: fakeDataForColSpan,
};
}
render() {
const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
width: 70,
fixed: true,
},
{
title: '姓',
dataIndex: 'lastName',
key: 'lastName',
width: 130,
fixed: true,
colSpan: 2,
resizable: true,
},
{
title: '名',
dataIndex: 'firstName',
key: 'firstName',
width: 130,
resizable: true,
fixed: true,
},
{
title: '城市',
dataIndex: 'city',
key: 'city',
width: 200,
colSpan: 2,
resizable: true,
},
{
title: '街道',
dataIndex: 'street',
key: 'street',
width: 300,
resizable: true,
拖拽
拖拽。
import { PerformanceTable, Icon } from 'choerodon-ui/pro';
import React from 'react';
import ReactDOM from 'react-dom';
import { useDrag, useDrop, DndProvider } from 'react-dnd';
// import { HTML5Backend } from 'react-dnd-html5-backend';
const { Column, Cell, HeaderCell } = PerformanceTable;
const style = {
border: '1px dashed gray',
cursor: 'move',
padding: '0 0.1rem',
};
const ItemTypes = {
COLUMN: 'column',
ROW: 'row',
};
function DraggableHeaderCell({ children, onDrag, id, ...rest }) {
const ref = React.useRef(null);
const [{ canDrop, isOver }, drop] = useDrop({
accept: ItemTypes.COLUMN,
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
drop(item, _monitor) {
onDrag(item.id, id);
},
});
const [{ isDragging }, drag] = useDrag({
item: { id, type: ItemTypes.COLUMN },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
const opacity = isDragging ? 0 : 1;
const isActive = canDrop && isOver;
drag(drop(ref));
const styles = {
...style,
opacity,
background: isActive ? '#ddd' : null,
};
return (
<HeaderCell {...rest} style={{ padding: 0 }}>
API
Table
属性名称 | 类型 (默认值) | 描述 |
---|---|---|
columns | Column[] | 表格列的配置描述,具体项见下表 |
affixHeader | boolean,number | 将表头固定到页面上的指定位置 |
affixHorizontalScrollbar | boolean,number | 将横向滚动条固定在页面底部的指定位置 |
autoHeight | boolean | 自动高度 |
bodyRef | React.Ref | 表格主体部分上的 ref |
bordered | boolean(true) | 表格边框 |
cellBordered | boolean | 单元格边框 |
data * | Array<Object> | 表格数据 |
defaultExpandAllRows | boolean | 默认展开所有节点 |
defaultExpandedRowKeys | string[] | 通过 rowKey 指定默认展开的行 |
defaultSortType | enum: ‘desc’, ‘asc’ | 排序类型 |
expandedRowKeys | string[] | 通过 rowKey 指定展开的行 (受控) |
headerHeight | number(30) | 表头高度 |
height | number(200) | 高度 |
hover | boolean (true) | 表格的行设置鼠标悬停效果 |
isTree | boolean | 是否展示为树表格 |
loading | boolean | 显示 loading 状态 |
locale | object | 本地化语言配置 |
minHeight | number (0) | 最小高度 |
onDataUpdated | (nextData: object[], scrollTo: (coord: { x: number; y: number }) => void) => void | 数据更新后的回调函数 |
onExpandChange | (expanded:boolean, rowData:object) => void | 树形表格,在展开节点的回调函数 |
onRowClick | (rowData:object) => void | 行点击后的回调函数, 返回 rowDate |
onScroll | (scrollX:object, scrollY:object) => void | 滚动条滚动时候的回调函数 |
onSortColumn | (dataKey:string, sortType:string) => void | 点击排序列的回调函数,返回 sortColumn , sortType 这两个值 |
renderEmpty | (info: React.Node) => React.Node | 自定义渲染数据为空的状态 |
renderLoading | (loading: React.Node) => React.Node | 自定义渲染数据加载中的状态 |
renderRowExpanded | (rowDate?: Object) => React.Node | 自定义可以展开区域的内容 |
renderTreeToggle | (icon:node, rowData:object, expanded:boolean) => node | 树形表格,在展开节点的回调函数 |
rowClassName | string , (rowData:object) => string | 为行自定义 className |
rowExpandedHeight | number (100) | 设置可展开区域的高度 |
rowHeight | (rowData:object) => number, number(46) | 行高 |
rowKey | string (‘key’) | 每一个行对应的 data 中的唯一 key |
shouldUpdateScroll | boolean(true) | 数据更新后更新滚动条位置 |
showHeader | boolean (true) | 显示表头 |
showScrollArrow | boolean (false) | 显示滚动条箭头 |
sortColumn | string | 排序列名称 |
sortType | enum: ‘desc’, ‘asc’ | 排序类型(受控) |
clickScrollLength | object ({horizontal?: 100;vertical?: 30;}) | 滚动条箭头点击滚动距离 |
virtualized | boolean | 呈现大表格数据 |
width | number | 宽度 |
wordWrap | boolean | 单元格自动换行 |
Form methods
- scrollTop
垂直滚动条滚动到指定位置
scrollTop: (top: number) => void;
- scrollLeft
横向滚动条滚动到指定位置
scrollLeft: (left: number) => void;
Column
属性名称 | 类型 (默认值) | 描述 |
---|---|---|
align | enum: ‘left’,’center’,’right’ | 对齐方式 |
colSpan | number | 合并列单元格,当被合并列的 dataKey 对应的值为 null 或者 undefined 时,才会合并。 |
fixed | boolean, ‘left’, ‘right’ | 固定列 |
flexGrow | number | 设置列宽自动调节,当设置了 flexGrow 就不能设置 resizable 与 width 属性 |
minWidth | number(200) | 当使用了 flexGrow 以后,可以通过 minWidth 设置一个最小宽度 |
onResize | (columnWidth?: number, dataKey?: string) => void | 列宽改变后的回调 |
resizable | boolean | 可自定义调整列宽 |
sortable | boolean | 可排序 |
treeCol | boolean | 指定列显示为 Tree |
verticalAlign | enum: ‘top’, ‘middle’, ‘bottom’ | 垂直对齐方式 |
width | number | 列宽 |
sortable
是用来定义该列是否可排序,但是根据什么key
排序需要 在Cell
设置一个dataKey
这里的排序是服务端排序,所以需要在<Table>
的onSortColumn
回调函数中处理逻辑,回调函数会返回sortColumn
,sortType
这两个值。
ColumnGroup
属性名称 | 类型 (默认值) | 描述 |
---|---|---|
align | enum: ‘left’,’center’,’right’ | 对齐方式 |
fixed | boolean, ‘left’, ‘right’ | 固定列组 |
verticalAlign | enum: ‘top’, ‘middle’, ‘bottom’ | 垂直对齐方式 |
header | React.ReactNode | 分组表头 |
Cell
属性名称 | 类型 (默认值) | 描述 |
---|---|---|
dataKey | string | 数据绑定的 key ,同时也是排序的 key |
rowData | object | 行数据 |
rowIndex | number | 行号 |
分页请结合 Pagination 组件。