修复移动端菜单样式和问题

This commit is contained in:
lsy 2025-04-19 22:17:33 +08:00
parent 7dcf3f2a8b
commit 5df41feb02
7 changed files with 165 additions and 124 deletions

View File

@ -1,5 +1,3 @@
import React from 'react';
interface Breadcrumb {
name: string;
path: string;

View File

@ -92,12 +92,11 @@ const normalizedPath =
NAV_LINKS.map((link) => (
<a
href={link.href}
class:list={[
"inline-flex items-center px-1 pt-1 text-sm font-medium",
class={`inline-flex items-center px-1 pt-1 text-sm font-medium ${
normalizedPath === link.href
? "text-primary-600 dark:text-primary-400 border-b-2 border-primary-600 dark:border-primary-400"
: "text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 hover:border-b-2 hover:border-primary-300 dark:hover:border-primary-700",
]}
: "text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 hover:border-b-2 hover:border-primary-300 dark:hover:border-primary-700"
}`}
>
{link.text}
</a>
@ -253,19 +252,18 @@ const normalizedPath =
class="hidden md:hidden fixed inset-x-0 top-16 z-40"
id="mobile-menu"
>
<div id="mobile-menu-bg">
<div id="mobile-menu-bg" class="bg-white/95 dark:bg-gray-800/95 backdrop-blur-sm shadow-lg border-t border-gray-200 dark:border-gray-700/50 rounded-b-lg">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2">
<div class="grid gap-1">
{
NAV_LINKS.map((link) => (
<a
href={link.href}
class:list={[
"flex items-center px-3 py-3 rounded-lg text-base font-medium",
class={`flex items-center px-3 py-3 rounded-lg text-base font-medium ${
normalizedPath === link.href
? "text-white bg-primary-600 dark:bg-primary-500 shadow-sm"
: "text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800/70",
]}
: "text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800/70"
}`}
>
{link.text}
</a>
@ -288,22 +286,12 @@ const normalizedPath =
</header>
<script>
// 确保脚本适用于视图转换
function initHeader() {
const header = document.getElementById("header-bg");
const scrollThreshold = 50;
// 记录当前路径和导航状态
const currentUrl = window.location.pathname;
const normalizedPath =
currentUrl === "/"
? "/"
: currentUrl.endsWith("/")
? currentUrl.slice(0, -1)
: currentUrl;
// 获取所有导航链接
const navLinks = document.querySelectorAll("nav a[href]");
// 获取桌面端导航链接(排除移动端菜单中的链接)
const navLinks = document.querySelectorAll('.hidden.md\\:flex a[href]');
// 更新导航高亮状态
function updateNavHighlight() {
@ -315,46 +303,43 @@ const normalizedPath =
? currentUrl.slice(0, -1)
: currentUrl;
// 更新桌面端导航链接
navLinks.forEach((link) => {
const href = link.getAttribute("href");
const isActive = href === normalizedPath;
// 使用 classList.toggle 来切换类
link.classList.toggle("text-primary-600", isActive);
link.classList.toggle("dark:text-primary-400", isActive);
link.classList.toggle("border-b-2", isActive);
link.classList.toggle("border-primary-600", isActive);
link.classList.toggle("dark:border-primary-400", isActive);
link.classList.toggle("text-secondary-600", !isActive);
link.classList.toggle("dark:text-secondary-400", !isActive);
link.classList.toggle("hover:text-primary-600", !isActive);
link.classList.toggle("dark:hover:text-primary-400", !isActive);
link.classList.toggle("hover:border-b-2", !isActive);
link.classList.toggle("hover:border-primary-300", !isActive);
link.classList.toggle("dark:hover:border-primary-700", !isActive);
});
if (href === normalizedPath) {
// 添加高亮类
link.classList.add(
"text-primary-600",
"dark:text-primary-400",
"border-b-2",
"border-primary-600",
"dark:border-primary-400",
);
link.classList.remove(
"text-secondary-600",
"dark:text-secondary-400",
"hover:text-primary-600",
"dark:hover:text-primary-400",
"hover:border-b-2",
"hover:border-primary-300",
"dark:hover:border-primary-700",
);
} else {
// 移除高亮类
link.classList.remove(
"text-primary-600",
"dark:text-primary-400",
"border-b-2",
"border-primary-600",
"dark:border-primary-400",
);
link.classList.add(
"text-secondary-600",
"dark:text-secondary-400",
"hover:text-primary-600",
"dark:hover:text-primary-400",
"hover:border-b-2",
"hover:border-primary-300",
"dark:hover:border-primary-700",
);
}
// 更新移动端导航链接
const mobileNavLinks = document.querySelectorAll('#mobile-menu a[href]');
mobileNavLinks.forEach((link) => {
const href = link.getAttribute("href");
const isActive = href === normalizedPath;
// 使用 classList.toggle 来切换类
link.classList.toggle("text-white", isActive);
link.classList.toggle("bg-primary-600", isActive);
link.classList.toggle("dark:bg-primary-500", isActive);
link.classList.toggle("shadow-sm", isActive);
link.classList.toggle("text-gray-700", !isActive);
link.classList.toggle("dark:text-gray-200", !isActive);
link.classList.toggle("hover:bg-gray-100", !isActive);
link.classList.toggle("dark:hover:bg-gray-800/70", !isActive);
});
}
@ -368,17 +353,43 @@ const normalizedPath =
// 初始检查
updateHeaderBackground();
// 初始化导航高亮
updateNavHighlight();
// 添加滚动事件监听
window.addEventListener("scroll", updateHeaderBackground);
// 监听路由变化
document.addEventListener('astro:page-load', updateNavHighlight);
document.addEventListener('astro:after-swap', updateNavHighlight);
// 移动端菜单逻辑
const mobileMenuButton = document.getElementById("mobile-menu-button");
const mobileMenu = document.getElementById("mobile-menu");
const menuOpenIcon = document.getElementById("menu-open-icon");
const menuCloseIcon = document.getElementById("menu-close-icon");
// 移动端搜索面板元素
const mobileSearchButton = document.getElementById("mobile-search-button");
const mobileSearchPanel = document.getElementById("mobile-search-panel");
const mobileSearch = document.getElementById("mobile-search");
const mobileSearchClose = document.getElementById("mobile-search-close");
// 关闭移动端菜单的函数
function closeMobileMenu() {
if (mobileMenuButton && mobileMenu && menuOpenIcon && menuCloseIcon) {
mobileMenuButton.setAttribute("aria-expanded", "false");
mobileMenu.classList.add("hidden");
menuOpenIcon.classList.remove("hidden");
menuCloseIcon.classList.add("hidden");
}
}
// 关闭移动端搜索面板的函数
function closeMobileSearch() {
if (mobileSearchPanel) {
mobileSearchPanel.classList.add("hidden");
}
}
if (mobileMenuButton && mobileMenu && menuOpenIcon && menuCloseIcon) {
mobileMenuButton.addEventListener("click", () => {
@ -392,6 +403,9 @@ const normalizedPath =
// 直接隐藏菜单,不使用过渡效果
mobileMenu.classList.add("hidden");
} else {
// 打开菜单前先关闭搜索面板
closeMobileSearch();
// 直接显示菜单,不使用过渡效果
mobileMenu.classList.remove("hidden");
}
@ -400,48 +414,36 @@ const normalizedPath =
menuOpenIcon.classList.toggle("hidden");
menuCloseIcon.classList.toggle("hidden");
});
}
// 移动端主题切换容器点击处理
const themeToggleContainer = document.getElementById(
"theme-toggle-container",
);
if (themeToggleContainer) {
themeToggleContainer.addEventListener("click", (e) => {
const target = e.target as HTMLElement;
const themeToggleButton =
themeToggleContainer.querySelector('[role="button"]');
// 如果点击的不是主题切换按钮本身,而是容器或文本
if (
themeToggleButton instanceof HTMLElement &&
target !== themeToggleButton &&
!themeToggleButton.contains(target)
) {
// 手动触发主题切换按钮的点击
themeToggleButton.click();
}
// 为移动端导航链接添加点击事件
const mobileNavLinks = document.querySelectorAll('#mobile-menu a[href]');
mobileNavLinks.forEach(link => {
link.addEventListener('click', closeMobileMenu);
});
}
// 移动端搜索按钮
const mobileSearchButton = document.getElementById("mobile-search-button");
const mobileSearchPanel = document.getElementById("mobile-search-panel");
const mobileSearch = document.getElementById("mobile-search");
const mobileSearchClose = document.getElementById("mobile-search-close");
if (mobileSearchButton && mobileSearchPanel) {
mobileSearchButton.addEventListener("click", () => {
mobileSearchPanel.classList.remove("hidden");
mobileSearchPanel.classList.add("show");
if (mobileSearch) mobileSearch.focus();
// 检查搜索面板是否已经打开
const isSearchVisible = !mobileSearchPanel.classList.contains("hidden");
if (isSearchVisible) {
// 如果搜索面板已打开,则关闭它
closeMobileSearch();
} else {
// 打开搜索面板前先关闭菜单
closeMobileMenu();
// 打开搜索面板
mobileSearchPanel.classList.remove("hidden");
if (mobileSearch) mobileSearch.focus();
}
});
if (mobileSearchClose) {
mobileSearchClose.addEventListener("click", () => {
mobileSearchPanel.classList.add("hidden");
mobileSearchPanel.classList.remove("show");
closeMobileSearch();
});
}
}
@ -731,7 +733,6 @@ const normalizedPath =
);
if (mobileSearchPanel) {
mobileSearchPanel.classList.add("hidden");
mobileSearchPanel.classList.remove("show");
}
}
});

View File

@ -48,9 +48,12 @@ const {
<script>
function setupThemeToggle() {
const themeToggleButton = document.getElementById('theme-toggle-button');
// 获取所有主题切换按钮
const themeToggleButtons = document.querySelectorAll('#theme-toggle-button');
if (!themeToggleButton) return;
if (!themeToggleButtons.length) {
return;
}
let transitioning = false;
let transitionTimeout: number | null = null;
@ -62,7 +65,9 @@ const {
// 切换主题
const toggleTheme = () => {
if (transitioning) return;
if (transitioning) {
return;
}
transitioning = true;
@ -92,17 +97,6 @@ const {
}, 300) as unknown as number;
};
// 添加点击事件
themeToggleButton.addEventListener('click', toggleTheme);
// 添加键盘事件
themeToggleButton.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleTheme();
}
});
// 监听系统主题变化
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
@ -116,10 +110,62 @@ const {
mediaQuery.addEventListener('change', handleMediaChange);
// 存储事件处理函数引用,用于清理
const clickHandlers = new Map();
// 为每个按钮添加点击事件
themeToggleButtons.forEach(button => {
const handler = (e: Event) => {
// 阻止事件冒泡
e.stopPropagation();
toggleTheme();
};
clickHandlers.set(button, handler);
button.addEventListener('click', handler);
// 恢复键盘事件
button.addEventListener('keydown', function(e) {
const keyEvent = e as KeyboardEvent;
if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {
e.preventDefault();
toggleTheme();
}
});
});
// 处理移动端主题切换容器
let containerHandler: ((e: Event) => void) | null = null;
const themeToggleContainer = document.getElementById('theme-toggle-container');
if (themeToggleContainer) {
containerHandler = (e) => {
const target = e.target as HTMLElement;
// 如果点击的不是主题切换按钮本身,则手动触发主题切换
if (target.id !== 'theme-toggle-button' && !target.closest('#theme-toggle-button')) {
e.stopPropagation();
toggleTheme();
}
};
themeToggleContainer.addEventListener('click', containerHandler);
}
// 清理
return () => {
mediaQuery.removeEventListener('change', handleMediaChange);
themeToggleButton.removeEventListener('click', toggleTheme);
// 清理按钮事件
themeToggleButtons.forEach(button => {
const handler = clickHandlers.get(button);
if (handler) {
button.removeEventListener('click', handler);
}
});
// 清理容器事件
if (themeToggleContainer && containerHandler) {
themeToggleContainer.removeEventListener('click', containerHandler);
}
if (transitionTimeout) {
clearTimeout(transitionTimeout);
}

View File

@ -138,6 +138,7 @@ function getArticleUrl(articleId: string) {
pageType="article"
pathSegments={breadcrumbs}
articleTitle={article.data.title}
client:load
/>
</div>

View File

@ -97,6 +97,13 @@ const mergedProps = {
tag,
view
};
// 生成页面标题
let pageTitle = path ? path : '文章列表';
if (tag) {
pageTitle = `标签: ${tag}`;
}
---
<h1 class="sr-only">{pageTitle}</h1>
<ArticlesPage {...mergedProps} />

View File

@ -201,6 +201,9 @@ function getArticleUrl(articleId: string) {
<Layout>
<div class="bg-gray-50 dark:bg-dark-bg min-h-screen">
<main class={`mx-auto px-4 sm:px-6 lg:px-8 py-6 ${viewMode === 'grid' ? 'max-w-7xl' : 'max-w-5xl'}`}>
<!-- 页面标题 -->
<h1 class="sr-only">{pageTitle}</h1>
<!-- 导航栏 -->
<div class="bg-white dark:bg-gray-800 rounded-xl mb-4 shadow-lg border border-gray-200 dark:border-gray-700">
<div class="px-4 py-3">
@ -209,6 +212,7 @@ function getArticleUrl(articleId: string) {
pageType="articles"
pathSegments={pathSegments}
tagFilter={tagFilter}
client:load
/>
<!-- 视图切换按钮 -->

View File

@ -14,20 +14,4 @@
[data-theme="dark"] #header-bg.scrolled {
background: rgba(15, 23, 42, 0.8);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
}
/* 移动端搜索面板动画 */
.show {
animation: slide-down 0.2s ease-out forwards;
}
@keyframes slide-down {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}