update
This commit is contained in:
@@ -18,8 +18,8 @@ function format(val: number, suffix: string = '', check: number = -1) {
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!practiceStore.total) return 0
|
||||
if (practiceStore.inputWordNumber > practiceStore.total) return 100
|
||||
return ((practiceStore.inputWordNumber / practiceStore.total) * 100)
|
||||
if (practiceStore.index > practiceStore.total) return 100
|
||||
return ((practiceStore.index / practiceStore.total) * 100)
|
||||
})
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, watch, watchEffect} from "vue"
|
||||
import {onMounted, watch} from "vue"
|
||||
import {$computed, $ref} from "vue/macros"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {DictType, DisplayStatistics, ShortKeyMap, Statistics, Word} from "../../types";
|
||||
import {DictType, DisplayStatistics, ShortKeyMap, Word} from "../../types";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts"
|
||||
import {cloneDeep} from "lodash-es"
|
||||
import {usePracticeStore} from "@/stores/practice.ts"
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {useEventListener, useOnKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import {useOnKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
|
||||
interface IProps {
|
||||
words: Word[],
|
||||
@@ -28,7 +31,6 @@ let data = $ref({
|
||||
originWrongWords: [],
|
||||
})
|
||||
|
||||
|
||||
let input = $ref('')
|
||||
let wrong = $ref('')
|
||||
let showFullWord = $ref(false)
|
||||
@@ -41,7 +43,7 @@ const playBeep = usePlayBeep()
|
||||
const playCorrect = usePlayCorrect()
|
||||
const playKeyboardAudio = usePlayKeyboardAudio()
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
|
||||
const volumeIconRef: any = $ref()
|
||||
|
||||
watch(() => props.words, () => {
|
||||
data.words = props.words
|
||||
@@ -56,8 +58,7 @@ watch(() => props.words, () => {
|
||||
practiceStore.wrongWordNumber = 0
|
||||
}, {immediate: true})
|
||||
|
||||
|
||||
let word = $computed(() => {
|
||||
const word = $computed(() => {
|
||||
return data.words[data.index] ?? {
|
||||
trans: [],
|
||||
name: '',
|
||||
@@ -66,6 +67,14 @@ let word = $computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const prevWord: Word = $computed(() => {
|
||||
return data.words?.[data.index - 1] ?? undefined
|
||||
})
|
||||
|
||||
const nextWord: Word = $computed(() => {
|
||||
return data.words?.[data.index + 1] ?? undefined
|
||||
})
|
||||
|
||||
let resetWord = $computed(() => {
|
||||
return word.name.slice(input.length + wrong.length)
|
||||
})
|
||||
@@ -76,7 +85,7 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
|
||||
function next() {
|
||||
function next(isTyping: boolean = true) {
|
||||
if (data.index === data.words.length - 1) {
|
||||
if (data.wrongWords.length) {
|
||||
console.log('当前背完了,但还有错词')
|
||||
@@ -86,12 +95,14 @@ function next() {
|
||||
}
|
||||
data.index = 0
|
||||
practiceStore.total = data.words.length
|
||||
practiceStore.index = 0
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
practiceStore.repeatNumber++
|
||||
data.wrongWords = []
|
||||
} else {
|
||||
console.log('这章节完了')
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
let now = Date.now()
|
||||
let stat: DisplayStatistics = {
|
||||
startDate: practiceStore.startDate,
|
||||
@@ -107,16 +118,27 @@ function next() {
|
||||
}
|
||||
} else {
|
||||
data.index++
|
||||
practiceStore.inputWordNumber++
|
||||
practiceStore.index++
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
console.log('这个词完了')
|
||||
if ([DictType.customDict, DictType.publicDict].includes(store.current.dictType)
|
||||
&& store.skipWordNames.includes(word.name.toLowerCase())) {
|
||||
next()
|
||||
} else {
|
||||
playWordAudio(word.name)
|
||||
volumeIconRef?.play()
|
||||
}
|
||||
}
|
||||
wrong = input = ''
|
||||
}
|
||||
|
||||
function prev() {
|
||||
data.index--
|
||||
playWordAudio(word.name)
|
||||
volumeIconRef?.play()
|
||||
wrong = input = ''
|
||||
}
|
||||
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
showFullWord = false
|
||||
}
|
||||
@@ -181,12 +203,12 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
store.skipWordDict.chapterWords = [store.skipWordDict.words]
|
||||
}
|
||||
activeBtnIndex = 0
|
||||
next()
|
||||
next(false)
|
||||
break
|
||||
case ShortKeyMap.Ignore:
|
||||
e.preventDefault()
|
||||
activeBtnIndex = 2
|
||||
next()
|
||||
next(false)
|
||||
break
|
||||
case ShortKeyMap.Show:
|
||||
if (settingStore.allowWordTip) {
|
||||
@@ -206,6 +228,28 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
|
||||
<template>
|
||||
<div class="type-word">
|
||||
<div class="near-word" v-if="settingStore.showNearWord">
|
||||
<div class="prev"
|
||||
@click="prev"
|
||||
v-if="prevWord">
|
||||
<Icon icon="bi:arrow-left" width="22"/>
|
||||
<div class="word">
|
||||
<div>{{ prevWord.name }}</div>
|
||||
<div>{{ prevWord.trans.join(';') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip title="快捷键:Tab">
|
||||
<div class="next"
|
||||
@click="next(false)"
|
||||
v-if="nextWord">
|
||||
<div class="word">
|
||||
<div :class="settingStore.dictation && 'shadow'">{{ nextWord.name }}</div>
|
||||
<div>{{ nextWord.trans.join(';') }}</div>
|
||||
</div>
|
||||
<Icon icon="bi:arrow-right" width="22"/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="translate"
|
||||
:style="{fontSize: settingStore.fontSize.wordTranslateFontSize +'rem'}"
|
||||
>{{ word.trans.join(';') }}
|
||||
@@ -226,7 +270,7 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
</template>
|
||||
<span class="letter" v-else>{{ resetWord }}</span>
|
||||
</div>
|
||||
<div class="audio" @click="playWordAudio(word.name)">播放</div>
|
||||
<VolumeIcon ref="volumeIconRef" :simple="true" @click="playWordAudio(word.name)"/>
|
||||
</div>
|
||||
<div class="phonetic">{{ word.usphone }}</div>
|
||||
<div class="options">
|
||||
@@ -237,7 +281,7 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
收藏
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Tab" :active="activeBtnIndex === 2">
|
||||
下一个
|
||||
跳过
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -256,6 +300,49 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
|
||||
.near-word {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: var(--toolbar-width);
|
||||
|
||||
.word {
|
||||
div {
|
||||
font-size: 24rem;
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
font-size: 14rem;
|
||||
}
|
||||
}
|
||||
|
||||
.prev {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
float: left;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
}
|
||||
|
||||
.next {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
float: right;
|
||||
|
||||
.word {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.shadow {
|
||||
color: transparent !important;
|
||||
text-shadow: #b0b0b0 0 0 6px;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
margin-top: 10rem;
|
||||
@@ -294,6 +381,4 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -89,7 +89,7 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
<div class="side" v-if="store.sideIsOpen">
|
||||
<header>
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="tabIndex===0&&'active'" @click="slideTo(0)">{{ store.dict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===0&&'active'" @click="slideTo(0)">{{ store.currentDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===1&&'active'" @click="slideTo(1)">{{ store.newWordDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===2&&'active'" @click="slideTo(2)">{{ store.wrongWordDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===3&&'active'" @click="slideTo(3)">{{ store.skipWordDict.name }}</div>
|
||||
@@ -100,18 +100,18 @@ const skipWordDictActiveIndex = computed(() => {
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
<div class="dict-name">{{ store.dict.chapterIndex + 1 }}.</div>
|
||||
<div class="dict-name">{{ store.currentDict.chapterIndex + 1 }}.</div>
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.dict,store.dict.chapterIndex,e)"
|
||||
@change="(e:number) => store.changeDict(store.currentDict,store.currentDict.chapterIndex,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 0"
|
||||
:list="store.dict.chapterWords[store.dict.chapterIndex]??[]"
|
||||
:list="store.currentDict.chapterWords[store.currentDict.chapterIndex]??[]"
|
||||
:activeIndex="dictActiveIndex"/>
|
||||
<footer v-if="![DictType.customDict,DictType.publicDict].includes(store.current.dictType)">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.dict)"
|
||||
@confirm="store.changeDict(store.currentDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
|
||||
@@ -185,7 +185,7 @@ useWatchAllSound()
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="row">
|
||||
<label class="item-title">字体设置</label>
|
||||
<label class="item-title">字体设置(仅可调整单词练习)</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="sut-title">外语字体</label>
|
||||
@@ -234,7 +234,7 @@ useWatchAllSound()
|
||||
|
||||
.setting-modal {
|
||||
width: 40vw;
|
||||
height: 80vh;
|
||||
height: 650rem;
|
||||
display: flex;
|
||||
color: var(--color-font-1);
|
||||
|
||||
|
||||
@@ -3,8 +3,12 @@ import {Icon} from "@iconify/vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import IconWrapper from "@/components/IconWrapper.vue";
|
||||
|
||||
const props = withDefaults(defineProps<{ time?: number }>(), {
|
||||
time: 400
|
||||
const props = withDefaults(defineProps<{
|
||||
time?: number,
|
||||
simple?: boolean
|
||||
}>(), {
|
||||
time: 400,
|
||||
simple: false
|
||||
})
|
||||
let step = $ref(2)
|
||||
let count = $ref(0)
|
||||
@@ -35,10 +39,17 @@ function click() {
|
||||
emit('click')
|
||||
play()
|
||||
}
|
||||
|
||||
defineExpose({play})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<IconWrapper @click.stop="click">
|
||||
<div class="center" @click.stop="click" v-if="props.simple">
|
||||
<Icon v-if="step === 0" icon="bx:volume"/>
|
||||
<Icon v-if="step === 1" icon="bx:volume-low"/>
|
||||
<Icon v-if="step === 2" icon="bx:volume-full"/>
|
||||
</div>
|
||||
<IconWrapper @click.stop="click" v-else>
|
||||
<div class="center">
|
||||
<Icon v-if="step === 0" icon="bx:volume"/>
|
||||
<Icon v-if="step === 1" icon="bx:volume-low"/>
|
||||
@@ -49,8 +60,15 @@ function click() {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.center {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
$w: 26rem;
|
||||
|
||||
:deep(svg) {
|
||||
width: $w;
|
||||
height: $w;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,11 +2,11 @@ import {defineStore} from "pinia"
|
||||
import {Word} from "@/types.ts"
|
||||
|
||||
export interface PracticeState {
|
||||
type: 'word' | 'article',
|
||||
wrongWords: Word[],
|
||||
repeatNumber: number,
|
||||
startDate: number,
|
||||
total: number,
|
||||
index: number,
|
||||
inputWordNumber: number,
|
||||
wrongWordNumber: number,
|
||||
correctRate: number,
|
||||
@@ -15,13 +15,12 @@ export interface PracticeState {
|
||||
export const usePracticeStore = defineStore('practice', {
|
||||
state: (): PracticeState => {
|
||||
return {
|
||||
// type: 'articles',
|
||||
type: 'word',
|
||||
wrongWords: [],
|
||||
repeatNumber: 0,
|
||||
startDate: Date.now(),
|
||||
correctRate: -1,
|
||||
total: 0,
|
||||
index: 0,
|
||||
inputWordNumber: 0,
|
||||
wrongWordNumber: 0,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user