重构数据结构
This commit is contained in:
@@ -6,6 +6,15 @@ import {useBaseStore} from "@/stores/base.ts"
|
||||
import Tooltip from "@/components/Tooltip.vue"
|
||||
import {Down} from "@icon-park/vue-next"
|
||||
|
||||
interface IProps {
|
||||
total: number,
|
||||
inputNumber: number
|
||||
wrongNumber: number
|
||||
correctRate: number
|
||||
}
|
||||
|
||||
const props = defineProps()
|
||||
|
||||
const store = useBaseStore()
|
||||
|
||||
function format(val: number, suffix: string = '') {
|
||||
|
||||
172
src/components/Footer2.vue
Normal file
172
src/components/Footer2.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<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 = '') {
|
||||
return val === 0 ? '-' : (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">{{ inputNumber }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(props.wrongNumber) }}</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: 100%;
|
||||
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>
|
||||
@@ -76,7 +76,7 @@ function next() {
|
||||
|
||||
console.log('这个词完了')
|
||||
}
|
||||
if ([DictType.customDict, DictType.innerDict].includes(store.current.dictType) && store.skipWordNames.includes(store.word.name)) {
|
||||
if ([DictType.customDict, DictType.innerDict].includes(store.current.dictType) && store.skipWordNames.includes(store.word.name.toLowerCase())) {
|
||||
next()
|
||||
}
|
||||
|
||||
@@ -103,12 +103,12 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
wrong = ''
|
||||
playKeySound()
|
||||
} else {
|
||||
if (!store.wrongWordDict.originWords.find((v: Word) => v.name === store.word.name)) {
|
||||
if (!store.wrongWordDict.originWords.find((v: Word) => v.name.toLowerCase() === store.word.name.toLowerCase())) {
|
||||
store.wrongWordDict.originWords.push(store.word)
|
||||
store.wrongWordDict.words.push(store.word)
|
||||
store.wrongWordDict.chapterWords = [store.wrongWordDict.words]
|
||||
}
|
||||
if (!store.current.wrongWords.find((v: Word) => v.name === store.word.name)) {
|
||||
if (!store.current.wrongWords.find((v: Word) => v.name.toLowerCase() === store.word.name.toLowerCase())) {
|
||||
store.current.wrongWords.push(store.word)
|
||||
}
|
||||
store.current.statistics.correctRate = Math.trunc(((store.current.index + 1 - store.current.wrongWords.length) / (store.current.index + 1)) * 100)
|
||||
|
||||
@@ -64,7 +64,7 @@ let index = $ref(0)
|
||||
let input = $ref('')
|
||||
let wrong = $ref('')
|
||||
let isSpace = $ref(false)
|
||||
let isDictation = $ref(true)
|
||||
let isDictation = $ref(false)
|
||||
let showFullWord = $ref(false)
|
||||
let hoverIndex = $ref({
|
||||
sectionIndex: 0,
|
||||
@@ -80,22 +80,20 @@ let article = reactive<Article>({
|
||||
sections: [],
|
||||
translate: [],
|
||||
})
|
||||
const simpleWord = [
|
||||
'a', 'an', 'of', 'and',
|
||||
'i', 'my', 'you', 'your',
|
||||
'me', 'am', 'is', 'do', 'are',
|
||||
'what', 'who', 'where', 'how', 'no', 'yes',
|
||||
'not', 'did', 'were', 'can', 'could'
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
let t1 = useSplitArticle(article.article)
|
||||
let sections = useSplitArticle(article.article)
|
||||
let wordNumber = 0
|
||||
t1.map(v => {
|
||||
sections.map(v => {
|
||||
v.map(w => {
|
||||
wordNumber += w.words.length
|
||||
w.words.map(s=>{
|
||||
if (!store.skipWordNames.includes(s.toLowerCase())){
|
||||
wordNumber++
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
console.log('t1', t1)
|
||||
console.log('sections', sections)
|
||||
console.log('wordNumber', wordNumber)
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -116,9 +114,8 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}, 1000)
|
||||
let a = ``
|
||||
let t = useSplitArticle(a, 'cn', CnKeyboardMap)
|
||||
t.map((v, i) => {
|
||||
let temp = useSplitArticle(article.articleTranslate, 'cn', CnKeyboardMap)
|
||||
temp.map((v, i) => {
|
||||
v.map((w, j) => {
|
||||
article.translate.push({
|
||||
sentence: w.sentence,
|
||||
@@ -126,7 +123,7 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
})
|
||||
article.sections = t1
|
||||
article.sections = sections
|
||||
console.log(cloneDeep(article))
|
||||
calcTranslateLocation()
|
||||
})
|
||||
@@ -172,7 +169,6 @@ function focus() {
|
||||
inputRef.focus()
|
||||
}
|
||||
|
||||
|
||||
const currentIndex = computed(() => {
|
||||
return `${sectionIndex}${sentenceIndex}${wordIndex}`
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Sentence} from "@/components/TypeArticle.vue";
|
||||
import {Sentence} from "@/types.ts";
|
||||
|
||||
interface KeyboardMap {
|
||||
Period: string,
|
||||
|
||||
@@ -85,12 +85,20 @@ export const useBaseStore = defineStore('base', {
|
||||
value3: 1,
|
||||
value4: false,
|
||||
},
|
||||
simpleWords: [
|
||||
'a', 'an', 'of', 'and',
|
||||
'i', 'my', 'you', 'your',
|
||||
'me', 'am', 'is', 'do', 'are',
|
||||
'what', 'who', 'where', 'how', 'no', 'yes',
|
||||
'not', 'did', 'were', 'can', 'could', 'it',
|
||||
'the','to'
|
||||
],
|
||||
theme: 'auto'
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.words.map(v => v.name)
|
||||
return state.skipWordDict.originWords.map(v => v.name.toLowerCase()).concat(state.simpleWords)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.current.dictType) {
|
||||
@@ -109,7 +117,7 @@ export const useBaseStore = defineStore('base', {
|
||||
return this.currentDict.wordIndex
|
||||
},
|
||||
chapter(state: State): Word[] {
|
||||
return this.currentDict.chapters[this.currentDict.chapterIndex] ?? []
|
||||
return this.currentDict.chapterWords[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
word(state: State): Word {
|
||||
return state.current.words[state.current.index] ?? {
|
||||
|
||||
@@ -155,6 +155,7 @@ export interface State {
|
||||
repeatNumber: number,
|
||||
statistics: Statistics
|
||||
},
|
||||
simpleWords: string[],
|
||||
sideIsOpen: boolean,
|
||||
isDictation: boolean,
|
||||
theme: string,
|
||||
|
||||
Reference in New Issue
Block a user