动态合并el-table的单元格:根据行或列的值是否相同

222 阅读2分钟

动机

做此需求前,搜了一些方案,但要么是根据数据去定制,要么晦涩难懂且只能合并列,遂写了个demo,自动按行或列去合并所有值相同的单元格

示例数据以及合并效果

image.png

image.png

如何使用

const props = ["prop1", "prop2", "prop3"]
改为实际所使用的列字段

HTML

<template>
  <div>
    <el-table
      :data="tableData"
      border
      :span-method="objectSpanMethod"
      :show-header="false"
    >
      <el-table-column prop="prop1" />
      <el-table-column prop="prop2" />
      <el-table-column prop="prop3" />
    </el-table>
  </div>
</template>

JS

import { reactive } from "vue";
const tableData = [
  { prop1: "竖三", prop2: "竖二", prop3: "单元格1" },
  { prop1: "竖三", prop2: "竖二", prop3: "单元格2" },
  { prop1: "竖三", prop2: "横二", prop3: "横二" },
];
const spanMap = reactive({});
const props = ["prop1", "prop2", "prop3"]; /// 列字段
function objectSpanMethod({ rowIndex, columnIndex }) {
  return spanMap[rowIndex * props.length + columnIndex];
}
/**
 * @description 合并单元格
 * @description 思想是:先按行遍历,收集横向合并的单元格,再按列遍历,收集纵向合并的单元格
 * @description 每个单元在spanMap中的索引:rowIndex * 列数 + columnIndex
 * @description 每一次循环,将相邻相同值的单元格在spanMap中的索引放进一个数组
 * @description 若第一行的内容为[1,2,2]-->[[0],[1,2]]
 * @description 若第二行的内容为['k','k','x']-->[[3,4],[5]]
 * @description 最终第二行的spanMap: 第一个k, rowspan=2, colspan=1;
 * 第二个k被合并, rowspan=0, colspan=0;
 * x, rowspan=1, colspan=1
 */
function setSpanMap() {
  const rowLength = tableData.length; /// 行数
  const colLength = props.length; /// 列数
  let groupedIndex = [];
  //. 先按行遍历,收集横向合并的单元格
  for (let row = 0; row < rowLength; row++) {
    const rowData = tableData[row];
    const rowIndexAndValue = []; /// [{index,value}]
    for (let col = 0; col < colLength; col++) {
      let prop = props[col];
      let index = row * colLength + col;
      rowIndexAndValue.push({ index, value: rowData[prop] });
    }
    groupedIndex = groupedIndex.concat(groupAdjacent(rowIndexAndValue));
  }
  for (let arr of groupedIndex) {
    if (arr.length == 1) {
      spanMap[arr[0]] = { rowspan: 1, colspan: 1 };
    } else {
      spanMap[arr[0]] = { rowspan: 1, colspan: arr.length };
      for (let i = 1; i < arr.length; i++) {
        spanMap[arr[i]] = { rowspan: 0, colspan: 0 };
      }
    }
  }
  //. 再按列遍历,收集纵向合并的单元格
  for (let col = 0; col < colLength; col++) {
    const colData = tableData.map((row) => row[props[col]]);
    const colIndexAndValue = colData.map((value, index) => ({
      index: index * colLength + col,
      value,
    }));
    groupedIndex = groupAdjacent(colIndexAndValue);
    for (let arr of groupedIndex) {
      if (arr.length == 1) {
        spanMap[arr[0]].rowspan !== 0 && (spanMap[arr[0]].rowspan = 1);
      } else {
        spanMap[arr[0]].rowspan = arr.length;
        for (let i = 1; i < arr.length; i++) {
          spanMap[arr[i]] = { rowspan: 0, colspan: 0 };
        }
      }
    }
  }
}
/**
 * @description 输入:[{index:3,value:'k'},{index:4,value:'k'},{index:5,value:'x'}]
 * @description 输出:[[3,4],[5]]
 */
function groupAdjacent(arr) {
  const result = [];
  let temp = [arr[0].index];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i].value === arr[i - 1].value) {
      temp.push(arr[i].index);
    } else {
      result.push(temp);
      temp = [arr[i].index];
    }
  }
  result.push(temp);
  return result;
}
setSpanMap();