如何使用ElementUI的table组件来实现单元格的行合并
前言
最近在编写一个值班的排班表,然后中间涉及到了表格应用。并且还要做出类似这种效果的行合并效果:
然后就开始找组件了。Html的table是有rowsSpan
和colsSpan
的属性来实现行合并和列合并的。然后就在网上找资料,发现没有几篇能把这两个属性将好的,并且大多数讲的都是列合并,没有行合并。我自己用的是Vutify组件进行页面绘制,但是Vutify的table组件没有合并的api。要实现的话,就得重写body的插槽,也就等于手写table。而且,重写也只能合并列,行的合并还是没法实现。然后Vutify其实是继承ElementUI实现的,然后又看了一下ElementUI的el-table,才发现了较好的解决方式。
ElementUI实现
官方主要是在el-table上加入一个objectSpanMethod
来实现的
objectSpanMethod的具体实现:
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2,
colspan: 1
};
} else {
return {
rowspan: 0,
colspan: 0
};
}
}
}
tableData的数据模型:
tableData: [{
id: '12987122',
name: '王小虎',
amount1: '234',
amount2: '3.2',
amount3: 10
}, {
id: '12987123',
name: '王小虎',
amount1: '165',
amount2: '4.43',
amount3: 12
}, {
id: '12987124',
name: '王小虎',
amount1: '324',
amount2: '1.9',
amount3: 9
}, {
id: '12987125',
name: '王小虎',
amount1: '621',
amount2: '2.2',
amount3: 17
}, {
id: '12987126',
name: '王小虎',
amount1: '539',
amount2: '4.1',
amount3: 15
}]
}
然后效果图:
但看这个效果,其实还不错,但其实有一些问题:
- 数据模型中的ID不是连续的,合并行的时候取得是该组的第一个元素的ID
- 数据模型只能是两两配对,中途不能出现其他配对的组合,否则,数据会错乱。由于,例子中的数据长度只有5条,最后一条没有合并的选项,因此看不出问题
实际应用中,后端返回的不会只是固定的两两或者三三或者固定行合并的数据。中途可能穿插四四或者五五的都有。单靠例子的实现是行不通的。好在本人研究了十分钟后,想到了解决办法。
解决非固定行合并
解决的关键在于读懂objectSpanMethod
这个方法,我们再回顾一下官方的实现模式:
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) { // 表示只操作第一列
if (rowIndex % 2 === 0) { // 表示只对奇数行进行操作(注意Index从0开始,因此不是偶数行)
return {
rowspan: 2, //需要合并的行数
colspan: 1 // 合并后的列数
};
} else {
return {
rowspan: 0, // 0消掉这一行
colspan: 0 // 0消掉这一列 二者合并起来就是消掉这一个单元格子
};
}
}
}
注意,这里的row
打印值得话,实际就是每个数据条目本身。
我将代码注释了一下,但是一般人估计还是看不懂,那我再上一张图:
蓝色的数据行是要合并的一组,这里是2行。黄色行是该组合并的基底行
格子(奇数行),黑色行是消掉的没有绘制的格子。合并过后,黄色的格子就填满了蓝色格子区间,实现了合并。最后一行因为没有待合并的行,所以就是它自己。
读懂这个例子后,我们应该清楚要合并的话,需要知道:
基底行
格子所在的行的索引值- 从基底行开始,向下要
合并的行的数量
官方实例中,这两条恰好都是写死的固定值,但是我们实际应用中,可能就不是了。
我在写这个查询表的时候,后端给我的数据长这个样子,一个2-3-2组合:
data=[
{
day:'2023-01-01',
workers:[
{
name:'小明',
mobile:'1334455667788'
},
{
name:'小黄',
mobile:'1334455667788'
}
]
},
{
day:'2023-01-02',
workers:[
{
name:'小郑',
mobile:'1334455667788'
},
{
name:'小朱',
mobile:'1334455667788'
},
{
name:'小刘',
mobile:'1334455667788'
}
]
},
{
day:'2023-01-03',
workers:[
{
name:'小罗',
mobile:'1334455667788'
},
{
name:'小李',
mobile:'1334455667788'
}
]
}
]
看过官方的例子后,其实应该把数据转换为:
[
{
day:'2022-01-01',
name:'小明',
mobile:'1334455667788'
},
{
day:'2022-01-01',
name:'小黄',
mobile:'1334455667788'
},
{
day:'2022-01-02',
name:'小郑',
mobile:'1334455667788'
},
{
day:'2022-01-02',
name:'小朱',
mobile:'1334455667788'
},
{
day:'2022-01-02',
name:'小刘',
mobile:'1334455667788'
},
{
day:'2022-01-03',
name:'小罗',
mobile:'1334455667788'
},
{
day:'2022-01-03',
name:'小李',
mobile:'1334455667788'
}
]
我在操作数据转换的时候,增加了isFirst
和sameIndex
属性:
const dataList=[] // 最终转换好的数据集合
data.forEach(({ day, workers = [] }) => {
if (Array.isArray(workers) && workers.length > 0) {
workers.forEach(({ name, mobile }, index) => {
dataList.push({
date: day,
name,
mobile,
isFirst: index === 0, // 合并行的时候用于标记起始合并的位置
sameIndex: workers.length // 需要合并的行数
})
})
} else {
// 如果没有返回worker就手动加个占位用
dataList.push({
date: day,
name: '--',
mobile: '',
isFirst: true,
sameIndex: 1
})
}
})
然后重写objectSpanMethod
方法:
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
if (row.isFirst === true) {
return {
rowspan: row.sameIndex,
colspan: 1
}
} else {
return {
rowspan: 0,
colspan: 0
}
}
}
}
由于row就是我们每行实际的数据,isFirst
实际就是定位基底行的位置,sameIndex
就是从该行开始一共要合并的行的数量,这样我们就比较巧妙地实现了行合并效果,而且是动态自动合并行数地。
最后看一下成品效果图: