feat:save

This commit is contained in:
zyronon
2025-07-23 00:00:30 +08:00
parent ba1dbdfbb1
commit d09da3227a
11 changed files with 76 additions and 156 deletions

View File

@@ -25,7 +25,7 @@ export function useWordOptions() {
}
function isWordSimple(val: Word) {
return !!store.simple.words.find(v => v.word.toLowerCase() === val.word.toLowerCase())
return !!store.known.words.find(v => v.word.toLowerCase() === val.word.toLowerCase())
}
function toggleWordSimple(val: Word) {
@@ -110,7 +110,7 @@ export function getCurrentStudyWord() {
// console.time()
const store = useBaseStore()
let data = {new: [], review: [], write: []}
let dict = store.currentStudyWordDict;
let dict = store.sdict;
if (dict.words?.length) {
for (let i = dict.lastLearnIndex; i < dict.words.length; i++) {
if (data.new.length >= dict.perDayStudyNumber) break

View File

@@ -92,11 +92,9 @@ export function usePlayWordAudio() {
const audio = $ref(new Audio())
function playAudio(word: string) {
let url = ''
let url = `${PronunciationApi}${word}&type=2`
if (settingStore.wordSoundType === 'uk') {
url = `${PronunciationApi}${word}&type=1`
} else if (settingStore.wordSoundType === 'us') {
url = `${PronunciationApi}${word}&type=2`
}
audio.src = url
audio.volume = settingStore.wordSoundVolume / 100

View File

@@ -12,8 +12,6 @@ import {useSettingStore} from "@/stores/setting.ts";
import {syncMyDictList} from "@/hooks/dict.ts";
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const settingStore = useSettingStore()
let wordData = $ref({
words: [],
@@ -21,11 +19,7 @@ let wordData = $ref({
})
function getCurrentPractice() {
if (store.chapter.length) {
wordData.index = 0
wordData.words = cloneDeep(store.chapter)
emitter.emit(EventKey.resetWord)
}
}
function sort(list: Word[]) {
@@ -66,4 +60,4 @@ defineExpose({getCurrentPractice})
flex: 1;
display: flex;
}
</style>
</style>

View File

@@ -82,7 +82,7 @@ function changeCollect() {
<header>
<div class="tabs">
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">当前</div>
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">{{ store.collect.name }}</div>
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">{{ store.collectWord.name }}</div>
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">{{ store.known.name }}</div>
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.wrong.name }}</div>
</div>

View File

@@ -18,6 +18,7 @@ import {useRoute, useRouter} from "vue-router";
import {useBaseStore} from "@/stores/base.ts";
import EditBook from "@/pages/pc/article/components/EditBook.vue";
import {_getDictDataByUrl, _nextTick} from "@/utils";
import {emitter, EventKey} from "@/utils/eventBus.ts";
const runtimeStore = useRuntimeStore()
const base = useBaseStore()
@@ -292,18 +293,26 @@ function formClose() {
}
async function addMyStudyList() {
//把其他的词典的单词数据都删掉,全保存在内存里太卡了
base.word.bookList.slice(3).map(v => {
if (!v.custom) {
v.words = []
}
})
let rIndex = base.word.bookList.findIndex(v => v.name === runtimeStore.editDict.name)
if (runtimeStore.editDict.words.length < runtimeStore.editDict.perDayStudyNumber) {
runtimeStore.editDict.perDayStudyNumber = runtimeStore.editDict.words.length
}
if (rIndex > -1) {
base.word.studyIndex = rIndex
base.word.bookList[base.word.studyIndex].words = runtimeStore.editDict.words
base.word.bookList[base.word.studyIndex].perDayStudyNumber = runtimeStore.editDict.perDayStudyNumber
} else {
base.word.bookList.push(runtimeStore.editDict)
base.word.studyIndex = base.word.bookList.length - 1
}
router.back()
}

View File

@@ -1,8 +1,6 @@
<script setup lang="ts">
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
import {useBaseStore} from "@/stores/base.ts";
import Ring from "@/pages/pc/components/Ring.vue";
import Tooltip from "@/pages/pc/components/Tooltip.vue";
import BaseButton from "@/components/BaseButton.vue";
import {ShortcutKey} from "@/types.ts";
import {emitter, EventKey, useEvent, useEvents} from "@/utils/eventBus.ts";
@@ -23,7 +21,7 @@ useEvent(EventKey.openStatModal, () => {
total: statStore.total,
wrong: statStore.wrong,
}
store.sdict.lastLearnIndex = store.sdict.lastLearnIndex + store.sdict.perDayStudyNumber
store.sdict.lastLearnIndex = store.sdict.lastLearnIndex + statStore.newWordNumber
store.sdict.statistics.push(data as any)
store.sdict.statistics.sort((a, b) => a.startDate - b.startDate)
@@ -46,6 +44,7 @@ function options(emitType: 'write' | 'repeat' | 'next') {
emitter.emit(EventKey[emitType])
}
//todo
const isEnd = $computed(() => {
return false
})
@@ -79,7 +78,9 @@ const isEnd = $computed(() => {
</div>
<div class="text-xl text-center flex flex-col justify-around">
<div>非常棒! 坚持了 <span class="color-green font-bold text-2xl">{{ dayjs().diff(statStore.startDate, 'm') }}</span>
<div>非常棒! 坚持了 <span class="color-green font-bold text-2xl">{{
dayjs().diff(statStore.startDate, 'm')
}}</span>
分钟
</div>
</div>

View File

@@ -8,12 +8,10 @@ import BaseIcon from "@/components/BaseIcon.vue";
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
import {_getAccomplishDate, _getAccomplishDays, _getDictDataByUrl, useNav} from "@/utils";
import BasePage from "@/pages/pc/components/BasePage.vue";
import {Dict, DictResource, getDefaultDict} from "@/types.ts";
import {onMounted} from "vue";
import {DictResource, getDefaultDict} from "@/types.ts";
import {onMounted, watch} from "vue";
import {getCurrentStudyWord} from "@/hooks/dict.ts";
import {EventKey, useEvent} from "@/utils/eventBus.ts";
import DictListPanel from "@/pages/pc/components/DictListPanel.vue";
import {cloneDeep} from "lodash-es";
import {useRuntimeStore} from "@/stores/runtime.ts";
import Book from "@/pages/pc/components/Book.vue";
import PopConfirm from "@/pages/pc/components/PopConfirm.vue";
@@ -34,17 +32,30 @@ let currentStudy = $ref({
})
onMounted(() => {
if (!currentStudy.new.length) {
currentStudy = getCurrentStudyWord()
}
init()
})
useEvent(EventKey.changeDict, () => {
currentStudy = getCurrentStudyWord()
watch(() => store.load, () => {
init()
})
async function init() {
if (store.word.studyIndex >= 3) {
if (!store.sdict.custom && !store.sdict.words.length) {
store.word.bookList[store.word.studyIndex] = await _getDictDataByUrl(store.sdict)
}
}
console.log(store.sdict)
if (!currentStudy.new.length && store.sdict.words.length) {
currentStudy = getCurrentStudyWord()
}
}
function study() {
if (store.sdict.id) {
if (!store.sdict.words.length) {
return ElMessage.warning('没有单词可学习!')
}
nav('study-word', {}, currentStudy)
} else {
ElMessage.warning('请先选择一本词典')

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import {inject, onMounted, onUnmounted, provide} from "vue"
import {inject, onMounted, onUnmounted} from "vue"
import {usePracticeStore} from "@/stores/practice.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {ShortcutKey, StudyData} from "@/types.ts";
@@ -10,7 +10,6 @@ import IconWrapper from "@/pages/pc/components/IconWrapper.vue";
import Tooltip from "@/pages/pc/components/Tooltip.vue";
import TranslateSetting from "@/pages/pc/components/toolbar/TranslateSetting.vue";
import RepeatSetting from "@/pages/pc/components/toolbar/RepeatSetting.vue";
import {emitter, EventKey} from "@/utils/eventBus.ts";
const statisticsStore = usePracticeStore()
const settingStore = useSettingStore()

View File

@@ -77,33 +77,30 @@ function repeat() {
async function onTyping(e: KeyboardEvent) {
if (inputLock) return
console.log('onTyping')
inputLock = true
let letter = e.key
let isTypingRight = false
let isWordRight = false
if (settingStore.ignoreCase) {
isTypingRight = letter.toLowerCase() === props.word.word[input.length].toLowerCase()
isWordRight = (input + letter).toLowerCase() === props.word.word.toLowerCase()
} else {
isTypingRight = letter === props.word.word[input.length]
isWordRight = (input + letter) === props.word.word
}
if (isTypingRight) {
input += letter
wrong = ''
playKeyboardAudio()
} else {
emit('wrong')
wrong = letter
playKeyboardAudio()
playBeep()
volumeIconRef?.play()
setTimeout(() => {
wrong = ''
}, 500)
emit('wrong')
}
if (isWordRight) {
if (input.toLowerCase() === props.word.word.toLowerCase()) {
playCorrect()
if (settingStore.repeatCount == 100) {
if (settingStore.repeatCustomCount <= wordRepeatCount + 1) {
@@ -212,13 +209,15 @@ let tab = $ref(2)
</div>
</template>
<div class="line-white my-4"></div>
<div class="tabs">
<div @click="tab = 0" class="tab" :class="tab === 0 && 'active'">短语</div>
<div @click="tab = 1" class="tab" :class="tab === 1 && 'active'">同近义词</div>
<div @click="tab = 2" class="tab" :class="tab === 2 && 'active'"></div>
<div @click="tab = 3" class="tab" :class="tab === 3 && 'active'"></div>
</div>
<template v-if="word.phrases.length || word.synos.length || word.relWords.root || word.etymology.length">
<div class="line-white my-4"></div>
<div class="tabs">
<div @click="tab = 0" class="tab" :class="tab === 0 && 'active'">短语</div>
<div @click="tab = 1" class="tab" :class="tab === 1 && 'active'">近义</div>
<div @click="tab = 2" class="tab" :class="tab === 2 && 'active'">同根</div>
<div @click="tab = 3" class="tab" :class="tab === 3 && 'active'">词源</div>
</div>
</template>
<template v-if="tab === 0">
<div class="my-2" v-for="item in word.phrases">
<SentenceHightLightWord class="text-lg" :text="item.c" :word="word.word" :high-light="false"

View File

@@ -51,7 +51,7 @@ const {
toggleWordSimple
} = useWordOptions()
let allWrongWords = []
let allWrongWords = new Set()
let data = $ref<StudyData>({
index: 0,
@@ -65,7 +65,7 @@ watch(() => props.data, () => {
data.words = props.data.new
data.index = 0
data.wrongWords = []
allWrongWords = []
allWrongWords = new Set()
statStore.step = 0
statStore.startDate = Date.now()
@@ -127,21 +127,28 @@ function next(isTyping: boolean = true) {
} else {
data.index++
isTyping && statStore.inputWordNumber++
// console.log('这个词完了')
console.log('这个词完了')
}
}
function wordWrong() {
if (!store.wrong.words.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
store.wrong.words.push(word)
}
if (!data.wrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
data.wrongWords.push(word)
}
if (!allWrongWords.find((v: Word) => v.word.toLowerCase() === word.word.toLowerCase())) {
allWrongWords.push(word)
function onTypeWrong() {
let temp = word.word.toLowerCase()
if (!allWrongWords.has(word.word.toLowerCase())) {
allWrongWords.add(word.word.toLowerCase())
statStore.wrong++
}
//todo 后续要测试有非常的多的错词时,这会还卡不卡
setTimeout(() => {
requestAnimationFrame(() => {
if (!store.wrong.words.find((v: Word) => v.word.toLowerCase() === temp)) {
store.wrong.words.push(word)
store.wrong.length = store.wrong.words.length
}
if (!data.wrongWords.find((v: Word) => v.word.toLowerCase() === temp)) {
data.wrongWords.push(word)
}
})
}, 500)
}
function onKeyUp(e: KeyboardEvent) {
@@ -235,7 +242,7 @@ useEvents([
v-loading="!store.load"
ref="typingRef"
:word="word"
@wrong="wordWrong"
@wrong="onTypeWrong"
@complete="next"
/>
<Footer

View File

@@ -91,10 +91,10 @@ export const useBaseStore = defineStore('base', {
return this.word.bookList[0]
},
collectWord(): Dict {
return this.word.bookList[1]
return this.word.bookList[0]
},
collectArticle(): Dict {
return this.word.bookList[2]
return this.article.bookList[0]
},
simple(): Dict {
return this.word.bookList[2]
@@ -132,9 +132,6 @@ export const useBaseStore = defineStore('base', {
sdict(): Dict {
return this.currentStudyWordDict
},
sword() {
return this.currentStudy.word
},
currentStudyProgress(): number {
if (!this.sdict.words?.length) return 0
return _getStudyProgress(this.sdict.lastLearnIndex, this.sdict.words?.length)
@@ -142,9 +139,6 @@ export const useBaseStore = defineStore('base', {
currentArticleCollectDict(): Dict {
return this.article.bookList[0]
},
chapter(state: BaseState): Word[] {
return this.currentDict.chapterWords[this.currentDict.chapterIndex] ?? []
},
currentBook(): Dict {
return this.article.bookList[this.article.studyIndex] ?? {}
},
@@ -173,14 +167,6 @@ export const useBaseStore = defineStore('base', {
console.error('读取本地dict数据失败', e)
}
if (this.word.studyIndex >= 3) {
// await _checkDictWords(this.currentStudyWordDict)
let current: Dict = this.word.bookList[this.word.studyIndex]
if (!current.custom) {
this.word.bookList[this.word.studyIndex] = await _getDictDataByUrl(current)
}
console.log('this.current', current)
}
if (this.article.studyIndex >= 1) {
let current = this.article.bookList[this.article.studyIndex]
let dictResourceUrl = `./dicts/${current.language}/${current.type}/${current.translateLanguage}/${current.url}`;
@@ -197,91 +183,7 @@ export const useBaseStore = defineStore('base', {
resolve(true)
})
},
async changeDict(dict: Dict, practiceType?: DictType, chapterIndex?: number, wordIndex?: number) {
},
async changeWordDict(dict: Dict) {
this.wordDictList.map((v) => v.words = [])
// await checkDictHasTranslate(newDict)
let rIndex = this.wordDictList.findIndex((v: Dict) => v.id === dict.id)
if (rIndex > -1) {
this.wordDictList[rIndex] = Object.assign(dict, this.wordDictList[rIndex])
this.currentStudy.word.dictIndex = rIndex
} else {
this.wordDictList.push(getDefaultDict(dict))
this.currentStudy.word.dictIndex = this.wordDictList.length - 1
}
// await _checkDictWords(this.currentStudyWordDict)
console.log(' store.currentStudyWordDict', this.currentStudyWordDict)
emitter.emit(EventKey.changeDict)
},
async changeArticleDict(dict: Dict) {
//TODO 保存统计
// this.saveStatistics()
console.log('changeDict', cloneDeep(dict),)
if ([DictType.collect,
DictType.simple,
DictType.wrong].includes(dict.type)) {
} else {
//TODO 需要和其他需要下载的地方统一
let url = `./dicts/${dict.language}/${dict.type}/${dict.translateLanguage}/${dict.url}`;
if (dict.type === DictType.article) {
if (!dict.articles.length) {
let r = await fetch(url)
let v = await r.json()
v.map(s => {
s.id = nanoid(6)
})
dict.articles = cloneDeep(v)
} else {
dict.length = dict.articles.length
}
} else {
//如果不是自定义词典并且有url地址才去下载
if (!dict.custom && dict.url) {
if (!dict.originWords.length) {
let v = await getDictFile(url)
v.map(s => {
s.id = nanoid(6)
})
dict.originWords = cloneDeep(v)
if (dict.sort === Sort.normal) {
dict.words = cloneDeep(dict.originWords)
} else if (dict.sort === Sort.random) {
dict.words = shuffle(dict.originWords)
} else {
dict.words = reverse(dict.originWords)
}
dict.words.map(v => v.checked = false)
dict.length = dict.words.length
} else {
dict.length = dict.words.length
}
}
}
}
// await checkDictHasTranslate(dict)
let rIndex = this.myDictList.findIndex((v: Dict) => v.id === dict.id)
if (rIndex > -1) {
this.myDictList[rIndex] = dict
this.current.index = rIndex
} else {
this.myDictList.push(cloneDeep(dict))
this.current.index = this.myDictList.length - 1
}
emitter.emit(EventKey.changeDict)
},
delWordDict(dict: Dict) {
let oldId = this.currentStudyWordDict.id;
let rIndex = this.wordDictList.findIndex((v: Dict) => v.id === dict.id)
if (rIndex > -1) {
this.wordDictList.splice(rIndex, 1)
}
rIndex = this.wordDictList.findIndex((v: Dict) => v.id === oldId)
if (rIndex > -1) {
this.currentStudy.word.dictIndex = rIndex
}
async changeDict() {
},
},
})