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>
相关推荐
Misnice1 分钟前
如何在 React 中监听 div 的滚动事件
前端·react.js·前端框架
雨汨26 分钟前
el-input限制输入数字,输入中文后数字校验失效
前端·javascript·vue.js
保持学习ing32 分钟前
帝可得- 人员管理
前端·vue.js·elementui
一嘴一个橘子32 分钟前
el-table 树形数据,子行数据可以异步加载
javascript·elementui
難釋懷33 分钟前
Vue全局事件总线
前端·javascript·vue.js
生产队队长33 分钟前
项目练习:element ui 的icon放在button的右侧
开发语言·javascript·ui
独立开阀者_FwtCoder1 小时前
使用这个新的 ECMAScript 运算符告别 Try/Catch!
前端·javascript·github
云浪1 小时前
让元素舞动!深度解密 CSS 旋转函数
前端·css
cdcdhj1 小时前
vue中events选项与$on监听自定义事件他们的区别与不同,以及$emit与$on之间通信和mounted生命周期钩子函数有哪些作用和属性
前端·javascript·vue.js
Jinxiansen02111 小时前
Vue 3 弹出式计算器组件(源码 + 教程)
前端·javascript·vue.js