vue3表格的合并

<template>
    <div class="feedback-table-container">
        <el-table :data="processedTableData" border style="width: 100%"
            :header-cell-style="{ background: '#f0f9eb', color: '#606266' }" :span-method="handleSpan">
            <el-table-column prop="company" label="节点" width="150"></el-table-column>
            <el-table-column prop="feedbackStatus" label="反馈情况" width="120">
                <template #default="{ row }">
                    <el-tag v-if="row.feedbackStatus === '已反馈'" type="success">
                        {{ row.feedbackStatus }}
                    </el-tag>
                    <el-tag v-else type="warning">{{ row.feedbackStatus }}</el-tag>
                </template>
            </el-table-column>
            <el-table-column prop="reviewerName" label="办理人"></el-table-column>
            <el-table-column prop="taskName" label="操作"></el-table-column>
            <el-table-column prop="opinion" label="办理意见"></el-table-column>
            <el-table-column prop="dealDate" label="时间"></el-table-column>
        </el-table>
    </div>
</template>
<script setup>
import { computed, ref } from 'vue';

const props = defineProps({
    tableData: {
        type: Array,
        required: true,
        default: () => [],
    },
});

// const feedbackList = ref([
//     {
//         "company": "电能碳资产公司",
//         "feedbackStatus": "待反馈",
//         "dept": "电能碳资产公司",
//         "reviewerName": "李云龙",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "电能碳资产公司",
//         "feedbackStatus": "待反馈",
//         "dept": "电能碳资产公司",
//         "reviewerName": "陈萧如",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "湖北代表处调整",
//         "feedbackStatus": "待反馈",
//         "dept": "湖北代表处调整",
//         "reviewerName": "张如意",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "湖北代表处调整",
//         "feedbackStatus": "待反馈",
//         "dept": "湖北代表处调整",
//         "reviewerName": "李洋",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "湖北代表处调整",
//         "feedbackStatus": "待反馈",
//         "dept": "湖北代表处调整",
//         "reviewerName": "白秀欣",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "国家电投",
//         "feedbackStatus": "待反馈",
//         "dept": "国家电投",
//         "reviewerName": "白铭",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "国家电投",
//         "feedbackStatus": "待反馈",
//         "dept": "国家电投",
//         "reviewerName": "张赛",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     },
//     {
//         "company": "国家电投/集团公司总部/直管、授权管理中心",
//         "feedbackStatus": "待反馈",
//         "dept": "国家电投/创新投资",
//         "reviewerName": "姜洪福",
//         "taskName": "办理中",
//         "opinion": null,
//         "dealDate": "2025-09-18",
//         "level": "1"
//     }
// ])


// 预处理数据,计算合并信息(只基于 company 列)
const processedTableData = computed(() => {
    const data = [...props.tableData];
    // const data = [...feedbackList.value];
    const spanMap = {}; // 统一存储合并信息(company 和 feedbackStatus 共用)

    // 1. 计算 company 的合并规则(和原先逻辑一致)
    data.forEach((item, index) => {
        if (!spanMap[item.company]) {
            spanMap[item.company] = {
                nodeRowspan: 1,
                nodeColspan: 1,
                feedbackRowspan: 1, // 新增:让 feedbackStatus 复用 company 的 rowspan
            };
        } else {
            spanMap[item.company].nodeRowspan += 1;
            data[index].__skipNode = true; // 标记需要跳过的行
        }
    });

    // 2. 将 spanMap 信息添加到数据中(feedbackStatus 直接复用 company 的 rowspan)
    return data.map((item) => ({
        ...item,
        ...(spanMap[item.company] || {}),
    }));
});

// 处理行合并的方法(company 和 feedbackStatus 共用同一套 spanMap)
const handleSpan = ({ row, column, rowIndex, columnIndex }) => {
    if (columnIndex === 0 || columnIndex === 1) {
        // 节点列 或 反馈情况列 都复用同一套合并规则
        if (row.__skipNode) {
            return {
                rowspan: 0,
                colspan: 0,
            };
        } else {
            return {
                rowspan: row.nodeRowspan,
                colspan: row.nodeColspan,
            };
        }
    }
};
</script>

<style scoped>
.feedback-table-container {
    padding: 20px;
    background-color: #fff;
}
</style>
<template>
    <div class="feedback-table-container">
        <el-table :data="processedTableData" border style="width: 100%"
            :header-cell-style="{ background: '#f0f9eb', color: '#606266' }" :span-method="handleSpan">
            <el-table-column prop="collectionCompanyName" label="节点" show-overflow-tooltip></el-table-column>
            <el-table-column prop="feedbackStatus" label="反馈情况">
                <template #default="{ row }">
                    <el-tag v-if="row.feedbackStatus === '已反馈'" type="success">
                        {{ row.feedbackStatus }}
                    </el-tag>
                    <el-tag v-else type="warning">{{ row.feedbackStatus }}</el-tag>
                </template>
            </el-table-column>
            <el-table-column label="办理人" prop="reviewerName">
                <!-- <template #default="{ row }">
                    <div v-for="item in row.reviewerList" :key="item.dept">
                        {{ item.reviewerName }}
                    </div>
                </template> -->
            </el-table-column>
            <el-table-column prop="taskName" label="操作">
                <!-- <template #default="{ row }">
                    <div v-for="item in row.reviewerList" :key="item.dept">
                        {{ item.taskName }}
                    </div>
                </template> -->
            </el-table-column>
            <el-table-column prop="opinion" label="办理意见">
                <!-- <template #default="{ row }">
                    <div v-for="item in row.reviewerList" :key="item.dept">
                        {{ item.opinion }}
                    </div>
                </template> -->
            </el-table-column>
            <el-table-column prop="dealDate" label="时间">
                <!-- <template #default="{ row }">
                    <div v-for="item in row.reviewerList" :key="item.dept">
                        {{ item.dealDate }}
                    </div>
                </template> -->
            </el-table-column>
        </el-table>
    </div>
</template>
<script setup>
import { computed, ref } from 'vue';

const props = defineProps({
    tableData: {
        type: Array,
        required: true,
        default: () => [],
    },
});

const feedbackList = ref([
    {
        "collectionCompanyName": "电能碳资产公司",
        "feedbackStatus": "待反馈",
        "dept": "电能碳资产公司",
        "reviewerName": "李云龙",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "电能碳资产公司",
        "feedbackStatus": "待反馈",
        "dept": "电能碳资产公司",
        "reviewerName": "陈萧如",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "湖北代表处调整",
        "feedbackStatus": "待反馈",
        "dept": "湖北代表处调整",
        "reviewerName": "张如意",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "湖北代表处调整",
        "feedbackStatus": "待反馈",
        "dept": "湖北代表处调整",
        "reviewerName": "李洋",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "湖北代表处调整",
        "feedbackStatus": "待反馈",
        "dept": "湖北代表处调整",
        "reviewerName": "白秀欣",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "国家电投",
        "feedbackStatus": "待反馈",
        "dept": "国家电投",
        "reviewerName": "白铭",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "国家电投",
        "feedbackStatus": "待反馈",
        "dept": "国家电投",
        "reviewerName": "张赛",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    },
    {
        "collectionCompanyName": "国家电投/集团公司总部/直管、授权管理中心",
        "feedbackStatus": "待反馈",
        "dept": "国家电投/创新投资",
        "reviewerName": "姜洪福",
        "taskName": "办理中",
        "opinion": null,
        "dealDate": "2025-09-18",
        "level": "1"
    }
])

const multiSort = (arr) => {
    return arr.sort((a, b) => {
        const nameCompare = a.collectionCompanyName.localeCompare(b.collectionCompanyName);
        if (nameCompare !== 0) return nameCompare;
        return a.collectionDeptName.localeCompare(b.collectionDeptName);
    });
};
let datas = [
    {
        "id": "1968595427840143361",
        "opinionCollectId": "1968595425327755265",
        "collectionCompanyId": "1833789943865933825",
        "collectionCompanyName": "国家电投",
        "reviewerId": "1849382629964525570",
        "reviewerName": "梁超",
        "collectionDeptId": "1939714629174341727",
        "collectionDeptName": "国家电投/上海电力/上电河北/综合部/电投核能本部",
        "delFlag": "0",
        "feedbackStatus": "待反馈",
        "reviewerList": [
            {
                "company": "国家电投",
                "feedbackStatus": "待反馈",
                "dept": "国家电投/上海电力/上电河北/综合部/电投核能本部",
                "reviewerName": "梁超",
                "taskName": "办理中",
                "opinion": null,
                "dealDate": "2025-09-18",
                "level": "1"
            }, {
                "company": "国家电投",
                "feedbackStatus": "待反馈",
                "dept": "国家电投/上海电力/上电河北/综合部/电投核能本部",
                "reviewerName": "梁超111",
                "taskName": "办理中",
                "opinion": null,
                "dealDate": "2025-09-18",
                "level": "1"
            }
        ]
    },
    {
        "id": "1968597018844491778",
        "opinionCollectId": "1968595425327755265",
        "collectionCompanyId": "1833789943865933825",
        "collectionCompanyName": "国家电投",
        "reviewerId": "1849383532671021057",
        "reviewerName": "卫子辰",
        "collectionDeptId": "1849327916250341636",
        "collectionDeptName": "国家电投/电投数科/中电汇智",
        "delFlag": "0",
        "feedbackStatus": "待反馈",
        "reviewerList": [
            {
                "company": "国家电投",
                "feedbackStatus": "待反馈",
                "dept": "国家电投/电投数科/中电汇智",
                "reviewerName": "卫子辰",
                "taskName": "办理中",
                "opinion": null,
                "dealDate": "2025-09-18",
                "level": "1"
            }
        ]
    }
]
// 预处理数据,计算合并信息(只基于 company 列)
const processedTableData = computed(() => {
    const spanMap = {}; // 统一存储合并信息(company 和 feedbackStatus 共用)
    // 1. 计算 company 的合并规则(和原先逻辑一致)
    let arrs = []
    multiSort(datas).forEach(item => {
        item.reviewerList.forEach(v => {
            arrs.push({
                ...v,
                collectionCompanyId: item.collectionCompanyId,
                collectionCompanyName: item.collectionCompanyName,
                collectionDeptId: item.collectionDeptId,
                collectionDeptName: item.collectionDeptName,
                delFlag: item.delFlag,
                id: item.id,
                opinionCollectId: item.opinionCollectId,
                reviewerId: item.reviewerId
            })
        })
    })
    console.log(props.tableData, 8888, arrs);
    const data = [...arrs];
    data.forEach((item, index) => {
        if (!spanMap[item.collectionCompanyName]) {
            spanMap[item.collectionCompanyName] = {
                nodeRowspan: 1,
                nodeColspan: 1,
                feedbackRowspan: 1, // 新增:让 feedbackStatus 复用 collectionCompanyName 的 rowspan
            };
        } else {
            spanMap[item.collectionCompanyName].nodeRowspan += 1;
            data[index].__skipNode = true; // 标记需要跳过的行
        }
    });

    // 2. 将 spanMap 信息添加到数据中(feedbackStatus 直接复用 collectionCompanyName 的 rowspan)
    return data.map((item) => ({
        ...item,
        ...(spanMap[item.collectionCompanyName] || {}),
    }));
});

// 处理行合并的方法(collectionCompanyName 和 feedbackStatus 共用同一套 spanMap)
const handleSpan = ({ row, column, rowIndex, columnIndex }) => {
    if (columnIndex === 0 || columnIndex === 1) {
        // 节点列 或 反馈情况列 都复用同一套合并规则
        if (row.__skipNode) {
            return {
                rowspan: 0,
                colspan: 0,
            };
        } else {
            return {
                rowspan: row.nodeRowspan,
                colspan: row.nodeColspan,
            };
        }
    }
};
</script>

<style scoped>
.feedback-table-container {
    padding: 20px;
    background-color: #fff;
}
</style>

第一种方式使用feedbackList 数据类型

第二种使用datas数据类型进行转换后渲染

注意:数据需做排序,按照collectionCompanyName进行排序,避免渲染混乱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值