前言

最近有个需求,要对相同值的单元格进行合并,踩了一下坑,顺便记录一下,方便以后的使用。

欢迎留言,允许转载,转载麻烦说明来源,创作不易

Vue2 实现

官方api

:span-method="objectSpanMethod"
objectSpanMethod({ row, column, rowIndex, columnIndex })

row代表行属性,column代表列属性,rowIndex代表行下标,columnIndex 代表列下标

初始数据

<template>
    <div>
     <el-table :data="tableData"  border style="width: 60%">
  <el-table-column prop="name" label="姓名" />
      <el-table-column prop="amount1"  label="数值1" />
      <el-table-column prop="amount2"  label="数值2" />
     </el-table>
    </div>
</template>

<script>
export default {
    data() {
        return {
          tableData:[
            {name:'Tom',amount1:10,amount2:15},
            {name:'Tom',amount1:11,amount2:16},
            {name:'Jack',amount1:12,amount2:17},
            {name:'Andy',amount1:13,amount2:18},
            {name:'Andy',amount1:14,amount2:19}
          ]
        };
    },
    methods: {
    }
};
</script>

在这里插入图片描述

实例1:合并第一列的前两行

//合并方法
 objectSpanMethod({ row, column, rowIndex, columnIndex }){
     if(columnIndex==0){
         //如果是第一行,就合并两行
         if(rowIndex==0){
             return {
                 rowspan:2,
                 colspan:1
             } 
         }
     }
 }

在这里插入图片描述
可以看出是数据错列了。原因如下:

虽然第0行,让他合并了两行一列,但是第二行的那个tom是还存在的,所以导致错行。
正确的方式应该是第一行合并后,其余多出来的应该隐藏掉。隐藏单元格可以通过{rowspan:0,colspan:0}来实现,表示不渲染(两个都等于0,或者某一个等于0都行,后面会用到这里)

 //合并方法
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      let _row = 2;
      if (columnIndex == 0) {
        //如果是第一行,就合并两行
        if (rowIndex == 0) {
          return {
            rowspan: _row,
            colspan: 1,
          };
        } else {
          //其他行
          //因为合并了前两行,第0行已经占两行了,第一行,应该隐藏,其他行正常显示
          if (rowIndex < _row) {
            return {
              rowspan: 0,
              colspan: 1,
            };
          }
        }
      }
    },

在这里插入图片描述

实例2:第一列相同姓名的行合并

上面那个例子,实际用处不大,但是可以作为一个很好理解的例子。基于实例1,开始实现实例2 。

数据可以简化成 Tom Tom Jack Andy Andy,按照实例1,合并的行数应该是2,0,1,2,0(2表示合并两行,0表示要隐藏,依次类推),得到这几个数字就是我们需要处理的逻辑,具体逻辑如下:

//获取要合并的行数
getSpanNumber(data, prop) {
  //data要处理的数组,prop要合并的属性,比如name

  //数组的长度,有时候后台可能返回个null而不是[]
  let length = Array.isArray(data) ? data.length : 0;
  if (length > 0) {
    //用于标识位置
    let position = 0;
    //用于对比的数据
    let temp = data[0][prop];
    //要返回的结果
    let result = [1];
    //假设数据是AABCC,我们的目标就是返回20120
    for (let i = 1; i < length; i++) {
      if (data[i][prop] == temp) {
        //标识位置的数据加一
        result[position] += 1;
        //当前位置添0
        result[i] = 0;
      } else {
        //不相同时,修改标识位置,该位置设为1,修改对比值
        position = i;
        result[i] = 1;
        temp = data[i][prop];
      }
    }
    //返回结果
    return result;
  } else {
    return [0];
  }
},

测试一下数据:

 mounted() {
   console.log("结果:", this.getSpanNumber(this.tableData,'name'));
 },

在这里插入图片描述

实现上面的需求


<template>
  <div>
    <el-table :data="tableData" border style="width: 60%" :span-method="objectSpanMethod">
      <el-table-column prop="name" label="姓名" />
      <el-table-column prop="amount1" label="数值1" />
      <el-table-column prop="amount2" label="数值2" />
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [
        { name: "Tom", amount1: 10, amount2: 15 },
        { name: "Tom", amount1: 11, amount2: 16 },
        { name: "Jack", amount1: 12, amount2: 17 },
        { name: "Andy", amount1: 13, amount2: 18 },
        { name: "Andy", amount1: 14, amount2: 19 },
      ],
    };
  },
  mounted() {
    console.log("结果:", this.getSpanNumber(this.tableData, "name"));
  },
  methods: {
    //合并方法
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      if (columnIndex == 0) {
        //合并相同的名字
        let nameSpan = this.getSpanNumber(this.tableData, "name");
        return {
          rowspan: nameSpan[rowIndex],
          colspan: 1,
        };
      }
    },
    //获取要合并的行数
    getSpanNumber(data, prop) {
      //data要处理的数组,prop要合并的属性,比如name

      //数组的长度,有时候后台可能返回个null而不是[]
      let length = Array.isArray(data) ? data.length : 0;
      if (length > 0) {
        //用于标识位置
        let position = 0;
        //用于对比的数据
        let temp = data[0][prop];
        //要返回的结果
        let result = [1];
        //假设数据是AABCC,我们的目标就是返回20120
        for (let i = 1; i < length; i++) {
          if (data[i][prop] == temp) {
            //标识位置的数据加一
            result[position] += 1;
            //当前位置添0
            result[i] = 0;
          } else {
            //不相同时,修改标识位置,该位置设为1,修改对比值
            position = i;
            result[i] = 1;
            temp = data[i][prop];
          }
        }
        //返回结果
        return result;
      } else {
        return [0];
      }
    },
  },
};
</script>

结果:

在这里插入图片描述

vue3 实现

<template>
  <el-table
    :data="tableData"
    style="width: 100%"
    border
    :span-method="objectSpanMethod"
  >
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

<script lang="ts" setup>
import { reactive } from "vue";
const states = reactive({
  mergeArr: ["name", "address"],
  mergeObj: {},
});
const tableData = [
  {
    date: "2016-05-03",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    date: "2016-05-02",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    date: "2016-05-04",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
  {
    date: "2016-05-01",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
  },
];

// 默认接受四个值 { 当前行的值, 当前列的值, 行的下标, 列的下标 }
const objectSpanMethod = ({ column, rowIndex }) => {
  // 判断列的属性
  if (states.mergeArr.indexOf(column.property) !== -1) {
    // 判断其值是不是为0
    if (states.mergeObj[column.property][rowIndex]) {
      return [states.mergeObj[column.property][rowIndex], 1];
    } else {
      // 如果为0则为需要合并的行
      return [0, 0];
    }
  }
};
const getSpanArr = (data) => {
  states.mergeArr.forEach((key) => {
    let count = 0; // 用来记录需要合并行的起始位置
    states.mergeObj[key] = []; // 记录每一列的合并信息
    data.forEach((item, index) => {
      if (index === 0) {
        states.mergeObj[key].push(1);
      } else {
        // 判断当前行是否与上一行其值相等 如果相等 在 count 记录的位置其值 +1 表示当前行需要合并 并push 一个 0 作为占位
        if (item["id"] === data[index - 1]["id"]) {
          states.mergeObj[key][count] += 1;
          states.mergeObj[key].push(0);
        } else {
          // 如果当前行和上一行其值不相等
          count = index; // 记录当前位置
          states.mergeObj[key].push(1); // 重新push 一个 1
        }
      }
    });
  });
};
getSpanArr(tableData);
</script>

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐