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

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

最终效果

相关推荐
y先森3 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy3 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189113 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿4 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡5 小时前
commitlint校验git提交信息
前端
虾球xz6 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇6 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒6 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员6 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐6 小时前
前端图像处理(一)
前端