设计背词逻辑

This commit is contained in:
zyronon
2023-08-28 18:47:06 +08:00
parent 9266e51c5b
commit 0ad15ff1a6
10 changed files with 158 additions and 69 deletions

View File

@@ -15,6 +15,7 @@
"hover.css": "^2.3.2",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
"pinia": "^2.1.6",
"swiper": "^10.1.0",
"tesseract.js": "^4.1.1",

7
pnpm-lock.yaml generated
View File

@@ -20,6 +20,9 @@ dependencies:
lodash:
specifier: ^4.17.21
version: 4.17.21
mitt:
specifier: ^3.0.1
version: 3.0.1
pinia:
specifier: ^2.1.6
version: 2.1.6(typescript@5.0.2)(vue@3.3.4)
@@ -1346,6 +1349,10 @@ packages:
brace-expansion: 2.0.1
dev: true
/mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
dev: false
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true

View File

@@ -15,22 +15,20 @@ const match: MediaQueryList = window.matchMedia("(prefers-color-scheme: dark)")
match.addEventListener('change', followSystem)
function followSystem() {
if (store.theme === 'auto') {
const theme = match.matches ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', theme)
// document.documentElement.setAttribute('data-theme', 'dark')
localStorage.setItem('appearance', theme)
}
const theme = match.matches ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', theme)
}
onMounted(() => {
store.init()
followSystem()
if (store.theme !== 'auto') {
document.documentElement.setAttribute('data-theme', store.theme)
}
})
</script>
<template>
<Backgorund/>
<!-- <Backgorund/>-->
<div class="main-page">
<div class="center">
<Toolbar/>
@@ -55,7 +53,7 @@ onMounted(() => {
font-size: 14rem;
display: flex;
justify-content: center;
//background-color: var(--color-main-bg);
background-color: var(--color-main-bg);
.center {
width: 70vw;

View File

@@ -7,8 +7,23 @@ import Tooltip from "@/components/Tooltip.vue";
import Fireworks from "@/components/Fireworks.vue";
import BaseButton from "@/components/BaseButton.vue";
import {DictType} from "@/types.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {onMounted} from "vue";
const store = useBaseStore()
let statModalIsOpen = $ref(false)
let currentStat = $ref({
wrongNumber: -1,
correctRate: -1
})
onMounted(() => {
emitter.on(EventKey.openStatModal, () => {
statModalIsOpen = true
store.lastStatistics.endDate = Date.now()
currentStat = store.lastStatistics
})
})
function write() {
store.isDictation = true
@@ -19,9 +34,17 @@ function write() {
function repeat() {
store.currentDict.wordIndex = 0
store.currentWrongDict.wordList = []
store.statModalIsOpen = false
store.lastStatistics.correctNumber = -1
store.lastStatistics.correctRate = -1
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)
}
function next() {
@@ -30,7 +53,7 @@ function next() {
}
function repeatWrong() {
if (store.currentDictType !== DictType.currentWrongDict){
if (store.currentDictType !== DictType.currentWrongDict) {
store.lastDictType = store.currentDictType
}
store.currentDictType = DictType.currentWrongDict
@@ -38,24 +61,26 @@ function repeatWrong() {
store.currentWrongDict.chapterIndex = 0
store.currentWrongDict.wordIndex = 0
store.currentWrongDict.wordList = []
store.statModalIsOpen = false
statModalIsOpen = false
emitter.on(EventKey.resetWord)
}
</script>
<template>
<Modal v-model="store.statModalIsOpen" @close="next">
<Modal v-model="statModalIsOpen" @close="next">
<div class="statistics">
<header>
<div class="title">{{ store.currentDict.name }} {{ store.currentDict.chapterIndex + 1 }}</div>
<div class="title">{{ store.currentDict.name }} {{ store.chapterName }}</div>
</header>
<div class="content">
<div class="rings">
<Ring
:value="store.lastStatistics.correctRate + '%'"
:value="currentStat.correctRate + '%'"
desc="正确率"
:percentage="store.lastStatistics.correctRate"/>
:percentage="currentStat.correctRate"/>
<Ring
:value="store.lastStatistics.correctNumber"
:value="currentStat.wrongNumber"
desc="正确数"
:percentage="0"
/>

View File

@@ -11,6 +11,7 @@ import {Swiper as SwiperClass} from "swiper/types"
import {Dict, DictType} from "@/types.ts"
import PopConfirm from "@/components/PopConfirm.vue"
import BaseButton from "@/components/BaseButton.vue";
import {emitter, EventKey} from "@/utils/eventBus.ts"
const store = useBaseStore()
const swiperIns0: SwiperClass = $ref(null as any)
@@ -22,33 +23,43 @@ function slideTo(index: number) {
}
onMounted(() => {
setTimeout(() => {
slideTo(1)
}, 300)
emitter.on(EventKey.openSide, () => {
store.sideIsOpen = !store.sideIsOpen
if (store.sideIsOpen) {
switch (store.currentDictType) {
case DictType.newWordDict:
return tabIndex = 2;
case DictType.skipWordDict:
return tabIndex = 4;
case DictType.allWrongDict:
return tabIndex = 3;
case DictType.currentWrongDict:
return tabIndex = 0;
case DictType.inner:
case DictType.custom:
return tabIndex = 1;
}
}
})
})
function changeDict(dict: Dict, i: number) {
if (store.currentDictType !== dict.type) {
store.currentDictType = dict.type
}
store.currentDict.wordIndex = i
}
</script>
<template>
<Transition name="fade">
<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)">错词模式</div>
<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)">生词本</div>
<div class="tab" :class="tabIndex===3&&'active'" @click="slideTo(3)">纠错本</div>
<div class="tab" :class="tabIndex===4&&'active'" @click="slideTo(4)">已忽略</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>
</header>
<div class="side-content">
<swiper @swiper="e=>swiperIns0 = e" class="mySwiper" :allow-touch-move="false">
<swiper :initial-slide="tabIndex" @swiper="e=>swiperIns0 = e" class="mySwiper" :allow-touch-move="false">
<swiper-slide>
<div class="page0">
<header>

View File

@@ -31,8 +31,9 @@ import IconEyeSlash from '~icons/heroicons/eye-slash-solid'
import IconRepeat from '~icons/tabler/repeat'
import IconRepeatOff from '~icons/tabler/repeat-off'
import {emitter, EventKey} from "@/utils/eventBus.ts"
const { toggle} = useTheme()
const {toggle} = useTheme()
const store = useBaseStore()
const showFeedbackModal = $ref(false)
const showSettingModal = $ref(false)
@@ -41,12 +42,13 @@ const showDictModal = $ref(false)
function t() {
console.log('t')
}
</script>
<template>
<header :class="!store.setting.showToolbar && 'hide'">
<div class="info" @click="showDictModal = true">
{{ store.currentDict.name }} {{ store.currentDict.chapterIndex + 1 }}
{{ store.currentDict.name }} {{ store.chapterName }}
</div>
<div class="options">
<Tooltip title="切换主题">
@@ -95,12 +97,12 @@ function t() {
<IconCog6Tooth @click="showSettingModal = true"></IconCog6Tooth>
</IconWrapper>
</Tooltip>
<div class="my-button" @click="store.statModalIsOpen = true">ok</div>
<div class="my-button" @click="emitter.emit(EventKey.openStatModal)">ok</div>
<Tooltip title="单词本">
<menu-fold class="menu" @click="store.sideIsOpen = !store.sideIsOpen"
theme="outline" size="20" fill="#929596"
:strokeWidth="2"/>
<IconWrapper>
<menu-fold class="menu" @click="emitter.emit(EventKey.openSide)"/>
</IconWrapper>
</Tooltip>
</div>
<Tooltip :title="store.setting.showToolbar?'收起':'展开'">

View File

@@ -28,7 +28,9 @@ import BaseButton from "@/components/BaseButton.vue";
import Type from "@/components/Type.vue";
import {
Down,
Delete,
} from "@icon-park/vue-next"
import {emitter, EventKey} from "@/utils/eventBus.ts"
let input = $ref('')
let wrong = $ref('')
@@ -50,12 +52,16 @@ const keyMap = {
Collect: 'Enter',
}
const restWord = $computed(() => {
const resetWord = $computed(() => {
return store.word.name.slice(input.length + wrong.length)
})
onMounted(() => {
window.addEventListener('keydown', onKeyDown)
window.addEventListener('keyup', onKeyUp)
emitter.on(EventKey.resetWord, () => {
input = ''
})
})
onUnmounted(() => {
@@ -68,18 +74,44 @@ watch(store.$state, (n) => {
localStorage.setItem(SaveKey, JSON.stringify(n))
})
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 = []
store.currentWrongDict.statistics = {
startDate: Date.now(),//开始日期
endDate: -1,
correctRate: -1,
wrongNumber: -1
}
}
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.wordIndex++
// store.currentDict.chapterIndex++
console.log('这一章节完了')
store.statModalIsOpen = true
if (store.currentWrongDict.wordList.length) {
repeatWrong()
}
// emitter.emit(EventKey.openStatModal)
} else {
console.log('这本书完了')
store.statModalIsOpen = true
return
if (store.currentDictType === DictType.currentWrongDict) {
if ( store.currentWrongDict.wordList.length){
repeatWrong()
}else {
emitter.emit(EventKey.openStatModal)
}
} else {
emitter.emit(EventKey.openStatModal)
}
}
} else {
store.currentDict.wordIndex++
@@ -93,18 +125,18 @@ function next() {
// window.speechSynthesis.speak(msg);
console.log('这个词完了')
if (store.currentDict.wordIndex) {
store.lastStatistics.wrongNumber = store.currentDict.wordIndex - store.currentWrongDict.wordList.length
store.lastStatistics.correctRate = Math.trunc(((store.currentDict.wordIndex - store.currentWrongDict.wordList.length) / (store.currentDict.wordIndex)) * 100)
} else {
store.lastStatistics.correctRate = -1
store.lastStatistics.wrongNumber = -1
}
}
if ([DictType.custom, DictType.inner].includes(store.currentDictType) && store.skipWordNames.includes(store.word.name)) {
next()
}
wrong = input = ''
if (store.currentDict.wordIndex) {
store.lastStatistics.correctNumber = store.currentDict.wordIndex - store.currentWrongDict.wordList.length
store.lastStatistics.correctRate = Math.trunc(((store.currentDict.wordIndex - store.currentWrongDict.wordList.length) / (store.currentDict.wordIndex)) * 100)
} else {
store.lastStatistics.correctRate = -1
store.lastStatistics.correctNumber = -1
}
}
function onKeyUp(e: KeyboardEvent) {
@@ -139,7 +171,6 @@ async function onKeyDown(e: KeyboardEvent) {
playCorrect()
setTimeout(next, 300)
}
} else {
// console.log('e', e)
switch (e.key) {
@@ -183,7 +214,7 @@ const progress = $computed(() => {
return ((store.currentDict.wordIndex / store.chapter.length) * 100)
})
const { toggle} = useTheme()
const {toggle} = useTheme()
function format(val: number, suffix: string = '') {
return val === -1 ? '-' : (val + suffix)
@@ -200,10 +231,10 @@ function format(val: number, suffix: string = '') {
<span class="wrong" v-if="wrong">{{ wrong }}</span>
<template v-if="store.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>
@mouseenter="showFullWord = true">{{ resetWord.split('').map(v => '_').join('') }}</span>
<span class="letter" v-else @mouseleave="showFullWord = false">{{ resetWord }}</span>
</template>
<span class="letter" v-else>{{ restWord }}</span>
<span class="letter" v-else>{{ resetWord }}</span>
</div>
<div class="audio" @click="playAudio(store.word.name)">播放</div>
</div>
@@ -237,9 +268,9 @@ function format(val: number, suffix: string = '') {
<div class="name">输入数</div>
</div>
<div class="row">
<div class="num">{{ format(store.lastStatistics.correctNumber) }}</div>
<div class="num">{{ format(store.currentWrongDict.wordList.length) }}</div>
<div class="line"></div>
<div class="name">正确</div>
<div class="name">错误</div>
</div>
<div class="row">
<div class="num">{{ format(store.lastStatistics.correctRate, '%') }}</div>

View File

@@ -1,6 +1,7 @@
import {defineStore} from 'pinia'
import {Config, Dict, DictType, SaveKey, Sort, State, Statistics, Word} from "../types.ts"
import {chunk, cloneDeep} from "lodash";
import {emitter, EventKey} from "@/utils/eventBus.ts"
export const useBaseStore = defineStore('base', {
state: (): State => {
@@ -26,8 +27,8 @@ export const useBaseStore = defineStore('base', {
skipWordDict: {
type: DictType.skipWordDict,
sort: Sort.normal,
name: '已忽略',
description: '已忽略',
name: '简单词',
description: '简单词',
category: '',
tags: [],
url: '',
@@ -62,8 +63,8 @@ export const useBaseStore = defineStore('base', {
currentWrongDict: {
type: DictType.currentWrongDict,
sort: Sort.normal,
name: '当前错词',
description: '错词',
name: '当前章节错词',
description: '当前章节错词',
category: '',
tags: [],
url: '',
@@ -79,7 +80,7 @@ export const useBaseStore = defineStore('base', {
startDate: Date.now(),//开始日期
endDate: -1,
correctRate: -1,
correctNumber: -1
wrongNumber: -1
},
chapterWordNumber: 15
},
@@ -113,7 +114,6 @@ export const useBaseStore = defineStore('base', {
lastDictType: DictType.inner,
sideIsOpen: false,
isDictation: true,
statModalIsOpen: false,
setting: {
showToolbar: true,
show: false,
@@ -170,6 +170,12 @@ export const useBaseStore = defineStore('base', {
}
}
return {} as any
},
chapterName(state: State) {
if ([DictType.custom, DictType.inner].includes(state.currentDictType)) {
return `${state.dict.chapterIndex + 1}`
}
return ''
}
},
actions: {
@@ -205,13 +211,14 @@ export const useBaseStore = defineStore('base', {
startDate: Date.now(),//开始日期
endDate: -1,
correctRate: -1,
correctNumber: -1
wrongNumber: -1
})
}
this.dict.wordIndex = 0
},
async changeDict(dict: Dict, chapterIndex: number = -1, wordIndex: number = -1) {
console.log('changeDict', dict)
emitter.emit(EventKey.resetWord)
if ([DictType.newWordDict,
DictType.skipWordDict,
DictType.allWrongDict].includes(dict.type)) {

View File

@@ -123,7 +123,7 @@ export interface Statistics {
startDate: number,//开始日期
endDate: number//结束日期
correctRate: number//正确率
correctNumber: number//正确
wrongNumber: number//错误
}
export enum Sort {
@@ -142,7 +142,6 @@ export interface State {
currentDictType: DictType,
lastDictType: DictType,
sideIsOpen: boolean,
statModalIsOpen: boolean,
isDictation: boolean,
theme: string,
setting: {

8
src/utils/eventBus.ts Normal file
View File

@@ -0,0 +1,8 @@
import mitt from 'mitt'
export const emitter = mitt()
export const EventKey = {
resetWord: 'resetWord',
openSide: 'openSide',
openStatModal: 'openStatModal',
}