相信大家在做业务需求的过程中都遇到过表格单元格合并的问题吧,今天我们来简单讲讲antDesign 配合 vue 来实现单元格合并
1. 首先我们来看一下官方给出的示例
antDesign 文档链接 (https://2x.antdv.com/docs/vue/introduce-cn)
我们来看下官方的这个例子
例子是在需要合并的列上增加了一个 customRender 函数,index 代表就是哪一行。rowSpan 是几就代表向下合并几列。显然这种固定的合并模式
是无法满足于我们的业务需求的。我们实际业务场景数据都是动态,可能需要根据某一个id 是一样的来进行动态的合并。比如 产品编码,订单id,
活动id. 我们就需要一个方法来算一下需要合并的列
const columns = [
{
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
customRender: ({ text, index }) => {
const obj = {
children: text,
props: {},
};
if (index === 2) {
obj.props.rowSpan = 2;
}
if (index === 3) {
obj.props.rowSpan = 0;
}
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
},
]
定义一个计算合并列的方法
这个函数总共接口两个参数,一个是需要渲染的数据,第二个参数是合并的唯一值。(比如,要根据订单号一样的进行合并,就需要把订单号orderId 传进来)
export function getSpanArr(tableData, itemProperty) {
if (tableData.length === 0) {
return false;
}
const spanArr = [];
let pos;
for (let i = 0; i < tableData.length; i++) {
if (i === 0) {
spanArr.push(1);
pos = 0;
} else {
if (tableData[i][itemProperty] && tableData[i][itemProperty] === tableData[i - 1][itemProperty]) {
spanArr[pos] += 1;
spanArr.push(0);
} else {
spanArr.push(1);
pos = i;
}
}
}
return {
spanArr: spanArr,
};
}
在页面中调用
import {getSpanArr } from '/@/utils/tools';
const dataList =ref([])
const dataObj = ref({})
const dataObj = getSpanArr(dataList, 'orderId');
spanObj.value = dataObj;
const renderContent = ({ text, index }: any) => {
const obj = {
children: text,
props: {} as any,
};
const spanArr = spanObj.value.spanArr;
const _row = spanArr[index];
obj.props.rowSpan = _row;
return obj;
};
const columns = [
{
title: '商品信息',
dataIndex: 'productName',
key: 'productName',
customRender: renderContent,
},
{
title: '产品型号',
dataIndex: 'productModel',
key: 'productModel',
},
{
title: ' 收货信息',
dataIndex: 'address',
customRender: renderContent,
},
];
这样的弊端
如果表格前面有 checkbox, checkbox 不能合并。需要按如下方法进行合并。如此一来就大功告成了。
<div id="tableContent">
<a-table
:scroll="{ y: 500 }"
bordered
:pagination="false"
:rowKey="
(record) => {
return record.orderId;
}
"
:row-selection="{ onSelect: onSelect, onSelectAll: onSelectAll, selectedRowKeys: selectedRowKeysList }"
:columns="columns"
:data-source="startLeaseDataList"
>
a-table>
div>
nextTick(() => {
if (dataList.length <= 0) {
return false;
}
var tableContent = document.getElementById('tableContent');
const tableBody = tableContent?.getElementsByClassName('ant-table-tbody');
const tr = tableBody[0].getElementsByTagName('tr');
const cellMegetList = spanObj.value.spanArr;
const trList = Array.from(tr);
const newTrList = trList.filter((item, index) => {
return index > 0;
})
newTrList.forEach((item, index) => {
if (cellMegetList[index] === 0) {
item.childNodes[1].setAttribute('style', 'display: none');
} else {
item.childNodes[1].setAttribute('rowspan', cellMegetList[index]);
}
});
});
demo 如下,可以复制到项目中自己调试一下 (如有别的问题请留言或私信作者)
<template>
<div>
<div class="dialog-content">
<a-spin :spinning="spinning">
<div id="tableContent">
<a-table
:scroll="{ y: 500 }"
bordered
:pagination="false"
:rowKey="
(record) => {
return record.subOrderDetailId;
}
"
:row-selection="{ onSelect }"
:columns="columns"
:data-source="startLeaseDataList"
/>
</div>
</a-spin>
</div>
</div>
</template>
<script name="shopCardSource" lang="ts" setup>
import { ref, nextTick } from 'vue';
import { getSpanArr } from '/@/utils/tools';
const spinning = ref(false);
const spanObj = ref({});
const renderContent = ({ text, index }: any) => {
const obj = {
children: text,
props: {} as any,
};
const spanArr = spanObj.value.spanArr;
const _row = spanArr[index];
obj.props.rowSpan = _row;
return obj;
};
const columns = [
{
title: '第一列',
dataIndex: 'productName',
key: 'productName',
width: '200px',
customRender: renderContent,
},
{
title: '第二列',
dataIndex: 'productModel',
key: 'productModel',
},
{
title: '第三列',
dataIndex: 'serialNumber',
key: 'serialNumber',
},
{
title: ' 第四列',
dataIndex: 'assetCode',
key: 'assetCode',
},
{
title: '第五列',
dataIndex: 'address',
key: 'address',
customRender: renderContent,
},
];
const startLeaseDataList = ref([
{
mainOrderId: '20230903073209292508',
subOrderId: '16936975294936948',
subOrderDetailId: '1',
productName: '【单品】苹果显示器(1080*900/36寸)',
address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺129号',
productModel: 'zh20230730001',
serialNumber: 'runaceshiB4',
assetCode: 'MS-XS-857042',
},
{
mainOrderId: '20230903073209292508',
subOrderId: '16936975294936948',
subOrderDetailId: '1',
productName: '【单品】苹果显示器(1080*900/36寸)',
address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺12',
productModel: 'zh20230730001',
serialNumber: 'runaceshiB4',
assetCode: 'MS-XS-857042',
},
{
mainOrderId: '20230903073209292508',
subOrderId: '16936975294936948',
subOrderDetailId: '1',
productName: '【单品】苹果显示器(1080*900/36寸)',
address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
productModel: 'zh20230730001',
serialNumber: 'runaceshiB4',
assetCode: 'MS-XS-857042',
},
{
mainOrderId: '20230903073209292508',
subOrderId: '16936975294936942',
subOrderDetailId: '3',
productName: '【单品】苹果显示器(1080*900/36寸)',
address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
productModel: 'zh20230730001',
serialNumber: 'runaceshiB4',
assetCode: 'MS-XS-857042',
},
{
mainOrderId: '20230903073209292508',
subOrderId: '16936975294936942',
subOrderDetailId: '4',
productName: '【单品】苹果显示器(1080*900/36寸)',
address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
productModel: 'zh20230730001',
serialNumber: 'runaceshiB4',
assetCode: 'MS-XS-857042',
},
{
mainOrderId: '20230903073209292508',
subOrderId: '16936975294936941',
subOrderDetailId: '4',
productName: '【单品】苹果显示器(1080*900/36寸)',
address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
productModel: 'zh20230730001',
serialNumber: 'runaceshiB4',
assetCode: 'MS-XS-857042',
},
]);
const dataObj = getSpanArr(startLeaseDataList.value, 'subOrderDetailId');
spanObj.value = dataObj;
nextTick(() => {
if (startLeaseDataList.value.length <= 0) {
return false;
}
var tableContent = document.getElementById('tableContent');
const tableBody = tableContent?.getElementsByClassName('ant-table-tbody');
const tr = tableBody[0].getElementsByTagName('tr');
const cellMegetList = dataObj.spanArr;
const trList = Array.from(tr);
const newTrList = trList.filter((item, index) => {
return index > 0;
});
newTrList.forEach((item, index) => {
if (cellMegetList[index] === 0) {
item.childNodes[1].setAttribute('style', 'display: none');
} else {
item.childNodes[1].setAttribute('rowspan', cellMegetList[index]);
}
});
});
</script>
<style scoped lang="less"></style>