<template>
<div class="results-page page-container">
<h1 class="section-title">面试评估结果</h1>
<div v-if="loading" class="loading-container">
<el-skeleton :rows="10" animated />
</div>
<div v-else-if="!results" class="no-results card">
<el-empty description="未找到面试结果" />
<el-button type="primary" @click="$router.push('/setup')" class="action-button">开始新的面试</el-button>
</div>
<div v-else class="results-content">
<!-- 能力雷达图 -->
<div class="card radar-section">
<h2>能力评估</h2>
<div class="radar-container">
<canvas ref="radarCanvas" width="400" height="400"></canvas>
</div>
<div class="ability-legend">
<div class="legend-item" v-for="(ability, index) in abilities" :key="index">
<span class="legend-color" :style="{backgroundColor: ability.color}"></span>
<span class="legend-label">{{ ability.name }}</span>
<span class="legend-score">{{ ability.score }}/100</span>
</div>
</div>
</div>
<!-- 优势与不足 -->
<div class="card strengths-weaknesses">
<el-row :gutter="20">
<el-col :span="12">
<h2>优势</h2>
<ul class="strengths-list">
<li v-for="(strength, index) in results.strengths" :key="index">
<i class="el-icon-check"></i> {{ strength }}
</li>
</ul>
</el-col>
<el-col :span="12">
<h2>需要改进</h2>
<ul class="improvements-list">
<li v-for="(improvement, index) in results.improvements" :key="index">
<i class="el-icon-warning"></i> {{ improvement }}
</li>
</ul>
</el-col>
</el-row>
</div>
<!-- 问题详情 -->
<div class="card questions-section">
<h2>问题详情</h2>
<el-collapse accordion>
<el-collapse-item
v-for="(item, index) in results.questionScores"
:key="index"
:title="`问题 ${index + 1}: ${item.question}`">
<div class="question-detail">
<div class="question-content">
<h4>你的回答:</h4>
<p class="answer-text">{{ item.answer }}</p>
<h4>反馈:</h4>
<p class="feedback-text">{{ item.feedback }}</p>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
<!-- 改进建议 -->
<div class="card recommendations">
<h2>改进建议</h2>
<div class="recommendation-card">
<p>{{ results.recommendations }}</p>
</div>
</div>
<div class="actions">
<el-button type="primary" @click="$router.push('/setup')" class="action-button">开始新的面试</el-button>
<el-button @click="$router.push('/profile')" class="action-button">返回个人中心</el-button>
<!-- 删除下面这行 -->
<!-- <el-button type="success" @click="downloadReport" class="action-button">下载报告</el-button> -->
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { interviewAPI } from '../services/api';
const route = useRoute();
const router = useRouter();
const sessionId = route.params.sessionId;
const results = ref(null);
const loading = ref(true);
const radarCanvas = ref(null);
// 五个能力维度
const abilities = ref([
{ name: '专业知识水平', score: 0, color: '#409EFF' },
{ name: '技能匹配度', score: 0, color: '#67C23A' },
{ name: '语言表达能力', score: 0, color: '#E6A23C' },
{ name: '逻辑思维能力', score: 0, color: '#F56C6C' },
{ name: '创新性', score: 0, color: '#909399' }
]);
onMounted(async () => {
try {
const response = await interviewAPI.getInterviewResults(sessionId);
results.value = response.data;
// 根据现有数据计算五个维度的分数
calculateAbilityScores();
console.log('计算后的能力数据:', abilities.value);
// 等待DOM更新后绘制雷达图
await nextTick();
// 延迟一点确保Canvas完全渲染
setTimeout(() => {
drawRadarChart();
}, 100);
} catch (error) {
console.error('获取面试结果失败:', error);
ElMessage.error('获取面试结果失败');
} finally {
loading.value = false;
}
});
// 计算五个维度的分数
const calculateAbilityScores = () => {
if (!results.value) return;
// 基于现有的评分数据计算各维度分数
// 这里可以根据实际的评估逻辑进行调整
abilities.value[0].score = Math.round(results.value.contentScore || 75); // 专业知识水平
abilities.value[1].score = 0; // 技能匹配度 - 修改为0分
abilities.value[2].score = 20; // 语言表达能力 - 修改为20分
abilities.value[3].score = Math.round(results.value.contentScore * 0.8 || 72); // 逻辑思维能力
abilities.value[4].score = Math.round(results.value.overallScore * 0.7 || 68); // 创新性
};
// 绘制雷达图
// 绘制雷达图
const drawRadarChart = () => {
const canvas = radarCanvas.value;
if (!canvas) {
console.log('Canvas元素未找到');
return;
}
const ctx = canvas.getContext('2d');
if (!ctx) {
console.log('无法获取Canvas上下文');
return;
}
// 设置Canvas实际尺寸
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const radius = Math.min(rect.width, rect.height) / 2 - 60; // 留出标签空间
const levels = 5;
console.log('开始绘制雷达图', { centerX, centerY, radius, abilities: abilities.value });
// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制背景网格 - 正五边形
ctx.strokeStyle = '#e0e0e0';
ctx.lineWidth = 1;
const angleStep = (2 * Math.PI) / 5; // 固定为5边形
// 绘制同心正五边形
for (let level = 1; level <= levels; level++) {
ctx.beginPath();
const levelRadius = (radius / levels) * level;
for (let i = 0; i < 5; i++) {
const angle = i * angleStep - Math.PI / 2; // 从顶部开始
const x = centerX + Math.cos(angle) * levelRadius;
const y = centerY + Math.sin(angle) * levelRadius;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.closePath();
ctx.stroke();
}
// 绘制轴线和标签
ctx.strokeStyle = '#e0e0e0';
for (let i = 0; i < 5; i++) {
const angle = i * angleStep - Math.PI / 2;
const x = centerX + Math.cos(angle) * radius;
const y = centerY + Math.sin(angle) * radius;
// 绘制轴线
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(x, y);
ctx.stroke();
// 绘制标签
ctx.fillStyle = '#333';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const labelDistance = radius + 30;
const labelX = centerX + Math.cos(angle) * labelDistance;
const labelY = centerY + Math.sin(angle) * labelDistance;
const abilityName = abilities.value[i]?.name || `维度${i+1}`;
ctx.fillText(abilityName, labelX, labelY);
}
// 绘制数据多边形
if (abilities.value && abilities.value.length >= 5) {
// 先绘制数据点
const dataPoints = [];
for (let i = 0; i < 5; i++) {
const angle = i * angleStep - Math.PI / 2;
const score = abilities.value[i]?.score || 0;
const distance = (score / 100) * radius;
const x = centerX + Math.cos(angle) * distance;
const y = centerY + Math.sin(angle) * distance;
dataPoints.push({ x, y });
// 绘制数据点
ctx.save();
ctx.fillStyle = abilities.value[i]?.color || '#409EFF';
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI); // 稍微增大数据点
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke(); // 给数据点加白色边框
ctx.restore();
}
// 绘制连接线形成的多边形 - 使用不同颜色
ctx.beginPath();
ctx.fillStyle = 'rgba(255, 99, 132, 0.15)'; // 粉红色半透明填充
ctx.strokeStyle = '#FF6384'; // 粉红色边框
ctx.lineWidth = 3; // 加粗连接线
// 连接所有数据点
for (let i = 0; i < dataPoints.length; i++) {
if (i === 0) {
ctx.moveTo(dataPoints[i].x, dataPoints[i].y);
} else {
ctx.lineTo(dataPoints[i].x, dataPoints[i].y);
}
}
ctx.closePath();
ctx.fill(); // 填充多边形
ctx.stroke(); // 绘制边框
// 可选:添加渐变效果
const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
gradient.addColorStop(0, 'rgba(255, 99, 132, 0.3)');
gradient.addColorStop(1, 'rgba(255, 99, 132, 0.05)');
ctx.beginPath();
for (let i = 0; i < dataPoints.length; i++) {
if (i === 0) {
ctx.moveTo(dataPoints[i].x, dataPoints[i].y);
} else {
ctx.lineTo(dataPoints[i].x, dataPoints[i].y);
}
}
ctx.closePath();
ctx.fillStyle = gradient;
ctx.fill();
}
console.log('雷达图绘制完成');
};
// 删除下面的函数
// const downloadReport = () => {
// ElMessage.success('报告下载功能将在后续版本中提供');
// };
</script>
<style scoped>
.results-page {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.loading-container {
padding: 30px;
background: white;
border-radius: 8px;
box-shadow: var(--card-shadow);
}
.radar-section {
margin-bottom: 30px;
text-align: center;
}
.radar-container {
display: flex;
justify-content: center;
margin: 20px 0;
}
.ability-legend {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 50%;
}
.legend-label {
font-size: 14px;
color: #333;
}
.legend-score {
font-size: 14px;
font-weight: bold;
color: #666;
}
.strengths-weaknesses, .questions-section, .recommendations {
margin-bottom: 30px;
}
.strengths-list li, .improvements-list li {
margin-bottom: 10px;
padding: 8px;
border-radius: 4px;
}
.strengths-list li {
background-color: rgba(103, 194, 58, 0.1);
color: #67C23A;
}
.improvements-list li {
background-color: rgba(230, 162, 60, 0.1);
color: #E6A23C;
}
.question-detail {
display: flex;
gap: 20px;
}
.question-content {
flex: 1;
}
.answer-text, .feedback-text {
background-color: #f5f7fa;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
white-space: pre-wrap;
}
.recommendation-card {
background-color: #f5f7fa;
border-radius: 8px;
padding: 20px;
line-height: 1.8;
white-space: pre-wrap;
}
.actions {
margin-top: 30px;
display: flex;
gap: 15px;
justify-content: center;
}
.action-button {
min-width: 120px;
}
.no-results {
padding: 40px;
text-align: center;
}
</style>