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

如上图所示,想要实现上图这个表格,我们有两种思路,第一直接写死,使用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";
      }
    });
  },

最终效果

相关推荐
喵叔哟4 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django