fix(admin): auto-expand sidebar navigation group for active child page
- Add getExpandedMenusForPath to auto-expand the parent menu group when navigating to a child page (e.g., /admin/users expands "系统管理") - Add hasActiveChild highlight on parent menu group button (bg-gray-100) - Use lazy initialization for expandedMenus state based on current path - Use useEffect to track pathname changes and auto-expand accordingly - Replace `path` unused param with `_path` to satisfy TypeScript strict
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import { useAuth } from './auth-context';
|
import { useAuth } from './auth-context';
|
||||||
@@ -82,11 +82,48 @@ const menuItems = [
|
|||||||
|
|
||||||
export function AdminLayout({ children }: { children: React.ReactNode }) {
|
export function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
const [expandedMenus, setExpandedMenus] = useState<Set<string>>(new Set(['内容管理']));
|
|
||||||
const { user, logout } = useAuth();
|
const { user, logout } = useAuth();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isActive = (href: string) => pathname === href || pathname?.startsWith(href + '/');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前路径自动展开对应的父菜单组
|
||||||
|
*/
|
||||||
|
const getExpandedMenusForPath = (_path: string): Set<string> => {
|
||||||
|
const result = new Set<string>();
|
||||||
|
for (const group of menuItems) {
|
||||||
|
for (const item of group.children) {
|
||||||
|
if (isActive(item.href)) {
|
||||||
|
result.add(group.label);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 若当前路径在某个组内,保留已有展开状态并追加;否则保留默认展开
|
||||||
|
if (result.size > 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return new Set(['内容管理']);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [expandedMenus, setExpandedMenus] = useState<Set<string>>(() => getExpandedMenusForPath(pathname));
|
||||||
|
|
||||||
|
// 路径变化时自动展开对应父菜单组
|
||||||
|
const prevPathname = useRef(pathname);
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevPathname.current !== pathname) {
|
||||||
|
prevPathname.current = pathname;
|
||||||
|
const menusForPath = getExpandedMenusForPath(pathname);
|
||||||
|
setExpandedMenus((prev) => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
menusForPath.forEach((label) => next.add(label));
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
const toggleMenu = (label: string) => {
|
const toggleMenu = (label: string) => {
|
||||||
setExpandedMenus((prev) => {
|
setExpandedMenus((prev) => {
|
||||||
const next = new Set(prev);
|
const next = new Set(prev);
|
||||||
@@ -101,8 +138,6 @@ export function AdminLayout({ children }: { children: React.ReactNode }) {
|
|||||||
router.push('/admin/login');
|
router.push('/admin/login');
|
||||||
};
|
};
|
||||||
|
|
||||||
const isActive = (href: string) => pathname === href || pathname?.startsWith(href + '/');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className="min-h-screen bg-gray-50">
|
||||||
{/* Mobile overlay */}
|
{/* Mobile overlay */}
|
||||||
@@ -146,11 +181,17 @@ export function AdminLayout({ children }: { children: React.ReactNode }) {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Menu groups */}
|
{/* Menu groups */}
|
||||||
{menuItems.map((group) => (
|
{menuItems.map((group) => {
|
||||||
|
const hasActiveChild = group.children.some((item) => isActive(item.href));
|
||||||
|
return (
|
||||||
<div key={group.label}>
|
<div key={group.label}>
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleMenu(group.label)}
|
onClick={() => toggleMenu(group.label)}
|
||||||
className="flex items-center justify-between w-full px-3 py-2.5 rounded-lg text-sm font-medium text-gray-600 hover:bg-gray-100 transition-colors"
|
className={`flex items-center justify-between w-full px-3 py-2.5 rounded-lg text-sm font-medium transition-colors ${
|
||||||
|
hasActiveChild
|
||||||
|
? 'bg-gray-100 text-gray-900'
|
||||||
|
: 'text-gray-600 hover:bg-gray-100'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-3">
|
<span className="flex items-center gap-3">
|
||||||
<group.icon className="w-4 h-4" />
|
<group.icon className="w-4 h-4" />
|
||||||
@@ -182,7 +223,8 @@ export function AdminLayout({ children }: { children: React.ReactNode }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* User info */}
|
{/* User info */}
|
||||||
|
|||||||
Reference in New Issue
Block a user