在 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];// 当前值和上一行的值相等,则计数+1if(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;// 补丁:上一列的 keyconst 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];// 当前值和上一行的值相等,则计数+1if(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。 也可以返回一个键名为rowspancolspan的对象。

引用自element-ui官网:Element - The world's most popular Vue UI framework

实际上,2.3.3 的代码中,item 就是当前行,column就是当前列。

剩下的,相信你一定可以!

那,为什么,我没有直接从element-ui的这个方法开始讲述呢?

因为我的实际是服务端渲染,用的模板语言,只能用原生htm了,呜呜呜。

五、参考资料

剑指Offer面试题03-找出数组中重复的数字(5种方法)_JohnArchie的博客-CSDN博客_剑指offer 寻找重复数

分治法_百度百科

保姆级教程 | 表格自动行合并实现相关推荐

  1. 量化交易-利用同花顺量化平台supermind 5行代码搞定多条件选股并微信实时收消息-保姆级教程

    利用supermind 5行代码搞定多条件选股并在微信实时收消息-保姆级教程 前言 对大部分炒股的朋友来说,日常最耗时的就是盯着选股条件然后不停的选股,我经常苦恼于有无程序能自动化实现选股,然后选中之 ...

  2. github博客自动同步到gitee(保姆级教程)

    github博客自动同步到gitee(保姆级教程) 前言: 由于国内网访问github实在太慢!虽然在公司可以连接外网访问还算可以,但是回学校想看看博客或者分享给别人的时候经常会崩掉!于是我想可不可以 ...

  3. Unity教程2:保姆级教程.几行代码实现输入控制2D人物的移动

    目录 人物的创建以及刚体的设置 图层渲染层级设置 角色碰撞箱设置 使用代码控制人物移动 创建脚本文件 初始函数解释 控制移动代码 初始化变量 获得键盘输入 调用函数 手册链接在这:Unity User ...

  4. Tkinter保姆级教程(下)

    继上次肝了几天的Tkinter保姆级教程(上),接下来继续肝教程下,冲冲冲. 目录 Scale控件 Canvas画布控件 Menu菜单控件 Scrollbar滚动条控件 Event事件处理 布局管理器 ...

  5. 图片适应窗口_毕业论文排版保姆级教程——图片和公式排版

    [小技巧]Origin作图过程中如何让图看起来更生动 2020-05-30 [小技巧]简单设置让你origin导出的图片不在有大白边 2020-05-29 [干货放送]萤火科研资源免费赠送第一期--必 ...

  6. 保姆级教程 CSS 知识点梳理大全,超详细!!!

    保姆级教程 CSS 知识点梳理大全,超详细!!! ✴️大家好,我是王同学,好久不见,趁着假期王同学把CSS 知识点梳理了一遍 ✴️如果对你有帮助就给我点个赞吧,这样我们就互不相欠了 ✴️星光不负赶路人 ...

  7. 前端学习笔记 HTML5 保姆级教程

    HTML5 保姆级教程 前端学习路线: HTML→CSS→JavaScript→jQuery→HTML5→CSS3→ES6→Vue.js→webpack→Node.js 除了掌握这些技术,后期我还需要 ...

  8. 50000字,数仓建设保姆级教程,离线和实时一网打尽(理论+实战) 下

    文档大纲: 本文上半部分之前已经发过了,传送门:50000字,数仓建设保姆级教程,离线和实时一网打尽(理论+实战) 上 此篇文章是整个文档的下半部分,将接着上半部分从第五章开始. 五.实时数仓建设核心 ...

  9. 金融数据获取:当爬虫遇上要鼠标滚轮滚动才会刷新数据的网页(保姆级教程)

    目录 1. 谁这么会给我整活儿 2. Selenium模拟网页浏览器爬取 2.1 安装和准备工作 2.2.1 高度判断 2.2.2 顶部距离判断 3: 爬取内容 4: 完整代码,结果展示 1. 谁这么 ...

最新文章

  1. mysql select语句详解_mysql学习笔记之完整的select语句用法实例详解
  2. 2020.12.07.记录
  3. Google protobuf解析消息逻辑的版本问题
  4. redis中的zset
  5. 调用函数,计算分段函数的值
  6. (36) css企业代码书写规范
  7. 非标自动化企业前十名_自动化输送+旋盖,竟如此简单!机械设计教程精选!...
  8. 【优化算法】爬虫搜索算法(RSA)【含Matlab源码 1838期】
  9. JS中 post和get的区别
  10. php 万能密码,万能用户名和万能密码
  11. 聚簇索引,非聚簇索引
  12. 数字图像处理——灰度级、动态范围、对比度
  13. css3 transition属性造成文字抖动
  14. Xshell、Xftp软件评估过期解决方案
  15. Linux:生成core的几种方式
  16. python 爬虫爬取无损图片 批量下载wallhaven超清壁纸
  17. 搜索优化—如何在Google搜索引擎上排名靠前Google左侧排名
  18. 【游戏开发】unity教程4 打飞碟小游戏
  19. 老旧车标致206能卖多少钱?且看团车网老旧车置换拍卖体验
  20. 6个习惯让你立刻远离拖延

热门文章

  1. 大津法Python实现
  2. 刚柔软机器人参数化建模仿真分析--多种案例操作步骤--案例代码分享
  3. Dockerfile、Docker-Compose
  4. struts的勤工俭学系统(带文档和演示)
  5. ls: 读取目录 .: 输入/输出错误 错误处理
  6. 掌握这些电子书搜索技巧与工具,拥有读不尽的书,从此不再闹书荒
  7. 牛津大学团队采用先进机器人技术,推动「人造肌腱」实际应用
  8. 利用python爬取豆瓣音乐_python爬虫之豆瓣音乐top250
  9. Python轻量级爬虫教程-网页下载器
  10. 家用台式计算机硬件配置清单,最新最全台式电脑配置清单2000-5000配置清单