el-table 组件实现 “合并单元格 + N行数据小计” 功能

16 篇文章 0 订阅
16 篇文章 0 订阅

需求 - 要实现的效果

在这里插入图片描述

父组件中 info 数据示例

const info = {
  itemMap: {
    警告: [
      {
        total: 28,
        cfzl: '1',
        cfzlView: '警告',
        wfxl: '12',
        wfxlView: '超速行驶',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 3,
        cfzl: '1',
        cfzlView: '警告',
        wfxl: '17',
        wfxlView: '未低速通过',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 6,
        cfzl: '1',
        cfzlView: '警告',
        wfxl: '26',
        wfxlView: '违法停车',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 21,
        cfzl: '1',
        cfzlView: '警告',
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 3,
        cfzl: '1',
        cfzlView: '警告',
        wfxl: '49',
        wfxlView: '其他影响安全行为',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 1,
        cfzl: '1',
        cfzlView: '警告',
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ],
    罚款: [
      {
        total: 56,
        cfzl: '2',
        cfzlView: '罚款',
        wfxl: '12',
        wfxlView: '超速行驶',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 6,
        cfzl: '2',
        cfzlView: '罚款',
        wfxl: '17',
        wfxlView: '未低速通过',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 12,
        cfzl: '2',
        cfzlView: '罚款',
        wfxl: '26',
        wfxlView: '违法停车',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 42,
        cfzl: '2',
        cfzlView: '罚款',
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 6,
        cfzl: '2',
        cfzlView: '罚款',
        wfxl: '49',
        wfxlView: '其他影响安全行为',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        total: 2,
        cfzl: '2',
        cfzlView: '罚款',
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ]
  },
  columns: [
    {
      // total: 28,
      // cfzl: '1',
      // cfzlView: '警告',
      // wfxl: '12',
      // wfxlView: '超速行驶',
      jtfs: 'B11',
      jtfsView: '重型栏板半挂车'
    },
    {
      // total: 1,
      // cfzl: '1',
      // cfzlView: '警告',
      // wfxl: '28',
      // wfxlView: '违法装载',
      jtfs: 'B21',
      jtfsView: '中型栏板半挂车'
    }
  ]
}

初始代码

父组件

<!-- info 数据来源 → info 数据示例 -->
<CommonTable :info="info"/>

CommonTable.vue 子组件

<template>
  <el-table
    :data="tableData"
    border
    stripe
    max-height="400"
    size="mini"
    :span-method="firstColMergeSpan"
  >
    <el-table-column align="center" prop="cfzl" label="处罚种类" />
    <el-table-column align="center" prop="wfxwfl" label="违法行为分类" />

    <el-table-column
      v-for="(item, index) in columns"
      :key="index"
      align="center"
      :prop="item.jtfs"
      :label="item.jtfsView || '-'"
    >
      <template slot-scope="{ row, $index }">
        <span>{{ row[item.jtfs] | noDataFilter }}</span>
      </template>
    </el-table-column>

    <el-table-column align="center" prop="xj" label="小计" />
  </el-table>
</template>

<script>
export default {
  name: 'CommonTable',
  components: {},

  props: {
    info: {
      type: Object,
      required: true
    }
  },

  data() {
    return {
      spanArr: []
    }
  },

  computed: {
    tableData() {
      const list = []
      Object.entries(this.info?.itemMap || {}).forEach((i) => {
        i[1]?.forEach((ii) => {
          list.push({
            // cfzl: ii.cfzlView,
            cfzl: i[0],
            wfxwfl: ii.wfxlView,
            [ii.jtfs]: ii.total,
            jtfs: ii.jtfs
          })
        })
      })
      return list.map((item) => {
        return {
          ...item,
          xj: this.columnKeyList.reduce((a, b) => {
            return a + (item[b] || 0)
          }, 0)
        }
      })
    },
    columns() {
      return this.info.columns || []
    },
    columnKeyList() {
      return this.columns.map((item) => item.jtfs)
    }
  },

  watch: {
    info() {
      this.xjPosition()
      this.firstColMergeCount()
    }
  },

  created() {},

  methods: {
    // 计算小计行插入位置
    xjPosition(Human = this.tableData) {
      const doctorMap = {}
      for (let i = 0; i < Human.length; i++) {
        // 找出相同名称的行数
        const doctorName = Human[i].cfzl
        if (doctorMap[doctorName] !== undefined) {
          doctorMap[doctorName].push(i)
        } else {
          doctorMap[doctorName] = [i]
        }
      }
      const keyArr = []
      for (const k in doctorMap) {
        // 取出key并倒序,防止正序插入会影响行下标
        keyArr.unshift(k)
      }
      keyArr.forEach((ele, index) => {
        const lastIndex = doctorMap[ele][doctorMap[ele].length - 1] // 找出相同名称最后一行插入合计数据
        const obj = this.xjRowDataCalc(Human, ele) // 计算出小计行数据
        Human.splice(lastIndex + 1, 0, obj) // 插入
      })
    },
    // 小计行计算
    xjRowDataCalc(data, name) {
      const obj = {
        cfzl: name, // 第一列用于合并单元格
        wfxwfl: '小计'
      }
      this.columnKeyList.forEach((key) => {
        obj[key] = 0
      })
      data.forEach((item) => {
        // “处罚种类” 相同的加起来
        if (item.cfzl === name) {
          this.columnKeyList.forEach((key) => {
            obj[key] += Number(item[key] || 0)
          })
        }
      })
      obj.xj = this.columnKeyList.reduce((a, b) => {
        return a + (obj[b] || 0)
      }, 0)
      return obj
    },
    // 合并单元格
    firstColMergeSpan({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0) {
        const _row = this.spanArr[rowIndex]
        const _col = _row > 0 ? 1 : 0
        return {
          rowspan: _row,
          colspan: _col
        }
      }
    },
    // 计算要合并的单元格
    firstColMergeCount() {
      let contactDot = 0
      this.spanArr = []
      this.tableData.forEach((item, index) => {
        item.index = index
        if (index === 0) {
          this.spanArr.push(1)
        } else {
          // 根据相同 “处罚种类” 来合并
          if (item.cfzl === this.tableData[index - 1].cfzl) {
            this.spanArr[contactDot] += 1
            this.spanArr.push(0)
          } else {
            contactDot = index
            this.spanArr.push(1)
          }
        }
      })
    }
  }
}
</script>

<style lang='scss' scoped>
</style>

代码升级(可供多个表格使用)

图1
在这里插入图片描述
图2
在这里插入图片描述
图3
在这里插入图片描述
图4
在这里插入图片描述

根据接口返回数据不同(数据格式一致,只是部分字段名不一致),向子组件传入不同的字段名。

CommonTable.vue 子组件

<template>
  <el-table
    :data="tableData"
    border
    stripe
    max-height="400"
    size="mini"
    :span-method="firstColMergeSpan"
  >
    <!-- 第一列 -->
    <el-table-column align="center" :prop="oneColPropField" :label="oneColLabelField" />
    <!-- 第二列 -->
    <el-table-column align="center" :prop="twoColPropField" :label="twoColLabelField" />

    <!-- 其他数量列 -->
    <el-table-column
      v-for="(item, index) in columns"
      :key="index"
      align="center"
      :prop="item[countColPropsField]"
      :label="item[countColLabelField] || '-'"
    >
      <template slot-scope="{ row, $index }">
        <span>{{ row[item[countColPropsField]] | noDataFilter }}</span>
      </template>
    </el-table-column>

    <!-- “小计”列 -->
    <el-table-column align="center" prop="xj" label="小计" />
  </el-table>
</template>

<script>
export default {
  name: 'CommonTable',
  components: {},

  props: {
    info: {
      type: Object,
      required: true
    },
    // 自定义表格列
    selfColumns: {
      type: Array,
      default: () => []
    },
    // 表格列非自定义时(接口获取)列字段名
    columnKeyField: {
      type: String,
      default: 'jtfs'
    },
    // 第一列字段名
    oneColPropField: {
      type: String,
      required: true
    },
    // 第一列展示header文本
    oneColLabelField: {
      type: String,
      required: true
    },
    // 第一列数据来源字段名
    oneColDataField: {
      type: String,
      required: true
    },
    // 第二列字段名
    twoColPropField: {
      type: String,
      required: true
    },
    // 第二列展示header文本
    twoColLabelField: {
      type: String,
      required: true
    },
    // 第二列数据来源字段名
    twoColDataField: {
      type: String,
      required: true
    },
    // 其他数量 列 字段名
    countColPropsField: {
      type: String,
      default: 'jtfs'
    },
    // 其他数量 列 展示header文本字段名
    countColLabelField: {
      type: String,
      default: 'jtfsView'
    },
    // 其他数量 列 数据来源字段名
    countColDataField: {
      type: String,
      default: 'total'
    }
  },

  data() {
    return {
      spanArr: []
    }
  },

  computed: {
    // 表格数据处理
    tableData() {
      const list = []
      Object.entries(this.info?.itemMap || {}).forEach((i) => {
        i[1]?.forEach((ii) => {
          list.push({
            // [this.oneColPropField]: ii[this.oneColDataField] // 第一列数据(第一列数据部分表格 ii 中无第一列数据,所以使用↓↓↓下一行代码 i[0] 取第一列数据)
            [this.oneColPropField]: i[0], // 第一列数据
            [this.twoColPropField]: ii[this.twoColDataField], // 第二列数据
            [ii[this.countColPropsField]]: ii[this.countColDataField] // 其他数量列数据
            // jtfs: ii.jtfs
          })
        })
      })
      return list.map((item) => {
        return {
          ...item,
          // 计算小计数量
          xj: this.columnKeyList.reduce((a, b) => {
            return a + (item[b] || 0)
          }, 0)
        }
      })
    },
    columns() {
      /**
       * 表格列获取
       *    父组件有传表格列 selfColumns 就用 selfColumns
       *    否则用接口获的表格列
       */
      return (this.selfColumns.length && this.selfColumns) || this.info.columns || []
    },
    columnKeyList() {
      // 获取表格每列字段名组成数组
      return this.columns.map((item) => item[this.columnKeyField])
    }
  },

  watch: {
    info() {
      this.xjPosition()
      this.oneColMergeCount()
    }
  },

  created() {},

  methods: {
    // 计算小计行插入位置
    xjPosition(Human = this.tableData) {
      const doctorMap = {}
      for (let i = 0; i < Human.length; i++) {
        // 找出相同名称的行数
        const doctorName = Human[i][this.oneColPropField]
        if (doctorMap[doctorName] !== undefined) {
          doctorMap[doctorName].push(i)
        } else {
          doctorMap[doctorName] = [i]
        }
      }
      const keyArr = []
      for (const k in doctorMap) {
        // 取出key并倒序,防止正序插入会影响行下标
        keyArr.unshift(k)
      }
      keyArr.forEach((ele, index) => {
        const lastIndex = doctorMap[ele][doctorMap[ele].length - 1] // 找出相同名称最后一行插入合计数据
        const obj = this.xjRowDataCalc(Human, ele) // 计算出小计行数据
        Human.splice(lastIndex + 1, 0, obj) // 插入
      })
    },
    // 小计行数据计算
    xjRowDataCalc(data, name) {
      const obj = {
        [this.oneColPropField]: name, // 第一列用于合并单元格
        [this.twoColPropField]: '小计' // 第二列数据
      }
      this.columnKeyList.forEach((key) => {
        obj[key] = 0 // 其他书两列数据
      })
      data.forEach((item) => {
        // 第一列 oneColPropField 数据相同的加起来
        if (item[this.oneColPropField] === name) {
          this.columnKeyList.forEach((key) => {
            obj[key] += Number(item[key] || 0)
          })
        }
      })
      // 小计列数据总和(小计行和小计列交汇处数据)
      obj.xj = this.columnKeyList.reduce((a, b) => {
        return a + (obj[b] || 0)
      }, 0)
      return obj
    },
    // 合并单元格
    firstColMergeSpan({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0) {
        const _row = this.spanArr[rowIndex]
        const _col = _row > 0 ? 1 : 0
        return {
          rowspan: _row,
          colspan: _col
        }
      }
    },
    // 计算要合并的单元格
    oneColMergeCount() {
      let contactDot = 0
      this.spanArr = []
      this.tableData.forEach((item, index) => {
        item.index = index
        if (index === 0) {
          this.spanArr.push(1)
        } else {
          // 第一列相同的合并
          if (item[this.oneColPropField] === this.tableData[index - 1][this.oneColPropField]) {
            this.spanArr[contactDot] += 1
            this.spanArr.push(0)
          } else {
            contactDot = index
            this.spanArr.push(1)
          }
        }
      })
    }
  }
}
</script>

<style lang='scss' scoped>
</style>

使用子组件1 - 父组件 - 图1~图3使用

<!-- info 数据来源 → info 数据示例 -->
<CommonTable
  :info="info"
  one-col-prop-field="cfzl"
  one-col-label-field="处罚种类"
  one-col-data-field="cfzlView"
  two-col-prop-field="wfxwfl"
  two-col-label-field="违法行为分类"
  two-col-data-field="wfxlView"
/>

效果展示

在这里插入图片描述

使用子组件2 - 父组件 - 图4使用

<CommonTable
  :info="info"
  one-col-prop-field="cfzl"
  one-col-label-field="处罚种类"
  one-col-data-field="cfzlView"
  two-col-prop-field="wfxwfl"
  two-col-label-field="违法行为分类"
  two-col-data-field="wfxlView"
  column-key-field="timenum"
  count-col-props-field="timenum"
  count-col-label-field="label"
  :self-columns="columns"
/>

<script>
	export default {
	  data() {
	    return {
	      columns: [...Array(24).keys()].map((item) => {
			return {
			  timenum: `${item}`,
			  label: `${item}-${item + 1}`
			}
	      }),
	      info: {
	        itemMap: {
	          警告: [
	            {
	              timenum: 17,
	              total: 9,
	              cfzl: '1',
	              cfzlView: '警告',
	              wfxl: '69',
	              wfxlView: '其他影响安全行为',
	              jtfs: null,
	              jtfsView: null
	            },
	            {
	              timenum: 17,
	              total: 3,
	              cfzl: '1',
	              cfzlView: '警告',
	              wfxl: '58',
	              wfxlView: '违法上道路行驶',
	              jtfs: null,
	              jtfsView: null
	            }
	          ]
	        },
	        columns: []
	      }
	    }
	  }
	}
</script>

效果展示

在这里插入图片描述

注意

  • 使用子组件1 和 使用子组件2 中 info 数据不同

【代码优化 - 解决bug】数据重复问题

解决数据重复问题
在这里插入图片描述

在这里插入图片描述

CommonTable 组件

<template>
  <el-table
    :data="tableData"
    border
    stripe
    max-height="400"
    size="mini"
    :span-method="firstColMergeSpan"
  >
    <!-- 第一列 -->
    <el-table-column align="center" :prop="oneColPropField" :label="oneColLabelField" />
    <!-- 第二列 -->
    <el-table-column align="center" :prop="twoColPropField" :label="twoColLabelField" />

    <!-- 其他数量列 -->
    <el-table-column
      v-for="(item, index) in columns"
      :key="index"
      align="center"
      :prop="item[countColPropsField]"
      :label="item[countColLabelField] || '-'"
    >
      <template slot-scope="{ row, $index }">
        <span>{{ row[item[countColPropsField]] | noDataFilter }}</span>
      </template>
    </el-table-column>

    <!-- “小计”列 -->
    <el-table-column align="center" prop="xj" label="小计" />
  </el-table>
</template>

<script>
export default {
  name: 'CommonTable',
  components: {},

  props: {
    info: {
      type: Object,
      required: true
    },
    // 自定义表格列
    selfColumns: {
      type: Array,
      default: () => []
    },
    // 表格列非自定义时(接口获取)列字段名
    columnKeyField: {
      type: String,
      default: 'jtfs'
    },
    // 第一列字段名
    oneColPropField: {
      type: String,
      required: true
    },
    // 第一列展示header文本
    oneColLabelField: {
      type: String,
      required: true
    },
    // 第一列数据来源字段名
    oneColDataField: {
      type: String,
      required: true
    },
    // 第二列字段名
    twoColPropField: {
      type: String,
      required: true
    },
    // 第二列展示header文本
    twoColLabelField: {
      type: String,
      required: true
    },
    // 第二列数据来源字段名
    twoColDataField: {
      type: String,
      required: true
    },
    // 其他数量 列 字段名
    countColPropsField: {
      type: String,
      default: 'jtfs'
    },
    // 其他数量 列 展示header文本字段名
    countColLabelField: {
      type: String,
      default: 'jtfsView'
    },
    // 其他数量 列 数据来源字段名
    countColDataField: {
      type: String,
      default: 'total'
    }
  },

  data() {
    return {
      spanArr: []
    }
  },

  computed: {
    // 表格数据处理
    tableData() {
      const list = []
      Object.entries(this.info?.itemMap || {}).forEach((i) => {
        i[1]?.forEach((ii) => {
          /** ** 解决数据重复问题 start ****/
          const listDataIndex = list.findIndex(
            (listItem) => listItem[this.twoColPropField] === ii[this.twoColDataField] 
          )
          // 判断即将要 push 进 list 的数据 是否与 list 中已有数据的第二列数据有重复,有重复的话,就在 list 中重复的第二列数据的行数据中只添加当前数据为“其他数量列数据”
          if (listDataIndex !== -1) {
            list[listDataIndex][ii[this.countColPropsField]] = ii[this.countColDataField]
            return
          }
          /** ** 解决数据重复问题 end ****/
          list.push({
            // [this.oneColPropField]: ii[this.oneColDataField] // 第一列数据(第一列数据部分表格 ii 中无第一列数据,所以使用↓↓↓下一行代码 i[0] 取第一列数据)
            [this.oneColPropField]: i[0], // 第一列数据
            [this.twoColPropField]: ii[this.twoColDataField], // 第二列数据
            [ii[this.countColPropsField]]: ii[this.countColDataField] // 其他数量列数据
            // jtfs: ii.jtfs
          })
        })
      })
      return list.map((item) => {
        return {
          ...item,
          // 计算小计数量
          xj: this.columnKeyList.reduce((a, b) => {
            return a + (item[b] || 0)
          }, 0)
        }
      })
    },
    columns() {
      /**
       * 表格列获取
       *    父组件有传表格列 selfColumns 就用 selfColumns
       *    否则用接口获的表格列
       */
      return (this.selfColumns.length && this.selfColumns) || this.info.columns || []
    },
    columnKeyList() {
      // 获取表格每列字段名组成数组
      return this.columns.map((item) => item[this.columnKeyField])
    }
  },

  watch: {
    info() {
      this.xjPosition()
      this.oneColMergeCount()
    }
  },

  created() {},

  methods: {
    // 计算小计行插入位置
    xjPosition(Human = this.tableData) {
      const doctorMap = {}
      for (let i = 0; i < Human.length; i++) {
        // 找出相同名称的行数
        const doctorName = Human[i][this.oneColPropField]
        if (doctorMap[doctorName] !== undefined) {
          doctorMap[doctorName].push(i)
        } else {
          doctorMap[doctorName] = [i]
        }
      }
      const keyArr = []
      for (const k in doctorMap) {
        // 取出key并倒序,防止正序插入会影响行下标
        keyArr.unshift(k)
      }
      keyArr.forEach((ele, index) => {
        const lastIndex = doctorMap[ele][doctorMap[ele].length - 1] // 找出相同名称最后一行插入合计数据
        const obj = this.xjRowDataCalc(Human, ele) // 计算出小计行数据
        Human.splice(lastIndex + 1, 0, obj) // 插入
      })
    },
    // 小计行数据计算
    xjRowDataCalc(data, name) {
      const obj = {
        [this.oneColPropField]: name, // 第一列用于合并单元格
        [this.twoColPropField]: '小计' // 第二列数据
      }
      this.columnKeyList.forEach((key) => {
        obj[key] = 0 // 其他书两列数据
      })
      data.forEach((item) => {
        // 第一列 oneColPropField 数据相同的加起来
        if (item[this.oneColPropField] === name) {
          this.columnKeyList.forEach((key) => {
            obj[key] += Number(item[key] || 0)
          })
        }
      })
      // 小计列数据总和(小计行和小计列交汇处数据)
      obj.xj = this.columnKeyList.reduce((a, b) => {
        return a + (obj[b] || 0)
      }, 0)
      return obj
    },
    // 合并单元格
    firstColMergeSpan({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0) {
        const _row = this.spanArr[rowIndex]
        const _col = _row > 0 ? 1 : 0
        return {
          rowspan: _row,
          colspan: _col
        }
      }
    },
    // 计算要合并的单元格
    oneColMergeCount() {
      let contactDot = 0
      this.spanArr = []
      this.tableData.forEach((item, index) => {
        item.index = index
        if (index === 0) {
          this.spanArr.push(1)
        } else {
          // 第一列相同的合并
          if (item[this.oneColPropField] === this.tableData[index - 1][this.oneColPropField]) {
            this.spanArr[contactDot] += 1
            this.spanArr.push(0)
          } else {
            contactDot = index
            this.spanArr.push(1)
          }
        }
      })
    }
  }
}
</script>

<style lang='scss' scoped>
</style>

【代码优化 - bug 解决】 tableData 缺少部分数据

  • 【问题】数据处理不对 - 缺少部分数据
  • 【原因】 现有数据中第一列数据重复、其他数据列重复情况未考虑,导致 其他数据列数据被替换/非同一第一列数据而同其他数据列数据相同时数据累加
    在这里插入图片描述

数据处理代码优化前 - 变量命名优化

【变量命名优化】

  • i 重命名为 itemMapKeyValueArray
  • ii 重命名为 itemMapListItem
  • list[listDataIndex] 命名变量为 listCurrent

CommonTable 组件 - 变量命名优化

computed: {
  // 表格数据处理
  tableData() {
    const list = []
    Object.entries(this.info?.itemMap || {}).forEach((itemMapKeyValueArray) => {
      itemMapKeyValueArray[1]?.forEach((itemMapListItem) => {
        /** ** 解决数据重复问题 start ****/
        const listDataIndex = list.findIndex(
          (listItem) => listItem[this.twoColPropField] === itemMapListItem[this.twoColDataField]
        )
        // 判断即将要 push 进 list 的数据 是否与 list 中已有数据的第二列数据有重复,有重复的话,就在 list 中重复的第二列数据的行数据中只添加当前数据为“其他数量列数据”
        if (listDataIndex !== -1) {
          const listCurrent = list[listDataIndex]
          listCurrent[itemMapListItem[this.countColPropsField]] =
            itemMapListItem[this.countColDataField]
          return
        }
        /** ** 解决数据重复问题 end ****/
        list.push({
          [this.oneColPropField]: itemMapKeyValueArray[0], // 第一列数据
          [this.twoColPropField]: itemMapListItem[this.twoColDataField], // 第二列数据
          [itemMapListItem[this.countColPropsField]]: itemMapListItem[this.countColDataField] // 其他数量列数据
        })
      })
    })
    return list.map((item) => {
      return {
        ...item,
        // 计算小计数量
        xj: this.columnKeyList.reduce((a, b) => {
          return a + (item[b] || 0)
        }, 0)
      }
    })
  },
  // ...
},

代码优化

CommonTable 组件 - tableData 数据优化 只摘出相关代码,未摘出代码不变

computed: {
  // 表格数据处理
  tableData() {
    const list = []
    Object.entries(this.info?.itemMap || {}).forEach((itemMapKeyValueArray) => {
      itemMapKeyValueArray[1]?.forEach((itemMapListItem) => {
        /** ** 解决数据重复问题 start ****/
        const listDataIndex = list.findIndex(
          (listItem) => listItem[this.twoColPropField] === itemMapListItem[this.twoColDataField]
        )
        // 判断即将要 push 进 list 的数据 是否与 list 中已有数据的第二列数据有重复,有重复的话,就在 list 中重复的第二列数据的行数据中只添加当前数据为“其他数量列数据”
        if (listDataIndex !== -1) {
          const listCurrent = list[listDataIndex]

          /** ** 解决数据缺失问题 start ****/
          /**
           * 现有 list 表格数据中当前数据 - 有无即将处理数据的 “其他数量列数据”
           *    没有:现有 list 表格数据中当前数据添加 “其他数量列数据”
           *    有:判断 现有 list 表格数据中当前数据 的 oneColDataField 是否和即将处理的数据中 oneColDataField 一致
           *        一致:说明现有 list 表格数据 已经存在 “相同第一列数据 && 相同其他数据列字段” --> 数据处理:其他数据列的数据叠加即将处理数据的其他数据列数据
           *        不一致:说明现有 list 数据中不存在 “第一列数据和即将处理数据的第一列数据相同 && 其他数据列字段和即将处理数据的其他数据列相同” 的数据 --> 数据处理:向 list 添加新一条数据
           */
          if (listCurrent[itemMapListItem[this.countColPropsField]]) {
            // 【判断】现有 list 表格数据中当前数据 - 【有】即将处理数据的 “其他数量列数据”
            if (
              listCurrent[this.oneColDataField] ===
              `${itemMapListItem[this.oneColDataField]}-${
                itemMapListItem[this.oneColDataField] + 1
              }`
            ) {
              // 【判断】 现有 list 表格数据中当前数据 的 oneColDataField  和即将处理的数据中 oneColDataField 【一致】
              // 一致:说明现有 list 表格数据 已经存在 “相同第一列数据 && 相同其他数据列字段” --> 数据处理:其他数据列的数据叠加即将处理数据的其他数据列数据
              listCurrent[itemMapListItem[this.countColPropsField]] +=
                itemMapListItem[this.countColDataField]
            } else {
              // 【判断】 现有 list 表格数据中当前数据 的 oneColDataField  和即将处理的数据中 oneColDataField 【不一致】
              // 不一致:说明现有 list 数据中不存在 “第一列数据和即将处理数据的第一列数据相同 && 其他数据列字段和即将处理数据的其他数据列相同” 的数据 --> 数据处理:向 list 添加新一条数据
              list.push({
                [this.oneColPropField]: itemMapKeyValueArray[0],
                [this.twoColPropField]: itemMapListItem[this.twoColDataField],
                [itemMapListItem[this.countColPropsField]]:
                  itemMapListItem[this.countColDataField]
              })
            }
            /** ** 解决数据缺失问题 end ****/
          } else {
            // 【判断】现有 list 表格数据中当前数据 - 【没有】即将处理数据的 “其他数量列数据”
            // 现有 list 表格数据中当前数据添加 “其他数量列数据”
            listCurrent[itemMapListItem[this.countColPropsField]] =
              itemMapListItem[this.countColDataField]
          }
          return
        }
        /** ** 解决数据重复问题 end ****/
        list.push({
          [this.oneColPropField]: itemMapKeyValueArray[0], // 第一列数据
          [this.twoColPropField]: itemMapListItem[this.twoColDataField], // 第二列数据
          [itemMapListItem[this.countColPropsField]]: itemMapListItem[this.countColDataField] // 其他数量列数据
        })
      })
    })
    return list.map((item) => {
      return {
        ...item,
        // 计算小计数量
        xj: this.columnKeyList.reduce((a, b) => {
          return a + (item[b] || 0)
        }, 0)
      }
    })
  }
  // ...
},

问题解决图

在这里插入图片描述

【注】相关组件使用、数据源

相关组件使用

违法Table使用 CommonTable 组件

<template>
  <CommonTable
    :info="info"
    one-col-prop-field="timenum"
    one-col-label-field="时段"
    one-col-data-field="timenum"
    two-col-prop-field="cfzl"
    two-col-label-field="处罚种类"
    two-col-data-field="cfzlView"
  />
</template>

<script>
import CommonTable from './CommonTable'

export default {
  name: 'JdcIllegalTable',
  components: {
    CommonTable
  },

  props: {
    info: {
      type: Object,
      required: true
    }
  }
}
</script>

<style lang='scss' scoped></style>

违法Table使用 CommonTable 组件

<template>
  <CommonTable
    :info="info"
    one-col-prop-field="timenum"
    one-col-label-field="时段"
    one-col-data-field="timenum"
    two-col-prop-field="wfxwfl"
    two-col-label-field="违法行为分类"
    two-col-data-field="wfxlView"
  />
</template>

<script>
import CommonTable from './CommonTable'

export default {
  name: 'JdcPunishTable',
  components: {
    CommonTable
  },

  props: {
    info: {
      type: Object,
      required: true
    }
  }
}
</script>

<style lang='scss' scoped>
</style>

数据源

违法Table数据源

this.jdcIllegalInfo = {
  itemMap: {
    '17-18': [
      {
        timenum: 17,
        total: 61,
        cfzl: '1',
        cfzlView: '警告',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 17,
        total: 1,
        cfzl: '1',
        cfzlView: '警告',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ],
    '18-19': [
      {
        timenum: 18,
        total: 122,
        cfzl: '1',
        cfzlView: '处罚',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 18,
        total: 2,
        cfzl: '1',
        cfzlView: '处罚',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ],
    '19-20': [
      {
        timenum: 19,
        total: 50,
        cfzl: '1',
        cfzlView: '警告',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 19,
        total: 50,
        cfzl: '1',
        cfzlView: '处罚',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      },
      {
        timenum: 19,
        total: 25,
        cfzl: '1',
        cfzlView: '罚款',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ]
  },
  columns: [
    {
      timenum: 17,
      total: 61,
      cfzl: '1',
      cfzlView: '警告',
      jtfs: 'B11',
      jtfsView: '重型栏板半挂车'
    },
    {
      timenum: 17,
      total: 1,
      cfzl: '1',
      cfzlView: '警告',
      jtfs: 'B21',
      jtfsView: '中型栏板半挂车'
    }
  ]
}

处罚Table数据源

this.jdcPunishInfo = {
  itemMap: {
    '17-18': [
      {
        timenum: 17,
        total: 6,
        wfxl: '26',
        wfxlView: '违法停车',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 17,
        total: 21,
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 17,
        total: 3,
        wfxl: '17',
        wfxlView: '未低速通过',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 17,
        total: 28,
        wfxl: '12',
        wfxlView: '超速行驶',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 17,
        total: 3,
        wfxl: '49',
        wfxlView: '其他影响安全行为',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 17,
        total: 1,
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ],
    '18-19': [
      {
        timenum: 18,
        total: 6 * 2,
        wfxl: '26',
        wfxlView: '违法停车',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 18,
        total: 21 * 2,
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 18,
        total: 3 * 2,
        wfxl: '17',
        wfxlView: '未低速通过',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 18,
        total: 28 * 2,
        wfxl: '12',
        wfxlView: '超速行驶',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 18,
        total: 3 * 2,
        wfxl: '49',
        wfxlView: '其他影响安全行为',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 18,
        total: 1 * 2,
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ],
    '19-20': [
      {
        timenum: 19,
        total: 6 * 3,
        wfxl: '26',
        wfxlView: '违法停车',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 19,
        total: 21 * 3,
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 19,
        total: 3 * 3,
        wfxl: '17',
        wfxlView: '未低速通过',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 19,
        total: 28 * 3,
        wfxl: '12',
        wfxlView: '超速行驶',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 19,
        total: 3 * 3,
        wfxl: '49',
        wfxlView: '其他影响安全行为',
        jtfs: 'B11',
        jtfsView: '重型栏板半挂车'
      },
      {
        timenum: 19,
        total: 1 * 3,
        wfxl: '28',
        wfxlView: '违法装载',
        jtfs: 'B21',
        jtfsView: '中型栏板半挂车'
      }
    ]
  },
  columns: [
    {
      timenum: 17,
      total: 6,
      wfxl: '26',
      wfxlView: '违法停车',
      jtfs: 'B11',
      jtfsView: '重型栏板半挂车'
    },
    {
      timenum: 17,
      total: 1,
      wfxl: '28',
      wfxlView: '违法装载',
      jtfs: 'B21',
      jtfsView: '中型栏板半挂车'
    }
  ]
}

【注】计算小计插入位置等部分方法参考文章 https://blog.csdn.net/seeeeeeeeeee/article/details/133122424

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于 el-table 组件,可以通过设置表头的 column 对象的 span 属性来合并单元格。span 属性可以设置为一个对象,包含两个属性:row 和 col,分别表示纵向和横向的合并单元格数量。 以下是一个示例代码,演示了如何在 el-table实现多级表头的单元格合并: ```html <template> <el-table :data="tableData" style="width: 100%"> <el-table-column label="一级表头" prop="name" :span="{ row: 2, col: 1 }"></el-table-column> <el-table-column label="二级表头" :span="{ row: 1, col: 2 }"></el-table-column> <el-table-column label="三级表头" prop="age" :span="{ row: 1, col: 2 }"></el-table-column> <el-table-column label="四级表头" prop="address" :span="{ row: 1, col: 2 }"></el-table-column> <el-table-column label="姓名" prop="name"></el-table-column> <el-table-column label="年龄" prop="age"></el-table-column> <el-table-column label="地址" prop="address"></el-table-column> </el-table> </template> <script> export default { data() { return { tableData: [ { name: 'John', age: 20, address: 'New York' }, { name: 'Jane', age: 25, address: 'London' } ] }; } }; </script> ``` 在这个示例中,我们使用了 el-table 组件,并设置了四个表头,分别是"一级表头"、"二级表头"、"三级表头"和"四级表头"。通过设置每个表头的 span 属性,指定了单元格的合并方式。 注意,设置 span 属性的时候,row 和 col 的值分别代表纵向和横向的合并单元格数量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值