el-table 动态合并列单元格

遍历表格数据,先横向合并相邻相同的单元格,再纵向合并。允许指定要合并的列,并根据自定义条件进行合并。若未指定合并列,可通过参数控制是否默认合并所有列;若未提供合并条件,则默认相等时合并。

:span-method="toMergeColumn"

/* 合并行或列的计算方法 */
toMergeColumn({ rowIndex, columnIndex }) {
  const propArr = this.fieldList.map(item => item.field)
  const mergeColumns = propArr.filter(item => item !== 'aaaa7') // 需要合并的列,排除备注列
  if (columnIndex === this.fieldList.length) return [1, 1] // 操作列不合并
  const spanMap = this.setSpanMap(this.formList, propArr, mergeColumns)
  return spanMap[rowIndex * this.fieldList.length + columnIndex]
},
/** 合并列单元格
 * @description 遍历表格数据,先横向合并相邻相同的单元格,再纵向合并。允许指定要合并的列,并根据自定义条件进行合并。若未指定合并列,可通过参数控制是否默认合并所有列;若未提供合并条件,则默认相等时合并
 * @description 每个单元在 spanMap 中的索引:rowIndex * 列数 + columnIndex。每一次循环,将相邻相同值的单元格在 spanMap 中的索引放进一个数组
 * @description 若第一行的内容为 [1, 2, 2] --> [[0], [1, 2]]。若第二行的内容为 ['k', 'k', 'x'] --> [[3, 4], [5]]
 * @description 最终第二行的 spanMap: 第一个 k, rowspan = 2, colspan = 1; 第二个 k 被合并, rowspan = 0, colspan = 0; x, rowspan = 1, colspan = 1
 * @param {Array} tableData - 表格数据,每行为一个对象,键为列名
 * @param {Array} props - 表头字段数组,对应 tableData 的 key 值(即prop字段)
 * @param {Array|null} mergeColumns - 需要进行合并的列(列名数组),若为 null 则根据 mergeAllColumns 参数决定是否合并所有列
 * @param {boolean} mergeAllColumns - 是否默认合并所有列,仅在 mergeColumns 为 null 时生效,默认为 false
 * @param {Function} [mergeCondition = (a, b) => a && b && a === b] - 自定义合并条件,默认只有非空且相等的单元格才会被合并
 * @returns {Object} spanMap - 记录每个单元格的 rowspan 和 colspan
 */
setSpanMap(tableData, props, mergeColumns = null, mergeAllColumns = false, mergeCondition = (a, b) => a && b && a === b) {
  const rowLength = tableData.length // 表格的行数
  const colLength = props.length // 表格的列数
  const spanMap = {} // 记录单元格合并信息
  // 如果 mergeColumns 为空,且 mergeAllColumns 为 true,则合并所有列
  const columnsToMerge = mergeColumns === null ? (mergeAllColumns ? props : []) : mergeColumns
  /** 处理相邻符合合并条件的单元格,并返回合并后的索引数组
   * @description 输入:[{ index: 3, value: 'k'}, { index: 4, value: 'k' }, { index: 5, value: 'x' }]
   * @description 输出:[[3, 4], [5]]
   * @param {Array} arr - 包含 { index, value } 的对象数组
   * @returns {Array} 合并后的索引数组,例如 [[0], [1, 2]]
   */
  function groupAdjacent(arr) {
    if (arr.length === 0) return []
    const result = []
    let temp = [arr[0].index]
    for (let i = 1; i < arr.length; i++) {
      if (mergeCondition(arr[i - 1].value, arr[i].value)) {
        temp.push(arr[i].index)
      } else {
        result.push(temp)
        temp = [arr[i].index]
      }
    }
    result.push(temp)
    return result
  }
  // 横向合并:先按行遍历,收集横向合并的单元格
  for (let row = 0; row < rowLength; row++) {
    const rowData = tableData[row]
    const rowIndexAndValue = props.map((prop, col) => ({
      index: row * colLength + col,
      value: rowData[prop]
    })).filter(({ index }) => columnsToMerge.includes(props[index % colLength]))
    if (rowIndexAndValue.length > 0) {
      groupAdjacent(rowIndexAndValue).forEach((group) => {
        if (group.length === 1) {
          spanMap[group[0]] = { rowspan: 1, colspan: 1 }
        } else {
          spanMap[group[0]] = { rowspan: 1, colspan: group.length }
          group.slice(1).forEach((index) => {
            spanMap[index] = { rowspan: 0, colspan: 0 }
          })
        }
      })
    }
  }
  // 纵向合并:再按列遍历,收集纵向合并的单元格
  columnsToMerge.forEach((col) => {
    const colIndex = props.indexOf(col)
    if (colIndex === -1) return
    const colIndexAndValue = tableData.map((row, rowIndex) => ({
      index: rowIndex * colLength + colIndex,
      value: row[col]
    }))
    if (colIndexAndValue.length > 0) {
      groupAdjacent(colIndexAndValue).forEach((group) => {
        if (group.length > 1) {
          spanMap[group[0]].rowspan = group.length
          group.slice(1).forEach((index) => {
            spanMap[index] = { rowspan: 0, colspan: 0 }
          })
        }
      })
    }
  })
  // 返回合并后的索引数组
  return spanMap
},

参考文章:

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

el-table表格动态合并相同数据单元格(可指定列+自定义合并)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值