最近做了两个关于表格合并的两个小需求,正好借此机会把行和列的合并来总结一下。
演示代码的技术栈:vben-admin、ant-design-vue
最基础的行合并
rowSpan是行合并属性,例如第一行和第二行合并,我们就这样写:
js
customCell: (_, index) => {
if(index == 0) {
return { rowSpan: 2 }; // 表示从index=0开始跨两行
} else if(index == 1) {
return { rowSpan: 0 } // 上面那一行跨了两行,所以这一行无需展示了
} else {
return { rowSpan: 1 } // 不做处理的行
}
}
带复杂逻辑的行合并
效果图一:按数字递增进行行合并
需求:一个蛋糕最多N个人分,需要设置两个人分按什么比例,三个人分按什么比例,N个人分按什么比例。
转化成页面,就是一个金字塔形的表格,第一行是100%,二三两行合并,然后后面四行合并
这里主要的逻辑点在于寻找行的索引与rowSpan之间的关系:
索引 | rowSpan |
---|---|
0 | 1 |
1 | 2 |
2 | 0 |
3 | 3 |
4 | 0 |
5 | 0 |
6 | 4 |
7 | 0 |
8 | 0 |
9 | 0 |
这种场景下,我们只需要把开始合并的那一列记录下来,其余的都是0,得到这样的数据:
合并起始位置 | 合并几列 |
---|---|
0 | 1 |
1 | 2 |
3 | 3 |
6 | 4 |
10 | 5 |
得到公式:(n-1)n/2
需要合并n行的时候,我们从第几行开始合并呢?
起始行的索引就是把已经合并过的行进行累加:
js
/*
const sum = (maxVal) => {
if(maxVal == 0) {
return 0
} else {
return sum(maxVal - 1) + maxVal
}
}
*/
// 尾递归的优化
const sum = (maxVal, result) => {
if(maxVal == 0) {
return result
} else {
return sum(maxVal - 1, result + maxVal)
}
}
customCell: (_, index) => {
let maxNum = _.maxNum // 人数最大值
if(index == sum(maxNum - 1, 0)) {
return { rowSpan: maxNum };
} else {
return { rowSpan: 0 }
}
},
// 用数学知识直接算好,也就是等差数列求和公式:
customCell: (_, index) => {
let maxNum = _.maxNum // 人数最大值
if(index == (maxNum - 1 * maxNum)/2) {
return { rowSpan: maxNum };
} else {
return { rowSpan: 0 }
}
},
展示的效果:

合并列(进阶)
效果图二:相邻相同的列数据进行合并
90, 100, 100, 99, 100, 100, 100, 99, 99, 90
对应的colSpan值是: [1, 2, 0, 1, 3, 0, 0, 2, 0, 1]
其实就三种状态:列合并起始数字、被合并列(0),无需合并的列(1)
js
function getColSpan(colums, dataObj) {
let arr = colums.map(v => {
return {
key: v.dataIndex,
value: dataObj[v.dataIndex]
}
})
var merged = arr.map((val, i) => {
// 与前后都不相等,就返回1
// 与前面一位相等,就返回0
// 与后面一位相等,就继续往后找,返回找到的长度
let v = val.value
let prev
let next
if (i != 0) {
prev = arr[i - 1].value
}
if (i != arr.length - 1) {
next = arr[i + 1].value
}
if (v != prev && v != next) {
return {
colSpan: 1
}
} else if (v == prev) {
return {
colSpan: 0
}
} else {
let temp = v
let j = i
while (v == temp) {
j = j + 1
temp = arr[j].value // 超出就是null了
}
return {
colSpan: j - i
}
}
})
return merged
}
核心逻辑就在于上面这个方法,处理了所有的场景,每个单元格都赋值了一个colSpan的值。
展示的效果:

总结:代码只是工具,分析问题才是核心。