對於企業級後臺產品來講,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。