From 30be8e1cf5e69f0def1bebfc0536fc2a6406d038 Mon Sep 17 00:00:00 2001 From: Zyronon Date: Mon, 22 Dec 2025 18:46:42 +0800 Subject: [PATCH 01/28] wip --- .cursorignore | 4 ++++ src/pages/word/DictDetail.vue | 14 +++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 .cursorignore diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 00000000..81a3b4f9 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,4 @@ +# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) + +public/ +js_node/ diff --git a/src/pages/word/DictDetail.vue b/src/pages/word/DictDetail.vue index 35098a90..3886172d 100644 --- a/src/pages/word/DictDetail.vue +++ b/src/pages/word/DictDetail.vue @@ -592,16 +592,20 @@ defineRender(() => { > 编辑 + + 测试 + 学习 - - 测试 - -
介绍:{runtimeStore.editDict.description}
-
+ {dict.description && ( + <> +
介绍:{dict.description}
+
+ + )} {/* 移动端标签页导航 */} {isMob && isOperate && ( From 560546c3bc7d026b072cdde14ade8b9e754df571 Mon Sep 17 00:00:00 2001 From: Zyronon Date: Tue, 23 Dec 2025 19:48:04 +0800 Subject: [PATCH 02/28] wip --- src/assets/css/style.scss | 9 +- src/components/BaseButton.vue | 4 +- src/components/base/BaseInput.vue | 2 +- src/pages/word/PracticeWords.vue | 104 ++++++++- src/pages/word/WordsPage.vue | 103 ++++++--- src/pages/word/components/Footer.vue | 216 ++++++++++-------- .../word/components/PracticeSettingDialog.vue | 150 ++++++------ .../ShufflePracticeSettingDialog.vue | 22 +- src/types/types.ts | 6 +- 9 files changed, 392 insertions(+), 224 deletions(-) diff --git a/src/assets/css/style.scss b/src/assets/css/style.scss index e94aec23..4fbe6a3e 100644 --- a/src/assets/css/style.scss +++ b/src/assets/css/style.scss @@ -52,6 +52,7 @@ --btn-primary: rgb(75, 85, 99); --btn-info: white; + --btn-info-hover: #eaeaea; --color-primary: #E6E8EB; --color-second: rgb(247, 247, 247); @@ -119,7 +120,8 @@ html.dark { --color-sub-gray: #383737; --color-scrollbar: rgb(92, 93, 94); - --btn-info: transparent; + --btn-info: #1b1b1b; + --btn-info-hover: #3a3a3a; --color-input-color: white; --color-input-bg: rgba(14, 18, 23, 1); @@ -536,3 +538,8 @@ a { margin-left: 0 !important; } } + +.target-number { + @apply text-3xl! mx-2; + color: rgb(176, 116, 211)!important; +} diff --git a/src/components/BaseButton.vue b/src/components/BaseButton.vue index d0d3482e..a602240f 100644 --- a/src/components/BaseButton.vue +++ b/src/components/BaseButton.vue @@ -51,7 +51,7 @@ defineEmits(['click']) justify-content: center; outline: none; text-align: center; - transition: .1s; + transition: all .3s; user-select: none; vertical-align: middle; white-space: nowrap; @@ -121,7 +121,7 @@ defineEmits(['click']) color: var(--color-main-text); &:hover:not(.disabled) { - opacity: 0.6; + background: var(--btn-info-hover); } } diff --git a/src/components/base/BaseInput.vue b/src/components/base/BaseInput.vue index d57853b1..5ece795d 100644 --- a/src/components/base/BaseInput.vue +++ b/src/components/base/BaseInput.vue @@ -1,5 +1,5 @@ diff --git a/src/types/types.ts b/src/types/types.ts index 7a80f247..1af5fd77 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -226,7 +226,11 @@ export enum PracticeArticleWordType { //练习模式 export enum WordPracticeMode { System = 0, - Free = 1 + Free = 1, + DictationOnly = 2, // 独立默写模式 + ListenOnly = 3, // 独立听写模式 + IdentifyOnly = 4, // 独立自测模式 + FollowWriteOnly = 5 // 独立跟写模式(内部会自动切换到 Spell) } //练习类型 From 7a3475cee10a72f1e1242894a8540afa2ac74d63 Mon Sep 17 00:00:00 2001 From: Zyronon Date: Wed, 24 Dec 2025 02:10:24 +0800 Subject: [PATCH 03/28] wip --- components.d.ts | 4 +- package.json | 1 + pnpm-lock.yaml | 10 + src/config/env.ts | 3 +- src/hooks/dict.ts | 12 +- src/pages/word/PracticeWords.vue | 489 +++++++++++++-------------- src/pages/word/WordsPage.vue | 33 +- src/pages/word/components/Footer.vue | 73 ++-- src/stores/practice.ts | 23 +- src/types/types.ts | 395 ++++++++++++---------- 10 files changed, 537 insertions(+), 506 deletions(-) diff --git a/components.d.ts b/components.d.ts index 20e2399b..ea0d8f0b 100644 --- a/components.d.ts +++ b/components.d.ts @@ -19,7 +19,6 @@ declare module 'vue' { BaseList: typeof import('./src/components/list/BaseList.vue')['default'] BasePage: typeof import('./src/components/BasePage.vue')['default'] BaseTable: typeof import('./src/components/BaseTable.vue')['default'] - BaseTable2: typeof import('./src/components/BaseTable2.vue')['default'] Book: typeof import('./src/components/Book.vue')['default'] ChannelIcons: typeof import('./src/components/ChannelIcons/ChannelIcons.vue')['default'] Checkbox: typeof import('./src/components/base/checkbox/Checkbox.vue')['default'] @@ -51,7 +50,6 @@ declare module 'vue' { IconFluentArrowClockwise20Regular: typeof import('~icons/fluent/arrow-clockwise20-regular')['default'] IconFluentArrowDownload20Regular: typeof import('~icons/fluent/arrow-download20-regular')['default'] IconFluentArrowLeft16Regular: typeof import('~icons/fluent/arrow-left16-regular')['default'] - IconFluentArrowMove20Regular: typeof import('~icons/fluent/arrow-move20-regular')['default'] IconFluentArrowRepeatAll20Regular: typeof import('~icons/fluent/arrow-repeat-all20-regular')['default'] IconFluentArrowRight16Regular: typeof import('~icons/fluent/arrow-right16-regular')['default'] IconFluentArrowShuffle16Regular: typeof import('~icons/fluent/arrow-shuffle16-regular')['default'] @@ -125,10 +123,12 @@ declare module 'vue' { IconMaterialSymbolsMail: typeof import('~icons/material-symbols/mail')['default'] IconMdiSparkles: typeof import('~icons/mdi/sparkles')['default'] IconPhExportLight: typeof import('~icons/ph/export-light')['default'] + IconPhMicrosoftWordLogoLight: typeof import('~icons/ph/microsoft-word-logo-light')['default'] IconRiTwitterFill: typeof import('~icons/ri/twitter-fill')['default'] IconSimpleIconsGithub: typeof import('~icons/simple-icons/github')['default'] IconSimpleIconsWechat: typeof import('~icons/simple-icons/wechat')['default'] IconSimpleIconsXiaohongshu: typeof import('~icons/simple-icons/xiaohongshu')['default'] + IconStreamlineColorPenDrawFlat: typeof import('~icons/streamline-color/pen-draw-flat')['default'] IconStreamlineDiscountPercentCoupon: typeof import('~icons/streamline/discount-percent-coupon')['default'] IconSystemUiconsImport: typeof import('~icons/system-uicons/import')['default'] IconUiwAlipay: typeof import('~icons/uiw/alipay')['default'] diff --git a/package.json b/package.json index f8e89b2a..ac71b78e 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "@iconify-json/ri": "^1.2.5", "@iconify-json/simple-icons": "^1.2.48", "@iconify-json/streamline": "^1.2.5", + "@iconify-json/streamline-color": "^1.2.2", "@iconify-json/system-uicons": "^1.2.4", "@iconify-json/uiw": "^1.2.3", "@types/file-saver": "^2.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5240a8fa..5910fe57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: '@iconify-json/streamline': specifier: ^1.2.5 version: 1.2.5 + '@iconify-json/streamline-color': + specifier: ^1.2.2 + version: 1.2.2 '@iconify-json/system-uicons': specifier: ^1.2.4 version: 1.2.4 @@ -571,6 +574,9 @@ packages: '@iconify-json/simple-icons@1.2.48': resolution: {integrity: sha512-EACOtZMoPJtERiAbX1De0asrrCtlwI27+03c9OJlYWsly9w1O5vcD8rTzh+kDPjo+K8FOVnq2Qy+h/CzljSKDA==} + '@iconify-json/streamline-color@1.2.2': + resolution: {integrity: sha512-+ypc4kzeKTFbHVM2uNWFqt1iR7E4XeMTI3Wa3LcmeHLPjyk6u6e9NJqqOHUU02rpA8GaN80XHySXzX+4DuZbzA==} + '@iconify-json/streamline@1.2.5': resolution: {integrity: sha512-u6l9BOJoIIPjjDXWl6D/hPDzeBk5WiaEHZ+U9SbkQ14N9hgotaYyIZVMfgF175CG1TTS06j8k15D3FM2OYaFIw==} @@ -4263,6 +4269,10 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/streamline-color@1.2.2': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/streamline@1.2.5': dependencies: '@iconify/types': 2.0.0 diff --git a/src/config/env.ts b/src/config/env.ts index c0c1ee9c..81d15e4d 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -19,7 +19,7 @@ export const ENV = Object.assign(map['DEV'], common) export let AppEnv = { TOKEN: localStorage.getItem('token') ?? '', - IS_OFFICIAL: true, + IS_OFFICIAL: false, IS_LOGIN: false, CAN_REQUEST: false, } @@ -92,6 +92,7 @@ export const TourConfig = { total: 7, } +export const IS_DEV = import.meta.env.MODE === 'development' export const LIB_JS_URL = { SHEPHERD: import.meta.env.MODE === 'development' diff --git a/src/hooks/dict.ts b/src/hooks/dict.ts index 1989ff1e..8253688b 100644 --- a/src/hooks/dict.ts +++ b/src/hooks/dict.ts @@ -144,12 +144,12 @@ export function getCurrentStudyWord(): TaskWords { } } - //如果是自由模式,那么统统设置到new字段里面去 - if (settingStore.wordPracticeMode === WordPracticeMode.Free) { - data.new = data.new.length ? data.new : data.review - data.review = [] - return data - } + // //如果是自由模式,那么统统设置到new字段里面去 + // if (settingStore.wordPracticeMode === WordPracticeMode.Free) { + // data.new = data.new.length ? data.new : data.review + // data.review = [] + // return data + // } // 上上次更早的单词 //默认只取start之前的单词 diff --git a/src/pages/word/PracticeWords.vue b/src/pages/word/PracticeWords.vue index 5f851143..d53c966e 100644 --- a/src/pages/word/PracticeWords.vue +++ b/src/pages/word/PracticeWords.vue @@ -12,6 +12,7 @@ import { TaskWords, Word, WordPracticeMode, + WordPracticeStage, WordPracticeType, } from '@/types/types.ts' import { @@ -45,7 +46,14 @@ import { getDefaultDict, getDefaultWord } from '@/types/func.ts' import ConflictNotice from '@/components/ConflictNotice.vue' import PracticeLayout from '@/components/PracticeLayout.vue' -import { AppEnv, DICT_LIST, LIB_JS_URL, PracticeSaveWordKey, TourConfig } from '@/config/env.ts' +import { + AppEnv, + DICT_LIST, + IS_DEV, + LIB_JS_URL, + PracticeSaveWordKey, + TourConfig, +} from '@/config/env.ts' import { ToastInstance } from '@/components/base/toast/type.ts' import { watchOnce } from '@vueuse/core' import { setUserDictProp } from '@/apis' @@ -115,11 +123,11 @@ async function loadDict() { } watch( - () => store.load, - n => { - if (n && loading) loadDict() - }, - { immediate: true } + () => store.load, + n => { + if (n && loading) loadDict() + }, + { immediate: true } ) onMounted(() => { @@ -144,50 +152,50 @@ onUnmounted(() => { }) watchOnce( - () => data.words.length, - (newVal, oldVal) => { - //如果是从无值变有值,代表是开始 - if (!oldVal && newVal) { - _nextTick(async () => { - const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD) - const tour = new Shepherd.Tour(TourConfig) - tour.on('cancel', () => { - localStorage.setItem('tour-guide', '1') - }) - tour.addStep({ - id: 'step5', - text: '这里可以练习拼写单词,只需要按下键盘上对应的按键即可,没有输入框!', - attachTo: { element: '#word', on: 'bottom' }, - buttons: [ - { - text: `下一步(5/${TourConfig.total})`, - action: tour.next, - }, - ], - }) + () => data.words.length, + (newVal, oldVal) => { + //如果是从无值变有值,代表是开始 + if (!oldVal && newVal) { + _nextTick(async () => { + const Shepherd = await loadJsLib('Shepherd', LIB_JS_URL.SHEPHERD) + const tour = new Shepherd.Tour(TourConfig) + tour.on('cancel', () => { + localStorage.setItem('tour-guide', '1') + }) + tour.addStep({ + id: 'step5', + text: '这里可以练习拼写单词,只需要按下键盘上对应的按键即可,没有输入框!', + attachTo: { element: '#word', on: 'bottom' }, + buttons: [ + { + text: `下一步(5/${TourConfig.total})`, + action: tour.next, + }, + ], + }) - tour.addStep({ - id: 'step6', - text: '这里是文章练习', - attachTo: { element: '#article', on: 'top' }, - buttons: [ - { - text: `下一步(6/${TourConfig.total})`, - action() { - tour.next() - router.push('/articles') - }, + tour.addStep({ + id: 'step6', + text: '这里是文章练习', + attachTo: { element: '#article', on: 'top' }, + buttons: [ + { + text: `下一步(6/${TourConfig.total})`, + action() { + tour.next() + router.push('/articles') }, - ], - }) + }, + ], + }) - const r = localStorage.getItem('tour-guide') - if (settingStore.first && !r && !isMobile()) { - tour.start() - } - }, 500) - } + const r = localStorage.getItem('tour-guide') + if (settingStore.first && !r && !isMobile()) { + tour.start() + } + }, 500) } + } ) useStartKeyboardEventListener() @@ -197,6 +205,10 @@ function initData(initVal: TaskWords, init: boolean = false) { let d = localStorage.getItem(PracticeSaveWordKey.key) if (d && init) { try { + //todo 记得删除 + if (IS_DEV) { + throw new Error('开发环境,抛出错误跳过缓存') + } let obj = JSON.parse(d) let s = obj.val taskWords = Object.assign(taskWords, s.taskWords) @@ -211,71 +223,41 @@ function initData(initVal: TaskWords, init: boolean = false) { // taskWords = initVal //不能直接赋值,会导致 inject 的数据为默认值 taskWords = Object.assign(taskWords, initVal) - - // 检查是否为独立模式 - const isStandaloneMode = settingStore.wordPracticeMode >= WordPracticeMode.DictationOnly - - if (isStandaloneMode) { - // 独立模式:根据模式设置对应的练习类型 - switch (settingStore.wordPracticeMode) { - case WordPracticeMode.DictationOnly: - settingStore.wordPracticeType = WordPracticeType.Dictation - break - case WordPracticeMode.ListenOnly: - settingStore.wordPracticeType = WordPracticeType.Listen - break - case WordPracticeMode.IdentifyOnly: - settingStore.wordPracticeType = WordPracticeType.Identify - break - case WordPracticeMode.FollowWriteOnly: - settingStore.wordPracticeType = WordPracticeType.FollowWrite - break - } - - // 独立模式:按优先级选择起始单词列表(新词 -> 复习上次 -> 复习之前) - let selectedWords: Word[] = [] - if (taskWords.new.length > 0) { - currentWordListStage = 'new' - selectedWords = taskWords.new - } else if (taskWords.review.length > 0) { - currentWordListStage = 'review' - selectedWords = taskWords.review - } else if (taskWords.write.length > 0) { - currentWordListStage = 'write' - selectedWords = taskWords.write - } else { - Toast.warning('没有可学习的单词!') - router.push('/word') - return - } - - data.words = selectedWords - statStore.step = 0 // 独立模式不使用 step 逻辑 - statStore.total = taskWords.review.length + taskWords.new.length + taskWords.write.length - statStore.newWordNumber = taskWords.new.length - statStore.reviewWordNumber = taskWords.review.length - statStore.writeWordNumber = taskWords.write.length - } else if (taskWords.shuffle.length === 0) { - // 原有的智能模式逻辑 + + if (taskWords.shuffle.length === 0) { if (taskWords.new.length === 0) { if (taskWords.review.length) { settingStore.wordPracticeType = WordPracticeType.Identify statStore.step = 3 + statStore.stage = WordPracticeStage.IdentifyReview data.words = taskWords.review } else { if (taskWords.write.length) { settingStore.wordPracticeType = WordPracticeType.Identify data.words = taskWords.write statStore.step = 6 + statStore.stage = WordPracticeStage.IdentifyReviewAll } else { Toast.warning('没有可学习的单词!') router.push('/word') } } } else { - settingStore.wordPracticeType = WordPracticeType.FollowWrite data.words = taskWords.new statStore.step = 0 + if (settingStore.wordPracticeMode === WordPracticeMode.System) { + statStore.stage = WordPracticeStage.FollowWriteNewWord + } else if (settingStore.wordPracticeMode === WordPracticeMode.Free) { + statStore.stage = WordPracticeStage.FollowWriteNewWord + } else if (settingStore.wordPracticeMode === WordPracticeMode.IdentifyOnly) { + statStore.stage = WordPracticeStage.IdentifyNewWord + } else if (settingStore.wordPracticeMode === WordPracticeMode.DictationOnly) { + statStore.stage = WordPracticeStage.DictationNewWord + } else if (settingStore.wordPracticeMode === WordPracticeMode.ListenOnly) { + statStore.stage = WordPracticeStage.ListenNewWord + } else if (settingStore.wordPracticeMode === WordPracticeMode.FollowWriteOnly) { + statStore.stage = WordPracticeStage.FollowWriteNewWord + } } statStore.total = taskWords.review.length + taskWords.new.length + taskWords.write.length statStore.newWordNumber = taskWords.new.length @@ -285,6 +267,7 @@ function initData(initVal: TaskWords, init: boolean = false) { settingStore.wordPracticeType = WordPracticeType.Dictation data.words = taskWords.shuffle statStore.step = 10 + statStore.stage = WordPracticeStage.Shuffle statStore.total = taskWords.shuffle.length statStore.newWordNumber = 0 statStore.reviewWordNumber = 0 @@ -321,31 +304,34 @@ const nextWord: Word = $computed(() => { }) watch( - () => settingStore.wordPracticeType, - n => { - // Free 模式不自动设置,System 模式和独立模式都需要设置 - if (settingStore.wordPracticeMode === WordPracticeMode.Free) return - switch (n) { - case WordPracticeType.Spell: - case WordPracticeType.Dictation: - settingStore.dictation = true - settingStore.translate = true - break - case WordPracticeType.Listen: - settingStore.dictation = true - settingStore.translate = false - break - case WordPracticeType.FollowWrite: - settingStore.dictation = false - settingStore.translate = true - break - case WordPracticeType.Identify: - settingStore.dictation = false - settingStore.translate = false - break - } - }, - { immediate: true } + () => settingStore.wordPracticeMode, + n => { + // Free 模式不自动设置,System 模式和独立模式都需要设置 + if (settingStore.wordPracticeMode === WordPracticeMode.Free) return + switch (n) { + case WordPracticeMode.DictationOnly: + settingStore.dictation = true + settingStore.translate = true + settingStore.wordPracticeType = WordPracticeType.Dictation + break + case WordPracticeMode.ListenOnly: + settingStore.dictation = true + settingStore.translate = false + settingStore.wordPracticeType = WordPracticeType.Listen + break + case WordPracticeMode.FollowWriteOnly: + settingStore.dictation = false + settingStore.translate = true + settingStore.wordPracticeType = WordPracticeType.FollowWrite + break + case WordPracticeMode.IdentifyOnly: + settingStore.dictation = false + settingStore.translate = false + settingStore.wordPracticeType = WordPracticeType.Identify + break + } + }, + { immediate: true } ) const groupSize = 7 @@ -389,11 +375,29 @@ function goNextStep(originList, mode, msg) { } } +function nextStage(originList, log, nextStage) { + //每次都判断,因为每次都可能新增已掌握的单词 + let list = originList.filter(v => !data.excludeWords.includes(v.word)) + console.log(log) + if (list.length) { + if (toastInstance) toastInstance.close() + toastInstance = Toast.info('输入完成后按空格键切换下一个', { duration: 5000 }) + data.words = list + data.index = 0 + statStore.stage = nextStage + } else { + console.log(log + ':无单词略过') + statStore.stage = nextStage + next() + } +} + async function next(isTyping: boolean = true) { + debugger if (isTyping) statStore.inputWordNumber++ if (settingStore.wordPracticeMode === WordPracticeMode.Free) { if (data.index === data.words.length - 1) { - data.wrongWords = data.wrongWords.filter(v => (!data.excludeWords.includes(v.word))) + data.wrongWords = data.wrongWords.filter(v => !data.excludeWords.includes(v.word)) if (data.wrongWords.length) { isTypingWrongWord.value = true settingStore.wordPracticeType = WordPracticeType.FollowWrite @@ -410,62 +414,9 @@ async function next(isTyping: boolean = true) { } else { data.index++ } - } else if (settingStore.wordPracticeMode >= WordPracticeMode.DictationOnly) { - // 独立模式 - if (data.index === data.words.length - 1) { - // 处理错词 - data.wrongWords = data.wrongWords.filter(v => (!data.excludeWords.includes(v.word))) - if (data.wrongWords.length) { - isTypingWrongWord.value = true - console.log('当前学完了,但还有错词') - data.words = shuffle(cloneDeep(data.wrongWords)) - data.index = 0 - data.wrongWords = [] - } else { - isTypingWrongWord.value = false - // 按顺序切换到下一个单词列表:新词 -> 复习上次 -> 复习之前 -> 结束 - let nextWords: Word[] = [] - let nextStage: 'new' | 'review' | 'write' | 'finished' = 'finished' - - if (currentWordListStage === 'new') { - // 新词完成,切换到复习上次 - if (taskWords.review.length > 0) { - nextWords = taskWords.review - nextStage = 'review' - } else if (taskWords.write.length > 0) { - // 如果没有复习上次,直接跳到复习之前 - nextWords = taskWords.write - nextStage = 'write' - } - } else if (currentWordListStage === 'review') { - // 复习上次完成,切换到复习之前 - if (taskWords.write.length > 0) { - nextWords = taskWords.write - nextStage = 'write' - } - } - // currentWordListStage === 'write' 时,nextStage 保持为 'finished' - - if (nextStage === 'finished') { - // 全部完成 - console.log('独立模式,全完学完了') - showStatDialog = true - clearInterval(timer) - setTimeout(() => localStorage.removeItem(PracticeSaveWordKey.key), 300) - } else { - // 切换到下一个阶段 - currentWordListStage = nextStage - data.words = nextWords - data.index = 0 - // 保持相同的练习类型 - } - } - } else { - data.index++ - } } else { if (data.index === data.words.length - 1) { - if (statStore.step === 0 || isTypingWrongWord.value) { + if (statStore.stage === WordPracticeStage.FollowWriteNewWord || isTypingWrongWord.value) { if (settingStore.wordPracticeType !== WordPracticeType.Spell) { //回到最后一组的开始位置 data.index = Math.floor(data.index / groupSize) * groupSize @@ -474,7 +425,7 @@ async function next(isTyping: boolean = true) { return } } - data.wrongWords = data.wrongWords.filter(v => (!data.excludeWords.includes(v.word))) + data.wrongWords = data.wrongWords.filter(v => !data.excludeWords.includes(v.word)) if (data.wrongWords.length) { isTypingWrongWord.value = true settingStore.wordPracticeType = WordPracticeType.FollowWrite @@ -485,57 +436,87 @@ async function next(isTyping: boolean = true) { } else { isTypingWrongWord.value = false console.log('当前学完了,没错词', statStore.total, statStore.step, data.index) - //学完了,这里第 7 步如果无单词,加 3 就是 9 了 - if (statStore.step >= 8) { + + const complete = () => { console.log('全完学完了') showStatDialog = true clearInterval(timer) setTimeout(() => localStorage.removeItem(PracticeSaveWordKey.key), 300) - return; } - //开始默写之前 - if (statStore.step === 7) { - return goNextStep(shuffle(taskWords.write), WordPracticeType.Dictation, '开始默写之前') - } - - //开始听写之前 - if (statStore.step === 6) { - return goNextStep(shuffle(taskWords.write), WordPracticeType.Listen, '开始听写之前') - } - - //开始自测之前 - if (statStore.step === 5) { - return goNextStep(taskWords.write, WordPracticeType.Identify, '开始自测之前') - } - - //开始默写上次 - if (statStore.step === 4) { - return goNextStep(shuffle(taskWords.review), WordPracticeType.Dictation, '开始默写上次') - } - - //开始听写上次 - if (statStore.step === 3) { - return goNextStep(shuffle(taskWords.review), WordPracticeType.Listen, '开始听写上次') - } - - //开始自测昨日 - if (statStore.step === 2) { - return goNextStep(taskWords.review, WordPracticeType.Identify, '开始自测昨日') - } - - //开始默写新词 - if (statStore.step === 1) { - return goNextStep(shuffle(taskWords.new), WordPracticeType.Dictation, '开始默写新词') - } - - //开始听写新词 - if (statStore.step === 0) { - return goNextStep(shuffle(taskWords.new), WordPracticeType.Listen, '开始听写新词') + if (settingStore.wordPracticeMode === WordPracticeMode.System) { + if (statStore.stage === WordPracticeStage.FollowWriteNewWord) { + settingStore.wordPracticeType = WordPracticeType.Listen + return nextStage(shuffle(taskWords.new), '开始听写新词', WordPracticeStage.ListenNewWord) + } + if (statStore.stage === WordPracticeStage.ListenNewWord) { + settingStore.wordPracticeType = WordPracticeType.Dictation + return nextStage(shuffle(taskWords.new), '开始默写新词', WordPracticeStage.DictationNewWord) + } + if (statStore.stage === WordPracticeStage.DictationNewWord) { + settingStore.wordPracticeType = WordPracticeType.Identify + return nextStage(taskWords.review, '开始自测昨日', WordPracticeStage.IdentifyReview) + } + if (statStore.stage === WordPracticeStage.IdentifyReview) { + settingStore.wordPracticeType = WordPracticeType.Listen + return nextStage(shuffle(taskWords.review), '开始听写上次', WordPracticeStage.ListenReview) + } + if (statStore.stage === WordPracticeStage.ListenReview) { + settingStore.wordPracticeType = WordPracticeType.Dictation + return nextStage(shuffle(taskWords.review), '开始默写上次', WordPracticeStage.DictationReview) + } + if (statStore.stage === WordPracticeStage.DictationReview) { + settingStore.wordPracticeType = WordPracticeType.Identify + return nextStage(taskWords.write, '开始自测之前', WordPracticeStage.IdentifyReviewAll) + } + if (statStore.stage === WordPracticeStage.IdentifyReviewAll) { + settingStore.wordPracticeType = WordPracticeType.Listen + return nextStage(shuffle(taskWords.review), '开始听写之前', WordPracticeStage.ListenReviewAll) + } + if (statStore.stage === WordPracticeStage.ListenReviewAll) { + settingStore.wordPracticeType = WordPracticeType.Dictation + return nextStage(shuffle(taskWords.review), '开始默写之前', WordPracticeStage.DictationReviewAll) + } + if (statStore.stage === WordPracticeStage.DictationReviewAll) { + complete() + } + } else if (settingStore.wordPracticeMode === WordPracticeMode.ListenOnly) { + settingStore.wordPracticeType = WordPracticeType.Listen + if (statStore.stage === WordPracticeStage.ListenNewWord) { + return nextStage(taskWords.review, '开始听写昨日', WordPracticeStage.ListenReview) + } + if (statStore.stage === WordPracticeStage.ListenReview) { + return nextStage(taskWords.write, '开始听写之前', WordPracticeStage.ListenReviewAll) + } + if (statStore.stage === WordPracticeStage.ListenReviewAll) { + complete() + } + } else if (settingStore.wordPracticeMode === WordPracticeMode.DictationOnly) { + settingStore.wordPracticeType = WordPracticeType.Dictation + if (statStore.stage === WordPracticeStage.DictationNewWord) { + return nextStage(taskWords.review, '开始默写昨日', WordPracticeStage.DictationReview) + } + if (statStore.stage === WordPracticeStage.DictationReview) { + return nextStage(taskWords.write, '开始默写之前', WordPracticeStage.DictationReviewAll) + } + if (statStore.stage === WordPracticeStage.DictationReviewAll) { + complete() + } + } else if (settingStore.wordPracticeMode === WordPracticeMode.IdentifyOnly) { + settingStore.wordPracticeType = WordPracticeType.Identify + if (statStore.stage === WordPracticeStage.IdentifyNewWord) { + return nextStage(taskWords.review, '开始自测昨日', WordPracticeStage.IdentifyReview) + } + if (statStore.stage === WordPracticeStage.IdentifyReview) { + return nextStage(taskWords.write, '开始自测之前', WordPracticeStage.IdentifyReviewAll) + } + if (statStore.stage === WordPracticeStage.IdentifyReviewAll) { + complete() + } } } } else { - if (statStore.step === 0) { + if (statStore.stage === WordPracticeStage.FollowWriteNewWord) { wordLoop() } else { if (isTypingWrongWord.value) wordLoop() @@ -580,15 +561,15 @@ function onTypeWrong() { function savePracticeData() { // console.log('savePracticeData') localStorage.setItem( - PracticeSaveWordKey.key, - JSON.stringify({ - version: PracticeSaveWordKey.version, - val: { - taskWords, - practiceData: data, - statStoreData: statStore.$state, - }, - }) + PracticeSaveWordKey.key, + JSON.stringify({ + version: PracticeSaveWordKey.version, + val: { + taskWords, + practiceData: data, + statStoreData: statStore.$state, + }, + }) ) } @@ -699,8 +680,8 @@ async function continueStudy() { if (taskWords.shuffle.length) { let ignoreList = [store.allIgnoreWords, store.knownWords][settingStore.ignoreSimpleWord ? 0 : 1] temp.shuffle = shuffle(store.sdict.words.filter(v => !ignoreList.includes(v.word))).slice( - 0, - runtimeStore.routeData.total + 0, + runtimeStore.routeData.total ) if (showStatDialog) showStatDialog = false } else { @@ -782,9 +763,9 @@ useEvents([
@@ -795,11 +776,11 @@ useEvents([
@@ -809,18 +790,18 @@ useEvents([
{{ store.sdict.name }} ({{ store.sdict.lastLearnIndex }} / + >{{ store.sdict.name }} ({{ store.sdict.lastLearnIndex }} / {{ store.sdict.length }}) @@ -828,14 +809,14 @@ useEvents([
@@ -844,12 +825,12 @@ useEvents([ diff --git a/src/pages/word/WordsPage.vue b/src/pages/word/WordsPage.vue index 56273fc1..dc2e0482 100644 --- a/src/pages/word/WordsPage.vue +++ b/src/pages/word/WordsPage.vue @@ -130,6 +130,7 @@ function startPractice(practiceMode?: WordPracticeMode): void { } // 如果传入了独立模式,临时设置 wordPracticeMode if (practiceMode !== undefined) { + //todo 临时处理 localStorage.removeItem(PracticeSaveWordKey.key) settingStore.wordPracticeMode = practiceMode } @@ -377,16 +378,14 @@ let isNewHost = $ref(window.location.host === Host)
{{ currentStudy.new.length }}
新词数
- +
+
{{ currentStudy.review.length }}
+
复习上次
+
+
+
{{ currentStudy.write.length }}
+
复习之前
+
+ +
+ 智能 +
+
自由练习 diff --git a/src/pages/word/components/Footer.vue b/src/pages/word/components/Footer.vue index 48479272..f959ee57 100644 --- a/src/pages/word/components/Footer.vue +++ b/src/pages/word/components/Footer.vue @@ -2,7 +2,7 @@ import { inject, Ref } from 'vue' import { usePracticeStore } from '@/stores/practice.ts' import { useSettingStore } from '@/stores/setting.ts' -import { PracticeData, ShortcutKey } from '@/types/types.ts' +import { PracticeData, ShortcutKey, WordPracticeStage } from '@/types/types.ts' import BaseIcon from '@/components/BaseIcon.vue' import Tooltip from '@/components/base/Tooltip.vue' import Progress from '@/components/base/Progress.vue' @@ -35,43 +35,52 @@ function format(val: number, suffix: string = '', check: number = -1) { const status = $computed(() => { if (isTypingWrongWord.value) return '复习错词' - return getStepStr(statStore.step) + return getStageStr(statStore.stage) }) -function getStepStr(step: number) { +function getStageStr(stage: WordPracticeStage) { let str = '' - switch (step) { - case 0: - str += `学习新词` + switch (stage) { + case WordPracticeStage.FollowWriteNewWord: + str += `跟写新词` break - case 1: + case WordPracticeStage.IdentifyNewWord: + str += `自测新词` + break + case WordPracticeStage.ListenNewWord: str += `听写新词` break - case 2: + case WordPracticeStage.DictationNewWord: str += `默写新词` break - case 3: + case WordPracticeStage.FollowWriteReview: + str += `跟写上次学习` + break + case WordPracticeStage.IdentifyReview: str += `自测上次学习` break - case 4: - str += '听写上次学习' + case WordPracticeStage.ListenReview: + str += `听写上次学习` break - case 5: - str += '默写上次学习' + case WordPracticeStage.DictationReview: + str += `默写上次学习` break - case 6: + case WordPracticeStage.FollowWriteReviewAll: + str += '跟写之前学习' + break + case WordPracticeStage.IdentifyReviewAll: str += '自测之前学习' break - case 7: + case WordPracticeStage.ListenReviewAll: str += '听写之前学习' break - case 8: + case WordPracticeStage.DictationReviewAll: str += '默写之前学习' break - case 9: + case WordPracticeStage.Complete: str += '学习完成' break - case 10: + case WordPracticeStage.Shuffle: str += '随机复习' break } @@ -128,31 +137,31 @@ const progress = $computed(() => {
- +
- - + + {{ - (!isSimple ? '标记为已掌握' : '取消标记已掌握') + + (!isSimple ? '标记已掌握' : '取消已掌握') + `(${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})` }}
- +
- - + + {{ (!isCollect ? '收藏' : '取消收藏') + @@ -161,17 +170,15 @@ const progress = $computed(() => { >
- +
- - - 跳过当前单词({{ settingStore.shortcutKeyMap[ShortcutKey.Next] }}) + + 跳过单词({{ settingStore.shortcutKeyMap[ShortcutKey.Next] }})
- +
diff --git a/src/stores/practice.ts b/src/stores/practice.ts index a3bac276..c19d2037 100644 --- a/src/stores/practice.ts +++ b/src/stores/practice.ts @@ -1,21 +1,24 @@ -import {defineStore} from "pinia" +import { defineStore } from 'pinia' +import { WordPracticeStage } from '@/types/types.ts' export interface PracticeState { - step: number, - startDate: number, - spend: number, - total: number, - newWordNumber: number, - reviewWordNumber: number, - writeWordNumber: number, - inputWordNumber: number,//当前总输入了多少个单词(不包含跳过) - wrong: number, + step: number + stage: WordPracticeStage + startDate: number + spend: number + total: number + newWordNumber: number + reviewWordNumber: number + writeWordNumber: number + inputWordNumber: number //当前总输入了多少个单词(不包含跳过) + wrong: number } export const usePracticeStore = defineStore('practice', { state: (): PracticeState => { return { step: 0, + stage: WordPracticeStage.FollowWriteNewWord, spend: 0, startDate: Date.now(), total: 0, diff --git a/src/types/types.ts b/src/types/types.ts index 1af5fd77..0a096d26 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,40 +1,40 @@ export type Word = { - id?: string, - custom?: boolean, - word: string, - phonetic0: string, - phonetic1: string, - trans: { - pos: string, - cn: string, - }[], - sentences: { - c: string,//content - cn: string, - }[], - phrases: { - c: string, - cn: string, - }[], - synos: { - pos: string, - cn: string, - ws: string[] - }[], - relWords: { - root: string, - rels: { - pos: string, - words: { - c: string, - cn: string, - }[], - }[] - }, - etymology: { - t: string,//title - d: string,//desc - }[], + id?: string + custom?: boolean + word: string + phonetic0: string + phonetic1: string + trans: { + pos: string + cn: string + }[] + sentences: { + c: string //content + cn: string + }[] + phrases: { + c: string + cn: string + }[] + synos: { + pos: string + cn: string + ws: string[] + }[] + relWords: { + root: string + rels: { + pos: string + words: { + c: string + cn: string + }[] + }[] + } + etymology: { + t: string //title + d: string //desc + }[] } export const PronunciationApi = 'https://dict.youdao.com/dictvoice?audio=' @@ -43,216 +43,237 @@ export type TranslateLanguageType = 'en' | 'zh-CN' | 'ja' | 'de' | 'common' | '' export type LanguageType = 'en' | 'ja' | 'de' | 'code' export enum DictType { - collect = 'collect', - simple = 'simple', - wrong = 'wrong', - known = 'known', - word = 'word', - article = 'article', + collect = 'collect', + simple = 'simple', + wrong = 'wrong', + known = 'known', + word = 'word', + article = 'article', } export interface ArticleWord extends Word { - nextSpace: boolean, - symbolPosition: 'start' | 'end' | '', - input: string - type: PracticeArticleWordType + nextSpace: boolean + symbolPosition: 'start' | 'end' | '' + input: string + type: PracticeArticleWordType } export interface Sentence { - text: string, - translate: string, - words: ArticleWord[], - audioPosition: number[] + text: string + translate: string + words: ArticleWord[] + audioPosition: number[] } export interface Article { - id?: number, - title: string, - titleTranslate: string, - text: string, - textTranslate: string, - newWords: Word[], - sections: Sentence[][], - audioSrc: string, - audioFileId: string, - lrcPosition: number[][], - nameList: string[], - questions: { - stem: string, - options: string[], - correctAnswer: string[], - explanation: string - }[] + id?: number + title: string + titleTranslate: string + text: string + textTranslate: string + newWords: Word[] + sections: Sentence[][] + audioSrc: string + audioFileId: string + lrcPosition: number[][] + nameList: string[] + questions: { + stem: string + options: string[] + correctAnswer: string[] + explanation: string + }[] } export interface Statistics { - startDate: number,//开始日期 - spend: number,//花费时间 - total: number//单词数量 - new: number//新学单词数量 - review: number//复习单词数量 - wrong: number//错误数 + startDate: number //开始日期 + spend: number //花费时间 + total: number //单词数量 + new: number //新学单词数量 + review: number //复习单词数量 + wrong: number //错误数 } export enum Sort { - normal = 0, - random = 1, - reverse = 2, - reverseAll = 3, - randomAll = 4, + normal = 0, + random = 1, + reverse = 2, + reverseAll = 3, + randomAll = 4, } export enum ShortcutKey { - ShowWord = 'ShowWord', - EditArticle = 'EditArticle', - Next = 'Next', - Previous = 'Previous', - ToggleSimple = 'ToggleSimple', - ToggleCollect = 'ToggleCollect', - NextChapter = 'NextChapter', - PreviousChapter = 'PreviousChapter', - RepeatChapter = 'RepeatChapter', - DictationChapter = 'DictationChapter', - PlayWordPronunciation = 'PlayWordPronunciation', - ToggleShowTranslate = 'ToggleShowTranslate', - ToggleDictation = 'ToggleDictation', - ToggleTheme = 'ToggleTheme', - ToggleConciseMode = 'ToggleConciseMode', - TogglePanel = 'TogglePanel', - RandomWrite = 'RandomWrite', - NextRandomWrite = 'NextRandomWrite', - KnowWord = 'KnowWord', - UnknownWord = 'UnknownWord', + ShowWord = 'ShowWord', + EditArticle = 'EditArticle', + Next = 'Next', + Previous = 'Previous', + ToggleSimple = 'ToggleSimple', + ToggleCollect = 'ToggleCollect', + NextChapter = 'NextChapter', + PreviousChapter = 'PreviousChapter', + RepeatChapter = 'RepeatChapter', + DictationChapter = 'DictationChapter', + PlayWordPronunciation = 'PlayWordPronunciation', + ToggleShowTranslate = 'ToggleShowTranslate', + ToggleDictation = 'ToggleDictation', + ToggleTheme = 'ToggleTheme', + ToggleConciseMode = 'ToggleConciseMode', + TogglePanel = 'TogglePanel', + RandomWrite = 'RandomWrite', + NextRandomWrite = 'NextRandomWrite', + KnowWord = 'KnowWord', + UnknownWord = 'UnknownWord', } export const DefaultShortcutKeyMap = { - [ShortcutKey.EditArticle]: 'Ctrl+E', - [ShortcutKey.ShowWord]: 'Escape', - [ShortcutKey.Previous]: 'Alt+⬅', - [ShortcutKey.Next]: 'Tab', - [ShortcutKey.ToggleSimple]: '`', - [ShortcutKey.ToggleCollect]: 'Enter', - [ShortcutKey.PreviousChapter]: 'Ctrl+⬅', - [ShortcutKey.NextChapter]: 'Ctrl+➡', - [ShortcutKey.RepeatChapter]: 'Ctrl+Enter', - [ShortcutKey.DictationChapter]: 'Alt+Enter', - [ShortcutKey.PlayWordPronunciation]: 'Ctrl+P', - [ShortcutKey.ToggleShowTranslate]: 'Ctrl+Z', - [ShortcutKey.ToggleDictation]: 'Ctrl+I', - [ShortcutKey.ToggleTheme]: 'Ctrl+Q', - [ShortcutKey.ToggleConciseMode]: 'Ctrl+M', - [ShortcutKey.TogglePanel]: 'Ctrl+L', - [ShortcutKey.RandomWrite]: 'Ctrl+R', - [ShortcutKey.NextRandomWrite]: 'Ctrl+Shift+R', - [ShortcutKey.KnowWord]: '1', - [ShortcutKey.UnknownWord]: '2', + [ShortcutKey.EditArticle]: 'Ctrl+E', + [ShortcutKey.ShowWord]: 'Escape', + [ShortcutKey.Previous]: 'Alt+⬅', + [ShortcutKey.Next]: 'Tab', + [ShortcutKey.ToggleSimple]: '`', + [ShortcutKey.ToggleCollect]: 'Enter', + [ShortcutKey.PreviousChapter]: 'Ctrl+⬅', + [ShortcutKey.NextChapter]: 'Ctrl+➡', + [ShortcutKey.RepeatChapter]: 'Ctrl+Enter', + [ShortcutKey.DictationChapter]: 'Alt+Enter', + [ShortcutKey.PlayWordPronunciation]: 'Ctrl+P', + [ShortcutKey.ToggleShowTranslate]: 'Ctrl+Z', + [ShortcutKey.ToggleDictation]: 'Ctrl+I', + [ShortcutKey.ToggleTheme]: 'Ctrl+Q', + [ShortcutKey.ToggleConciseMode]: 'Ctrl+M', + [ShortcutKey.TogglePanel]: 'Ctrl+L', + [ShortcutKey.RandomWrite]: 'Ctrl+R', + [ShortcutKey.NextRandomWrite]: 'Ctrl+Shift+R', + [ShortcutKey.KnowWord]: '1', + [ShortcutKey.UnknownWord]: '2', } export enum TranslateEngine { - Baidu = 0, + Baidu = 0, } export type DictResource = { - id: string - name: string - description: string - url: string - length: number - category: string - tags: string[] - translateLanguage: TranslateLanguageType - //todo 可以考虑删除了 - type?: DictType - version?: number - language: LanguageType + id: string + name: string + description: string + url: string + length: number + category: string + tags: string[] + translateLanguage: TranslateLanguageType + //todo 可以考虑删除了 + type?: DictType + version?: number + language: LanguageType } export interface Dict extends DictResource { - lastLearnIndex: number, - perDayStudyNumber: number, - words: Word[], - articles: Article[], - statistics: Statistics[], - custom: boolean,//是否是自定义词典 - complete: boolean,//是否学习完成,学完了设为true,然后lastLearnIndex重置 - //后端字段 - en_name?: string - createdBy?: string - category_id?: number - is_default?: boolean - update?: boolean - cover?: string - sync?: boolean - userDictId?: number + lastLearnIndex: number + perDayStudyNumber: number + words: Word[] + articles: Article[] + statistics: Statistics[] + custom: boolean //是否是自定义词典 + complete: boolean //是否学习完成,学完了设为true,然后lastLearnIndex重置 + //后端字段 + en_name?: string + createdBy?: string + category_id?: number + is_default?: boolean + update?: boolean + cover?: string + sync?: boolean + userDictId?: number } export interface ArticleItem { - item: Article, - index: number + item: Article + index: number } export const SlideType = { - HORIZONTAL: 0, - VERTICAL: 1, + HORIZONTAL: 0, + VERTICAL: 1, } export interface PracticeData { - index: number, - words: Word[], - wrongWords: Word[], - excludeWords: string[], + index: number + words: Word[] + wrongWords: Word[] + excludeWords: string[] } export interface TaskWords { - new: Word[], - review: Word[], - write: Word[], - shuffle: Word[], + new: Word[] + review: Word[] + write: Word[] + shuffle: Word[] } export class DictId { - static wordCollect = 'wordCollect' - static wordWrong = 'wordWrong' - static wordKnown = 'wordKnown' - static articleCollect = 'articleCollect' + static wordCollect = 'wordCollect' + static wordWrong = 'wordWrong' + static wordKnown = 'wordKnown' + static articleCollect = 'articleCollect' } export enum PracticeArticleWordType { - Symbol, - Number, - Word + Symbol, + Number, + Word, } //练习模式 export enum WordPracticeMode { - System = 0, - Free = 1, - DictationOnly = 2, // 独立默写模式 - ListenOnly = 3, // 独立听写模式 - IdentifyOnly = 4, // 独立自测模式 - FollowWriteOnly = 5 // 独立跟写模式(内部会自动切换到 Spell) + System = 0, + Free = 1, + IdentifyOnly = 2, // 独立自测模式 + DictationOnly = 3, // 独立默写模式 + ListenOnly = 4, // 独立听写模式 + FollowWriteOnly = 5, // 独立跟写模式(内部会自动切换到 Spell) } //练习类型 export enum WordPracticeType { - FollowWrite,//跟写 - Spell, - Identify, - Listen, - Dictation + FollowWrite, //跟写 + Spell, + Identify, + Listen, + Dictation, } export enum CodeType { - Login = 0, - Register = 1, - ResetPwd = 2, - ChangeEmail = 3, - ChangePhoneNew = 4, - ChangePhoneOld = 5 + Login = 0, + Register = 1, + ResetPwd = 2, + ChangeEmail = 3, + ChangePhoneNew = 4, + ChangePhoneOld = 5, } export enum ImportStatus { - Idle = 0, - Success = 1, - Fail = 2 -} \ No newline at end of file + Idle = 0, + Success = 1, + Fail = 2, +} + +//练习阶段 +export enum WordPracticeStage { + FollowWriteNewWord = 0, + IdentifyNewWord = 1, + ListenNewWord = 2, + DictationNewWord = 3, + + FollowWriteReview = 4, + IdentifyReview = 5, + ListenReview = 6, + DictationReview = 7, + + FollowWriteReviewAll = 8, + IdentifyReviewAll = 9, + ListenReviewAll = 10, + DictationReviewAll = 11, + + Shuffle = 12, + Complete = 13, +} From db4b0ba18f422998d922e04cdb2e920ba633db06 Mon Sep 17 00:00:00 2001 From: Zyronon Date: Wed, 24 Dec 2025 20:02:01 +0800 Subject: [PATCH 04/28] wip --- src/components/base/Tooltip.vue | 1 - src/pages/word/PracticeWords.vue | 244 +++++++++++++-------------- src/pages/word/WordsPage.vue | 4 +- src/pages/word/components/Footer.vue | 56 +----- src/stores/practice.ts | 14 +- src/types/types.ts | 62 +++++++ 6 files changed, 199 insertions(+), 182 deletions(-) diff --git a/src/components/base/Tooltip.vue b/src/components/base/Tooltip.vue index 1833ba29..3c200c9b 100644 --- a/src/components/base/Tooltip.vue +++ b/src/components/base/Tooltip.vue @@ -63,7 +63,6 @@ export default { this.show = false} onmouseenter={(e) => this.showPop(e)} onmouseleave={() => this.show = false} /> diff --git a/src/pages/word/PracticeWords.vue b/src/pages/word/PracticeWords.vue index d53c966e..0900e645 100644 --- a/src/pages/word/PracticeWords.vue +++ b/src/pages/word/PracticeWords.vue @@ -12,6 +12,7 @@ import { TaskWords, Word, WordPracticeMode, + WordPracticeModeStageMap, WordPracticeStage, WordPracticeType, } from '@/types/types.ts' @@ -224,19 +225,47 @@ function initData(initVal: TaskWords, init: boolean = false) { //不能直接赋值,会导致 inject 的数据为默认值 taskWords = Object.assign(taskWords, initVal) - if (taskWords.shuffle.length === 0) { + if (settingStore.wordPracticeMode === WordPracticeMode.Shuffle) { + settingStore.wordPracticeType = WordPracticeType.Dictation + data.words = taskWords.shuffle + statStore.stage = WordPracticeStage.Shuffle + statStore.total = taskWords.shuffle.length + statStore.newWordNumber = 0 + statStore.reviewWordNumber = 0 + statStore.writeWordNumber = statStore.total + } else { if (taskWords.new.length === 0) { if (taskWords.review.length) { - settingStore.wordPracticeType = WordPracticeType.Identify - statStore.step = 3 - statStore.stage = WordPracticeStage.IdentifyReview data.words = taskWords.review + if (settingStore.wordPracticeMode === WordPracticeMode.System) { + statStore.stage = WordPracticeStage.IdentifyReview + } else if (settingStore.wordPracticeMode === WordPracticeMode.Free) { + statStore.stage = WordPracticeStage.FollowWriteReview + } else if (settingStore.wordPracticeMode === WordPracticeMode.IdentifyOnly) { + statStore.stage = WordPracticeStage.IdentifyReview + } else if (settingStore.wordPracticeMode === WordPracticeMode.DictationOnly) { + statStore.stage = WordPracticeStage.DictationReview + } else if (settingStore.wordPracticeMode === WordPracticeMode.ListenOnly) { + statStore.stage = WordPracticeStage.ListenReview + } else if (settingStore.wordPracticeMode === WordPracticeMode.FollowWriteOnly) { + statStore.stage = WordPracticeStage.FollowWriteReview + } } else { if (taskWords.write.length) { - settingStore.wordPracticeType = WordPracticeType.Identify data.words = taskWords.write - statStore.step = 6 - statStore.stage = WordPracticeStage.IdentifyReviewAll + if (settingStore.wordPracticeMode === WordPracticeMode.System) { + statStore.stage = WordPracticeStage.IdentifyReviewAll + } else if (settingStore.wordPracticeMode === WordPracticeMode.Free) { + statStore.stage = WordPracticeStage.FollowWriteReviewAll + } else if (settingStore.wordPracticeMode === WordPracticeMode.IdentifyOnly) { + statStore.stage = WordPracticeStage.IdentifyReviewAll + } else if (settingStore.wordPracticeMode === WordPracticeMode.DictationOnly) { + statStore.stage = WordPracticeStage.DictationReviewAll + } else if (settingStore.wordPracticeMode === WordPracticeMode.ListenOnly) { + statStore.stage = WordPracticeStage.ListenReviewAll + } else if (settingStore.wordPracticeMode === WordPracticeMode.FollowWriteOnly) { + statStore.stage = WordPracticeStage.FollowWriteReviewAll + } } else { Toast.warning('没有可学习的单词!') router.push('/word') @@ -244,34 +273,12 @@ function initData(initVal: TaskWords, init: boolean = false) { } } else { data.words = taskWords.new - statStore.step = 0 - if (settingStore.wordPracticeMode === WordPracticeMode.System) { - statStore.stage = WordPracticeStage.FollowWriteNewWord - } else if (settingStore.wordPracticeMode === WordPracticeMode.Free) { - statStore.stage = WordPracticeStage.FollowWriteNewWord - } else if (settingStore.wordPracticeMode === WordPracticeMode.IdentifyOnly) { - statStore.stage = WordPracticeStage.IdentifyNewWord - } else if (settingStore.wordPracticeMode === WordPracticeMode.DictationOnly) { - statStore.stage = WordPracticeStage.DictationNewWord - } else if (settingStore.wordPracticeMode === WordPracticeMode.ListenOnly) { - statStore.stage = WordPracticeStage.ListenNewWord - } else if (settingStore.wordPracticeMode === WordPracticeMode.FollowWriteOnly) { - statStore.stage = WordPracticeStage.FollowWriteNewWord - } + statStore.stage = WordPracticeModeStageMap[settingStore.wordPracticeMode][0] } statStore.total = taskWords.review.length + taskWords.new.length + taskWords.write.length statStore.newWordNumber = taskWords.new.length statStore.reviewWordNumber = taskWords.review.length statStore.writeWordNumber = taskWords.write.length - } else { - settingStore.wordPracticeType = WordPracticeType.Dictation - data.words = taskWords.shuffle - statStore.step = 10 - statStore.stage = WordPracticeStage.Shuffle - statStore.total = taskWords.shuffle.length - statStore.newWordNumber = 0 - statStore.reviewWordNumber = 0 - statStore.writeWordNumber = statStore.total } data.index = 0 @@ -304,29 +311,55 @@ const nextWord: Word = $computed(() => { }) watch( - () => settingStore.wordPracticeMode, + () => settingStore.wordPracticeType, n => { - // Free 模式不自动设置,System 模式和独立模式都需要设置 if (settingStore.wordPracticeMode === WordPracticeMode.Free) return switch (n) { - case WordPracticeMode.DictationOnly: + case WordPracticeType.Spell: + case WordPracticeType.Dictation: settingStore.dictation = true settingStore.translate = true + break + case WordPracticeType.Listen: + settingStore.dictation = true + settingStore.translate = false + break + case WordPracticeType.FollowWrite: + settingStore.dictation = false + settingStore.translate = true + break + case WordPracticeType.Identify: + settingStore.dictation = false + settingStore.translate = false + break + } + }, + { immediate: true } +) + +watch( + () => statStore.stage, + n => { + switch (n) { + case WordPracticeStage.DictationNewWord: + case WordPracticeStage.DictationReview: + case WordPracticeStage.DictationReviewAll: + case WordPracticeStage.Shuffle: settingStore.wordPracticeType = WordPracticeType.Dictation break - case WordPracticeMode.ListenOnly: - settingStore.dictation = true - settingStore.translate = false + case WordPracticeStage.ListenNewWord: + case WordPracticeStage.ListenReview: + case WordPracticeStage.ListenReviewAll: settingStore.wordPracticeType = WordPracticeType.Listen break - case WordPracticeMode.FollowWriteOnly: - settingStore.dictation = false - settingStore.translate = true + case WordPracticeStage.FollowWriteNewWord: + case WordPracticeStage.FollowWriteReview: + case WordPracticeStage.FollowWriteReviewAll: settingStore.wordPracticeType = WordPracticeType.FollowWrite break - case WordPracticeMode.IdentifyOnly: - settingStore.dictation = false - settingStore.translate = false + case WordPracticeStage.IdentifyNewWord: + case WordPracticeStage.IdentifyReview: + case WordPracticeStage.IdentifyReviewAll: settingStore.wordPracticeType = WordPracticeType.Identify break } @@ -357,38 +390,19 @@ function wordLoop() { let toastInstance: ToastInstance = null -function goNextStep(originList, mode, msg) { - //每次都判断,因为每次都可能新增已掌握的单词 - let list = originList.filter(v => !data.excludeWords.includes(v.word)) - console.log(msg) - if (list.length) { - if (toastInstance) toastInstance.close() - toastInstance = Toast.info('输入完成后按空格键切换下一个', { duration: 5000 }) - data.words = list - settingStore.wordPracticeType = mode - data.index = 0 - statStore.step++ - } else { - console.log(msg + ':无单词略过') - statStore.step += 3 - next() - } -} - -function nextStage(originList, log, nextStage) { +function nextStage(originList, log) { //每次都判断,因为每次都可能新增已掌握的单词 let list = originList.filter(v => !data.excludeWords.includes(v.word)) console.log(log) + statStore.stage = statStore.nextStage if (list.length) { if (toastInstance) toastInstance.close() toastInstance = Toast.info('输入完成后按空格键切换下一个', { duration: 5000 }) data.words = list data.index = 0 - statStore.stage = nextStage } else { console.log(log + ':无单词略过') - statStore.stage = nextStage - next() + next(false) } } @@ -416,12 +430,18 @@ async function next(isTyping: boolean = true) { } } else { if (data.index === data.words.length - 1) { - if (statStore.stage === WordPracticeStage.FollowWriteNewWord || isTypingWrongWord.value) { + //如果手动敲的,才轮询 + if ( + (statStore.stage === WordPracticeStage.FollowWriteNewWord || isTypingWrongWord.value) && + isTyping + ) { if (settingStore.wordPracticeType !== WordPracticeType.Spell) { //回到最后一组的开始位置 data.index = Math.floor(data.index / groupSize) * groupSize emitter.emit(EventKey.resetWord) settingStore.wordPracticeType = WordPracticeType.Spell + //如果单词是已掌握的,则跳过 + if (isWordSimple(word)) next(false) return } } @@ -446,73 +466,44 @@ async function next(isTyping: boolean = true) { if (settingStore.wordPracticeMode === WordPracticeMode.System) { if (statStore.stage === WordPracticeStage.FollowWriteNewWord) { - settingStore.wordPracticeType = WordPracticeType.Listen - return nextStage(shuffle(taskWords.new), '开始听写新词', WordPracticeStage.ListenNewWord) - } - if (statStore.stage === WordPracticeStage.ListenNewWord) { - settingStore.wordPracticeType = WordPracticeType.Dictation - return nextStage(shuffle(taskWords.new), '开始默写新词', WordPracticeStage.DictationNewWord) - } - if (statStore.stage === WordPracticeStage.DictationNewWord) { - settingStore.wordPracticeType = WordPracticeType.Identify - return nextStage(taskWords.review, '开始自测昨日', WordPracticeStage.IdentifyReview) - } - if (statStore.stage === WordPracticeStage.IdentifyReview) { - settingStore.wordPracticeType = WordPracticeType.Listen - return nextStage(shuffle(taskWords.review), '开始听写上次', WordPracticeStage.ListenReview) - } - if (statStore.stage === WordPracticeStage.ListenReview) { - settingStore.wordPracticeType = WordPracticeType.Dictation - return nextStage(shuffle(taskWords.review), '开始默写上次', WordPracticeStage.DictationReview) - } - if (statStore.stage === WordPracticeStage.DictationReview) { - settingStore.wordPracticeType = WordPracticeType.Identify - return nextStage(taskWords.write, '开始自测之前', WordPracticeStage.IdentifyReviewAll) - } - if (statStore.stage === WordPracticeStage.IdentifyReviewAll) { - settingStore.wordPracticeType = WordPracticeType.Listen - return nextStage(shuffle(taskWords.review), '开始听写之前', WordPracticeStage.ListenReviewAll) - } - if (statStore.stage === WordPracticeStage.ListenReviewAll) { - settingStore.wordPracticeType = WordPracticeType.Dictation - return nextStage(shuffle(taskWords.review), '开始默写之前', WordPracticeStage.DictationReviewAll) - } - if (statStore.stage === WordPracticeStage.DictationReviewAll) { + nextStage(shuffle(taskWords.new), '开始听写新词') + } else if (statStore.stage === WordPracticeStage.ListenNewWord) { + nextStage(shuffle(taskWords.new), '开始默写新词') + } else if (statStore.stage === WordPracticeStage.DictationNewWord) { + nextStage(taskWords.review, '开始自测昨日') + } else if (statStore.stage === WordPracticeStage.IdentifyReview) { + nextStage(shuffle(taskWords.review), '开始听写上次') + } else if (statStore.stage === WordPracticeStage.ListenReview) { + nextStage(shuffle(taskWords.review), '开始默写上次') + } else if (statStore.stage === WordPracticeStage.DictationReview) { + nextStage(taskWords.write, '开始自测之前') + } else if (statStore.stage === WordPracticeStage.IdentifyReviewAll) { + nextStage(shuffle(taskWords.review), '开始听写之前') + } else if (statStore.stage === WordPracticeStage.ListenReviewAll) { + nextStage(shuffle(taskWords.review), '开始默写之前') + } else if (statStore.stage === WordPracticeStage.DictationReviewAll) { complete() } } else if (settingStore.wordPracticeMode === WordPracticeMode.ListenOnly) { - settingStore.wordPracticeType = WordPracticeType.Listen if (statStore.stage === WordPracticeStage.ListenNewWord) { - return nextStage(taskWords.review, '开始听写昨日', WordPracticeStage.ListenReview) - } - if (statStore.stage === WordPracticeStage.ListenReview) { - return nextStage(taskWords.write, '开始听写之前', WordPracticeStage.ListenReviewAll) - } - if (statStore.stage === WordPracticeStage.ListenReviewAll) { - complete() - } + nextStage(taskWords.review, '开始听写昨日') + } else if (statStore.stage === WordPracticeStage.ListenReview) { + nextStage(taskWords.write, '开始听写之前') + } else if (statStore.stage === WordPracticeStage.ListenReviewAll) complete() } else if (settingStore.wordPracticeMode === WordPracticeMode.DictationOnly) { - settingStore.wordPracticeType = WordPracticeType.Dictation if (statStore.stage === WordPracticeStage.DictationNewWord) { - return nextStage(taskWords.review, '开始默写昨日', WordPracticeStage.DictationReview) - } - if (statStore.stage === WordPracticeStage.DictationReview) { - return nextStage(taskWords.write, '开始默写之前', WordPracticeStage.DictationReviewAll) - } - if (statStore.stage === WordPracticeStage.DictationReviewAll) { - complete() - } + nextStage(taskWords.review, '开始默写昨日') + } else if (statStore.stage === WordPracticeStage.DictationReview) { + nextStage(taskWords.write, '开始默写之前') + } else if (statStore.stage === WordPracticeStage.DictationReviewAll) complete() } else if (settingStore.wordPracticeMode === WordPracticeMode.IdentifyOnly) { - settingStore.wordPracticeType = WordPracticeType.Identify if (statStore.stage === WordPracticeStage.IdentifyNewWord) { - return nextStage(taskWords.review, '开始自测昨日', WordPracticeStage.IdentifyReview) - } - if (statStore.stage === WordPracticeStage.IdentifyReview) { - return nextStage(taskWords.write, '开始自测之前', WordPracticeStage.IdentifyReviewAll) - } - if (statStore.stage === WordPracticeStage.IdentifyReviewAll) { - complete() - } + nextStage(taskWords.review, '开始自测昨日') + } else if (statStore.stage === WordPracticeStage.IdentifyReview) { + nextStage(taskWords.write, '开始自测之前') + } else if (statStore.stage === WordPracticeStage.IdentifyReviewAll) complete() + } else if (settingStore.wordPracticeMode === WordPracticeMode.Shuffle) { + if (statStore.stage === WordPracticeStage.Shuffle) complete() } } } else { @@ -524,12 +515,13 @@ async function next(isTyping: boolean = true) { } } } + //如果单词是已掌握的,则跳过 + if (isWordSimple(word)) next(false) savePracticeData() } function skipStep() { data.index = data.words.length - 1 - settingStore.wordPracticeType = WordPracticeType.Spell data.wrongWords = [] next(false) } @@ -596,7 +588,7 @@ function repeat() { let temp = cloneDeep(taskWords) let ignoreList = [store.allIgnoreWords, store.knownWords][settingStore.ignoreSimpleWord ? 0 : 1] //随机练习单独处理 - if (taskWords.shuffle.length) { + if (settingStore.wordPracticeMode === WordPracticeMode.Shuffle) { temp.shuffle = shuffle(temp.shuffle.filter(v => !ignoreList.includes(v.word))) } else { if (settingStore.wordPracticeMode === WordPracticeMode.System) settingStore.dictation = false diff --git a/src/pages/word/WordsPage.vue b/src/pages/word/WordsPage.vue index dc2e0482..f2aa546a 100644 --- a/src/pages/word/WordsPage.vue +++ b/src/pages/word/WordsPage.vue @@ -238,7 +238,7 @@ async function onShufflePracticeSettingOk(total) { }) isSaveData = false localStorage.removeItem(PracticeSaveWordKey.key) - + settingStore.wordPracticeMode = WordPracticeMode.Shuffle let ignoreList = [store.allIgnoreWords, store.knownWords][settingStore.ignoreSimpleWord ? 0 : 1] currentStudy.shuffle = shuffle( store.sdict.words.slice(0, store.sdict.lastLearnIndex).filter(v => !ignoreList.includes(v.word)) @@ -301,7 +301,7 @@ let isNewHost = $ref(window.location.host === Host) 已完成 {{ progressTextRight }} 词 / 共 {{ store.sdict.words.length }} 词 预计完成日期:{{ - _getAccomplishDate(store.sdict.words.length, store.sdict.perDayStudyNumber) + _getAccomplishDate(store.sdict.words.length - store.sdict.lastLearnIndex, store.sdict.perDayStudyNumber) }}
diff --git a/src/pages/word/components/Footer.vue b/src/pages/word/components/Footer.vue index f959ee57..c28c479e 100644 --- a/src/pages/word/components/Footer.vue +++ b/src/pages/word/components/Footer.vue @@ -2,7 +2,7 @@ import { inject, Ref } from 'vue' import { usePracticeStore } from '@/stores/practice.ts' import { useSettingStore } from '@/stores/setting.ts' -import { PracticeData, ShortcutKey, WordPracticeStage } from '@/types/types.ts' +import { PracticeData, ShortcutKey, WordPracticeModeStageMap, WordPracticeStage, WordPracticeStageNameMap } from '@/types/types.ts' import BaseIcon from '@/components/BaseIcon.vue' import Tooltip from '@/components/base/Tooltip.vue' import Progress from '@/components/base/Progress.vue' @@ -35,62 +35,14 @@ function format(val: number, suffix: string = '', check: number = -1) { const status = $computed(() => { if (isTypingWrongWord.value) return '复习错词' - return getStageStr(statStore.stage) + return statStore.getStageName }) -function getStageStr(stage: WordPracticeStage) { - let str = '' - switch (stage) { - case WordPracticeStage.FollowWriteNewWord: - str += `跟写新词` - break - case WordPracticeStage.IdentifyNewWord: - str += `自测新词` - break - case WordPracticeStage.ListenNewWord: - str += `听写新词` - break - case WordPracticeStage.DictationNewWord: - str += `默写新词` - break - case WordPracticeStage.FollowWriteReview: - str += `跟写上次学习` - break - case WordPracticeStage.IdentifyReview: - str += `自测上次学习` - break - case WordPracticeStage.ListenReview: - str += `听写上次学习` - break - case WordPracticeStage.DictationReview: - str += `默写上次学习` - break - case WordPracticeStage.FollowWriteReviewAll: - str += '跟写之前学习' - break - case WordPracticeStage.IdentifyReviewAll: - str += '自测之前学习' - break - case WordPracticeStage.ListenReviewAll: - str += '听写之前学习' - break - case WordPracticeStage.DictationReviewAll: - str += '默写之前学习' - break - case WordPracticeStage.Complete: - str += '学习完成' - break - case WordPracticeStage.Shuffle: - str += '随机复习' - break - } - return str -} - const progress = $computed(() => { if (!practiceData.words.length) return 0 return (practiceData.index / practiceData.words.length) * 100 }) +