重构数据结构

This commit is contained in:
zyronon
2023-08-29 18:49:45 +08:00
parent 992c7fac5a
commit 0bf3b90b31
9 changed files with 171 additions and 171 deletions

View File

@@ -5,8 +5,9 @@ import Type from "@/components/Type.vue";
import Side from "@/components/Side.vue";
import Statistics from "@/components/Modal/Statistics.vue";
import Backgorund from "@/components/Backgorund.vue";
import {onMounted} from "vue";
import {onMounted, watch} from "vue";
import {useBaseStore} from "@/stores/base.ts";
import {SaveKey} from "@/types.ts"
const store = useBaseStore()
// 查询当前系统主题颜色
@@ -19,6 +20,11 @@ function followSystem() {
document.documentElement.setAttribute('data-theme', theme)
}
watch(store.$state, (n) => {
// console.log('state', JSON.stringify(n.current, null, 2))
localStorage.setItem(SaveKey, JSON.stringify(n))
})
onMounted(() => {
store.init()
if (store.theme !== 'auto') {
@@ -28,7 +34,7 @@ onMounted(() => {
</script>
<template>
<!-- <Backgorund/>-->
<!-- <Backgorund/>-->
<div class="main-page">
<div class="center">
<Toolbar/>

View File

@@ -16,7 +16,7 @@
--color-item-bg: white;
--color-item-hover: white;
--color-item-active: rgb(75, 110, 175);
--toolbar-width: 600rem;
--toolbar-width: 700rem;
}
html[data-theme='dark'] {

View File

@@ -1,27 +1,30 @@
<script setup>
<script setup lang="ts">
import Modal from "@/components/Modal/Modal.vue";
import {useBaseStore} from "@/stores/base.ts";
import {KeyboardOne, Like, ShareThree, Tea} from '@icon-park/vue-next'
import {Like, ShareThree, Tea} from '@icon-park/vue-next'
import Ring from "@/components/Ring.vue";
import Tooltip from "@/components/Tooltip.vue";
import Fireworks from "@/components/Fireworks.vue";
import BaseButton from "@/components/BaseButton.vue";
import {DictType} from "@/types.ts";
import {DefaultStatistics, Statistics} from "@/types.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {onMounted} from "vue";
import {onMounted, reactive} from "vue";
import {cloneDeep} from "lodash";
const store = useBaseStore()
let statModalIsOpen = $ref(false)
let currentStat = $ref({
wrongNumber: -1,
correctRate: -1
})
let currentStat = reactive<Statistics>(cloneDeep(DefaultStatistics))
onMounted(() => {
emitter.on(EventKey.openStatModal, () => {
statModalIsOpen = true
store.lastStatistics.endDate = Date.now()
currentStat = store.lastStatistics
currentStat = cloneDeep(store.current.statistics)
currentStat.endDate = Date.now()
currentStat.spend = Date.now() - currentStat.startDate
currentStat.wrongWordNumber = store.current.originWrongWords.length
currentStat.correctRate = 100 - Math.trunc((currentStat.wrongWordNumber / currentStat.wordNumber) * 100)
console.log(cloneDeep(currentStat))
store.currentDict.statistics.push(currentStat)
})
})
@@ -32,19 +35,9 @@ function write() {
//TODO 需要判断是否已忽略
function repeat() {
store.currentDict.wordIndex = 0
store.currentWrongDict.wordList = []
store.setCurrentWord(store.chapter, true)
statModalIsOpen = false
let stat = store.currentDict.dictStatistics[store.currentDict.dictStatistics.length - 1]
if (stat.statistics.length) {
stat.statistics.push({
startDate: Date.now(),//开始日期
endDate: -1,
correctRate: -1,
wrongNumber: -1
})
}
emitter.on(EventKey.resetWord)
emitter.emit(EventKey.resetWord)
}
function next() {
@@ -52,26 +45,13 @@ function next() {
repeat()
}
function repeatWrong() {
if (store.currentDictType !== DictType.currentWrongDict) {
store.lastDictType = store.currentDictType
}
store.currentDictType = DictType.currentWrongDict
store.currentWrongDict.chapterList = [store.currentWrongDict.wordList]
store.currentWrongDict.chapterIndex = 0
store.currentWrongDict.wordIndex = 0
store.currentWrongDict.wordList = []
statModalIsOpen = false
emitter.on(EventKey.resetWord)
}
</script>
<template>
<Modal v-model="statModalIsOpen" @close="next">
<div class="statistics">
<header>
<div class="title">{{ store.currentDict.name }} {{ store.chapterName }}</div>
<div class="title">{{ store.currentDict.name }}</div>
</header>
<div class="content">
<div class="rings">
@@ -80,24 +60,24 @@ function repeatWrong() {
desc="正确率"
:percentage="currentStat.correctRate"/>
<Ring
:value="currentStat.wrongNumber"
desc="正确数"
:value="currentStat.wrongWordNumber"
desc="错误数"
:percentage="0"
/>
<Ring
:value="store.currentDict.wordIndex"
desc="输入数"
:value="currentStat.wordNumber"
desc="单词总数"
:percentage="0"
style="margin-bottom: 0;"/>
</div>
<div class="result">
<div class="wrong-words-wrapper">
<div class="wrong-words">
<div class="word" v-for="i in store.currentWrongDict.wordList">{{ i.name }}</div>
<div class="word" v-for="i in store.current.originWrongWords">{{ i.name }}</div>
<!-- <div class="word" v-for="i in 100">{{ i }}</div>-->
</div>
</div>
<div class="notice" v-if="!store.currentWrongDict.wordList.length">
<div class="notice" v-if="!store.current.originWrongWords.length">
<!-- <div class="notice">-->
<like theme="filled" size="20" fill="#ffffff" :strokeWidth="2"/>
表现不错全对了
@@ -122,16 +102,13 @@ function repeatWrong() {
<BaseButton keyboard="Alt + Enter" @click="repeat">
重复本章
</BaseButton>
<BaseButton keyboard="Enter" @click="repeatWrong">
重复错词
</BaseButton>
<BaseButton keyboard="Tab" @click="next">
下一章
</BaseButton>
</div>
</div>
</Modal>
<Fireworks v-if="store.statModalIsOpen"/>
<Fireworks v-if="statModalIsOpen"/>
</template>
<style scoped lang="scss">
@import "@/assets/css/style";

View File

@@ -69,6 +69,7 @@ $w2: calc($w / 2);
}
.desc {
font-size: 14rem;
opacity: .6;
}
}

View File

@@ -15,7 +15,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts"
const store = useBaseStore()
const swiperIns0: SwiperClass = $ref(null as any)
let tabIndex = $ref(1)
let tabIndex = $ref(0)
provide('tabIndex', computed(() => tabIndex))
function slideTo(index: number) {
@@ -26,18 +26,16 @@ onMounted(() => {
emitter.on(EventKey.openSide, () => {
store.sideIsOpen = !store.sideIsOpen
if (store.sideIsOpen) {
switch (store.currentDictType) {
switch (store.current.dictType) {
case DictType.newWordDict:
return tabIndex = 2;
return tabIndex = 1;
case DictType.skipWordDict:
return tabIndex = 4;
case DictType.allWrongDict:
return tabIndex = 3;
case DictType.currentWrongDict:
return tabIndex = 0;
case DictType.wrongWordDict:
return tabIndex = 2;
case DictType.inner:
case DictType.custom:
return tabIndex = 1;
return tabIndex = 0;
}
}
})
@@ -49,40 +47,14 @@ onMounted(() => {
<div class="side" v-if="store.sideIsOpen">
<header>
<div class="tabs">
<div class="tab" v-if="store.isWrongMode" :class="tabIndex===0&&'active'" @click="slideTo(0)">
{{ store.currentWrongDict.name }}
</div>
<div class="tab" :class="tabIndex===1&&'active'" @click="slideTo(1)">单词表</div>
<div class="tab" :class="tabIndex===2&&'active'" @click="slideTo(2)">{{ store.newWordDict.name }}</div>
<div class="tab" :class="tabIndex===3&&'active'" @click="slideTo(3)">{{ store.allWrongDict.name }}</div>
<div class="tab" :class="tabIndex===4&&'active'" @click="slideTo(4)">{{ store.skipWordDict.name }}</div>
<div class="tab" :class="tabIndex===0&&'active'" @click="slideTo(0)">{{ store.dict.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>
</div>
</header>
<div class="side-content">
<swiper :initial-slide="tabIndex" @swiper="e=>swiperIns0 = e" class="mySwiper" :allow-touch-move="false">
<swiper-slide>
<div class="page0">
<header>
<div class="dict-name">总词数{{ store.currentDict.chapterList[0].length }}</div>
</header>
<WordList
class="word-list"
@change="(e:number) => store.changeDict(store.currentWrongDict,-1,e)"
:isActive="store.sideIsOpen && tabIndex === 0"
:list="store.currentDict.chapterList[0]??[]"
:activeIndex="store.currentDict.wordIndex"/>
<footer v-if="![DictType.custom,DictType.inner].includes(store.currentDictType)">
<PopConfirm
:title="`确认切换?`"
@confirm="store.changeDict(store.dict)"
>
<div class="my-button">
切换
</div>
</PopConfirm>
</footer>
</div>
</swiper-slide>
<swiper-slide>
<div class="page0">
<header>
@@ -91,10 +63,10 @@ onMounted(() => {
<WordList
class="word-list"
@change="(e:number) => store.changeDict(store.dict,-1,e)"
:isActive="store.sideIsOpen && tabIndex === 1"
:list="store.dict.chapterList[store.dict.chapterIndex]??[]"
:activeIndex="store.dict.wordIndex"/>
<footer v-if="![DictType.custom,DictType.inner].includes(store.currentDictType)">
:isActive="store.sideIsOpen && tabIndex === 0"
:list="store.dict.chapters[store.dict.chapterIndex]??[]"
:activeIndex="store.dict.chapterWordIndex"/>
<footer v-if="![DictType.custom,DictType.inner].includes(store.current.dictType)">
<PopConfirm
:title="`确认切换?`"
@confirm="store.changeDict(store.dict)"
@@ -109,15 +81,15 @@ onMounted(() => {
<swiper-slide>
<div class="page0">
<header>
<div class="dict-name">总词数{{ store.newWordDict.wordList.length }}</div>
<div class="dict-name">总词数{{ store.newWordDict.originWords.length }}</div>
</header>
<WordList
class="word-list"
@change="(e:number) => store.changeDict(store.newWordDict,-1,e)"
:isActive="store.sideIsOpen && tabIndex === 2"
:list="store.newWordDict.wordList"
:activeIndex="store.newWordDict.wordIndex"/>
<footer v-if="store.currentDictType !== DictType.newWordDict && store.newWordDict.wordList.length">
:isActive="store.sideIsOpen && tabIndex === 1"
:list="store.newWordDict.originWords"
:activeIndex="store.newWordDict.chapterWordIndex"/>
<footer v-if="store.current.dictType !== DictType.newWordDict && store.newWordDict.originWords.length">
<PopConfirm
:title="`确认切换?`"
@confirm="store.changeDict(store.newWordDict)"
@@ -133,18 +105,18 @@ onMounted(() => {
<div class="page0">
<header>
<a href="" target="_blank"></a>
<div class="dict-name">总词数{{ store.allWrongDict.wordList.length }}</div>
<div class="dict-name">总词数{{ store.wrongWordDict.originWords.length }}</div>
</header>
<WordList
class="word-list"
@change="(e:number) => store.changeDict(store.allWrongDict,-1,e)"
:isActive="store.sideIsOpen && tabIndex === 3"
:list="store.allWrongDict.wordList"
:activeIndex="store.allWrongDict.wordIndex"/>
<footer v-if="store.currentDictType !== DictType.allWrongDict && store.allWrongDict.wordList.length">
@change="(e:number) => store.changeDict(store.wrongWordDict,-1,e)"
:isActive="store.sideIsOpen && tabIndex === 2"
:list="store.wrongWordDict.originWords"
:activeIndex="store.wrongWordDict.chapterWordIndex"/>
<footer v-if="store.current.dictType !== DictType.wrongWordDict && store.wrongWordDict.originWords.length">
<PopConfirm
:title="`确认切换?`"
@confirm="store.changeDict(store.allWrongDict)"
@confirm="store.changeDict(store.wrongWordDict)"
>
<BaseButton>
切换
@@ -156,15 +128,15 @@ onMounted(() => {
<swiper-slide>
<div class="page0">
<header>
<div class="dict-name">总词数{{ store.skipWordDict.wordList.length }}</div>
<div class="dict-name">总词数{{ store.skipWordDict.originWords.length }}</div>
</header>
<WordList
class="word-list"
@change="(e:number) => store.changeDict(store.skipWordDict,-1,e)"
:isActive="store.sideIsOpen && tabIndex === 4"
:list="store.skipWordDict.wordList"
:activeIndex="store.skipWordDict.wordIndex"/>
<footer v-if="store.currentDictType !== DictType.skipWordDict && store.skipWordDict.wordList.length">
:isActive="store.sideIsOpen && tabIndex === 3"
:list="store.skipWordDict.originWords"
:activeIndex="store.skipWordDict.chapterWordIndex"/>
<footer v-if="store.current.dictType !== DictType.skipWordDict && store.skipWordDict.originWords.length">
<PopConfirm
:title="`确认切换?`"
@confirm="store.changeDict(store.skipWordDict)"

View File

@@ -48,7 +48,7 @@ function t() {
<template>
<header :class="!store.setting.showToolbar && 'hide'">
<div class="info" @click="showDictModal = true">
{{ store.currentDict.name }} {{ store.chapterName }}
{{ store.dictTitle }}
</div>
<div class="options">
<Tooltip title="切换主题">
@@ -136,7 +136,7 @@ header {
transition: all .3s;
.info {
font-size: 18rem;
font-size: 16rem;
color: var(--color-font-1);
display: flex;
justify-content: center;

View File

@@ -63,62 +63,43 @@ onUnmounted(() => {
window.removeEventListener('keyup', onKeyUp)
})
watch(store.$state, (n) => {
localStorage.setItem(SaveKey, JSON.stringify(n))
})
function repeatWrong() {
store.current.words = cloneDeep(store.current.wrongWords)
store.current.index = 0
store.current.wrongWords = []
store.current.statistics = {
startDate: -1,
endDate: -1,
spend: -1,
wordNumber: -1,
correctRate: -1,
wrongWordNumber: -1,
}
}
function next() {
if (store.current.wrongWords.length) {
repeatWrong()
} else {
if (store.current.index === store.chapter.length - 1) {
if (store.current.index === store.current.words.length - 1) {
if (store.current.wrongWords.length) {
store.setCurrentWord(store.current.wrongWords)
} else {
if (store.currentDict.chapterIndex !== store.currentDict.chapters.length - 1) {
console.log('这一章节完了')
// emitter.emit(EventKey.openStatModal)
emitter.emit(EventKey.openStatModal)
} else {
console.log('这本书完了')
emitter.emit(EventKey.openStatModal)
}
} else {
store.current.index++
// 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 (store.current.index) {
store.current.statistics.wrongWordNumber = store.current.index - store.current.wrongWords.length
store.current.statistics.correctRate = Math.trunc(((store.current.index - store.current.wrongWords.length) / (store.current.index)) * 100)
} else {
store.current.statistics.correctRate = -1
store.current.statistics.wrongWordNumber = -1
}
}
if ([DictType.custom, DictType.inner].includes(store.current.dictType) && store.skipWordNames.includes(store.word.name)) {
next()
}
} else {
store.current.index++
// 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.current.dictType) && store.skipWordNames.includes(store.word.name)) {
next()
}
if (store.current.index) {
store.current.statistics.wrongWordNumber = store.current.wrongWords.length
store.current.statistics.correctRate = Math.trunc(((store.current.index - store.current.wrongWords.length) / (store.current.index)) * 100)
} else {
store.current.statistics.wrongWordNumber = -1
store.current.statistics.correctRate = -1
}
wrong = input = ''
}
@@ -135,13 +116,13 @@ async function onKeyDown(e: KeyboardEvent) {
wrong = ''
playKeySound()
} else {
if (!store.allWrongDict.wordList.find((v: Word) => v.name === store.word.name)) {
store.allWrongDict.wordList.push(store.word)
if (!store.wrongWordDict.originWords.find((v: Word) => v.name === store.word.name)) {
store.wrongWordDict.originWords.push(store.word)
}
if (!store.currentWrongDict.wordList.find((v: Word) => v.name === store.word.name)) {
store.currentWrongDict.wordList.push(store.word)
if (!store.current.wrongWords.find((v: Word) => v.name === store.word.name)) {
store.current.wrongWords.push(store.word)
}
store.lastStatistics.correctRate = Math.trunc(((store.currentDict.wordIndex + 1 - store.currentWrongDict.wordList.length) / (store.currentDict.wordIndex + 1)) * 100)
store.current.statistics.correctRate = Math.trunc(((store.current.index + 1 - store.current.wrongWords.length) / (store.current.index + 1)) * 100)
wrong = letter
playKeySound()
playBeep()
@@ -165,14 +146,14 @@ async function onKeyDown(e: KeyboardEvent) {
}
break
case keyMap.Collect:
if (!store.newWordDict.wordList.find((v: Word) => v.name === store.word.name)) {
store.newWordDict.wordList.push(store.word)
if (!store.newWordDict.originWords.find((v: Word) => v.name === store.word.name)) {
store.newWordDict.originWords.push(store.word)
}
activeIndex = 1
break
case keyMap.Remove:
if (!store.skipWordNames.includes(store.word.name)) {
store.skipWordDict.wordList.push(store.word)
store.skipWordDict.originWords.push(store.word)
}
activeIndex = 0
next()
@@ -194,7 +175,7 @@ async function onKeyDown(e: KeyboardEvent) {
const progress = $computed(() => {
if (!store.chapter.length) return 0
return ((store.currentDict.wordIndex / store.chapter.length) * 100)
return ((store.current.index / store.current.words.length) * 100)
})
const {toggle} = useTheme()
@@ -203,6 +184,17 @@ function format(val: number, suffix: string = '') {
return val === -1 ? '-' : (val + suffix)
}
let speedMinute = $ref(0)
let timer = $ref(0)
onMounted(() => {
timer = setInterval(() => {
speedMinute = Math.floor((Date.now() - store.current.statistics.startDate) / 1000 / 60)
}, 1000)
})
onUnmounted(() => {
timer && clearInterval(timer)
})
</script>
<template>
@@ -246,17 +238,27 @@ function format(val: number, suffix: string = '') {
:show-text="false"/>
<div class="stat">
<div class="row">
<div class="num">{{ store.currentDict.wordIndex }}</div>
<div class="num">{{ speedMinute }}分钟</div>
<div class="line"></div>
<div class="name">时间</div>
</div>
<div class="row">
<div class="num">{{ store.current.statistics.wordNumber }}</div>
<div class="line"></div>
<div class="name">单词总数</div>
</div>
<div class="row">
<div class="num">{{ store.current.index }}</div>
<div class="line"></div>
<div class="name">输入数</div>
</div>
<div class="row">
<div class="num">{{ format(store.currentWrongDict.wordList.length) }}</div>
<div class="num">{{ format(store.current.wrongWords.length) }}</div>
<div class="line"></div>
<div class="name">错误数</div>
</div>
<div class="row">
<div class="num">{{ format(store.lastStatistics.correctRate, '%') }}</div>
<div class="num">{{ format(store.current.statistics.correctRate, '%') }}</div>
<div class="line"></div>
<div class="name">正确率</div>
</div>

View File

@@ -64,7 +64,8 @@ export const useBaseStore = defineStore('base', {
words: [],
index: -1,
wrongWords: [],
repeatNumber: -1,
originWrongWords: [],
repeatNumber: 0,
statistics: {
startDate: -1,
endDate: -1,
@@ -121,6 +122,14 @@ export const useBaseStore = defineStore('base', {
ukphone: '',
}
},
dictTitle(state: State) {
let title = this.currentDict.name
if ([DictType.inner, DictType.custom].includes(this.current.dictType)) {
title += `${this.currentDict.chapterIndex + 1}`
title += this.current.repeatNumber ? ' 复习错词' : ''
}
return title
}
},
actions: {
setState(obj: any) {
@@ -129,6 +138,30 @@ export const useBaseStore = defineStore('base', {
}
// console.log('this/', this)
},
setCurrentWord(words: Word[], restart: boolean = false) {
this.current.words = cloneDeep(words)
if (restart) {
this.current.repeatNumber = 0
this.current.originWrongWords = []
this.current.statistics = {
startDate: Date.now(),
endDate: -1,
spend: -1,
wordNumber: words.length,
correctRate: -1,
wrongWordNumber: -1,
}
} else {
this.current.repeatNumber++
if (!this.current.originWrongWords.length) {
this.current.originWrongWords = cloneDeep(this.current.wrongWords)
}
this.current.statistics.correctRate = -1
this.current.statistics.wrongWordNumber = -1
}
this.current.index = 0
this.current.wrongWords = []
},
async init() {
// let configStr = localStorage.getItem(SaveKey)
// if (configStr) {
@@ -141,9 +174,9 @@ export const useBaseStore = defineStore('base', {
this.dict.originWords = cloneDeep(v)
this.dict.words = cloneDeep(v)
this.dict.chapters = chunk(this.dict.originWords, this.dict.chapterWordNumber)
this.setCurrentWord(this.chapter, true)
})
}
this.dict.wordIndex = 0
},
async changeDict(dict: Dict, chapterIndex: number = -1, chapterWordIndex: number = -1) {
console.log('changeDict', dict)

View File

@@ -1,4 +1,3 @@
export type Word = {
"name": string,
"usphone": string,
@@ -107,6 +106,15 @@ export interface Statistics {
wrongWordNumber: number//错误数
}
export const DefaultStatistics: Statistics = {
startDate: Date.now(),
endDate: -1,
spend: -1,
wordNumber: -1,
correctRate: -1,
wrongWordNumber: -1,
}
export enum Sort {
normal = 0,
random = 1,
@@ -124,6 +132,7 @@ export interface State {
words: Word[],
index: number,
wrongWords: Word[],
originWrongWords: Word[],
repeatNumber: number,
statistics: Statistics
},