表格是組件庫中交互較爲複雜的組件之一,須要面對的狀況比較多,單純靠css是沒法寫出完備的表格組件的。咱們的表格組件應該具有易用易拓展能充分適應需求的特性。在前端所接觸的表格中,最多見到也是最基礎的就是普通的二維表格,咱們能夠先從這個簡單的二維表格入手,一步步去完善這個組件。css
如下的Demo均以React爲例,其中使用了集成在SluckyUI中的樣式,雖然說Vue,Angular的實現有些不一樣,但差異不大,思路是同樣的。前端
首先一個表格由表頭和表內容組成,其中內容的數據結構是二維的,分別是行和列。webpack
想象一下,表格的展現須要一種怎樣的數據結構呢?沒錯,後端接口這時應該會返回一個像這樣的數組,其中數組即做爲列,數組中的對象即包含一行中全部的數據。git
const data=[{
sex:'man',
age:'19'
},{
sex:'feman',
age:'20'
}]
複製代碼
如今肯定了輸入表格組件的數據結構。接下來就是靈魂三問,表格怎樣才能知道對應的key渲染到哪一列,怎樣才知道列的長度,怎樣才知道列的標題呢?嗯,這個時候咱們的表格組件就應該要有一個選項可針對每一列進行配置。github
ok,就像這樣對每一列進行相應的配置web
dataConf=[{
title:'自定義列的名稱(別名)',
name:'匹配data裏對應的字段如age',
width:'200px||20%'
},{
title:'年齡',
name:'age',
width:'20%'
}]
複製代碼
那麼關鍵的地方來了,如今咱們有了配置和數據,須要作的就是根據所寫的配置對輸入表格的數據進行相應的渲染。sql
即列的標題,這一部分是與表的內容分開的,因此咱們單獨去處理它後端
...
<div className="table-head">
{
this.props.dataconf.map((conf, i) => {
return <div style={{ 'width': conf.width }} key={i}>{conf.title||''}</div>
})
}
</div>
...
複製代碼
這個處理很簡單,一個循環就搞定了數組
渲染一個二維的數據結構也不難,兩個普通的for
循環完成了。sass
// table.jsx
...
<div className="table-body">
{
dataset.map((data, i)=>{
return (
<div className="table-row">
{
dataconf.map((conf, k) => {
return <div className="table-data">data[conf.name]</div>
})
}
</div>
)
})
}
</div>
...
複製代碼
就這樣,一個表格組件的基本部分就搭建起來了。
曾經想過直接用<table></table>
系列直接去解決表格的佈局問題,這樣作的確方便快捷,改動又小。但後來隨着需求逐漸變得複雜時,發現單單靠<table></table>
系列會有不少問題沒法解決,面對複雜需求時侷限性比較大。 在對比幾種佈局方式以後,決定使用dev-flex
佈局,緣由很簡單,dev-flex
佈局簡潔而強大,可以很好地處理各類複雜的需求。
//table.css
//表格中一行的樣式
.table-row{
display: flex;
justify-content: space-between;
align-items: center;
}
複製代碼
Note:用div-flex這種佈局很靈活,對不一樣場景的適應性很強,可是有一個小缺點,這是後來才知道的,就是鼠標去框選複製渲染出來的表格內容後,再粘貼到excel中就會出現格式混亂,而用傳統table佈局就能顯示出完整表格,也不知道excel是何時支持表格識別的。。。
不少時候咱們須要的表格不僅僅只是去展現數據,還有對列進行排序,對行進行增刪減。關鍵點又來了,咱們究竟怎樣才能方便地將對應行的數據傳到須要用到的地方呢?聽起來可能很繞,就拿刪除某行數據來說,咱們須要作的就是獲取對應行的id,而後發起網絡請求,刪除id對應的行。嗯,思路很清晰。若是用React去實現的話,應該怎樣作呢?
// table.jsx
...
<div class="table-body">
{
dataset.map((data, i)=>{
return (
<div className="table-row">
{
dataconf.map((conf, k) => {
return (
<div style={{ 'width': conf.width }}>
{
!conf.rander?<div className="table-data">data[conf.name]</div>:null
}
{
conf.rander?<div className="table-data">{conf.rander(data, i)}</div>:null
}
</div>
)
})
}
</div>
)
})
}
</div>
...
複製代碼
沒錯,在恰當的地方很巧妙地設置了一個函數回調,用來傳出對應行的數據,而後咱們這樣去進行配置。
dataConfig=[{
width: '10%',
rander:(data,index)=>{
return <div onClick={()=>{
conslog(data)
http.delRecord(data.id)
}}>刪除</div>
}
}]
複製代碼
若是用Angular2+去實現相似的Api的話,最關鍵就是要解決做用域傳遞的問題,沒有jsx這麼靈活,這裏就不作過多分析了。
固然,咱們的表格有了rander選項以後,就已經將用戶操做徹底解耦出來。這種狀況下再爲表格組件集成用戶操做相關的功能只是爲了方便調用。
// table.jsx
...
<div class="table-body">
{
dataset.map((data, i)=>{
return (
<div className="table-row">
{
dataconf.map((conf, k) => {
return (
<div style={{ 'width': conf.width }}>
...
<div className="d-il">
{
!conf.pipe ? (
<span className="p-r z10">{data[conf.name]}</span>
) : null
}
<progress max="100" value={conf.progress && conf.progress(data)}
className="progress-loading"></progress>
</div>
...
</div>
)
})
}
</div>
)
})
}
</div>
...
複製代碼
dataConfig=[{
width: '10%',
title: 'progress',
width: '20%',
progress: () => {
//返回0-100表示進度百分比
return 50
},
}]
複製代碼
// table.jsx
...
<div class="table-body">
{
dataset.map((data, i)=>{
return (
<div className="table-row">
{
dataconf.map((conf, k) => {
return (
<div style={{ 'width': conf.width }}>
...
<div class="pop-box">
<div className="pop-toggle ptb4 mlr4">
<div className="pop-main pr8">
<div className="pop-content">
{conf.popup(data, i)}
</div>
</div>
</div>
</div>
...
</div>
)
})
}
</div>
)
})
}
</div>
...
複製代碼
dataConfig=[{
width: '10%',
title: 'popup',
width: '20%',
popup: () => {
return <button>氣泡提示</button>
},
}]
複製代碼
完整版請看這裏完整版Table組件&&所用到的樣式,以爲不錯的話不妨點個star哈哈。
注:樣式又是另外一個話題了,可參看《Re從零開始的UI庫編寫生活之規範制定》
其實表格組件並不像想象中那麼難,只要理清楚思路,把該解耦的部分抽離出來,剩下的工做就是發揮想象力,往裏面添加各類功能而已。更多有趣的組件盡在SluckyUI中,歡迎多多交流,期待你的加入。