VUE3+element plus 实现表格行合并

最近写项目时遇到了将表格前两列相同数据进行单元格合并,故以此分享一下表格合并的具体实现逻辑,如有更简单的方法可互相交流!!!

一.表格前两列进行合并

前提条件:第二列合并需在第一列合并的前提下才能进行合并,构建一个element plus表格,通过span-method方法进行表格列合并,先获取第一列合并后的span再进行第二列合并数据,只有相邻的单元格可合并。

ini 复制代码
const tableData = ref([
{ category: '水果', subCategory: '热带', name: '芒果', type: '111' },
{ category: '水果', subCategory: '热带', name: '香蕉', type: '111' },
{ category: '水果', subCategory: '热带', name: '香蕉', type: '111' },
{ category: '水果', subCategory: '11', name: '苹果', type: '4' },
{ category: '水果', subCategory: '热带', name: '苹果', type: '5' },
{ category: '水果', subCategory: '热带', name: '胡萝卜', type: '6' },
{ category: '蔬菜', subCategory: '热带', name: '胡萝卜', type: '7' },
{ category: '蔬菜', subCategory: '11', name: '菠菜', type: '8' },
{ category: '蔬菜', subCategory: '白菜', name: '菠菜', type: '8' },
{ category: '蔬菜', subCategory: '白菜', name: '菠菜', type: '8' },
{ category: '蔬菜', subCategory: '热带', name: '菠菜', type: '8' },
]);

const mergeMap = computed(() => {
const map = [];
const data = tableData.value;

// 1. 第一列:合并相同的category
const col1 = Array(data.length).fill(1);
let i = 0;
while (i < data.length) {
 const currentCategory = data[i].category;
 let span = 1;
 
 for (let j = i + 1; j < data.length; j++) {
   if (data[j].category === currentCategory) {
     span++;
   } else {
     break;
   }
 }
 
 if (span > 1) {
   col1[i] = span;
   for (let k = i + 1; k < i + span; k++) {
     col1[k] = 0;
   }
 }
 
 i += span;
}
map.push(col1);

// 2. 第二列:在相同category下合并相同的subCategory
const col2 = Array(data.length).fill(1);
let row = 0;
while (row < data.length) {
 // 找到当前category组
 let categoryEnd = row + (col1[row] || 1);
 
 // 在当前category组内处理subCategory
 let j = row;
 while (j < categoryEnd) {
   const currentSubCategory = data[j].subCategory;
   let span = 1;
   
   for (let k = j + 1; k < categoryEnd; k++) {
     if (data[k].subCategory === currentSubCategory) {
       span++;
     } else {
       break;
     }
   }
   
   if (span > 1) {
     col2[j] = span;
     for (let x = j + 1; x < j + span; x++) {
       col2[x] = 0;
     }
   }
   
   j += span;
 }
 
 row = categoryEnd;
}
map.push(col2);

// 3. 第三列:永远不合并,每行都显示
const col3 = Array(data.length).fill(1);
map.push(col3);

return map;
});

// 合并方法
const handleSpan = ({ rowIndex, columnIndex }) => {
if (columnIndex >= mergeMap.value.length) return { rowspan: 1, colspan: 1 };
const span = mergeMap.value[columnIndex][rowIndex];
return span === 0 ? { rowspan: 0, colspan: 0 } : { rowspan: span, colspan: 1 };
};

实现效果如下:

二.表格中所有列均可以合并,可以让最后一列不合并,以防两行数据合并成一行,效果不明显,具体代码实现如下:

ini 复制代码
const tableData = ref([
  { category: '水果', subCategory: '热带', name: '芒果', type: '111' },
  { category: '水果', subCategory: '热带', name: '香蕉', type: '111' },
  { category: '水果', subCategory: '热带', name: '香蕉', type: '111' },
  { category: '水果', subCategory: '11', name: '苹果', type: '4' },
  { category: '水果', subCategory: '热带', name: '苹果', type: '5' },
  { category: '水果', subCategory: '热带', name: '胡萝卜', type: '6' },
  { category: '蔬菜', subCategory: '热带', name: '胡萝卜', type: '7' },
  { category: '蔬菜', subCategory: '热带', name: '菠菜', type: '8' },
]);
 
![f094ea88-1cc2-4c17-abda-0310ca556ce1.png](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6f53d3042ad748fb80e19d1c69d2a894~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgY2FpY2FpX2xmX25pdW5pdQ==:q75.awebp?rk3s=f64ab15b&x-expires=1759655616&x-signature=X9335hq4YG9%2B2mjpYFIG4w3avM8%3D)
// 预处理合并信息
const mergeMap = computed(() => {
  const map = [];
  const keys = ['category', 'subCategory', 'name', 'type'];
  
  // 为每一列计算合并信息
  for (let col = 0; col < keys.length; col++) {
    const currentCol = new Array(tableData.value.length).fill(1);
    
    if (col === 0) {
      // 第一列:直接合并相同值
      let i = 0;
      while (i < tableData.value.length) {
        const currentValue = tableData.value[i][keys[col]];
        let span = 1;
        
        for (let j = i + 1; j < tableData.value.length; j++) {
          if (tableData.value[j][keys[col]] === currentValue) {
            span++;
          } else {
            break;
          }
        }
        
        if (span > 1) {
          currentCol[i] = span;
          for (let k = i + 1; k < i + span; k++) {
            currentCol[k] = 0;
          }
        }
        
        i += span;
      }
    } else {
      // 后续列:在前一列合并的基础上合并相同值
      let i = 0;
      while (i < tableData.value.length) {
        // 找到前一列的合并组
        let groupEnd = i;
        if (map[col-1][i] > 1) {
          // 前一列有合并
          groupEnd = i + map[col-1][i];
        } else {
          // 前一列没有合并
          groupEnd = i + 1;
        }
        
        // 在当前组内处理当前列的合并
        let j = i;
        while (j < groupEnd) {
          const currentValue = tableData.value[j][keys[col]];
          let span = 1;
          
          for (let k = j + 1; k < groupEnd; k++) {
            if (tableData.value[k][keys[col]] === currentValue) {
              span++;
            } else {
              break;
            }
          }
          
          if (span > 1) {
            currentCol[j] = span;
            for (let x = j + 1; x < j + span; x++) {
              currentCol[x] = 0;
            }
          }
          
          j += span;
        }
        
        i = groupEnd;
      }
    }
    
    map.push(currentCol);
  }
  
  return map;
});
 
// 合并方法
const handleSpan = ({ rowIndex, columnIndex }) => {
  if (columnIndex >= mergeMap.value.length) return { rowspan: 1, colspan: 1 };
  const span = mergeMap.value[columnIndex][rowIndex];
  return span === 0 ? { rowspan: 0, colspan: 0 } : { rowspan: span, colspan: 1 };
};

结束语

如果你也遇到了相同的需求,希望可以帮到你!

相关推荐
李宏伟~2 小时前
uniapp生成二维码组件全能组件复制即用
前端·uni-app
TZOF2 小时前
TypeScript的新类型(三):never
前端·后端·typescript
余防2 小时前
文件上传漏洞(二)iis6.0 CGI漏洞
前端·安全·web安全·网络安全
毕业设计制作和分享2 小时前
springboot523基于Spring Boot的大学校园生活信息平台的设计与实现
前端·vue.js·spring boot·后端·生活
我是ed.2 小时前
vite + vue3 实现打包后 dist 文件夹可以直接打开 html 文件预览
前端·html
☆cwlulu3 小时前
解码Android 系统蓝牙音频全流程
前端·人工智能·算法
嘀咕博客3 小时前
GPTEngineer:AI 驱动的Web应用开发平台
前端·人工智能·ai工具
文心快码BaiduComate3 小时前
北京互联网大会 | 百度副总裁陈洋:AI Coding为新质生产力注入“新码力”
前端·后端·程序员