【vue + element】el-table支持多层级合并列

可直接使用,按照配置,支持多层级合并行

文章目录

页面使用

在需要调用的页面使用:

html 复制代码
<el-table v-loading="loading" :data="tableList" height="500px" style="width: 100%" border
          :span-method="handleSpanMethod">
    ...
javascript 复制代码
import mergeCellsMixin from '@/mixins/mergeCellsMixin'

mixins: [mergeCellsMixin],
method:{
 getList() {
      console.log('getList', this.form)
      this.tableList = [{
        id: '12987122',
        demandNo: '12347214823',
        deliveryLocation: '送货地点1',
        applicant: '申请人1',
        costCenter: '王小虎',
        amount1: '234',
        amount2: '3.2',
        amount3: 10
      }, {
        id: '12987123',
        demandNo: '12347214823',
        deliveryLocation: '送货地点1',
        applicant: '申请人1',
        costCenter: '王小虎',
        amount1: '165',
        amount2: '4.43',
        amount3: 12
      }, {
        id: '12987124',
        demandNo: '12347214823',
        deliveryLocation: '送货地点2',
        applicant: '申请人1',
        costCenter: '王小虎',
        amount1: '324',
        amount2: '1.9',
        amount3: 9
      }, {
        id: '12987125',
        demandNo: '12347214823',
        deliveryLocation: '送货地点2',
        applicant: '申请人1',
        costCenter: '王小虎',
        amount1: '621',
        amount2: '2.2',
        amount3: 17
      }, {
        id: '12987126',
        demandNo: '12347214823',
        deliveryLocation: '送货地点2',
        applicant: '申请人2',
        costCenter: '王小虎',
        amount1: '539',
        amount2: '4.1',
        amount3: 15
      }, {
        id: '12987126',
        demandNo: '123472148000',
        deliveryLocation: '送货地点2',
        applicant: '申请人2',
        costCenter: '王小虎',
        amount1: '539',
        amount2: '4.1',
        amount3: 15
      }]
      this.getSpanArr()
    },
    // 获取合并数组
    getSpanArr() {
      // key: 当前字段名 dependsOn: 当前字段依赖哪些前面字段
      this.mergeRules = [
        { key: 'demandNo', dependsOn: [] },
        { key: 'deliveryLocation', dependsOn: ['demandNo'] },
        { key: 'applicant', dependsOn: ['demandNo', 'deliveryLocation'] },
        { key: 'costCenter', dependsOn: ['demandNo', 'deliveryLocation', 'applicant'] }
      ]
      this.spanArr = this.createSpanArr(this.tableList, this.mergeRules)
    },
    // 合并单元格
    handleSpanMethod({ row, column, rowIndex, columnIndex }) {
      // 获取对应的合并数组
      const spanMap = {
        0: this.spanArr.demandNo, // 需求单合并
        1: this.spanArr.deliveryLocation, // 送货地点合并
        2: this.spanArr.applicant, // 申请人合并
        3: this.spanArr.costCenter // 成本中心合并
      };
      return this.mergeCells(spanMap, { row, column, rowIndex, columnIndex })
    },
}

mergeCellsMixin.js

javascript 复制代码
export default {
    data() {
        return {

        }
    },
    methods: {
        /**
         * @description 创建合并数组
         * @param {Array} tableList 表格数据
         * @param {Array} mergeRules 合并规则 ({key: 当前字段名 dependsOn: 当前字段依赖哪些前面字段})
         * @example  tableList = [
         {demandNo: '', deliveryLocation: '',applicant: '',costCenter: '',materialName:''}]
         * @example mergeRules = [
         { key: 'demandNo', dependsOn: [] },
         { key: 'deliveryLocation', dependsOn: ['demandNo'] },
         { key: 'applicant', dependsOn: ['demandNo', 'deliveryLocation'] },
         { key: 'costCenter', dependsOn: ['demandNo', 'deliveryLocation', 'applicant'] }]
         * */
        createSpanArr(tableList, mergeRules) {
            // 存放每个字段对应的合并数组
            const spanArr = {}
            // 记录每个字段当前合并块的起始索引位置
            const posMap = {}
            // 最终返回值
            const result = {}
            // 初始化每个字段的数组
            mergeRules.forEach(rule => {
                spanArr[rule.key] = [];
                result[rule.key] = spanArr[rule.key];
                posMap[rule.key] = 0;
            });

            for (let i = 0; i < tableList.length; i++) {
                if (i === 0) {
                    mergeRules.forEach(rule => {
                        // 所有字段在第一行都开启新块,设为 1
                        spanArr[rule.key].push(1)
                        // 当前字段从第 0 行开始合并
                        posMap[rule.key] = 0
                    });
                } else {
                    // 当前行
                    const curr = tableList[i]
                    // 上一行
                    const prev = tableList[i - 1]
                    for (const rule of mergeRules) {
                        // key: 当前字段名 dependsOn: 当前字段依赖哪些前面字段
                        const { key, dependsOn } = rule
                        // 检查依赖字段是否一致
                        const depsMatch = dependsOn.every(dep => curr[dep] === prev[dep])
                        if (!depsMatch) {
                            // 如果依赖不一致,则当前字段开启新块,并重置后续字段
                            spanArr[key].push(1);
                            posMap[key] = i;
                            // 后续字段都设为新块
                            const index = mergeRules.indexOf(rule);
                            for (let j = index + 1; j < mergeRules.length; j++) {
                                const nextKey = mergeRules[j].key;
                                spanArr[nextKey].push(1);
                                posMap[nextKey] = i;
                            }
                            break;
                        } else {
                            // 当前字段值一致时继续合并
                            if (curr[key] === prev[key]) {
                                // 当前字段值一致时,行数加 1
                                spanArr[key][posMap[key]] += 1;
                                // 当前行标记为被合并
                                spanArr[key].push(0);
                            } else {
                                // 否则开启新块
                                spanArr[key].push(1);
                                // 更新当前字段的起始行号
                                posMap[key] = i;
                            }
                        }
                    }
                }
            }
            return result;
        },
        /**
         * @description 合并单元格
         * @param {Object} spanMap 创建的合并数组
         * @param {} row 当前行
         * @param {} column 当前列
         * @param {} rowIndex 当前行索引
         * @param {} columnIndex 当前列索引
         * @example spanMap = {
            0: this.spanArr.demandNo,
            1: this.spanArr.deliveryLocation,
            2: this.spanArr.applicant,
            3: this.spanArr.costCenter }
         * */
        mergeCells(spanMap, { row, column, rowIndex, columnIndex }) {
            // 获取对应的 spanArr
            const spanArr = spanMap[columnIndex];
            if (spanArr) {
                const r = spanArr[rowIndex];
                const c = spanArr[rowIndex] > 0 ? 1 : 0;
                return [r, c];
            }
            return [1, 1]; // 默认不合并
        }
    }
}
相关推荐
念何架构之路几秒前
zap扩展生态与总结
java·前端·数据库
独孤九剑打醒他15 分钟前
【原创开源】【概念设计】源 - 栅 - 漏 - 栅 - 源 横向双栅 MOS,低压交流多值逻辑芯片探索
前端·其他·架构·开源·硬件工程
mantou1321 小时前
我给 AI Agent 做了个「油猴」:让 Claude Code / Codex 直接用你已登录的浏览器
前端·javascript·后端
默_笙1 小时前
🍕 一个主编、三个工种、两本手册:搭一支 AI 调研队
前端·javascript
纸片人1 小时前
Blender 建模 + Three.js 展示:和 AI 一起做一个光储充超充站数字孪生大屏
前端
智能直播1 小时前
网络RTMP拉流不卡顿、声音不同步?一文讲透缓冲区、时间戳与MEDAI V2实战调优
前端
第七页独白1 小时前
汽车零件厂如何通过 QMS 真正落地 IATF 16949——QMS软件系统:品质检验-内审稽核-8d客诉管理:全星质量管理软件系统
java·前端·数据库
纸片人1 小时前
只用 three.js + OpenStreetMap,手搓一个「成都城市 3D」数据大屏
前端
星若生辉1 小时前
Hugo 静态站点搭建|从本地调试到上线部署实操指南
前端
沐浴露z1 小时前
Agent 响应延迟过高?从工具治理角度提供两条思路
前端·网络