前言
日常开发过程,后台管理系统跟表格打交道必不可少,单行、单列的表格数据形式较为常见,今天笔者总结一下自己在实现多行,多列表格数据的过程
原型图
分析
区别于一般表格形式,要实现上面效果需要注意以下几点
-
- 单元格水平方向合并(账单日期,账单编号,账单类别,用户等)
-
- 单元格竖直方向合并(商品类别,商品名称,商品条码等)
-
- 点击“更多”按钮显示剩余的商品,默认最多显示 3 个
单元格合并需要用到colSpan和rowSpan属性,参考
然后需要根据返回商品数据包装结果,实现表格中的形式
实现
封装数据
接口返回的是订单记录,每条订单的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]"> </div>
<div class="w-[200px]"> </div>
<div class="w-[100px]"> </div>
<div class="w-[70px]"> </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> </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',
width: 100,
customCell: (record, rowIndex, column) => {
return {
rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
}
}
},
{
title: '商品类别',
dataIndex: 'goodsCategoryStr',
width: 100,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 8 : 1
}
}
},
{
title: '商品名称',
dataIndex: 'goodsName',
ellipsis: true,
width: 200,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
}
}
},
{
title: '商品条码',
dataIndex: 'goodsCode',
ellipsis: true,
width: 100,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
}
}
},
{
title: '单位',
dataIndex: 'goodsSpecStr',
width: 70,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
}
}
},
{
title: '数量',
dataIndex: 'goodsCount',
width: 70,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
}
}
},
{
title: '单价',
dataIndex: 'goodsUnitPrice',
width: 70,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
}
}
},
{
title: '应收/退',
dataIndex: 'goodsTotalPrice',
width: 100,
customCell: (record, rowIndex, column) => {
return {
colspan: tableList.value[rowIndex].id === record.id && record[column.dataIndex] === '-' ? 0 : 1
}
}
},
{
title: '实收/退',
dataIndex: 'payPrice',
width: 100,
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',
width: 80,
customCell: (record, rowIndex, column) => {
return {
rowSpan: rowIndex === 0 || tableList.value[rowIndex - 1].id !== record.id ? record.goodsListLength : 0
}
}
}
])