尝试修复导航切换阻塞

This commit is contained in:
mtvpls
2025-11-30 21:33:05 +08:00
parent 73ac9f79cb
commit a8965119ce
5 changed files with 43 additions and 29 deletions

1
.next Symbolic link
View File

@@ -0,0 +1 @@
/.next

17
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/start"
}
]
}

View File

@@ -4,7 +4,7 @@
import { Cat, Clover, Film, Home, Radio, Star, Tv } from 'lucide-react'; import { Cat, Clover, Film, Home, Radio, Star, Tv } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
import { usePathname } from 'next/navigation'; import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
interface MobileBottomNavProps { interface MobileBottomNavProps {
@@ -16,9 +16,14 @@ interface MobileBottomNavProps {
const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => { const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => {
const pathname = usePathname(); const pathname = usePathname();
const searchParams = useSearchParams();
// 当前激活路径:优先使用传入的 activePath否则回退到浏览器地址 // 直接使用当前路由状态,确保立即响应路由变化
const currentActive = activePath ?? pathname; const getCurrentFullPath = () => {
const queryString = searchParams.toString();
return queryString ? `${pathname}?${queryString}` : pathname;
};
const currentActive = activePath ?? getCurrentFullPath();
const [navItems, setNavItems] = useState([ const [navItems, setNavItems] = useState([
{ icon: Home, label: '首页', href: '/' }, { icon: Home, label: '首页', href: '/' },
@@ -98,6 +103,7 @@ const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => {
> >
<Link <Link
href={item.href} href={item.href}
prefetch={false}
className='flex flex-col items-center justify-center w-full h-14 gap-1 text-xs' className='flex flex-col items-center justify-center w-full h-14 gap-1 text-xs'
> >
<item.icon <item.icon

View File

@@ -20,6 +20,7 @@ const MobileHeader = ({ showBackButton = false }: MobileHeaderProps) => {
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Link <Link
href='/search' href='/search'
prefetch={false}
className='w-10 h-10 p-2 rounded-full flex items-center justify-center text-gray-600 hover:bg-gray-200/50 dark:text-gray-300 dark:hover:bg-gray-700/50 transition-colors' className='w-10 h-10 p-2 rounded-full flex items-center justify-center text-gray-600 hover:bg-gray-200/50 dark:text-gray-300 dark:hover:bg-gray-700/50 transition-colors'
> >
<svg <svg
@@ -51,6 +52,7 @@ const MobileHeader = ({ showBackButton = false }: MobileHeaderProps) => {
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'> <div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
<Link <Link
href='/' href='/'
prefetch={false}
className='text-2xl font-bold text-green-600 tracking-tight hover:opacity-80 transition-opacity' className='text-2xl font-bold text-green-600 tracking-tight hover:opacity-80 transition-opacity'
> >
{siteName} {siteName}

View File

@@ -4,7 +4,7 @@
import { Cat, Clover, Film, Home, Menu, Radio, Search, Star, Tv } from 'lucide-react'; import { Cat, Clover, Film, Home, Menu, Radio, Search, Star, Tv } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { usePathname, useSearchParams } from 'next/navigation';
import { import {
createContext, createContext,
useCallback, useCallback,
@@ -54,7 +54,6 @@ declare global {
} }
const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
// 若同一次 SPA 会话中已经读取过折叠状态,则直接复用,避免闪烁 // 若同一次 SPA 会话中已经读取过折叠状态,则直接复用,避免闪烁
@@ -92,19 +91,14 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
const [active, setActive] = useState(activePath); const [active, setActive] = useState(activePath);
useEffect(() => { useEffect(() => {
// 优先使用传入的 activePath // 立即根据当前路径更新状态,不等待页面加载
if (activePath) {
setActive(activePath);
} else {
// 否则使用当前路径
const getCurrentFullPath = () => { const getCurrentFullPath = () => {
const queryString = searchParams.toString(); const queryString = searchParams.toString();
return queryString ? `${pathname}?${queryString}` : pathname; return queryString ? `${pathname}?${queryString}` : pathname;
}; };
const fullPath = getCurrentFullPath(); const fullPath = getCurrentFullPath();
setActive(fullPath); setActive(fullPath);
} }, [pathname, searchParams]);
}, [activePath, pathname, searchParams]);
const handleToggle = useCallback(() => { const handleToggle = useCallback(() => {
const newState = !isCollapsed; const newState = !isCollapsed;
@@ -116,10 +110,6 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
onToggle?.(newState); onToggle?.(newState);
}, [isCollapsed, onToggle]); }, [isCollapsed, onToggle]);
const handleSearchClick = useCallback(() => {
router.push('/search');
}, [router]);
const contextValue = { const contextValue = {
isCollapsed, isCollapsed,
}; };
@@ -203,7 +193,11 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
<nav className='px-2 mt-4 space-y-1'> <nav className='px-2 mt-4 space-y-1'>
<Link <Link
href='/' href='/'
onClick={() => setActive('/')} prefetch={false}
onClick={(e) => {
// 确保点击事件立即生效,不被其他状态更新阻塞
e.currentTarget.blur();
}}
data-active={active === '/'} data-active={active === '/'}
className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 font-medium transition-colors duration-200 min-h-[40px] dark:text-gray-300 dark:hover:text-green-400 dark:data-[active=true]:bg-green-500/10 dark:data-[active=true]:text-green-400 ${isCollapsed ? 'w-full max-w-none mx-0' : 'mx-0' className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 font-medium transition-colors duration-200 min-h-[40px] dark:text-gray-300 dark:hover:text-green-400 dark:data-[active=true]:bg-green-500/10 dark:data-[active=true]:text-green-400 ${isCollapsed ? 'w-full max-w-none mx-0' : 'mx-0'
} gap-3 justify-start`} } gap-3 justify-start`}
@@ -219,11 +213,6 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
</Link> </Link>
<Link <Link
href='/search' href='/search'
onClick={(e) => {
e.preventDefault();
handleSearchClick();
setActive('/search');
}}
data-active={active === '/search'} data-active={active === '/search'}
className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 font-medium transition-colors duration-200 min-h-[40px] dark:text-gray-300 dark:hover:text-green-400 dark:data-[active=true]:bg-green-500/10 dark:data-[active=true]:text-green-400 ${isCollapsed ? 'w-full max-w-none mx-0' : 'mx-0' className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 font-medium transition-colors duration-200 min-h-[40px] dark:text-gray-300 dark:hover:text-green-400 dark:data-[active=true]:bg-green-500/10 dark:data-[active=true]:text-green-400 ${isCollapsed ? 'w-full max-w-none mx-0' : 'mx-0'
} gap-3 justify-start`} } gap-3 justify-start`}
@@ -259,7 +248,6 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => {
<Link <Link
key={item.label} key={item.label}
href={item.href} href={item.href}
onClick={() => setActive(item.href)}
data-active={isActive} data-active={isActive}
className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-sm text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 transition-colors duration-200 min-h-[40px] dark:text-gray-300 dark:hover:text-green-400 dark:data-[active=true]:bg-green-500/10 dark:data-[active=true]:text-green-400 ${isCollapsed ? 'w-full max-w-none mx-0' : 'mx-0' className={`group flex items-center rounded-lg px-2 py-2 pl-4 text-sm text-gray-700 hover:bg-gray-100/30 hover:text-green-600 data-[active=true]:bg-green-500/20 data-[active=true]:text-green-700 transition-colors duration-200 min-h-[40px] dark:text-gray-300 dark:hover:text-green-400 dark:data-[active=true]:bg-green-500/10 dark:data-[active=true]:text-green-400 ${isCollapsed ? 'w-full max-w-none mx-0' : 'mx-0'
} gap-3 justify-start`} } gap-3 justify-start`}