ant-design-vue 表格单元格合并

404 阅读3分钟

前言

日常开发过程,后台管理系统跟表格打交道必不可少,单行、单列的表格数据形式较为常见,今天笔者总结一下自己在实现多行,多列表格数据的过程

原型图

分析

区别于一般表格形式,要实现上面效果需要注意以下几点

    1. 单元格水平方向合并(账单日期,账单编号,账单类别,用户等)
    1. 单元格竖直方向合并(商品类别,商品名称,商品条码等)
    1. 点击“更多”按钮显示剩余的商品,默认最多显示 3 个

单元格合并需要用到colSpanrowSpan属性,参考

然后需要根据返回商品数据包装结果,实现表格中的形式

实现

封装数据

接口返回的是订单记录,每条订单的orderInfoDetailDtos包含商品信息,需要将每条商品信息提取出来,组成新的记录

    function dealTableData(record) {
        let tempRawData = JSON.parse(JSON.stringify(rawData.value))

        const tableData = []

        tempRawData.records.forEach((item) => {
      // 深拷贝单条记录
            let temp = JSON.parse(JSON.stringify(item))
            delete temp.orderInfoDetailDtos

            // 保存超出 3 条剩余商品信息
            let extraItem = []
            // 实际商品数量
            let realLength = item.orderInfoDetailDtos.length
            // 折叠状态,默认 true
            let isCollapse = true

      // 表格中存在 展开/折叠 记录,需要重新组织数据,列表查询时候也需要组织数据
            // 根据点击时候是否存在 record,判断更多/收起状态
      // 判断点击的订单
            if (record?.id && record.id === item.id) {
                // 如果折叠状态,点击展开更多
                if (record.isCollapse) {
                    extraItem = item.orderInfoDetailDtos.slice(3)
                    isCollapse = false
                } else {
                    isCollapse = true
          // 截取大于 3 部分数据,同时保证 item.orderInfoDetailDtos 的长度为 3,便于后面循环商品数据
                    extraItem = item.orderInfoDetailDtos.splice(3)
                }
            } else {
                // 如果长度大于 3,需要截取长度等于 3,多余部分放在 extraItem 数组中
                if (realLength > 3) {
                    extraItem = item.orderInfoDetailDtos.splice(3)
                }
            }

      // 遍历商品数据
            for (let i = 0; i < item.orderInfoDetailDtos.length; i++) {
                item.orderInfoDetailDtos[i].goodsId = item.orderInfoDetailDtos[i].id
                item.orderInfoDetailDtos[i].goodsTotalPrice = item.orderInfoDetailDtos[i].totalPrice

                delete item.orderInfoDetailDtos[i].id
                delete item.orderInfoDetailDtos[i].totalPrice

                tableData.push({
                    isCollapse: isCollapse,
                    extraItem: extraItem,
                    goodsListLength: item.orderInfoDetailDtos.length + 1,
                    ...temp,
                    ...item.orderInfoDetailDtos[i]
                })
            }

      // 当前同一订单商品记录最后位置插入统计记录
            tableData.push({
                goodsListLength: item.orderInfoDetailDtos.length + 1,
                ...temp,
                ...{
                    extraItem: extraItem,
                    isCollapse: isCollapse,
                    goodsCategoryStr'-',
                    goodsTotalPrice'-',
                    goodsName'-',
                    goodsCode'-',
                    goodsSpecStr'-',
                    goodsCount'-',
                    goodsUnitPrice'-',
                    payPrice'-'
                }
            })
        })
        tableList.value = tableData
    }

通过上面代码完成数据包装

表格设计

<a-table
  :columns="columns"
  :dataSource="tableList"
  bordered
  :tool-config="toolConfig"
  :pagination="false"
  :loading="tableLoading"
  :scroll="{ x: 'max-content', y: scrollYHeight }"
  >
  <template #bodyCell="{ column, record }">

    <template v-if="column.dataIndex === 'goodsCategoryStr'">
    <div v-if="record.goodsCategoryStr === '-'" class="flex">
    <div class="w-[100px]">&nbsp;</div>
    <div class="w-[200px]">&nbsp;</div>
    <div class="w-[100px]">&nbsp;</div>
    <div class="w-[70px]">&nbsp;</div>
    <div v-if="record.extraItem.length > 0" class="flex-auto underline text-[#0052d9]">
    <span class="cursor-pointer" @click="dealTableData(record)">{{
    record.isCollapse ? '更多' : '收起'
    }}</span>
  </div>
    <div v-else class="flex-auto"><span>&nbsp;</span></div>
    <div class="w-[70px] font-7">合计</div>
    <div class="w-[100px] font-7" :style="{ color: record.type == 1 ? '#000' : '#f5222d' }">
    {{ record.settlePrice }}
  </div>
    <div class="w-[70px] font-7" :style="{ color: record.type == 1 ? '#000' : '#f5222d' }">
    {{ record.realPayPrice }}
  </div>
  </div>
    <div v-else>{{ record.goodsCategoryStr }}</div>
  </template>
  </template>
</a-table>

表头部分判断单元格合并条件

rowSpan:每页第一条取值 record.goodsListLength,其余通过判断当前 record.id 和 上一条记录 id 判断,如果不相等说明是新的订单,则设置值为 record.goodsListLength

colSpan:需要注意在商品类别(goodsCategoryStr)列,且值为“-”时,设置值为 8,此时为合计列,其他列需要设置为 0。不满足上面条件的时候,设置为 1

const columns = ref([
{
title'账单日期',
dataIndex'createTime',

customCell(record, rowIndex, column) => {
  return {
    rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
  }
}
  },
  {
    title'账单编号',
      dataIndex'orderNo',

      customCell(record, rowIndex, column) => {
      return {
        rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
      }
    }
  },
  {
    title'账单类别',
      dataIndex'type',
      width100,
      customCell(record, rowIndex, column) => {
      return {
        rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
      }
    }
  },
  {
    title'商品类别',
      dataIndex'goodsCategoryStr',
      width100,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 8 : 1
      }
    }
  },
  {
    title'商品名称',
      dataIndex'goodsName',
      ellipsistrue,
      width200,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'商品条码',
      dataIndex'goodsCode',
      ellipsistrue,
      width100,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'单位',
      dataIndex'goodsSpecStr',
      width70,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'数量',
      dataIndex'goodsCount',
      width70,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'单价',
      dataIndex'goodsUnitPrice',
      width70,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'应收/退',
      dataIndex'goodsTotalPrice',
      width100,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'实收/退',
      dataIndex'payPrice',
      width100,
      customCell(record, rowIndex, column) => {
      return {
        colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
      }
    }
  },
  {
    title'用户',
      dataIndex'customerName',

      customCell(record, rowIndex, column) => {
      return {
        rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
      }
    }
  },
  {
    title'卡号',
      dataIndex'customerAccount',

      customCell(record, rowIndex, column) => {
      return {
        rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
      }
    }
  },
  {
    title'结算方式',
      dataIndex'payType',

      customCell(record, rowIndex, column) => {
      return {
        rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
      }
    }
  },
  {
    title'收银员',
      dataIndex'cashier',
      fixed'right',
      width80,
      customCell(record, rowIndex, column) => {
      return {
        rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
      }
    }
  }
  ])

实现效果