表格实现合并单元格

实现的效果

在这里插入图片描述

一、列合并

此需求的列合并比较简单, 直接使用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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式-门面模式

设计模式专栏 模式介绍模式特点应用场景门面模式和代理模式的区别代码示例Java实现门面模式Python实现门面模式 门面模式在spring中的应用 模式介绍 门面模式是一种常用的软件设计模式&#xff0c;也称为外观模式。它提供了一个高层次的接口&#xff0c;将一个子系统的外部与内…

1. 行为模式 - 责任链模式

亦称&#xff1a; 职责链模式、命令链、CoR、Chain of Command、Chain of Responsibility 意图 责任链模式是一种行为设计模式&#xff0c; 允许你将请求沿着处理者链进行发送。 收到请求后&#xff0c; 每个处理者均可对请求进行处理&#xff0c; 或将其传递给链上的下个处理…

SaaS智慧校园云平台源码,智慧班牌系统,家校互联小程序源码

SaaS智慧校园云平台源码&#xff0c;智慧班牌系统&#xff0c;原生小程序 集智慧教学、智慧教务、智慧校务、智慧办公于一体的校园管理平台源码。集成智能硬件及第三方服务&#xff0c;面向学校、教师、家长、学生&#xff0c;将校内外管理、教学等信息资源进行整合&#xff0c…

vue微乾坤子应用开发及ele组件开发时问题记录

一. 微乾坤 1. 新增page页面路由,pmi权限中心配置正常&#xff0c;跳转链接正确&#xff0c;但路由未找到403. 解决&#xff1a; 新增的配置是page类型&#xff0c;transformQianKunRoute方法转换微前端路由数据 时&#xff0c;过滤未兼容page型的路由&#xff0c; 解决 [menu,…

Android开发——添加图片

1、首先选择一张需要的图片&#xff0c;通过左侧的Resource Manage选择“”并选择Import Drawables 选择一张图片 并调整以下两个内容 这两个内容的作用借用谷歌官方的Android开发教程的内容&#xff1a; *Android 设备具有不同的屏幕尺寸&#xff08;手机、平板电脑和电视等…

Histcite下载教程

这个安装就很简单了&#xff0c;可以通过百度网盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1WVkXnh3LiJJ08nKqmI5LQw?pwdug9c 提取码&#xff1a;ug9c 解压后&#xff0c;将savedress_for_test.txt放入TXT文件当中 双击main.exe文件&#xff0c;输入1或2…

Kubectl 部署简单应用

创建新服务 kubectl create deployment kubernetes-bootcamp --imagegcr.io/google-samples/kubernetes-bootcamp:v1查看 kubectl get deployments打开新的终端执行 kubectl proxy此时&#xff0c;切回上一个终端&#xff0c;通过 kubectl get pods可查看已部署好的pod。并通…

五种简单保护网站安全的方法看这里!

随着互联网的快速发展&#xff0c;随着品牌效应的加大&#xff0c;企业网站已经成为了企业对外展示的明信片&#xff0c;以及宣传获取私有流量的重要渠道。所以保护企业网站安全至关重要。这里我们就来一起了解一下五种简单保护网站安全的方法&#xff0c;仅供参考哦&#xff0…

【开源工程及源码】超级经典开源项目实景三维数字孪生智慧机场

智慧机场可视化平台通过可视化手段&#xff0c;将复杂的机场运营数据以图形、图表等形式展现&#xff0c;使管理者能够更直观、实时地了解机场的各个方面。飞渡科技通过整合物联网IOT、人工智能、大数据分析等技术&#xff0c;围绕机场管理、运控、安防、服务、监测等业务领域&…

Python模块导入的相关介绍

浅谈python模块的导入操作 1.什么是模块 在Python中有一个概念叫做模块(module)。所谓模块&#xff0c;就是将代码量较大的程序分割成多个有组织的&#xff0c;彼此独立但双能互相交互的代码片段&#xff0c;这些自我包含的有组织的代码段就是模块。 2.模块的特点 python中…

前端真的没有出路了嘛?不,当然不是!一定不是!绝对不是!

hello world&#xff01; 一、为什么会出现“前端已死”的言论 作为一个老前端的老公&#xff0c;我认为前端行业的就业前景非常广阔&#xff0c;而且未来也很有潜力。 我认为前端行业的就业前景非常广阔&#xff0c;而且未来也很有潜力。 有些人却唱衰前端这个行业。我觉得这…

两种方法解决win10开机慢,经验分享

方法一&#xff1a; 1、按快捷键“winR”打开 运行窗口。 2、这时候输入“msconfig”后 &#xff0c;点击“确定”或者按“ENTER”键。 3、这时候会打开一个名为“系统配置”的窗口&#xff0c; 在“常规”选项框下 勾选“有选择的启动”下的“加载系统服务”和“加载启动项”。…

【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 思路 1、继承关系 2、运行时类型检查 结语 我的其他博客 前言 在Java编程中&#xff0c;强制类型转换是一个常见的操作&#xf…

RateLimiter速率了解

RateLimiter <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>22.0</version> </dependency>这个同名的类在nacos的jar包中也有出现。速率限制于java.util.concurrent.Semaphore功能…

ACL实现固定时间访问资源——项目

文章目录 一、前言二、项目拓扑三、项目需求四、配置思路五、配置步骤1 IP地址2 端口类型3 静态路由4 流策略 六、结语 免责声明 本文旨在提供信息和解决问题的建议&#xff0c;观点和建议可能不适用于个人情况&#xff0c;仅供参考&#xff01;&#xff01;&#xff01; 文章中…

C# WPF上位机开发(软件的发布和部署)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 软件编译完成之后&#xff0c;本地测试完之后&#xff0c;一般都要发布和部署到客户的电脑上面。客户电脑的配置未必和开发的电脑是一样的&#xf…

02 特征点提取器 ORBextractor

文章目录 02 特征点提取器 ORBextractor2.0 基础知识2.0.1 图像金字塔2.0.2 ORB 特征点的关键点和描述子 2.1 构造函数&#xff1a;ORBextractor()2.2 构建图像金字塔 ComputePyramid()2.3 提取特征点并筛选 ComputeKeyPointsOctTree()2.4 筛选特征点 DistributeOctTree()2.5 计…

springboot+vue前后端分离的社区养老服务管理管理系统(有文档)

springbootvue前后端分离的社区养老服务管理管理系统。系统功能齐全&#xff0c;配置完成可运行&#xff0c;有文档&#xff0c;演示视频&#xff0c;配置说明&#xff0c;数据库文件&#xff0c;虚拟产品下单不退不换&#xff01; 技术&#xff1a;springbootmybatisplusmysql…

NPOI 导出Excel提示内容有问题的解决方法

NPOI导出Excel 使用Microsoft Excel 打开提示内容有问题&#xff0c;如下&#xff1a; 原因是&#xff1a;在使用NPOI导出excel时&#xff0c;获得 workbook.Write(ms)生成的 MemoryStream后&#xff0c;使用了 ms.GetBuffer()返回文件内容&#xff0c;导致生成的 Excel文件结尾…

【读论文】PSFusion

【读论文】Rethinking the necessity of image fusion in high-level vision tasks: A practical infrared and visible image fusion network based on progressive semantic injection and scene fidelity 介绍解决的问题网络架构整体架构稀疏语义感知分支&#xff08; spars…