refactor(web): restructure Vue3 app layout

This commit is contained in:
2026-02-26 12:20:19 +08:00
parent a4c2b6a40b
commit 52f691f02e
30 changed files with 3290 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
<template>
<div class="wrap">
<el-card>
<template #header>
<div class="h">
<div class="title">复习模式</div>
<el-select v-model="mode" style="width: 220px" @change="loadOne">
<el-option label="听音拼写" value="spelling" />
<el-option label="英译中" value="en2cn" />
<el-option label="中译英" value="cn2en" />
</el-select>
</div>
</template>
<el-empty v-if="!record" description="暂无可复习的单词" />
<div v-else>
<el-alert v-if="modeHint" type="info" :title="modeHint" show-icon style="margin-bottom:12px" />
<el-card shadow="never" style="margin-bottom:12px">
<template v-if="mode === 'spelling'">
<el-button @click="play">播放读音(uk)</el-button>
</template>
<template v-else-if="mode === 'en2cn'">
<div class="q">{{ record.word.word }}</div>
</template>
<template v-else>
<div class="q">{{ record.word.definition }}</div>
</template>
</el-card>
<el-input v-model="answer" placeholder="输入答案并回车" @keyup.enter="submit" />
<div style="margin-top:12px; display:flex; gap:12px">
<el-button type="primary" :loading="loading" @click="submit">提交</el-button>
<el-button @click="loadOne" :disabled="loading">换一个</el-button>
</div>
<el-result v-if="result" :icon="result.correct ? 'success' : 'error'" :title="result.correct ? '正确' : '不对'" style="margin-top:16px">
<template #sub-title>
<div>单词{{ result.word.word }} / 释义{{ result.word.definition }}</div>
<div v-if="!result.correct">你的答案{{ result.answer }}正确{{ result.correct_ans }}</div>
</template>
</el-result>
</div>
</el-card>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { audioUrl, getReviewWords, submitReview } from '../services/api'
const mode = ref('spelling')
const record = ref(null) // MemoryRecord (含 word)
const answer = ref('')
const loading = ref(false)
const result = ref(null)
const modeHint = computed(() => {
if (mode.value === 'spelling') return '听读音,拼写单词'
if (mode.value === 'en2cn') return '看到英文,写中文意思(允许包含匹配)'
return '看到中文意思,写英文单词'
})
async function loadOne() {
result.value = null
answer.value = ''
const res = await getReviewWords({ mode: mode.value, limit: 1 })
const arr = res.data ?? res
record.value = Array.isArray(arr) && arr.length ? arr[0] : null
}
function play() {
if (!record.value?.word?.word) return
const a = new Audio(audioUrl({ word: record.value.word.word, type: 'uk' }))
a.play()
}
async function submit() {
if (!record.value) return
if (!answer.value.trim()) return
loading.value = true
try {
const res = await submitReview({ recordId: record.value.id, answer: answer.value, mode: mode.value })
result.value = res.data ?? res
} finally {
loading.value = false
}
}
loadOne().catch(console.error)
</script>
<style scoped>
.wrap{padding:24px;}
.h{display:flex; align-items:center; justify-content:space-between; gap:12px;}
.title{font-size:22px; font-weight:700;}
.q{font-size:24px; font-weight:700;}
</style>