save
This commit is contained in:
@@ -82,8 +82,7 @@ async function selectDict(val: { dict: DictResource | Dict, index: number }) {
|
||||
}
|
||||
if (runtimeStore.editDict.type === DictType.article) {
|
||||
if (!runtimeStore.editDict.articles.length) {
|
||||
let r = await fetch(url)
|
||||
let v = await r.json()
|
||||
let v = await getDictFile(url)
|
||||
v.map(s => {
|
||||
s.id = nanoid(6)
|
||||
})
|
||||
|
||||
@@ -50,7 +50,7 @@ defineExpose({scrollToBottom, scrollToItem})
|
||||
<div class="item-title">
|
||||
<span class="word" :class="!showWord && 'text-shadow'">{{ item.word }}</span>
|
||||
<span class="phonetic">{{ item.phonetic0 }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(item.name)"></VolumeIcon>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(item.word)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="item-sub-title" v-if="item.trans.length && showTranslate">
|
||||
<div v-for="v in item.trans">{{ (v.pos ? v.pos + '.' : '') + v.cn }}</div>
|
||||
|
||||
357
src/pages/mobile/practice/practice-word/Typing.vue
Normal file
357
src/pages/mobile/practice/practice-word/Typing.vue
Normal file
@@ -0,0 +1,357 @@
|
||||
<script setup lang="ts">
|
||||
import {DefaultWord, ShortcutKey, Word} from "@/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio, useTTsPlayAudio} from "@/hooks/sound.ts";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {onUnmounted, watch, onMounted, nextTick} from "vue";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
|
||||
interface IProps {
|
||||
word: Word,
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
word: () => cloneDeep(DefaultWord),
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
complete: [],
|
||||
wrong: []
|
||||
}>()
|
||||
|
||||
let input = $ref('')
|
||||
let wrong = $ref('')
|
||||
let showFullWord = $ref(false)
|
||||
//输入锁定,因为跳转到下一个单词有延时,如果重复在延时期间内重复输入,导致会跳转N次
|
||||
let inputLock = false
|
||||
let wordRepeatCount = 0
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const playBeep = usePlayBeep()
|
||||
const playCorrect = usePlayCorrect()
|
||||
const playKeyboardAudio = usePlayKeyboardAudio()
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
const ttsPlayAudio = useTTsPlayAudio()
|
||||
const volumeIconRef: any = $ref()
|
||||
const volumeTranslateIconRef: any = $ref()
|
||||
|
||||
let displayWord = $computed(() => {
|
||||
return props.word.word.slice(input.length + wrong.length)
|
||||
})
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.resetWord, () => {
|
||||
wrong = input = ''
|
||||
})
|
||||
|
||||
emitter.on(EventKey.onTyping, onTyping)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.resetWord)
|
||||
emitter.off(EventKey.onTyping, onTyping)
|
||||
})
|
||||
|
||||
function repeat() {
|
||||
setTimeout(() => {
|
||||
wrong = input = ''
|
||||
wordRepeatCount++
|
||||
inputLock = false
|
||||
|
||||
if (settingStore.wordSound) {
|
||||
volumeIconRef?.play()
|
||||
}
|
||||
}, settingStore.waitTimeForChangeWord)
|
||||
}
|
||||
|
||||
async function onTyping(e: KeyboardEvent) {
|
||||
if (inputLock) return
|
||||
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)
|
||||
}
|
||||
|
||||
if (isWordRight) {
|
||||
playCorrect()
|
||||
if (settingStore.repeatCount == 100) {
|
||||
if (settingStore.repeatCustomCount <= wordRepeatCount + 1) {
|
||||
setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
|
||||
} else {
|
||||
repeat()
|
||||
}
|
||||
} else {
|
||||
if (settingStore.repeatCount <= wordRepeatCount + 1) {
|
||||
setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
|
||||
} else {
|
||||
repeat()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inputLock = false
|
||||
}
|
||||
}
|
||||
|
||||
function del() {
|
||||
playKeyboardAudio()
|
||||
|
||||
if (wrong) {
|
||||
wrong = ''
|
||||
} else {
|
||||
input = input.slice(0, -1)
|
||||
}
|
||||
}
|
||||
|
||||
function showWord() {
|
||||
if (settingStore.allowWordTip) {
|
||||
showFullWord = true
|
||||
}
|
||||
}
|
||||
|
||||
function hideWord() {
|
||||
showFullWord = false
|
||||
}
|
||||
|
||||
function play() {
|
||||
volumeIconRef?.play()
|
||||
}
|
||||
|
||||
defineExpose({del, showWord, hideWord, play})
|
||||
|
||||
let transHeight = $ref(150)
|
||||
let transWrapperRef = $ref<HTMLDivElement>()
|
||||
let showEnd = $ref(true)
|
||||
|
||||
const transStyle = $computed(() => {
|
||||
return {
|
||||
'justify-content': showEnd ? 'flex-end' : 'unset',
|
||||
height: transHeight + 'px',
|
||||
opacity: settingStore.translate ? 1 : 0
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.word, () => {
|
||||
wrong = input = ''
|
||||
wordRepeatCount = 0
|
||||
inputLock = false
|
||||
if (settingStore.wordSound) {
|
||||
volumeIconRef?.play(400, true)
|
||||
}
|
||||
transHeight = 150
|
||||
nextTick(() => {
|
||||
console.log('transWrapperRef.scrollHeight', transWrapperRef.scrollHeight)
|
||||
let scrollHeight = transWrapperRef.scrollHeight
|
||||
if (scrollHeight <= 240) {
|
||||
showEnd = true
|
||||
if (scrollHeight > transHeight) {
|
||||
transHeight = scrollHeight
|
||||
}
|
||||
} else {
|
||||
showEnd = scrollHeight <= transHeight
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="typing-word">
|
||||
<div class="translate"
|
||||
:style="transStyle"
|
||||
>
|
||||
<div class="wrapper" ref="transWrapperRef">
|
||||
<div class="translate-item" v-for="(v,i) in word.trans">
|
||||
<span>{{ (v.pos ? v.pos + '.' : '') + v.cn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="word-wrapper"
|
||||
:style="{marginTop: transHeight + 6 + 'px'}"
|
||||
>
|
||||
<div class="word"
|
||||
:class="wrong && 'is-wrong'"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'rem'}"
|
||||
>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="settingStore.dictation">
|
||||
<span class="letter" v-if="!showFullWord"
|
||||
@mouseenter="settingStore.allowWordTip && (showFullWord = true)">{{
|
||||
displayWord.split('').map(() => '_').join('')
|
||||
}}</span>
|
||||
<span class="letter" v-else @mouseleave="showFullWord = false">{{ displayWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</div>
|
||||
<Tooltip
|
||||
:title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
>
|
||||
<VolumeIcon ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="phonetic" v-if="settingStore.wordSoundType === 'us' && word.phonetic0">[{{ word.phonetic0 }}]</div>
|
||||
<div class="phonetic" v-if="settingStore.wordSoundType === 'uk' && word.phonetic1">[{{ word.phonetic1 }}]</div>
|
||||
<transition name="fade">
|
||||
<div class="other" v-if="settingStore.detail">
|
||||
<div class="sentences" v-if="word.sentences.length">
|
||||
<div class="title">例句</div>
|
||||
<div class="sentence" v-for="item in word.sentences">
|
||||
<div class="tran">{{ item.tran }}</div>
|
||||
<div class="v">{{ item.v }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sentences" v-if="word.phrases.length">
|
||||
<div class="title">短语</div>
|
||||
<div class="sentence" v-for="item in word.phrases">
|
||||
<div class="tran">{{ item.tran }}</div>
|
||||
<div class="v">{{ item.v }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
.typing-word {
|
||||
width: 95%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
word-break: break-word;
|
||||
position: relative;
|
||||
color: var(--color-font-2);
|
||||
overflow: auto;
|
||||
padding-bottom: 20rem;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none; /* Chrome Safari */
|
||||
}
|
||||
|
||||
.other {
|
||||
margin-top: 10rem;
|
||||
width: 100%;
|
||||
font-size: 18rem;
|
||||
|
||||
.sentences {
|
||||
margin-bottom: 20rem;
|
||||
|
||||
.title {
|
||||
}
|
||||
|
||||
.sentence {
|
||||
margin-bottom: 8rem;
|
||||
|
||||
.tran {
|
||||
color: white;
|
||||
font-size: 18rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.v {
|
||||
color: var(--color-font-1);
|
||||
font-size: 14rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.phonetic, .translate {
|
||||
transition: opacity .3s;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
margin-top: 5rem;
|
||||
font-family: var(--word-font-family);
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 18rem;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
|
||||
.wrapper {
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.volumeIcon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.translate-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
}
|
||||
|
||||
.volumeIcon {
|
||||
transition: opacity .3s;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
margin-top: 150px;
|
||||
margin-left: 30rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
color: var(--color-font-1);
|
||||
|
||||
.word {
|
||||
font-size: 48rem;
|
||||
line-height: 1;
|
||||
font-family: var(--word-font-family);
|
||||
letter-spacing: 5rem;
|
||||
|
||||
.input {
|
||||
color: rgb(22, 163, 74);
|
||||
}
|
||||
|
||||
.wrong {
|
||||
color: rgba(red, 0.6);
|
||||
}
|
||||
|
||||
&.is-wrong {
|
||||
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@ import {cloneDeep, reverse, shuffle} from "lodash-es"
|
||||
import {usePracticeStore} from "@/stores/practice.ts"
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useOnKeyboardEventListener, useWindowClick} from "@/hooks/event.ts";
|
||||
import Typing from "@/pages/pc/practice/practice-word/Typing.vue";
|
||||
import Typing from "@/pages/mobile/practice/practice-word/Typing.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useWordOptions} from "@/hooks/dict.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
@@ -21,6 +21,8 @@ import SlideItem from "@/components/slide/SlideItem.vue";
|
||||
import MobilePanel from "@/pages/mobile/components/MobilePanel.vue";
|
||||
import router from "@/router.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import IconWrapper from "@/components/IconWrapper.vue";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
interface IProps {
|
||||
words: Word[],
|
||||
@@ -42,6 +44,7 @@ const store = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const practiceStore = usePracticeStore()
|
||||
const settingStore = useSettingStore()
|
||||
const {toggleTheme} = useTheme()
|
||||
|
||||
const {
|
||||
isWordCollect,
|
||||
@@ -80,23 +83,15 @@ watch(data, () => {
|
||||
practiceStore.index = data.index
|
||||
})
|
||||
|
||||
const word = $computed(() => {
|
||||
const word: Word = $computed(() => {
|
||||
return data.words[data.index] ?? {
|
||||
trans: [],
|
||||
name: '',
|
||||
usphone: '',
|
||||
ukphone: '',
|
||||
word: '',
|
||||
phonetic0: '',
|
||||
phonetic1: '',
|
||||
}
|
||||
})
|
||||
|
||||
const prevWord: Word = $computed(() => {
|
||||
return data.words?.[data.index - 1] ?? undefined
|
||||
})
|
||||
|
||||
const nextWord: Word = $computed(() => {
|
||||
return data.words?.[data.index + 1] ?? undefined
|
||||
})
|
||||
|
||||
function next(isTyping: boolean = true) {
|
||||
if (data.index === data.words.length - 1) {
|
||||
|
||||
@@ -251,14 +246,19 @@ function change(e) {
|
||||
inputRef.value = ''
|
||||
}
|
||||
|
||||
function know(isTyping: boolean = false) {
|
||||
inputRef.blur()
|
||||
function nextWord() {
|
||||
settingStore.translate = false
|
||||
settingStore.detail = false
|
||||
setTimeout(() => {
|
||||
next(isTyping)
|
||||
next(true)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function complete() {
|
||||
inputRef.blur()
|
||||
settingStore.detail = true
|
||||
}
|
||||
|
||||
function unknow() {
|
||||
settingStore.translate = true
|
||||
inputRef.focus()
|
||||
@@ -283,6 +283,12 @@ onMounted(() => {
|
||||
/>
|
||||
</div>
|
||||
<div class="right">
|
||||
<IconWrapper>
|
||||
<Icon icon="ep:moon" v-if="settingStore.theme === 'dark'"
|
||||
@click="toggleTheme"/>
|
||||
<Icon icon="tabler:sun" v-else @click="toggleTheme"/>
|
||||
</IconWrapper>
|
||||
|
||||
<BaseIcon
|
||||
v-if="!isWordCollect(word)"
|
||||
class="collect"
|
||||
@@ -303,16 +309,18 @@ onMounted(() => {
|
||||
@input="change"
|
||||
type="text">
|
||||
<Typing
|
||||
style="width: 90%;"
|
||||
v-loading="!store.load"
|
||||
ref="typingRef"
|
||||
:word="word"
|
||||
@complete="know(true)"
|
||||
@complete="complete"
|
||||
/>
|
||||
<div class="options">
|
||||
<div class="wrapper">
|
||||
<BaseButton @click="unknow">不认识</BaseButton>
|
||||
<BaseButton @click="know">认识</BaseButton>
|
||||
<BaseButton size="large" v-if="settingStore.detail" @click="nextWord">下一个</BaseButton>
|
||||
<template v-else>
|
||||
<BaseButton size="large" @click="unknow">不认识</BaseButton>
|
||||
<BaseButton size="large" @click="nextWord">认识</BaseButton>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -419,10 +427,9 @@ onMounted(() => {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
padding: 10rem;
|
||||
padding: 0 10rem;
|
||||
box-sizing: border-box;
|
||||
|
||||
.tool-bar {
|
||||
@@ -437,7 +444,7 @@ onMounted(() => {
|
||||
|
||||
:deep(.word) {
|
||||
letter-spacing: 0;
|
||||
font-size: 40rem !important;
|
||||
font-size: 36rem !important;
|
||||
}
|
||||
|
||||
.options {
|
||||
@@ -450,12 +457,11 @@ onMounted(() => {
|
||||
.wrapper {
|
||||
width: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rem;
|
||||
gap: 20rem;
|
||||
}
|
||||
|
||||
.base-button {
|
||||
width: 100%;
|
||||
:deep(.base-button) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ async function onSubmit() {
|
||||
|
||||
if (props.isAdd) {
|
||||
data.id = 'custom-dict-' + Date.now()
|
||||
//TODO 允许同名?
|
||||
if (store.myDictList.find(v => v.name === data.name)) {
|
||||
return ElMessage.warning('已有相同名称词典!')
|
||||
} else {
|
||||
|
||||
@@ -283,7 +283,6 @@ function syncEditDict2MyDictList() {
|
||||
let wordFormData = $ref({
|
||||
where: '',
|
||||
type: '',
|
||||
name: '',
|
||||
id: '',
|
||||
index: 0
|
||||
})
|
||||
|
||||
@@ -82,7 +82,7 @@ function setArticle(val: Article) {
|
||||
articleData.article.sections.map((v, i) => {
|
||||
v.map((w, j) => {
|
||||
w.words.map(s => {
|
||||
if (!store.skipWordNamesWithSimpleWords.includes(s.name.toLowerCase()) && !s.isSymbol) {
|
||||
if (!store.skipWordNamesWithSimpleWords.includes(s.word.toLowerCase()) && !s.isSymbol) {
|
||||
practiceStore.total++
|
||||
}
|
||||
})
|
||||
|
||||
@@ -163,17 +163,17 @@ defineExpose({del, showWord, hideWord, play})
|
||||
>
|
||||
<div class="translate-item" v-for="(v,i) in word.trans">
|
||||
<span>{{ (v.pos ? v.pos + '.' : '') + v.cn }}</span>
|
||||
<!-- <div class="volumeIcon">-->
|
||||
<!-- <Tooltip-->
|
||||
<!-- v-if="i === word.trans.length - 1"-->
|
||||
<!-- :title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlayTranslatePronunciation]})`"-->
|
||||
<!-- >-->
|
||||
<!-- <VolumeIcon-->
|
||||
<!-- ref="volumeTranslateIconRef"-->
|
||||
<!-- :simple="true"-->
|
||||
<!-- :cb="()=>ttsPlayAudio(word.trans.join(';'))"/>-->
|
||||
<!-- </Tooltip>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="volumeIcon">-->
|
||||
<!-- <Tooltip-->
|
||||
<!-- v-if="i === word.trans.length - 1"-->
|
||||
<!-- :title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlayTranslatePronunciation]})`"-->
|
||||
<!-- >-->
|
||||
<!-- <VolumeIcon-->
|
||||
<!-- ref="volumeTranslateIconRef"-->
|
||||
<!-- :simple="true"-->
|
||||
<!-- :cb="()=>ttsPlayAudio(word.trans.join(';'))"/>-->
|
||||
<!-- </Tooltip>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="word-wrapper">
|
||||
@@ -215,6 +215,7 @@ defineExpose({del, showWord, hideWord, play})
|
||||
justify-content: center;
|
||||
word-break: break-word;
|
||||
position: relative;
|
||||
color: var(--color-font-2);
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 20rem;
|
||||
@@ -230,7 +231,6 @@ defineExpose({del, showWord, hideWord, play})
|
||||
position: absolute;
|
||||
transform: translateY(-50%);
|
||||
margin-bottom: 90rem;
|
||||
color: var(--color-font-2);
|
||||
|
||||
&:hover {
|
||||
.volumeIcon {
|
||||
|
||||
@@ -389,6 +389,7 @@ onUnmounted(() => {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
& > div {
|
||||
width: 45%;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import * as localforage from "localforage";
|
||||
import {nanoid} from "nanoid";
|
||||
import {SAVE_DICT_KEY, SAVE_SETTING_KEY} from "@/utils/const.ts";
|
||||
import {checkAndUpgradeSaveDict} from "@/utils";
|
||||
import {checkAndUpgradeSaveDict, getDictFile} from "@/utils";
|
||||
|
||||
export interface BaseState {
|
||||
myDictList: Dict[],
|
||||
@@ -120,10 +120,10 @@ export const useBaseStore = defineStore('base', {
|
||||
return this.myDictList[2]
|
||||
},
|
||||
skipWordNames() {
|
||||
return this.simple.originWords.map(v => v.name.toLowerCase())
|
||||
return this.simple.originWords.map(v => v.word.toLowerCase())
|
||||
},
|
||||
skipWordNamesWithSimpleWords() {
|
||||
return this.simple.originWords.map(v => v.name.toLowerCase()).concat(this.simpleWords)
|
||||
return this.simple.originWords.map(v => v.word.toLowerCase()).concat(this.simpleWords)
|
||||
},
|
||||
isArticle(state: BaseState): boolean {
|
||||
//如果是收藏时,特殊判断
|
||||
@@ -215,8 +215,8 @@ export const useBaseStore = defineStore('base', {
|
||||
let dictResourceUrl = `./dicts/${this.currentDict.language}/${this.currentDict.type}/${this.currentDict.translateLanguage}/${this.currentDict.url}`;
|
||||
if ([DictType.word].includes(this.currentDict.type)) {
|
||||
if (!this.currentDict.originWords.length) {
|
||||
let r = await fetch(dictResourceUrl)
|
||||
let v = await r.json()
|
||||
let v = await getDictFile(dictResourceUrl)
|
||||
v = v.slice(0,50)
|
||||
v.map(s => {
|
||||
s.id = nanoid(6)
|
||||
})
|
||||
@@ -228,8 +228,7 @@ export const useBaseStore = defineStore('base', {
|
||||
|
||||
if ([DictType.article].includes(this.currentDict.type)) {
|
||||
if (!this.currentDict.articles.length) {
|
||||
let r = await fetch(dictResourceUrl)
|
||||
let s: any[] = await r.json()
|
||||
let s = await getDictFile(dictResourceUrl)
|
||||
this.currentDict.articles = cloneDeep(s.map(v => {
|
||||
v.id = nanoid(6)
|
||||
return v
|
||||
|
||||
@@ -5,104 +5,106 @@ import {SAVE_SETTING_KEY} from "@/utils/const.ts";
|
||||
import {checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting} from "@/utils";
|
||||
|
||||
export interface SettingState {
|
||||
showToolbar: boolean,
|
||||
show: boolean,
|
||||
showToolbar: boolean,
|
||||
show: boolean,
|
||||
|
||||
allSound: boolean,
|
||||
wordSound: boolean,
|
||||
wordSoundVolume: number,
|
||||
wordSoundSpeed: number,
|
||||
wordSoundType: string,
|
||||
keyboardSound: boolean,
|
||||
keyboardSoundVolume: number,
|
||||
keyboardSoundFile: string,
|
||||
translateSound: boolean,
|
||||
translateSoundVolume: number,
|
||||
effectSound: boolean,
|
||||
effectSoundVolume: number,
|
||||
repeatCount: number,
|
||||
repeatCustomCount?: number,
|
||||
dictation: boolean,
|
||||
translate: boolean,
|
||||
showNearWord: boolean
|
||||
ignoreCase: boolean
|
||||
allowWordTip: boolean
|
||||
waitTimeForChangeWord: number
|
||||
fontSize: {
|
||||
articleForeignFontSize: number,
|
||||
articleTranslateFontSize: number,
|
||||
wordForeignFontSize: number,
|
||||
wordTranslateFontSize: number,
|
||||
},
|
||||
showPanel: boolean,
|
||||
theme: string,
|
||||
collapse: boolean,
|
||||
chapterWordNumber: number,
|
||||
shortcutKeyMap: Record<string, string>,
|
||||
first: boolean
|
||||
load: boolean
|
||||
allSound: boolean,
|
||||
wordSound: boolean,
|
||||
wordSoundVolume: number,
|
||||
wordSoundSpeed: number,
|
||||
wordSoundType: string,
|
||||
keyboardSound: boolean,
|
||||
keyboardSoundVolume: number,
|
||||
keyboardSoundFile: string,
|
||||
translateSound: boolean,
|
||||
translateSoundVolume: number,
|
||||
effectSound: boolean,
|
||||
effectSoundVolume: number,
|
||||
repeatCount: number,
|
||||
repeatCustomCount?: number,
|
||||
dictation: boolean,
|
||||
translate: boolean,
|
||||
detail: boolean,
|
||||
showNearWord: boolean
|
||||
ignoreCase: boolean
|
||||
allowWordTip: boolean
|
||||
waitTimeForChangeWord: number
|
||||
fontSize: {
|
||||
articleForeignFontSize: number,
|
||||
articleTranslateFontSize: number,
|
||||
wordForeignFontSize: number,
|
||||
wordTranslateFontSize: number,
|
||||
},
|
||||
showPanel: boolean,
|
||||
theme: string,
|
||||
collapse: boolean,
|
||||
chapterWordNumber: number,
|
||||
shortcutKeyMap: Record<string, string>,
|
||||
first: boolean
|
||||
load: boolean
|
||||
}
|
||||
|
||||
export const DefaultSettingState = (): SettingState => ({
|
||||
showToolbar: true,
|
||||
show: false,
|
||||
showPanel: true,
|
||||
showToolbar: true,
|
||||
show: false,
|
||||
showPanel: true,
|
||||
|
||||
allSound: true,
|
||||
wordSound: true,
|
||||
wordSoundVolume: 100,
|
||||
wordSoundSpeed: 1,
|
||||
wordSoundType: 'us',
|
||||
keyboardSound: true,
|
||||
keyboardSoundVolume: 100,
|
||||
keyboardSoundFile: '机械键盘2',
|
||||
translateSound: true,
|
||||
translateSoundVolume: 100,
|
||||
effectSound: true,
|
||||
effectSoundVolume: 100,
|
||||
repeatCount: 1,
|
||||
repeatCustomCount: null,
|
||||
dictation: false,
|
||||
translate: true,
|
||||
allSound: true,
|
||||
wordSound: true,
|
||||
wordSoundVolume: 100,
|
||||
wordSoundSpeed: 1,
|
||||
wordSoundType: 'us',
|
||||
keyboardSound: true,
|
||||
keyboardSoundVolume: 100,
|
||||
keyboardSoundFile: '机械键盘2',
|
||||
translateSound: true,
|
||||
translateSoundVolume: 100,
|
||||
effectSound: true,
|
||||
effectSoundVolume: 100,
|
||||
repeatCount: 1,
|
||||
repeatCustomCount: null,
|
||||
dictation: false,
|
||||
translate: true,
|
||||
detail: false,
|
||||
|
||||
showNearWord: true,
|
||||
ignoreCase: true,
|
||||
allowWordTip: true,
|
||||
fontSize: {
|
||||
articleForeignFontSize: 48,
|
||||
articleTranslateFontSize: 20,
|
||||
wordForeignFontSize: 48,
|
||||
wordTranslateFontSize: 20,
|
||||
},
|
||||
waitTimeForChangeWord: 300,
|
||||
showNearWord: true,
|
||||
ignoreCase: true,
|
||||
allowWordTip: true,
|
||||
fontSize: {
|
||||
articleForeignFontSize: 48,
|
||||
articleTranslateFontSize: 20,
|
||||
wordForeignFontSize: 48,
|
||||
wordTranslateFontSize: 20,
|
||||
},
|
||||
waitTimeForChangeWord: 300,
|
||||
|
||||
theme: 'auto',
|
||||
collapse: false,
|
||||
chapterWordNumber: DefaultChapterWordNumber,
|
||||
shortcutKeyMap: cloneDeep(DefaultShortcutKeyMap),
|
||||
first: true,
|
||||
load: false
|
||||
theme: 'auto',
|
||||
collapse: false,
|
||||
chapterWordNumber: DefaultChapterWordNumber,
|
||||
shortcutKeyMap: cloneDeep(DefaultShortcutKeyMap),
|
||||
first: true,
|
||||
load: false
|
||||
})
|
||||
export const DefaultChapterWordNumber = 30
|
||||
export const useSettingStore = defineStore('setting', {
|
||||
state: (): SettingState => {
|
||||
return DefaultSettingState()
|
||||
state: (): SettingState => {
|
||||
return DefaultSettingState()
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
//这样不会丢失watch的值的引用
|
||||
merge(this, obj)
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
//这样不会丢失watch的值的引用
|
||||
merge(this, obj)
|
||||
},
|
||||
init() {
|
||||
return new Promise(resolve => {
|
||||
let configStr = localStorage.getItem(SAVE_SETTING_KEY.key)
|
||||
if (!configStr) configStr = localStorage.getItem(SAVE_SETTING_KEY.oldKey)
|
||||
let data = checkAndUpgradeSaveSetting(configStr)
|
||||
this.setState(data)
|
||||
localStorage.setItem(SAVE_SETTING_KEY.key, JSON.stringify({val: this.$state, version: SAVE_SETTING_KEY.version}))
|
||||
this.load = true
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
init() {
|
||||
return new Promise(resolve => {
|
||||
let configStr = localStorage.getItem(SAVE_SETTING_KEY.key)
|
||||
if (!configStr) configStr = localStorage.getItem(SAVE_SETTING_KEY.oldKey)
|
||||
let data = checkAndUpgradeSaveSetting(configStr)
|
||||
this.setState(data)
|
||||
localStorage.setItem(SAVE_SETTING_KEY.key, JSON.stringify({val: this.$state, version: SAVE_SETTING_KEY.version}))
|
||||
this.load = true
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -10,12 +10,17 @@ export type WordTrans = {
|
||||
en: string
|
||||
}
|
||||
export type Word = {
|
||||
id?: any,
|
||||
"word": string,
|
||||
"phonetic0": string,
|
||||
"phonetic1": string,
|
||||
"trans": WordTrans[]
|
||||
checked?: boolean,
|
||||
id?: any,
|
||||
sentences?: { v: string, tran: string }[],
|
||||
relWords?: { w: string, tran: string }[],
|
||||
phrases?: { v: string, tran: string } [],
|
||||
synos?: { w: string, tran: string } [],
|
||||
memory?: any,
|
||||
}
|
||||
|
||||
export const DefaultWord: Word = {
|
||||
|
||||
Reference in New Issue
Block a user