|
|
@@ -1,84 +1,92 @@
|
|
|
<template>
|
|
|
<div class="overall-report">
|
|
|
- <!-- 完成提示 -->
|
|
|
- <div class="completion-header">
|
|
|
- <div class="completion-emoji">🎉</div>
|
|
|
- <h2 class="completion-title">对话完成!</h2>
|
|
|
- <p class="completion-subtitle">
|
|
|
- 与 {{ role?.name || 'AI' }} 对话 {{ evaluation.statistics.totalRounds }} 轮 · {{ formatDuration(evaluation.statistics.totalDuration) }}
|
|
|
- </p>
|
|
|
- </div>
|
|
|
+ <template v-if="evaluation">
|
|
|
+ <!-- 完成提示 -->
|
|
|
+ <div class="completion-header">
|
|
|
+ <div class="completion-emoji">🎉</div>
|
|
|
+ <h2 class="completion-title">对话完成!</h2>
|
|
|
+ <p class="completion-subtitle">
|
|
|
+ 与 {{ role?.name || 'AI' }} 对话 {{ evaluation.statistics.totalRounds }} 轮 · {{ formatDuration(evaluation.statistics.totalDuration) }}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 综合评分区域 -->
|
|
|
- <div v-if="scoreDisplayMode !== 'comment_only'" class="score-section">
|
|
|
- <div class="score-header">
|
|
|
- <div>
|
|
|
- <div class="score-label">综合评分</div>
|
|
|
- <!-- 数字模式 -->
|
|
|
- <div v-if="scoreDisplayMode === 'numeric'" class="score-display">
|
|
|
- <span class="score-number">{{ evaluation.overallScore }}</span>
|
|
|
- <span class="score-unit">分</span>
|
|
|
- </div>
|
|
|
- <!-- 字母模式 -->
|
|
|
- <div v-else class="score-display">
|
|
|
- <span class="score-letter" :style="{ color: letterColorMap[scoreToLetter(evaluation.overallScore)] }">
|
|
|
- {{ scoreToLetter(evaluation.overallScore) }}
|
|
|
- </span>
|
|
|
- <span class="score-unit">级</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="score-meta">
|
|
|
- <div class="level-badge" :style="{ backgroundColor: levelConfig.bgColor, color: levelConfig.color }">
|
|
|
- {{ levelConfig.label }}
|
|
|
+ <!-- 综合评分区域 -->
|
|
|
+ <div v-if="scoreDisplayMode !== 'comment_only'" class="score-section">
|
|
|
+ <div class="score-header">
|
|
|
+ <div>
|
|
|
+ <div class="score-label">综合评分</div>
|
|
|
+ <!-- 数字模式 -->
|
|
|
+ <div v-if="scoreDisplayMode === 'numeric'" class="score-display">
|
|
|
+ <span class="score-number">{{ evaluation.overallScore }}</span>
|
|
|
+ <span class="score-unit">分</span>
|
|
|
+ </div>
|
|
|
+ <!-- 字母模式 -->
|
|
|
+ <div v-else class="score-display">
|
|
|
+ <span class="score-letter" :style="{ color: letterColorMap[scoreToLetter(evaluation.overallScore)] }">
|
|
|
+ {{ scoreToLetter(evaluation.overallScore) }}
|
|
|
+ </span>
|
|
|
+ <span class="score-unit">级</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="percentile-text">
|
|
|
- 超过 <span class="percentile-value">{{ evaluation.percentile }}%</span> 同学
|
|
|
+ <div class="score-meta">
|
|
|
+ <div class="level-badge" :style="{ backgroundColor: levelConfig.bgColor, color: levelConfig.color }">
|
|
|
+ {{ levelConfig.label }}
|
|
|
+ </div>
|
|
|
+ <div class="percentile-text">
|
|
|
+ 超过 <span class="percentile-value">{{ evaluation.percentile }}%</span> 同学
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 四维能力分析 -->
|
|
|
- <div class="ability-section">
|
|
|
- <div class="ability-label">能力分析</div>
|
|
|
- <div class="ability-bars">
|
|
|
- <div v-for="dim in visibleDimensions" :key="dim.key" class="ability-row">
|
|
|
- <span class="ability-name">{{ dim.label }}</span>
|
|
|
- <div class="ability-track">
|
|
|
- <div class="ability-fill" :style="{ width: `${getDimValue(dim.key)}%` }" />
|
|
|
+ <!-- 四维能力分析 -->
|
|
|
+ <div class="ability-section">
|
|
|
+ <div class="ability-label">能力分析</div>
|
|
|
+ <div class="ability-bars">
|
|
|
+ <div v-for="dim in visibleDimensions" :key="dim.key" class="ability-row">
|
|
|
+ <span class="ability-name">{{ dim.label }}</span>
|
|
|
+ <div class="ability-track">
|
|
|
+ <div class="ability-fill" :style="{ width: `${getDimValue(dim.key)}%` }" />
|
|
|
+ </div>
|
|
|
+ <span class="ability-value">
|
|
|
+ {{ scoreDisplayMode === 'letter' ? scoreToLetter(getDimValue(dim.key)) : getDimValue(dim.key) }}
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- <span class="ability-value">
|
|
|
- {{ scoreDisplayMode === 'letter' ? scoreToLetter(getDimValue(dim.key)) : getDimValue(dim.key) }}
|
|
|
- </span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- AI 点评 -->
|
|
|
- <div class="ai-comment-section">
|
|
|
- <div class="ai-comment-inner">
|
|
|
- <div class="ai-avatar-circle">{{ role?.avatar || '👦' }}</div>
|
|
|
- <div class="ai-comment-body">
|
|
|
- <div class="ai-comment-name">{{ role?.name || 'AI' }} 说</div>
|
|
|
- <p class="ai-comment-text">{{ evaluation.aiComment }}</p>
|
|
|
+ <!-- AI 点评 -->
|
|
|
+ <div class="ai-comment-section">
|
|
|
+ <div class="ai-comment-inner">
|
|
|
+ <div class="ai-avatar-circle">{{ role?.avatar || '👦' }}</div>
|
|
|
+ <div class="ai-comment-body">
|
|
|
+ <div class="ai-comment-name">{{ role?.name || 'AI' }} 说</div>
|
|
|
+ <p class="ai-comment-text">{{ evaluation.aiComment }}</p>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 亮点与建议 -->
|
|
|
- <div class="highlights-grid">
|
|
|
- <div class="highlight-card">
|
|
|
- <div class="highlight-header"><span class="highlight-icon-good">✓</span> 亮点</div>
|
|
|
- <div class="highlight-list">
|
|
|
- <div v-for="(h, i) in evaluation.highlights.slice(0, 3)" :key="i" class="highlight-item">• {{ h }}</div>
|
|
|
+ <!-- 亮点与建议 -->
|
|
|
+ <div class="highlights-grid">
|
|
|
+ <div class="highlight-card">
|
|
|
+ <div class="highlight-header"><span class="highlight-icon-good">✓</span> 亮点</div>
|
|
|
+ <div class="highlight-list">
|
|
|
+ <div v-for="(h, i) in evaluation.highlights.slice(0, 3)" :key="i" class="highlight-item">• {{ h }}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="highlight-card">
|
|
|
- <div class="highlight-header"><span class="highlight-icon-improve">→</span> 建议</div>
|
|
|
- <div class="highlight-list">
|
|
|
- <div v-for="(h, i) in evaluation.improvements.slice(0, 3)" :key="i" class="highlight-item">• {{ h }}</div>
|
|
|
+ <div class="highlight-card">
|
|
|
+ <div class="highlight-header"><span class="highlight-icon-improve">→</span> 建议</div>
|
|
|
+ <div class="highlight-list">
|
|
|
+ <div v-for="(h, i) in evaluation.improvements.slice(0, 3)" :key="i" class="highlight-item">• {{ h }}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 无评分时的占位提示 -->
|
|
|
+ <div v-else class="completion-header">
|
|
|
+ <div class="completion-emoji">🎉</div>
|
|
|
+ <h2 class="completion-title">对话完成!</h2>
|
|
|
</div>
|
|
|
|
|
|
<!-- 统计数据 -->
|
|
|
@@ -132,7 +140,7 @@ import { computed } from 'vue'
|
|
|
import type { OverallEvaluation, ScoreLevel, ScoreDisplayMode, PreviewAIRole } from '@/types/englishSpeaking'
|
|
|
|
|
|
interface Props {
|
|
|
- evaluation: OverallEvaluation
|
|
|
+ evaluation: OverallEvaluation | null
|
|
|
role: PreviewAIRole | null
|
|
|
scoreDisplayMode?: ScoreDisplayMode
|
|
|
}
|
|
|
@@ -164,7 +172,7 @@ const dimensionConfig = [
|
|
|
{ label: '流畅度', key: 'fluency' },
|
|
|
]
|
|
|
|
|
|
-const levelConfig = computed(() => scoreLevelConfig[props.evaluation.scoreLevel])
|
|
|
+const levelConfig = computed(() => scoreLevelConfig[props.evaluation?.scoreLevel ?? 'good'])
|
|
|
const visibleDimensions = computed(() => dimensionConfig)
|
|
|
|
|
|
function scoreToLetter(score: number): string {
|
|
|
@@ -182,7 +190,8 @@ function formatDuration(seconds: number) {
|
|
|
}
|
|
|
|
|
|
function getDimValue(key: string): number {
|
|
|
- return (props.evaluation.dimensions as Record<string, number>)[key] || 0
|
|
|
+ const dims = props.evaluation?.dimensions as Record<string, number> | undefined
|
|
|
+ return dims?.[key] || 0
|
|
|
}
|
|
|
</script>
|
|
|
|