修改数据结构

This commit is contained in:
zyronon
2023-09-07 18:47:57 +08:00
parent c663f6f90e
commit cf14d527dc
10 changed files with 163 additions and 380 deletions

View File

@@ -4,7 +4,7 @@ import Statistics from "@/components/Modal/Statistics.vue";
import {onMounted, watch} from "vue";
import {useBaseStore} from "@/stores/base.ts";
import {SaveKey} from "@/types.ts"
import Practice from "@/components/Practice.vue"
import Practice from "@/components/Practice/Practice.vue"
const store = useBaseStore()
// 查询当前系统主题颜色

View File

@@ -5,32 +5,25 @@ import {onMounted, onUnmounted} from "vue"
import {useBaseStore} from "@/stores/base.ts"
import Tooltip from "@/components/Tooltip.vue"
import {Down} from "@icon-park/vue-next"
import {usePracticeStore} from "@/components/Practice/usePracticeStore.ts";
interface IProps {
total: number,
inputNumber: number
wrongNumber: number
correctRate: number
}
const props = defineProps()
const practiceStore = usePracticeStore()
const store = useBaseStore()
function format(val: number, suffix: string = '') {
return val === -1 ? '-' : (val + suffix)
function format(val: number, suffix: string = '', check: number = -1) {
return val === check ? '-' : (val + suffix)
}
const progress = $computed(() => {
if (!store.chapter.length) return 0
return ((store.current.index / store.current.statistics.wordNumber) * 100)
if (!practiceStore.total) return 0
return ((practiceStore.inputNumber / practiceStore.total) * 100)
})
let speedMinute = $ref(0)
let timer = $ref(0)
onMounted(() => {
timer = setInterval(() => {
speedMinute = Math.floor((Date.now() - store.current.statistics.startDate) / 1000 / 60)
speedMinute = Math.floor((Date.now() - practiceStore.startDate) / 1000 / 60)
}, 1000)
})
@@ -60,22 +53,22 @@ onUnmounted(() => {
<div class="name">时间</div>
</div>
<div class="row">
<div class="num">{{ store.current.statistics.wordNumber }}</div>
<div class="num">{{ practiceStore.total }}</div>
<div class="line"></div>
<div class="name">单词总数</div>
</div>
<div class="row">
<div class="num">{{ store.current.index }}</div>
<div class="num">{{ format(practiceStore.inputNumber, '', 0) }}</div>
<div class="line"></div>
<div class="name">输入数</div>
</div>
<div class="row">
<div class="num">{{ format(store.current.wrongWords.length) }}</div>
<div class="num">{{ format(practiceStore.wrongNumber, '', 0) }}</div>
<div class="line"></div>
<div class="name">错误数</div>
</div>
<div class="row">
<div class="num">{{ format(store.current.statistics.correctRate, '%') }}</div>
<div class="num">{{ format(practiceStore.correctRate, '%') }}</div>
<div class="line"></div>
<div class="name">正确率</div>
</div>
@@ -93,7 +86,7 @@ onUnmounted(() => {
@import "@/assets/css/colors.scss";
.footer {
width: 100%;
width: var(--toolbar-width);
margin-bottom: 30rem;
transition: all .3s;
position: relative;

View File

@@ -1,172 +0,0 @@
<script setup lang="ts">
import {$computed, $ref} from "vue/macros"
import {onMounted, onUnmounted} from "vue"
import {useBaseStore} from "@/stores/base.ts"
import Tooltip from "@/components/Tooltip.vue"
import {Down} from "@icon-park/vue-next"
interface IProps {
total: number,
startDate: number
inputNumber: number
wrongNumber: number
correctRate: number
}
const props = withDefaults(defineProps<IProps>(), {
total: 0,
startDate: Date.now(),
inputNumber: 0,
wrongNumber: 0,
correctRate: 0,
})
const store = useBaseStore()
function format(val: number, suffix: string = '', check: number = -1) {
return val === check ? '-' : (val + suffix)
}
const progress = $computed(() => {
if (!props.total) return 0
return ((props.inputNumber / props.total) * 100)
})
let speedMinute = $ref(0)
let timer = $ref(0)
onMounted(() => {
timer = setInterval(() => {
speedMinute = Math.floor((Date.now() - props.startDate) / 1000 / 60)
}, 1000)
})
onUnmounted(() => {
timer && clearInterval(timer)
})
</script>
<template>
<div class="footer" :class="!store.setting.showToolbar && 'hide'">
<Tooltip :title="store.setting.showToolbar?'收起':'展开'">
<Down
@click="store.setting.showToolbar = !store.setting.showToolbar"
class="arrow"
:class="!store.setting.showToolbar && 'down'"
theme="outline" size="24" fill="#999"/>
</Tooltip>
<div class="bottom">
<el-progress :percentage="progress"
:stroke-width="8"
:show-text="false"/>
<div class="stat">
<div class="row">
<div class="num">{{ speedMinute }}分钟</div>
<div class="line"></div>
<div class="name">时间</div>
</div>
<div class="row">
<div class="num">{{ total }}</div>
<div class="line"></div>
<div class="name">单词总数</div>
</div>
<div class="row">
<div class="num">{{ format(inputNumber, '', 0) }}</div>
<div class="line"></div>
<div class="name">输入数</div>
</div>
<div class="row">
<div class="num">{{ format(props.wrongNumber, '', 0) }}</div>
<div class="line"></div>
<div class="name">错误数</div>
</div>
<div class="row">
<div class="num">{{ format(props.correctRate, '%') }}</div>
<div class="line"></div>
<div class="name">正确率</div>
</div>
</div>
</div>
<div class="progress">
<el-progress :percentage="progress"
:stroke-width="8"
:show-text="false"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/colors.scss";
.footer {
width: var(--toolbar-width);
margin-bottom: 30rem;
transition: all .3s;
position: relative;
margin-top: 30rem;
&.hide {
margin-bottom: -90rem;
margin-top: 65rem;
.arrow {
transform: translate3d(-50%, -220%, 0) rotate(180deg);
}
.progress {
bottom: calc(100% + 30rem);
}
}
.arrow {
position: absolute;
top: 0;
left: 50%;
cursor: pointer;
transition: all .3s;
transform: translate3d(-50%, -100%, 0) rotate(0);
padding: 5rem;
}
.bottom {
position: relative;
width: 100%;
box-sizing: border-box;
border-radius: 10rem;
background: var(--color-header-bg);
padding: 2rem 10rem 10rem 10rem;
z-index: 2;
.stat {
margin-top: 10rem;
display: flex;
justify-content: space-around;
.row {
display: flex;
flex-direction: column;
align-items: center;
gap: 10rem;
width: 80rem;
.line {
height: 1px;
width: 100%;
background: gainsboro;
}
}
}
}
.progress {
width: 100%;
transition: all .3s;
padding: 0 10rem;
box-sizing: border-box;
position: absolute;
bottom: 0;
}
}
</style>

View File

@@ -1,15 +1,32 @@
<script setup lang="ts">
import Toolbar from "@/components/Toolbar/Toolbar.vue"
import TypeArticle from "@/components/TypeArticle.vue"
import {watch} from "vue";
import {usePracticeStore} from "@/components/Practice/usePracticeStore.ts";
import Footer from "@/components/Practice/Footer.vue";
import TypeWord from "@/components/Practice/TypeWord.vue";
import TypeArticle from "@/components/Practice/TypeArticle.vue";
const practiceStore = usePracticeStore()
watch(practiceStore, () => {
if (practiceStore.inputNumber < 1) {
return practiceStore.correctRate = -1
}
if (practiceStore.wrongNumber > practiceStore.inputNumber) {
return practiceStore.correctRate = 0
}
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongNumber) / (practiceStore.inputNumber)) * 100)
})
</script>
<template>
<div class="practice">
<Toolbar/>
<TypeArticle/>
<TypeArticle v-if="practiceStore.type === 'article'"/>
<TypeWord v-else/>
<Footer/>
</div>
</template>
@@ -22,5 +39,4 @@ import TypeArticle from "@/components/TypeArticle.vue"
justify-content: center;
align-items: center;
}
</style>

View File

@@ -3,26 +3,24 @@
import {usePlayWordAudio} from "@/hooks/usePlayWordAudio.ts"
import {computed, nextTick, onMounted, reactive, watch} from "vue"
import {cloneDeep} from "lodash"
import 快速打字的机械键盘声音Mp3 from '../assets/sound/key-sounds/快速打字的机械键盘声音.mp3'
import 键盘快速打字的声音Mp3 from '../assets/sound/key-sounds/键盘快速打字的声音.mp3'
import 电话打字的声音Mp3 from '../assets/sound/key-sounds/电话打字的声音.mp3'
import 老式机械 from '../assets/sound/key-sounds/老式机械.mp3'
import 机械0 from '../assets/sound/key-sounds/jixie/机械0.mp3'
import 机械1 from '../assets/sound/key-sounds/jixie/机械1.mp3'
import 机械2 from '../assets/sound/key-sounds/jixie/机械2.mp3'
import 机械3 from '../assets/sound/key-sounds/jixie/机械3.mp3'
import beep from '../assets/sound/beep.wav'
import correct from '../assets/sound/correct.wav'
import 快速打字的机械键盘声音Mp3 from '../..//assets/sound/key-sounds/快速打字的机械键盘声音.mp3'
import 键盘快速打字的声音Mp3 from '../..//assets/sound/key-sounds/键盘快速打字的声音.mp3'
import 电话打字的声音Mp3 from '../..//assets/sound/key-sounds/电话打字的声音.mp3'
import 老式机械 from '../..//assets/sound/key-sounds/老式机械.mp3'
import 机械0 from '../..//assets/sound/key-sounds/jixie/机械0.mp3'
import 机械1 from '../..//assets/sound/key-sounds/jixie/机械1.mp3'
import 机械2 from '../..//assets/sound/key-sounds/jixie/机械2.mp3'
import 机械3 from '../..//assets/sound/key-sounds/jixie/机械3.mp3'
import beep from '../..//assets/sound/beep.wav'
import correct from '../..//assets/sound/correct.wav'
import {useSound} from "@/hooks/useSound.ts"
import {CnKeyboardMap, useSplitArticle} from "@/hooks/useSplitArticle";
import {$computed, $ref} from "vue/macros";
import {Article, DictType, SaveKey, Sentence, ShortKeyMap, Word} from "@/types";
import {Article, DefaultWord, DictType, SaveKey, Sentence, ShortKeyMap, Word} from "@/types";
import {useBaseStore} from "@/stores/base";
import Footer2 from "@/components/Footer2.vue"
import {Swiper, SwiperSlide} from "swiper/vue";
import 'swiper/css';
import {Swiper as SwiperClass} from "swiper/types";
import Type2 from "@/components/Type2.vue"
import Footer from "@/components/Practice/Footer.vue"
import {usePracticeStore} from "@/components/Practice/usePracticeStore.ts";
import {useEventListener} from "@/hooks/useEvent.ts";
let article1 = `How does the older investor differ in his approach to investment from the younger investor?
There is no shortage of tipsters around offering 'get-rich-quick' opportunities. But if you are a serious private investor, leave the Las Vegas mentality to those with money to fritter. The serious investor needs a proper 'portfolio' -- a well-planned selection of investments, with a definite structure and a clear aim. But exactly how does a newcomer to the stock market go about achieving that?
@@ -48,9 +46,6 @@ Whether the remarkable growth of organized camping means the eventual death of t
NIGEL BUXTON The Great Escape from The Weekend Telegraph`
// article2 = `Economy is one powerful motive for camping? since after the initial outlay upon equipment, or through hiring it, the total expense can be far less than the cost of hotels. But, contrary to a popular assumption, it is far from being the only one, or even the greatest. The man who manoeuvres carelessly into his twenty pounds' worth of space at one of Europe's myriad permanent sites may find himself bumping a Bentley. More likely, Ford Escort will be hub to hub with Renault or Mercedes, but rarely with bicycles made for two.`
let isPlay = $ref(false)
let inputRef = $ref<HTMLInputElement>(null)
let articleWrapperRef = $ref<HTMLInputElement>(null)
const [playAudio] = usePlayWordAudio()
// const [playKeySound, setAudio] = useSound([机械0, 机械1, 机械2, 机械3], 1)
@@ -60,7 +55,10 @@ const [playBeep] = useSound([beep], 1)
const [playCorrect] = useSound([correct], 1)
const store = useBaseStore()
const practiceStore = usePracticeStore()
let isPlay = $ref(false)
let articleWrapperRef = $ref<HTMLInputElement>(null)
let sectionIndex = $ref(0)
let sentenceIndex = $ref(0)
let wordIndex = $ref(6)
@@ -74,14 +72,6 @@ let hoverIndex = $ref({
sectionIndex: 0,
sentenceIndex: 0,
})
let statistics = $ref({
wrongWords: [],
total: 0,
startDate: 0,
inputNumber: 0,
wrongNumber: 0,
correctRate: -1,
})
let article = reactive<Article>({
article: article1,
@@ -93,29 +83,19 @@ let article = reactive<Article>({
translate: [],
})
watch(statistics, () => {
if (statistics.inputNumber < 1) {
return statistics.correctRate = -1
}
if (statistics.wrongNumber > statistics.inputNumber) {
return statistics.correctRate = 0
}
statistics.correctRate = 100 - Math.trunc(((statistics.wrongNumber) / (statistics.inputNumber)) * 100)
})
onMounted(() => {
let sections = useSplitArticle(article.article)
let wordNumber = 0
practiceStore.total = 0
sections.map(v => {
v.map(w => {
w.words.map(s => {
if (!store.skipWordNamesWithSimpleWords.includes(s.toLowerCase())) {
wordNumber++
practiceStore.total++
}
})
})
})
statistics.total = wordNumber
statistics.startDate = Date.now()
practiceStore.startDate = Date.now()
let temp = useSplitArticle(article.articleTranslate, 'cn', CnKeyboardMap)
temp.map((v, i) => {
@@ -168,9 +148,6 @@ function play() {
window.speechSynthesis.speak(msg);
}
function focus() {
inputRef.focus()
}
const currentIndex = computed(() => {
return `${sectionIndex}${sentenceIndex}${wordIndex}`
@@ -178,9 +155,7 @@ const currentIndex = computed(() => {
function onKeyDown(e: KeyboardEvent) {
// console.log('keyDown', e.key, e.code, e.keyCode)
wrong = ''
let currentSection = article.sections[sectionIndex]
let currentSentence = currentSection[sentenceIndex]
let currentWord = currentSentence.words[wordIndex]
@@ -191,7 +166,7 @@ function onKeyDown(e: KeyboardEvent) {
index = 0
wordIndex++
if (!store.skipWordNamesWithSimpleWords.includes(currentWord.toLowerCase())) {
statistics.inputNumber++
practiceStore.inputNumber++
}
playCorrect()
@@ -269,9 +244,9 @@ function onKeyDown(e: KeyboardEvent) {
}
if (!store.skipWordNamesWithSimpleWords.includes(currentWord.toLowerCase())) {
if (!statistics.wrongWords.find((v) => v.toLowerCase() === currentWord.toLowerCase())) {
statistics.wrongWords.push(currentWord)
statistics.wrongNumber++
if (!practiceStore.wrongWords.find((v) => v.name.toLowerCase() === currentWord.toLowerCase())) {
practiceStore.wrongWords.push(word)
practiceStore.wrongNumber++
}
}
@@ -317,7 +292,6 @@ function onKeyDown(e: KeyboardEvent) {
// 'wordIndex', wordIndex,
// 'index', index,
// )
inputRef.value = ''
e.preventDefault()
}
@@ -328,6 +302,9 @@ function onKeyUp() {
}
}
useEventListener('keydown', onKeyDown)
useEventListener('keyup', onKeyUp)
function playWord(word: string) {
playAudio(word)
}
@@ -435,25 +412,12 @@ function otherWord(word: string, i: number, i2: number, i3: number) {
</div>
</div>
<div class="swiper-item">
<!-- <Type2-->
<!-- />-->
<!-- <Type2-->
<!-- />-->
</div>
</div>
</div>
<input ref="inputRef"
class="inputEl"
type="text"
@keyup="onKeyUp"
@keydown="onKeyDown">
</div>
<Footer2
:total="statistics.total"
:startDate="statistics.startDate"
:inputNumber="statistics.inputNumber"
:wrongNumber="statistics.wrongNumber"
:correctRate="statistics.correctRate"
/>
</template>
<style scoped lang="scss">
@@ -558,11 +522,6 @@ function otherWord(word: string, i: number, i2: number, i3: number) {
}
}
.inputEl {
position: fixed;
//left: 100vw;
}
.swiper-wrapper {
height: 100%;
width: 100%;
@@ -579,6 +538,4 @@ function otherWord(word: string, i: number, i2: number, i3: number) {
}
}
}
</style>

View File

@@ -25,6 +25,42 @@ import {
import {emitter, EventKey} from "@/utils/eventBus.ts"
import {cloneDeep} from "lodash"
import {usePracticeStore} from "@/components/Practice/usePracticeStore.ts"
import {useEventListener} from "@/hooks/useEvent.ts";
interface IProps {
words: Word[],
index: number,
}
const props = withDefaults(defineProps<IProps>(), {
words: [],
index: -1
})
let data = $ref({
index: props.index,
words: props.words,
wrongWords: [],
originWrongWords: [],
repeatNumber: 0,
startDate: Date.now(),
correctRate: -1,
})
watch(() => props.words, (n: Word[]) => {
data.words = n
data.index = n.length ? 0 : -1
})
let word = $computed(() => {
return data.words[data.index] ?? {
trans: [],
name: '',
usphone: '',
ukphone: '',
}
})
let input = $ref('')
let wrong = $ref('')
@@ -42,43 +78,38 @@ const [playAudio] = usePlayWordAudio()
const practiceStore = usePracticeStore()
let resetWord = $computed(() => {
return practiceStore.word.name.slice(input.length + wrong.length)
return word.name.slice(input.length + wrong.length)
})
onMounted(() => {
window.addEventListener('keydown', onKeyDown)
window.addEventListener('keyup', onKeyUp)
emitter.on(EventKey.resetWord, () => {
input = ''
})
})
onUnmounted(() => {
// console.log('onUnmounted')
window.removeEventListener('keydown', onKeyDown)
window.removeEventListener('keyup', onKeyUp)
})
useEventListener('keydown', onKeyDown)
useEventListener('keyup', onKeyUp)
function next() {
if (practiceStore.index === practiceStore.words.length - 1) {
if (practiceStore.wrongWords.length) {
practiceStore.words = cloneDeep(practiceStore.wrongWords)
if (!practiceStore.originWrongWords.length) {
practiceStore.originWrongWords = cloneDeep(practiceStore.wrongWords)
if (data.index === data.words.length - 1) {
if (data.wrongWords.length) {
data.words = cloneDeep(data.wrongWords)
if (!data.originWrongWords.length) {
data.originWrongWords = cloneDeep(data.wrongWords)
}
practiceStore.index = 0
practiceStore.repeatNumber++
practiceStore.wrongWords = []
data.index = 0
data.repeatNumber++
data.wrongWords = []
} else {
console.log('这本书完了')
emitter.emit(EventKey.openStatModal)
}
} else {
practiceStore.index++
data.index++
console.log('这个词完了')
if ([DictType.customDict, DictType.innerDict].includes(store.current.dictType)
&& store.skipWordNames.includes(practiceStore.word.name.toLowerCase())) {
&& store.skipWordNames.includes(word.name.toLowerCase())) {
next()
}
}
@@ -94,18 +125,18 @@ async function onKeyDown(e: KeyboardEvent) {
//TODO 还有横杠
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.code === 'Space') {
let letter = e.key
if ((input + letter).toLowerCase() === practiceStore.word.name.toLowerCase().slice(0, input.length + 1)) {
if ((input + letter).toLowerCase() === word.name.toLowerCase().slice(0, input.length + 1)) {
input += letter
wrong = ''
playKeySound()
} else {
if (!store.wrongWordDict.originWords.find((v: Word) => v.name.toLowerCase() === practiceStore.word.name.toLowerCase())) {
store.wrongWordDict.originWords.push(practiceStore.word)
store.wrongWordDict.words.push(practiceStore.word)
if (!store.wrongWordDict.originWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
store.wrongWordDict.originWords.push(word)
store.wrongWordDict.words.push(word)
store.wrongWordDict.chapterWords = [store.wrongWordDict.words]
}
if (!practiceStore.wrongWords.find((v: Word) => v.name.toLowerCase() === practiceStore.word.name.toLowerCase())) {
practiceStore.wrongWords.push(practiceStore.word)
if (!data.wrongWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
data.wrongWords.push(word)
}
wrong = letter
playKeySound()
@@ -114,7 +145,7 @@ async function onKeyDown(e: KeyboardEvent) {
wrong = ''
}, 500)
}
if (input.toLowerCase() === practiceStore.word.name.toLowerCase()) {
if (input.toLowerCase() === word.name.toLowerCase()) {
playCorrect()
setTimeout(next, 300)
}
@@ -129,17 +160,17 @@ async function onKeyDown(e: KeyboardEvent) {
}
break
case ShortKeyMap.Collect:
if (!store.newWordDict.originWords.find((v: Word) => v.name === practiceStore.word.name)) {
store.newWordDict.originWords.push(practiceStore.word)
store.newWordDict.words.push(practiceStore.word)
if (!store.newWordDict.originWords.find((v: Word) => v.name === word.name)) {
store.newWordDict.originWords.push(word)
store.newWordDict.words.push(word)
store.newWordDict.chapterWords = [store.newWordDict.words]
}
activeIndex = 1
break
case ShortKeyMap.Remove:
if (!store.skipWordNames.includes(practiceStore.word.name)) {
store.skipWordDict.originWords.push(practiceStore.word)
store.skipWordDict.words.push(practiceStore.word)
if (!store.skipWordNames.includes(word.name)) {
store.skipWordDict.originWords.push(word)
store.skipWordDict.words.push(word)
store.skipWordDict.chapterWords = [store.skipWordDict.words]
}
activeIndex = 0
@@ -163,7 +194,7 @@ async function onKeyDown(e: KeyboardEvent) {
<template>
<div class="type-word">
<div class="translate">{{ practiceStore.word.trans.join('') }}</div>
<div class="translate">{{ word.trans.join('') }}</div>
<div class="word-wrapper">
<div class="word" :class="wrong && 'is-wrong'">
<span class="input" v-if="input">{{ input }}</span>
@@ -175,9 +206,9 @@ async function onKeyDown(e: KeyboardEvent) {
</template>
<span class="letter" v-else>{{ resetWord }}</span>
</div>
<div class="audio" @click="playAudio(practiceStore.word.name)">播放</div>
<div class="audio" @click="playAudio(word.name)">播放</div>
</div>
<div class="phonetic">{{ practiceStore.word.usphone }}</div>
<div class="phonetic">{{ word.usphone }}</div>
<div class="options">
<BaseButton keyboard="`" :active="activeIndex === 0">
忽略
@@ -196,6 +227,7 @@ async function onKeyDown(e: KeyboardEvent) {
@import "@/assets/css/colors.scss";
.type-word {
flex: 1;
display: flex;
//display: none;
align-items: center;

View File

@@ -1,43 +0,0 @@
<script setup lang="ts">
import TypeWord from "@/components/TypeWord.vue"
import {onMounted, watch} from "vue"
import {$ref} from "vue/macros"
import Footer2 from "@/components/Footer2.vue"
import {usePracticeStore} from "@/components/Practice/usePracticeStore.ts"
const store = usePracticeStore()
watch(store.$state, () => {
if (store.index < 1) {
return store.correctRate = -1
}
if (store.wrongWords.length > store.index) {
return store.correctRate = 0
}
store.correctRate = 100 - Math.trunc((store.wrongWords.length / store.index) * 100)
})
onMounted(() => {
})
</script>
<template>
<TypeWord
v-model:data="data"
/>
<Footer2
:total="data.words.length"
:startDate="data.startDate"
:inputNumber="data.index"
:wrongNumber="data.wrongWords"
:correctRate="data.correctRate"
/>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,42 +1,28 @@
import {defineStore} from "pinia"
import {State, Word} from "@/types.ts"
import {Word} from "@/types.ts"
export interface PracticeState {
index: number,
words: Word[],
wrongWords: Word[],
originWrongWords: Word[],
repeatNumber: number,
startDate: number,
correctRate: number,
total: number,
inputNumber: number,
wrongNumber: number,
type: 'word' | 'article',
wrongWords: Word[],
repeatNumber: number,
startDate: number,
total: number,
inputNumber: number,
wrongNumber: number,
correctRate: number,
}
export const usePracticeStore = defineStore('practice', {
state: (): PracticeState => {
return {
index: -1,
words: [],
wrongWords: [],
originWrongWords: [],
repeatNumber: 0,
startDate: Date.now(),
correctRate: -1,
total: -1,
inputNumber: -1,
wrongNumber: -1,
}
},
getters: {
word(state: PracticeState): Word {
return state.words[state.index] ?? {
trans: [],
name: '',
usphone: '',
ukphone: '',
}
},
state: (): PracticeState => {
return {
type: 'word',
wrongWords: [],
repeatNumber: 0,
startDate: Date.now(),
correctRate: -1,
total: -1,
inputNumber: -1,
wrongNumber: -1,
}
},
})

7
src/hooks/useEvent.ts Normal file
View File

@@ -0,0 +1,7 @@
// event.js
import {onMounted, onUnmounted} from 'vue'
export function useEventListener(type: string, listener: EventListenerOrEventListenerObject) {
onMounted(() => window.addEventListener(type, listener))
onUnmounted(() => window.removeEventListener(type, listener))
}

View File

@@ -5,7 +5,14 @@ export type Word = {
"trans": string[]
}
export const SaveKey = 'bb-word-config'
export const DefaultWord: Word = {
name: '',
usphone: '',
ukphone: '',
trans: []
}
export const SaveKey = 'type-word-config'
export const PronunciationApi = 'https://dict.youdao.com/dictvoice?audio='