element plus 根据嵌套数据合并表格

介绍

因为最近项目中遇到合并表格行的需求,所以用这篇文章记录下实现思路。

看下实现效果:

实现逻辑

element plus 实现 table 的合并行或者列需要实现 span-method 方法。 官网地址element-plus-table

案例中的代码是实现行的合并,假设接口返回的数据是嵌套的模式,父级就是需要合并的列表,内层的数据长度就是需要合并的行数。当然如果嵌套的层次比较深,就需要遍历内层的数量统计最外层需要合并的数量。

案例代码是三层的结构,使用简单的嵌套循环,一般项目中也够用了,更多层级的数据结构也是一样的道理,多加n 次循环,此时建议使用递归来实现了。

代码实现

vue 复制代码
<script setup>
import { ref } from "vue";

// 模拟数据
const testData = [
  {
    fromMajorCity: 'BFN',
    data: [
      {
        level: 1,
        toMajorCity: 'BFN,CPT',
        data: [
          {
            weightGt: 0,
            weightLte: 15,
            basic_weight: 5,
            starting_price: 10,
            thereafter_price: 4,
            per_nkg: 1
          },
          {
            weightGt: 15,
            weightLte: null,
            basic_weight: 5,
            starting_price: 9.5,
            thereafter_price: 4,
            per_nkg: 1
          }
        ]
      },
      {
        level: 2,
        toMajorCity: 'DUR,ELS,KIM,WLK.Other',
        data: [
          {
            weightGt: 0,
            weightLte: null,
            basic_weight: 5,
            starting_price: 10,
            thereafter_price: 5,
            per_nkg: 1
          }
        ]
      }
    ]
  }
]

// 扁平化数据并且计算合并行的 rowspan
const flattenDataWithRowspan = (nestData) => {
  const flatData = []

  nestData.forEach((row) => {
    let firstRowSpan = 0 // 最外层合并行数
    row.data?.forEach((item) => {  
      const secondRowSpan = item.data.length // 第二层合并行数
      item.secondRowSpan = secondRowSpan
      firstRowSpan += secondRowSpan
    })
    row.firstRowSpan = firstRowSpan
  })
  
  nestData.forEach((row) => {
    row.data?.forEach((item, i) => {
      item.data?.forEach((val, index) => {
        flatData.push({
          fromMajorCity: row.fromMajorCity,
          level: item.level,
          toMajorCity: item.toMajorCity,
          ...val,
          firstRowSpan: (index === 0 && i == 0) ? row.firstRowSpan : 0,
          secondRowSpan: index === 0 ? item.secondRowSpan : 0,
        })
      })
    })
  })

  return flatData
}

// 合并行的方法
function spanMethod({ row, columnIndex, column }) {
  if (column.label === 'From Major City' || column.label === 'No' || column.label === 'Action') {
    // 如果有值需要合并
    if (row.firstRowSpan > 0) {
      return {
        rowspan: row.firstRowSpan,
        colspan: 1,
      }
    } else {
      return {
        rowspan: 0,
        colspan: 0,
      }
    }
  }
  if (column.label === 'Level' || column.label === 'To Major City') {
    // 如果有值需要合并
    if (row.secondRowSpan > 0) {
      return {
        rowspan: row.secondRowSpan,
        colspan: 1,
      }
    } else {
      return {
        rowspan: 0,
        colspan: 0,
      }
    }
  }
}

const tableData = ref(flattenDataWithRowspan(testData))
</script>

<template>
  <div>
    <el-table :data="tableData" :span-method="spanMethod" border>
      <el-table-column label="No" type="index" width="55" align="center"></el-table-column>
      <el-table-column label="From Major City" prop="fromMajorCity" width="200" align="center"></el-table-column>
      <el-table-column label="Level" prop="level" width="100" align="center"></el-table-column>
      <el-table-column label="To Major City" prop="toMajorCity" width="200" align="center"></el-table-column>
      <el-table-column label="Weight (>)" prop="weightGt" width="150" align="center"></el-table-column>
      <el-table-column label="Weight (<=)" prop="weightLte" width="150" align="center"></el-table-column>
      <el-table-column label="Basic Weight (kg)" prop="basic_weight" width="150" align="center"></el-table-column>
      <el-table-column label="Starting Price" prop="starting_price" width="150" align="center"></el-table-column>
      <el-table-column label="Thereafter Price" prop="thereafter_price" width="150" align="center"></el-table-column>
      <el-table-column label="per N Kg" prop="perNkg" width="150" align="center"></el-table-column>
      <el-table-column label="Action" width="200" align="center" fixed="right">
        <template #default>
          <el-button type="primary">Edit</el-button>
          <el-button type="primary">Copy</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<style scoped></style>
相关推荐
阿珊和她的猫1 小时前
v-scale-scree: 根据屏幕尺寸缩放内容
开发语言·前端·javascript
加班是不可能的,除非双倍日工资5 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi6 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip6 小时前
vite和webpack打包结构控制
前端·javascript
excel7 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国7 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼7 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy7 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT7 小时前
promise & async await总结
前端
Jerry说前后端7 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化