update
This commit is contained in:
@@ -7,8 +7,11 @@ interface TocLink {
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
links: TocLink[];
|
links: TocLink[];
|
||||||
|
isMobile?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
const activeId = ref("");
|
const activeId = ref("");
|
||||||
|
|
||||||
const flattenLinks = (links: TocLink[]) => {
|
const flattenLinks = (links: TocLink[]) => {
|
||||||
@@ -44,9 +47,9 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full lg:w-1/3 hidden lg:block sticky top-28">
|
<div v-if="!isMobile" class="w-full lg:w-1/3 hidden lg:block sticky top-28">
|
||||||
<div
|
<div
|
||||||
class="h-fit max-h-[calc(100vh-8rem)] overflow-y-auto bg-white/40 dark:bg-slate-900/40 backdrop-blur-md border border-white/20 dark:border-white/5 p-6 rounded-3xl shadow-sm">
|
class="h-fit max-h-[calc(100vh-8rem)] overflow-y-auto bg-white/40 dark:bg-slate-800/40 backdrop-blur-md border border-white/20 dark:border-white/5 p-6 rounded-3xl shadow-sm">
|
||||||
<h3
|
<h3
|
||||||
class="text-xs font-bold uppercase tracking-widest text-zinc-400 dark:text-zinc-500 mb-4 flex items-center gap-2">
|
class="text-xs font-bold uppercase tracking-widest text-zinc-400 dark:text-zinc-500 mb-4 flex items-center gap-2">
|
||||||
<Icon name="heroicons:list-bullet" class="w-4 h-4" />
|
<Icon name="heroicons:list-bullet" class="w-4 h-4" />
|
||||||
@@ -98,4 +101,70 @@ onMounted(() => {
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="w-full">
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h3
|
||||||
|
class="text-xs font-bold uppercase tracking-widest text-zinc-400 dark:text-zinc-500 flex items-center gap-2">
|
||||||
|
<Icon name="heroicons:list-bullet" class="w-4 h-4" />
|
||||||
|
Table Of Content
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
class="p-2 hover:bg-zinc-100 dark:hover:bg-white/5 rounded-full transition-colors"
|
||||||
|
@click="emit('close')">
|
||||||
|
<Icon name="heroicons:x-mark" class="w-5 h-5 text-zinc-500" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="max-h-[60vh] overflow-y-auto pr-2">
|
||||||
|
<nav class="space-y-1">
|
||||||
|
<template v-for="link in props.links" :key="link.id">
|
||||||
|
<NuxtLink
|
||||||
|
:to="`#${link.id}`"
|
||||||
|
class="block text-sm py-1.5 px-3 rounded-xl transition-all duration-200"
|
||||||
|
:class="[
|
||||||
|
activeId === link.id
|
||||||
|
? 'text-violet-600 dark:text-violet-400 bg-violet-500/10 font-bold translate-x-1'
|
||||||
|
: 'text-zinc-600 dark:text-zinc-400 hover:text-violet-600 dark:hover:text-violet-400 hover:bg-violet-500/5',
|
||||||
|
]"
|
||||||
|
@click="emit('close')">
|
||||||
|
{{ link.text }}
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<div v-if="link.children" class="ml-3 space-y-1">
|
||||||
|
<template v-for="child in link.children" :key="child.id">
|
||||||
|
<NuxtLink
|
||||||
|
:to="`#${child.id}`"
|
||||||
|
class="block text-xs py-1.5 px-3 rounded-xl transition-all duration-200"
|
||||||
|
:class="[
|
||||||
|
activeId === child.id
|
||||||
|
? 'text-violet-600 dark:text-violet-400 bg-violet-500/10 font-bold translate-x-1'
|
||||||
|
: 'text-zinc-500 dark:text-zinc-500 hover:text-violet-600 dark:hover:text-violet-400 hover:bg-violet-500/5',
|
||||||
|
]"
|
||||||
|
@click="emit('close')">
|
||||||
|
{{ child.text }}
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
|
<div v-if="child.children" class="ml-3 space-y-1">
|
||||||
|
<NuxtLink
|
||||||
|
v-for="grandchild in child.children"
|
||||||
|
:key="grandchild.id"
|
||||||
|
:to="`#${grandchild.id}`"
|
||||||
|
class="block text-xs py-1.5 px-3 rounded-xl transition-all duration-200"
|
||||||
|
:class="[
|
||||||
|
activeId === grandchild.id
|
||||||
|
? 'text-violet-600 dark:text-violet-400 bg-violet-500/10 font-bold translate-x-1'
|
||||||
|
: 'text-zinc-400 dark:text-zinc-600 hover:text-violet-600 dark:hover:text-violet-400 hover:bg-violet-500/5',
|
||||||
|
]"
|
||||||
|
@click="emit('close')">
|
||||||
|
{{ grandchild.text }}
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
<!-- 备案信息 -->
|
<!-- 备案信息 -->
|
||||||
<p v-if="contact?.beian" class="text-text-muted text-xs m-0">
|
<p v-if="contact?.beian" class="text-text-muted text-xs m-0">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="contact.beianLink || '/'"
|
:to="contact.beianLink || 'https://beian.miit.gov.cn/'"
|
||||||
class="opacity-85 transition-all duration-200 hover:text-primary hover:opacity-100">
|
class="opacity-85 transition-all duration-200 hover:text-accent hover:opacity-100">
|
||||||
{{ contact.beian }}
|
{{ contact.beian }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</p>
|
</p>
|
||||||
@@ -49,6 +49,13 @@
|
|||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
© {{ new Date(siteConfig.siteMeta.startTime).getFullYear() }}-{{ new Date().getFullYear() }}
|
||||||
|
{{ siteConfig.siteMeta.author }} |
|
||||||
|
<NuxtLink to="/rss.xml" class="text-primary">RSS</NuxtLink> |
|
||||||
|
<NuxtLink to="/sitemap.xml" class="text-primary">Sitemap</NuxtLink>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||||
<div v-if="contact?.customHtml" v-html="contact.customHtml" />
|
<div v-if="contact?.customHtml" v-html="contact.customHtml" />
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ watch(
|
|||||||
<li v-for="link in siteConfig.navbar.links" :key="link.path">
|
<li v-for="link in siteConfig.navbar.links" :key="link.path">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="link.path"
|
:to="link.path"
|
||||||
class="relative h-12 px-6 rounded-full transition-all duration-200 flex items-center text-zinc-700 dark:text-zinc-200"
|
class="relative h-12 px-3 rounded-full transition-all duration-200 flex items-center text-zinc-700 dark:text-zinc-200"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-white dark:bg-slate-800 shadow-sm font-bold': isActive(link.path),
|
'bg-white dark:bg-slate-800 shadow-sm font-bold': isActive(link.path),
|
||||||
'hover:bg-zinc-100 dark:hover:bg-white/10': !isActive(link.path),
|
'hover:bg-zinc-100 dark:hover:bg-white/10': !isActive(link.path),
|
||||||
@@ -344,4 +344,97 @@ watch(
|
|||||||
.icon-svg.opacity-0 {
|
.icon-svg.opacity-0 {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 调整顶栏logo和主题切换键距离屏幕两端的距离 */
|
||||||
|
header .absolute.left-0 {
|
||||||
|
margin-left: -15px; /* 减小左侧距离 */
|
||||||
|
}
|
||||||
|
|
||||||
|
header .absolute.right-0 {
|
||||||
|
margin-right: -15px; /* 减小右侧距离 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 强制显示桌面端navbar,隐藏移动端 */
|
||||||
|
@media (min-width: 769px) and (max-width: 1023px) {
|
||||||
|
/* 显示桌面端navbar,隐藏移动端 */
|
||||||
|
.hidden.lg\:flex.items-center.justify-center.w-full.relative.h-14 {
|
||||||
|
display: flex !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
.lg\:hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
/* logo和右侧按钮绝对定位,navbar居中 */
|
||||||
|
header .absolute.left-0 {
|
||||||
|
position: absolute !important;
|
||||||
|
left: 0.5rem !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
z-index: 10;
|
||||||
|
height: 2.5rem !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
header .absolute.left-0 .h-14 {
|
||||||
|
height: 2.5rem !important;
|
||||||
|
min-width: 2.5rem !important;
|
||||||
|
width: auto !important;
|
||||||
|
padding-left: 0.5rem !important;
|
||||||
|
padding-right: 0.5rem !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
header .absolute.left-0 .h-12 {
|
||||||
|
height: 2.5rem !important;
|
||||||
|
font-size: 1rem !important;
|
||||||
|
padding-left: 0.5rem !important;
|
||||||
|
padding-right: 0.5rem !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
header .absolute.right-0 {
|
||||||
|
position: absolute !important;
|
||||||
|
right: 0.5rem !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
/* 缩小主题切换和线路按钮尺寸 */
|
||||||
|
header .absolute.right-0 .h-14,
|
||||||
|
header .absolute.right-0 .w-14 {
|
||||||
|
height: 2.5rem !important;
|
||||||
|
width: 2.5rem !important;
|
||||||
|
}
|
||||||
|
header .absolute.right-0 .gap-3 {
|
||||||
|
gap: 0.5rem !important;
|
||||||
|
}
|
||||||
|
/* navbar最大宽度,居中显示 */
|
||||||
|
.inline-flex.items-center.h-14 {
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
height: 2.5rem !important;
|
||||||
|
border-radius: 1.5rem !important;
|
||||||
|
padding-left: 0.5rem !important;
|
||||||
|
padding-right: 0.5rem !important;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 700px !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
}
|
||||||
|
/* 缩小ul间距和字体 */
|
||||||
|
.inline-flex.items-center.h-14 ul.flex.items-center.space-x-1\.5.text-lg {
|
||||||
|
gap: 0.25rem !important;
|
||||||
|
font-size: 1rem !important;
|
||||||
|
}
|
||||||
|
/* 缩小每个链接的padding和高度 */
|
||||||
|
.inline-flex.items-center.h-14 ul.flex.items-center li .h-12 {
|
||||||
|
height: 2rem !important;
|
||||||
|
}
|
||||||
|
.inline-flex.items-center.h-14 ul.flex.items-center li .px-3 {
|
||||||
|
padding-left: 0.75rem !important;
|
||||||
|
padding-right: 0.75rem !important;
|
||||||
|
}
|
||||||
|
/* 缩小图标和文字间距 */
|
||||||
|
.inline-flex.items-center.h-14 ul.flex.items-center li .mr-2 {
|
||||||
|
margin-right: 0.25rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ const siteConfig = {
|
|||||||
navbar: {
|
navbar: {
|
||||||
links: [
|
links: [
|
||||||
// { name: "Home", path: "/", icon: "fa6-solid:house" },
|
// { name: "Home", path: "/", icon: "fa6-solid:house" },
|
||||||
{ name: "Archive", path: "/archive", icon: "fa-solid:newspaper" },
|
{ name: "归档", path: "/archive", icon: "fa-solid:newspaper" },
|
||||||
{ name: "Categories", path: "/categories", icon: "fa-solid:folder" },
|
{ name: "分类", path: "/categories", icon: "fa-solid:folder" },
|
||||||
{ name: "Tags", path: "/tags", icon: "fa-solid:tags" },
|
{ name: "标签", path: "/tags", icon: "fa-solid:tags" },
|
||||||
{ name: "About", path: "/about", icon: "fa-solid:user" },
|
{ name: "关于", path: "/about", icon: "fa-solid:user" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -59,9 +59,9 @@ const siteConfig = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
footer: {
|
footer: {
|
||||||
beian: "津ICP备2025039003号-1",
|
beian: "津ICP备2025039003号-2",
|
||||||
beianLink: "https://beian.miit.gov.cn/",
|
beianLink: "https://beian.miit.gov.cn/",
|
||||||
customHtml: '<span style="opacity:.8">© 2025 <a href="https://rhen.cloud">RhenCloud</a></span>',
|
customHtml: "",
|
||||||
hitokoto: {
|
hitokoto: {
|
||||||
enable: true,
|
enable: true,
|
||||||
type: "a&b&c&d&j",
|
type: "a&b&c&d&j",
|
||||||
|
|||||||
@@ -23,3 +23,47 @@ const { data: content } = await useAsyncData(route.path, () => queryCollection("
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.container {
|
||||||
|
padding-left: 0.5rem !important;
|
||||||
|
padding-right: 0.5rem !important;
|
||||||
|
max-width: 100vw !important;
|
||||||
|
}
|
||||||
|
.flex.gap-8 {
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 1.5rem !important;
|
||||||
|
}
|
||||||
|
.prose {
|
||||||
|
word-break: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
white-space: normal !important;
|
||||||
|
line-break: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
.prose p,
|
||||||
|
.prose ul,
|
||||||
|
.prose li,
|
||||||
|
.prose h1,
|
||||||
|
.prose h2,
|
||||||
|
.prose h3,
|
||||||
|
.prose h4,
|
||||||
|
.prose h5,
|
||||||
|
.prose h6 {
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: normal !important;
|
||||||
|
line-break: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
main.prose {
|
||||||
|
width: 100% !important;
|
||||||
|
margin-top: 1.5rem !important;
|
||||||
|
}
|
||||||
|
aside.w-1\/3 {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import siteConfig from "~/config";
|
|||||||
|
|
||||||
const { path } = useRoute();
|
const { path } = useRoute();
|
||||||
|
|
||||||
|
const isTocOpen = ref(false);
|
||||||
|
|
||||||
const { data: articles, error } = await useAsyncData(`blog-post-${path}`, () =>
|
const { data: articles, error } = await useAsyncData(`blog-post-${path}`, () =>
|
||||||
queryCollection("content").path(path).first(),
|
queryCollection("content").path(path).first(),
|
||||||
);
|
);
|
||||||
@@ -54,7 +56,7 @@ useHead({
|
|||||||
:description="data.description"
|
:description="data.description"
|
||||||
:tags="data.tags" />
|
:tags="data.tags" />
|
||||||
<div
|
<div
|
||||||
class="prose prose-zinc dark:prose-invert max-w-none w-full prose-headings:scroll-mt-28 prose-headings:tracking-tight prose-headings:font-bold prose-h1:text-4xl prose-h2:text-3xl prose-h3:text-2xl prose-p:leading-relaxed prose-p:text-zinc-600 dark:prose-p:text-zinc-400 prose-a:text-violet-600 dark:prose-a:text-violet-400 prose-a:no-underline hover:prose-a:underline prose-blockquote:border-l-4 prose-blockquote:border-violet-500 prose-blockquote:bg-violet-500/5 prose-blockquote:py-1 prose-blockquote:px-6 prose-blockquote:rounded-r-xl prose-blockquote:italic prose-img:rounded-3xl prose-img:shadow-xl prose-code:text-violet-600 dark:prose-code:text-violet-400 prose-code:bg-violet-500/10 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:before:content-none prose-code:after:content-none prose-pre:bg-slate-900 dark:prose-pre:bg-slate-950 prose-pre:rounded-2xl prose-pre:shadow-2xl prose-pre:border prose-pre:border-white/5">
|
class="prose prose-zinc dark:prose-invert max-w-none w-full prose-headings:scroll-mt-28 prose-headings:tracking-tight prose-headings:font-bold prose-h1:text-4xl prose-h2:text-3xl prose-h3:text-2xl prose-p:leading-relaxed prose-a:no-underline hover:prose-a:underline prose-blockquote:py-1 prose-blockquote:px-6 prose-blockquote:rounded-r-xl prose-blockquote:italic prose-img:rounded-3xl prose-img:shadow-xl prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:before:content-none prose-code:after:content-none prose-pre:bg-slate-900 dark:prose-pre:bg-slate-950 prose-pre:rounded-2xl prose-pre:shadow-2xl prose-pre:border prose-pre:border-white/5">
|
||||||
<ContentRenderer v-if="articles" :value="articles">
|
<ContentRenderer v-if="articles" :value="articles">
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<p>No content found.</p>
|
<p>No content found.</p>
|
||||||
@@ -68,5 +70,51 @@ useHead({
|
|||||||
|
|
||||||
<!-- 侧边目录 -->
|
<!-- 侧边目录 -->
|
||||||
<BlogToc v-if="articles?.body?.toc?.links?.length" :links="articles.body.toc.links" />
|
<BlogToc v-if="articles?.body?.toc?.links?.length" :links="articles.body.toc.links" />
|
||||||
|
|
||||||
|
<!-- 移动端目录 -->
|
||||||
|
<ClientOnly>
|
||||||
|
<div v-if="articles?.body?.toc?.links?.length" class="lg:hidden">
|
||||||
|
<!-- 悬浮按钮 -->
|
||||||
|
<button
|
||||||
|
class="fixed bottom-6 right-6 z-50 h-12 w-12 rounded-full bg-primary text-white shadow-2xl flex items-center justify-center transition-all active:scale-90 hover:opacity-90"
|
||||||
|
@click="isTocOpen = true">
|
||||||
|
<Icon name="heroicons:list-bullet" class="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- 弹出层背景遮罩 -->
|
||||||
|
<Teleport to="body">
|
||||||
|
<Transition
|
||||||
|
enter-active-class="transition-opacity duration-300"
|
||||||
|
enter-from-class="opacity-0"
|
||||||
|
enter-to-class="opacity-100"
|
||||||
|
leave-active-class="transition-opacity duration-300"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0">
|
||||||
|
<div
|
||||||
|
v-if="isTocOpen"
|
||||||
|
class="fixed inset-0 bg-black/50 backdrop-blur-sm z-100"
|
||||||
|
@click="isTocOpen = false" />
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<!-- 弹出层内容 -->
|
||||||
|
<Transition
|
||||||
|
enter-active-class="transition-transform duration-300 ease-out"
|
||||||
|
enter-from-class="translate-y-full"
|
||||||
|
enter-to-class="translate-y-0"
|
||||||
|
leave-active-class="transition-transform duration-300 ease-in"
|
||||||
|
leave-from-class="translate-y-0"
|
||||||
|
leave-to-class="translate-y-full">
|
||||||
|
<div v-if="isTocOpen" class="fixed bottom-0 left-0 right-0 z-101 max-h-[80vh]">
|
||||||
|
<div class="bg-white dark:bg-slate-900 rounded-t-3xl shadow-2xl">
|
||||||
|
<BlogToc
|
||||||
|
:links="articles.body.toc.links"
|
||||||
|
:is-mobile="true"
|
||||||
|
@close="isTocOpen = false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</Teleport>
|
||||||
|
</div>
|
||||||
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user