element-ui复杂表格的封装(多头表格及合并单元格)

632 阅读1分钟

实际的前端开发过程中,table表格的数据都有后端返回,所以table表格的表头以及合并单元格需要动态去渲染。简单的一级表头可使用v-for循环去渲染el-table-column,多头表格可以将其封装成递归组件进行渲染。而单元格的合并也可动态去计算。

父组件的写法

<template>
  <div class="array-table-area" id="array-table-area">
    <el-table
      :data="tbody"
      :span-method="objectSpanMethod"
      border
      stripe>
      <--将el-table-column单独抽取成组件,使用vue的插槽功能-->
      <template v-for="(item, index) in thead">
        <array-table-child :list="item" :key="'tables' + index"></array-table-child>
      </template>
    </el-table>
  </div>
</template>
<script>
import arrayTableChild from './public/array-table-child.vue' // 剥离的递归组件
export default {
  name: 'achievements',
  data () {
    return {
      thead: [],
      tbody: [],
      columnFiled: '',
      rowFiled: [],
      spanArr: [],
      position: 0
    }
  },
  props: {
    tableData: {
      type: Object,
      default: null
    }
  },
  components: {
    arrayTableChild
  },
  methods: {
    // 计算合并所需数据
    rowspan () {
      this.tbody.forEach((item, index) => {
        if (index === 0) {
          this.spanArr.push(1)
          this.position = 0
        } else {
          // 这里是判断的条件
          if ((this.tbody[index][this.columnFiled] === this.tbody[index - 1][this.columnFiled])) {
            this.spanArr[this.position] += 1
            this.spanArr.push(0)
          } else {
            this.spanArr.push(1)
            this.position = index
          }
        }
      })
    },
    objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0) {
        const _row = this.spanArr[rowIndex]
        const _col = _row > 0 ? 1 : 0
        this.rowFiled.includes(row[this.columnFiled])
        if (this.rowFiled.includes(row[this.columnFiled])) {
          return {
            rowspan: 1,
            colspan: 2
          }
        } else {
          return {
            rowspan: _row,
            colspan: _col
          }
        }
      } else if (columnIndex === 1) {
        if (this.rowFiled.includes(row[this.columnFiled])) {
          return {
            rowspan: 0,
            colspan: 0
          }
        }
      }
    }
  },
  watch: {
    tableData: { // 监听传入的数据,动态渲染表格
      handler (oldVal, newVal) {
        this.thead = newVal.thead
        this.tbody = newVal.tbody
        this.columnFiled = newVal.columnFiled
        this.rowFiled = newVal.rowFiled
        setTimeout(() => {
          this.spanArr = []
          this.position = 0
          this.rowspan()
        })
      },
      deep: true
    }
  }
}
</script>

子组件的写法

<template>
  <el-table-column
    header-align="center"
    align="center"
    :key="'table1' + index"
    :label="list.name">
    <template slot-scope="scope">
      <span v-html="scope.row[list.filed]"></span>
    </template>
    <template v-if="list.children && list.children.length !== 0">
      <template v-for="(item1, index1) in list.children">
        <table-child :list="item1" :key="'tabless' + index1"></table-child>
      </template>
    </template>
  </el-table-column>
</template>

<script>
export default {
  name: 'table-child',
  props: {
    list: Array
  }
}
</script>

需要注意的是el-table标签和el-table-column之间不能嵌套其他标签,可用template否则渲染出来的列表第一列会跑到最后一列去

代码中的引用

传入组件的数据格式

tableData: {
        thead: [
          {filed: 'bigClass', name: '大类'},
          {filed: 'detail', name: '细项'},
          {
            field: 'allDetail',
            name: '详情',
            children: [
              {filed: 'weight', name: '权重'},
              {filed: 'score', name: '得分'},
              {filed: 'ranking', name: '排名'}
            ]
          }
        ],
        tbody: [
          {bigClass: '收入<br/>(25%)', detail: '码号收入预算完成', ranking: 1, score: 0, weight: 25},
          {bigClass: '宽带攻防<br/>(75%)', detail: '宽带新增', ranking: 1, score: 0, weight: 75},
          {bigClass: '小计', detail: '小计', ranking: 1, score: 0, weight: 100},
          {bigClass: '加扣分', detail: '客户经理、装维即时满意度测评', ranking: 1, score: 3, weight: 10},
          {bigClass: '加扣分', detail: '小计', ranking: 1, score: 3, weight: 10},
          {bigClass: '合计', detail: '合计', ranking: 1, score: 3, weight: 110}
        ],
        columnFiled: 'bigClass',  需要合并的行,适合合并第一行(相同值必须放一起)
        rowFiled: ['合计', '小计']  需要合并的列名,适合合并前两列
      },

引用

<array-table :tableData="tableData"></array-table>

呈现出的效果

微信图片_20230512094903.png