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>
相关推荐
用户69371750013845 小时前
Google 正在“收紧侧加载”:陌生 APK 安装或需等待 24 小时
android·前端
蓝帆傲亦5 小时前
Web 前端搜索文字高亮实现方法汇总
前端
用户69371750013845 小时前
Room 3.0:这次不是升级,是重来
android·前端·google
漫随流水6 小时前
旅游推荐系统(view.py)
前端·数据库·python·旅游
踩着两条虫7 小时前
VTJ.PRO 核心架构全公开!从设计稿到代码,揭秘AI智能体如何“听懂人话”
前端·vue.js·ai编程
jzlhll1238 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
用头发抵命9 小时前
Vue 3 中优雅地集成 Video.js 播放器:从组件封装到功能定制
开发语言·javascript·ecmascript
蓝冰凌9 小时前
Vue 3 中 defineExpose 的行为【defineExpose暴露ref变量】详解:自动解包、响应性与实际使用
前端·javascript·vue.js
奔跑的呱呱牛9 小时前
generate-route-vue基于文件系统的 Vue Router 动态路由生成工具
前端·javascript·vue.js