From fad4842c079cc68ec4ebadbb5681337bf2e4d2a8 Mon Sep 17 00:00:00 2001 From: charilezhou Date: Sat, 17 Jan 2026 16:22:22 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E6=B7=BB=E5=8A=A0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E7=8A=B6=E6=80=81=E6=8B=A6=E6=88=AA=E5=92=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dashboard layout: 未登录时重定向到登录页,携带 redirect 参数 - sidebar: 移除静态菜单 fallback,仅使用动态权限菜单 - MenuEditDialog: 修复 type 字段类型为 MenuType - RoleEditDialog: 修复 isSystem 属性访问使用 roleDetail Co-Authored-By: Claude Opus 4.5 --- apps/web/src/app/(dashboard)/layout.tsx | 13 +++--- apps/web/src/components/layout/Sidebar.tsx | 25 ++--------- .../src/components/menus/MenuEditDialog.tsx | 45 +++++++++---------- .../src/components/roles/RoleEditDialog.tsx | 2 +- 4 files changed, 30 insertions(+), 55 deletions(-) diff --git a/apps/web/src/app/(dashboard)/layout.tsx b/apps/web/src/app/(dashboard)/layout.tsx index 4993d5e..0289f45 100644 --- a/apps/web/src/app/(dashboard)/layout.tsx +++ b/apps/web/src/app/(dashboard)/layout.tsx @@ -6,26 +6,23 @@ import { useEffect } from 'react'; import { Header } from '@/components/layout/Header'; import { Sidebar, MobileSidebar } from '@/components/layout/Sidebar'; import { getRoutePermission, isPublicRoute } from '@/config/route-permissions'; +import { getLoginUrl } from '@/lib/auth'; import { getUserMenusAndPermissions } from '@/services/permission.service'; import { useAuthStore, usePermissionStore } from '@/stores'; -export default function DashboardLayout({ - children, -}: { - children: React.ReactNode; -}) { +export default function DashboardLayout({ children }: { children: React.ReactNode }) { const pathname = usePathname(); const router = useRouter(); const { token, isHydrated: authHydrated } = useAuthStore(); const { isLoaded, setPermissionData, hasPermission, clearPermissionData } = usePermissionStore(); - // 加载用户权限数据 + // 登录状态检查:未登录时重定向到登录页 useEffect(() => { if (!authHydrated) return; - // 未登录时清空权限数据 if (!token) { clearPermissionData(); + router.replace(getLoginUrl(pathname)); return; } @@ -41,7 +38,7 @@ export default function DashboardLayout({ setPermissionData({ menus: [], permissions: [], isSuperAdmin: false }); }); } - }, [authHydrated, token, isLoaded, setPermissionData, clearPermissionData]); + }, [authHydrated, token, isLoaded, pathname, router, setPermissionData, clearPermissionData]); // 路由权限检查 useEffect(() => { diff --git a/apps/web/src/components/layout/Sidebar.tsx b/apps/web/src/components/layout/Sidebar.tsx index d10fdb6..5a8f5cb 100644 --- a/apps/web/src/components/layout/Sidebar.tsx +++ b/apps/web/src/components/layout/Sidebar.tsx @@ -10,7 +10,6 @@ import { Button } from '@/components/ui/button'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Sheet, SheetContent, SheetTitle } from '@/components/ui/sheet'; -import { sidebarNav } from '@/config/nav'; import { siteConfig } from '@/config/site'; import { getIcon } from '@/lib/icons'; import { cn } from '@/lib/utils'; @@ -182,28 +181,12 @@ function SidebarContent({ collapsed = false, onItemClick }: SidebarContentProps) const pathname = usePathname(); const { menus, isLoaded } = usePermissionStore(); - // 如果权限数据已加载且有菜单,使用动态菜单 - // 否则使用静态菜单作为 fallback + // 只使用动态菜单,未加载时返回空数组 const menuItems = useMemo(() => { - if (isLoaded && menus.length > 0) { - return menus; + if (!isLoaded) { + return []; } - - // 将静态导航转换为 MenuTreeNode 格式 - return sidebarNav.flatMap((group) => - group.items.map((item) => ({ - id: item.href, - parentId: null, - code: item.href, - name: item.title, - type: 'menu' as const, - path: item.href, - icon: item.icon.displayName || null, - permission: null, - isHidden: false, - meta: null, - })) - ); + return menus; }, [isLoaded, menus]); // 将动态菜单按分组渲染(如果有 parentId 则是子菜单) diff --git a/apps/web/src/components/menus/MenuEditDialog.tsx b/apps/web/src/components/menus/MenuEditDialog.tsx index 28fce4f..971f809 100644 --- a/apps/web/src/components/menus/MenuEditDialog.tsx +++ b/apps/web/src/components/menus/MenuEditDialog.tsx @@ -2,7 +2,7 @@ import type { MenuResponse, MenuTreeNode } from '@seclusion/shared'; import { MenuType } from '@seclusion/shared'; -import { useEffect } from 'react'; +import React, { useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { toast } from 'sonner'; @@ -48,7 +48,7 @@ interface MenuFormValues { parentId: string; code: string; name: string; - type: string; + type: MenuType; path: string; icon: string; permission: string; @@ -67,7 +67,8 @@ function renderMenuOptions(menus: MenuTreeNode[], level = 0): React.ReactNode[] const prefix = '\u00A0\u00A0'.repeat(level); options.push( - {prefix}{menu.name} + {prefix} + {menu.name} ); @@ -173,9 +174,7 @@ export function MenuEditDialog({ menu, open, onOpenChange }: MenuEditDialogProps {isEditing ? '编辑菜单' : '新建菜单'} - - {isEditing ? '修改菜单配置。' : '创建新的菜单项。'} - + {isEditing ? '修改菜单配置。' : '创建新的菜单项。'}
@@ -221,11 +220,7 @@ export function MenuEditDialog({ menu, open, onOpenChange }: MenuEditDialogProps 菜单编码 - + @@ -317,7 +312,15 @@ export function MenuEditDialog({ menu, open, onOpenChange }: MenuEditDialogProps - 参考 Lucide Icons + 参考{' '} + + Lucide Icons + @@ -335,9 +338,7 @@ export function MenuEditDialog({ menu, open, onOpenChange }: MenuEditDialogProps - - 关联的权限编码,格式为 资源:操作 - + 关联的权限编码,格式为 资源:操作 )} @@ -350,12 +351,9 @@ export function MenuEditDialog({ menu, open, onOpenChange }: MenuEditDialogProps render={({ field }) => ( - + - 隐藏菜单 + 隐藏菜单 )} /> @@ -366,12 +364,9 @@ export function MenuEditDialog({ menu, open, onOpenChange }: MenuEditDialogProps render={({ field }) => ( - + - 启用 + 启用 )} /> diff --git a/apps/web/src/components/roles/RoleEditDialog.tsx b/apps/web/src/components/roles/RoleEditDialog.tsx index 5c96d81..74774cf 100644 --- a/apps/web/src/components/roles/RoleEditDialog.tsx +++ b/apps/web/src/components/roles/RoleEditDialog.tsx @@ -184,7 +184,7 @@ export function RoleEditDialog({ role, open, onOpenChange }: RoleEditDialogProps 唯一标识,创建后不可修改