对于企业级后台产品来讲,Table 应该是使用最频繁的组件了,它一般比 Form 和 Chart 的使用还频繁。对于这么一个经常使用的组件,咱们决定要把它从 RSuite 中单独出来开发,而且要具备必定的通用性,适应不少场景。 首先看一下,Table 完成的效果。html
最开始促使咱们去实现这个 Table 组件是由于产品经理但愿表格能够像 Excel同样固定表头和列,咱们都知道 HTML table 是不支持这个功能,可是在实际应用中,对于数据行多列多的状况下,固定表头和列很是有用,方便数据关联浏览。咱们的组件库都是 React 的, 开源环境中也没有找到一个适合的咱们的 Table 组件。 Ant Design 中的 Table 估计有些人用过,UI 比较漂亮,可是在固定表头和列的这个功能上我仍是有些不满意,特别是要同时固定表头和列的时候,在 Retina 屏幕上,Ant Table 经过触摸板滚动表格,固定的区域和非固定的区域会对不整齐,看上去会抖动,这个体验不是特别好。我知道 facebook 的 FixedDataTable 针对这块的处理作的还不错,是一个好的参考,特别是大数据量渲染也不卡顿,可是有些功能也不能知足咱们的业务场景,好比在要 Table 中呈现一个树形结构就没有这个功能。因此仍是决定本身造这个轮子。git
在 UI 的设计上符合 RSuite 的总体风格。 咱们具体看一下组件的设计,整个 Table 提供了 5 个组件,分别是:github
<Table>
定义表格,能够设置数据源,表格类型等等<Column>
定义列,设置列与数据源关联的 key, 设置列宽度,设置是否能够排序,是否须要固定列等等。<Cell>
定义单元格,用于渲染数据的组件,能够自定义显示的方式。<HeaderCell>
定义列头的单元格。<TablePagination>
定义分页,是一个可选组件。看一个简单的示例:chrome
npm i rsuite rsuite-table --save
有些地方依赖了 RSuite 中的基础组件,全部须要安装
rsuite
。npm
import { Table, Column, HeaderCell, Cell } from 'rsuite-table';
<Table data={data} > <Column width={100} sort fixed resizable> <HeaderCell>ID</HeaderCell> <Cell dataKey="id" /> </Column> <Column width={100} sort resizable> <HeaderCell>Name</HeaderCell> <Cell dataKey="name" /> </Column> <Column width={100} sort resizable> <HeaderCell>Email</HeaderCell> <Cell dataKey="email" /> </Column> </Table>
这是一个简单 3 列的表格,接下来咱们来看一下具体的功能点。后端
表头是默认固定的不须要额外的配置,要固定列,须要在固定的列 <Column>
添加 fixed
属性。浏览器
<Column width={100} fixed> <HeaderCell>ID</HeaderCell> <Cell dataKey="id" /> </Column>
这个功能是全部功能里面最麻烦的,特别是表头和列同时固定的时候,前面我也提到过 Ant Design 的 Table 就存在一个问题,滚动的时候固定列和非固定列未对齐,如下是一个 Ant Design 的 Table 的一个截图和访问连接。数据结构
访问地址: https://ant.design/components...dom
形成这个问题的主要缘由是 onScroll
触发的频率和渲染的速度跟不上形成的, 若是要列和表头都固定,那必然会在一个方向上须要手动修改元素的位置,这里确定不能用 React state 存储位置,而后等待渲染,那太慢了。全部须要操做 DOM, 去改变元素的位置,这里有这几个须要注意的技术点:函数
onScroll
触发的频率和渲染的速度会存在跟不上的状况,全部这里最好是本身实现一个滚动条,在 Table Body 上监听 onWheel
事件,在滚动条上监听 onMouse*
事件。 在本身实现滚动条的时候须要注意的是,在 Mac 的 chrome 上,左右滑动的时候会触发浏览器的上一页和下一页功能,因此这里的事件冒泡要处理好(原本想找一个开源的滚动条轮子,发现有好多组件这个问题没有处理好,因此就本身写了)。对 DOM 操做用到了 dom-lib
咱们的 Table 在处理上面两点之后,就解决了 Ant Design 的 Table 滚动存在的问题,固然若是你们有更好的方案,感谢你分享一下。
另外,Ant Table 有不少方面作得是比咱们好的,好比它支持固定右侧的列,支持嵌套表格等等功能。
在表格中有些列的数据有长有短,不太好预测,但仍是但愿在一个单元格内显示,若是给列固定好一个宽度之后,那超出单元格的内容就会被截断隐藏,致使信息显示不完整。Excel 的列是能够调整宽度的,因此咱们也但愿列能够调整宽度,只须要在 <Column>
设置一个 resizable
属性。
<Column width={130} sortable> <HeaderCell>First Name</HeaderCell> <Cell dataKey="firstName" /> </Column>
有一种状况,Table 在页面中的宽度好比是 1000px
+ (可能更宽,根据显示器屏幕的宽度决定), 可是这个 Table 只有 3 列,若是每列都固定一个 200px
, 确定 撑不满整个 Table,致使不美观, 咱们都知道 HTML table, 当给 table 设置 width:100%
之后,列会根据内容自动撑满,若是给其中一个 td 设置了 width
, 那 Table 剩下的 width, 会被剩下的几列撑满。那在 rsuite-table 怎么解决问题呢? 看如下示例:
<Table width={1000}> <Column width={100}> <HeaderCell>First Name</HeaderCell> <Cell dataKey="firstName" /> </Column> <Column flexGrow={1}> <HeaderCell>City</HeaderCell> <Cell dataKey="city" /> </Column> <Column flexGrow={2}> <HeaderCell>Company Name</HeaderCell> <Cell dataKey="companyName" /> </Column> </Table>
在 <Column>
组件上提供了一个 flexGrow
属性,有点相似 CSS3 中的 flex-grow
属性。上面示例中,Table 的 width
为 1000
, 第一列的 width:100
, 第二列设置为 flexGrow:1
, 第三列设置为 flexGrow:2
。 渲染后计算的结果是:
100px
flexGrow:1
, (1000 - 100)/(2 + 1) * 1
= 300px
flexGrow:2
, (1000 - 100)/(2 + 1) * 2
= 600px
排序是一个基础的功能,在须要排序的列 <Column>
设置一个 sortable
属性。 同时在 <Table>
定义一个 onSortColumn:Function
回调函数,点击列头排序图标的时候,会触发该方法,并返回 sortColumn:String
和 sortType:String('asc'|desc)
。 看一下示例:
<Table onSortColumn={(sortColumn, sortType)=>{ console.log(sortColumn, sortType); }} > <Column width={50} sortable> <HeaderCell>Id</HeaderCell> <Cell dataKey="id" /> </Column> <Column width={130} sortable > <HeaderCell>First Name</HeaderCell> <Cell dataKey="firstName" /> </Column> <!--... --> </Table>
提供了一个 <TablePagination>
组件,用于显示分页栏,这里的分页须要开发人员本身去处理数据,看一下示例代码:
function formatLengthMenu(lengthMenu) { return ( <div className="table-length"> <span> 每页 </span> {lengthMenu} <span> 条 </span> </div> ); } function formatInfo(total, activePage) { return ( <span>共 <i>{total}</i> 条数据</span> ); }
<TablePagination formatLengthMenu={formatLengthMenu} formatInfo={formatInfo} displayLength={100} total={500} onChangePage={this.handleChangePage} onChangeLength={this.handleChangeLength} />
看一下,效果:
先看一下树形表格的样子
渲染成树形的表格须要设置两个地方,首先 <Table>
组件上设置一个 isTree
属性,同时 data
中的数据须要经过 children
来定义关系结构。
<Table data={data} isTree expand height={400}>
data 中的数据结构
[{ labelName: '汽车', status: 'ENABLED', children: [ { labelName: '梅赛德斯-奔驰', status: 'ENABLED', count: 460 } ... ] ... }]
单元格中的内容每每须要能交互的,好比设置为一个链接,或者 hover
的时候能显示一段信息等等。 在 rsuite-table 中,能够对 Cell
进行自定义。
先看一下如下是一个自定义后的表格图例:
好比,显示一个图片,定义一个 ImageCell
组件:
const ImageCell = ({ rowData, dataKey, ...props }) => ( <Cell {...props}> <img src={rowData[dataKey]} width="50" /> </Cell> );
用的时候:
<Column width={200} > <HeaderCell>Avartar</HeaderCell> <ImageCell dataKey="avartar" /> </Column>
好比,要格式化日期,就定义一个 DateCell
组件:
const DateCell = ({ rowData, dataKey, ...props }) => ( <Cell {...props}> {rowData[dataKey].toLocaleString()} </Cell> );
用的时候:
<Column width={200} > <HeaderCell>Action</HeaderCell> <DateCell dataKey="date" /> </Column>
自定义行高
若是在实际应用中须要根据数据内容来定义行高,可使用如下方式
<Table onRerenderRowHeight={(rowData) => { if (rowData.firstName === 'Janis') { return 30; } }} > ... </Table>
可编辑的表格,只须要自定义一个 <Cell>
, 而后经过 state
管理状态。
export const EditCell = ({ rowData, dataKey, onChange, ...props }) => { return ( <Cell {...props}> {rowData.status === 'EDIT' ? ( <input className="input" defaultValue={rowData[dataKey]} onChange={(event) => { onChange && onChange(rowData.id, dataKey, event.target.value); }} /> ) : rowData[dataKey]} </Cell> ); };
<Table>
是的经过 CSS 布局控制也许能实现这个功能,可是在实现的时候,不少地方都是经过 JS 控制高度,好比: 行高、单元格的 left,top 相对位置等等,因此要根据内容来自动行高是比较麻烦的事情,暂时没想到好的解决办法,可是咱们提供了一个 onRerenderRowHeight
函数,可让用户本身根据内容来控制行高。flexGrow
来填充剩余宽度。若是,你对这些问题有好的想法欢迎你 提交 pull request。
若是,你在使用中存在任何问题,能够提交 issues。