175 lines
4.3 KiB
Vue
175 lines
4.3 KiB
Vue
<template>
|
||
<div class="pronunciation-view">
|
||
<q-page class="q-pa-md">
|
||
<div class="text-center q-mb-lg">
|
||
<h1 class="text-h4 q-mb-sm">發音練習</h1>
|
||
<p class="text-body1 text-grey-7">AI 輔助發音矯正練習</p>
|
||
</div>
|
||
|
||
<div class="row justify-center">
|
||
<div class="col-12 col-md-8">
|
||
<q-card class="pronunciation-card">
|
||
<q-card-section>
|
||
<div class="text-h6">練習詞彙: {{ pronunciationId }}</div>
|
||
<div class="pronunciation-target">
|
||
<div class="target-word">"Restaurant"</div>
|
||
<div class="phonetic">/ˈrɛstərɑnt/</div>
|
||
</div>
|
||
</q-card-section>
|
||
|
||
<q-card-section class="recording-section">
|
||
<div class="recording-area">
|
||
<q-btn
|
||
round
|
||
size="xl"
|
||
:color="isRecording ? 'negative' : 'primary'"
|
||
:icon="isRecording ? 'stop' : 'mic'"
|
||
@click="toggleRecording"
|
||
class="recording-btn"
|
||
/>
|
||
<div class="recording-status">
|
||
{{ isRecording ? '錄音中...' : '點擊開始錄音' }}
|
||
</div>
|
||
|
||
<div v-if="hasRecording" class="playback-controls q-mt-md">
|
||
<q-btn
|
||
icon="play_arrow"
|
||
label="播放我的發音"
|
||
@click="playRecording"
|
||
class="q-mr-sm"
|
||
/>
|
||
<q-btn
|
||
icon="volume_up"
|
||
label="播放標準發音"
|
||
@click="playTarget"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</q-card-section>
|
||
|
||
<q-card-section v-if="feedback">
|
||
<div class="feedback-section">
|
||
<div class="text-h6 q-mb-sm">發音評估</div>
|
||
<q-linear-progress
|
||
:value="feedback.accuracy"
|
||
color="positive"
|
||
class="q-mb-sm"
|
||
/>
|
||
<div class="text-body2">準確度: {{ Math.round(feedback.accuracy * 100) }}%</div>
|
||
<div class="feedback-tips q-mt-sm">
|
||
<strong>建議:</strong> {{ feedback.tip }}
|
||
</div>
|
||
</div>
|
||
</q-card-section>
|
||
|
||
<q-card-actions class="justify-center">
|
||
<q-btn color="primary" label="下一個詞彙" />
|
||
<q-btn flat label="重新練習" />
|
||
</q-card-actions>
|
||
</q-card>
|
||
</div>
|
||
</div>
|
||
</q-page>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import { useRoute } from 'vue-router'
|
||
|
||
const route = useRoute()
|
||
const pronunciationId = computed(() => route.params.id)
|
||
|
||
const isRecording = ref(false)
|
||
const hasRecording = ref(false)
|
||
const feedback = ref(null as any)
|
||
|
||
const toggleRecording = () => {
|
||
isRecording.value = !isRecording.value
|
||
if (!isRecording.value) {
|
||
hasRecording.value = true
|
||
// 模擬AI評估
|
||
setTimeout(() => {
|
||
feedback.value = {
|
||
accuracy: 0.85,
|
||
tip: '注意 "au" 的發音,可以更圓潤一些'
|
||
}
|
||
}, 1000)
|
||
}
|
||
}
|
||
|
||
const playRecording = () => {
|
||
// 播放用戶錄音
|
||
}
|
||
|
||
const playTarget = () => {
|
||
// 播放標準發音
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.pronunciation-view {
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.pronunciation-card {
|
||
max-width: 700px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.pronunciation-target {
|
||
text-align: center;
|
||
padding: 24px;
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
margin: 16px 0;
|
||
}
|
||
|
||
.target-word {
|
||
font-size: 2.5em;
|
||
font-weight: bold;
|
||
color: #1976d2;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.phonetic {
|
||
font-size: 1.2em;
|
||
color: #666;
|
||
font-family: 'Courier New', monospace;
|
||
}
|
||
|
||
.recording-section {
|
||
text-align: center;
|
||
padding: 32px;
|
||
}
|
||
|
||
.recording-area {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.recording-btn {
|
||
margin-bottom: 16px;
|
||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.recording-status {
|
||
font-size: 1.1em;
|
||
color: #666;
|
||
}
|
||
|
||
.feedback-section {
|
||
background: #e8f5e8;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
border-left: 4px solid #4caf50;
|
||
}
|
||
|
||
.feedback-tips {
|
||
background: #fff;
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
margin-top: 12px;
|
||
}
|
||
</style> |