antd Table 组件动态合并单元格
antd Table 组件动态合并单元格
- 前言
- Table 组件静态数据合并单元格
- Table 组件动态数据合并单元格
- 总结
前言
使用 antd 开发 PC端应用的人肯定知道的 Table 组件。一个功能很完善,界面很优化的表格组件。通过查阅官方文档,你可以很轻松地使用这个组件。但是如果表格中涉及到合并单元格昵?
文档中也有关于合并单元格的处理,但是只是静态数据的范例。而我们开发中往往都是获取的动态数据。那么Table 组件改如何动态合并单元格昵?
Table 组件静态数据合并单元格
这里简单地讲一下核心步骤,详细的步骤请直接移步官方文档。
以上是个简单的表格。现在我们将【分类】中相同的值合并,达到如下效果。
实现思路:
- 找到要合并列的第一列所在位置。记为 index。并且设置 rowSpan 为合并总列数。
这里需要将 index 为 0 的 rowSpan 设置为 2。后面的相同项(也就是 index 为 1) 的 rowSpan 设置为 0。 - 同样地,需要将 index 为 2 的 rowSpan 设置为 1(没有相同项,就设置为1。也可以不用设置 rowSpan)。
最后,将 index 为 3 的 rowSpan 设置为 3,后面的相同项(也就是 index 为 4/5) 的 rowSpan 设置为 0。
const columns = [
{
title: '分类',
dataIndex: 'category',
render: (value, row, index) => {
const obj = {
children: value,
props: { },
};
if (index === 0) {
obj.props.rowSpan = 2;
}
if (index === 1) {
obj.props.rowSpan = 0;
}
//没有相同项时可以不用设置 colSpan
if (index === 2) {
obj.props.rowSpan = 1;
}
if (index === 3) {
obj.props.rowSpan = 3;
}
if (index === 4) {
obj.props.rowSpan = 0;
}
if (index === 5) {
obj.props.rowSpan = 0;
}
return obj;
}
},
{
title: '名称',
dataIndex: 'name',
},
{
title: '评价',
dataIndex: 'desc',
},
];
const data = [
{"category":"水果",
"name": "桃子",
"desc": "好吃"
},{
"category":"水果",
"name": "梨子",
"desc": "真好吃"
},{
"category":"蔬菜",
"name": "茄子",
"desc": "真TM好吃"
},{
"category":"家禽",
"name": "牛肉",
"desc": "太好吃了"
},{
"category":"家禽",
"name": "羊肉",
"desc": "好吃到停不下来"
},{
"category":"家禽",
"name": "猪肉",
"desc": "吃不起,太贵"
}
]
实现静态数据单元格合并总结一下就是两点。
- 设置合并列的第一列 rowSpan 为合并数。
- 设置其他相同项 rowSpan 为 0。
以上是处理静态数据,我们能够很快找到合并列的第一列以及合并数。但是如果是动态数据昵?我们不知道有几个【水果】,几个【蔬菜】和几个【肉类】。
Table 组件动态数据合并单元格
我最开始的想法是在 columns 对象的 render 中去完成这个功能。因为 render 中本来就是循环,暴露的参数又有限,所以很难去对比每个对象的相同项。后来经大牛指导可以先处理数据源。render 中直接使用带有逻辑的数据源就好了。
于是茅塞顿开,涉及到要处理数据逻辑,那么这个问题就转换成了一道算法题。
算法题:
假如在真实的开发场景中接口返回如下数据格式。
let data = [
{
"category":"水果",
"name": "桃子",
"desc": "好吃"
},{
"category":"水果",
"name": "梨子",
"desc": "真好吃"
},{
"category":"蔬菜",
"name": "茄子",
"desc": "真TM好吃"
},{
"category":"家禽",
"name": "牛肉",
"desc": "太好吃了"
},{
"category":"家禽",
"name": "羊肉",
"desc": "好吃到停不下来"
},{
"category":"家禽",
"name": "猪肉",
"desc": "吃不起,太贵"
}
]
界面要求:将【分类】中相同值进行合并单元格。
经过上面对静态数据的逻辑处理,我们知道需要将每个相同项的第一项的 rowSpan 设置为合并数。并且其他的相同项设置为 0。得到后的数据格式如下:
data = [
{
"category":"水果",
"name": "桃子",
"desc": "好吃",
"rowSpan":2
},{
"category":"水果",
"name": "梨子",
"desc": "真好吃",
"rowSpan":0
},{
"category":"蔬菜",
"name": "茄子",
"desc": "真TM好吃",
"rowSpan":1
},{
"category":"家禽",
"name": "牛肉",
"desc": "太好吃了",
"rowSpan":3
},{
"category":"家禽",
"name": "羊肉",
"desc": "真不错",
"rowSpan":0
},{
"category":"家禽",
"name": "猪肉",
"desc": "吃不起,太贵",
"rowSpan":0
}
]
算法题解思路:
- 定义 count 为重复项的第一项索引。
- 定义 indexCount 为下一项的索引。
- 取出数组第一个对象 item,先将 rowSpan 初始化为1。然后将 item 的【分类】值与下一项进行对比,发现一个相同项,rowSpan 就加1,并且下一项的 rowSpan 设置为 0。每对比一次,indexCount 累加。当没有遇到相同项时将 indexCount 赋值给 count。获取下一个相同项首项。
- 按照1/2/3的步骤,数组循环完之后就可以正确的赋值 rowSpan。
代码:
import dataJson from './data/Table.json';
let data = dataJson.data;
let field = 'category';
const changeData = (data,field)=>{
let count = 0;//重复项的第一项
let indexCount = 1;//下一项
while (indexCount<data.length){
var item = data.slice(count,count+1)[0];//获取没有比较的第一个对象
if(!item.rowSpan){
item.rowSpan = 1;//初始化为1
}
if(item[field] === data[indexCount][field]){ //第一个对象与后面的对象相比,有相同项就累加,并且后面相同项设置为0
item.rowSpan++;
data[indexCount].rowSpan = 0;
}else {
count = indexCount;
}
indexCount++;
}
}
changeData(data,field);//处理数据
总结
总结就一句话:数据处理逻辑思维,MVC 和 MVP 思想。
最开始做这个功能的时候,我一直沉浸在渲染页面 column 对象的 render 函数里去处理逻辑。但是 render 里面本来就是一个循环,参数有限,所以思维变得非常拘泥。
经大神点播可以先把数据源处理好再直接渲染页面,我才发应过来这种业务逻辑最好的处理方式就是直接处理数据状态,用数据状态代替业务逻辑。这不就是所谓的 MVC 和 MVP 思想吗?所以程序设计思维真的很重要。
还没有评论,来说两句吧...