Просмотр исходного кода

feat(speaking): add accuracy-based warning tier + word spacing in pronunciation display

- error_type=None now layered by accuracy_score: <60 red, 60-79 orange, >=80 green
- Words wrapped as inline-block with 8px margin-right so underlines don't run together

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jimmylee 1 месяц назад
Родитель
Сommit
eb9fe306b6

+ 21 - 8
src/views/Editor/EnglishSpeaking/preview/WordPronunciationDisplay.vue

@@ -4,25 +4,35 @@
       v-for="(w, i) in words"
       :key="i"
       class="wpd-word"
-      :class="`wpd-${classFor(w.errorType)}`"
+      :class="`wpd-${classFor(w)}`"
       :title="`${w.word} · ${w.errorType} · ${Math.round(w.accuracyScore)}`"
-    >{{ w.word }}<span v-if="i < words.length - 1"> </span></span>
+    >{{ w.word }}</span>
   </div>
 </template>
 
 <script lang="ts" setup>
-import type { WordAnalysisItem, WordErrorType } from '@/types/englishSpeaking'
+import type { WordAnalysisItem } from '@/types/englishSpeaking'
 
 interface Props {
   words: WordAnalysisItem[]
 }
 defineProps<Props>()
 
-function classFor(t: WordErrorType): 'correct' | 'wrong' | 'missed' | 'ignored' {
-  if (t === 'None') return 'correct'
-  if (t === 'Mispronunciation' || t === 'Insertion') return 'wrong'
-  if (t === 'Omission') return 'missed'
-  return 'ignored'
+// 判定规则:
+//  - Omission        → missed(灰)
+//  - Mispronunciation / Insertion → wrong(红)
+//  - error_type=None 时按 accuracy_score 分层:
+//      < 60   → wrong(红)
+//      60-79  → warning(橙)
+//      ≥ 80   → correct(绿)
+function classFor(w: WordAnalysisItem): 'correct' | 'warning' | 'wrong' | 'missed' | 'ignored' {
+  if (w.errorType === 'Omission') return 'missed'
+  if (w.errorType === 'Mispronunciation' || w.errorType === 'Insertion') return 'wrong'
+  if (w.errorType !== 'None') return 'ignored'
+  const acc = w.accuracyScore
+  if (acc < 60) return 'wrong'
+  if (acc < 80) return 'warning'
+  return 'correct'
 }
 </script>
 
@@ -35,11 +45,14 @@ function classFor(t: WordErrorType): 'correct' | 'wrong' | 'missed' | 'ignored'
 }
 
 .wpd-word {
+  display: inline-block;
+  margin-right: 8px;
   border-bottom: 2px solid transparent;
   padding-bottom: 1px;
 }
 
 .wpd-correct { border-bottom-color: #16a34a; }
+.wpd-warning { border-bottom-color: #f59e0b; }
 .wpd-wrong   { border-bottom-color: #ef4444; }
 .wpd-missed  { border-bottom-color: #9ca3af; }
 .wpd-ignored { border-bottom-color: transparent; }