feat:add seo,learning page can be directly studied through the url
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -19,6 +19,9 @@ declare module 'vue' {
|
||||
IconBiArrowRight: typeof import('~icons/bi/arrow-right')['default']
|
||||
IconBiKeyboard: typeof import('~icons/bi/keyboard')['default']
|
||||
IconBxHeadphone: typeof import('~icons/bx/headphone')['default']
|
||||
IconBxVolume: typeof import('~icons/bx/volume')['default']
|
||||
IconBxVolumeFull: typeof import('~icons/bx/volume-full')['default']
|
||||
IconBxVolumeLow: typeof import('~icons/bx/volume-low')['default']
|
||||
IconCarbonCloseOutline: typeof import('~icons/carbon/close-outline')['default']
|
||||
IconCarbonMove: typeof import('~icons/carbon/move')['default']
|
||||
IconEosIconsLoading: typeof import('~icons/eos-icons/loading')['default']
|
||||
|
||||
21
src/App.vue
21
src/App.vue
@@ -6,8 +6,8 @@ import {useSettingStore} from "@/stores/setting.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import CollectNotice from "@/pages/pc/components/CollectNotice.vue";
|
||||
import {SAVE_DICT_KEY, SAVE_SETTING_KEY} from "@/utils/const.ts";
|
||||
import {isMobile, shakeCommonDict} from "@/utils";
|
||||
import router, {routes} from "@/router.ts";
|
||||
import {shakeCommonDict} from "@/utils";
|
||||
import {routes} from "@/router.ts";
|
||||
import {set} from 'idb-keyval'
|
||||
|
||||
import {useRoute} from "vue-router";
|
||||
@@ -22,29 +22,18 @@ watch(store.$state, (n: BaseState) => {
|
||||
})
|
||||
|
||||
watch(settingStore.$state, (n) => {
|
||||
console.log('watch',settingStore.$state)
|
||||
set(SAVE_SETTING_KEY.key, JSON.stringify({val: n, version: SAVE_SETTING_KEY.version}))
|
||||
})
|
||||
|
||||
async function init() {
|
||||
// console.time()
|
||||
store.init().then(() => {
|
||||
store.load = true
|
||||
// console.timeEnd()
|
||||
})
|
||||
await store.init()
|
||||
await settingStore.init()
|
||||
store.load = true
|
||||
setTheme(settingStore.theme)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
onMounted(init)
|
||||
|
||||
if (isMobile()) {
|
||||
// 当前设备是移动设备
|
||||
console.log('当前设备是移动设备')
|
||||
router.replace('/mobile')
|
||||
}
|
||||
})
|
||||
let transitionName = $ref('go')
|
||||
const route = useRoute()
|
||||
watch(() => route.path, (to, from) => {
|
||||
|
||||
@@ -49,11 +49,23 @@ defineExpose({play})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseIcon @click.stop="click"
|
||||
v-if="props.simple"
|
||||
no-bg
|
||||
:icon="iconList[step]"/>
|
||||
<BaseIcon @click.stop="click" v-else :icon="iconList[step]"/>
|
||||
<template v-if="props.simple">
|
||||
<BaseIcon @click.stop="click"
|
||||
no-bg
|
||||
>
|
||||
<IconBxVolume v-if="step === 0"/>
|
||||
<IconBxVolumeLow v-if="step === 1"/>
|
||||
<IconBxVolumeFull v-if="step === 2"/>
|
||||
</BaseIcon>
|
||||
</template>
|
||||
<template v-else>
|
||||
<BaseIcon @click.stop="click"
|
||||
>
|
||||
<IconBxVolume v-if="step === 0"/>
|
||||
<IconBxVolumeLow v-if="step === 1"/>
|
||||
<IconBxVolumeFull v-if="step === 2"/>
|
||||
</BaseIcon>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
61
src/directives/loading.tsx
Normal file
61
src/directives/loading.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
// src/directives/loading.js
|
||||
import {createApp, h} from 'vue'
|
||||
import IconEosIconsLoading from '~icons/eos-icons/loading'
|
||||
|
||||
// 创建一个 Loading 组件
|
||||
const LoadingComponent = {
|
||||
name: 'LoadingComponent',
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
background: 'rgba(255, 255, 255, 0.7)',
|
||||
zIndex: 9999
|
||||
}}
|
||||
>
|
||||
<IconEosIconsLoading class="text-3xl"/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义指令
|
||||
export default {
|
||||
mounted(el, binding) {
|
||||
const position = getComputedStyle(el).position
|
||||
if (position === 'static' || !position) {
|
||||
el.style.position = 'relative' // 保证 loading 居中
|
||||
}
|
||||
|
||||
const app = createApp(LoadingComponent)
|
||||
const instance = app.mount(document.createElement('div'))
|
||||
el.__loadingInstance = instance
|
||||
|
||||
if (binding.value) {
|
||||
el.appendChild(instance.$el)
|
||||
}
|
||||
},
|
||||
updated(el, binding) {
|
||||
const instance = el.__loadingInstance
|
||||
if (binding.value && !el.contains(instance.$el)) {
|
||||
el.appendChild(instance.$el)
|
||||
} else if (!binding.value && el.contains(instance.$el)) {
|
||||
el.removeChild(instance.$el)
|
||||
}
|
||||
},
|
||||
unmounted(el) {
|
||||
const instance = el.__loadingInstance
|
||||
if (instance && instance.$el.parentNode) {
|
||||
instance.$el.parentNode.removeChild(instance.$el)
|
||||
}
|
||||
delete el.__loadingInstance
|
||||
}
|
||||
}
|
||||
@@ -546,7 +546,7 @@ export function usePlaySentenceAudio() {
|
||||
ref.currentTime = start
|
||||
ref.play()
|
||||
let end = sentence.audioPosition?.[1]
|
||||
console.log(sentence.audioPosition,(end - start) * 1000)
|
||||
// console.log(sentence.audioPosition,(end - start) * 1000)
|
||||
|
||||
if (end && end !== -1) {
|
||||
timer = setTimeout(() => {
|
||||
|
||||
@@ -112,20 +112,10 @@ export function useOnKeyboardEventListener(onKeyDown: (e: KeyboardEvent) => void
|
||||
})
|
||||
}
|
||||
|
||||
//因为如果用useStartKeyboardEventListener局部变量控制,当出现多个hooks时就不行了,所以用全局变量来控制
|
||||
export function useDisableEventListener(watchVal: any) {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
watch(watchVal, (n: any) => {
|
||||
if (n === true) runtimeStore.disableEventListener = true
|
||||
if (n === false) runtimeStore.disableEventListener = false
|
||||
})
|
||||
onMounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.disableEventListener = true
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.disableEventListener = false
|
||||
}
|
||||
runtimeStore.disableEventListener = n
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ export function useSound(audioSrcList?: string[], audioFileLength?: number) {
|
||||
}
|
||||
|
||||
function play(volume: number = 100) {
|
||||
console.log('play')
|
||||
index++
|
||||
if (audioList.length > 1 && audioList.length !== audioLength) {
|
||||
audioList[index % audioList.length].volume = volume / 100
|
||||
|
||||
@@ -7,6 +7,7 @@ import router from "@/router.ts";
|
||||
import VueVirtualScroller from 'vue-virtual-scroller'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
import './types/global.d.ts'
|
||||
import loadingDirective from './directives/loading.tsx'
|
||||
|
||||
const pinia = createPinia()
|
||||
const app = createApp(App)
|
||||
@@ -18,5 +19,6 @@ app.use(router)
|
||||
app.directive('opacity', (el, binding) => {
|
||||
el.style.opacity = binding.value ? 1 : 0
|
||||
})
|
||||
app.directive('loading', loadingDirective)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {ref, watch} from "vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {getAudioFileUrl, useChangeAllSound, usePlayAudio, useWatchAllSound} from "@/hooks/sound.ts";
|
||||
import {getShortcutKey, useDisableEventListener, useEventListener} from "@/hooks/event.ts";
|
||||
import {getShortcutKey, useEventListener} from "@/hooks/event.ts";
|
||||
import {checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting, cloneDeep, shakeCommonDict} from "@/utils";
|
||||
import {DefaultShortcutKeyMap, ShortcutKey} from "@/types/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
@@ -31,7 +31,6 @@ const store = useBaseStore()
|
||||
//@ts-ignore
|
||||
const gitLastCommitHash = ref(LATEST_COMMIT_HASH);
|
||||
|
||||
useDisableEventListener(() => undefined)
|
||||
useWatchAllSound()
|
||||
|
||||
let editShortcutKey = $ref('')
|
||||
|
||||
@@ -43,7 +43,7 @@ function startStudy() {
|
||||
custom: base.sbook.custom,
|
||||
complete: base.sbook.complete,
|
||||
})
|
||||
nav('/study-article')
|
||||
nav('/study-article', {name: store.sbook.name, id: store.sbook.id})
|
||||
} else {
|
||||
window.umami?.track('no-book')
|
||||
Toast.warning('请先选择一本书籍')
|
||||
@@ -120,7 +120,7 @@ async function goBookDetail(val: DictResource) {
|
||||
<div class="title">我的书籍</div>
|
||||
<div class="flex gap-4 items-center">
|
||||
<PopConfirm title="确认删除所有选中书籍?" @confirm="handleBatchDel" v-if="selectIds.length">
|
||||
<BaseIcon class="del" title="删除" >
|
||||
<BaseIcon class="del" title="删除">
|
||||
<DeleteIcon/>
|
||||
</BaseIcon>
|
||||
</PopConfirm>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
import List from "@/pages/pc/components/list/List.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useDisableEventListener, useWindowClick} from "@/hooks/event.ts";
|
||||
import {useWindowClick} from "@/hooks/event.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {nanoid} from "nanoid";
|
||||
@@ -42,8 +42,6 @@ onUnmounted(() => {
|
||||
emitter.off(EventKey.openArticleListModal)
|
||||
})
|
||||
|
||||
useDisableEventListener(() => show)
|
||||
|
||||
async function selectArticle(item: Article) {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {Article, ArticleItem, ArticleWord, ShortcutKey, Word} from "@/types/types.ts";
|
||||
import {useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import {Article, ArticleItem, ArticleWord, Dict, DictType, ShortcutKey, Word} from "@/types/types.ts";
|
||||
import {useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import Toast from '@/pages/pc/components/base/toast/Toast.ts'
|
||||
import {cloneDeep} from "@/utils";
|
||||
import {_getDictDataByUrl, cloneDeep} from "@/utils";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useArticleOptions} from "@/hooks/dict.ts";
|
||||
import {genArticleSectionData, usePlaySentenceAudio} from "@/hooks/article.ts";
|
||||
import router from "@/router.ts";
|
||||
import {getDefaultArticle} from "@/types/func.ts";
|
||||
import {getDefaultArticle, getDefaultDict} from "@/types/func.ts";
|
||||
import TypingArticle from "@/pages/pc/article/components/TypingArticle.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Panel from "@/pages/pc/components/Panel.vue";
|
||||
@@ -21,6 +20,9 @@ import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import EditSingleArticleModal from "@/pages/pc/article/components/EditSingleArticleModal.vue";
|
||||
import Tooltip from "@/pages/pc/components/base/Tooltip.vue";
|
||||
import ConflictNotice from "@/pages/pc/components/ConflictNotice.vue";
|
||||
import {enArticle} from "@/assets/dictionary.ts";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
@@ -37,10 +39,9 @@ let articleData = $ref({
|
||||
})
|
||||
let showEditArticle = $ref(false)
|
||||
let typingArticleRef = $ref<any>()
|
||||
let loading = $ref<boolean>(true)
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
function write() {
|
||||
// console.log('write')
|
||||
settingStore.dictation = true
|
||||
@@ -84,14 +85,61 @@ function next() {
|
||||
getCurrentPractice()
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (!store.sbook?.articles?.length) {
|
||||
router.push('/article')
|
||||
return
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
|
||||
watch(() => store.load, (n) => {
|
||||
if (n && loading && runtimeStore.editDict.id) {
|
||||
console.log('load好了开始加载')
|
||||
store.changeBook(runtimeStore.editDict)
|
||||
articleData.list = cloneDeep(store.sbook.articles)
|
||||
getCurrentPractice()
|
||||
loading = false
|
||||
}
|
||||
},{immediate: true})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
useDisableEventListener(() => loading)
|
||||
|
||||
function init() {
|
||||
if (store.sbook?.articles?.length) {
|
||||
articleData.list = cloneDeep(store.sbook.articles)
|
||||
getCurrentPractice()
|
||||
loading = false
|
||||
} else {
|
||||
let dictName = route.query.name
|
||||
let dictId = route.query.id
|
||||
//如果url里有词典id或name,那么直接请求词典数据,并加到bookList里面进行学习
|
||||
//todo 这里要处理自定义词典的问题
|
||||
if (dictName || dictId) {
|
||||
let dictResource = getDefaultDict()
|
||||
if (dictId) dictResource = enArticle.find(v => v.id === dictId) as Dict
|
||||
else if (dictName) dictResource = enArticle.find(v => v.name === dictName) as Dict
|
||||
if (dictResource.id) {
|
||||
loading = true
|
||||
_getDictDataByUrl(dictResource, DictType.article).then(r => {
|
||||
if (!r.articles.length) {
|
||||
router.push('/article')
|
||||
return Toast.warning('没有文章可学习!')
|
||||
}
|
||||
runtimeStore.editDict = r
|
||||
if (store.load) {
|
||||
console.log('直接加载')
|
||||
store.changeBook(r)
|
||||
articleData.list = cloneDeep(store.sbook.articles)
|
||||
getCurrentPractice()
|
||||
loading = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
router.push('/article')
|
||||
}
|
||||
} else {
|
||||
router.push('/article')
|
||||
}
|
||||
}
|
||||
articleData.list = cloneDeep(store.sbook.articles)
|
||||
getCurrentPractice()
|
||||
console.log('init', articleData.article)
|
||||
}
|
||||
|
||||
function setArticle(val: Article) {
|
||||
@@ -183,7 +231,6 @@ function show() {
|
||||
typingArticleRef?.showSentence()
|
||||
}
|
||||
|
||||
|
||||
function onKeyUp() {
|
||||
typingArticleRef.hideSentence()
|
||||
}
|
||||
@@ -199,7 +246,6 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
|
||||
useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
|
||||
|
||||
onMounted(init)
|
||||
|
||||
useEvents([
|
||||
@@ -240,7 +286,7 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<div class="practice-wrapper" v-loading="loading">
|
||||
<div class="practice-article">
|
||||
<TypingArticle
|
||||
ref="typingArticleRef"
|
||||
@@ -290,11 +336,11 @@ const {playSentenceAudio} = usePlaySentenceAudio()
|
||||
<div class="footer" :class="!settingStore.showToolbar && 'hide'">
|
||||
<Tooltip :title="settingStore.showToolbar?'收起':'展开'">
|
||||
<IconIconParkOutlineDown
|
||||
@click="settingStore.showToolbar = !settingStore.showToolbar"
|
||||
class="arrow"
|
||||
:class="!settingStore.showToolbar && 'down'"
|
||||
width="24"
|
||||
color="#999"/>
|
||||
@click="settingStore.showToolbar = !settingStore.showToolbar"
|
||||
class="arrow"
|
||||
:class="!settingStore.showToolbar && 'down'"
|
||||
width="24"
|
||||
color="#999"/>
|
||||
</Tooltip>
|
||||
|
||||
<div class="bottom">
|
||||
|
||||
@@ -5,6 +5,7 @@ import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import EditArticle from "@/pages/pc/article/components/EditArticle.vue";
|
||||
import {getDefaultArticle} from "@/types/func.ts";
|
||||
import {defineAsyncComponent} from "vue";
|
||||
|
||||
const Dialog = defineAsyncComponent(() => import('@/pages/pc/components/dialog/Dialog.vue'))
|
||||
|
||||
interface IProps {
|
||||
|
||||
@@ -41,7 +41,6 @@ const emit = defineEmits<{
|
||||
edit: [val: Article]
|
||||
}>()
|
||||
|
||||
let isPlay = $ref(false)
|
||||
let typeArticleRef = $ref<HTMLInputElement>(null)
|
||||
let articleWrapperRef = $ref<HTMLInputElement>(null)
|
||||
let sectionIndex = $ref(0)
|
||||
@@ -93,7 +92,6 @@ watch(() => settingStore.translate, () => {
|
||||
checkTranslateLocation().then(() => checkCursorPosition())
|
||||
})
|
||||
|
||||
|
||||
function checkCursorPosition(a = sectionIndex, b = sentenceIndex, c = wordIndex) {
|
||||
// console.log('checkCursorPosition')
|
||||
_nextTick(() => {
|
||||
@@ -143,7 +141,6 @@ function checkTranslateLocation() {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
let lockNextSentence = false
|
||||
|
||||
function nextSentence() {
|
||||
|
||||
@@ -2,23 +2,24 @@
|
||||
|
||||
import {defineAsyncComponent, onMounted, watch} from "vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
const Dialog = defineAsyncComponent(() => import('@/pages/pc/components/dialog/Dialog.vue'))
|
||||
|
||||
let settingStore = useSettingStore()
|
||||
let show = $ref(false)
|
||||
|
||||
watch(() => settingStore.load, (n) => {
|
||||
show = settingStore.conflictNotice
|
||||
})
|
||||
onMounted(() => {
|
||||
if (settingStore.load) {
|
||||
show = settingStore.conflictNotice
|
||||
if (n && settingStore.conflictNotice) {
|
||||
setTimeout(() => {
|
||||
show = true
|
||||
}, 300)
|
||||
}
|
||||
})
|
||||
}, {immediate: true})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog :modelValue="show"
|
||||
<Dialog v-model="show"
|
||||
title="提示"
|
||||
footer
|
||||
cancel-button-text="不再提醒"
|
||||
@@ -39,7 +40,7 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="pl-4">
|
||||
<div>①:请打开浏览器无痕模式尝试</div>
|
||||
<div>②:无痕模式下无法正常使用,请给<a href="https://github.com/zyronon/TypeWords/issues">作者提一个 BUG</a>
|
||||
<div>②:无痕模式下无法正常使用,请给<a href="https://github.com/zyronon/TypeWords/issues">作者提 BUG</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,8 @@ import Statistics from "@/pages/pc/word/Statistics.vue";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {ShortcutKey, StudyData, Word} from "@/types/types.ts";
|
||||
import {useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import {Dict, ShortcutKey, StudyData, Word} from "@/types/types.ts";
|
||||
import {useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import {getCurrentStudyWord, useWordOptions} from "@/hooks/dict.ts";
|
||||
import {_getDictDataByUrl, cloneDeep, shuffle} from "@/utils";
|
||||
@@ -23,7 +23,7 @@ import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {dictionaryResources} from "@/assets/dictionary.ts";
|
||||
import Toast from '@/pages/pc/components/base/toast/Toast.ts'
|
||||
import {getDefaultWord} from "@/types/func.ts";
|
||||
import {getDefaultDict, getDefaultWord} from "@/types/func.ts";
|
||||
import ConflictNotice from "@/pages/pc/components/ConflictNotice.vue";
|
||||
|
||||
interface IProps {
|
||||
@@ -61,26 +61,48 @@ let data = $ref<StudyData>({
|
||||
wrongWords: [],
|
||||
})
|
||||
|
||||
watch(() => store.load, (n) => {
|
||||
if (n && loading && runtimeStore.editDict.id) {
|
||||
console.log('load好了开始加载')
|
||||
store.changeDict(runtimeStore.editDict)
|
||||
studyData = getCurrentStudyWord()
|
||||
loading = false
|
||||
}
|
||||
}, {immediate: true})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
useDisableEventListener(() => loading)
|
||||
|
||||
onMounted(() => {
|
||||
let dictId = route.query.q
|
||||
//如果url里有词典id,那么直接请求词典数据,并加到bookList里面进行学习
|
||||
if (dictId) {
|
||||
let dictResource = dictionaryResources.find(v => v.id === dictId)
|
||||
if (dictResource) {
|
||||
loading = true
|
||||
requestIdleCallback(() => {
|
||||
_getDictDataByUrl(dictResource).then(r => {
|
||||
store.changeDict(r)
|
||||
studyData = getCurrentStudyWord()
|
||||
loading = false
|
||||
})
|
||||
})
|
||||
} else {
|
||||
router.push('/word')
|
||||
}
|
||||
if (runtimeStore.routeData) {
|
||||
studyData = runtimeStore.routeData
|
||||
} else {
|
||||
if (runtimeStore.routeData) {
|
||||
studyData = runtimeStore.routeData
|
||||
let dictName = route.query.name
|
||||
let dictId = route.query.id
|
||||
//如果url里有词典id或name,那么直接请求词典数据,并加到bookList里面进行学习
|
||||
//todo 这里要处理自定义词典的问题
|
||||
if (dictName || dictId) {
|
||||
let dictResource = getDefaultDict()
|
||||
if (dictId) dictResource = dictionaryResources.find(v => v.id === dictId) as Dict
|
||||
else if (dictName) dictResource = dictionaryResources.find(v => v.name === dictName) as Dict
|
||||
if (dictResource.id) {
|
||||
loading = true
|
||||
_getDictDataByUrl(dictResource).then(r => {
|
||||
if (!r.words.length) {
|
||||
router.push('/word')
|
||||
return Toast.warning('没有单词可学习!')
|
||||
}
|
||||
runtimeStore.editDict = r
|
||||
if (store.load) {
|
||||
console.log('直接加载')
|
||||
store.changeDict(r)
|
||||
studyData = getCurrentStudyWord()
|
||||
loading = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
router.push('/word')
|
||||
}
|
||||
} else {
|
||||
router.push('/word')
|
||||
}
|
||||
@@ -124,9 +146,6 @@ watch(() => studyData, () => {
|
||||
|
||||
provide('studyData', data)
|
||||
|
||||
const dictIsEnd = $computed(() => {
|
||||
return store.sdict.lastLearnIndex === store.sdict.length
|
||||
})
|
||||
const word = $computed(() => {
|
||||
return data.words[data.index] ?? getDefaultWord()
|
||||
})
|
||||
@@ -248,7 +267,6 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
|
||||
useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
|
||||
@@ -354,7 +372,7 @@ useEvents([
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="practice-wrapper">
|
||||
<div class="practice-wrapper" v-loading="loading">
|
||||
<div class="practice-word">
|
||||
<div class="absolute z-1 top-4 w-full" v-if="settingStore.showNearWord">
|
||||
<div class="center gap-2 cursor-pointer float-left"
|
||||
@@ -375,7 +393,7 @@ useEvents([
|
||||
>
|
||||
<div class="word" :class="settingStore.dictation && 'word-shadow'">{{ nextWord.word }}</div>
|
||||
</Tooltip>
|
||||
<IconBiArrowRight class="arrow" width="22"/>
|
||||
<IconBiArrowRight class="arrow" width="22"/>
|
||||
</div>
|
||||
</div>
|
||||
<TypeWord
|
||||
|
||||
@@ -24,7 +24,7 @@ const store = useBaseStore()
|
||||
const router = useRouter()
|
||||
const {nav} = useNav()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let loading = $ref(true)
|
||||
let currentStudy = $ref({
|
||||
new: [],
|
||||
review: [],
|
||||
@@ -45,6 +45,7 @@ async function init() {
|
||||
if (!currentStudy.new.length && store.sdict.words.length) {
|
||||
currentStudy = getCurrentStudyWord()
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
|
||||
function startStudy() {
|
||||
@@ -59,7 +60,7 @@ function startStudy() {
|
||||
custom: store.sdict.custom,
|
||||
complete: store.sdict.complete,
|
||||
})
|
||||
nav('study-word', {}, currentStudy)
|
||||
nav('study-word', {name: store.sdict.name, id: store.sdict.id}, currentStudy)
|
||||
} else {
|
||||
window.umami?.track('no-dict')
|
||||
Toast.warning('请先选择一本词典')
|
||||
@@ -188,7 +189,9 @@ const progressTextRight = $computed(() => {
|
||||
</div>
|
||||
个单词 <span class="color-blue cursor-pointer" @click="setPerDayStudyNumber">更改</span>
|
||||
</div>
|
||||
<BaseButton size="large" :disabled="!store.sdict.name" @click="startStudy">
|
||||
<BaseButton size="large" :disabled="!store.sdict.name"
|
||||
:loading="loading"
|
||||
@click="startStudy">
|
||||
<!-- <BaseButton size="large" @click="startStudy">-->
|
||||
<div class="flex items-center gap-2">
|
||||
<span>开始学习</span>
|
||||
|
||||
@@ -99,13 +99,16 @@ export const useSettingStore = defineStore('setting', {
|
||||
},
|
||||
init() {
|
||||
return new Promise(async resolve => {
|
||||
//TODO 后面记得删除了
|
||||
let configStr = localStorage.getItem(SAVE_SETTING_KEY.key)
|
||||
if (!configStr) {
|
||||
configStr = await get(SAVE_SETTING_KEY.key)
|
||||
let configStr2 = await get(SAVE_SETTING_KEY.key)
|
||||
if (configStr2) {
|
||||
//兼容localStorage.getItem
|
||||
configStr = configStr2
|
||||
}
|
||||
let data = checkAndUpgradeSaveSetting(configStr)
|
||||
this.setState(data)
|
||||
this.load = true
|
||||
this.setState({...data, load: true})
|
||||
// this.load = true
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export const EventKey = {
|
||||
write: 'write',
|
||||
editDict: 'editDict',
|
||||
openMyDictDialog: 'openMyDictDialog',
|
||||
stateInitEnd: 'stateInitEnd',
|
||||
}
|
||||
|
||||
export function useEvent(key: string, func: any) {
|
||||
|
||||
@@ -408,7 +408,12 @@ export function _parseLRC(lrc: string): { start: number, end: number, text: stri
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export async function sleep(time: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, time));
|
||||
}
|
||||
|
||||
export async function _getDictDataByUrl(val: DictResource, type: DictType = DictType.word): Promise<Dict> {
|
||||
// await sleep(2000);
|
||||
let dictResourceUrl = `./dicts/${val.language}/word/${val.url}`
|
||||
if (type === DictType.article) {
|
||||
dictResourceUrl = `./dicts/${val.language}/${val.type}/${val.url}`;
|
||||
|
||||
Reference in New Issue
Block a user