表格实现合并单元格

实现的效果

在这里插入图片描述

一、列合并

此需求的列合并比较简单, 直接使用el-table-column包括即可

<el-table-column align="center" sortable label="目标"><el-table-column prop="target1" sortable label="预设目标" /><el-table-column prop="target1" sortable label="目标值" />
</el-table-column>

二、行合并

1. 排序

1)原因

因为哪些单元格需要合并,哪些单元格就必须挨着,不挨着就无法进行单元格合并

2)实现思路

1、使用sort
2、由于大多数场景判断的字段都是字符串格式,不能直接使用a-b的形式来判断,所以使用判断大小来代替;
3、由于可能存在多个判断条件,比如按照学校和专业排序,学校相同的还需要按专业排序;
4、排序规则

  • 如果a.xx < b.xx,则返回-1,代表升序;
  • 如果a.xx > b.xx,则返回1,代表降序;
  • 如果相等,则需要判断是否还有其他判断条件,如果没有,则返回0,代表不做处理;如果有,则执行递归,继续以其他判断条件按照以上两个步骤进行判断大小;

3)代码实现

/*** 按一系列参数排序* @param {*} table 原数组* @param {*} names 排序的列的数组*/
getSortArr(table, names) {return table.sort((a, b) => {function sortFn(names, index) {const itemA = a[names[index]]const itemB = b[names[index]]if (itemA < itemB) {return -1;} else if (itemA > itemB) {return 1;} else {// 如果当前列的值相同// 如果最大列索引值还大于当前列索引,则递归, 判断下一列的值,否则返回0即可if (names.length - 1 > index) {return sortFn(names, index + 1);} else {return 0;}}}return sortFn(names, 0)});
},

2. 生成单元格数据

1)原因

因为每一个单元格是否需要合并、是否需要被合并,以及需要合并的话要合并多个单元格,需要用一个固定的数据来控制

2)实现思路?

1、遍历排序之后的数据,判断该项是否与前一项的条件相等(第一项无需判断)
2、判断条件是有层级的,比如需要判断学校和专业,如果当前是在判断学校是否相等,那么就不需要考虑专业的情况;但如果当前是在判断专业,那么必须保证两者学校也是同一个,否则不能作合并处理;
3、定义一个数组,用于存储各个单元格的值;当不需要作合并处理时,存储一个和所在列索引值一致的数据即可,当需要作合并处理时,存储一个大于所在列索引值的数据,当需要被合并(即该单元格不会显示)时,存储0;

  • 举例:存储数据为[0,1,3,0,4],
    - 索引为0,1,4的单元格,存储值和索引一致,代表不需要合并
    - 索引为2的单元格,存储值为3,代表需要合并到索引为3的单元格
    - 索引为3的单元格,存储值为0,代表需要被合并,该单元格不需要显示

3)代码实现

/*** 生成各单元格的数据* @param {*} tableData 原数据* @param {*} rowSpanType 判断条件列的数组*/
handleTableData(tableData, rowSpanType) {const result = {}; // 存储数据// 由于是多个判断条件,所以需要遍历for (var i = 0; i < rowSpanType.length; i++) {const rowSpanArr = []; // 存储数据let position = 0; // 存储数据的索引值// 遍历原数据的每一项,与前一项对比tableData.forEach((item, index) => {// 第一项直接存储,不作处理if (index == 0) {rowSpanArr.push(0);position = 0;} else {// 判断与前一项的值是否相等(包括此列之前所有的列的值)function isEqual() {for (var j = i; j >= 0; j--) {if (item[rowSpanType[j]] == tableData[index - 1][rowSpanType[j]]) {continue;} else {return false;}}return true;}if (isEqual()) {rowSpanArr[position] += 1; // 前一项需要合并,存储值+1,代表需要合并到哪一行rowSpanArr.push(0); // 该项需要被合并,存储值为0,} else {// 与索引相等,代表不需要合并rowSpanArr.push(index);position = index;}}});result[rowSpanType[i]] = rowSpanArr;}return result;
},

3. 合并

思路分析

el-table提供了合并行或列的计算方法,即span-method,直接使用即可
参数: row当前行,column当前列的数据、rowIndex当前行索引、columnIndex当前列索引
返回值(可返回数组或对象)
- 没有处理,代表不需要合并,也不需要被合并
- 返回1,代表不作合并处理
- 返回0,代表被合并
- 返回值大于1,代表会合并到多少单元格
- 举例:当columnIndex=0, rowIndex=0时,return [2,1]或者{rowspan:2,colspan:1},代表第一行第一列合并了第二行第一列

难点

但是什么条件下返回,返回什么值是个问题,所以每个单元格都需要一个数据来控制自己是否需要合并,是否需要被合并,以及如果合并需要合并多少格,通过思路2我们已经实现。

代码实现

spanMethod({ row, column, rowIndex, columnIndex }) {// 由于是多个判断条件,多个列都需要合并,所以需要遍历for (var i = 0; i < this.rowSpanType.length; i++) {// 作某一列的合并处理if (column.property === this.rowSpanType[i]) {// 拿到当前单元格的存储数据const rowspan = this.rowSpanArr[this.rowSpanType[i]][rowIndex];// rowspan == rowIndex 代表不需要合并单元格,此处只需处理需要合并、需要被合并的单元格if (rowspan != rowIndex) {// rowspan===0代表被合并,!=0代表合并的目标行数if (rowspan === 0) {// 被合并的单元格,返回0即可return { rowspan: 0 };// return [0, 1]} else {// 合并数 = rowspan合并到的行数 - 当前所在的行数 + 1return { rowspan: rowspan - rowIndex + 1, colspan: 1 };// return [rowspan - rowIndex + 1, 1]}}}}
},

三、整体代码实现

使用的话,直接更改data中两个值即可,其他地方不需要动。

  • tableData 改成你的表格数据
  • rowSpanType 存放你需要合并的列(有顺序)
<template><div><el-table :data="tableData" :span-method="spanMethod" border style="width: 100%"><el-table-column prop="company" label="公司" /><el-table-column prop="division" label="部门" /><el-table-column prop="type" label="类别" /><el-table-column prop="name" label="项目" /><el-table-column align="center" sortable label="指标"><el-table-column prop="amount1" sortable label="指标1" /><el-table-column prop="amount2" sortable label="指标2" /></el-table-column><el-table-column align="center" sortable label="目标"><el-table-column prop="target1" sortable label="预设目标" /><el-table-column prop="target1" sortable label="目标值" /></el-table-column></el-table></div>
</template><script>
export default {data() {return {tableData: [],// 单元格的数据rowSpanArr: {},// 需要排序和合并的列。按顺序// 此处代表先按公司排序,公司相同的按部分排序,再相同的按类型排序,以此类推rowSpanType: ['company', 'division', 'type'],};},created() {this.tableData = this.initTableData(10);this.tableData = this.getSortArr(this.tableData, this.rowSpanType);this.rowSpanArr = this.handleTableData(this.tableData, this.rowSpanType);},methods: {/*** 生成模拟表格数据* @param {*} num 数据数量(行数)*/initTableData(num) {const result = []// 生成随机数据for (var i = 0; i <= num; i++) {const company = ['A', 'B', 'C'][this.getRandomInt(0, 2)];const division = ['x', 'y', 'z'][this.getRandomInt(0, 2)];const type = ['a', 'b', 'c', 'd', 'e', 'f'][this.getRandomInt(0, 5)];const name = this.getRandomInt(1, 4);result.push({id: i,company: `公司${company}`,division: `部门${division}`,type: `类别${type}`,name: `${company}、${division}、${type}`,amount1: `${this.getRandomInt(100, 1000)}`,amount2: `${this.getRandomInt(100, 1000)}`,target1: `${this.getRandomInt(100, 1000)}`,target2: `${this.getRandomInt(100, 1000)}`,});}return result;},/*** 按一系列参数排序* @param {*} table 原数组* @param {*} names 排序的列的数组*/getSortArr(table, names) {return table.sort((a, b) => {function sortFn(names, index) {const itemA = a[names[index]]const itemB = b[names[index]]if (itemA < itemB) {return -1;} else if (itemA > itemB) {return 1;} else {// 如果当前列的值相同// 如果最大列索引值还大于当前列索引,则递归, 判断下一列的值,否则返回0即可if (names.length - 1 > index) {return sortFn(names, index + 1);} else {return 0;}}}return sortFn(names, 0)});},/*** 生成随机数* @param {*} min  最小值* @param {*} max  最大值*/getRandomInt(min, max) {return min + parseInt(Math.random() * (max - min + 1));},spanMethod({ row, column, rowIndex, columnIndex }) {for (var i = 0; i < this.rowSpanType.length; i++) {if (column.property === this.rowSpanType[i]) {const rowspan = this.rowSpanArr[this.rowSpanType[i]][rowIndex];// rowspan == rowIndex 代表不需要合并单元格,不作处理,在此只处理需要合并的if (rowspan != rowIndex) {// rowspan===0代表被合并,!=0代表合并的目标行数if (rowspan === 0) {// 被合并的域return { rowspan: 0 };} else {// rowspan合并到的行数 - 当前所在的行数 + 1 = 合并数return { rowspan: rowspan - rowIndex + 1, colspan: 1 };}}}}},/*** 生成各行的合并数据* @param {*} tableData 原数据* @param {*} rowSpanType 需要合并的列的数组*/handleTableData(tableData, rowSpanType) {const result = {};for (var i = 0; i < rowSpanType.length; i++) {const rowSpanArr = [];let position = 0; // 当前索引tableData.forEach((item, index) => {if (index == 0) {rowSpanArr.push(0);position = 0;} else {// 判断与前一项的值是否相等(包括此列之前所有的列的值)function isEqual() {for (var j = i; j >= 0; j--) {if (item[rowSpanType[j]] == tableData[index - 1][rowSpanType[j]]) {continue;} else {return false;}}return true;}if (isEqual()) {// 代表需要合并到哪一行rowSpanArr[position] += 1;rowSpanArr.push(0);} else {// 与索引相等,代表不需要合并rowSpanArr.push(index);position = index;}}});result[rowSpanType[i]] = rowSpanArr;}return result;},},
};
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/292201.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4. 可用性

文章目录 4. 第4章 可用性4.1. 可用性一般场景4.2. 可用性策略故障检测&#xff08;Detect Faults&#xff09;故障恢复&#xff08;Recover from Faults &#xff09;故障预防&#xff08;Prevent Faults&#xff09; 4.3. 基于策略的可用性调查表4.4. 可用性模式4.5. 扩展阅读…

【pentaho】kettle读取Hive表不支持bigint和timstamp类型解决。

一、bigint类型 报错: Unable to get value BigNumber(16) from database resultset显示kettle认为此应该是decimal类型(kettle中是TYPE_BIGNUMBER或称BigNumber)&#xff0c;但实际hive数据库中是big类型。 修改kettle源码解决&#xff1a; kettle中java.sql.Types到kettle…

仓储1、10、11代电子标签接口文档

标签注册 仓储1代注册 侧面按钮连按三次&#xff0c; 注册成功&#xff1a;红灯变绿灯 仓储10代注册 右下角左下角组合按键触发注册 注册成功&#xff1a;右上角绿灯变红灯 仓储11代注册 磁体靠近条码附近&#xff0c;触发标签注册到系统 注册成功&#xff1a;闪红灯边绿…

Ubuntu20.04 及深度学习环境anaconda、cuda、cudnn、pytorch、paddle2.3安装记录

学习目标&#xff1a; Ubuntu20.04下装好torch、paddle深度学习环境。 选择的版本环境是 &#xff1a;最新的nvidia驱动、cuda 11.1 、cudnn v8.1.1&#xff0c;下面会说为啥这么选。 学习内容&#xff1a; 1. Ubuntu20.04仓库换源 本节参考Ubuntu 20.04 Linux更换源教程 2…

C/C++ 块作用域的静态变量static的应用

块作用域的静态变量 静态变量(static variable)听起来自相矛盾&#xff0c;像是一个不可变的变量。实际上&#xff0c;静态的意思是该变量在内存中原地不动&#xff0c;并不是说它的值不变。具有文件作用域的变量自动具有&#xff08;也必须是&#xff09;静态存储器。创建的具…

Go语言与HTTP/2协议的实践探索

随着互联网技术的发展&#xff0c;HTTP/2协议逐渐成为主流。Go语言作为一种高效、简洁的编程语言&#xff0c;与HTTP/2协议的结合具有很大的潜力。本文将探讨Go语言与HTTP/2协议的实践探索。 一、HTTP/2协议的优势 HTTP/2协议相比HTTP/1.1协议&#xff0c;具有以下优势&#…

PCB变压器相关记录

PCB平面变压器设计指南--转载自21世纪电源网_北京泰科斯德技术有限公司 PCB变压器电流密度

Patreon怎么订阅付款?Patreon会员订阅付款保姆级教程,用虚拟VISA卡订阅Patreon作者艺术家

Patreon 是目前世界上最受欢迎的会员平台之一。 内容创作者和艺术家通常很难让粉丝在经济上支持他们。 通过使用像 Patreon 这样的平台&#xff0c;创作者和艺术家可以很容易地从他们的作品中获得报酬。粉丝也能更方便的支持他们&#xff0c;今天就教大家如何订阅Patreon 首先我…

【让云服务器更灵活】iptables转发tcp/udp端口请求

iptables转发tcp/udp端口请求 文章目录 前言一、路由转发涉及点二、转发如何配置本机端口转发到本机其它端口本机端口转发到其它机器 三、固化iptables总结 前言 路由转发是计算机网络中的一种重要概念&#xff0c;特别是在网络设备和系统之间。它涉及到如何处理和传递数据包&…

结构型模式 | 适配器模式

一、适配器模式 1、原理 适配器模式&#xff08;Adapter&#xff09;&#xff0c;将一个类的接口转换成客户希望的另外一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器模式主要分为三类&#xff1a;类适配器模式、对象适配器模式、接口…

qt简单连接摄像头

要使用摄像头&#xff0c;就需要链接多媒体模块以及多媒体工具模块 需要在.pro文件中添加QT multimedia multimediawidgets 是用的库文件 QCamera 类用于打开系统的摄像头设备&#xff0c; QCameraViewfinder 用于显示捕获的视频&#xff0c; QCameraImageCapt…

【智慧校园】基于国标GB28181协议EasyCVR视频技术的高校宿舍智能监管方案

现如今&#xff0c;各大学校不乏众多住校生&#xff0c;但由于很多学生年龄较小 &#xff0c;又缺乏独自生活的经历&#xff0c;如何给在校住宿生做到安全与生活双重保障&#xff1f;旭帆科技校园智能视频监控通过人工智能技术对住宿区域进行智能监管&#xff0c;确保学生住宿安…