<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进行排序,避免渲染混乱