重构数据结构
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"element-plus": "^2.3.9",
|
||||
"hover.css": "^2.3.2",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.6",
|
||||
"swiper": "^10.1.0",
|
||||
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -14,6 +14,9 @@ dependencies:
|
||||
hover.css:
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2
|
||||
localforage:
|
||||
specifier: ^1.10.0
|
||||
version: 1.10.0
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
@@ -1191,6 +1194,10 @@ packages:
|
||||
resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
|
||||
dev: false
|
||||
|
||||
/immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
dev: false
|
||||
|
||||
/immutable@4.3.2:
|
||||
resolution: {integrity: sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==}
|
||||
dev: true
|
||||
@@ -1256,11 +1263,23 @@ packages:
|
||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
||||
dev: true
|
||||
|
||||
/lie@3.1.1:
|
||||
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
dev: false
|
||||
|
||||
/local-pkg@0.4.3:
|
||||
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/localforage@1.10.0:
|
||||
resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
|
||||
dependencies:
|
||||
lie: 3.1.1
|
||||
dev: false
|
||||
|
||||
/locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
265
src/App.vue
265
src/App.vue
@@ -1,166 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, onUnmounted, provide, watch} from "vue"
|
||||
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 {$ref} from "vue/macros"
|
||||
import {useSound} from "@/hooks/useSound.ts"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {DictType, SaveKey, Word} from "./types";
|
||||
import WordList from "./components/WordList.vue";
|
||||
import Side from "@/components/Side.vue"
|
||||
import {usePlayWordAudio} from "@/hooks/usePlayWordAudio.ts"
|
||||
import DictModal from "@/components/Modal/DictModal.vue"
|
||||
import Backgorund from "@/components/Backgorund.vue"
|
||||
|
||||
import Toolbar from "@/components/Toolbar/Toolbar.vue";
|
||||
import Type from "@/components/Type.vue";
|
||||
import Side from "@/components/Side.vue";
|
||||
import Statistics from "@/components/Modal/Statistics.vue";
|
||||
import useThemeColor from "@/hooks/useThemeColor";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import Toolbar from "@/components/Toolbar/Toolbar.vue"
|
||||
import {KeyboardOne} from "@icon-park/vue-next";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
|
||||
let input = $ref('')
|
||||
let wrong = $ref('')
|
||||
let showFullWord = $ref(false)
|
||||
let isDictation = $ref(false)
|
||||
let activeIndex = $ref(-1)
|
||||
const store = useBaseStore()
|
||||
|
||||
// const [playKeySound, setAudio] = useSound([机械0, 机械1, 机械2, 机械3], 1)
|
||||
// const [playKeySound, setAudio] = useSound([老式机械], 3)
|
||||
const [playKeySound, setAudio] = useSound([电话打字的声音Mp3], 3)
|
||||
const [playBeep] = useSound([beep], 1)
|
||||
const [playCorrect] = useSound([correct], 1)
|
||||
const [playAudio] = usePlayWordAudio()
|
||||
|
||||
const keyMap = {
|
||||
Show: 'Escape',
|
||||
Ignore: 'Tab',
|
||||
Remove: '`',
|
||||
Collect: 'Enter',
|
||||
}
|
||||
|
||||
const restWord = $computed(() => {
|
||||
return store.word.name.slice(input.length + wrong.length)
|
||||
})
|
||||
onMounted(() => {
|
||||
store.init()
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
window.addEventListener('keyup', onKeyUp)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// console.log('onUnmounted')
|
||||
window.removeEventListener('keydown', onKeyDown)
|
||||
window.removeEventListener('keyup', onKeyUp)
|
||||
})
|
||||
|
||||
watch(store.$state, (n) => {
|
||||
localStorage.setItem(SaveKey, JSON.stringify(n))
|
||||
})
|
||||
|
||||
function next() {
|
||||
if (store.currentDict.wordIndex === store.chapter.length - 1) {
|
||||
if (store.currentDict.chapterIndex !== store.currentDict.chapterList.length - 1) {
|
||||
store.currentDict.wordIndex = 0
|
||||
store.currentDict.chapterIndex++
|
||||
console.log('这一章节完了')
|
||||
} else {
|
||||
console.log('这本书完了')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
store.currentDict.wordIndex++
|
||||
|
||||
// var msg = new SpeechSynthesisUtterance();
|
||||
// // msg.text = store.word.name
|
||||
// msg.text = 'Hawaii wildfires burn historic town of Lahaina to the ground'
|
||||
// msg.rate = 0.8;
|
||||
// msg.pitch = 1;
|
||||
// msg.lang = 'en-US';
|
||||
// window.speechSynthesis.speak(msg);
|
||||
|
||||
console.log('这个词完了')
|
||||
}
|
||||
if ([DictType.custom, DictType.inner].includes(store.currentDictType.name) && store.skipWordNames.includes(store.word.name)) {
|
||||
next()
|
||||
}
|
||||
wrong = input = ''
|
||||
}
|
||||
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
showFullWord = false
|
||||
}
|
||||
|
||||
async function onKeyDown(e: KeyboardEvent) {
|
||||
//TODO 还有横杠
|
||||
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.code === 'Space') {
|
||||
let letter = e.key
|
||||
if ((input + letter).toLowerCase() === store.word.name.toLowerCase().slice(0, input.length + 1)) {
|
||||
input += letter
|
||||
wrong = ''
|
||||
playKeySound()
|
||||
} else {
|
||||
wrong = letter
|
||||
playKeySound()
|
||||
playBeep()
|
||||
setTimeout(() => {
|
||||
wrong = ''
|
||||
// wrong = input = ''
|
||||
}, 500)
|
||||
}
|
||||
if (input.toLowerCase() === store.word.name.toLowerCase()) {
|
||||
playCorrect()
|
||||
setTimeout(next, 300)
|
||||
}
|
||||
} else {
|
||||
// console.log('e', e)
|
||||
switch (e.key) {
|
||||
case 'Backspace':
|
||||
if (wrong) {
|
||||
wrong = ''
|
||||
} else {
|
||||
input = input.slice(0, -1)
|
||||
}
|
||||
break
|
||||
case keyMap.Collect:
|
||||
if (!store.newWordDict.wordList.find((v: Word) => v.name === store.word.name)) {
|
||||
store.newWordDict.wordList.push(store.word)
|
||||
}
|
||||
activeIndex = 1
|
||||
break
|
||||
case keyMap.Remove:
|
||||
if (!store.skipWordNames.includes(store.word.name)) {
|
||||
store.skipWordDict.wordList.push(store.word)
|
||||
}
|
||||
activeIndex = 0
|
||||
next()
|
||||
break
|
||||
case keyMap.Ignore:
|
||||
e.preventDefault()
|
||||
activeIndex = 2
|
||||
next()
|
||||
break
|
||||
case keyMap.Show:
|
||||
showFullWord = true
|
||||
break
|
||||
}
|
||||
setTimeout(() => {
|
||||
activeIndex = -1
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const show = $ref(false)
|
||||
const {appearance, toggle} = useThemeColor()
|
||||
import DictModal from "@/components/Modal/DictModal.vue";
|
||||
|
||||
</script>
|
||||
|
||||
@@ -170,34 +14,7 @@ const {appearance, toggle} = useThemeColor()
|
||||
<div class="center">
|
||||
<Toolbar/>
|
||||
<div class="content">
|
||||
<div class="type-word">
|
||||
<div class="translate">{{ store.word.trans.join(';') }}</div>
|
||||
<div class="word-wrapper">
|
||||
<div class="word" :class="wrong&&'is-wrong'">
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="isDictation">
|
||||
<span class="letter" v-if="!showFullWord"
|
||||
@mouseenter="showFullWord = true">{{ restWord.split('').map(v => '_').join('') }}</span>
|
||||
<span class="letter" v-else @mouseleave="showFullWord = false">{{ restWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ restWord }}</span>
|
||||
</div>
|
||||
<div class="audio" @click="playAudio(store.word.name)">播放</div>
|
||||
</div>
|
||||
<div class="phonetic">{{ store.word.usphone }}</div>
|
||||
<div class="options">
|
||||
<BaseButton keyboard="`" :active="activeIndex === 0">
|
||||
忽略
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Enter" :active="activeIndex === 1">
|
||||
收藏
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Tab" :active="activeIndex === 2">
|
||||
下一个
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<Type/>
|
||||
</div>
|
||||
<Side/>
|
||||
</div>
|
||||
@@ -236,78 +53,8 @@ const {appearance, toggle} = useThemeColor()
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.type-word {
|
||||
display: flex;
|
||||
//display: none;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
gap: 2rem;
|
||||
|
||||
.options {
|
||||
margin-top: 10rem;
|
||||
display: flex;
|
||||
gap: 15rem;
|
||||
font-size: 18rem;
|
||||
|
||||
}
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 20rem;
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
|
||||
.word {
|
||||
font-size: 48rem;
|
||||
line-height: 1;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
letter-spacing: 5rem;
|
||||
|
||||
.input {
|
||||
color: rgba(74, 222, 128, 0.8);
|
||||
}
|
||||
|
||||
.wrong {
|
||||
color: rgba(red, 0.6);
|
||||
}
|
||||
|
||||
&.is-wrong {
|
||||
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10%,
|
||||
90% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
}
|
||||
|
||||
20%,
|
||||
80% {
|
||||
transform: translate3d(2px, 0, 0);
|
||||
}
|
||||
|
||||
30%,
|
||||
50%,
|
||||
70% {
|
||||
transform: translate3d(-4px, 0, 0);
|
||||
}
|
||||
|
||||
40%,
|
||||
60% {
|
||||
transform: translate3d(4px, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,13 +20,9 @@ function slideTo(index: number) {
|
||||
swiperIns0.slideTo(tabIndex = index)
|
||||
}
|
||||
|
||||
|
||||
function changeDict(dict: Dict, i: number) {
|
||||
if (store.currentDictType.name !== dict.type) {
|
||||
store.currentDictType = {
|
||||
name: dict.type,
|
||||
dictUrl: dict.url
|
||||
}
|
||||
if (store.currentDictType !== dict.type) {
|
||||
store.currentDictType = dict.type
|
||||
}
|
||||
store.currentDict.wordIndex = i
|
||||
}
|
||||
@@ -55,7 +51,7 @@ function changeDict(dict: Dict, i: number) {
|
||||
:isActive="store.sideIsOpen && tabIndex === 0"
|
||||
:list="store.dict.chapterList[store.dict.chapterIndex]??[]"
|
||||
:activeIndex="store.dict.wordIndex"/>
|
||||
<footer v-if="![DictType.custom,DictType.inner].includes(store.currentDictType.name)">
|
||||
<footer v-if="![DictType.custom,DictType.inner].includes(store.currentDictType)">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.dict)"
|
||||
@@ -78,7 +74,7 @@ function changeDict(dict: Dict, i: number) {
|
||||
:isActive="store.sideIsOpen && tabIndex === 1"
|
||||
:list="store.newWordDict.wordList"
|
||||
:activeIndex="store.newWordDict.wordIndex"/>
|
||||
<footer v-if="store.currentDictType.name !== DictType.newWordDict && store.newWordDict.wordList.length">
|
||||
<footer v-if="store.currentDictType !== DictType.newWordDict && store.newWordDict.wordList.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.newWordDict)"
|
||||
@@ -90,6 +86,29 @@ function changeDict(dict: Dict, i: number) {
|
||||
</footer>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ store.wrongDict.wordList.length }}</div>
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.wrongDict,-1,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 2"
|
||||
:list="store.wrongDict.wordList"
|
||||
:activeIndex="store.wrongDict.wordIndex"/>
|
||||
<footer v-if="store.currentDictType !== DictType.wrongDict && store.wrongDict.wordList.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.wrongDict)"
|
||||
>
|
||||
<div class="my-button hvr-grow">
|
||||
切换
|
||||
</div>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
@@ -98,10 +117,10 @@ function changeDict(dict: Dict, i: number) {
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.skipWordDict,-1,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 2"
|
||||
:isActive="store.sideIsOpen && tabIndex === 3"
|
||||
:list="store.skipWordDict.wordList"
|
||||
:activeIndex="store.skipWordDict.wordIndex"/>
|
||||
<footer v-if="store.currentDictType.name !== DictType.skipWordDict && store.skipWordDict.wordList.length">
|
||||
<footer v-if="store.currentDictType !== DictType.skipWordDict && store.skipWordDict.wordList.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.skipWordDict)"
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
SettingTwo,
|
||||
SunOne,
|
||||
VolumeNotice,
|
||||
Bug
|
||||
Bug,
|
||||
UploadOne
|
||||
} from "@icon-park/vue-next"
|
||||
import useThemeColor from "@/hooks/useThemeColor.ts"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
@@ -72,6 +73,11 @@ const showSettingModal = $ref(false)
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="反馈">
|
||||
<IconWrapper>
|
||||
<upload-one/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<Tooltip title="反馈">
|
||||
<IconWrapper>
|
||||
<bug @click="showFeedbackModal = true"/>
|
||||
|
||||
275
src/components/Type.vue
Normal file
275
src/components/Type.vue
Normal file
@@ -0,0 +1,275 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, onUnmounted, provide, watch} from "vue"
|
||||
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 {$ref} from "vue/macros"
|
||||
import {useSound} from "@/hooks/useSound.ts"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {DictType, SaveKey, Word} from "../types";
|
||||
import WordList from "../components/WordList.vue";
|
||||
import Side from "@/components/Side.vue"
|
||||
import {usePlayWordAudio} from "@/hooks/usePlayWordAudio.ts"
|
||||
import DictModal from "@/components/Modal/DictModal.vue"
|
||||
import Backgorund from "@/components/Backgorund.vue"
|
||||
import Statistics from "@/components/Modal/Statistics.vue";
|
||||
import useThemeColor from "@/hooks/useThemeColor";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import Toolbar from "@/components/Toolbar/Toolbar.vue"
|
||||
import {KeyboardOne} from "@icon-park/vue-next";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import Type from "@/components/Type.vue";
|
||||
|
||||
let input = $ref('')
|
||||
let wrong = $ref('')
|
||||
let showFullWord = $ref(false)
|
||||
let isDictation = $ref(false)
|
||||
let activeIndex = $ref(-1)
|
||||
const store = useBaseStore()
|
||||
|
||||
// const [playKeySound, setAudio] = useSound([机械0, 机械1, 机械2, 机械3], 1)
|
||||
// const [playKeySound, setAudio] = useSound([老式机械], 3)
|
||||
const [playKeySound, setAudio] = useSound([电话打字的声音Mp3], 3)
|
||||
const [playBeep] = useSound([beep], 1)
|
||||
const [playCorrect] = useSound([correct], 1)
|
||||
const [playAudio] = usePlayWordAudio()
|
||||
|
||||
const keyMap = {
|
||||
Show: 'Escape',
|
||||
Ignore: 'Tab',
|
||||
Remove: '`',
|
||||
Collect: 'Enter',
|
||||
}
|
||||
|
||||
const restWord = $computed(() => {
|
||||
return store.word.name.slice(input.length + wrong.length)
|
||||
})
|
||||
onMounted(() => {
|
||||
store.init()
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
window.addEventListener('keyup', onKeyUp)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// console.log('onUnmounted')
|
||||
window.removeEventListener('keydown', onKeyDown)
|
||||
window.removeEventListener('keyup', onKeyUp)
|
||||
})
|
||||
|
||||
watch(store.$state, (n) => {
|
||||
localStorage.setItem(SaveKey, JSON.stringify(n))
|
||||
})
|
||||
|
||||
function next() {
|
||||
if (store.currentDict.wordIndex === store.chapter.length - 1) {
|
||||
if (store.currentDict.chapterIndex !== store.currentDict.chapterList.length - 1) {
|
||||
store.currentDict.wordIndex = 0
|
||||
store.currentDict.chapterIndex++
|
||||
console.log('这一章节完了')
|
||||
} else {
|
||||
console.log('这本书完了')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
store.currentDict.wordIndex++
|
||||
|
||||
// var msg = new SpeechSynthesisUtterance();
|
||||
// // msg.text = store.word.name
|
||||
// msg.text = 'Hawaii wildfires burn historic town of Lahaina to the ground'
|
||||
// msg.rate = 0.8;
|
||||
// msg.pitch = 1;
|
||||
// msg.lang = 'en-US';
|
||||
// window.speechSynthesis.speak(msg);
|
||||
|
||||
console.log('这个词完了')
|
||||
}
|
||||
if ([DictType.custom, DictType.inner].includes(store.currentDictType) && store.skipWordNames.includes(store.word.name)) {
|
||||
next()
|
||||
}
|
||||
wrong = input = ''
|
||||
}
|
||||
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
showFullWord = false
|
||||
}
|
||||
|
||||
async function onKeyDown(e: KeyboardEvent) {
|
||||
//TODO 还有横杠
|
||||
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.code === 'Space') {
|
||||
let letter = e.key
|
||||
if ((input + letter).toLowerCase() === store.word.name.toLowerCase().slice(0, input.length + 1)) {
|
||||
input += letter
|
||||
wrong = ''
|
||||
playKeySound()
|
||||
} else {
|
||||
wrong = letter
|
||||
playKeySound()
|
||||
playBeep()
|
||||
setTimeout(() => {
|
||||
wrong = ''
|
||||
// wrong = input = ''
|
||||
}, 500)
|
||||
}
|
||||
if (input.toLowerCase() === store.word.name.toLowerCase()) {
|
||||
playCorrect()
|
||||
setTimeout(next, 300)
|
||||
}
|
||||
} else {
|
||||
// console.log('e', e)
|
||||
switch (e.key) {
|
||||
case 'Backspace':
|
||||
if (wrong) {
|
||||
wrong = ''
|
||||
} else {
|
||||
input = input.slice(0, -1)
|
||||
}
|
||||
break
|
||||
case keyMap.Collect:
|
||||
if (!store.newWordDict.wordList.find((v: Word) => v.name === store.word.name)) {
|
||||
store.newWordDict.wordList.push(store.word)
|
||||
}
|
||||
activeIndex = 1
|
||||
break
|
||||
case keyMap.Remove:
|
||||
if (!store.skipWordNames.includes(store.word.name)) {
|
||||
store.skipWordDict.wordList.push(store.word)
|
||||
}
|
||||
activeIndex = 0
|
||||
next()
|
||||
break
|
||||
case keyMap.Ignore:
|
||||
e.preventDefault()
|
||||
activeIndex = 2
|
||||
next()
|
||||
break
|
||||
case keyMap.Show:
|
||||
showFullWord = true
|
||||
break
|
||||
}
|
||||
setTimeout(() => {
|
||||
activeIndex = -1
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const show = $ref(false)
|
||||
const {appearance, toggle} = useThemeColor()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="type-word">
|
||||
<div class="translate">{{ store.word.trans.join(';') }}</div>
|
||||
<div class="word-wrapper">
|
||||
<div class="word" :class="wrong&&'is-wrong'">
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="isDictation">
|
||||
<span class="letter" v-if="!showFullWord"
|
||||
@mouseenter="showFullWord = true">{{ restWord.split('').map(v => '_').join('') }}</span>
|
||||
<span class="letter" v-else @mouseleave="showFullWord = false">{{ restWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ restWord }}</span>
|
||||
</div>
|
||||
<div class="audio" @click="playAudio(store.word.name)">播放</div>
|
||||
</div>
|
||||
<div class="phonetic">{{ store.word.usphone }}</div>
|
||||
<div class="options">
|
||||
<BaseButton keyboard="`" :active="activeIndex === 0">
|
||||
忽略
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Enter" :active="activeIndex === 1">
|
||||
收藏
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Tab" :active="activeIndex === 2">
|
||||
下一个
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors.scss";
|
||||
|
||||
.type-word {
|
||||
display: flex;
|
||||
//display: none;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
gap: 2rem;
|
||||
|
||||
.options {
|
||||
margin-top: 10rem;
|
||||
display: flex;
|
||||
gap: 15rem;
|
||||
font-size: 18rem;
|
||||
|
||||
}
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 20rem;
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
|
||||
.word {
|
||||
font-size: 48rem;
|
||||
line-height: 1;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
letter-spacing: 5rem;
|
||||
|
||||
.input {
|
||||
color: rgba(74, 222, 128, 0.8);
|
||||
}
|
||||
|
||||
.wrong {
|
||||
color: rgba(red, 0.6);
|
||||
}
|
||||
|
||||
&.is-wrong {
|
||||
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10%,
|
||||
90% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
}
|
||||
|
||||
20%,
|
||||
80% {
|
||||
transform: translate3d(2px, 0, 0);
|
||||
}
|
||||
|
||||
30%,
|
||||
50%,
|
||||
70% {
|
||||
transform: translate3d(-4px, 0, 0);
|
||||
}
|
||||
|
||||
40%,
|
||||
60% {
|
||||
transform: translate3d(4px, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,140 +1,177 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import {Config, Dict, DictType, SaveKey, State, Word} from "../types.ts"
|
||||
import {Config, Dict, DictType, SaveKey, Sort, State, Word} from "../types.ts"
|
||||
import {chunk, cloneDeep} from "lodash";
|
||||
|
||||
export const useBaseStore = defineStore('base', {
|
||||
state: (): State => {
|
||||
return {
|
||||
newWordDict: {
|
||||
type: DictType.newWordDict,
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
skipWordDict: {
|
||||
type: DictType.skipWordDict,
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
dict: {
|
||||
type: DictType.inner,
|
||||
name: '新概念英语-2',
|
||||
description: '新概念英语第二册',
|
||||
category: '青少年英语',
|
||||
tags: ['新概念英语'],
|
||||
url: '/dicts/NCE_2.json',
|
||||
length: 858,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
},
|
||||
currentDictType: {
|
||||
name: DictType.inner,
|
||||
dictUrl: '/dicts/NCE_2.json'
|
||||
},
|
||||
sideIsOpen: false,
|
||||
dictModalIsOpen: false,
|
||||
dictModalIsOpen2: false,
|
||||
setting: {
|
||||
showToolbar: true,
|
||||
show: false,
|
||||
value1: false,
|
||||
value2: 50,
|
||||
value3: 1,
|
||||
value4: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.wordList.map(v => v.name)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.currentDictType.name) {
|
||||
case DictType.newWordDict:
|
||||
return state.newWordDict
|
||||
case DictType.skipWordDict:
|
||||
return state.skipWordDict
|
||||
case DictType.inner:
|
||||
case DictType.custom:
|
||||
return state.dict
|
||||
}
|
||||
},
|
||||
chapter(): Word[] {
|
||||
return this.currentDict.chapterList[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
word(): Word {
|
||||
return this.chapter[this.currentDict.wordIndex] ?? {
|
||||
trans: [],
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
this[key] = value
|
||||
}
|
||||
console.log('this/', this)
|
||||
},
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveKey)
|
||||
if (configStr) {
|
||||
let obj: Config = JSON.parse(configStr)
|
||||
this.setState(obj)
|
||||
}
|
||||
if (this.currentDictType.name === DictType.inner) {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
if (this.currentDictType.name === DictType.custom) {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
this.dictModalIsOpen = false
|
||||
this.dictModalIsOpen2 = false
|
||||
},
|
||||
async changeDict(dict: Dict, chapterIndex: number = -1, wordIndex: number = -1) {
|
||||
console.log('changeDict')
|
||||
if ([DictType.newWordDict, DictType.skipWordDict].includes(dict.type)) {
|
||||
this.currentDictType.name = dict.type
|
||||
this.currentDictType.dictUrl = ''
|
||||
this[dict.type].chapterList = [this[dict.type].wordList]
|
||||
this[dict.type].chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this[dict.type].wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
} else {
|
||||
if (dict.name === this.dict.name) {
|
||||
this.currentDictType.name = dict.type
|
||||
this.currentDictType.dictUrl = dict.url
|
||||
if (wordIndex !== -1) this.dict.wordIndex = wordIndex
|
||||
if (chapterIndex !== -1) this.dict.chapterIndex = chapterIndex
|
||||
} else {
|
||||
// let r = await fetch(`/public/${dict.url}`)
|
||||
// r.json().then(v => {
|
||||
// this.currentDictType.name === dict.type
|
||||
// this.currentDictType.dictUrl = dict.url
|
||||
//
|
||||
// })
|
||||
this.dict = cloneDeep(dict)
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
this.dict.chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this.dict.wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
state: (): State => {
|
||||
return {
|
||||
newWordDict: {
|
||||
type: DictType.newWordDict,
|
||||
sort: Sort.normal,
|
||||
name: '生词本',
|
||||
description: '生词本',
|
||||
category: '',
|
||||
tags: [],
|
||||
url: '',
|
||||
length: -1,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
dictStatistics: []
|
||||
},
|
||||
skipWordDict: {
|
||||
type: DictType.skipWordDict,
|
||||
sort: Sort.normal,
|
||||
name: '已忽略',
|
||||
description: '已忽略',
|
||||
category: '',
|
||||
tags: [],
|
||||
url: '',
|
||||
length: -1,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
dictStatistics: []
|
||||
},
|
||||
wrongDict: {
|
||||
type: DictType.wrongDict,
|
||||
sort: Sort.normal,
|
||||
name: '已忽略',
|
||||
description: '已忽略',
|
||||
category: '',
|
||||
tags: [],
|
||||
url: '',
|
||||
length: -1,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
dictStatistics: []
|
||||
},
|
||||
dict: {
|
||||
type: DictType.inner,
|
||||
sort: Sort.normal,
|
||||
name: '新概念英语-2',
|
||||
description: '新概念英语第二册',
|
||||
category: '青少年英语',
|
||||
tags: ['新概念英语'],
|
||||
url: '/dicts/NCE_2.json',
|
||||
length: 858,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
dictStatistics: []
|
||||
},
|
||||
oldDicts: [],
|
||||
currentDictType: DictType.inner,
|
||||
sideIsOpen: false,
|
||||
dictModalIsOpen: false,
|
||||
dictModalIsOpen2: false,
|
||||
setting: {
|
||||
showToolbar: true,
|
||||
show: false,
|
||||
value1: false,
|
||||
value2: 50,
|
||||
value3: 1,
|
||||
value4: false,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.wordList.map(v => v.name)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.currentDictType) {
|
||||
case DictType.newWordDict:
|
||||
return state.newWordDict
|
||||
case DictType.skipWordDict:
|
||||
return state.skipWordDict
|
||||
case DictType.wrongDict:
|
||||
return state.wrongDict
|
||||
case DictType.inner:
|
||||
case DictType.custom:
|
||||
return state.dict
|
||||
}
|
||||
},
|
||||
chapter(): Word[] {
|
||||
return this.currentDict.chapterList[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
word(): Word {
|
||||
return this.chapter[this.currentDict.wordIndex] ?? {
|
||||
trans: [],
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
this[key] = value
|
||||
}
|
||||
// console.log('this/', this)
|
||||
},
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveKey)
|
||||
if (configStr) {
|
||||
let obj: Config = JSON.parse(configStr)
|
||||
this.setState(obj)
|
||||
}
|
||||
if (this.currentDictType === DictType.inner) {
|
||||
let r = await fetch(`/public/${this.dict.url}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
if (this.currentDictType === DictType.custom) {
|
||||
let r = await fetch(`/public/${this.dict.url}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
this.dictModalIsOpen = false
|
||||
this.dictModalIsOpen2 = false
|
||||
},
|
||||
async changeDict(dict: Dict, chapterIndex: number = -1, wordIndex: number = -1) {
|
||||
console.log('changeDict')
|
||||
if ([DictType.newWordDict,
|
||||
DictType.skipWordDict,
|
||||
DictType.wrongDict].includes(dict.type)) {
|
||||
this.currentDictType = dict.type
|
||||
this[dict.type].chapterList = [this[dict.type].wordList]
|
||||
this[dict.type].chapterIndex = 0
|
||||
this[dict.type].wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
} else {
|
||||
if (dict.name === this.dict.name) {
|
||||
this.currentDictType = dict.type
|
||||
if (wordIndex !== -1) this.dict.wordIndex = wordIndex
|
||||
if (chapterIndex !== -1) this.dict.chapterIndex = chapterIndex
|
||||
} else {
|
||||
// let r = await fetch(`/public/${dict.url}`)
|
||||
// r.json().then(v => {
|
||||
// this.currentDictType.name === dict.type
|
||||
// this.currentDictType.dictUrl = dict.url
|
||||
//
|
||||
// })
|
||||
this.dict = cloneDeep(dict)
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
this.dict.chapterIndex = chapterIndex === -1 ? 0 : chapterIndex
|
||||
this.dict.wordIndex = wordIndex === -1 ? 0 : wordIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
202
src/types.ts
202
src/types.ts
@@ -1,22 +1,22 @@
|
||||
export type Config = {
|
||||
newWords: Word[],
|
||||
skipWords: Word[],
|
||||
skipWordNames: string[],
|
||||
dict: string,
|
||||
currentDict: {
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
name: string,
|
||||
desc: string
|
||||
}
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
newWords: Word[],
|
||||
skipWords: Word[],
|
||||
skipWordNames: string[],
|
||||
dict: string,
|
||||
currentDict: {
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
name: string,
|
||||
desc: string
|
||||
}
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
}
|
||||
export type Word = {
|
||||
"name": string,
|
||||
"usphone": string,
|
||||
"ukphone": string,
|
||||
"trans": string[]
|
||||
"name": string,
|
||||
"usphone": string,
|
||||
"ukphone": string,
|
||||
"trans": string[]
|
||||
}
|
||||
|
||||
export const SaveKey = 'bb-word-config'
|
||||
@@ -30,121 +30,121 @@ export type LanguageCategoryType = 'en' | 'ja' | 'de' | 'code'
|
||||
|
||||
|
||||
export type DictionaryResource = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
}
|
||||
|
||||
export type Dictionary = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
// calculated in the store
|
||||
chapterCount: number
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
// calculated in the store
|
||||
chapterCount: number
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
}
|
||||
|
||||
export type PronunciationConfig = {
|
||||
name: string
|
||||
pron: PronunciationType
|
||||
name: string
|
||||
pron: PronunciationType
|
||||
}
|
||||
|
||||
export type LanguagePronunciationMapConfig = {
|
||||
defaultPronIndex: number
|
||||
pronunciation: PronunciationConfig[]
|
||||
defaultPronIndex: number
|
||||
pronunciation: PronunciationConfig[]
|
||||
}
|
||||
|
||||
export type LanguagePronunciationMap = {
|
||||
[key in LanguageType]: LanguagePronunciationMapConfig
|
||||
[key in LanguageType]: LanguagePronunciationMapConfig
|
||||
}
|
||||
|
||||
export type SoundResource = {
|
||||
key: string
|
||||
name: string
|
||||
filename: string
|
||||
key: string
|
||||
name: string
|
||||
filename: string
|
||||
}
|
||||
|
||||
|
||||
export interface DictJson {
|
||||
name: string,
|
||||
description: string,
|
||||
category: string,
|
||||
tags: string[],
|
||||
url: string,
|
||||
length: number,
|
||||
language: string,
|
||||
languageCategory: string,
|
||||
name: string,
|
||||
description: string,
|
||||
category: string,
|
||||
tags: string[],
|
||||
url: string,
|
||||
length: number,
|
||||
language: string,
|
||||
languageCategory: string,
|
||||
}
|
||||
|
||||
export enum DictType {
|
||||
newWordDict = 'newWordDict',
|
||||
skipWordDict = 'skipWordDict',
|
||||
inner = 'inner',
|
||||
custom = 'custom',
|
||||
newWordDict = 'newWordDict',
|
||||
skipWordDict = 'skipWordDict',
|
||||
wrongDict = 'wrongDict',
|
||||
inner = 'inner',
|
||||
custom = 'custom',
|
||||
}
|
||||
|
||||
export interface Dict extends DictJson {
|
||||
type: DictType,
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
sort: Sort,
|
||||
type: DictType,
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
dictStatistics: DictStatistics[]
|
||||
}
|
||||
|
||||
interface DictLog {
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
chapterWordNumber: number//章节单词数量
|
||||
statistics: Array<ChapterStatistics>
|
||||
export interface DictStatistics {
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
chapterWordNumber: number//章节单词数量
|
||||
statistics: Array<Statistics>
|
||||
}
|
||||
|
||||
interface ChapterStatistics {
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
correctRate: number//正确率
|
||||
correctNumber: number//正确数
|
||||
export interface Statistics {
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
correctRate: number//正确率
|
||||
correctNumber: number//正确数
|
||||
}
|
||||
|
||||
export enum Sort {
|
||||
normal = 0,
|
||||
random = 1,
|
||||
reverse = 2
|
||||
}
|
||||
|
||||
export interface State {
|
||||
newWordDict: {
|
||||
wordList: Word[],
|
||||
wordIndex: number,
|
||||
statistics: Array<ChapterStatistics>
|
||||
},
|
||||
skipWordDict: {
|
||||
wordList: Word[],
|
||||
wordIndex: number,
|
||||
statistics: Array<ChapterStatistics>
|
||||
},
|
||||
dict: Dict,
|
||||
currentDictType: {
|
||||
name: DictType,
|
||||
dictUrl: string
|
||||
}
|
||||
sideIsOpen: boolean,
|
||||
dictModalIsOpen: boolean,
|
||||
dictModalIsOpen2: boolean,
|
||||
setting: {
|
||||
showToolbar: boolean,
|
||||
show: boolean,
|
||||
value1: boolean,
|
||||
value2: number,
|
||||
value3: number,
|
||||
value4: boolean,
|
||||
}
|
||||
newWordDict: Dict,
|
||||
skipWordDict: Dict,
|
||||
wrongDict: Dict,
|
||||
dict: Dict,
|
||||
oldDicts: Dict[],
|
||||
currentDictType: DictType
|
||||
sideIsOpen: boolean,
|
||||
dictModalIsOpen: boolean,
|
||||
dictModalIsOpen2: boolean,
|
||||
setting: {
|
||||
showToolbar: boolean,
|
||||
show: boolean,
|
||||
value1: boolean,
|
||||
value2: number,
|
||||
value3: number,
|
||||
value4: boolean,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user