保姆级教程 | 表格自动行合并实现
在 element-ui 和 antv 中都有表格合并,但如何确定哪几行要合并呢? 在随机给定数据的情况下,如何实现自动合并呢?本文将一个一个地解答这些问题。
并在延伸中,谈到了,如何将本文的方法应用到element-ui和antv中。
一、需求描述及样例展示
① 自动行合并
② 当两列为层级关系的时候,合并要有层级关系。
在举例之前,我们先规定一下展示数据。
展示的是不同 app 在不同手机型号下的下载量。
数据为:
-
// 行信息
-
const columns = [
-
{
-
key:'app',
-
label:'app',
-
},
-
{
-
key:'phone',
-
label:'手机类型',
-
},
-
{
-
key:'phoneType',
-
label:'手机型号',
-
},
-
{
-
key:'downloadCount',
-
label:'下载量',
-
},
-
];
-
-
//数据信息
-
const data = [
-
{
-
app:'微信',
-
phone:'iphone',
-
phoneType:'iphone11',
-
downloadCount:'138,999,999'
-
},
-
{
-
app:'微信',
-
phone:'iphone',
-
phoneType:'iphone12',
-
downloadCount:'138,999,999'
-
},
-
{
-
app:'微信',
-
phone:'huawei',
-
phoneType:'mate40',
-
downloadCount:'138,999,999'
-
},
-
{
-
app:'微信',
-
phone:'huawei',
-
phoneType:'mate40pro',
-
downloadCount:'138,999,999'
-
},
-
{
-
app:'抖音',
-
phone:'huawei',
-
phoneType:'mate40',
-
downloadCount:'138,999,999'
-
},
-
{
-
app:'抖音',
-
phone:'iphone',
-
phoneType:'iphone12',
-
downloadCount:'138,999,999'
-
},
-
{
-
app:'抖音',
-
phone:'iphone',
-
phoneType:'iphone11',
-
downloadCount:'138,999,999'
-
},
-
]
常规表格展现结果为:
APP | 手机类型 | 手机型号 | 下载量 |
---|---|---|---|
微信 | iphone | iphone11 | 138,999,999 |
微信 | iphone | iphone12 | 138,999,999 |
微信 | huawei | mate40 | 138,999,999 |
微信 | huawei | mate40pro | 138,999,999 |
抖音 | huawei | mate40 | 138,999,999 |
抖音 | iphone | iphone12 | 138,999,999 |
抖音 | iphone | iphone11 |
138,999,999 |
产品最终要的结果是:
这里要注意的是:
虽然手机类型都是华为,但如果是不同 app,也不能合并。
虽然手机型号都是 mate40 pro,也要依据 app 是否相同才能进行合并。
二、需求剖析
2.1 合并功能原生实现
这里我们以原生html实现这种合并功能。
先来看看,html 实现表格的行合并的方式,代码如下。
-
<table border="1">
-
<tr>
-
<th>First Name</th>
-
<th>Bill Gates</td>
-
</tr>
-
<tr>
-
<td rowspan="2">Telephone:</th>
-
<td>555 77 854</td>
-
</tr>
-
<tr>
-
<td style="display:none;">Telephone:</th>
-
<td>555 77 855</td>
-
</tr>
-
</table>
效果图如下:
可以得知,原生html 是通过 在列上设置 rowspan 来实现行合并。rowspan 的 数值为几则合并几行。
2.2 vue 实现表格渲染
但在实际需求中,表格的渲染一定是批量完成的。
以上述手机下载量数据为例,在vue中,渲染这个表格的代码为:
-
<table>
-
<tr>
-
<td v-for="column in columns">
-
{{column.label}}
-
</td>
-
</tr>
-
<tr v-for="(item, index) in data">
-
<td v-for="column in columns">
-
{{item[column.key]}}
-
</td>
-
</tr>
-
</table>
2.3 vue 实现表格行合并渲染
由上述信息可知,为了实现行合并,我们需要知道,每一列数据中,
①开始合并的行,在开始合并的行添加 rowspan 和 数值
②结束合并的行,在开始合并行和结束合并行之间的所有行style 添加 display :none。
将上述三个需要计算的数值可以抽象为:
-
/** 合并行 */
-
interface MergeRow {
-
/** 开始合并的行 */
-
start: number;
-
-
/** 开始合并的行 */
-
end: number;
-
-
/** 一共合并的行数: end - start 1 */
-
count: number;
-
}
2.3.1 计算行合并
计算以上三个数值,可以抽象为: 在数组中,找到连续重复的数。
这是一个很简单的OJ问题,需要一个变量记录个数即可。 那么,把这个问题稍稍扩展一下,变成,找到数组中有几组连续重复的数,并记录开始重复,结束重复和重复数量。
那么解法就变成了如下代码:
-
const {data, columns} = table;
-
const newColumns = columns.map((column)=>{
-
/** 当前列的key */
-
const valKey = column.key;
-
-
/** 当前列中需要 行合并的信息 */
-
const merge:MergeRow[] = [];
-
-
//第一行数据
-
let lastVal = data[0][valKey];
-
let valNum = 0;
-
let startIndex = 0;
-
-
data.forEach((item,index)=>{
-
/** 当前行,当前列对应的数值 */
-
const curValue = item[valKey];
-
-
// 当前值和上一行的值相等,则计数 1
-
if(curValue === lastVal) {
-
valNum ;
-
}
-
-
// 如果当前值和上一行的值不相等,并且计数大于1的话,则上几行存在相邻相等的数值,需要记录
-
if(curValue !== lastVal) {
-
merge.push({
-
start:startIndex,
-
end: index - 1,
-
count: valNum,
-
});
-
// 如果当前值和上一行的值不相等,并且计数小于等于1. 则上一行没有数据需要记录,进行覆盖
-
lastVal = curValue;
-
valNum = 1;
-
startIndex = index;
-
}
-
-
if(index === data.length -1) {
-
merge.push({
-
start:startIndex,
-
end: index - 1,
-
count: valNum,
-
});
-
}
-
-
-
});
-
column.merge = merge;
-
return column;
-
});
注意,在在上面的代码中,我把每一列中,存在的合并信息,存到了merge属性中。
这是为了渲染的时候可以读取这些信息准备的。
2.3.2 vue 渲染行合并
渲染的代码如下:
-
<table>
-
<tr>
-
<td v-for="column in columns"> {{column.label}}</td>
-
</tr>
-
<tr v-for="(item,index) in data">
-
<td v-for="column in columns"
-
display="display:`${colum.merge ? colum.merge.find(m=>m.start == index) ? 'auto':'none':'auto'}`"
-
rowspan="`${colum.merge && colum.merge.find(m=>m.start == index) ? colum.merge.find(m=>m.start == index).count : 1}`"
-
>{{item[column.key]}}</td>
-
</tr>
-
</table>
主要是添加了display 和 rowspan的逻辑。
display这里使用了两次三元运算符,
第一次,判断当前列的数据中是否存在merge ,如果不存在merge,则当前行正常渲染;
第二次,当存在merge的时候,判断是否是开始合并行,如果是,则正常渲染,不是则隐藏当前面行。
rowspan 只使用了一次三元判断,判断是否是开始合并行,如果是则读取count, 不是则为1.
2.3.3 级联渲染
看似我们已经实现了自动行合并,但,实际还有一个问题,上述方法,每一列的行合并是独立的。
再看这个图,手机类型这一列,有三行都是huawei,但是,不隶属于同一个app,所以不能合并。
为了解决这个问题,我们需要在计算合并的时候,打一个补丁:
判断一下,当这两行相等的时候,上一列的这两行是否也相等。
友情提示: 可看补丁部分。
-
const newColumns = columns.map((column,colIndex)=>{
-
/** 当前列的key */
-
const valKey = column.key;
-
// 补丁:上一列的 key
-
const prevColKey = colIndex -1 >= 0 ? columns[colIndex - 1].key : valKey;
-
-
/** 当前列中需要 行合并的信息 */
-
const merge:MergeRow[] = [];
-
-
let lastVal = data[0][valKey];
-
// 补丁:上一列当前行的值
-
let lastPrevColVal = data[0][prevColKey];
-
let valNum = 0;
-
let startIndex = 0;
-
-
data.forEach((item,index)=>{
-
/** 当前行,当前列对应的数值 */
-
const curValue = item[valKey];
-
const curPrevColValue = item[prevColKey];
-
-
// 当前值和上一行的值相等,则计数 1
-
if(curValue === lastVal) {
-
// 补丁: 判断上一列是否相等
-
if(lastPrevColVal === curPrevColValue) {
-
valNum ;
-
} else {
-
merge.push({
-
start:startIndex,
-
end: index - 1,
-
count: valNum,
-
});
-
// 如果当前值和上一行的值不相等,并且计数小于等于1. 则上一行没有数据需要记录,进行覆盖
-
lastVal = curValue;
-
// 这里也要重新赋值
-
lastPrevColVal = curPrevColValue;
-
valNum = 1;
-
startIndex = index;
-
}
-
}
-
-
// console.log('this is index', curValue, lastVal, valNum);
-
// 如果当前值和上一行的值不相等,并且计数大于1的话,则上几行存在相邻相等的数值,需要记录
-
if(curValue !== lastVal) {
-
merge.push({
-
start:startIndex,
-
end: index - 1,
-
count: valNum,
-
});
-
// 如果当前值和上一行的值不相等,并且计数小于等于1. 则上一行没有数据需要记录,进行覆盖
-
lastVal = curValue;
-
// 这里也要重新赋值
-
lastPrevColVal = curPrevColValue;
-
valNum = 1;
-
startIndex = index;
-
}
-
-
if(index === data.length -1) {
-
merge.push({
-
start:startIndex,
-
end: index - 1,
-
count: valNum,
-
});
-
}
-
-
-
});
-
merge.length && (column.merge = merge);
-
return column;
-
});
三、总结
到此为止,也就实现了表格的自动级联行合并。
其实预计这是一篇很短就可以说清楚的问题。但没想到写了整整半天。
在写的过程中,遇到的第一个问题是:如何把问题界定清楚?
也就是文章的第一部分。 为什么这么难?因为在跟产品讨论时,不需要上下文,天然我们明白彼此的问题。但读者并不清楚需求上下文的,所以我需要站到一个产品的角度去描述这个需求的边界。
第二个问题是,如何告诉阐述思考的过程?
这也是一定要写这篇文章的原因,在工作的这一年里,我第一次把学生时代的内容进行了实战,发现了很多落地的实践。 但在现在的技术博客和当今的大学计算机教学中,都极少见到类似文章。所以,就想通过本文展示如何将具象问题抽象成一个教科书问题。
即是希望给看到这篇文章的学生们一点工程视野,也想听听各位大佬的看法,看我思考问题是否有所偏差,这个问题有没有更好的解决方法。
那么,针对这个问题,我最终确定了如下思路:
① 用demo 界定问题。
② 放置前置知识(table 行合并 和vue 渲染表格)
③ 拆解行合并问题,并进一步抽象细化成数组重复问题。
④ 解决数组重复问题
⑤ 向上包装,解决行合并。
⑥ 向上包装,解决级联行合并。
⑦ 实现 vue 渲染。
这个过程可以说是一个经典的洋葱式思考,层层抽丝剥茧,找到问题的根源。 然后,再一层层往上包装。
私以为,这种解决问题的方式和算法中的分治法异曲同工,其精髓都是问题细化,逐步击破。
四、延伸
这一部分稍稍谈一下,如何把计算得到的合并参数应用到element-ui中。
通过给
table
传入span-method
方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row
、当前列column
、当前行号rowIndex
、当前列号columnIndex
四个属性。该函数可以返回一个包含两个元素的数组,第一个元素代表rowspan
,第二个元素代表colspan
。 也可以返回一个键名为rowspan
和colspan
的对象。引用自element-ui官网:Element - The world's most popular Vue UI framework
实际上,2.3.3 的代码中,item 就是当前行,column就是当前列。
剩下的,相信你一定可以!
那,为什么,我没有直接从element-ui的这个方法开始讲述呢?
因为我的实际是服务端渲染,用的模板语言,只能用原生htm了,呜呜呜。
五、参考资料
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfjjfge
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24