表格实现合并单元格

news2024/9/20 8:38:22

实现的效果

在这里插入图片描述

一、列合并

此需求的列合并比较简单, 直接使用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合并到的行数 - 当前所在的行数 + 1
          return { 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.coloradmin.cn/o/1326025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

红队打靶练习:SOLIDSTATE: 1

信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:69:c7:bf, IPv4: 192.168.12.128 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.12.1 00:50:56:c0:00:08 …

pip的基本命令与使用

一、pip简介 pip是Python的包管理器&#xff0c;类似于其他编程语言中的包管理器&#xff0c;如Ruby的gem或Node.js的npm。它可以帮助你轻松地安装、更新和卸载Python包&#xff08;库或工具&#xff09;。你可以把pip想象成一个应用商店&#xff0c;你可以从中获取你需要的Py…

网络安全:专科及普通本科的温柔乡

当代普通大学生的现状是卷又卷不过、躺又躺不平&#xff0c;把大把的青春都荒废在了思考我应该做什么才能有前途的问题上面。当然&#xff0c;这里说的是那些普通学历且对自己的职业生涯甚至是人生没有规划的大学生&#xff0c;包括专科、普通一本二本&#xff0c;并非985、211…

如何在 JavaScript 中过滤嵌套对象?

概述 在 JavaScript 中&#xff0c;嵌套对象是一个简单的对象&#xff0c;它被括在大括号中&#xff0c;要使嵌套对象成为一个对象&#xff0c;就必须继承它自己的对象。因此&#xff0c;为了在 JavaScript 中过滤对象&#xff0c;JavaScript 提供了名为 "filter() "…

基于CTF探讨Web漏洞的利用与防范

写在前面 Copyright © [2023] [Myon⁶]. All rights reserved. 基于自己之前在CTF中Web方向的学习&#xff0c;总结出与Web相关的漏洞利用方法&#xff0c;主要包括&#xff1a;密码爆破、文件上传、SQL注入、PHP伪协议、反序列化漏洞、命令执行漏洞、文件包含漏洞、Vim…

Switch Transformers 的模型架构

Switch Transformers 的模型架构主要由以下几个部分组成&#xff1a; **专家&#xff1a;**Switch Transformers 由多个专家组成&#xff0c;每个专家都具有独立的参数。专家的数量可以根据需要进行调整。 **路由器&#xff1a;**路由器负责根据输入选择合适的专家。路由器可…

vue2 按钮限制 点击按钮一前 灰色不可以点击 点击按钮一后 可以点击

代码 <template> <div> <button click"enableButtons">按钮1</button> <button :disabled"!isButton2Enabled" click"ann">按钮2</button> <button :disabled"!isButton3Enabled" c…

hive企业级调优策略之数据倾斜

测试所用到的数据参考&#xff1a; 原文链接&#xff1a;https://blog.csdn.net/m0_52606060/article/details/135080511 本教程的计算环境为Hive on MR。计算资源的调整主要包括Yarn和MR。 数据倾斜概述 数据倾斜问题&#xff0c;通常是指参与计算的数据分布不均&#xff0…

MySQL-3

复习 DML操纵数据语句更新&#xff1a;insert/update/delete查询&#xff1a;select select 列1 as 别名,列2 as 别名 from 表名 as 对表取别名 where 对行的筛选 group by 分组的列名 having 配合统计函数进行对组的筛选 order by 排序的列 asc/desc limit 偏移量,获得条数 嵌…

route 路由使用记录

一、路由的基本介绍 路由是计算机网络中的一个重要概念&#xff0c;它用于确定数据包从源地址到目的地址的路径。在网络中&#xff0c;路由器是负责转发数据包的设备。 下面是关于路由的基本知识和使用方法的介绍&#xff1a; 路由表&#xff1a;路由器通过路由表来确定数据包…

配置自定义RedisTemplate 解决redis序列化java8 LocalDateTime

目录 配置自定义RedisTemplate 引入依赖 配置连接redis 编写测试类 出现问题 配置序列化 解决redis序列化java8 LocalDateTime 问题背景 问题描述 问题分析 解决方案一&#xff08;全局&#xff09; 解决方案二&#xff08;单个字段&#xff09; 配置自定义RedisTe…

某电子文档安全管理系统存在任意用户登录漏洞

漏洞简介 某电子文档安全管理系统存在任意用户登录漏洞&#xff0c;攻击者可以通过用户名获取对应的cookie&#xff0c;登录后台。 资产测绘 Hunter语法&#xff1a;web.icon“9fd216c3e694850445607451fe3b3568” 漏洞复现 获取Cookie POST /CDGServer3/LinkFilterServi…

MySQL 8.0 InnoDB Tablespaces之File-per-table tablespaces(单独表空间)

文章目录 MySQL 8.0 InnoDB Tablespaces之File-per-table tablespaces&#xff08;单独表空间&#xff09;File-per-table tablespaces&#xff08;单独表空间&#xff09;相关变量&#xff1a;innodb_file_per_table使用TABLESPACE子句指定表空间变量innodb_file_per_table设置…

ESD静电的危害与失效类型及模式?|深圳比创达电子

一、ESD的危害 1、失效的电子设备有60%~75%都是由ESD造成的&#xff1b; 2、对于新兴技术行业&#xff0c;尤其是高科技微电子&#xff0c;半导体&#xff0c;电磁敏感类及光器件的应用&#xff0c;比例将上升到90%。 因静电原因造成的电子行业的损失每年都多达几百亿美元&am…

JavaWeb笔记之前端开发HTML

一、引言 1.1HTML概念 网页&#xff0c;是网站中的一个页面&#xff0c;通常是网页是构成网站的基本元素&#xff0c;是承载各种网站应用的平台。通俗的说&#xff0c;网站就是由网页组成的。通常我们看到的网页都是以htm或html后缀结尾的文件&#xff0c;俗称 HTML文件。 …

【SpringCloud】设计原则之CAP与EDA事件驱动

一、设计原则之CAP CAP 原则又称 CAP 定理&#xff0c;指的是在一个分布式系统中&#xff0c;Consistency&#xff08;一致性&#xff09;、Availability&#xff08;可用性&#xff09;和 Partition tolerance&#xff08;分区容错性&#xff09;&#xff0c;三者不可兼得&…

美颜技术详解:深入了解视频美颜SDK的工作机制

本文将深入探讨视频美颜SDK的工作机制&#xff0c;揭示其背后的科技奥秘和算法原理。 1.引言 视频美颜SDK作为一种集成到应用程序中的技术工具&#xff0c;通过先进的算法和图像处理技术&#xff0c;为用户提供令人印象深刻的实时美颜效果。 2.视频美颜SDK的基本工作原理 首…

C#上位机与欧姆龙PLC的通信04---- 欧姆龙plc的存储区

1、存储区概念 欧姆龙PLC将整个数据存储器分为10个区&#xff1a;输入继电器区、输出继电器区、内部辅助继电器区、特殊继电器区、保持继电器区、暂存继电器区、定时/计数器区、数据存储区、辅助存储继电器区、链接继电器区。 输入输出继电器区 CP1E系列PLC输入继电器区有16…

Modbus-ASCII数据帧

Modbus-ASCIl传输模式中&#xff0c;每个字节均以ASCI编码&#xff0c;实际报文中1个字节会以两ASCIl字符发送&#xff0c;因此这种模式比Modbus-RTU模式效率要低。 例如报文数据 x5B "5""B" X35 X42 . 数据帧格式如下: 从ASCI报文帧可以看出&#xff0…