合并单元格原理:
rowSpan 属性:通过设置 rowSpan 控制单元格合并的行数
隐藏单元格:非首行单元格设置 rowSpan: 0 实现隐藏效果
分组计算:基于主分组字段计算合并范围
<template>
<a-table
:data-source="dataSource"
:columns="columns"
>
</a-table>
</template>
<script setup lang="ts">
const dataSource = [
{ id: '001', createTime: '2025-01-01', content: '内容A' },
{ id: '001', createTime: '2025-01-01', content: '内容B' },
{ id: '001', createTime: '2025-01-02', content: '内容C' },
{ id: '002', createTime: '2025-01-02', content: '内容D' }
]
const columns = [
{
title: '序号', key: 'index', dataIndex: 'index',
customRender: ({ index }) => { return filterIndex.value[index] || '' },
customCell: (_, index) => { return { rowSpan: filterRowSpan('id', index) }}
},
{
title: '日期', key: 'createTime', dataIndex: 'createTime',
customCell: (_, index) => { return { rowSpan: filterRowSpan('createTime', index) }}
},
{
title: '内容', key: 'content', dataIndex: 'content'
},
]
/**
* 动态计算序号
* 只在每个分组的第一行显示序号,非首行显示空字符串
*/
const filterIndex = computed(() => {
const indexes = []
let count = 1
dataSource.value.forEach((record, index) => {
const previous = dataSource.value[index - 1]
// 如果当前行是合并组的第一行,则递增序号
if (!previous || record['id'] !== previous['id']) {
indexes.push(count++)
} else {
indexes.push(null) // 非首行不显示序号
}
})
return indexes
})
/**
* 合并单元格计算函数
* @param key 要合并的字段名
* @param index 当前行索引
* @param groupKey 分组字段名(默认为 id)
* @returns 合并的行数(0表示隐藏)
*/
const filterRowSpan = (key: string, index: number, groupKey: string = 'id') => {
const data = dataSource.value
const current = data[index]
const previous = data[index - 1]
// 如果是第一行或者值与上一行不同(并且不属于同一组),则显示该组的行数
if (!previous || current[key] !== previous[key] || current[groupKey] !== previous[idgroupKey) {
return data.filter(
(item) => item[key] === current[key] && item[groupKey] === current[groupKey]
).length
}
// 否则设置为 0(隐藏当前单元格)
return 0
}
</script>