save
This commit is contained in:
@@ -126,7 +126,6 @@ const vFocus = {
|
||||
display: inline-flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background: var(--color-input-bg);
|
||||
border: 1px solid var(--color-input-border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
@@ -173,6 +172,7 @@ const vFocus = {
|
||||
transition: all .3s;
|
||||
height: 1.5rem;
|
||||
color: var(--color-input-color);
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, provide, ref, watch} from "vue";
|
||||
import { onMounted, provide, ref, watch } from "vue";
|
||||
|
||||
import Statistics from "@/pages/word/Statistics.vue";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {Dict, PracticeData, ShortcutKey, TaskWords, Word} from "@/types/types.ts";
|
||||
import {useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { Dict, PracticeData, PracticeMode, ShortcutKey, TaskWords, Word } from "@/types/types.ts";
|
||||
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import {getCurrentStudyWord, useWordOptions} from "@/hooks/dict.ts";
|
||||
import {_getDictDataByUrl, cloneDeep, resourceWrap, shuffle, sleep} from "@/utils";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import { getCurrentStudyWord, useWordOptions } from "@/hooks/dict.ts";
|
||||
import { _getDictDataByUrl, cloneDeep, resourceWrap, shuffle, sleep } from "@/utils";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import Footer from "@/pages/word/components/Footer.vue";
|
||||
import Panel from "@/components/Panel.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
@@ -19,15 +19,16 @@ import Tooltip from "@/components/base/Tooltip.vue";
|
||||
import WordList from "@/components/list/WordList.vue";
|
||||
import TypeWord from "@/pages/word/components/TypeWord.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
import {getDefaultDict, getDefaultWord} from "@/types/func.ts";
|
||||
import { getDefaultDict, getDefaultWord } from "@/types/func.ts";
|
||||
import ConflictNotice from "@/components/ConflictNotice.vue";
|
||||
import PracticeLayout from "@/components/PracticeLayout.vue";
|
||||
|
||||
import {DICT_LIST, PracticeSaveWordKey} from "@/config/env.ts";
|
||||
import {set} from "idb-keyval";
|
||||
import { DICT_LIST, PracticeSaveWordKey } from "@/config/env.ts";
|
||||
import { set } from "idb-keyval";
|
||||
import { ToastInstance } from "@/components/base/toast/type.ts";
|
||||
|
||||
const {
|
||||
isWordCollect,
|
||||
@@ -58,8 +59,11 @@ let data = $ref<PracticeData>({
|
||||
wrongWords: [],
|
||||
})
|
||||
let isTypingWrongWord = ref(false)
|
||||
|
||||
let practiceMode = ref(PracticeMode.FollowWrite)
|
||||
provide('isTypingWrongWord', isTypingWrongWord)
|
||||
provide('practiceData', data)
|
||||
provide('practiceMode', practiceMode)
|
||||
|
||||
async function loadDict() {
|
||||
// console.log('load好了开始加载')
|
||||
@@ -167,16 +171,13 @@ const nextWord: Word = $computed(() => {
|
||||
})
|
||||
|
||||
function readMode() {
|
||||
practiceMode.value = PracticeMode.FollowWrite
|
||||
settingStore.dictation = false
|
||||
settingStore.translate = true
|
||||
}
|
||||
|
||||
function reviewMode() {
|
||||
settingStore.dictation = true
|
||||
settingStore.translate = true
|
||||
}
|
||||
|
||||
function writeMode() {
|
||||
practiceMode.value = PracticeMode.Dictation
|
||||
settingStore.dictation = true
|
||||
settingStore.translate = false
|
||||
}
|
||||
@@ -184,11 +185,11 @@ function writeMode() {
|
||||
function wordLoop() {
|
||||
let d = Math.floor(data.index / 6) - 1
|
||||
if (data.index > 0 && data.index % 6 === (d < 0 ? 0 : d)) {
|
||||
if (!settingStore.dictation) {
|
||||
reviewMode()
|
||||
if (practiceMode.value === PracticeMode.FollowWrite) {
|
||||
practiceMode.value = PracticeMode.Spell
|
||||
data.index -= 6
|
||||
} else {
|
||||
readMode()
|
||||
practiceMode.value = PracticeMode.FollowWrite
|
||||
data.index++
|
||||
}
|
||||
} else {
|
||||
@@ -196,6 +197,7 @@ function wordLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
let toastInstance: ToastInstance = null
|
||||
|
||||
async function next(isTyping: boolean = true) {
|
||||
debugger
|
||||
@@ -212,8 +214,8 @@ async function next(isTyping: boolean = true) {
|
||||
}
|
||||
} else {
|
||||
if (data.index === data.words.length - 1) {
|
||||
if ([0, 2].includes(statStore.step) || isTypingWrongWord.value) {
|
||||
if (!settingStore.dictation) {
|
||||
if (statStore.step === 0 || isTypingWrongWord.value) {
|
||||
if (practiceMode.value !== PracticeMode.Spell) {
|
||||
let i = data.index
|
||||
i--
|
||||
let d = Math.floor(i / 6) - 1
|
||||
@@ -228,17 +230,13 @@ async function next(isTyping: boolean = true) {
|
||||
}
|
||||
data.index = i + 1
|
||||
emitter.emit(EventKey.resetWord)
|
||||
reviewMode()
|
||||
practiceMode.value = PracticeMode.Spell
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (settingStore.dictation && !settingStore.translate) {
|
||||
return readMode()
|
||||
}
|
||||
}
|
||||
if (data.wrongWords.length) {
|
||||
isTypingWrongWord.value = true
|
||||
readMode()
|
||||
practiceMode.value = PracticeMode.FollowWrite
|
||||
console.log('当前学完了,但还有错词')
|
||||
data.words = shuffle(cloneDeep(data.wrongWords))
|
||||
data.index = 0
|
||||
@@ -248,7 +246,7 @@ async function next(isTyping: boolean = true) {
|
||||
console.log('当前学完了,没错词', statStore.total, statStore.step, data.index)
|
||||
|
||||
//学完了
|
||||
if (statStore.step === 4) {
|
||||
if (statStore.step === 8) {
|
||||
statStore.spend = Date.now() - statStore.startDate
|
||||
console.log('全完学完了')
|
||||
showStatDialog = true
|
||||
@@ -257,73 +255,143 @@ async function next(isTyping: boolean = true) {
|
||||
// emit('complete', {})
|
||||
}
|
||||
|
||||
//开始默认所有单词
|
||||
if (statStore.step === 3) {
|
||||
//开始默写之前
|
||||
if (statStore.step === 7) {
|
||||
statStore.step++
|
||||
if (taskWords.write.length) {
|
||||
console.log('开始默认所有单词')
|
||||
Toast.info('默写完成后按空格键切换下一个', {duration: 10000})
|
||||
writeMode()
|
||||
data.words = shuffle(taskWords.write)
|
||||
console.log('开始默写之前')
|
||||
toastInstance?.close()
|
||||
toastInstance = Toast.info('输入完成后按空格键切换下一个', {duration: 10000})
|
||||
practiceMode.value = PracticeMode.Dictation
|
||||
data.words = shuffle(taskWords.new)
|
||||
data.index = 0
|
||||
}
|
||||
|
||||
//开始听写之前
|
||||
if (statStore.step === 6) {
|
||||
statStore.step++
|
||||
console.log('开始听写之前')
|
||||
toastInstance?.close()
|
||||
toastInstance = Toast.info('输入完成后按空格键切换下一个', {duration: 10000})
|
||||
practiceMode.value = PracticeMode.Listen
|
||||
data.words = shuffle(taskWords.review)
|
||||
data.index = 0
|
||||
}
|
||||
|
||||
//开始复写之前
|
||||
if (statStore.step === 5) {
|
||||
statStore.step++
|
||||
if (taskWords.review.length) {
|
||||
console.log('开始复写之前')
|
||||
data.words = taskWords.write
|
||||
practiceMode.value = PracticeMode.Identify
|
||||
data.index = 0
|
||||
} else {
|
||||
console.log('开始默认所有单词-无单词略过')
|
||||
console.log('开始复写之前-无单词略过')
|
||||
statStore.step++
|
||||
statStore.step++
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
//开始默写昨日
|
||||
//开始默写上次
|
||||
if (statStore.step === 4) {
|
||||
statStore.step++
|
||||
console.log('开始默写上次')
|
||||
toastInstance?.close()
|
||||
toastInstance = Toast.info('输入完成后按空格键切换下一个', {duration: 10000})
|
||||
practiceMode.value = PracticeMode.Dictation
|
||||
data.words = shuffle(taskWords.new)
|
||||
data.index = 0
|
||||
}
|
||||
|
||||
//开始听写上次
|
||||
if (statStore.step === 3) {
|
||||
statStore.step++
|
||||
console.log('开始听写上次')
|
||||
toastInstance?.close()
|
||||
toastInstance = Toast.info('输入完成后按空格键切换下一个', {duration: 10000})
|
||||
practiceMode.value = PracticeMode.Listen
|
||||
data.words = shuffle(taskWords.review)
|
||||
data.index = 0
|
||||
}
|
||||
|
||||
//开始复写昨日
|
||||
if (statStore.step === 2) {
|
||||
statStore.step++
|
||||
if (taskWords.review.length) {
|
||||
console.log('开始默写昨日')
|
||||
Toast.info('默写完成后按空格键切换下一个', {duration: 10000})
|
||||
writeMode()
|
||||
data.words = shuffle(taskWords.review)
|
||||
console.log('开始复写昨日')
|
||||
data.words = taskWords.review
|
||||
practiceMode.value = PracticeMode.Identify
|
||||
data.index = 0
|
||||
} else {
|
||||
console.log('开始默写昨日-无单词略过')
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
//开始复习昨日
|
||||
if (statStore.step === 1) {
|
||||
statStore.step++
|
||||
if (taskWords.review.length) {
|
||||
console.log('开始复习昨日')
|
||||
readMode()
|
||||
data.words = shuffle(taskWords.review)
|
||||
data.index = 0
|
||||
} else {
|
||||
console.log('开始复习昨日-无单词略过')
|
||||
console.log('开始复写昨日-无单词略过')
|
||||
statStore.step++
|
||||
statStore.step++
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
||||
//开始默写新词
|
||||
if (statStore.step === 0) {
|
||||
if (statStore.step === 1) {
|
||||
statStore.step++
|
||||
console.log('开始默写新词')
|
||||
Toast.info('默写完成后按空格键切换下一个', {duration: 10000})
|
||||
writeMode()
|
||||
toastInstance?.close()
|
||||
toastInstance = Toast.info('输入完成后按空格键切换下一个', {duration: 10000})
|
||||
practiceMode.value = PracticeMode.Dictation
|
||||
data.words = shuffle(taskWords.new)
|
||||
data.index = 0
|
||||
}
|
||||
|
||||
//开始听写新词
|
||||
if (statStore.step === 0) {
|
||||
statStore.step++
|
||||
console.log('开始听写新词')
|
||||
toastInstance = Toast.info('输入完成后按空格键切换下一个', {duration: 10000})
|
||||
practiceMode.value = PracticeMode.Listen
|
||||
data.words = shuffle(taskWords.new)
|
||||
data.index = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ([0, 2].includes(statStore.step) || isTypingWrongWord.value) {
|
||||
if (statStore.step === 0) {
|
||||
wordLoop()
|
||||
} else {
|
||||
if (settingStore.dictation && !settingStore.translate) {
|
||||
readMode()
|
||||
} else {
|
||||
writeMode()
|
||||
await sleep(100)
|
||||
data.index++
|
||||
}
|
||||
// await sleep(2000)
|
||||
} else if (statStore.step === 1) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 2) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 3) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 4) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 5) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 6) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 7) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
} else if (statStore.step === 8) {
|
||||
if (isTypingWrongWord.value) wordLoop()
|
||||
else data.index++
|
||||
}
|
||||
// if ([0, 2].includes(statStore.step) || isTypingWrongWord.value) {
|
||||
// wordLoop()
|
||||
// } else {
|
||||
// if (settingStore.dictation && !settingStore.translate) {
|
||||
// readMode()
|
||||
// } else {
|
||||
// writeMode()
|
||||
// await sleep(100)
|
||||
// data.index++
|
||||
// }
|
||||
// // await sleep(2000)
|
||||
// }
|
||||
}
|
||||
}
|
||||
savePracticeData()
|
||||
@@ -503,8 +571,8 @@ useEvents([
|
||||
|
||||
<template>
|
||||
<PracticeLayout
|
||||
v-loading="loading"
|
||||
panelLeft="var(--word-panel-margin-left)">
|
||||
v-loading="loading"
|
||||
panelLeft="var(--word-panel-margin-left)">
|
||||
<template v-slot:practice>
|
||||
<div class="practice-word">
|
||||
<div class="absolute z-1 top-4 w-full" v-if="settingStore.showNearWord">
|
||||
@@ -513,7 +581,7 @@ useEvents([
|
||||
v-if="prevWord">
|
||||
<IconFluentArrowLeft16Regular class="arrow" width="22"/>
|
||||
<Tooltip
|
||||
:title="`上一个(${settingStore.shortcutKeyMap[ShortcutKey.Previous]})`"
|
||||
:title="`上一个(${settingStore.shortcutKeyMap[ShortcutKey.Previous]})`"
|
||||
>
|
||||
<div class="word">{{ prevWord.word }}</div>
|
||||
</Tooltip>
|
||||
@@ -522,7 +590,7 @@ useEvents([
|
||||
@click="next(false)"
|
||||
v-if="nextWord">
|
||||
<Tooltip
|
||||
:title="`下一个(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
:title="`下一个(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`"
|
||||
>
|
||||
<div class="word" :class="settingStore.dictation && 'word-shadow'">{{ nextWord.word }}</div>
|
||||
</Tooltip>
|
||||
@@ -530,10 +598,10 @@ useEvents([
|
||||
</div>
|
||||
</div>
|
||||
<TypeWord
|
||||
ref="typingRef"
|
||||
:word="word"
|
||||
@wrong="onTypeWrong"
|
||||
@complete="next"
|
||||
ref="typingRef"
|
||||
:word="word"
|
||||
@wrong="onTypeWrong"
|
||||
@complete="next"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -545,41 +613,41 @@ useEvents([
|
||||
<span>{{ store.sdict.name }} ({{ store.sdict.lastLearnIndex }} / {{ store.sdict.length }})</span>
|
||||
|
||||
<BaseIcon
|
||||
@click="continueStudy"
|
||||
:title="`下一组(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`">
|
||||
@click="continueStudy"
|
||||
:title="`下一组(${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`">
|
||||
<IconFluentArrowRight16Regular class="arrow" width="22"/>
|
||||
</BaseIcon>
|
||||
<BaseIcon
|
||||
@click="randomWrite"
|
||||
:title="`随机默写(${settingStore.shortcutKeyMap[ShortcutKey.RandomWrite]})`">
|
||||
@click="randomWrite"
|
||||
:title="`随机默写(${settingStore.shortcutKeyMap[ShortcutKey.RandomWrite]})`">
|
||||
<IconFluentArrowShuffle16Regular class="arrow" width="22"/>
|
||||
</BaseIcon>
|
||||
</div>
|
||||
</template>
|
||||
<div class="panel-page-item pl-4">
|
||||
<WordList
|
||||
v-if="data.words.length"
|
||||
:is-active="settingStore.showPanel"
|
||||
:static="false"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"
|
||||
@click="(val:any) => data.index = val.index"
|
||||
v-if="data.words.length"
|
||||
:is-active="settingStore.showPanel"
|
||||
:static="false"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"
|
||||
@click="(val:any) => data.index = val.index"
|
||||
>
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
:class="!isWordCollect(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordCollect(item)"
|
||||
:title="!isWordCollect(item) ? '收藏' : '取消收藏'">
|
||||
:class="!isWordCollect(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordCollect(item)"
|
||||
:title="!isWordCollect(item) ? '收藏' : '取消收藏'">
|
||||
<IconFluentStar16Regular v-if="!isWordCollect(item)"/>
|
||||
<IconFluentStar16Filled v-else/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
:class="!isWordSimple(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordSimple(item)"
|
||||
:title="!isWordSimple(item) ? '标记为已掌握' : '取消标记已掌握'">
|
||||
:class="!isWordSimple(item)?'collect':'fill'"
|
||||
@click.stop="toggleWordSimple(item)"
|
||||
:title="!isWordSimple(item) ? '标记为已掌握' : '取消标记已掌握'">
|
||||
<IconFluentCheckmarkCircle16Regular v-if="!isWordSimple(item)"/>
|
||||
<IconFluentCheckmarkCircle16Filled v-else/>
|
||||
</BaseIcon>
|
||||
@@ -591,11 +659,11 @@ useEvents([
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<Footer
|
||||
:is-simple="isWordSimple(word)"
|
||||
@toggle-simple="toggleWordSimpleWrapper"
|
||||
:is-collect="isWordCollect(word)"
|
||||
@toggle-collect="toggleWordCollect(word)"
|
||||
@skip="next(false)"
|
||||
:is-simple="isWordSimple(word)"
|
||||
@toggle-simple="toggleWordSimpleWrapper"
|
||||
:is-collect="isWordCollect(word)"
|
||||
@toggle-collect="toggleWordCollect(word)"
|
||||
@skip="next(false)"
|
||||
/>
|
||||
</template>
|
||||
</PracticeLayout>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {inject, Ref, watch} from "vue"
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {PracticeData, ShortcutKey} from "@/types/types.ts";
|
||||
import { inject, Ref, watch } from "vue"
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { PracticeData, PracticeMode, ShortcutKey } from "@/types/types.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Tooltip from "@/components/base/Tooltip.vue";
|
||||
import Progress from '@/components/base/Progress.vue'
|
||||
@@ -26,6 +26,7 @@ const emit = defineEmits<{
|
||||
|
||||
let practiceData = inject<PracticeData>('practiceData')
|
||||
let isTypingWrongWord = inject<Ref<boolean>>('isTypingWrongWord')
|
||||
let practiceMode = inject<Ref<PracticeMode>>('practiceMode')
|
||||
|
||||
function format(val: number, suffix: string = '', check: number = -1) {
|
||||
return val === check ? '-' : (val + suffix)
|
||||
@@ -39,16 +40,28 @@ const status = $computed(() => {
|
||||
str += `学习新词`
|
||||
break
|
||||
case 1:
|
||||
str += `默写所有新词`
|
||||
str += `听写新词`
|
||||
break
|
||||
case 2:
|
||||
str += `复习上次`
|
||||
str += `默写新词`
|
||||
break
|
||||
case 3:
|
||||
str += `默写上次所有`
|
||||
str += `复习上次学习`
|
||||
break
|
||||
case 4:
|
||||
str += '默写之前'
|
||||
str += '听写上次学习'
|
||||
break
|
||||
case 5:
|
||||
str += '默写上次学习'
|
||||
break
|
||||
case 6:
|
||||
str += '复习之前学习'
|
||||
break
|
||||
case 7:
|
||||
str += '听写之前学习'
|
||||
break
|
||||
case 8:
|
||||
str += '默写之前学习'
|
||||
break
|
||||
}
|
||||
return str
|
||||
@@ -65,17 +78,17 @@ const progress = $computed(() => {
|
||||
<div class="footer">
|
||||
<Tooltip :title="settingStore.showToolbar?'收起':'展开'">
|
||||
<IconFluentChevronLeft20Filled
|
||||
@click="settingStore.showToolbar = !settingStore.showToolbar"
|
||||
class="arrow"
|
||||
:class="!settingStore.showToolbar && 'down'"
|
||||
color="#999"/>
|
||||
@click="settingStore.showToolbar = !settingStore.showToolbar"
|
||||
class="arrow"
|
||||
:class="!settingStore.showToolbar && 'down'"
|
||||
color="#999"/>
|
||||
</Tooltip>
|
||||
|
||||
<div class="bottom">
|
||||
<Progress
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
:percentage="progress"
|
||||
:stroke-width="8"
|
||||
:show-text="false"/>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="stat">
|
||||
<div class="row">
|
||||
@@ -84,7 +97,7 @@ const progress = $computed(() => {
|
||||
<div class="name">{{ status }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ statisticsStore.total }}</div>
|
||||
<div class="num">{{ statisticsStore.total }}{{ practiceMode }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
@@ -101,44 +114,44 @@ const progress = $computed(() => {
|
||||
</div>
|
||||
<div class="flex gap-2 justify-center items-center">
|
||||
<BaseIcon
|
||||
:class="!isSimple?'collect':'fill'"
|
||||
@click="$emit('toggleSimple')"
|
||||
:title="(!isSimple ? '标记为已掌握' : '取消标记已掌握')+`(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`">
|
||||
:class="!isSimple?'collect':'fill'"
|
||||
@click="$emit('toggleSimple')"
|
||||
:title="(!isSimple ? '标记为已掌握' : '取消标记已掌握')+`(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`">
|
||||
<IconFluentCheckmarkCircle16Regular v-if="!isSimple"/>
|
||||
<IconFluentCheckmarkCircle16Filled v-else/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
:class="!isCollect?'collect':'fill'"
|
||||
@click.stop="$emit('toggleCollect')"
|
||||
:title="(!isCollect ? '收藏' : '取消收藏')+`(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`">
|
||||
:class="!isCollect?'collect':'fill'"
|
||||
@click.stop="$emit('toggleCollect')"
|
||||
:title="(!isCollect ? '收藏' : '取消收藏')+`(${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`">
|
||||
<IconFluentStarAdd16Regular v-if="!isCollect"/>
|
||||
<IconFluentStar16Filled v-else/>
|
||||
</BaseIcon>
|
||||
<BaseIcon
|
||||
@click="emit('skip')"
|
||||
:title="`跳过(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`">
|
||||
@click="emit('skip')"
|
||||
:title="`跳过(${settingStore.shortcutKeyMap[ShortcutKey.Next]})`">
|
||||
<IconFluentArrowBounce20Regular class="transform-rotate-180"/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.dictation = !settingStore.dictation"
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
@click="settingStore.dictation = !settingStore.dictation"
|
||||
:title="`开关默写模式(${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
|
||||
>
|
||||
<IconFluentEyeOff16Regular v-if="settingStore.dictation"/>
|
||||
<IconFluentEye16Regular v-else/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
:title="`开关释义显示(${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
|
||||
@click="settingStore.translate = !settingStore.translate">
|
||||
:title="`开关释义显示(${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
|
||||
@click="settingStore.translate = !settingStore.translate">
|
||||
<IconFluentTranslate16Regular v-if="settingStore.translate"/>
|
||||
<IconFluentTranslateOff16Regular v-else/>
|
||||
</BaseIcon>
|
||||
|
||||
<BaseIcon
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`单词本(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`">
|
||||
@click="settingStore.showPanel = !settingStore.showPanel"
|
||||
:title="`单词本(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`">
|
||||
<IconFluentTextListAbcUppercaseLtr20Regular/>
|
||||
</BaseIcon>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import {ShortcutKey, Word} from "@/types/types.ts";
|
||||
import { PracticeMode, ShortcutKey, Word } from "@/types/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio, useTTsPlayAudio} from "@/hooks/sound.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {nextTick, onMounted, onUnmounted, watch} from "vue";
|
||||
import Tooltip from "@/components/base/Tooltip.vue";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio } from "@/hooks/sound.ts";
|
||||
import { emitter, EventKey } from "@/utils/eventBus.ts";
|
||||
import { inject, onMounted, onUnmounted, Ref, watch } from "vue";
|
||||
import SentenceHightLightWord from "@/pages/word/components/SentenceHightLightWord.vue";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {getDefaultWord} from "@/types/func.ts";
|
||||
import {_nextTick, sleep} from "@/utils";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { getDefaultWord } from "@/types/func.ts";
|
||||
import { _nextTick, sleep } from "@/utils";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
|
||||
interface IProps {
|
||||
word: Word,
|
||||
@@ -34,6 +34,8 @@ let cursor = $ref({
|
||||
top: 0,
|
||||
left: 0,
|
||||
})
|
||||
let practiceMode = inject<Ref<PracticeMode>>('practiceMode')
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const statStore = usePracticeStore()
|
||||
|
||||
@@ -67,7 +69,9 @@ function reset() {
|
||||
wordRepeatCount = 0
|
||||
inputLock = false
|
||||
if (settingStore.wordSound) {
|
||||
volumeIconRef?.play(400, true)
|
||||
if (practiceMode.value !== PracticeMode.Dictation) {
|
||||
volumeIconRef?.play(400, true)
|
||||
}
|
||||
}
|
||||
// 更新当前单词信息
|
||||
updateCurrentWordInfo();
|
||||
@@ -287,16 +291,16 @@ function checkCursorPosition() {
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="flex gap-1 mt-26">
|
||||
<div class="phonetic"
|
||||
:class="(settingStore.dictation && !showFullWord) && 'word-shadow'"
|
||||
:class="((settingStore.dictation || [PracticeMode.Spell,PracticeMode.Listen,PracticeMode.Dictation].includes(practiceMode)) && !showFullWord) && 'word-shadow'"
|
||||
v-if="settingStore.soundType === 'us' && word.phonetic0">[{{ word.phonetic0 }}]
|
||||
</div>
|
||||
<div class="phonetic"
|
||||
:class="(settingStore.dictation && !showFullWord) && 'word-shadow'"
|
||||
:class="((settingStore.dictation || [PracticeMode.Spell,PracticeMode.Listen,PracticeMode.Dictation].includes(practiceMode)) && !showFullWord) && 'word-shadow'"
|
||||
v-if="settingStore.soundType === 'uk' && word.phonetic1">[{{ word.phonetic1 }}]
|
||||
</div>
|
||||
<VolumeIcon
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
</div>
|
||||
|
||||
<div class="word my-1"
|
||||
@@ -307,48 +311,71 @@ function checkCursorPosition() {
|
||||
>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="settingStore.dictation">
|
||||
<span class="letter" v-if="!showFullWord">{{ displayWord.split('').map(() => '_').join('') }}</span>
|
||||
<template v-if="settingStore.wordPracticeMode === 0">
|
||||
<template
|
||||
v-if="[PracticeMode.Spell,PracticeMode.Listen,PracticeMode.Dictation].includes(practiceMode)">
|
||||
<span class="letter" v-if="!showFullWord">{{
|
||||
displayWord.split('').map(() => (PracticeMode.Dictation === practiceMode ? ' ' : '_')).join('')
|
||||
}}</span>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="letter" v-if="(settingStore.dictation && !showFullWord)">{{
|
||||
displayWord.split('').map(() => '_').join('')
|
||||
}}</span>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-4" v-if="practiceMode === PracticeMode.Identify">
|
||||
<BaseButton size="large">不认识</BaseButton>
|
||||
<BaseButton size="large">我认识</BaseButton>
|
||||
</div>
|
||||
|
||||
<div class="translate anim flex flex-col gap-2 my-3"
|
||||
v-opacity="settingStore.translate || showFullWord"
|
||||
v-opacity="(settingStore.translate && ![PracticeMode.Listen,PracticeMode.Identify].includes(practiceMode)) || showFullWord"
|
||||
:style="{
|
||||
fontSize: settingStore.fontSize.wordTranslateFontSize +'px',
|
||||
}"
|
||||
>
|
||||
<div class="flex" v-for="(v,i) in word.trans">
|
||||
<div class="shrink-0" :class="v.pos ? 'w-12 en-article-family' : '-ml-3'">{{ v.pos }}</div>
|
||||
<span v-if="settingStore.dictation && !showFullWord" v-html="hideWordInTranslation(v.cn, word.word)"></span>
|
||||
<span
|
||||
v-if="(settingStore.dictation || [PracticeMode.Spell,PracticeMode.Listen].includes(practiceMode)) && !showFullWord"
|
||||
v-html="hideWordInTranslation(v.cn, word.word)"></span>
|
||||
<span v-else>{{ v.cn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="other">
|
||||
<div class="other anim"
|
||||
v-opacity="![PracticeMode.Listen,PracticeMode.Dictation,PracticeMode.Identify].includes(practiceMode) || showFullWord ">
|
||||
<div class="line-white my-2"></div>
|
||||
<template v-if="word?.sentences?.length">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="sentence" v-for="item in word.sentences">
|
||||
<SentenceHightLightWord class="text-xl" :text="item.c" :word="word.word"
|
||||
:dictation="(settingStore.dictation && !showFullWord)"/>
|
||||
<div class="text-base anim" v-opacity="settingStore.translate || showFullWord">{{ item.cn }}</div>
|
||||
:dictation="((settingStore.dictation || [PracticeMode.Spell,PracticeMode.Listen].includes(practiceMode)) && !showFullWord)"/>
|
||||
<div class="text-base anim"
|
||||
v-opacity="(settingStore.translate && ![PracticeMode.Listen].includes(practiceMode)) || showFullWord">
|
||||
{{ item.cn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line-white my-2 mb-5 anim" v-opacity="settingStore.translate || showFullWord"></div>
|
||||
</template>
|
||||
|
||||
|
||||
<div class="anim" v-opacity="(settingStore.translate && !settingStore.dictation) || showFullWord ">
|
||||
<div class="anim"
|
||||
v-opacity="(settingStore.translate && !(settingStore.dictation || [PracticeMode.Spell].includes(practiceMode))) || showFullWord ">
|
||||
<template v-if="word?.phrases?.length">
|
||||
<div class="flex">
|
||||
<div class="label">短语</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center gap-4" v-for="item in word.phrases">
|
||||
<SentenceHightLightWord class="en" :text="item.c" :word="word.word"
|
||||
:dictation="(settingStore.dictation && !showFullWord)"/>
|
||||
:dictation="((settingStore.dictation || [PracticeMode.Spell,PracticeMode.Listen].includes(practiceMode)) && !showFullWord)"/>
|
||||
<div class="cn anim" v-opacity="settingStore.translate">{{ item.cn }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -208,4 +208,13 @@ export enum PracticeArticleWordType {
|
||||
Symbol,
|
||||
Number,
|
||||
Word
|
||||
}
|
||||
|
||||
export enum PracticeMode {
|
||||
Free,
|
||||
FollowWrite,//跟写
|
||||
Spell,
|
||||
Identify,
|
||||
Listen,
|
||||
Dictation
|
||||
}
|
||||
Reference in New Issue
Block a user