This commit is contained in:
2026-01-03 22:21:55 +08:00
parent 77e1d5ed9e
commit f2f21218a8
4 changed files with 172 additions and 147 deletions

View File

@@ -17,6 +17,12 @@ function toggleMenu() {
isMenuOpen.value = !isMenuOpen.value;
}
// 移动端子菜单展开状态
const mobileOpen = ref<Record<string, boolean>>({});
function toggleMobileSubmenu(path: string) {
mobileOpen.value[path] = !mobileOpen.value[path];
}
const isLinesOpen = ref(false);
function toggleLines() {
isLinesOpen.value = !isLinesOpen.value;
@@ -66,6 +72,8 @@ watch(
() => {
isMenuOpen.value = false;
isLinesOpen.value = false;
// 关闭所有移动端子菜单
mobileOpen.value = {};
},
);
</script>
@@ -170,22 +178,56 @@ watch(
<div
class="inline-flex items-center h-14 bg-white/70 dark:bg-slate-900/50 backdrop-blur-xl shadow-[0_4px_20px_-2px_rgba(0,0,0,0.1)] border border-zinc-200/50 dark:border-white/5 rounded-full px-1.5 transition-all duration-300 hover:shadow-xl">
<ul class="flex items-center space-x-1.5 text-lg">
<li v-for="link in siteConfig.navbar.links" :key="link.path">
<NuxtLink
:to="link.path"
class="relative h-12 px-3 rounded-full transition-all duration-200 flex items-center text-zinc-700 dark:text-zinc-200"
:class="{
'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),
}">
<Icon
v-if="link.icon"
:name="link.icon"
size="20"
class="mr-2 flex items-center" />
<span>{{ link.name }}</span>
</NuxtLink>
</li>
<template v-for="link in siteConfig.navbar.links" :key="link.path">
<li v-if="!link.children">
<NuxtLink
:to="link.path"
class="relative h-12 px-3 rounded-full transition-all duration-200 flex items-center text-zinc-700 dark:text-zinc-200"
:class="{
'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),
}">
<Icon
v-if="link.icon"
:name="link.icon"
size="20"
class="mr-2 flex items-center" />
<span>{{ link.name }}</span>
</NuxtLink>
</li>
<li v-else class="relative group">
<div
class="relative h-12 px-3 rounded-full transition-all duration-200 flex items-center cursor-pointer text-zinc-700 dark:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-white/10"
:class="{
'bg-white dark:bg-slate-800 shadow-sm font-bold': isActive(link.path),
}">
<Icon
v-if="link.icon"
:name="link.icon"
size="20"
class="mr-2 flex items-center" />
<span>{{ link.name }}</span>
<Icon name="fa6-solid:chevron-down" size="14" class="ml-1 opacity-60" />
</div>
<ul
class="absolute left-0 top-full mt-0 min-w-32 bg-white dark:bg-slate-900 border border-zinc-200/50 dark:border-white/10 rounded-xl shadow-lg z-50 hidden group-hover:block hover:block pointer-events-auto">
<li v-for="child in link.children" :key="child.path">
<NuxtLink
:to="child.path"
class="block px-4 py-2 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-white/10 rounded-xl transition-colors">
<div class="flex items-center gap-2">
<Icon
v-if="child.icon"
:name="child.icon"
size="14"
class="mr-2 opacity-60" />
<span>{{ child.name }}</span>
</div>
</NuxtLink>
</li>
</ul>
</li>
</template>
</ul>
</div>
@@ -280,26 +322,80 @@ watch(
<div class="p-2">
<ul class="space-y-1">
<li v-for="link in siteConfig.navbar.links" :key="link.path">
<NuxtLink
:to="link.path"
class="flex items-center justify-between px-4 py-3.5 rounded-2xl text-zinc-700 dark:text-zinc-200 transition-all active:scale-[0.98]"
:class="
isActive(link.path)
? 'bg-violet-500/10 text-violet-600 dark:text-violet-400 font-bold'
: 'hover:bg-zinc-100 dark:hover:bg-white/5'
">
<div class="flex items-center gap-3">
<div
class="w-8 h-8 rounded-xl flex items-center justify-center transition-colors"
:class="
isActive(link.path) ? 'bg-violet-500/20' : 'bg-zinc-100 dark:bg-white/5'
">
<Icon v-if="link.icon" :name="link.icon" size="16" />
<template v-if="!link.children">
<NuxtLink
:to="link.path"
class="flex items-center justify-between px-4 py-3.5 rounded-2xl text-zinc-700 dark:text-zinc-200 transition-all active:scale-[0.98]"
:class="
isActive(link.path)
? 'bg-violet-500/10 text-violet-600 dark:text-violet-400 font-bold'
: 'hover:bg-zinc-100 dark:hover:bg-white/5'
"
@click="isMenuOpen = false">
<div class="flex items-center gap-3">
<div
class="w-8 h-8 rounded-xl flex items-center justify-center transition-colors"
:class="
isActive(link.path) ? 'bg-violet-500/20' : 'bg-zinc-100 dark:bg-white/5'
">
<Icon v-if="link.icon" :name="link.icon" size="16" />
</div>
<span class="text-sm">{{ link.name }}</span>
</div>
<span class="text-sm">{{ link.name }}</span>
<Icon name="fa6-solid:chevron-right" size="10" class="opacity-30" />
</NuxtLink>
</template>
<template v-else>
<div
class="flex items-center justify-between px-4 py-3.5 rounded-2xl text-zinc-700 dark:text-zinc-200 transition-all active:scale-[0.98]"
:class="
isActive(link.path)
? 'bg-violet-500/10 text-violet-600 dark:text-violet-400 font-bold'
: 'hover:bg-zinc-100 dark:hover:bg-white/5'
"
@click="toggleMobileSubmenu(link.path)">
<div class="flex items-center gap-3">
<div
class="w-8 h-8 rounded-xl flex items-center justify-center transition-colors"
:class="
isActive(link.path) ? 'bg-violet-500/20' : 'bg-zinc-100 dark:bg-white/5'
">
<Icon v-if="link.icon" :name="link.icon" size="16" />
</div>
<span class="text-sm">{{ link.name }}</span>
</div>
<Icon
name="fa6-solid:chevron-right"
size="10"
:class="[
'opacity-30 transition-transform',
mobileOpen[link.path] ? 'rotate-90' : '',
]" />
</div>
<Icon name="fa6-solid:chevron-right" size="10" class="opacity-30" />
</NuxtLink>
<transition
enter-active-class="transition duration-200"
leave-active-class="transition duration-150">
<ul v-if="mobileOpen[link.path]" class="pl-12 pr-3 pb-2 pt-2 space-y-1">
<li v-for="child in link.children" :key="child.path">
<NuxtLink
:to="child.path"
class="block px-3 py-2 rounded-lg text-sm text-zinc-700 dark:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-white/5"
@click="isMenuOpen = false">
<div class="flex items-center gap-2">
<Icon
v-if="child.icon"
:name="child.icon"
size="14"
class="mr-2 opacity-60" />
<span>{{ child.name }}</span>
</div>
</NuxtLink>
</li>
</ul>
</transition>
</template>
</li>
</ul>
</div>
@@ -437,4 +533,33 @@ header .absolute.right-0 {
margin-right: 0.25rem !important;
}
}
/* 解决 navbar 子菜单 hover 闪烁问题,提升可用性 */
.navbar-dropdown-group {
position: relative;
}
.navbar-dropdown-toggle {
cursor: pointer;
}
.navbar-dropdown-list {
display: none;
position: absolute;
left: 0;
top: 100%;
margin-top: 0.5rem;
min-width: 8rem;
background: #fff;
border-radius: 0.75rem;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.18);
z-index: 50;
border: 1px solid rgba(200, 200, 200, 0.15);
}
.dark .navbar-dropdown-list {
background: #18181b;
border-color: rgba(255, 255, 255, 0.08);
}
.navbar-dropdown-group:hover .navbar-dropdown-list,
.navbar-dropdown-list:hover {
display: block;
}
</style>