开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
element-ui 的 Table 组件有一个合并行或列的功能,官方分别给出了合并行和列的示例,但不够清晰明确,初看之下并不能很好的理解和运用,而且官方示例只做了一个简单展示,要是遇到更多层次的合并,得有一番费时的磋磨才能折腾出来。
那么,如何将后端接口返回的数据进行合并行或列呢?
下面是一个完成的效果,我们会对一级菜单做对应的行合并,二级菜单做行合并,这样在表格内做出一个整洁的层级结构。
按照上面的效果图,后端接口会返回一个三层的树形结构,我们会对这个数据进行处理,来达到要求。
需要注意的是,树形结构的子级字段不能是 children,不然 Table 组件会自动渲染成树形表格。
数据结构
后端返回以下数据结构,一个典型的三级树形结构
[
{
"id":"160502903066808320",
"parentId":"",
"name":"一级菜单",
"parentName":null,
"type": [
{
"id":"160502903075196928",
"parentId":"160502903066808320",
"name":"二级菜单",
"parentName":"一级菜单",
"sortList":[]
},
{
"id":"375627622619828224",
"parentId":"160502903066808320",
"name":"二级菜单",
"parentName":"一级菜单",
"sortList":[
{
"id":"400237691110522880",
"parentId":"375627622619828224",
"name":"三级菜单",
"parentName":"二级菜单"
},
]
]
}
// ...
]
span-method
要想解决问题,不要急于立马就想得到一个能用的代码,静下心来,一步一步来。
先来理解一下 span-method 函数的使用。
该函数的参数是一个对象,里面包含四个属性:
row:当前行的数据column:当前列rowIndex: 表格的行索引,不包括表头,从0开始columnIndex:表格的列索引,从0开始
spanMethod ({ row, column, rowIndex, columnIndex }) {
// ...
}
最后我们会通过该函数来决定哪些行是需要进行合并的。
思路
对于上面的结果示意图中,我们其实要合并的是表格第一列和第二列的行。
在拿到后端接口数据之后,我们会调用 formatTable 函数对数据进行遍历处理,一个基本的思路是:
- 创建变量用于保存需要合并的行数和新的表格数据。
- 遍历接口数据,优先统计二级级菜单下的三级菜单的数量,该数量就是二级菜单需要合并的行数。
- 遍历到所有二级菜单的最后一个三级菜单时,保存一个行数,该行数就是一级菜单需要合并的行数。
- 给每一条新的表格数据添加 nameIndex 和 typeIndex 属性来控制是否需要合并行。
- 在 span-method 函数中处理行的合并。
下面直接来看代码。
// 格式表格数据,获取 nameIndex 和 typeIndex
formatTable() {
let newDate = [] // 存储处理后的表格数据
let nameIndex = [] // 保存第一列需要合并的行数
let typeIndex = [] // 保存第二列需要合并的行数
let total // 用于统计第一列需要合并的行数
// 遍历接口数据
tableData.forEach((v, index, list) => {
if (v.type && v.type.length) {
// 先初始化统计行数
total = 0
v.type.forEach((subV, i, typeData) => {
// 先统计三级菜单的数量,因为三级菜单的数量有多少,对应的二级菜单就需要合并多少行
if (subV.sortList && subV.sortList.length) {
subV.sortList.forEach((ss, k, data) => {
// 判断是否是最后一个三级菜单
if (k === data.length - 1) {
typeIndex.push(data.length) // 把每一个二级菜单下的三级菜单数量保存起来
total += data.length // 一级菜单需要合并的行数等于对应的二级菜单下的三级的所有总和
}
// 新的数据
newDate.push({
id: ss.id || subV.id || v.id,
parentName: v.name,
name: subV.name,
sortName: ss.name
})
})
} else {
// 如果没有二级菜单,我们需要填充一个空数据,以保持表格列的对齐,此时 total 和 tyoeIndex 要加 1
// 再加上二级菜单的数量,获取到一级菜单该合并的行数, 二级菜单的行合并也是同理
total += 1
typeIndex.push(1)
getDate.push({
id: subV.id,
parentName: subV.parentName,
name: subV.name,
sortName: '无'
})
}
// 循环完成后把 total 保存起来
if (i === typeData.length - 1) {
nameIndex.push(a)
}
})
}
})
// 然后给 newData 的数据添加 nameIndex 和 typeIndex 属性来控制是否合并
let k = 0
let t = 0
// 对于第一列的合并,并不是每一条数据都需要设置 nameIndex,比如第一条数据的nameIndex等于10,也就是需要合并10行,,那么下一个需要设置nameIndex的数据的索引是10,然后重新设置索引,因为数量是经过上面计算的,所以不用担心会将nameIndex设置给错误的数据,下面的typeIndex同理。
nameIndex.forEach((v, i, nameArr) => {
if (nameArr[i]) {
getDate[k].nameIndex = nameArr[i]
k += nameArr[i]
}
})
typeIndex.forEach((v, i, typeArr) => {
if (typeArr[i]) {
getDate[t].typeIndex = typeArr[i]
t += typeArr[i]
}
})
// 最后重新赋值给tableData
this.tableData = getDate
}
接着编写 spanMethod 函数来控制哪些行是需要合并的。
spanMethod ({ row, column, rowIndex, columnIndex }) {
// 对第一列做行合并
if (columnIndex === 0) {
if (row.nameIndex) { // 如果有值,说明需要合并
return [row.nameIndex, 1]
} else return [0, 0]
}
// 对第一列做行合并
if (columnIndex === 1) {
if (row.typeIndex) {
return [row.typeIndex, 1]
} else return [0, 0]
}
}
至此,我们就完成了行的合并,其实对于列的合并也是同理,大家可以自己上手操作试试。