SSR test
This commit is contained in:
parent
8e66b3356a
commit
d74d5845d4
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -16,6 +16,7 @@
|
||||||
"Codepen",
|
"Codepen",
|
||||||
"commonmark",
|
"commonmark",
|
||||||
"cout",
|
"cout",
|
||||||
|
"crossorigin",
|
||||||
"dompurify",
|
"dompurify",
|
||||||
"fontawesome",
|
"fontawesome",
|
||||||
"galgame",
|
"galgame",
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
"non-moe",
|
"non-moe",
|
||||||
"nord",
|
"nord",
|
||||||
"nprogress",
|
"nprogress",
|
||||||
|
"nuxt",
|
||||||
"okaidia",
|
"okaidia",
|
||||||
"Otome",
|
"Otome",
|
||||||
"Owaru",
|
"Owaru",
|
||||||
|
|
13
index.html
13
index.html
|
@ -10,12 +10,17 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="The CUTEST Visual Novel Forum!世界上最萌的 Galgame 论坛. Topic, Technique. NO ADs Forever. Free Forever"
|
content="KUN Visual Novel | 鲲 Galgame. The CUTEST Visual Novel Forum!世界上最萌的 Galgame 论坛. Topic, Technique. NO ADs Forever. Free Forever"
|
||||||
/>
|
/>
|
||||||
<title>KUNGalgame</title>
|
<title>KUN Visual Novel | 鲲 Galgame</title>
|
||||||
|
<!--preload-links-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"><!--ssr-outlet--></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/entry-client.ts"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.__INITIAL_STATE__ = '<!--pinia-state-->'
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
17
package.json
17
package.json
|
@ -13,8 +13,11 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "ts-node-esm server-dev.ts",
|
||||||
"build": "vue-tsc && vite build",
|
"prod": "ts-node-esm server-prod.ts",
|
||||||
|
"build:client": "vite build --outDir dist/client --ssrManifest",
|
||||||
|
"build:server": "vite build --outDir dist/server --ssr src/entry-server.ts",
|
||||||
|
"build:ssr": "pnpm build:client && pnpm build:server",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -34,6 +37,7 @@
|
||||||
"@milkdown/utils": "^7.3.1",
|
"@milkdown/utils": "^7.3.1",
|
||||||
"@milkdown/vue": "^7.3.1",
|
"@milkdown/vue": "^7.3.1",
|
||||||
"@prosemirror-adapter/vue": "^0.2.6",
|
"@prosemirror-adapter/vue": "^0.2.6",
|
||||||
|
"@vue/server-renderer": "^3.3.7",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"dompurify": "^3.0.6",
|
"dompurify": "^3.0.6",
|
||||||
|
@ -48,13 +52,22 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/vue": "^4.1.1",
|
"@iconify/vue": "^4.1.1",
|
||||||
|
"@koa/router": "^12.0.1",
|
||||||
|
"@nuxt/devalue": "^2.0.2",
|
||||||
"@types/dompurify": "^3.0.4",
|
"@types/dompurify": "^3.0.4",
|
||||||
"@types/js-cookie": "^3.0.5",
|
"@types/js-cookie": "^3.0.5",
|
||||||
|
"@types/koa": "^2.13.10",
|
||||||
|
"@types/koa-send": "^4.1.5",
|
||||||
"@types/node": "^20.8.10",
|
"@types/node": "^20.8.10",
|
||||||
"@types/nprogress": "^0.2.2",
|
"@types/nprogress": "^0.2.2",
|
||||||
"@vitejs/plugin-vue": "^4.4.0",
|
"@vitejs/plugin-vue": "^4.4.0",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"koa": "^2.14.2",
|
||||||
|
"koa-connect": "^2.1.0",
|
||||||
|
"koa-send": "^5.0.1",
|
||||||
"rollup-plugin-visualizer": "^5.9.2",
|
"rollup-plugin-visualizer": "^5.9.2",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.5.0",
|
"vite": "^4.5.0",
|
||||||
"vue-tsc": "^1.8.22"
|
"vue-tsc": "^1.8.22"
|
||||||
|
|
943
pnpm-lock.yaml
943
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
61
server-dev.ts
Normal file
61
server-dev.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
import Koa from 'koa'
|
||||||
|
import koaConnect from 'koa-connect'
|
||||||
|
|
||||||
|
import { createServer as createViteServer } from 'vite'
|
||||||
|
|
||||||
|
const HOST_NAME = '127.0.0.1'
|
||||||
|
const APP_PORT = 1007
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
const app = new Koa()
|
||||||
|
|
||||||
|
const vite = await createViteServer({
|
||||||
|
server: {
|
||||||
|
middlewareMode: true,
|
||||||
|
watch: {
|
||||||
|
// During tests we edit the files too fast and sometimes chokidar
|
||||||
|
// misses change events, so enforce polling for consistency
|
||||||
|
usePolling: true,
|
||||||
|
interval: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(koaConnect(vite.middlewares))
|
||||||
|
|
||||||
|
app.use(async (ctx) => {
|
||||||
|
try {
|
||||||
|
let template = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, 'index.html'),
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
|
||||||
|
template = await vite.transformIndexHtml(ctx.path, template)
|
||||||
|
|
||||||
|
const { render } = await vite.ssrLoadModule('/src/entry-server.ts')
|
||||||
|
|
||||||
|
const [renderedHtml, state] = await render(ctx, {})
|
||||||
|
|
||||||
|
const html = template
|
||||||
|
.replace('<!--ssr-outlet-->', renderedHtml)
|
||||||
|
.replace('<!--pinia-state-->', state)
|
||||||
|
|
||||||
|
ctx.type = 'text/html'
|
||||||
|
ctx.body = html
|
||||||
|
} catch (e) {
|
||||||
|
vite.ssrFixStacktrace(e as Error)
|
||||||
|
ctx.throw(500, e as Error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(APP_PORT, () => {
|
||||||
|
console.log(`Server is listening in http://${HOST_NAME}:${APP_PORT}`)
|
||||||
|
})
|
||||||
|
})()
|
46
server-prod.ts
Normal file
46
server-prod.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
|
||||||
|
import Koa from 'koa'
|
||||||
|
import sendFile from 'koa-send'
|
||||||
|
|
||||||
|
import path from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
const HOST_NAME = '127.0.0.1'
|
||||||
|
const APP_PORT = 7777
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
const resolve = (pathName: string) => path.resolve(__dirname, pathName)
|
||||||
|
|
||||||
|
const clientRoot = resolve('dist/client')
|
||||||
|
const template = fs.readFileSync(resolve('dist/client/index.html'), 'utf-8')
|
||||||
|
// @ts-ignore
|
||||||
|
import { render } from './dist/server/entry-server.js'
|
||||||
|
// @ts-ignore
|
||||||
|
import manifest from './dist/client/ssr-manifest.json' assert { type: 'json' }
|
||||||
|
;(async () => {
|
||||||
|
const app = new Koa()
|
||||||
|
|
||||||
|
app.use(async (ctx) => {
|
||||||
|
if (ctx.path.startsWith('/kun')) {
|
||||||
|
await sendFile(ctx, ctx.path, { root: clientRoot })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [renderedHtml, state, preloadLinks] = await render(ctx, manifest)
|
||||||
|
|
||||||
|
const html = template
|
||||||
|
.replace('<!--preload-links-->', preloadLinks)
|
||||||
|
.replace('<!--pinia-state-->', state)
|
||||||
|
.replace('<!--ssr-outlet-->', renderedHtml)
|
||||||
|
|
||||||
|
ctx.type = 'text/html'
|
||||||
|
ctx.body = html
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(APP_PORT, () => {
|
||||||
|
console.log(`Server is listening in http://${HOST_NAME}:${APP_PORT}`)
|
||||||
|
})
|
||||||
|
})()
|
|
@ -11,7 +11,6 @@ import Message from '@/components/alert/Message'
|
||||||
|
|
||||||
import { backgroundImages } from './background'
|
import { backgroundImages } from './background'
|
||||||
import { getBackgroundURL } from '@/hooks/useBackgroundPicture'
|
import { getBackgroundURL } from '@/hooks/useBackgroundPicture'
|
||||||
import { restoreBackground } from '@/hooks/useBackgroundPicture'
|
|
||||||
|
|
||||||
const imageArray = ref<string[]>([])
|
const imageArray = ref<string[]>([])
|
||||||
// Use the settings panel store
|
// Use the settings panel store
|
||||||
|
@ -28,6 +27,11 @@ const handleChangeImage = (index: number) => {
|
||||||
showKUNGalgameBackground.value = `bg${index}`
|
showKUNGalgameBackground.value = `bg${index}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the blank background
|
||||||
|
const restoreBackground = () => {
|
||||||
|
showKUNGalgameBackground.value = 'bg0'
|
||||||
|
}
|
||||||
|
|
||||||
// Custom background
|
// Custom background
|
||||||
const url = ref('')
|
const url = ref('')
|
||||||
|
|
||||||
|
|
17
src/entry-client.ts
Normal file
17
src/entry-client.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { createApp } from './main'
|
||||||
|
import i18n from '@/language/i18n'
|
||||||
|
import '@/styles/index.scss'
|
||||||
|
|
||||||
|
const { app, router, pinia } = createApp()
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.use(i18n)
|
||||||
|
|
||||||
|
if (window.__INITIAL_STATE__) {
|
||||||
|
pinia.state.value = JSON.parse(window.__INITIAL_STATE__)
|
||||||
|
}
|
||||||
|
|
||||||
|
router.isReady().then(() => {
|
||||||
|
app.mount('#app')
|
||||||
|
console.log('hydrated')
|
||||||
|
})
|
69
src/entry-server.ts
Normal file
69
src/entry-server.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import { basename } from 'path'
|
||||||
|
import { createApp } from './main'
|
||||||
|
|
||||||
|
import { renderToString } from '@vue/server-renderer'
|
||||||
|
|
||||||
|
import type { ParameterizedContext } from 'koa'
|
||||||
|
|
||||||
|
import '@/styles/index.scss'
|
||||||
|
|
||||||
|
const renderPreloadLink = (file: string) => {
|
||||||
|
if (file.endsWith('.js')) {
|
||||||
|
return `<link rel="modulepreload" crossorigin href="${file}">`
|
||||||
|
} else if (file.endsWith('.css')) {
|
||||||
|
return `<link rel="stylesheet" href="${file}">`
|
||||||
|
} else if (file.endsWith('.png')) {
|
||||||
|
return ` <link rel="preload" href="${file}" as="image" type="image/png">`
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderPreloadLinks = (
|
||||||
|
modules: undefined | string[],
|
||||||
|
manifest: Record<string, string[]>
|
||||||
|
) => {
|
||||||
|
let links = ''
|
||||||
|
const seen = new Set()
|
||||||
|
if (modules === undefined) throw new Error()
|
||||||
|
|
||||||
|
modules.forEach((id) => {
|
||||||
|
const files = manifest[id]
|
||||||
|
if (files) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (!seen.has(file)) {
|
||||||
|
seen.add(file)
|
||||||
|
const filename = basename(file)
|
||||||
|
if (manifest[filename]) {
|
||||||
|
for (const depFile of manifest[filename]) {
|
||||||
|
links += renderPreloadLink(depFile)
|
||||||
|
seen.add(depFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
links += renderPreloadLink(file)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
|
export const render = async (
|
||||||
|
ctx: ParameterizedContext,
|
||||||
|
manifest: Record<string, string[]>
|
||||||
|
): Promise<[string, string]> => {
|
||||||
|
const { app, router } = createApp()
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
await router.push(ctx.path)
|
||||||
|
await router.isReady()
|
||||||
|
|
||||||
|
const renderCtx: { modules?: string[] } = {}
|
||||||
|
|
||||||
|
const renderedHtml = await renderToString(app, renderCtx)
|
||||||
|
|
||||||
|
const preloadLinks = renderPreloadLinks(renderCtx.modules, manifest)
|
||||||
|
|
||||||
|
return [renderedHtml, preloadLinks]
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
// Global message component (top)
|
// Global message component (top)
|
||||||
import Message from '@/components/alert/Message'
|
import Message from '@/components/alert/Message'
|
||||||
import { generateTokenByRefreshTokenApi } from '@/api'
|
import { generateTokenByRefreshTokenApi } from '@/api'
|
||||||
// Use the user store
|
// Use the user store
|
||||||
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
||||||
// Import the router
|
// Import the router
|
||||||
import router from '@/router'
|
import { createKUNGalgameRouter } from '@/router'
|
||||||
// Import known error handling functions
|
// Import known error handling functions
|
||||||
import { kungalgameErrorHandler } from './errorHandler'
|
import { kungalgameErrorHandler } from './errorHandler'
|
||||||
|
|
||||||
|
@ -13,6 +14,8 @@ interface ErrorResponseData {
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acts as an interceptor, first recognizing common errors based on predictable status codes.
|
* Acts as an interceptor, first recognizing common errors based on predictable status codes.
|
||||||
* Then identifies errors based on custom backend status codes
|
* Then identifies errors based on custom backend status codes
|
||||||
|
@ -28,7 +31,9 @@ export async function onRequestError(response: Response) {
|
||||||
if (accessTokenResponse.code === 200 && accessTokenResponse.data.token) {
|
if (accessTokenResponse.code === 200 && accessTokenResponse.data.token) {
|
||||||
useKUNGalgameUserStore().setToken(accessTokenResponse.data.token)
|
useKUNGalgameUserStore().setToken(accessTokenResponse.data.token)
|
||||||
// Set the page to reload with the new token applied
|
// Set the page to reload with the new token applied
|
||||||
location.reload()
|
if (typeof window !== 'undefined') {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, prompt the user to log in again
|
// Otherwise, prompt the user to log in again
|
||||||
Message(
|
Message(
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
// Import the settings panel store
|
// Import the settings panel store
|
||||||
import { useKUNGalgameSettingsStore } from '@/store/modules/settings'
|
|
||||||
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
import { useKUNGalgameUserStore } from '@/store/modules/kungalgamer'
|
||||||
import { saveImage, getImage } from './useLocalforage'
|
import { saveImage, getImage } from './useLocalforage'
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
|
|
||||||
// Use the settings panel store
|
|
||||||
const { showKUNGalgameBackground, showKUNGalgameCustomBackground } =
|
|
||||||
storeToRefs(useKUNGalgameSettingsStore())
|
|
||||||
|
|
||||||
// Fetch background image data from the backend
|
// Fetch background image data from the backend
|
||||||
const fetchGetBackground = async (imageName: string): Promise<Blob> => {
|
const fetchGetBackground = async (imageName: string): Promise<Blob> => {
|
||||||
|
@ -39,25 +33,4 @@ const getBackgroundURL = async (imageName: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The image names here are defined by the backend as bg1.webp (large image) and bg1-m.webp (preview image)
|
export { getBackgroundURL }
|
||||||
const getCurrentBackground = async () => {
|
|
||||||
if (
|
|
||||||
showKUNGalgameBackground.value === 'bg0' ||
|
|
||||||
showKUNGalgameBackground.value === 'none'
|
|
||||||
) {
|
|
||||||
return 'none'
|
|
||||||
}
|
|
||||||
if (showKUNGalgameBackground.value === 'bg1007') {
|
|
||||||
return `${showKUNGalgameCustomBackground.value}`
|
|
||||||
}
|
|
||||||
// Get the image's blob URL
|
|
||||||
const url = await getBackgroundURL(showKUNGalgameBackground.value)
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore the blank background
|
|
||||||
const restoreBackground = () => {
|
|
||||||
showKUNGalgameBackground.value = 'bg0'
|
|
||||||
}
|
|
||||||
|
|
||||||
export { getBackgroundURL, getCurrentBackground, restoreBackground }
|
|
||||||
|
|
|
@ -8,10 +8,7 @@ import en from './en'
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
locale: KUNGalgameLanguage,
|
locale: KUNGalgameLanguage,
|
||||||
// 支持 Vue3 composition API
|
|
||||||
legacy: false,
|
legacy: false,
|
||||||
// 全局注册 t 方法
|
|
||||||
globalInjection: true,
|
|
||||||
messages: {
|
messages: {
|
||||||
zh,
|
zh,
|
||||||
en,
|
en,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { onMounted, watch, ref } from 'vue'
|
import { onMounted, watch, ref } from 'vue'
|
||||||
// Import animations
|
// Import animations
|
||||||
import 'animate.css'
|
import 'animate.css'
|
||||||
import { getCurrentBackground } from '@/hooks/useBackgroundPicture'
|
import { getBackgroundURL } from '@/hooks/useBackgroundPicture'
|
||||||
import KUNGalgameTopBar from '@/components/top-bar/KUNGalgameTopBar.vue'
|
import KUNGalgameTopBar from '@/components/top-bar/KUNGalgameTopBar.vue'
|
||||||
|
|
||||||
import { useKUNGalgameSettingsStore } from '@/store/modules/settings'
|
import { useKUNGalgameSettingsStore } from '@/store/modules/settings'
|
||||||
|
@ -10,6 +10,20 @@ import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
const { showKUNGalgameBackground, showKUNGalgameCustomBackground } =
|
const { showKUNGalgameBackground, showKUNGalgameCustomBackground } =
|
||||||
storeToRefs(useKUNGalgameSettingsStore())
|
storeToRefs(useKUNGalgameSettingsStore())
|
||||||
|
const getCurrentBackground = async () => {
|
||||||
|
if (
|
||||||
|
showKUNGalgameBackground.value === 'bg0' ||
|
||||||
|
showKUNGalgameBackground.value === 'none'
|
||||||
|
) {
|
||||||
|
return 'none'
|
||||||
|
}
|
||||||
|
if (showKUNGalgameBackground.value === 'bg1007') {
|
||||||
|
return `${showKUNGalgameCustomBackground.value}`
|
||||||
|
}
|
||||||
|
// Get the image's blob URL
|
||||||
|
const url = await getBackgroundURL(showKUNGalgameBackground.value)
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
const imageURL = ref('')
|
const imageURL = ref('')
|
||||||
|
|
||||||
|
|
29
src/main.ts
29
src/main.ts
|
@ -1,22 +1,25 @@
|
||||||
// Vue core
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import { createSSRApp } from 'vue'
|
||||||
// Import vue i18n
|
|
||||||
|
import { createKUNGalgameRouter } from './router'
|
||||||
|
import { setupRouterGuard } from '@/router/guard'
|
||||||
|
|
||||||
import i18n from '@/language/i18n'
|
import i18n from '@/language/i18n'
|
||||||
|
|
||||||
import { setupRouterGuard } from '@/router/guard'
|
|
||||||
import { setupPinia } from '@/store/index'
|
import { setupPinia } from '@/store/index'
|
||||||
|
|
||||||
// Import css styles, color, theme, etc.
|
|
||||||
import '@/styles/index.scss'
|
import '@/styles/index.scss'
|
||||||
|
|
||||||
// Get vue App instance
|
export function createApp() {
|
||||||
const app = createApp(App)
|
const app = createSSRApp(App)
|
||||||
|
const pinia = setupPinia(app)
|
||||||
|
app.use(pinia)
|
||||||
|
|
||||||
// Setup router guard
|
const router = createKUNGalgameRouter()
|
||||||
setupRouterGuard(router)
|
setupRouterGuard(router)
|
||||||
// Setup pinia
|
app.use(router)
|
||||||
setupPinia(app)
|
|
||||||
|
|
||||||
app.use(router).use(i18n).mount('#app')
|
app.use(i18n)
|
||||||
|
|
||||||
|
return { app, router, pinia }
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
import { type RouteRecordRaw, createWebHistory, createRouter } from 'vue-router'
|
import type { RouteRecordRaw, Router } from 'vue-router'
|
||||||
|
import { createWebHistory, createRouter, createMemoryHistory } from 'vue-router'
|
||||||
import { constantRoutes } from './router'
|
import { constantRoutes } from './router'
|
||||||
import { asyncRoutes } from './router'
|
import { asyncRoutes } from './router'
|
||||||
|
|
||||||
// Create a Vue Router instance
|
export const createKUNGalgameRouter = (): Router =>
|
||||||
const router = createRouter({
|
createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: import.meta.env.SSR
|
||||||
routes: [...constantRoutes, ...asyncRoutes] as RouteRecordRaw[],
|
? createMemoryHistory(import.meta.env.BASE_URL)
|
||||||
// Scroll to the top of the page with a smooth animation on each page navigation
|
: createWebHistory(import.meta.env.BASE_URL),
|
||||||
scrollBehavior(to, from, savedPosition) {
|
|
||||||
if (savedPosition) {
|
|
||||||
return savedPosition
|
|
||||||
} else {
|
|
||||||
return { top: 0, behavior: 'smooth' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
routes: [...constantRoutes, ...asyncRoutes] as RouteRecordRaw[],
|
||||||
|
|
||||||
|
// Scroll to the top of the page with a smooth animation on each page navigation
|
||||||
|
scrollBehavior(to, from, savedPosition) {
|
||||||
|
if (savedPosition) {
|
||||||
|
return savedPosition
|
||||||
|
} else {
|
||||||
|
return { top: 0, behavior: 'smooth' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
|
@ -33,16 +33,20 @@ import { useKUNGalgameSettingsStore } from '@/store/modules/settings'
|
||||||
// Import store for the topic detail page
|
// Import store for the topic detail page
|
||||||
import { useKUNGalgameTopicStore } from './modules/topic'
|
import { useKUNGalgameTopicStore } from './modules/topic'
|
||||||
|
|
||||||
const store = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
// Function to set up Pinia, to be called in main.ts
|
// Function to set up Pinia, to be called in main.ts
|
||||||
export function setupPinia(app: App<Element>) {
|
export const setupPinia = (app: App<Element>) => {
|
||||||
store.use(piniaPluginPersistedstate)
|
if (!import.meta.env.SSR) {
|
||||||
app.use(store)
|
pinia.use(piniaPluginPersistedstate)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(pinia)
|
||||||
|
return pinia
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all stores, used for logging out
|
// Reset all stores, used for logging out
|
||||||
export function kungalgameStoreReset() {
|
export const kungalgameStoreReset = () => {
|
||||||
const balanceStore = useKUNGalgameBalanceStore()
|
const balanceStore = useKUNGalgameBalanceStore()
|
||||||
const editStore = useKUNGalgameEditStore()
|
const editStore = useKUNGalgameEditStore()
|
||||||
const homeStore = useKUNGalgameHomeStore()
|
const homeStore = useKUNGalgameHomeStore()
|
||||||
|
@ -63,5 +67,3 @@ export function kungalgameStoreReset() {
|
||||||
settingsStore.$reset()
|
settingsStore.$reset()
|
||||||
topicStore.$reset()
|
topicStore.$reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
export { store }
|
|
||||||
|
|
3
src/types/shims-global.d.ts
vendored
Normal file
3
src/types/shims-global.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
interface Window {
|
||||||
|
__INITIAL_STATE__: string;
|
||||||
|
}
|
|
@ -3,11 +3,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Read language configuration from local storage
|
// Read language configuration from local storage
|
||||||
const localStorageString = localStorage.getItem('KUNGalgameSettings')
|
let localStorageString = ''
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const storage = localStorage.getItem('KUNGalgameSettings')
|
||||||
|
localStorageString = storage ? storage : ''
|
||||||
|
}
|
||||||
|
|
||||||
// To ensure compatibility with various browsers, some browsers have 'zh-CN' as navigator.language, which may cause errors.
|
// To ensure compatibility with various browsers, some browsers have 'zh-CN' as navigator.language, which may cause errors.
|
||||||
const getInitLanguage = () => {
|
const getInitLanguage = () => {
|
||||||
const userLanguage = navigator.language
|
let userLanguage = ''
|
||||||
|
|
||||||
|
if (typeof navigator !== 'undefined') {
|
||||||
|
userLanguage = navigator.language
|
||||||
|
}
|
||||||
|
|
||||||
if (userLanguage.includes('en')) {
|
if (userLanguage.includes('en')) {
|
||||||
return 'en'
|
return 'en'
|
||||||
|
@ -24,6 +32,11 @@ export const KUNGalgameLanguage = localStorageString
|
||||||
: getInitLanguage()
|
: getInitLanguage()
|
||||||
|
|
||||||
// Read local day-night mode, this function will return 'true' if it's in dark mode.
|
// Read local day-night mode, this function will return 'true' if it's in dark mode.
|
||||||
export const mode = window.matchMedia('(prefers-color-scheme: dark)').matches
|
export const mode = () => {
|
||||||
? 'dark'
|
if (typeof window !== 'undefined') {
|
||||||
: ''
|
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
? 'dark'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
"src/**/*.d.ts",
|
"src/**/*.d.ts",
|
||||||
"src/**/**/*.d.ts",
|
"src/**/**/*.d.ts",
|
||||||
"src/**/*.tsx",
|
"src/**/*.tsx",
|
||||||
"src/**/*.vue"
|
"src/**/*.vue",
|
||||||
|
"server-dev.ts",
|
||||||
|
"server-prod.ts"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules", "dist"],
|
"exclude": ["node_modules", "dist"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
|
Loading…
Reference in a new issue