element ui 构建复杂表格,动态合并行列及多级表头

Elementui中Tale实现根据id相同动态合并单元格 vue3版本

<template>
  <div class="merge-table-container">
    <el-table
      :data="tableData"
      border
      style="width: 100%"
      :span-method="objectSpanMethod"
    >
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
      <el-table-column prop="name" label="项目名称"></el-table-column>
      <el-table-column prop="value" label="数值"></el-table-column>
    </el-table>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const tableData = ref([])
const spanArr = ref([]) // 存储合并规则

// 生成测试数据
const generateData = () => {
  const mockData = [
    { id: 1, name: '项目A', value: 100 },
    { id: 1, name: '项目A', value: 200 },
    { id: 1, name: '项目A', value: 300 },
    { id: 2, name: '项目B', value: 400 },
    { id: 2, name: '项目B', value: 500 },
    { id: 3, name: '项目C', value: 600 },
    { id: 3, name: '项目C', value: 700 },
    { id: 3, name: '项目C', value: 800 },
    { id: 3, name: '项目C', value: 900 }
  ]
  tableData.value = mockData
  calculateSpans(mockData)
}

// 计算合并规则
const calculateSpans = (data) => {
  const tempSpanArr = []
  let pos = 0
  
  data.forEach((item, index) => {
    if (index === 0) {
      tempSpanArr.push(1)
      pos = 0
    } else {
      if (data[index].id === data[index - 1].id) {
        tempSpanArr[pos] += 1
        tempSpanArr.push(0)
      } else {
        tempSpanArr.push(1)
        pos = index
      }
    }
  })
  
  spanArr.value = tempSpanArr
}

// 合并单元格方法
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
  if (columnIndex === 0 ) {//|| columnIndex === 1
    const rowspan = spanArr.value[rowIndex]
    const colspan = rowspan > 0 ? 1 : 0
    return {
      rowspan: rowspan,
      colspan: colspan
    }
  }
}

onMounted(() => {
  generateData()
})
</script>

<style scoped>
.merge-table-container {
  padding: 20px;
}
</style>

另一种

<template>
  <div>    
    <el-table
      :data="tableData"
      :span-method="objectSpanMethod"
      border
      style="width: 70%; margin-top: 20px">
      <el-table-column
        prop="id"
        label="ID">
      </el-table-column>
      <el-table-column
        prop="name"
        label="姓名">
      </el-table-column>
      <el-table-column
        prop="home"
        label="家庭住址">
      </el-table-column>
      <el-table-column
        prop="phone"
        label="电话">
      </el-table-column>
      <el-table-column
        prop="amount1"
        label="数值 1(元)">
      </el-table-column>
      <el-table-column
        prop="amount2"
        label="数值 2(元)">
      </el-table-column>
      <el-table-column
        prop="amount3"
        label="数值 3(元)">
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        tableData: [{
        id: '1',
        name: '王小虎',
        home: '上海',
        phone: '123456',
        amount1: '234',
        amount2: '3.2',
        amount3: 10
      }, {
        id: '1',
        name: '王小虎',
        home: '上海',
        phone: '123456',
        amount1: '165',
        amount2: '4.43',
        amount3: 12
      }, {
        id: '1',
        name: '王小虎',
        home: '上海',
        phone: '123456',
        amount1: '324',
        amount2: '1.9',
        amount3: 9
      }, {
        id: '2',
        name: '小张',
        home: '北京',
        phone: '456789',
        amount1: '621',
        amount2: '2.2',
        amount3: 17
      }, {
        id: '3',
        name: '小刘',
        home: '南京',
        phone: '789456',
        amount1: '539',
        amount2: '4.1',
        amount3: 15
      }]
      };
    },
    methods: {
     objectSpanMethod({ row, column, rowIndex, columnIndex }) {
       if(columnIndex === 0){
         //此处处理第一列
         const _row = (this.flitterData(this.tableData).one)[rowIndex]
         const _col = _row > 0 ? 1 : 0
         return {
           rowspan: _row,
           colspan: _col
         }
        }else if(columnIndex === 1 || columnIndex === 2 || columnIndex === 3){
         //此处处理第二、三、四列
         const _row = (this.flitterData2(this.tableData).one)[rowIndex]
         const _col = _row > 0 ? 1 : 0
         return {
            rowspan: _row,
            colspan: _col
          }
        }
      },
      flitterData(arr) {
       let spanOneArr = []; // 初始化一个空数组,用于存储合并行的标记
       let concatOne = 0; // 初始化变量,用于记录上一个需要合并的元素的索引
       arr.forEach((item, index)=>{ // 遍历数组
       if(index === 0){
         spanOneArr.push(1) // 如果是第一个元素,直接标记为1(表示这是一个新的开始)
         }else{
           if(item.id === arr[index-1].id){ // 如果当前元素的id与上一个元素的id相同
           spanOneArr[concatOne] += 1 // 则将上一个标记加1(表示需要合并的行数增加)
           spanOneArr.push(0) // 然后标记当前位置为0(表示这不是一个新的开始)
         }else{
           spanOneArr.push(1) // 如果id不同,则标记当前位置为1(表示这是一个新的开始)
           concatOne = index // 更新记录上一个需要合并的元素的索引
         };
       }
      });
      return {
        one: spanOneArr // 返回标记数组
      }
    },
    flitterData2(arr) {
     let spanOneArr = []; // 初始化空数组
     let concatOne = 0; // 初始化变量
     arr.forEach((item, index)=>{ // 遍历数组
     if(index === 0){
       spanOneArr.push(1) // 第一个元素标记为1
       }else{
         if(item.id === arr[index-1].id && item.name === arr[index-1].name){ // 判断id和name是否都相同
         spanOneArr[concatOne] += 1 // 如果相同,则更新上一个标记
         spanOneArr.push(0) // 标记当前位置为0
       }else{
         spanOneArr.push(1) // 如果不同,标记当前位置为1
         concatOne = index // 更新索引
       };
      }
     });
     return {
       one: spanOneArr // 返回标记数组
     }
    }
   }
 };
</script>

如图

image

参考https://blog.csdn.net/neverr_/article/details/145699744

<!-- 表格 -->
    <el-table
      :data="tableData"
      style="width: 80%; margin: 20px"
      :header-cell-style="{background: '#F8F8F8', height: '40px', padding: '0', color: '#333333'}"
      border
      :span-method="objectSpanMethod"

  >
      <el-table-column align="center" header-align="center" :show-overflow-tooltip="true" width="100" >
        <template slot-scope="scope">
          {{ scope.row.index + 1 }}
        </template>
      </el-table-column>
      <el-table-column prop="subjects" label="费用科目" align="center" header-align="center" :show-overflow-tooltip="true"></el-table-column>
      <el-table-column prop="classType" label="分类" align="center" header-align="center" :show-overflow-tooltip="true" ></el-table-column>
      <el-table-column prop="price" label="金额" align="center" header-align="center" :show-overflow-tooltip="true" ></el-table-column>
  </el-table>
View Code
return{
      tableData:[{
        index:0,
        subjects:'一次性就废',
        price:'1,200.00元'
      },{
        index:1,
        subjects:'医疗备用金',
        price:'1,200.00元'
      },{
        index:2,
        subjects:'试住费',
        price:'1,200.00元'
      },{
        index:3,
        subjects:'餐费',
        classType: '餐费(长者+保姆)',
        price:'1,200.00元'
      },{
        index:4,
        subjects:'餐费',
        classType: '餐费(长者)',
        price:'1,200.00元'
      }]
    }
View Code
//合并第一列
    flitterData (arr) {
      let spanOneArr = []
      let concatOne = 0
      arr.forEach((item, index) => {
        console.log(item,index)
        if (index === 0) {
          spanOneArr.push(1)
        } else {
          if (item.subjects === arr[index - 1].subjects) { //第一列需合并相同内容的判断条件
            spanOneArr[concatOne] += 1
            spanOneArr.push(0)
          } else {
            spanOneArr.push(1)
            concatOne = index
          };
        }
      });
      return {
        one: spanOneArr,
      }
    },
    objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 1) {  // 判断哪一列
        const _row = (this.flitterData(this.tableData).one)[rowIndex]
        const _col = _row > 0 ? 1 : 0
        return {
          rowspan: _row,
          colspan: _col
        };
      }
    },
View Code

如图

image

 参考https://blog.csdn.net/weixin_51565477/article/details/119749150

image

 

参考https://zhuanlan.zhihu.com/p/717324878

 

posted @ 2025-09-05 13:48  瞎BB的是2B  阅读(264)  评论(0)    收藏  举报