diff --git a/apps/web/src/app/(auth)/layout.tsx b/apps/web/src/app/(auth)/layout.tsx index a60e9c8..f825035 100644 --- a/apps/web/src/app/(auth)/layout.tsx +++ b/apps/web/src/app/(auth)/layout.tsx @@ -1,37 +1,111 @@ +'use client'; + +import type { MenuTreeNode } from '@seclusion/shared'; import Link from 'next/link'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { Suspense, useEffect, useState } from 'react'; import { ThemeToggle } from '@/components/layout/ThemeToggle'; import { siteConfig } from '@/config/site'; +import { getUserMenusAndPermissions } from '@/services/permission.service'; +import { useAuthStore, usePermissionStore } from '@/stores'; -export default function AuthLayout({ - children, -}: { - children: React.ReactNode; -}) { +function AuthRedirect() { + const router = useRouter(); + const searchParams = useSearchParams(); + const { token, isHydrated: authHydrated } = useAuthStore(); + const { isLoaded, isExpired, setPermissionData, canAccessRoute, menus } = usePermissionStore(); + const [hasRedirected, setHasRedirected] = useState(false); + + // 加载权限数据 + useEffect(() => { + if (!authHydrated || !token) return; + + if (!isLoaded || isExpired()) { + getUserMenusAndPermissions() + .then((data) => { + setPermissionData(data); + }) + .catch((error) => { + console.error('加载权限数据失败:', error); + setPermissionData({ menus: [], permissions: [], isSuperAdmin: false }); + }); + } + }, [authHydrated, token, isLoaded, isExpired, setPermissionData]); + + // 执行智能重定向(只执行一次) + useEffect(() => { + // 未登录、权限未加载或已经执行过重定向,则不处理 + if (!authHydrated || !token || !isLoaded || hasRedirected) return; + + const targetRedirect = searchParams.get('redirect') || '/dashboard'; + + // 检查目标路由是否可访问 + if (canAccessRoute(targetRedirect)) { + setHasRedirected(true); + router.replace(targetRedirect); + return; + } + + // 目标路由不可访问,找到第一个可访问的菜单路径 + const findFirstAccessiblePath = (menuList: MenuTreeNode[]): string | null => { + for (const menu of menuList) { + // 跳过隐藏菜单、外部链接和没有路径的菜单 + if (!menu.isHidden && menu.path && !menu.isExternal) { + return menu.path; + } + // 递归查找子菜单 + if (menu.children && menu.children.length > 0) { + const childPath = findFirstAccessiblePath(menu.children); + if (childPath) return childPath; + } + } + return null; + }; + + const firstPath = findFirstAccessiblePath(menus); + + setHasRedirected(true); + if (firstPath) { + router.replace(firstPath); + } else { + // 没有任何可访问的路由,跳转到无权限页面 + router.replace('/403'); + } + }, [authHydrated, token, isLoaded, hasRedirected, searchParams, canAccessRoute, menus, router]); + + return null; +} + +export default function AuthLayout({ children }: { children: React.ReactNode }) { return ( -