feat:add seo,learning page can be directly studied through the url
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user