bfd
This commit is contained in:
@@ -5,15 +5,13 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"test": "vite",
|
||||
"build": "vite build",
|
||||
"build-tsc": "vue-tsc && vite build",
|
||||
"report": "vite build",
|
||||
"preview": "vite preview",
|
||||
"commit": "git-cz",
|
||||
"prepare": "husky install",
|
||||
"start": "vite",
|
||||
"test": "",
|
||||
"deploy": "push-dir --dir=dist --branch=gh-pages --cleanup",
|
||||
"i18n:write": "gulp i18nwrite"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -54,7 +52,6 @@
|
||||
"esm": "^3.2.25",
|
||||
"gulp": "^4.0.2",
|
||||
"husky": "^8.0.3",
|
||||
"push-dir": "^0.4.1",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.64.2",
|
||||
"tslib": "^2.6.2",
|
||||
@@ -65,7 +62,7 @@
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"unplugin-vue-define-options": "^1.4.1",
|
||||
"vite": "^5.2.11",
|
||||
"vue-tsc": "^1.8.5",
|
||||
"vue-tsc": "^2.0.19",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"config": {
|
||||
|
||||
9147
pnpm-lock.yaml
generated
9147
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,7 @@ function nextChapter() {
|
||||
}
|
||||
|
||||
let stat = cloneDeep(DefaultDisplayStatistics)
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
|
||||
function next(isTyping: boolean = true) {
|
||||
if (data.index === data.words.length - 1) {
|
||||
@@ -72,12 +72,12 @@ function next(isTyping: boolean = true) {
|
||||
if (stat.total === -1) {
|
||||
let now = Date.now()
|
||||
stat = {
|
||||
startDate: practiceStore.startDate,
|
||||
startDate: statisticsStore.startDate,
|
||||
endDate: now,
|
||||
spend: now - practiceStore.startDate,
|
||||
spend: now - statisticsStore.startDate,
|
||||
total: props.words.length,
|
||||
correctRate: -1,
|
||||
inputWordNumber: practiceStore.inputWordNumber,
|
||||
inputWordNumber: statisticsStore.inputWordNumber,
|
||||
wrongWordNumber: data.wrongWords.length,
|
||||
wrongWords: data.wrongWords,
|
||||
}
|
||||
@@ -88,15 +88,14 @@ function next(isTyping: boolean = true) {
|
||||
console.log('当前背完了,但还有错词')
|
||||
data.words = cloneDeep(data.wrongWords)
|
||||
|
||||
practiceStore.total = data.words.length
|
||||
practiceStore.index = data.index = 0
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
practiceStore.repeatNumber++
|
||||
statisticsStore.total = data.words.length
|
||||
statisticsStore.index = data.index = 0
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrongWordNumber = 0
|
||||
data.wrongWords = []
|
||||
} else {
|
||||
console.log('这章节完了')
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
isTyping && statisticsStore.inputWordNumber++
|
||||
|
||||
let now = Date.now()
|
||||
stat.endDate = now
|
||||
|
||||
@@ -18,21 +18,21 @@ defineOptions({
|
||||
name: 'PracticeWord'
|
||||
})
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(practiceStore, () => {
|
||||
if (practiceStore.inputWordNumber < 1) {
|
||||
return practiceStore.correctRate = -1
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
|
||||
return practiceStore.correctRate = 0
|
||||
if (statisticsStore.wrongWordNumber > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.inputWordNumber)) * 100)
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrongWordNumber) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ const emit = defineEmits<{
|
||||
const typingRef: any = $ref()
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
|
||||
@@ -66,19 +66,17 @@ watch(() => props.words, () => {
|
||||
data.index = props.index
|
||||
data.wrongWords = []
|
||||
|
||||
practiceStore.wrongWords = []
|
||||
practiceStore.repeatNumber = 0
|
||||
practiceStore.startDate = Date.now()
|
||||
practiceStore.correctRate = -1
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
statisticsStore.startDate = Date.now()
|
||||
statisticsStore.correctRate = -1
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrongWordNumber = 0
|
||||
stat = cloneDeep(DefaultDisplayStatistics)
|
||||
|
||||
}, {immediate: true})
|
||||
|
||||
watch(data, () => {
|
||||
practiceStore.total = data.words.length
|
||||
practiceStore.index = data.index
|
||||
statisticsStore.total = data.words.length
|
||||
statisticsStore.index = data.index
|
||||
})
|
||||
|
||||
const word: Word = $computed(() => {
|
||||
@@ -92,12 +90,12 @@ function next(isTyping: boolean = true) {
|
||||
if (stat.total === -1) {
|
||||
let now = Date.now()
|
||||
stat = {
|
||||
startDate: practiceStore.startDate,
|
||||
startDate: statisticsStore.startDate,
|
||||
endDate: now,
|
||||
spend: now - practiceStore.startDate,
|
||||
spend: now - statisticsStore.startDate,
|
||||
total: props.words.length,
|
||||
correctRate: -1,
|
||||
inputWordNumber: practiceStore.inputWordNumber,
|
||||
inputWordNumber: statisticsStore.inputWordNumber,
|
||||
wrongWordNumber: data.wrongWords.length,
|
||||
wrongWords: data.wrongWords,
|
||||
}
|
||||
@@ -108,15 +106,14 @@ function next(isTyping: boolean = true) {
|
||||
console.log('当前背完了,但还有错词')
|
||||
data.words = cloneDeep(data.wrongWords)
|
||||
|
||||
practiceStore.total = data.words.length
|
||||
practiceStore.index = data.index = 0
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
practiceStore.repeatNumber++
|
||||
statisticsStore.total = data.words.length
|
||||
statisticsStore.index = data.index = 0
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrongWordNumber = 0
|
||||
data.wrongWords = []
|
||||
} else {
|
||||
console.log('这章节完了')
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
isTyping && statisticsStore.inputWordNumber++
|
||||
|
||||
let now = Date.now()
|
||||
stat.endDate = now
|
||||
@@ -126,7 +123,7 @@ function next(isTyping: boolean = true) {
|
||||
}
|
||||
} else {
|
||||
data.index++
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
isTyping && statisticsStore.inputWordNumber++
|
||||
console.log('这个词完了')
|
||||
if ([DictType.word].includes(store.currentDict.type)
|
||||
&& store.skipWordNames.includes(word.word.toLowerCase())) {
|
||||
@@ -141,7 +138,7 @@ function wordWrong() {
|
||||
}
|
||||
if (!data.wrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
data.wrongWords.push(word)
|
||||
practiceStore.wrongWordNumber++
|
||||
statisticsStore.wrongWordNumber++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,21 +17,21 @@ import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(practiceStore, () => {
|
||||
if (practiceStore.inputWordNumber < 1) {
|
||||
return practiceStore.correctRate = -1
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
|
||||
return practiceStore.correctRate = 0
|
||||
if (statisticsStore.wrongWordNumber > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.inputWordNumber)) * 100)
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrongWordNumber) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -17,21 +17,21 @@ import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
const practiceRef: any = $ref()
|
||||
|
||||
watch(practiceStore, () => {
|
||||
if (practiceStore.inputWordNumber < 1) {
|
||||
return practiceStore.correctRate = -1
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
|
||||
return practiceStore.correctRate = 0
|
||||
if (statisticsStore.wrongWordNumber > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.inputWordNumber)) * 100)
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrongWordNumber) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ function close() {
|
||||
|
||||
function showAllWordModal() {
|
||||
emitter.emit(EventKey.openWordListModal, {
|
||||
title: runtimeStore.editDict.name,
|
||||
translateLanguage: runtimeStore.editDict.translateLanguage,
|
||||
list: runtimeStore.editDict.words
|
||||
title: store.currentStudyWordDict.name,
|
||||
translateLanguage: store.currentStudyWordDict.translateLanguage,
|
||||
list: store.currentStudyWordDict.words
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import {useNav} from "@/utils";
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
|
||||
const headerRef = $ref<HTMLDivElement>(null)
|
||||
const moreOptionsRef = $ref<HTMLDivElement>(null)
|
||||
@@ -80,6 +80,9 @@ const {nav} = useNav()
|
||||
:title="`单词本(${settingStore.shortcutKeyMap[ShortcutKey.TogglePanel]})`"
|
||||
icon="tdesign:menu-unfold"/>
|
||||
</div>
|
||||
<div class="absolute left-1/2" style="transform: translateX(-50%)">
|
||||
{{ statisticsStore.step ? '复习' : '学习新词'}}
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip :title="settingStore.showToolbar?'收起':'展开'">
|
||||
<Icon icon="icon-park-outline:down"
|
||||
@@ -146,7 +149,6 @@ header {
|
||||
.options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
gap:.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
@@ -4,7 +4,7 @@ import {onMounted, onUnmounted} from "vue"
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
function format(val: number, suffix: string = '', check: number = -1) {
|
||||
@@ -12,16 +12,16 @@ function format(val: number, suffix: string = '', check: number = -1) {
|
||||
}
|
||||
|
||||
const progress = $computed(() => {
|
||||
if (!practiceStore.total) return 0
|
||||
if (practiceStore.index > practiceStore.total) return 100
|
||||
return ((practiceStore.index / practiceStore.total) * 100)
|
||||
if (!statisticsStore.total) return 0
|
||||
if (statisticsStore.index > statisticsStore.total) return 100
|
||||
return ((statisticsStore.index / statisticsStore.total) * 100)
|
||||
})
|
||||
|
||||
let speedMinute = $ref(0)
|
||||
let timer = $ref(0)
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => {
|
||||
speedMinute = Math.floor((Date.now() - practiceStore.startDate) / 1000 / 60)
|
||||
speedMinute = Math.floor((Date.now() - statisticsStore.startDate) / 1000 / 60)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
@@ -45,22 +45,22 @@ onUnmounted(() => {
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ practiceStore.total }}</div>
|
||||
<div class="num">{{ statisticsStore.total }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(practiceStore.inputWordNumber, '', 0) }}</div>
|
||||
<div class="num">{{ format(statisticsStore.inputWordNumber, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">输入数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(practiceStore.wrongWordNumber, '', 0) }}</div>
|
||||
<div class="num">{{ format(statisticsStore.wrongWordNumber, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">错误数</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ format(practiceStore.correctRate, '%') }}</div>
|
||||
<div class="num">{{ format(statisticsStore.correctRate, '%') }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">正确率</div>
|
||||
</div>
|
||||
|
||||
@@ -11,24 +11,21 @@ import {onMounted, reactive} from "vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {Icon} from '@iconify/vue';
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {dayjs} from "element-plus";
|
||||
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
let statModalIsOpen = $ref(false)
|
||||
let currentStat = reactive<DisplayStatistics>(cloneDeep(DefaultDisplayStatistics))
|
||||
const statStore = usePracticeStore()
|
||||
let open = $ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openStatModal, (stat: DisplayStatistics) => {
|
||||
if (stat) {
|
||||
currentStat = {...DefaultDisplayStatistics, ...stat}
|
||||
store.saveStatistics(stat)
|
||||
console.log('stat', stat)
|
||||
}
|
||||
statModalIsOpen = true
|
||||
emitter.on(EventKey.openStatModal, () => {
|
||||
open = true
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
statModalIsOpen = false
|
||||
open = false
|
||||
}
|
||||
|
||||
emitter.on(ShortcutKey.NextChapter, close)
|
||||
@@ -38,7 +35,7 @@ onMounted(() => {
|
||||
|
||||
|
||||
function options(emitType: 'write' | 'repeat' | 'next') {
|
||||
statModalIsOpen = false
|
||||
open = false
|
||||
emitter.emit(EventKey[emitType])
|
||||
}
|
||||
|
||||
@@ -53,7 +50,7 @@ const isEnd = $computed(() => {
|
||||
<template>
|
||||
<Dialog
|
||||
:header="false"
|
||||
v-model="statModalIsOpen">
|
||||
v-model="open">
|
||||
<div class="statistics relative flex flex-col gap-6">
|
||||
<header>
|
||||
<div class="text-2xl">{{ store.currentStudyWordDict.name }}</div>
|
||||
@@ -61,18 +58,20 @@ const isEnd = $computed(() => {
|
||||
<div class="flex justify-center gap-10">
|
||||
<div class="text-xl text-center flex flex-col justify-around">
|
||||
<div class="font-bold">非常棒!</div>
|
||||
<div>坚持了 <span class="color-green font-bold text-2xl">10</span> 分钟</div>
|
||||
<div>坚持了 <span class="color-green font-bold text-2xl">{{ dayjs().diff(statStore.startDate, 'm') }}</span>
|
||||
分钟
|
||||
</div>
|
||||
</div>
|
||||
<Ring
|
||||
:value="currentStat.wrongWordNumber"
|
||||
:value="statStore.newWordNumber"
|
||||
desc="New"
|
||||
:percentage="10"
|
||||
:percentage="35"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-center gap-10">
|
||||
<div class="flex justify-center items-center py-3 px-10 rounded-md color-red-500 flex-col"
|
||||
style="background: rgb(254,236,236)">
|
||||
<div class="text-3xl">3</div>
|
||||
<div class="text-3xl">{{ statStore.inputWordNumber }}</div>
|
||||
<div class="center gap-2">
|
||||
<Icon icon="iconamoon:close" class="text-2xl"/>
|
||||
错词
|
||||
@@ -80,7 +79,7 @@ const isEnd = $computed(() => {
|
||||
</div>
|
||||
<div class="flex justify-center items-center py-3 px-10 rounded-md color-green-600 flex-col"
|
||||
style="background: rgb(231,248,241)">
|
||||
<div class="text-3xl">3</div>
|
||||
<div class="text-3xl">{{ statStore.total - statStore.inputWordNumber }}</div>
|
||||
<div class="center gap-2">
|
||||
<Icon icon="tabler:check" class="text-2xl"/>
|
||||
正确
|
||||
@@ -96,11 +95,6 @@ const isEnd = $computed(() => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.DictationChapter]"
|
||||
@click="options('write')">
|
||||
默写
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.RepeatChapter]"
|
||||
@click="options('repeat')">
|
||||
@@ -109,12 +103,17 @@ const isEnd = $computed(() => {
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.NextChapter]"
|
||||
@click="options('next')">
|
||||
{{ isEnd ? '重新练习' : '继续' }}
|
||||
{{ isEnd ? '重新练习' : '再来一组' }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
type="primary"
|
||||
@click="options('next')">
|
||||
分享
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<Fireworks v-if="statModalIsOpen"/>
|
||||
<Fireworks v-if="open"/>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/style";
|
||||
|
||||
@@ -65,7 +65,7 @@ const playKeyboardAudio = usePlayKeyboardAudio()
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
|
||||
const store = useBaseStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
watch(() => props.article, () => {
|
||||
@@ -97,7 +97,7 @@ function nextSentence() {
|
||||
|
||||
//todo 计得把略过的单词加上统计里面去
|
||||
// if (!store.skipWordNamesWithSimpleWords.includes(currentWord.word.toLowerCase()) && !currentWord.isSymbol) {
|
||||
// practiceStore.inputNumber++
|
||||
// statisticsStore.inputNumber++
|
||||
// }
|
||||
|
||||
sentenceIndex++
|
||||
|
||||
@@ -31,7 +31,7 @@ import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import {useOnKeyboardEventListener} from "@/hooks/event.ts";
|
||||
|
||||
const store = useBaseStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let tabIndex = $ref(0)
|
||||
@@ -74,17 +74,15 @@ function setArticle(val: Article) {
|
||||
let tempVal = cloneDeep(val)
|
||||
articleData.articles[store.currentArticleDict.chapterIndex] = tempVal
|
||||
articleData.article = tempVal
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
practiceStore.repeatNumber = 0
|
||||
practiceStore.total = 0
|
||||
practiceStore.wrongWords = []
|
||||
practiceStore.startDate = Date.now()
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrongWordNumber = 0
|
||||
statisticsStore.total = 0
|
||||
statisticsStore.startDate = Date.now()
|
||||
articleData.article.sections.map((v, i) => {
|
||||
v.map((w, j) => {
|
||||
w.words.map(s => {
|
||||
if (!store.skipWordNamesWithSimpleWords.includes(s.word.toLowerCase()) && !s.isSymbol) {
|
||||
practiceStore.total++
|
||||
statisticsStore.total++
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -193,39 +191,33 @@ function wrong(word: Word) {
|
||||
store.wrong.originWords.push(word)
|
||||
}
|
||||
if (!store.skipWordNamesWithSimpleWords.includes(lowerName)) {
|
||||
if (!practiceStore.wrongWords.find((v) => v.word.toLowerCase() === lowerName)) {
|
||||
practiceStore.wrongWords.push(word)
|
||||
practiceStore.wrongWordNumber++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function over() {
|
||||
if (practiceStore.wrongWordNumber === 0) {
|
||||
if (statisticsStore.wrongWordNumber === 0) {
|
||||
// if (false) {
|
||||
console.log('这章节完了')
|
||||
let now = Date.now()
|
||||
let stat: DisplayStatistics = {
|
||||
startDate: practiceStore.startDate,
|
||||
startDate: statisticsStore.startDate,
|
||||
endDate: now,
|
||||
spend: now - practiceStore.startDate,
|
||||
total: practiceStore.total,
|
||||
spend: now - statisticsStore.startDate,
|
||||
total: statisticsStore.total,
|
||||
correctRate: -1,
|
||||
wrongWordNumber: practiceStore.wrongWordNumber,
|
||||
wrongWords: practiceStore.wrongWords,
|
||||
wrongWordNumber: statisticsStore.wrongWordNumber,
|
||||
}
|
||||
stat.correctRate = 100 - Math.trunc(((stat.wrongWordNumber) / (stat.total)) * 100)
|
||||
emitter.emit(EventKey.openStatModal, stat)
|
||||
} else {
|
||||
tabIndex = 1
|
||||
wordData.words = practiceStore.wrongWords
|
||||
wordData.index = 0
|
||||
}
|
||||
}
|
||||
|
||||
function nextWord(word: ArticleWord) {
|
||||
if (!store.skipWordNamesWithSimpleWords.includes(word.word.toLowerCase()) && !word.isSymbol) {
|
||||
practiceStore.inputWordNumber++
|
||||
statisticsStore.inputWordNumber++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted, watch} from "vue"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {DefaultDisplayStatistics, DictType, ShortcutKey, Sort, Word} from "@/types.ts";
|
||||
import {DefaultDisplayStatistics, DictType, getDefaultWord, ShortcutKey, Sort, Word} from "@/types.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts"
|
||||
import {cloneDeep, reverse, shuffle} from "lodash-es"
|
||||
import {usePracticeStore} from "@/stores/practice.ts"
|
||||
@@ -21,24 +21,29 @@ import MiniDialog from "@/pages/pc/components/dialog/MiniDialog.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
|
||||
interface IProps {
|
||||
words: Word[],
|
||||
index: number,
|
||||
data: {
|
||||
new: any[],
|
||||
review: any[],
|
||||
}
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
words: [],
|
||||
index: -1
|
||||
data: {
|
||||
new: [],
|
||||
review: [],
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:words': [val: Word[]],
|
||||
sort: [val: Word[]]
|
||||
sort: [val: Word[]],
|
||||
complete: [val: any]
|
||||
}>()
|
||||
|
||||
const typingRef: any = $ref()
|
||||
const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const {
|
||||
@@ -48,100 +53,73 @@ const {
|
||||
toggleWordSimple
|
||||
} = useWordOptions()
|
||||
|
||||
let data = $ref({
|
||||
index: props.index,
|
||||
words: props.words,
|
||||
let allWrongWords = []
|
||||
let current = $ref({
|
||||
index: 0,
|
||||
words: [],
|
||||
wrongWords: [],
|
||||
})
|
||||
|
||||
let stat = cloneDeep(DefaultDisplayStatistics)
|
||||
let showSortOption = $ref(false)
|
||||
useWindowClick(() => showSortOption = false)
|
||||
|
||||
watch(() => props.words, () => {
|
||||
data.words = props.words
|
||||
data.index = props.index
|
||||
data.wrongWords = []
|
||||
watch(() => props.data, () => {
|
||||
current.words = props.data.new
|
||||
current.index = 0
|
||||
current.wrongWords = []
|
||||
allWrongWords = []
|
||||
|
||||
practiceStore.wrongWords = []
|
||||
practiceStore.repeatNumber = 0
|
||||
practiceStore.startDate = Date.now()
|
||||
practiceStore.correctRate = -1
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
stat = cloneDeep(DefaultDisplayStatistics)
|
||||
|
||||
}, {immediate: true})
|
||||
|
||||
watch(data, () => {
|
||||
practiceStore.total = data.words.length
|
||||
practiceStore.index = data.index
|
||||
})
|
||||
statisticsStore.step = 0
|
||||
statisticsStore.startDate = Date.now()
|
||||
statisticsStore.correctRate = -1
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrongWordNumber = 0
|
||||
statisticsStore.total = props.data.review.concat(props.data.new).length
|
||||
statisticsStore.newWordNumber = props.data.new.length
|
||||
statisticsStore.index = 0
|
||||
}, {immediate: true, deep: true})
|
||||
|
||||
const word = $computed(() => {
|
||||
return data.words[data.index] ?? {
|
||||
trans: [],
|
||||
word: '',
|
||||
usphone: '',
|
||||
ukphone: '',
|
||||
}
|
||||
return current.words[current.index] ?? getDefaultWord()
|
||||
})
|
||||
|
||||
const prevWord: Word = $computed(() => {
|
||||
return data.words?.[data.index - 1] ?? undefined
|
||||
return current.words?.[current.index - 1] ?? undefined
|
||||
})
|
||||
|
||||
const nextWord: Word = $computed(() => {
|
||||
return data.words?.[data.index + 1] ?? undefined
|
||||
return current.words?.[current.index + 1] ?? undefined
|
||||
})
|
||||
|
||||
function next(isTyping: boolean = true) {
|
||||
if (data.index === data.words.length - 1) {
|
||||
|
||||
//复制当前错词,因为第一遍错词是最多的,后续的练习都是从错词中练习
|
||||
if (stat.total === -1) {
|
||||
let now = Date.now()
|
||||
stat = {
|
||||
startDate: practiceStore.startDate,
|
||||
endDate: now,
|
||||
spend: now - practiceStore.startDate,
|
||||
total: props.words.length,
|
||||
correctRate: -1,
|
||||
inputWordNumber: practiceStore.inputWordNumber,
|
||||
wrongWordNumber: data.wrongWords.length,
|
||||
wrongWords: data.wrongWords,
|
||||
}
|
||||
stat.correctRate = 100 - Math.trunc(((stat.wrongWordNumber) / (stat.total)) * 100)
|
||||
}
|
||||
|
||||
if (data.wrongWords.length) {
|
||||
if (current.index === current.words.length - 1) {
|
||||
if (current.wrongWords.length) {
|
||||
console.log('当前背完了,但还有错词')
|
||||
data.words = cloneDeep(data.wrongWords)
|
||||
|
||||
practiceStore.total = data.words.length
|
||||
practiceStore.index = data.index = 0
|
||||
practiceStore.inputWordNumber = 0
|
||||
practiceStore.wrongWordNumber = 0
|
||||
practiceStore.repeatNumber++
|
||||
data.wrongWords = []
|
||||
current.words = cloneDeep(current.wrongWords)
|
||||
current.wrongWords = []
|
||||
} else {
|
||||
console.log('这章节完了')
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
|
||||
let now = Date.now()
|
||||
stat.endDate = now
|
||||
stat.spend = now - stat.startDate
|
||||
|
||||
emitter.emit(EventKey.openStatModal, stat)
|
||||
console.log('这章节完了', statisticsStore.total)
|
||||
isTyping && statisticsStore.inputWordNumber++
|
||||
statisticsStore.speed = Date.now() - statisticsStore.startDate
|
||||
if (statisticsStore.step) {
|
||||
emitter.emit(EventKey.openStatModal, {})
|
||||
// emit('complete', {})
|
||||
} else {
|
||||
if (props.data.review.length) {
|
||||
settingStore.dictation = true
|
||||
statisticsStore.step++
|
||||
current.words = shuffle(props.data.review.concat(props.data.new))
|
||||
current.index = 0
|
||||
} else {
|
||||
emitter.emit(EventKey.openStatModal, {})
|
||||
// emit('complete', {})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data.index++
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
current.index++
|
||||
isTyping && statisticsStore.inputWordNumber++
|
||||
console.log('这个词完了')
|
||||
if ([DictType.word].includes(store.currentDict.type)
|
||||
&& store.skipWordNames.includes(word.word.toLowerCase())) {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +127,12 @@ function wordWrong() {
|
||||
if (!store.wrong2.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
store.wrong2.push(word)
|
||||
}
|
||||
if (!data.wrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
data.wrongWords.push(word)
|
||||
practiceStore.wrongWordNumber++
|
||||
if (!current.wrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
current.wrongWords.push(word)
|
||||
}
|
||||
if (!allWrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
|
||||
allWrongWords.push(word)
|
||||
statisticsStore.wrongWordNumber++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,10 +153,10 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
|
||||
//TODO 略过忽略的单词上
|
||||
function prev() {
|
||||
if (data.index === 0) {
|
||||
if (current.index === 0) {
|
||||
ElMessage.warning('已经是第一个了~')
|
||||
} else {
|
||||
data.index--
|
||||
current.index--
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,11 +190,11 @@ function play() {
|
||||
function sort(type: Sort) {
|
||||
if (type === Sort.reverse) {
|
||||
ElMessage.success('已翻转排序')
|
||||
emit('sort', reverse(cloneDeep(data.words)))
|
||||
emit('sort', reverse(cloneDeep(current.words)))
|
||||
}
|
||||
if (type === Sort.random) {
|
||||
ElMessage.success('已随机排序')
|
||||
emit('sort', shuffle(data.words))
|
||||
emit('sort', shuffle(current.words))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +267,7 @@ onUnmounted(() => {
|
||||
v-loading="!store.load"
|
||||
>
|
||||
<div class="list-header">
|
||||
<div>{{ data.words.length }}个单词</div>
|
||||
<div>{{ current.words.length }}个单词</div>
|
||||
<div style="position:relative;"
|
||||
@click.stop="null">
|
||||
<BaseIcon
|
||||
@@ -309,14 +290,14 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<WordList
|
||||
v-if="data.words.length"
|
||||
v-if="current.words.length"
|
||||
:is-active="active"
|
||||
:static="false"
|
||||
:show-word="!settingStore.dictation"
|
||||
:show-translate="settingStore.translate"
|
||||
:list="data.words"
|
||||
:activeIndex="data.index"
|
||||
@click="(val:any) => data.index = val.index"
|
||||
:list="current.words"
|
||||
:activeIndex="current.index"
|
||||
@click="(val:any) => current.index = val.index"
|
||||
>
|
||||
<template v-slot:suffix="{item,index}">
|
||||
<BaseIcon
|
||||
|
||||
@@ -10,33 +10,28 @@ import Statistics from "@/pages/pc/practice/Statistics.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
|
||||
import PracticeWord from "@/pages/pc/practice/practice-word/index.vue";
|
||||
import {ShortcutKey, Word} from "@/types.ts";
|
||||
import DictModal from "@/pages/pc/components/dialog/DictDiglog.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import Logo from "@/pages/pc/components/Logo.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import TypingWord from "@/pages/pc/practice/practice-word/TypingWord.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {syncMyDictList} from "@/hooks/dict.ts";
|
||||
import {cloneDeep, shuffle} from "lodash-es";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const statisticsStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
|
||||
watch(practiceStore, () => {
|
||||
if (practiceStore.inputWordNumber < 1) {
|
||||
return practiceStore.correctRate = -1
|
||||
watch(statisticsStore, () => {
|
||||
if (statisticsStore.inputWordNumber < 1) {
|
||||
return statisticsStore.correctRate = -1
|
||||
}
|
||||
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
|
||||
return practiceStore.correctRate = 0
|
||||
if (statisticsStore.wrongWordNumber > statisticsStore.inputWordNumber) {
|
||||
return statisticsStore.correctRate = 0
|
||||
}
|
||||
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.inputWordNumber)) * 100)
|
||||
statisticsStore.correctRate = 100 - Math.trunc(((statisticsStore.wrongWordNumber) / (statisticsStore.inputWordNumber)) * 100)
|
||||
})
|
||||
|
||||
function next() {
|
||||
@@ -133,20 +128,39 @@ onUnmounted(() => {
|
||||
emitter.off(ShortcutKey.TogglePanel, togglePanel)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getCurrentPractice()
|
||||
emitter.on(EventKey.changeDict, getCurrentPractice)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.changeDict, getCurrentPractice)
|
||||
})
|
||||
|
||||
let wordData = $ref({
|
||||
words: [],
|
||||
index: -1
|
||||
})
|
||||
let data = $ref({
|
||||
new: [],
|
||||
review: []
|
||||
})
|
||||
|
||||
function getCurrentPractice() {
|
||||
let c = store.currentStudy.word
|
||||
let wordDict = store.currentStudyWordDict;
|
||||
if (wordDict.words?.length) {
|
||||
wordData.index = 0
|
||||
wordData.statistics.map(v=>{
|
||||
wordData.words = []
|
||||
statisticsStore.step = 0
|
||||
for (let i = c.lastLearnIndex; i < wordDict.words.length; i++) {
|
||||
if (data.new.length >= c.perDayStudyNumber) break
|
||||
let item = wordDict.words[i]
|
||||
if (!store.skipWordNames.includes(item.word.toLowerCase())) {
|
||||
data.new.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
wordData.words = cloneDeep(wordDict.words.slice(c.lastLearnIndex, c.lastLearnIndex + c.perDayStudyNumber))
|
||||
emitter.emit(EventKey.resetWord)
|
||||
}
|
||||
}
|
||||
@@ -158,15 +172,9 @@ function sort(list: Word[]) {
|
||||
syncMyDictList(store.currentDict)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCurrentPractice()
|
||||
emitter.on(EventKey.changeDict, getCurrentPractice)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.changeDict, getCurrentPractice)
|
||||
})
|
||||
function complete() {
|
||||
|
||||
}
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
@@ -177,8 +185,8 @@ useStartKeyboardEventListener()
|
||||
<div class="flex flex-1">
|
||||
<TypingWord
|
||||
@sort="sort"
|
||||
v-model:words="wordData.words"
|
||||
:index="wordData.index"/>
|
||||
@complete="complete"
|
||||
:data="data"/>
|
||||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
|
||||
@@ -134,8 +134,8 @@ export const DefaultBaseState = (): BaseState => ({
|
||||
currentStudy: {
|
||||
word: {
|
||||
dictIndex: 0,
|
||||
perDayStudyNumber: 30,
|
||||
lastLearnIndex: 10,
|
||||
perDayStudyNumber: 3,
|
||||
lastLearnIndex: 0,
|
||||
},
|
||||
article: {
|
||||
dictIndex: 0,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import {defineStore} from "pinia"
|
||||
import {Word} from "@/types.ts"
|
||||
|
||||
export interface PracticeState {
|
||||
wrongWords: Word[],
|
||||
//todo 废弃
|
||||
repeatNumber: number,
|
||||
step: number,
|
||||
startDate: number,
|
||||
speed: number,
|
||||
total: number,
|
||||
index: number,//当前输入的第几个,用于和total计算进度
|
||||
newWordNumber: number,
|
||||
@@ -17,8 +15,8 @@ export interface PracticeState {
|
||||
export const usePracticeStore = defineStore('practice', {
|
||||
state: (): PracticeState => {
|
||||
return {
|
||||
wrongWords: [],
|
||||
repeatNumber: 0,
|
||||
step: 0,
|
||||
speed: 0,
|
||||
startDate: Date.now(),
|
||||
correctRate: -1,
|
||||
total: 0,
|
||||
|
||||
16
src/types.ts
16
src/types.ts
@@ -32,6 +32,17 @@ export const DefaultWord: Word = {
|
||||
trans: [],
|
||||
}
|
||||
|
||||
export function getDefaultWord(val?: any) {
|
||||
return {
|
||||
id:'',
|
||||
word: '',
|
||||
phonetic0: '',
|
||||
phonetic1: '',
|
||||
trans: [],
|
||||
...val
|
||||
}
|
||||
}
|
||||
|
||||
export type StudyWord = {
|
||||
type: 'new' | 'repeat' | 'wrong'
|
||||
word: Word
|
||||
@@ -122,8 +133,8 @@ export const DefaultArticle: Article = {
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
startIndex:number,
|
||||
endIndex:number,
|
||||
startIndex: number,
|
||||
endIndex: number,
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
spend: number,//花费时间
|
||||
@@ -173,6 +184,7 @@ export enum ShortcutKey {
|
||||
NextChapter = 'NextChapter',
|
||||
PreviousChapter = 'PreviousChapter',
|
||||
RepeatChapter = 'RepeatChapter',
|
||||
//todo 废弃
|
||||
DictationChapter = 'DictationChapter',
|
||||
PlayWordPronunciation = 'PlayWordPronunciation',
|
||||
// PlayTranslatePronunciation = 'PlayTranslatePronunciation',
|
||||
|
||||
@@ -200,3 +200,38 @@ export function useNav() {
|
||||
|
||||
return {nav, back: router.back}
|
||||
}
|
||||
|
||||
export function _dateFormat(val, type?): string {
|
||||
if (!val) return
|
||||
if (String(val).length === 10) {
|
||||
val = val * 1000
|
||||
}
|
||||
const d = new Date(Number(val))
|
||||
const year = d.getFullYear()
|
||||
const m = d.getMonth() + 1
|
||||
const mStr = m < 10 ? '0' + m : m
|
||||
const day = d.getDate()
|
||||
const dayStr = day < 10 ? '0' + day : day
|
||||
const h = d.getHours()
|
||||
const hStr = h < 10 ? '0' + h : h
|
||||
const min = d.getMinutes()
|
||||
const minStr = min < 10 ? '0' + min : min
|
||||
const sec = d.getSeconds()
|
||||
const secStr = sec < 10 ? '0' + sec : sec
|
||||
switch (type) {
|
||||
case 'Y':
|
||||
return year + ''
|
||||
case 'M':
|
||||
return `${year}-${mStr}`
|
||||
case 'M_D':
|
||||
return `${mStr}-${dayStr}`
|
||||
case 'M_CN':
|
||||
return `${year}年${mStr}月`
|
||||
case 'D':
|
||||
return `${year}-${mStr}-${dayStr}`
|
||||
case 'm':
|
||||
return `${year}-${mStr}-${dayStr} ${hStr}:${minStr}`
|
||||
default:
|
||||
return `${year}-${mStr}-${dayStr} ${hStr}:${minStr}:${secStr}`
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user