如何实现一个复杂的单元格合并?

如上图所示,想要实现上图这个表格,我们有两种思路,第一直接写死,使用table tr td 第二种动态生成,这里我选用的是第二种思路,借用el-table 组件合并单元格的思路进行实现。

简单介绍下 el-table 合并单元格的用法

在 Element UI 的 el-table 组件中,要合并单元格,可以使用 table 组件的 span-method 属性。这个属性接受一个方法,该方法的返回值决定单元格应该如何被合并。

span-method 方法会传入一个参数,该参数是一个对象,包含 rowcolumnrowIndexcolumnIndex 四个字段,表示当前单元格的行对象、列对象、行索引和列索引。

该方法应该返回一个数组,数组的两个元素分别指定需要合并的行数和列数。如果你不需要合并当前的单元格,则返回一个 [1, 1] 的数组。

这里是一个简单的例子,说明如何使用 span-method 来合并表格单元格:

html 复制代码
<template>
  <el-table :data="tableData" border style="width: 100%" :span-method="arraySpanMethod">
    <el-table-column prop="date" label="日期" width="150"></el-table-column>
    <el-table-column prop="name" label="姓名" width="150"></el-table-column>
    <el-table-column prop="address" label="地址"></el-table-column>
  </el-table>
</template>

<script>
export default {
  data() {
    return {
      tableData: [
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        // ...其他数据
      ]
    };
  },
  methods: {
    arraySpanMethod({ row, column, rowIndex, columnIndex }) {
      if (rowIndex % 2 === 0) {
        if (columnIndex === 0) {
          // 例如:合并行索引为偶数的第一列的单元格
          return [2, 1];
        }
      }
      return [1, 1];
    }
  }
};
</script>

在上述例子中,arraySpanMethod 方法会检查当前的行索引 rowIndex 是否是偶数,如果是并且当前是第一列(columnIndex === 0),它会合并当前行和下一行的这一列单元格。

记住,合并单元格时要确保其他被合并的单元格(即除了第一个单元格以外的单元格)返回 [0, 0]

请注意,合并单元格可能会复杂化表格的设计,尤其是当涉及动态行数据和多个合并条件时。务必仔细规划你的合并策略并测试确保没有遗漏任何情况。

我们通过分析设计原型图,发现有些表头下是一列,有些是两列,这里可以使用 el-table-conlumn 嵌套的写法实现。 粘上实现代码=》

js 复制代码
<template>
  <div class="table">
    <el-table
      :data="tableData"
      :span-method="objectSpanMethods"
      border
      class="custom-hide-second-row"
      style="width: 100%"
    >
      <!-- 需要合并的列 -->

      <el-table-column
        v-for="(el, i) in colConfigs"
        :key="i"
        align="center"
        :label="el.label"
      >
        <el-table-column :prop="el.prop[0]" align="center"> </el-table-column>
        <el-table-column v-if="el.prop[1]" align="center" :prop="el.prop[1]">
        </el-table-column>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
export default {
  name: "Table",
  props: {
    tableData: {
      type: Array,
      default: () => {
        return [];
      },
    },
    colConfigs: {
      type: Array,
      default: () => {
        return [];
      },
    },
    mergeCols: {
      type: Array,
      default: null,
    },
  },
  data() {
    return {
      tableMergeIndex: [],
    };
  },
 

  methods: {
    objectSpanMethods({ row, column, rowIndex, columnIndex }) {
      const key = columnIndex + "_" + rowIndex;
      // console.log(this.tableMergeIndex);
      if (this.tableMergeIndex[key]) {
        return this.tableMergeIndex[key];
      }
    },
    newTableMergeData() {
      for (let i = 0; i < this.tableData.length; i++) {
        for (let j = 0; j < this.mergeCols.length; j++) {
          // 初始化行、列坐标信息
          let rowIndex = 1;
          let columnIndex = 1;
          // 比较横坐标左方的第一个元素
          if (
            j > 0 &&
            this.tableData[i][this.mergeCols[j]["name"]] ===
              this.tableData[i][this.mergeCols[j - 1]["name"]]
          ) {
            columnIndex = 0;
          }
          // 比较纵坐标上方的第一个元素
          if (
            i > 0 &&
            this.tableData[i][this.mergeCols[j]["name"]] ===
              this.tableData[i - 1][this.mergeCols[j]["name"]]
          ) {
            rowIndex = 0;
          }
          // 比较横坐标右方元素
          if (columnIndex > 0) {
            columnIndex = this.onColIndex(
              this.tableData[i],
              j,
              j + 1,
              1,
              this.mergeCols.length,
            );
          }
          // 比较纵坐标下方元素
          if (rowIndex > 0) {
            rowIndex = this.onRowIndex(
              this.tableData,
              i,
              i + 1,
              1,
              this.mergeCols[j]["name"],
            );
          }
          const key = this.mergeCols[j]["index"] + "_" + i;
          this.tableMergeIndex[key] = [rowIndex, columnIndex];
        }
      }
    },
    /**
     * 计算列坐标信息
     * data 单元格所在行数据
     * index 当前下标
     * nextIndex 下一个元素坐标
     * count 相同内容的数量
     * maxLength 当前行的列总数
     */
    onColIndex(data, index, nextIndex, count, maxLength) {
      // 比较当前单元格中的数据与同一行之后的单元格是否相同
      if (
        nextIndex < maxLength &&
        data[this.mergeCols[index]["name"]] ===
          data[this.mergeCols[nextIndex]["name"]]
      ) {
        return this.onColIndex(data, index, ++nextIndex, ++count, maxLength);
      }
      return count;
    },
    /**
     * 计算行坐标信息
     * data 表格总数据
     * index 当前下标
     * nextIndex 下一个元素坐标
     * count 相同内容的数量
     * name 数据的key
     */
    onRowIndex(data, index, nextIndex, count, name) {
      // 比较当前单元格中的数据与同一列之后的单元格是否相同
      if (
        nextIndex < data.length &&
        data[index][name] === data[nextIndex][name]
      ) {
        return this.onRowIndex(data, index, ++nextIndex, ++count, name);
      }
      return count;
    },
  },
};
</script>

父组件内使用:

js 复制代码
<template>
  <div class="main">
  
    <Table
      ref="tableRef"
      :table-data="tableData"
      :col-configs="column"
      :merge-cols="mergeCols"
    ></Table>
  </div>
</template>

  data() {
    return {
   
      tableData: [
        {
          product: "农",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "种植业",
        },
        {
          product: "农",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "权重",
        },
        {
          product: "(权重)",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "0.97",
        },
        {
          product: "(权重)",
          name_1: "耕",
          count_1: "0.41",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "设施",
        },
        {
          product: "0.61",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "设施",
        },
        {
          product: "0.61",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "权重",
        },
        {
          product: "0.61",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "权重",
        },
        {
          product: "0.61",
          name_1: "耕",
          count_1: "0.4",
          name_2: "0.5",
          count_2: "0.6",
          name_3: "0.5",
          count_3: "0.6",
          name_4: "0.5",
          count_4: "0.5",
          index_1: "粮、油、棉、特色、设施",
          index_2: "0.03",
        },
      ],
      // 表格的信息 需要合并的需要放在 children 中
      column: [
            { prop: ["product"], label: "产业" },
            { prop: ["index_1", "index_2"], label: "指标简称" },
            { prop: ["name_1", "count_1"], label: "权重" },
            { prop: ["name_2", "count_2"], label: "2023年机械化水平" },
            { prop: ["name_3", "count_3"], label: "各产业综合机械化水平" },
            { prop: ["count_4"], label: "大农业综合机械化水平" },
      ],
      // 需要合并的行列信息 index必须是table表格对应的下标 不能随意修改
      mergeCols: [
        { index: 0, name: "product" },
        { index: 1, name: "index_1" },
        { index: 2, name: "index_2" },
        { index: 7, name: "name_3" },
        { index: 8, name: "count_3" },
        { index: 9, name: "count_4" },
        // { index: 5, name: 'age' }
      ],
    };
  },

最后我们发现多出一行空白表头

去掉方法 是在table 组件mounted内调用

js 复制代码
  mounted() {
    this.$nextTick(() => {
      const secondRow = this.$el.querySelector(
        ".el-table__header-wrapper tr:nth-child(2)",
      );
      if (secondRow) {
        secondRow.style.display = "none";
      }
    });
  },

最终效果

相关推荐
酒尘&15 分钟前
JS数组不止Array!索引集合类全面解析
开发语言·前端·javascript·学习·js
学历真的很重要39 分钟前
VsCode+Roo Code+Gemini 2.5 Pro+Gemini Balance AI辅助编程环境搭建(理论上通过多个Api Key负载均衡达到无限免费Gemini 2.5 Pro)
前端·人工智能·vscode·后端·语言模型·负载均衡·ai编程
用户47949283569152 小时前
"讲讲原型链" —— 面试官最爱问的 JavaScript 基础
前端·javascript·面试
用户47949283569152 小时前
2025 年 TC39 都在忙什么?Import Bytes、Iterator Chunking 来了
前端·javascript·面试
大怪v3 小时前
【Virtual World 04】我们的目标,无限宇宙!!
前端·javascript·代码规范
狂炫冰美式3 小时前
不谈技术,搞点文化 🧀 —— 从复活一句明代残诗破局产品迭代
前端·人工智能·后端
xw54 小时前
npm几个实用命令
前端·npm
!win !4 小时前
npm几个实用命令
前端·npm
代码狂想家4 小时前
使用openEuler从零构建用户管理系统Web应用平台
前端
dorisrv5 小时前
优雅的React表单状态管理
前端