Set up Nuxt config and routing shell
Agent-Logs-Url: https://github.com/hykilpikonna/corner/sessions/5ad4b623-9a6b-480e-80fa-d83eee774f01 Co-authored-by: hykilpikonna <22280294+hykilpikonna@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1f385afee0
commit
12c0dc46bf
+3
-1
@@ -1,7 +1,9 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
/dist
|
/dist
|
||||||
|
/.nuxt
|
||||||
|
/.output
|
||||||
|
/.nitro
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Project setup
|
# Project setup
|
||||||
yarn install
|
npm install
|
||||||
|
|
||||||
# Compiles and hot-reloads for development
|
# Compiles and hot-reloads for development
|
||||||
yarn serve
|
npm run dev
|
||||||
|
|
||||||
|
# Generate static site
|
||||||
|
npm run generate
|
||||||
```
|
```
|
||||||
|
|||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
|
||||||
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
|
|
||||||
<title>Home</title>
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" integrity="sha512-uto9mlQzrs59VwILcLiRYeLKPPbS/bT71da/OEBYEwcdNUk8jYIy+D176RYoop1Da+f9mvkYrmj5MCLZWEtQuA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Load this last */
|
|
||||||
#nav * {
|
|
||||||
transition: all .25s ease
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export default defineNuxtConfig({
|
||||||
|
srcDir: 'src/',
|
||||||
|
ssr: true,
|
||||||
|
modules: ['@unocss/nuxt'],
|
||||||
|
css: ['@fortawesome/fontawesome-free/css/all.min.css'],
|
||||||
|
app: {
|
||||||
|
head: {
|
||||||
|
script: [
|
||||||
|
{
|
||||||
|
src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js',
|
||||||
|
integrity: 'sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==',
|
||||||
|
crossorigin: 'anonymous',
|
||||||
|
referrerpolicy: 'no-referrer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
|
||||||
|
integrity: 'sha512-uto9mlQzrs59VwILcLiRYeLKPPbS/bT71da/OEBYEwcdNUk8jYIy+D176RYoop1Da+f9mvkYrmj5MCLZWEtQuA==',
|
||||||
|
crossorigin: 'anonymous',
|
||||||
|
referrerpolicy: 'no-referrer'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nitro: {
|
||||||
|
prerender: {
|
||||||
|
crawlLinks: true,
|
||||||
|
routes: ['/', '/about', '/life', '/blog', '/photo', '/others', '/kitchen-menu', '/friends', '/projects']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
+34
-36
@@ -1,38 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "home-page",
|
"name": "home-page",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "nuxt dev",
|
||||||
"serve": "vite",
|
"build": "nuxt build",
|
||||||
"build": "vue-tsc --noEmit && vite build"
|
"generate": "nuxt generate",
|
||||||
},
|
"preview": "nuxt preview",
|
||||||
"dependencies": {
|
"typecheck": "nuxt typecheck"
|
||||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
},
|
||||||
"animsition": "^4.0.2",
|
"dependencies": {
|
||||||
"core-js": "^3.37.1",
|
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||||
"emoji-regex": "^10.3.0",
|
"animsition": "^4.0.2",
|
||||||
"linkify-urls": "^4.1.0",
|
"emoji-regex": "^10.3.0",
|
||||||
"lxgw-wenkai-webfont": "^1.7.0",
|
"linkify-urls": "^4.1.0",
|
||||||
"marked": "^4.3.0",
|
"lxgw-wenkai-webfont": "^1.7.0",
|
||||||
"moment": "^2.30.1",
|
"marked": "^4.3.0",
|
||||||
"tg-blog": "^1.1.7",
|
"moment": "^2.30.1",
|
||||||
"vue": "^3.4.35",
|
"tg-blog": "^1.1.7",
|
||||||
"vue-i18n": "^11.1.12",
|
"vue-i18n": "^11.1.12",
|
||||||
"vue-router": "^4.4.0"
|
"nuxt": "^4.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jquery": "^3.5.30",
|
"@types/jquery": "^3.5.30",
|
||||||
"@types/jqueryui": "^1.12.23",
|
"@types/jqueryui": "^1.12.23",
|
||||||
"@types/marked": "^4.3.2",
|
"@types/marked": "^4.3.2",
|
||||||
"@types/node": "^24.9.1",
|
"@types/node": "^24.9.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"eslint": "^9.8.0",
|
||||||
"eslint": "^9.8.0",
|
"sass": "^1.77.8",
|
||||||
"sass": "^1.77.8",
|
"tslib": "^2.6.3",
|
||||||
"tslib": "^2.6.3",
|
"typescript": "^5.5.4",
|
||||||
"typescript": "^5.5.4",
|
"unocss": "^66.5.4",
|
||||||
"unocss": "^66.5.4",
|
"@unocss/nuxt": "^66.6.8"
|
||||||
"vite": "^7.1.12",
|
}
|
||||||
"vue-tsc": "3.1.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-266
@@ -1,266 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="nav" class="fbox-v"
|
|
||||||
:class="(currentRoute) + ' ' + (menuOpen ? 'open' : '')"
|
|
||||||
v-if="currentRoute !== 'colorpicker'">
|
|
||||||
<div id="menu" @click="showMenu"><i class="fas fa-bars"></i></div>
|
|
||||||
|
|
||||||
<div id="items" class="fbox-v">
|
|
||||||
<router-link class="router-link" :ref="setNavRef('others')" to="/others">{{ $t('nav.others') }}</router-link>
|
|
||||||
<div class="dot">·</div>
|
|
||||||
<router-link class="router-link" :ref="setNavRef('photo')" to="/photo">{{ $t('nav.photo') }}</router-link>
|
|
||||||
<div class="dot">·</div>
|
|
||||||
<router-link class="router-link" :ref="setNavRef('blog')" to="/blog">{{ $t('nav.blog') }}</router-link>
|
|
||||||
<div class="dot">·</div>
|
|
||||||
<router-link class="router-link" :ref="setNavRef('life')" to="/life">{{ $t('nav.life') }}</router-link>
|
|
||||||
<div class="dot">·</div>
|
|
||||||
<router-link class="router-link" :ref="setNavRef('about')" to="/about">{{ $t('nav.about') }}</router-link>
|
|
||||||
<div class="dot">·</div>
|
|
||||||
<router-link class="router-link" :ref="setNavRef('home')" to="/">
|
|
||||||
<svg focusable="false" data-prefix="fal" data-icon="house-night" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-house-night fa-w-20"><path fill="currentColor" d="M112,224a111.5,111.5,0,0,0,87-41.45,20.51,20.51,0,0,0-19.75-33.08A59.2,59.2,0,0,1,138.84,39.85a20.3,20.3,0,0,0,10.07-21.27,20.26,20.26,0,0,0-16.47-16.7A136,136,0,0,0,112,0a112,112,0,0,0,0,224ZM97.78,33.27a91.21,91.21,0,0,0,54.47,147.9A80,80,0,1,1,97.78,33.27Zm97.15,35.51,39.72,16.56,16.56,39.72a5.33,5.33,0,0,0,9.55,0l16.56-39.72L317,68.78a5.33,5.33,0,0,0,0-9.54L277.32,42.68,260.76,3a5.33,5.33,0,0,0-9.55,0L234.65,42.68,194.93,59.24a5.34,5.34,0,0,0,0,9.54ZM157,379.24l-39.72-16.57L100.76,323a5.34,5.34,0,0,0-9.55,0L74.65,362.67,34.93,379.24a5.34,5.34,0,0,0,0,9.54l39.72,16.56,16.56,39.72a5.33,5.33,0,0,0,9.55,0l16.56-39.72L157,388.78a5.33,5.33,0,0,0,0-9.54Zm179-101.9v85.33A21.39,21.39,0,0,0,357.36,384h85.31A21.39,21.39,0,0,0,464,362.67V277.34A21.4,21.4,0,0,0,442.67,256H357.36A21.4,21.4,0,0,0,336,277.34ZM368,288H432v64H368Zm266.49,8L576,244.75V144a16,16,0,0,0-32,0v72.75L410.53,100a16,16,0,0,0-21.07,0l-224,196a16,16,0,0,0,21.07,24.09L224,287.28V464a48.05,48.05,0,0,0,48,48H528a48.06,48.06,0,0,0,48-48V287.28l37.46,32.78A16,16,0,0,0,634.53,296ZM544,464a16,16,0,0,1-16,16H272a16,16,0,0,1-16-16V264a15.94,15.94,0,0,0-.81-4L400,133.27l144,126Z" class=""></path></svg></router-link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="nav-bookmark" ref="bookmark" :style="bookmarkCss"></div>
|
|
||||||
<div id="nav-background"></div>
|
|
||||||
|
|
||||||
<img id="meru" src="/meru_256px.png" alt="">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<router-view/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {nextTick, onMounted, onUnmounted, ref, ComponentPublicInstance} from 'vue';
|
|
||||||
import router from "@/scripts/router";
|
|
||||||
import {RouteLocationNormalized, RouteLocationRaw, useRoute} from "vue-router";
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const currentRoute = ref('')
|
|
||||||
const bookmarkCss = ref('')
|
|
||||||
const lastTop = ref(0)
|
|
||||||
const menuOpen = ref(false)
|
|
||||||
const bookmarkUpdateIntervalId = ref<number | null>(null)
|
|
||||||
const removeAfterEach = ref<(() => void) | null>(null)
|
|
||||||
|
|
||||||
const navRefs = ref<Record<string, Element | ComponentPublicInstance | null>>({})
|
|
||||||
|
|
||||||
const showMenu = (): void => {
|
|
||||||
menuOpen.value = !menuOpen.value
|
|
||||||
|
|
||||||
if (menuOpen.value) setTimeout(() => menuOpen.value = false, 2000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const setNavRef = (name: string) => (el: Element | ComponentPublicInstance | null) => {
|
|
||||||
navRefs.value[name] = el
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveNavElement = (target: Element | ComponentPublicInstance | null | undefined): Element | null => {
|
|
||||||
if (!target) return null
|
|
||||||
if (target instanceof Element) return target
|
|
||||||
return (target.$el as Element | undefined) ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateBookmarkCss = (): void => {
|
|
||||||
const currentLink = resolveNavElement(navRefs.value[currentRoute.value])
|
|
||||||
if (!currentLink) return
|
|
||||||
|
|
||||||
const box = currentLink.getBoundingClientRect()
|
|
||||||
if (box.top === lastTop.value) return
|
|
||||||
lastTop.value = box.top
|
|
||||||
|
|
||||||
const h = box.bottom - box.top
|
|
||||||
const width = Math.round(h / 2) + 8
|
|
||||||
|
|
||||||
bookmarkCss.value = `top: ${box.top - 8}px;` +
|
|
||||||
`border-width: ${width}px 20px ${width}px 50px;`
|
|
||||||
}
|
|
||||||
|
|
||||||
const getRouteBookmark = (to: RouteLocationNormalized): string => {
|
|
||||||
return ((to.meta?.navBookmark ?? to.name) as string).toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateBookmark = (to: RouteLocationNormalized): void => {
|
|
||||||
nextTick(() => {
|
|
||||||
if (to.name == 'Blog' && Object.keys(to.query).length != 0) return
|
|
||||||
document.title = to.meta.title ? `Aza - ${to.meta.title}` : 'Aza - Home';
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('AfterEach called', to)
|
|
||||||
currentRoute.value = getRouteBookmark(to)
|
|
||||||
|
|
||||||
calculateBookmarkCss()
|
|
||||||
menuOpen.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('Mounted called', route)
|
|
||||||
removeAfterEach.value = router.afterEach(updateBookmark)
|
|
||||||
if (route.name) {
|
|
||||||
currentRoute.value = getRouteBookmark(route as unknown as RouteLocationNormalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', calculateBookmarkCss, true)
|
|
||||||
bookmarkUpdateIntervalId.value = window.setInterval(calculateBookmarkCss, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
removeAfterEach.value?.()
|
|
||||||
window.removeEventListener('resize', calculateBookmarkCss, true)
|
|
||||||
if (bookmarkUpdateIntervalId.value !== null) {
|
|
||||||
window.clearInterval(bookmarkUpdateIntervalId.value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="sass">
|
|
||||||
@use "css/global"
|
|
||||||
@use "css/animations"
|
|
||||||
|
|
||||||
#nav
|
|
||||||
position: fixed
|
|
||||||
left: 0
|
|
||||||
height: 100%
|
|
||||||
font-size: 1.4em
|
|
||||||
align-items: flex-start
|
|
||||||
|
|
||||||
z-index: 100
|
|
||||||
pointer-events: none
|
|
||||||
|
|
||||||
#menu
|
|
||||||
padding: 5px 10px
|
|
||||||
margin: 15px 10px
|
|
||||||
background-color: rgba(255, 255, 255, 0.49)
|
|
||||||
border-radius: 10px
|
|
||||||
text-shadow: -1px -1px 1px rgba(255,255,255,.1), 1px 1px 1px rgba(0,0,0,.5)
|
|
||||||
filter: drop-shadow(0 0 30px rgb(255, 255, 255))
|
|
||||||
|
|
||||||
z-index: 100
|
|
||||||
pointer-events: auto
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
|
|
||||||
#nav-bookmark
|
|
||||||
position: absolute
|
|
||||||
left: 0
|
|
||||||
width: 20px
|
|
||||||
height: 0
|
|
||||||
z-index: 5
|
|
||||||
--bookmark-color: rgb(255, 225, 230)
|
|
||||||
border-color: var(--bookmark-color) transparent var(--bookmark-color) var(--bookmark-color)
|
|
||||||
border-style: solid
|
|
||||||
border-width: 20px
|
|
||||||
|
|
||||||
#items
|
|
||||||
justify-content: flex-end
|
|
||||||
z-index: 10
|
|
||||||
|
|
||||||
.router-link
|
|
||||||
color: rgba(128, 112, 92, 0.71)
|
|
||||||
position: relative
|
|
||||||
|
|
||||||
z-index: 100
|
|
||||||
pointer-events: auto
|
|
||||||
|
|
||||||
.dot
|
|
||||||
content: '·'
|
|
||||||
margin: 20px 0
|
|
||||||
user-select: none
|
|
||||||
|
|
||||||
.router-link, .dot
|
|
||||||
text-decoration: none
|
|
||||||
writing-mode: vertical-rl
|
|
||||||
text-orientation: sideways
|
|
||||||
transform: scale(-1)
|
|
||||||
padding-right: 20px
|
|
||||||
|
|
||||||
#nav-background
|
|
||||||
position: absolute
|
|
||||||
height: 100%
|
|
||||||
width: 100px
|
|
||||||
left: 0
|
|
||||||
$nav-bg-color: #f9f2e0
|
|
||||||
//$nav-bg-color: rgb(255, 230, 214)
|
|
||||||
border-left: 20px solid $nav-bg-color
|
|
||||||
background: linear-gradient(to right, $nav-bg-color, transparent)
|
|
||||||
|
|
||||||
z-index: 4
|
|
||||||
pointer-events: none
|
|
||||||
|
|
||||||
#meru
|
|
||||||
height: 160px
|
|
||||||
|
|
||||||
z-index: 100
|
|
||||||
pointer-events: auto
|
|
||||||
|
|
||||||
svg
|
|
||||||
display: inline-block
|
|
||||||
height: 1em
|
|
||||||
transform: rotate(180deg)
|
|
||||||
*
|
|
||||||
box-sizing: inherit
|
|
||||||
|
|
||||||
// Tablet layout
|
|
||||||
@media screen and (max-width: 800px)
|
|
||||||
#nav #menu
|
|
||||||
opacity: 1
|
|
||||||
|
|
||||||
#nav.home
|
|
||||||
#menu, #nav-background
|
|
||||||
opacity: 0
|
|
||||||
|
|
||||||
#nav:not(.home).open
|
|
||||||
#items, #nav-bookmark, #nav-background, #meru
|
|
||||||
opacity: 1
|
|
||||||
animation: fade-in-left .5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
|
||||||
|
|
||||||
#nav:not(.home)
|
|
||||||
#items, #nav-bookmark, #nav-background, #meru
|
|
||||||
opacity: 0
|
|
||||||
animation: fade-out-left 1s 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
|
||||||
|
|
||||||
// Phone layout
|
|
||||||
@media screen and (max-width: 500px), (max-height: 660px)
|
|
||||||
#app
|
|
||||||
font-size: 14px
|
|
||||||
|
|
||||||
#nav
|
|
||||||
#nav-bookmark
|
|
||||||
width: 10px
|
|
||||||
|
|
||||||
#meru
|
|
||||||
height: 120px
|
|
||||||
|
|
||||||
// Smaller phone layout
|
|
||||||
@media screen and (max-width: 370px)
|
|
||||||
#nav #menu
|
|
||||||
opacity: unset
|
|
||||||
|
|
||||||
#nav.home
|
|
||||||
#menu, #nav-background
|
|
||||||
opacity: unset
|
|
||||||
|
|
||||||
#nav.open
|
|
||||||
#items, #nav-bookmark, #nav-background, #meru
|
|
||||||
opacity: 1
|
|
||||||
animation: fade-in-left .5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
|
||||||
|
|
||||||
#nav
|
|
||||||
#items, #nav-bookmark, #nav-background, #meru
|
|
||||||
opacity: 0
|
|
||||||
animation: fade-out-left 1s 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
|
||||||
|
|
||||||
// Phone Height
|
|
||||||
@media screen and (max-height: 600px)
|
|
||||||
#nav
|
|
||||||
.dot
|
|
||||||
margin: 10px 0
|
|
||||||
|
|
||||||
// Phone Height
|
|
||||||
@media screen and (max-height: 500px)
|
|
||||||
#nav
|
|
||||||
.router-link
|
|
||||||
margin-bottom: 10px
|
|
||||||
|
|
||||||
.dot
|
|
||||||
display: none
|
|
||||||
</style>
|
|
||||||
+273
@@ -0,0 +1,273 @@
|
|||||||
|
<template>
|
||||||
|
<div id="nav" class="fbox-v" :class="`${currentRoute} ${menuOpen ? 'open' : ''}`" v-if="currentRoute !== 'colorpicker'">
|
||||||
|
<div id="menu" @click="showMenu"><i class="fas fa-bars"></i></div>
|
||||||
|
|
||||||
|
<div id="items" class="fbox-v">
|
||||||
|
<NuxtLink class="router-link" :ref="setNavRef('others')" to="/others">{{ $t('nav.others') }}</NuxtLink>
|
||||||
|
<div class="dot">·</div>
|
||||||
|
<NuxtLink class="router-link" :ref="setNavRef('photo')" to="/photo">{{ $t('nav.photo') }}</NuxtLink>
|
||||||
|
<div class="dot">·</div>
|
||||||
|
<NuxtLink class="router-link" :ref="setNavRef('blog')" to="/blog">{{ $t('nav.blog') }}</NuxtLink>
|
||||||
|
<div class="dot">·</div>
|
||||||
|
<NuxtLink class="router-link" :ref="setNavRef('life')" to="/life">{{ $t('nav.life') }}</NuxtLink>
|
||||||
|
<div class="dot">·</div>
|
||||||
|
<NuxtLink class="router-link" :ref="setNavRef('about')" to="/about">{{ $t('nav.about') }}</NuxtLink>
|
||||||
|
<div class="dot">·</div>
|
||||||
|
<NuxtLink class="router-link" :ref="setNavRef('home')" to="/">
|
||||||
|
<svg focusable="false" data-prefix="fal" data-icon="house-night" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-house-night fa-w-20"><path fill="currentColor" d="M112,224a111.5,111.5,0,0,0,87-41.45,20.51,20.51,0,0,0-19.75-33.08A59.2,59.2,0,0,1,138.84,39.85a20.3,20.3,0,0,0,10.07-21.27,20.26,20.26,0,0,0-16.47-16.7A136,136,0,0,0,112,0a112,112,0,0,0,0,224ZM97.78,33.27a91.21,91.21,0,0,0,54.47,147.9A80,80,0,1,1,97.78,33.27Zm97.15,35.51,39.72,16.56,16.56,39.72a5.33,5.33,0,0,0,9.55,0l16.56-39.72L317,68.78a5.33,5.33,0,0,0,0-9.54L277.32,42.68,260.76,3a5.33,5.33,0,0,0-9.55,0L234.65,42.68,194.93,59.24a5.34,5.34,0,0,0,0,9.54ZM157,379.24l-39.72-16.57L100.76,323a5.34,5.34,0,0,0-9.55,0L74.65,362.67,34.93,379.24a5.34,5.34,0,0,0,0,9.54l39.72,16.56,16.56,39.72a5.33,5.33,0,0,0,9.55,0l16.56-39.72L157,388.78a5.33,5.33,0,0,0,0-9.54Zm179-101.9v85.33A21.39,21.39,0,0,0,357.36,384h85.31A21.39,21.39,0,0,0,464,362.67V277.34A21.4,21.4,0,0,0,442.67,256H357.36A21.4,21.4,0,0,0,336,277.34ZM368,288H432v64H368Zm266.49,8L576,244.75V144a16,16,0,0,0-32,0v72.75L410.53,100a16,16,0,0,0-21.07,0l-224,196a16,16,0,0,0,21.07,24.09L224,287.28V464a48.05,48.05,0,0,0,48,48H528a48.06,48.06,0,0,0,48-48V287.28l37.46,32.78A16,16,0,0,0,634.53,296ZM544,464a16,16,0,0,1-16,16H272a16,16,0,0,1-16-16V264a15.94,15.94,0,0,0-.81-4L400,133.27l144,126Z" class=""></path></svg>
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="nav-bookmark" ref="bookmark" :style="bookmarkCss"></div>
|
||||||
|
<div id="nav-background"></div>
|
||||||
|
|
||||||
|
<img id="meru" src="/meru_256px.png" alt="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NuxtPage/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {nextTick, onMounted, onUnmounted, ref, ComponentPublicInstance, watch} from 'vue';
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const currentRoute = ref('')
|
||||||
|
const bookmarkCss = ref('')
|
||||||
|
const lastTop = ref(0)
|
||||||
|
const menuOpen = ref(false)
|
||||||
|
const bookmarkUpdateIntervalId = ref<number | null>(null)
|
||||||
|
|
||||||
|
const navRefs = ref<Record<string, Element | ComponentPublicInstance | null>>({})
|
||||||
|
|
||||||
|
const showMenu = (): void => {
|
||||||
|
menuOpen.value = !menuOpen.value
|
||||||
|
|
||||||
|
if (menuOpen.value) setTimeout(() => menuOpen.value = false, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNavRef = (name: string) => (el: Element | ComponentPublicInstance | null) => {
|
||||||
|
navRefs.value[name] = el
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveNavElement = (target: Element | ComponentPublicInstance | null | undefined): Element | null => {
|
||||||
|
if (!target) return null
|
||||||
|
if (target instanceof Element) return target
|
||||||
|
return (target.$el as Element | undefined) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateBookmarkCss = (): void => {
|
||||||
|
const currentLink = resolveNavElement(navRefs.value[currentRoute.value])
|
||||||
|
if (!currentLink) return
|
||||||
|
|
||||||
|
const box = currentLink.getBoundingClientRect()
|
||||||
|
if (box.top === lastTop.value) return
|
||||||
|
lastTop.value = box.top
|
||||||
|
|
||||||
|
const h = box.bottom - box.top
|
||||||
|
const width = Math.round(h / 2) + 8
|
||||||
|
|
||||||
|
bookmarkCss.value = `top: ${box.top - 8}px;` +
|
||||||
|
`border-width: ${width}px 20px ${width}px 50px;`
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRouteBookmark = (path: string): string => {
|
||||||
|
if (path.startsWith('/photo')) return 'photo'
|
||||||
|
if (path === '/kitchen-menu' || path === '/friends' || path === '/projects' || path === '/others') return 'others'
|
||||||
|
if (path === '/about') return 'about'
|
||||||
|
if (path === '/life') return 'life'
|
||||||
|
if (path === '/blog') return 'blog'
|
||||||
|
return 'home'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRouteTitle = (path: string): string => {
|
||||||
|
if (path === '/about') return '关于'
|
||||||
|
if (path === '/life') return '生活'
|
||||||
|
if (path === '/blog') return '记事本'
|
||||||
|
if (path.startsWith('/photo')) return '相册'
|
||||||
|
if (path === '/others') return '更多'
|
||||||
|
if (path === '/kitchen-menu') return '菜单'
|
||||||
|
if (path === '/friends') return '朋友们'
|
||||||
|
if (path === '/projects') return '我做的'
|
||||||
|
return 'Home'
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateBookmark = (): void => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (route.path === '/blog' && Object.keys(route.query).length !== 0) return
|
||||||
|
const title = getRouteTitle(route.path)
|
||||||
|
document.title = title ? `Aza - ${title}` : 'Aza - Home'
|
||||||
|
})
|
||||||
|
|
||||||
|
currentRoute.value = getRouteBookmark(route.path)
|
||||||
|
|
||||||
|
calculateBookmarkCss()
|
||||||
|
menuOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
updateBookmark()
|
||||||
|
|
||||||
|
window.addEventListener('resize', calculateBookmarkCss, true)
|
||||||
|
bookmarkUpdateIntervalId.value = window.setInterval(calculateBookmarkCss, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => route.fullPath, updateBookmark)
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', calculateBookmarkCss, true)
|
||||||
|
if (bookmarkUpdateIntervalId.value !== null) {
|
||||||
|
window.clearInterval(bookmarkUpdateIntervalId.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="sass">
|
||||||
|
@use "css/global"
|
||||||
|
@use "css/animations"
|
||||||
|
|
||||||
|
#nav
|
||||||
|
position: fixed
|
||||||
|
left: 0
|
||||||
|
height: 100%
|
||||||
|
font-size: 1.4em
|
||||||
|
align-items: flex-start
|
||||||
|
|
||||||
|
z-index: 100
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
#menu
|
||||||
|
padding: 5px 10px
|
||||||
|
margin: 15px 10px
|
||||||
|
background-color: rgba(255, 255, 255, 0.49)
|
||||||
|
border-radius: 10px
|
||||||
|
text-shadow: -1px -1px 1px rgba(255,255,255,.1), 1px 1px 1px rgba(0,0,0,.5)
|
||||||
|
filter: drop-shadow(0 0 30px rgb(255, 255, 255))
|
||||||
|
|
||||||
|
z-index: 100
|
||||||
|
pointer-events: auto
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
#nav-bookmark
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
width: 20px
|
||||||
|
height: 0
|
||||||
|
z-index: 5
|
||||||
|
--bookmark-color: rgb(255, 225, 230)
|
||||||
|
border-color: var(--bookmark-color) transparent var(--bookmark-color) var(--bookmark-color)
|
||||||
|
border-style: solid
|
||||||
|
border-width: 20px
|
||||||
|
|
||||||
|
#items
|
||||||
|
justify-content: flex-end
|
||||||
|
z-index: 10
|
||||||
|
|
||||||
|
.router-link
|
||||||
|
color: rgba(128, 112, 92, 0.71)
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
z-index: 100
|
||||||
|
pointer-events: auto
|
||||||
|
|
||||||
|
.dot
|
||||||
|
content: '·'
|
||||||
|
margin: 20px 0
|
||||||
|
user-select: none
|
||||||
|
|
||||||
|
.router-link, .dot
|
||||||
|
text-decoration: none
|
||||||
|
writing-mode: vertical-rl
|
||||||
|
text-orientation: sideways
|
||||||
|
transform: scale(-1)
|
||||||
|
padding-right: 20px
|
||||||
|
|
||||||
|
#nav-background
|
||||||
|
position: absolute
|
||||||
|
height: 100%
|
||||||
|
width: 100px
|
||||||
|
left: 0
|
||||||
|
$nav-bg-color: #f9f2e0
|
||||||
|
border-left: 20px solid $nav-bg-color
|
||||||
|
background: linear-gradient(to right, $nav-bg-color, transparent)
|
||||||
|
|
||||||
|
z-index: 4
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
#meru
|
||||||
|
height: 160px
|
||||||
|
|
||||||
|
z-index: 100
|
||||||
|
pointer-events: auto
|
||||||
|
|
||||||
|
svg
|
||||||
|
display: inline-block
|
||||||
|
height: 1em
|
||||||
|
transform: rotate(180deg)
|
||||||
|
*
|
||||||
|
box-sizing: inherit
|
||||||
|
|
||||||
|
#nav *
|
||||||
|
transition: all .25s ease
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px)
|
||||||
|
#nav #menu
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
#nav.home
|
||||||
|
#menu, #nav-background
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
#nav:not(.home).open
|
||||||
|
#items, #nav-bookmark, #nav-background, #meru
|
||||||
|
opacity: 1
|
||||||
|
animation: fade-in-left .5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
||||||
|
|
||||||
|
#nav:not(.home)
|
||||||
|
#items, #nav-bookmark, #nav-background, #meru
|
||||||
|
opacity: 0
|
||||||
|
animation: fade-out-left 1s 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
||||||
|
|
||||||
|
@media screen and (max-width: 500px), (max-height: 660px)
|
||||||
|
#app
|
||||||
|
font-size: 14px
|
||||||
|
|
||||||
|
#nav
|
||||||
|
#nav-bookmark
|
||||||
|
width: 10px
|
||||||
|
|
||||||
|
#meru
|
||||||
|
height: 120px
|
||||||
|
|
||||||
|
@media screen and (max-width: 370px)
|
||||||
|
#nav #menu
|
||||||
|
opacity: unset
|
||||||
|
|
||||||
|
#nav.home
|
||||||
|
#menu, #nav-background
|
||||||
|
opacity: unset
|
||||||
|
|
||||||
|
#nav.open
|
||||||
|
#items, #nav-bookmark, #nav-background, #meru
|
||||||
|
opacity: 1
|
||||||
|
animation: fade-in-left .5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
||||||
|
|
||||||
|
#nav
|
||||||
|
#items, #nav-bookmark, #nav-background, #meru
|
||||||
|
opacity: 0
|
||||||
|
animation: fade-out-left 1s 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
|
||||||
|
|
||||||
|
@media screen and (max-height: 600px)
|
||||||
|
#nav
|
||||||
|
.dot
|
||||||
|
margin: 10px 0
|
||||||
|
|
||||||
|
@media screen and (max-height: 500px)
|
||||||
|
#nav
|
||||||
|
.router-link
|
||||||
|
margin-bottom: 10px
|
||||||
|
|
||||||
|
.dot
|
||||||
|
display: none
|
||||||
|
</style>
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
import {createApp, h} from 'vue'
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './scripts/router'
|
|
||||||
import {i18n} from "@/scripts/messages"
|
|
||||||
import '@fortawesome/fontawesome-free/css/all.min.css'
|
|
||||||
import Collapse from "@/components/Collapse.vue"
|
|
||||||
import BlogIndex from "@/components/BlogIndex.vue";
|
|
||||||
import Tag from "@/components/Tag.vue";
|
|
||||||
import 'virtual:uno.css'
|
|
||||||
|
|
||||||
const app = createApp(App).use(router).use(i18n)
|
|
||||||
.component('Collapse', Collapse)
|
|
||||||
.component('BlogIndex', BlogIndex)
|
|
||||||
.component('Dynamic', {
|
|
||||||
props: ['template'],
|
|
||||||
render() {
|
|
||||||
return h({template: this.template})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.component('Tag', Tag)
|
|
||||||
|
|
||||||
// app.config.performance = true
|
|
||||||
app.mount('#app')
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<AboutView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AboutView from '@/views/About.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<BlogView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import BlogView from '@/views/Blog.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<FriendsView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import FriendsView from '@/views/others/Friends.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<HomeView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import HomeView from '@/views/Home.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<MenuView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MenuView from '@/views/others/Menu.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<LifeView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LifeView from '@/views/Life.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<OthersView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import OthersView from '@/views/Others.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<PhotoView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PhotoView from '@/views/Photo.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<PhotoView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PhotoView from '@/views/Photo.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<ProjectsView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import ProjectsView from '@/views/others/Projects.vue'
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import {createI18n} from 'vue-i18n'
|
||||||
|
import {messages, resolveLocale} from '@/scripts/messages'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
const i18n = createI18n({
|
||||||
|
messages,
|
||||||
|
locale: resolveLocale(),
|
||||||
|
fallbackLocale: 'en'
|
||||||
|
})
|
||||||
|
|
||||||
|
nuxtApp.vueApp.use(i18n)
|
||||||
|
})
|
||||||
File diff suppressed because one or more lines are too long
+15
-27
@@ -1,31 +1,19 @@
|
|||||||
import {createI18n} from "vue-i18n";
|
|
||||||
|
|
||||||
export const messages = {
|
export const messages = {
|
||||||
en: {
|
en: {
|
||||||
nav: {
|
nav: {
|
||||||
home: 'Home',
|
home: 'Home',
|
||||||
about: '关于',
|
about: '关于',
|
||||||
blog: '记事本',
|
blog: '记事本',
|
||||||
life: '生活',
|
life: '生活',
|
||||||
projects: 'Projects',
|
projects: 'Projects',
|
||||||
others: '更多',
|
others: '更多',
|
||||||
photo: '相册'
|
photo: '相册'
|
||||||
}
|
|
||||||
},
|
|
||||||
zh: {
|
|
||||||
// nav: {
|
|
||||||
// home: '温馨家',
|
|
||||||
// about: '关于我',
|
|
||||||
// blog: '部落格',
|
|
||||||
// projects: '我做的',
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
zh: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const i18n = createI18n({
|
export const resolveLocale = (): string => {
|
||||||
messages,
|
if (typeof navigator === 'undefined') return 'en'
|
||||||
locale: navigator.language.split('-')[0],
|
return navigator.language.split('-')[0]
|
||||||
fallbackLocale: 'en'
|
}
|
||||||
})
|
|
||||||
|
|
||||||
console.log(navigator.language)
|
|
||||||
|
|||||||
+10
-83
@@ -1,88 +1,15 @@
|
|||||||
import {createRouter, createWebHistory, NavigationFailure, RouteRecordRaw} from 'vue-router'
|
import {NavigationFailure, useRoute, useRouter} from 'vue-router'
|
||||||
import Home from '../views/Home.vue'
|
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
export function pushQuery(query: {[id: string]: string | null}): Promise<void | NavigationFailure | undefined> {
|
||||||
{
|
const route = useRoute()
|
||||||
path: '/',
|
const router = useRouter()
|
||||||
name: 'Home',
|
|
||||||
meta: {title: 'Home', nav: true},
|
|
||||||
component: Home
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/about',
|
|
||||||
name: 'About',
|
|
||||||
meta: {title: '关于', nav: true},
|
|
||||||
component: () => import('../views/About.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/life',
|
|
||||||
name: 'Life',
|
|
||||||
meta: {title: '生活', nav: true},
|
|
||||||
component: () => import('../views/Life.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/blog',
|
|
||||||
name: 'Blog',
|
|
||||||
meta: {title: '记事本', nav: true},
|
|
||||||
component: () => import('../views/Blog.vue'),
|
|
||||||
props: route => (route.query)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/photo',
|
|
||||||
name: 'Photo',
|
|
||||||
meta: {title: '相册', nav: true},
|
|
||||||
component: () => import('../views/Photo.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/photo/:id',
|
|
||||||
name: 'Photo ID',
|
|
||||||
meta: {title: '相册', navBookmark: 'Photo'},
|
|
||||||
component: () => import('../views/Photo.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/others',
|
|
||||||
name: 'Others',
|
|
||||||
meta: {title: '更多', nav: true},
|
|
||||||
component: () => import('../views/Others.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/kitchen-menu',
|
|
||||||
name: 'Menu',
|
|
||||||
meta: {title: '菜单', navBookmark: 'Others'},
|
|
||||||
component: () => import('../views/others/Menu.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/friends',
|
|
||||||
name: 'Friends',
|
|
||||||
meta: {title: '朋友们', navBookmark: 'Others'},
|
|
||||||
component: () => import('../views/others/Friends.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/projects',
|
|
||||||
name: 'Projects',
|
|
||||||
meta: {title: '我做的', navBookmark: 'Others'},
|
|
||||||
component: () => import('../views/others/Projects.vue')
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const router = createRouter({
|
const queries = {...route.query}
|
||||||
history: createWebHistory(),
|
|
||||||
routes
|
|
||||||
})
|
|
||||||
|
|
||||||
export function pushQuery(query: {[id: string]: string | null}): Promise<void | NavigationFailure | undefined>
|
for (const k of Object.keys(query)) {
|
||||||
{
|
if (query[k] == null) delete queries[k]
|
||||||
const queries = {...router.currentRoute.value.query ?? {}}
|
else queries[k] = query[k]
|
||||||
|
}
|
||||||
|
|
||||||
console.log(query)
|
return router.push({query: queries})
|
||||||
|
|
||||||
for (const k of Object.keys(query))
|
|
||||||
{
|
|
||||||
if (query[k] == null) delete queries[k]
|
|
||||||
else queries[k] = query[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
return router.push({query: queries})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default router
|
|
||||||
|
|||||||
+38
-39
@@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="Blog" class="general-page" v-if="meta">
|
<div id="Blog" class="general-page" v-if="meta">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h2>记事本</h2>
|
<h2>记事本</h2>
|
||||||
<div class="subtitle">一些值得留下来的文字</div>
|
<div class="subtitle">一些值得留下来的文字</div>
|
||||||
</div>
|
|
||||||
<div id="breadcrumb">
|
|
||||||
<span class="clickable" @click="() => $router.push({query: {}})">索引</span>
|
|
||||||
<span v-if="tag">🏷️{{tag}}</span>
|
|
||||||
<span v-if="category">📂{{category}}</span>
|
|
||||||
<span class="no-after" v-if="post && activePost">{{activePost.title}}</span>
|
|
||||||
</div>
|
|
||||||
<BlogPostPreview v-for="m of filteredPosts" :key="m.id" :meta="m" :active="m === activePost"/>
|
|
||||||
</div>
|
</div>
|
||||||
<Loading v-else></Loading>
|
<div id="breadcrumb">
|
||||||
|
<span class="clickable" @click="clearQuery">索引</span>
|
||||||
|
<span v-if="tag">🏷️{{tag}}</span>
|
||||||
|
<span v-if="category">📂{{category}}</span>
|
||||||
|
<span class="no-after" v-if="post && activePost">{{activePost.title}}</span>
|
||||||
|
</div>
|
||||||
|
<BlogPostPreview v-for="m of filteredPosts" :key="m.id" :meta="m" :active="m === activePost"/>
|
||||||
|
</div>
|
||||||
|
<Loading v-else></Loading>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -22,40 +22,39 @@ import {BlogMeta} from "@/scripts/models";
|
|||||||
import {Ref, ref, computed, onMounted} from "vue";
|
import {Ref, ref, computed, onMounted} from "vue";
|
||||||
import {hosts} from "@/scripts/constants";
|
import {hosts} from "@/scripts/constants";
|
||||||
import {globals} from "@/scripts/global";
|
import {globals} from "@/scripts/global";
|
||||||
import {Router} from "vue-router";
|
|
||||||
|
|
||||||
let $router: Router
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
const p = defineProps<{
|
|
||||||
post?: string,
|
|
||||||
category?: string,
|
|
||||||
tag?: string
|
|
||||||
}>()
|
|
||||||
|
|
||||||
let meta: Ref<BlogMeta> = ref({tags: [], categories: [], posts: []})
|
let meta: Ref<BlogMeta> = ref({tags: [], categories: [], posts: []})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetch(`${hosts.content}/content/generated/metas.json`).then(it => it.json()).then(it => {
|
fetch(`${hosts.content}/content/generated/metas.json`).then(it => it.json()).then(it => {
|
||||||
meta.value = it
|
meta.value = it
|
||||||
globals.staticMeta = it
|
globals.staticMeta = it
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const post = computed(() => typeof route.query.post === 'string' ? route.query.post : undefined)
|
||||||
|
const category = computed(() => typeof route.query.category === 'string' ? route.query.category : undefined)
|
||||||
|
const tag = computed(() => typeof route.query.tag === 'string' ? route.query.tag : undefined)
|
||||||
|
|
||||||
|
const clearQuery = (): Promise<void> => router.push({query: {}})
|
||||||
|
|
||||||
const filteredPosts = computed(() => {
|
const filteredPosts = computed(() => {
|
||||||
const posts = meta.value.posts.filter(it => it.pinned || (p.tag ? it.tags.includes(p.tag) :
|
const posts = meta.value.posts.filter(it => it.pinned || (tag.value ? it.tags.includes(tag.value) :
|
||||||
p.category ? it.category == p.category : true))
|
category.value ? it.category == category.value : true))
|
||||||
|
|
||||||
// Put pinned posts on top
|
posts.sort((a, b) => (b.pinned ?? 0) - (a.pinned ?? 0))
|
||||||
posts.sort((a, b) => (b.pinned ?? 0) - (a.pinned ?? 0))
|
|
||||||
|
|
||||||
return posts
|
return posts
|
||||||
})
|
})
|
||||||
|
|
||||||
const activePost = computed(() => {
|
const activePost = computed(() => {
|
||||||
const posts = filteredPosts.value
|
const posts = filteredPosts.value
|
||||||
if (posts.length == 0) return null
|
if (posts.length == 0) return null
|
||||||
if (!p.post) return null
|
if (!post.value) return null
|
||||||
return p.post ? posts.filter(it => it.url_name == p.post)[0] : posts[0].pinned ? posts[0] : null
|
return post.value ? posts.filter(it => it.url_name == post.value)[0] : posts[0].pinned ? posts[0] : null
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -64,11 +63,11 @@ const activePost = computed(() => {
|
|||||||
@use "../css/responsive"
|
@use "../css/responsive"
|
||||||
|
|
||||||
#breadcrumb
|
#breadcrumb
|
||||||
color: colors.$color-text-light
|
color: colors.$color-text-light
|
||||||
margin-bottom: 20px
|
margin-bottom: 20px
|
||||||
|
|
||||||
span:not(.no-after):after
|
span:not(.no-after):after
|
||||||
content: ">"
|
content: ">"
|
||||||
margin: 0 10px
|
margin: 0 10px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+4
-39
@@ -1,41 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"extends": "./.nuxt/tsconfig.json",
|
||||||
"target": "esnext",
|
"compilerOptions": {
|
||||||
"module": "esnext",
|
"types": ["@types/jquery", "@types/jqueryui"]
|
||||||
"strict": false,
|
}
|
||||||
"jsx": "preserve",
|
|
||||||
"importHelpers": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": [
|
|
||||||
"src/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"lib": [
|
|
||||||
"esnext",
|
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"scripthost"
|
|
||||||
],
|
|
||||||
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"allowJs": true,
|
|
||||||
"resolveJsonModule": true
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src/**/*.ts",
|
|
||||||
"src/**/*.tsx",
|
|
||||||
"src/**/*.vue",
|
|
||||||
"tests/**/*.ts",
|
|
||||||
"tests/**/*.tsx"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { defineConfig } from "vite";
|
|
||||||
import vue from "@vitejs/plugin-vue";
|
|
||||||
import UnoCSS from 'unocss/vite';
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
const src = path.resolve(__dirname, 'src')
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
vue(), UnoCSS()
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': src,
|
|
||||||
|
|
||||||
// Runtime compilation
|
|
||||||
vue: "vue/dist/vue.esm-bundler.js"
|
|
||||||
},
|
|
||||||
dedupe: ['vue'],
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
watch: {
|
|
||||||
usePolling: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user