51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Element Plus 表格行合并方法简单封装

Preface {#preface}

表格行合并是一个非常常用的功能,每次都手写十分的麻烦,故做一下简单的封装。

效果图 {#效果图}

image-20230731211904101

使用步骤 {#使用步骤}

  1. 在获取到表格数据之后,调用generateTableDataSpanMap()函数,生成关于表格行合并的Map
    • 第一个参数是表格的数组数据。
    • 第二个参数是要合并的对象属性,可以配置成包含子数组的形式,子数组内的属性必须要子数组之外的属性完全相同才会合并。
    • 返回值是Map对象,键的组成方式是行序号-列属性;如第 1 行,name属性列即为:1-name,对应的值就是要合并的行列配置数组,如:[1, 1]
// 生成行合并信息 map,合并 age、sex、score1、score2 列,其中 score1,score2 要在 age和sex 都相等的前提下才会计算是否要合并
const tableDataSpanMap = ref(null)
tableDataSpanMap.value = generateTableDataSpanMap(tableData.value, ['age', 'sex', ['score1', 'score2']])
  1. 在 Element Plus 表格合并方法:span-method="handleSpanMethod"中从生成的Map对象中取数据即可
/**
 * 表格行合并方法
 *
 * @param rowIndex 行号,从 0 开始
 * @param column 列信息,column.property 为列名
 * @returns 合并信息
 */
const handleSpanMethod = ({rowIndex, column}) => {
  return tableDataSpanMap.value.get(`${rowIndex}-${column.property}`)
}

封装方法 {#封装方法}

完整的封装方法如下:

/**
 * 生成表格行合并 map
 *
 * @param tableData 表格数据
 * @param spanColumnArr 要合并的列属性数组,数组中可以包含子数组,子数组中的属性需要父数组中的所有属性都相同才会合并
 *                      (也就是分组的意思,只会进行组内合并)
 * @return map 键:行号-列属性名(如:第一行 username 属性名为 1-username,第五行 password 属性名为 5-password)
 *             值:数组:[行合并个数, 列合并个数]
 */
export const generateTableDataSpanMap = (tableData, spanColumnArr) => {
    const spanColumnMap = new Map()
     /*
      转换存放要合并列名的数组为map,键为当前要合并的列名,值为上一层列名数组
      如: ['a', 'b', ['c', 'd', ['e']], 'f'] =>
        {
        'a': [],
        'b': [],
        'f': [],
        'c': ['a', 'b', 'f'],
        'd': ['a', 'b', 'f'],
        'e': ['a', 'b', 'f', 'c', 'd']
        }
      列 c 可以合并的前提是:列 a、b、f 值相同且列 c 本身的值也要相同
      列 e 可以合并的前提是:列 a、b、f、c、d 值相同且列 e 本身的值也要相同
     */
    const convertSpanColumnArr = (outSpanColumnArr, innerSpanColumnArr) => {
      const tempOutSpanColumnArr = []
      const tempInnerSpanColumnArr = []
      for (const item of innerSpanColumnArr) {
        if (typeof item === 'string') {
          // 外层列
          tempOutSpanColumnArr.push(item)
        } else if (item instanceof Array) {
          // 内层列
          tempInnerSpanColumnArr.push(...item)
       }
      }
  tempOutSpanColumnArr.forEach(item => spanColumnMap.set(item, outSpanColumnArr))

  if (tempInnerSpanColumnArr.length > 0) {
    convertSpanColumnArr(outSpanColumnArr.concat(tempOutSpanColumnArr), tempInnerSpanColumnArr)
  }
}

 // 转换存放要合并列名的数组为map
convertSpanColumnArr([], spanColumnArr)

// 存放表格合并信息 map
const tableSpanMap = new Map()
for (const [rowIndex, row] of tableData.entries()) {
  // 遍历每一行

  for (const columnName in row) {
    // 遍历每一行中对象的每一个属性

    if (!spanColumnMap.has(columnName)) {
      // 当前列属性不需要合并
      tableSpanMap.set(`${rowIndex}-${columnName}`, [1, 1])
      continue
    }

    if (rowIndex > 0 && tableData[rowIndex][columnName] === tableData[rowIndex - 1][columnName]) {
      // 当前单元格值与上一行对应的单元格值相同,则此单元格不显示
      let spanBool = true
      for (const item of spanColumnMap.get(columnName)) {
        if (tableData[rowIndex][item] !== tableData[rowIndex - 1][item]) {
          // 父合并属性的值不相同也不合并
          spanBool = false
        }
      }
      if (spanBool) {
        tableSpanMap.set(`${rowIndex}-${columnName}`, [0, 0])
        continue
      }
    }

    let rowSpan = 1
    outFor: for (let index = rowIndex; index < tableData.length - 1; index++) {
      if (tableData[rowIndex][columnName] !== tableData[index + 1][columnName]) {
        // 当前单元格值和下一行对应的单元格值不相同
        tableSpanMap.set(`${rowIndex}-${columnName}`, [rowSpan, 1])
        break
      }

      for (const item of spanColumnMap.get(columnName)) {
        if (tableData[rowIndex][item] !== tableData[index + 1][item]) {
          // 父合并属性的值不相同也不合并
          tableSpanMap.set(`${rowIndex}-${columnName}`, [rowSpan, 1])
          // 结束外层循环
          break outFor
        }
      }

      rowSpan++
    }

    if (!tableSpanMap.has(`${rowIndex}-${columnName}`)) {
      // 上面的 for 循环中没有设置值才在这里设置
      tableSpanMap.set(`${rowIndex}-${columnName}`, [rowSpan, 1])
    }
  }

}

return tableSpanMap

}

完整案例 {#完整案例}

实现效果图的完整案例如下:

<template>
  <el-table :data="tableData" :span-method="handleSpanMethod" border>
    <el-table-column label="姓名" prop="name"/>
    <el-table-column label="年龄" prop="age"/>
    <el-table-column label="性别" prop="sex"/>
    <el-table-column label="分数1" prop="score1"/>
    <el-table-column label="分数2" prop="score2"/>
  </el-table>
</template>

<script setup> import {ref} from 'vue' import {generateTableDataSpanMap} from '@/tool-box/element-plus-table'

// 表格数据 const tableData = ref([ { name: '张三', age: 18, sex: '男', score1: 99, score2: 88 }, { name: '李四', age: 18, sex: '男', score1: 100, score2: 88 }, { name: '王五', age: 20, sex: '女', score1: 104, score2: 62 }, { name: '赵六', age: 18, sex: '女', score1: 104, score2: 62 }, { name: '钱七', age: 18, sex: '男', score1: 99, score2: 88 }, ])

// 生成行合并信息 map,合并 age、sex、score1、score2 列,其中 score1,score2 要在 age和sex 都相等的前提下才会计算是否要合并 const tableDataSpanMap = ref(null) tableDataSpanMap.value = generateTableDataSpanMap(tableData.value, ['age', 'sex', ['score1', 'score2']])

/**

  • 表格行合并方法
  • @param rowIndex 行号,从 0 开始
  • @param column 列信息,column.property 为列名
  • @returns 合并信息 */ const handleSpanMethod = ({rowIndex, column}) => { return tableDataSpanMap.value.get(${rowIndex}-${column.property}) } </script>

<style scoped lang="scss"> .el-table--border, .el-table--group { box-sizing: border-box; border: 1px solid black !important; }

:deep(.el-table__cell) { border-color: black !important; } </style>

赞(3)
未经允许不得转载:工具盒子 » Element Plus 表格行合并方法简单封装