Element-ui Table 合并行或列

项目场景:

表格中数值相同且相临的单元格内合并显示
演示代码在页面最后
在这里插入图片描述

在这里插入图片描述


解决方案:

使用 Element-ui Table 插件完成

原理:

Element-ui Table 提供了一个 span-method 属性,用来合并单元格.
通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性。该函数可以返回一个包含两个元素的数组,第一个元素代表rowspan,第二个元素代表colspan。 也可以返回一个键名为rowspan和colspan的对象。
文档地址

如下面的代码,我们就能完成"目标效果"

<template>
  <div class="w">
    <h4>目标效果</h4>
    <el-table ref="dTable" :data="list" :span-method="arraySpanMethod" border stripe>
      <el-table-column prop="c1" label="C1" width="180" sortable />
      <el-table-column prop="c2" label="C2" />
      <el-table-column prop="c3" label="C3" />
      <el-table-column prop="c4" label="C4" />
      <el-table-column prop="c5" label="C5" width="220" />
      <el-table-column prop="c6" label="c6" width="220" />
    </el-table>
  </div>
</template>

<script>
export default {
      
      
  data() {
      
      
    return {
      
      
      list: [{
      
       c1: '第一类', c2: 'Siri', c3: 'B1', c4: '2022年5月27日', c5: '1', c6: '1' }, {
      
       c1: '第一类', c2: 'Siri', c3: 'B2', c4: '2022年5月27日', c5: '1', c6: '2' }, {
      
       c1: '第一类', c2: 'Siri', c3: 'B3', c4: '2022年5月27日', c5: null, c6: '3' }, {
      
       c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '4' }, {
      
       c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '5' }, {
      
       c1: '小爱同学', c2: '小爱同学', c3: '小爱同学', c4: '2022年5月27日', c5: '201', c6: '6' }, {
      
       c1: '小明', c2: '小明', c3: '小明', c4: '2022年5 月27日', c5: '201', c6: '7' }, {
      
       c1: '小明', c2: '小明', c3: '小明', c4: '2022年5月27日', c5: '201', c6: '8' }]
    }
  },
  mounted() {
      
      },
  methods: {
      
      
    // el-table span-method	合并行或列的计算方法
    // 原理 : 合并方法会遍历所有的单元格,返回该单元格所占的行,列( [rowspan,colspan] )就是显示的结果
    // ps : [1,1] 不变, [0,0] [1,0] [0,1] 隐藏,只要有一个值为0就会隐藏
    arraySpanMethod({
       
        row, column, rowIndex, columnIndex }) {
      
      
      var map = [
        [[3, 1], [3, 1], [2, 1]],
        [[0, 1], [0, 1], [0, 1]],
        [[0, 1], [0, 1], [1, 1]],
        [[2, 1], [2, 2], [0, 0]],
        [[0, 1], [0, 0], [0, 0]],
        [[1, 3], [1, 0], [0, 0]],
        [[2, 3], [1, 0], [0, 0]],
        [[0, 0], [1, 0], [0, 0]]
      ]
      
      try {
      
      
        var r = map[rowIndex][columnIndex]
        return r
      } catch (error) {
      
      
        return [1, 1]
      }
    }
  }
}
</script>

设计思路:

按上面的原理代码,我们将问题转化为 : 如何生成一个反映表格合并的二维数组

坑1

table使用的数据是对象数组 [{c1:123}] ,对象中的key不能对应列的显示顺序

坑2

Table插件内部数据发生变化时(如:排序,筛选),父组件传递的值是不变的
所以必须监听子组件内部的值

最终代码:

/**
 * \src\views\table\merge-cell.js
 * @param {*} table vue对象, Element-ui Table 插件
 * @param {*} colMap 数组, 需要合并的列名,如: ['c1','c2','c2']
 * @param {*} isSpanRow 布尔,是否合并行
 * @param {*} isSpanCol 布尔,是否合并列
 * @returns 二维数组,Table的显示结果
 *
 * mergeCell(this.$refs.dTable, ['c1', 'c2', 'c3'], true, true)
 */
var mergeCell = function(table, colMap, isSpanRow = true, isSpanCol = true) {
    
    
  var list = table.tableData

  // table.tableData 中 每行的数据是一个对象,不能直接的反应显示的顺序
  // 即你不能知道第一列右则的列是那个.
  // 需要通过table.columns建立显示的顺序
  var columns = []
  for (const item of table.columns) {
    
    
    columns.push(item.property)
  }

  // 结果应是一个二维数组
  // 先充填数据,使用其成为全显示的数组
  var result = []
  for (let row = 0, rowLen = list.length; row < rowLen; row++) {
    
    
    const ss = []
    for (let col = 0, colLen = columns.length; col < colLen; col++) {
    
    
      ss.push([1, 1])
    }
    result.push(ss)
  }

  // 合并行
  if (isSpanRow) {
    
    
    result = mergeRow(list, columns, colMap, result)
  }

  // 合并列
  if (isSpanCol) {
    
    
    result = mergeCol(list, columns, colMap, result)
  }

  return result
}

var mergeRow = function(list, columns, colMap, result) {
    
    
  for (const item of colMap) {
    
    
    const p = {
    
    
      colKey: item,
      col: columns.indexOf(item),
      row: 0,
      val: ''
    }
    for (let row = 0, rowLen = list.length; row < rowLen; row++) {
    
    
      if (p.val === list[row][p.colKey]) {
    
    
        result[p.row][p.col][0] += 1
        result[row][p.col][0] = 0
      } else {
    
    
        p.row = row
        p.val = list[row][p.colKey]
      }
    }
  }
  return result
}

var mergeCol = function(list, columns, colMap, result) {
    
    
  for (let row = 0, rowLen = list.length; row < rowLen; row++) {
    
    
    const p = {
    
    
      col: 0,
      row: row,
      val: ''
    }

    for (let col = 0, colLen = columns.length; col < colLen; col++) {
    
    
      if (!colMap.includes(columns[col])) {
    
    
        p.col = col
        p.val = list[row][columns[col]]
        continue
      }

      if (p.val === list[row][columns[col]]) {
    
    
        result[p.row][p.col][1] += 1
        result[p.row][col][1] = 0
      } else {
    
    
        p.col = col
        p.val = list[row][columns[col]]
      }
    }
  }

  return result
}
export default mergeCell
/**
 * src\views\table\merge.vue
 * 合并单元格 DEMO
 */
<template>
  <div class="w">
    <h4>原始表</h4>
    <el-table
      border
      :data="list"
      style="width: 100%"
      header-row-class-name="t-header"
      stripe
      class="dTable"
    >
      <el-table-column prop="c1" label="C1" width="180" />
      <el-table-column prop="c2" label="C2" />
      <el-table-column prop="c3" label="C3" />
      <el-table-column prop="c4" label="C4" />
      <el-table-column prop="c5" label="C5" width="220" />
      <el-table-column prop="c6" label="c6" width="220" />
    </el-table>

    <h4>目标效果</h4>
    <el-table
      ref="dTable"
      border
      :data="list"
      style="width: 100%"
      header-row-class-name="t-header"
      stripe
      class="dTable"
      :span-method="arraySpanMethod"
    >
      <el-table-column prop="c1" label="C1" width="180" sortable />
      <el-table-column
        prop="c2"
        label="C2"
        :filters="[
          { text: 'Siri', value: 'Siri' },
          { text: 'YOYO', value: 'YOYO' },
          { text: '小爱同学', value: '小爱同学' }
        ]"
        :filter-method="filterHandler"
      />
      <el-table-column prop="c3" label="C3" />
      <el-table-column prop="c4" label="C4" />

      <el-table-column prop="c5" label="C5" width="220" />
      <el-table-column prop="c6" label="c6" width="220" />
    </el-table>
  </div>
</template>

<script>
import mergeCell from './merge-cell.js'
export default {
      
      
  filters: {
      
      },
  data() {
      
      
    return {
      
      
      list: [
        {
      
      
          c1: '第一类', c2: 'Siri', c3: 'B1', c4: '2022年5月27日', c5: '1', c6: '1'
		},
		{
      
      
		  c1: '第一类', c2: 'Siri', c3: 'B2', c4: '2022年5月27日', c5: '1', c6: '2'
		},
		{
      
      
		  c1: '第一类', c2: 'Siri', c3: 'B3', c4: '2022年5月27日', c5: null, c6: '3' 
		},
		{
      
      
		  c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '4'
		},
		{
      
      
		  c1: '第二类', c2: 'YOYO', c3: 'YOYO', c4: '2022年5月27日', c5: '29', c6: '5'
		},
		{
      
      
		  c1: '小爱同学', c2: '小爱同学', c3: '小爱同学', c4: '2022年5月27日', c5: '201', c6: '6'
		},
		{
      
      
		  c1: '小明', c2: '小明', c3: '小明', c4: '2022年5月27日', c5: '201', c6: '7'
		},
		{
      
      
		  c1: '小明', c2: '小明', c3: '小明', c4: '2022年5月27日', c5: '201', c6: '8'
		}
      ],
      mergeMap: []
    }
  },
  mounted() {
      
      
    // 当el-table 内部数据发生变化时(如:排序,筛选),父组件传递的值是不变的.
    // 所以必须监听子组件内部的值
    // https://cn.vuejs.org/v2/api/#vm-watch
    this.$watch(
      function() {
      
      
        return this.$refs.dTable.tableData
      },
      function(newVal, oldVal) {
      
      
        var ss = mergeCell(this.$refs.dTable, ['c1', 'c2', 'c3'], true, true)
        this.mergeMap = ss
      },
      {
      
      
        immediate: true
      }
    )
  },
  methods: {
      
      
    // el-table span-method	合并行或列的计算方法
    // 原理 : 合并方法会遍历所有的单元格,返回该单元格所占的行,列( [rowspan,colspan] )就是显示的结果
    // ps : [1,1] 不变, [0,0] [1,0] [0,1] 隐藏,只要有一个值为0就会隐藏
    arraySpanMethod({
       
        row, column, rowIndex, columnIndex }) {
      
      
      var map = this.mergeMap
      try {
      
      
        var r = map[rowIndex][columnIndex]
        return r
      } catch (error) {
      
      
        return r
      }
    },
    filterHandler(value, row, column) {
      
      
      const property = column['property']
      return row[property] === value
    }
  }
}
</script>

猜你喜欢

转载自blog.csdn.net/alxw2010/article/details/125099715