fix:modify the page routing method
This commit is contained in:
11
package.json
11
package.json
@@ -25,17 +25,16 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"idb-keyval": "^6.2.2",
|
||||
"libarchive-wasm": "^1.2.0",
|
||||
"md5": "^2.2.1",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"pinia": "^3.0.3",
|
||||
"string-comparison": "^1.3.0",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-virtual-scroller": "2.0.0-beta.8",
|
||||
"md5": "^2.2.1"
|
||||
"vue-virtual-scroller": "2.0.0-beta.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/md5": "^2.1.33",
|
||||
"@alicloud/pop-core": "^1.8.0",
|
||||
"@iconify-json/basil": "^1.2.4",
|
||||
"@iconify-json/bi": "^1.2.6",
|
||||
@@ -67,6 +66,7 @@
|
||||
"@iconify-json/uil": "^1.2.3",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/md5": "^2.1.33",
|
||||
"@unocss/postcss": "^66.4.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||
@@ -86,10 +86,11 @@
|
||||
"unplugin-icons": "^22.2.0",
|
||||
"unplugin-vue-components": "^29.0.0",
|
||||
"unplugin-vue-macros": "^2.14.5",
|
||||
"vite-plugin-externals": "^0.6.2",
|
||||
"vite": "^7.0.3",
|
||||
"vite-plugin-externals": "^0.6.2",
|
||||
"vue-tsc": "^3.0.1",
|
||||
"xlsx": "^0.18.5"
|
||||
"xlsx": "^0.18.5",
|
||||
"sitemap": "^8.0.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
36
pnpm-lock.yaml
generated
36
pnpm-lock.yaml
generated
@@ -44,6 +44,9 @@ importers:
|
||||
pinia:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
|
||||
sitemap:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0
|
||||
string-comparison:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
@@ -953,12 +956,18 @@ packages:
|
||||
'@types/md5@2.3.5':
|
||||
resolution: {integrity: sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==}
|
||||
|
||||
'@types/node@17.0.45':
|
||||
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
|
||||
|
||||
'@types/node@20.19.11':
|
||||
resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==}
|
||||
|
||||
'@types/node@24.3.0':
|
||||
resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==}
|
||||
|
||||
'@types/sax@1.2.7':
|
||||
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
|
||||
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
|
||||
@@ -1404,6 +1413,9 @@ packages:
|
||||
archy@1.0.0:
|
||||
resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
|
||||
|
||||
arg@5.0.2:
|
||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
@@ -3293,6 +3305,11 @@ packages:
|
||||
resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
sitemap@8.0.0:
|
||||
resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==}
|
||||
engines: {node: '>=14.0.0', npm: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
snapdragon-node@2.1.1:
|
||||
resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4595,6 +4612,8 @@ snapshots:
|
||||
|
||||
'@types/md5@2.3.5': {}
|
||||
|
||||
'@types/node@17.0.45': {}
|
||||
|
||||
'@types/node@20.19.11':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
@@ -4602,7 +4621,10 @@ snapshots:
|
||||
'@types/node@24.3.0':
|
||||
dependencies:
|
||||
undici-types: 7.10.0
|
||||
optional: true
|
||||
|
||||
'@types/sax@1.2.7':
|
||||
dependencies:
|
||||
'@types/node': 24.3.0
|
||||
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
optional: true
|
||||
@@ -5278,6 +5300,8 @@ snapshots:
|
||||
|
||||
archy@1.0.0: {}
|
||||
|
||||
arg@5.0.2: {}
|
||||
|
||||
argparse@2.0.1:
|
||||
optional: true
|
||||
|
||||
@@ -7340,6 +7364,13 @@ snapshots:
|
||||
mrmime: 2.0.1
|
||||
totalist: 3.0.1
|
||||
|
||||
sitemap@8.0.0:
|
||||
dependencies:
|
||||
'@types/node': 17.0.45
|
||||
'@types/sax': 1.2.7
|
||||
arg: 5.0.2
|
||||
sax: 1.4.1
|
||||
|
||||
snapdragon-node@2.1.1:
|
||||
dependencies:
|
||||
define-property: 1.0.0
|
||||
@@ -7599,8 +7630,7 @@ snapshots:
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
undici-types@7.10.0:
|
||||
optional: true
|
||||
undici-types@7.10.0: {}
|
||||
|
||||
unescape@1.0.1:
|
||||
dependencies:
|
||||
|
||||
41
scripts/generate-sitemap.js
Normal file
41
scripts/generate-sitemap.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const {SitemapStream, streamToPromise} = require('sitemap')
|
||||
const {createWriteStream} = require('fs')
|
||||
const {resolve} = require('path')
|
||||
|
||||
// 你的网站域名
|
||||
const SITE_URL = 'https://yourdomain.com'
|
||||
|
||||
// 静态路由(首页、练习页等)
|
||||
const staticPages = [
|
||||
{url: '/', changefreq: 'daily', priority: 1.0},
|
||||
{url: '/word', changefreq: 'daily', priority: 0.9},
|
||||
{url: '/article', changefreq: 'daily', priority: 0.9},
|
||||
{url: '/setting', changefreq: 'monthly', priority: 0.3},
|
||||
]
|
||||
|
||||
// 动态页面示例(假设你有文章或单词数据)
|
||||
const dynamicPages = [
|
||||
{url: '/article/vue-seo', changefreq: 'weekly', priority: 0.8},
|
||||
{url: '/article/js-tips', changefreq: 'weekly', priority: 0.8},
|
||||
// 如果文章很多,可以用 JSON / API 自动生成数组
|
||||
]
|
||||
|
||||
async function generateSitemap() {
|
||||
const sitemap = new SitemapStream({hostname: SITE_URL})
|
||||
const writeStream = createWriteStream(resolve(__dirname, '../dist/sitemap.xml'))
|
||||
|
||||
sitemap.pipe(writeStream)
|
||||
|
||||
// 添加静态页
|
||||
staticPages.forEach(page => sitemap.write(page))
|
||||
|
||||
// 添加动态页
|
||||
dynamicPages.forEach(page => sitemap.write(page))
|
||||
|
||||
sitemap.end()
|
||||
|
||||
await streamToPromise(sitemap)
|
||||
console.log('✅ sitemap.xml 已生成在 dist 目录')
|
||||
}
|
||||
|
||||
generateSitemap()
|
||||
@@ -43,7 +43,7 @@ function startStudy() {
|
||||
custom: base.sbook.custom,
|
||||
complete: base.sbook.complete,
|
||||
})
|
||||
nav('/study-article', {name: store.sbook.name, id: store.sbook.id})
|
||||
nav('/study-article/' + store.sbook.id)
|
||||
} else {
|
||||
window.umami?.track('no-book')
|
||||
Toast.warning('请先选择一本书籍')
|
||||
@@ -128,7 +128,7 @@ async function goBookDetail(val: DictResource) {
|
||||
<div class="color-blue cursor-pointer" v-if="base.article.bookList.length > 1"
|
||||
@click="isMultiple = !isMultiple; selectIds = []">{{ isMultiple ? '取消' : '管理书籍' }}
|
||||
</div>
|
||||
<div class="color-blue cursor-pointer" @click="nav('dict-detail', { isAdd: true })">创建个人书籍</div>
|
||||
<div class="color-blue cursor-pointer" @click="nav('book-detail', { isAdd: true })">创建个人书籍</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 flex-wrap mt-4">
|
||||
|
||||
@@ -146,7 +146,7 @@ const {
|
||||
|
||||
<div class="card mb-0 h-[95vh]" v-else>
|
||||
<div class="flex justify-between items-center relative">
|
||||
<BackIcon class="z-2" @click="isAdd ? $router.back():(isEdit = false)"/>
|
||||
<BackIcon class="z-2" @click="isAdd ? $router.back:(isEdit = false)"/>
|
||||
<div class="absolute text-2xl text-align-center w-full">{{ runtimeStore.editDict.id ? '修改' : '创建' }}书籍
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import useTheme from "@/hooks/theme.ts";
|
||||
import Toast from '@/pages/pc/components/base/toast/Toast.ts'
|
||||
import {_getDictDataByUrl, cloneDeep} from "@/utils";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useArticleOptions} from "@/hooks/dict.ts";
|
||||
import {getCurrentStudyWord, useArticleOptions} from "@/hooks/dict.ts";
|
||||
import {genArticleSectionData, usePlaySentenceAudio} from "@/hooks/article.ts";
|
||||
import {getDefaultArticle, getDefaultDict} from "@/types/func.ts";
|
||||
import TypingArticle from "@/pages/pc/article/components/TypingArticle.vue";
|
||||
@@ -20,7 +20,7 @@ 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 {dictionaryResources, enArticle} from "@/assets/dictionary.ts";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
@@ -39,7 +39,7 @@ let articleData = $ref({
|
||||
})
|
||||
let showEditArticle = $ref(false)
|
||||
let typingArticleRef = $ref<any>()
|
||||
let loading = $ref<boolean>(true)
|
||||
let loading = $ref<boolean>(false)
|
||||
let editArticle = $ref<Article>(getDefaultArticle())
|
||||
|
||||
function write() {
|
||||
@@ -87,61 +87,51 @@ function next() {
|
||||
|
||||
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 {
|
||||
async function init() {
|
||||
console.log('load好了开始加载')
|
||||
let dict = getDefaultDict()
|
||||
let dictId = route.params.id
|
||||
if (dictId) {
|
||||
//先在自己的词典列表里面找,如果没有再在资源列表里面找
|
||||
dict = store.article.bookList.find(v => v.id === dictId)
|
||||
if (!dict) dict = enArticle.find(v => v.id === dictId) as Dict
|
||||
if (dict && dict.id) {
|
||||
//如果是不是自定义词典,就请求数据
|
||||
if (!dict.custom) dict = await _getDictDataByUrl(dict, DictType.article)
|
||||
if (!dict.articles.length) {
|
||||
router.push('/article')
|
||||
return Toast.warning('没有文章可学习!')
|
||||
}
|
||||
store.changeBook(dict)
|
||||
articleData.list = cloneDeep(store.sbook.articles)
|
||||
getCurrentPractice()
|
||||
loading = false
|
||||
} else {
|
||||
router.push('/article')
|
||||
}
|
||||
} else {
|
||||
router.push('/article')
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => store.load, (n) => {
|
||||
if (n && loading) init()
|
||||
}, {immediate: true})
|
||||
|
||||
onMounted(() => {
|
||||
if (store.sbook?.articles?.length) {
|
||||
articleData.list = cloneDeep(store.sbook.articles)
|
||||
getCurrentPractice()
|
||||
} else {
|
||||
loading = true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
useDisableEventListener(() => loading)
|
||||
|
||||
function setArticle(val: Article) {
|
||||
statisticsStore.inputWordNumber = 0
|
||||
statisticsStore.wrong = 0
|
||||
@@ -246,8 +236,6 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
|
||||
useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
|
||||
onMounted(init)
|
||||
|
||||
useEvents([
|
||||
[EventKey.write, write],
|
||||
[EventKey.repeatStudy, repeat],
|
||||
|
||||
@@ -61,54 +61,47 @@ 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
|
||||
async function init() {
|
||||
console.log('load好了开始加载')
|
||||
let dict = getDefaultDict()
|
||||
let dictId = route.params.id
|
||||
if (dictId) {
|
||||
//先在自己的词典列表里面找,如果没有再在资源列表里面找
|
||||
dict = store.word.bookList.find(v => v.id === dictId)
|
||||
if (!dict) dict = dictionaryResources.find(v => v.id === dictId) as Dict
|
||||
if (dict && dict.id) {
|
||||
//如果是不是自定义词典,就请求数据
|
||||
if (!dict.custom) dict = await _getDictDataByUrl(dict)
|
||||
if (!dict.words.length) {
|
||||
router.push('/word')
|
||||
return Toast.warning('没有单词可学习!')
|
||||
}
|
||||
store.changeDict(dict)
|
||||
studyData = getCurrentStudyWord()
|
||||
loading = false
|
||||
} else {
|
||||
router.push('/word')
|
||||
}
|
||||
} else {
|
||||
router.push('/word')
|
||||
}
|
||||
}, {immediate: true})
|
||||
}
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
useDisableEventListener(() => loading)
|
||||
watch(() => store.load, (n) => {
|
||||
if (n && loading) init()
|
||||
}, {immediate: true})
|
||||
|
||||
onMounted(() => {
|
||||
if (runtimeStore.routeData) {
|
||||
studyData = runtimeStore.routeData
|
||||
} 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 = 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')
|
||||
}
|
||||
loading = true
|
||||
}
|
||||
})
|
||||
|
||||
useStartKeyboardEventListener()
|
||||
useDisableEventListener(() => loading)
|
||||
|
||||
watch(() => studyData, () => {
|
||||
if (studyData.new.length === 0) {
|
||||
if (studyData.review.length) {
|
||||
|
||||
@@ -60,7 +60,7 @@ function startStudy() {
|
||||
custom: store.sdict.custom,
|
||||
complete: store.sdict.complete,
|
||||
})
|
||||
nav('study-word', {name: store.sdict.name, id: store.sdict.id}, currentStudy)
|
||||
nav('study-word/' + store.sdict.id, {}, currentStudy)
|
||||
} else {
|
||||
window.umami?.track('no-dict')
|
||||
Toast.warning('请先选择一本词典')
|
||||
|
||||
@@ -21,11 +21,11 @@ export const routes: RouteRecordRaw[] = [
|
||||
// {path: 'home', component: HomeIndex},
|
||||
{path: 'word', component: WordHomePage},
|
||||
{path: 'dict-list', component: DictList},
|
||||
{path: 'study-word', component: StudyWord},
|
||||
{path: 'study-word/:id', component: StudyWord},
|
||||
{path: 'dict-detail', component: DictDetail},
|
||||
|
||||
{path: 'article', component: ArticleHomePage},
|
||||
{path: 'study-article', component: StudyArticle},
|
||||
{path: 'study-article/:id', component: StudyArticle},
|
||||
{path: 'book-detail', component: BookDetail},
|
||||
{path: 'book-list', component: BookList},
|
||||
{path: 'edit-article', component: () => import("@/pages/pc/article/EditArticlePage.vue")},
|
||||
@@ -39,8 +39,8 @@ export const routes: RouteRecordRaw[] = [
|
||||
]
|
||||
|
||||
const router = VueRouter.createRouter({
|
||||
// history: VueRouter.createWebHistory(),
|
||||
history: VueRouter.createWebHashHistory(),
|
||||
history: VueRouter.createWebHistory(),
|
||||
// history: VueRouter.createWebHashHistory(),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// console.log('savedPosition', savedPosition)
|
||||
|
||||
@@ -414,9 +414,9 @@ export async function sleep(time: number) {
|
||||
|
||||
export async function _getDictDataByUrl(val: DictResource, type: DictType = DictType.word): Promise<Dict> {
|
||||
// await sleep(2000);
|
||||
let dictResourceUrl = `./dicts/${val.language}/word/${val.url}`
|
||||
let dictResourceUrl = `/dicts/${val.language}/word/${val.url}`
|
||||
if (type === DictType.article) {
|
||||
dictResourceUrl = `./dicts/${val.language}/${val.type}/${val.url}`;
|
||||
dictResourceUrl = `/dicts/${val.language}/${val.type}/${val.url}`;
|
||||
}
|
||||
let s = await getDictFile(dictResourceUrl)
|
||||
if (s) {
|
||||
|
||||
Reference in New Issue
Block a user