表格是组件库中交互较为复杂的组件之一,须要面对的状况比较多,单纯靠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中,欢迎多多交流,期待你的加入。