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>
赞(0)
未经允许不得转载:工具盒子 » Element Plus 表格行合并方法简单封装