el-table 动态表格 + 动态合并多列单元格方法

动态合并单元格

之前有篇文章写了 el-table 通过 :span-method 方法实现合并单元格的方法, 但是当时只写了合并第一列的, 就有小伙伴询问, 如果多列合并怎么办, 刚好最近有个项目遇到了动态表格并且要合并多列单元格, 在详细的记录一下吧

合并的原理, 见之前的博客 el-table :span-method 方法动态合并单元格

背景

这次遇到的这个表格, 比之前的更加动态了, 他的表头除了前几个和后几个固定的之外, 中间的分类也是动态的, 内容和个数都不确定
要实现的就是将类别一二三和类别一合计相同的合并

在这里插入图片描述

具体操作

先看一下服务端下发的数据结构把 服务端为了方便我们前端知道什么时候该合并, 就下发了树形的结构
其中 appealTypes 就是对应的动态表头, item 对应的就是表格的数据

在这里插入图片描述
但是如果我们通过 :span-method 来合并, 我们需要就是平铺的数据

设计结构

因为这个项目是之前的项目, 新开的功能, 所以没有用之前封装的table组件, 而是直接用的 el-table

处理数据

  1. 先将树形图的所有path遍历出来
  2. 处理我们想要的平铺格式的数据
  3. 通过平铺的数据动态的计算需要合并列的 spanArr

也就是说我们最终想要的数据是这样的, 把树形的所有路径, 都处理成一个对象放到一个数组里, 平铺开, 我们就可以通过某个字段来计算出需要合并的个数了
在这里插入图片描述
在这里插入图片描述

具体代码

<el-table
  :header-cell-style="{ background: '#EFF1F5', color: '#76829A' }"
  :data="tableData"
  height="100%"
  :span-method="spanMethod"
  border
  style="width: 100%">
  <el-table-column
    align="center"
    prop="category1"
    label="类别一">
  </el-table-column>
  <el-table-column
    align="center"
    prop="category2"
    label="类别二">
  </el-table-column>
  <el-table-column
    align="center"
    prop="category3"
    label="类别三">
  </el-table-column>
  <el-table-column
    align="center"
    prop="category4"
    label="类别四">
  </el-table-column>
  <el-table-column
    align="center"
    v-for="item in propfigData"
    :prop="item.prop"
    :label="item.label">
  </el-table-column>
  <el-table-column
    align="center"
    prop="totalNumber"
    label="类别四合计">
  </el-table-column>
  <el-table-column
    align="center"
    prop="proportion"
    label="占比">
  </el-table-column>
  <el-table-column
    align="center"
    prop="category1TotalNumber"
    label="类别一合计">
  </el-table-column>
</el-table>
export default {
  data() {
    return {
      spanArrOne: [],
      spanArrTwo: [],
      spanArrThree: [],
      propfigData: [],
      tableData: [],
    }
  },
  mounted() {
    this.configData();
  },
  methods: {
    configData() {
      this.reqGetStatisticPageList();
    },
    reqGetStatisticPageList() {
      this.$client.getStatisticPageList()
      .then(res => {
        const data = res.data;
        // 拿到所有路径
        const allPath = this.getAllPath(data.item);
        // 平铺成想要的格式
        const allTileList = this.getAllTileList(allPath, data.appealTypes);
        // 处理动态表头
        this.propfigData = data.appealTypes.map((item, idx) => {
          return {
            prop: `appealTypes${idx}`, // 自定义的prop
            label: item,
          }
        })
        this.tableData = [ ...allTileList ];
		// 根据id值来计算合并的数量, 相邻的数据如果id一样(因为已经排好序了), 就认为合并
        this.spanArrOne = this.getSpanArr(this.tableData, 'category1Id');
        this.spanArrTwo = this.getSpanArr(this.tableData, 'category2Id');
        this.spanArrThree = this.getSpanArr(this.tableData, 'category3Id');
      })
      .catch(err => {
        this.$message({
          showClose: true,
          message: err.data.message,
          type: "error",
        });
      })
    },
    getAllPath(tree) {
      const paths = [];

      for (let i = 0; i < tree.length; i++) {
        if (tree[i].list && tree[i].list.length > 0) {
          const res = this.getAllPath(tree[i].list); //如果有子节点便继续深入,直到到达叶子节点
          for (let j = 0; j < res.length; j++) {
            paths.push([tree[i], ...res[j]]); //子节点返回后将其返回的路径与自身拼接
          }
        } else {
          paths.push([tree[i]]);
        }
      }

      return paths;
    },
    getAllTileList(list) {
      let result = [];

      list.forEach((item, index) => {
        let tempArr = {};
        item[3].dataList.forEach((itm, idx) => {
          tempArr[`appealTypes${idx}`] = itm;
        })

        result.push({
          category1: item[0].name || '',
          category1Id: item[0].id + '' || '',
          category1Level: item[0].level || '',
          category2: item[1].name || '',
          category2Id: item[1].id + '' || '',
          category2Level: item[1].level || '',
          category3: item[2].name || '',
          category3Id: item[2].id + '' || '',
          category3Level: item[2].level || '',
          category4: item[3].name || '',
          category4Id: item[3].id + '' || '',
          category4Level: item[3].level || '',
          totalNumber: item[3].totalNumber,
          proportion: this.formatNumber(item[3].proportion * 100),
          category1TotalNumber: item[0].totalNumber,
          ...tempArr,
        })
      })

      return result;
    },
    formatNumber(val) {
      let result = val.toFixed(2) - 0;

      if (parseInt(result % 0.1)) {
        return result + '%';
      } else {
        return parseInt(result / 1) + '%';
      }
    },
    // 计算 数据合并 索引
    getSpanArr(data, params) {
      let arr = []; // 接收重构数组
      let spanArr = []; // 控制合并的数组
      let pos = 0; // 设置索引

      // 排序
      this.groupBy(data, params).map(v => (arr = arr.concat(v)))
      
      arr.map(res => { // 双向绑定 把源数据改为arr
        data.shift();
        data.push(res);
      })
            
      const redata = arr.map(v => v[params]);
      
      redata.reduce((old, cur, i) => {
        if (cur === old) {
          spanArr[pos] += 1;
          spanArr.push(0);
        } else {
          spanArr.push(1);
          pos = i;
        }

        return cur;
      }, {});

      return spanArr;
    },
    // 根据某个字段进行排序 输出二维数组
    groupBy(data, params) {
      const groups = {};
      data.forEach(v => {
        const group = JSON.stringify(v[params]);
        groups[group] = groups[group] || [];
        groups[group].push(v);
      })

      return Object.values(groups);
    },
    spanMethod({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0 || column.property == 'category1TotalNumber') {
        const _row = this.spanArrOne[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        }
      }
      if(columnIndex === 1) {
        const _row = this.spanArrTwo[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        }
      }
      if(columnIndex === 2) {
        const _row = this.spanArrThree[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        }
      }
    }
  }
}

🎉🎉
不过还是建议, 尽量根据唯一的字段来合并, 比如id, 如果根据名称合并, 可能会出现不同父级下的子级名称相同, 导致出现问题, 但是用id合并一定不会出现问题的~
欢迎大家一起讨论学习😊~

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于el-table动态列的单元格合并,可以通过循环数据源数据获取该列需要合并的行数,然后在el-table的template中使用v-if指令来判断是否需要合并单元格,如果需要则使用rowspan属性来合并单元格。具体实现可以参考以下步骤: 1. 在el-table的columns中定义需要合并的列,并设置prop属性为该列的数据源字段名。 2. 在el-table的template中使用v-for指令循环数据源,并使用v-if指令判断是否需要合并单元格。 3. 如果需要合并单元格,则使用rowspan属性来合并单元格,同时将该单元格的内容设置为空。 4. 如果不需要合并单元格,则正常显示该单元格的内容。 例如,假设我们需要合并动态列中的第二列单元格,可以按照以下方式实现: ```html <el-table :data="tableData"> <el-table-column label="姓名" prop="name"></el-table-column> <el-table-column label="动态列" v-for="(column, index) in dynamicColumns" :key="index" :prop="column.prop"> <template slot-scope="scope"> <div v-if="index === 1 && scope.row.span > 1" :rowspan="scope.row.span" :style="{ 'line-height': (scope.row.span * 40) + 'px' }"></div> <div v-else>{{ scope.row[column.prop] }}</div> </template> </el-table-column> </el-table> ``` 其中,tableData为el-table的数据源,dynamicColumns为动态列的配置数组,每个元素包含prop属性表示该列的数据源字段名。在template中,我们使用v-for指令循环动态列,并使用v-if指令判断是否需要合并单元格。如果需要合并单元格,则使用rowspan属性来合并单元格,并设置该单元格的内容为空。如果不需要合并单元格,则正常显示该单元格的内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值