import { useState, useEffect } from 'react'; import { Link, useLocation, useNavigate } from '@remix-run/react'; import type { UserRole } from '~/root'; import { getUserRoutesByRole, mapUserRoleToRoleKey, type MenuItem } from '~/api/auth/user-routes'; interface SidebarProps { onToggle: () => void; collapsed: boolean; userRole: UserRole; frontendJWT?: string; } export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: SidebarProps) { const location = useLocation(); const [expandedMenus, setExpandedMenus] = useState>({}); const [menuItems, setMenuItems] = useState([]); // 动态菜单项 const [isLoadingRoutes, setIsLoadingRoutes] = useState(true); // 路由加载状态 const [isMobile, setIsMobile] = useState(false); // 移动端检测 const [selectedModuleName, setSelectedModuleName] = useState(''); // 当前选中的模块名称 const [selectedModulePicPath, setSelectedModulePicPath] = useState(''); // 当前选中的模块图片路径 const navigate = useNavigate(); // 移动端检测 useEffect(() => { const checkMobile = () => { const mobile = window.innerWidth <= 768; // 768px以下视为移动端 setIsMobile(mobile); }; // 初始检测 checkMobile(); // 监听窗口大小变化 window.addEventListener('resize', checkMobile); return () => window.removeEventListener('resize', checkMobile); }, []); // 获取用户路由权限 useEffect(() => { // console.log('🔍 [Sidebar] useEffect 触发,开始获取路由权限'); const fetchUserRoutes = async () => { setIsLoadingRoutes(true); try { // 优先使用传入的 frontendJWT,否则从 localStorage 读取 let jwt = frontendJWT; if (!jwt && typeof window !== 'undefined') { jwt = localStorage.getItem('access_token') || ''; console.log('📖 [Sidebar] 从 localStorage 读取 JWT'); } if (!jwt) { console.error('❌ [Sidebar] JWT token 未找到,props.frontendJWT:', frontendJWT, 'localStorage.access_token:', localStorage.getItem('access_token')); setMenuItems([]); setIsLoadingRoutes(false); return; } // console.log('🔍 [Sidebar] 当前用户角色:', userRole, 'JWT前20字符:', jwt.substring(0, 20)); // console.log('🔍 [Sidebar] 映射后的角色key:', roleKey); const result = await getUserRoutesByRole(userRole, jwt); if (result.success && result.data) { setMenuItems(result.data); // console.log('✅ [Sidebar] 用户路由权限加载成功:', result.data); } else { console.error('❌ [Sidebar] 获取用户路由权限失败:', result.error); // 如果需要重定向到首页 if (result.shouldRedirectToHome) { // console.log('🔄 [Sidebar] 重定向到首页'); navigate('/'); return; } // 其他错误情况,使用空数组 setMenuItems([]); } } catch (error) { console.error('❌ [Sidebar] 获取用户路由权限时发生错误:', error); // 发生异常时也重定向到首页 navigate('/'); return; } finally { setIsLoadingRoutes(false); } }; fetchUserRoutes(); }, [userRole, frontendJWT, navigate]); // 🔑 检查是否处于系统设置模式 const [isSettingsMode, setIsSettingsMode] = useState(false); // 从 sessionStorage 读取当前选中的模块名称和图片路径,以及系统设置模式标志 useEffect(() => { if (typeof window !== 'undefined') { try { const moduleName = sessionStorage.getItem('selectedModuleName'); const modulePicPath = sessionStorage.getItem('selectedModulePicPath'); const settingsMode = sessionStorage.getItem('settingsMode'); if (moduleName) { setSelectedModuleName(moduleName); console.log('📌 [Sidebar] 当前选中模块:', moduleName); } if (modulePicPath) { setSelectedModulePicPath(modulePicPath); console.log('🖼️ [Sidebar] 模块图片路径:', modulePicPath); } // 🔑 检查是否处于系统设置模式 if (settingsMode === 'true') { setIsSettingsMode(true); console.log('⚙️ [Sidebar] 进入系统设置模式'); } else { setIsSettingsMode(false); } } catch (error) { console.error('❌ [Sidebar] 读取 sessionStorage 失败:', error); } } }, [location.pathname]); // 路由变化时重新读取 // 初始化展开状态,默认全部展开 useEffect(() => { const initialExpandedState: Record = {}; menuItems.forEach(item => { if (item.children) { initialExpandedState[item.id] = true; } }); setExpandedMenus(initialExpandedState); }, [menuItems]); const toggleMenu = (id: string, e: React.MouseEvent) => { // 我们只防止事件冒泡,不阻止默认行为 e.stopPropagation(); // console.log('父菜单展开/折叠:', id); setExpandedMenus(prev => ({ ...prev, [id]: !prev[id] })); }; const isActive = (path: string) => { return location.pathname === path || location.pathname.startsWith(`${path}/`); }; // 处理侧边栏切换事件 const handleToggleSidebar = (e: React.MouseEvent) => { // console.log('侧边栏折叠/展开'); // 只防止事件冒泡,不阻止默认行为 e.stopPropagation(); onToggle(); }; // 处理子菜单项点击事件 const handleSubMenuClick = (child: MenuItem, e: React.MouseEvent) => { // 只需要阻止冒泡,不阻止默认行为 e.stopPropagation(); // console.log('子菜单点击:', child.title, '路径:', child.path); }; // const isPort51707 = typeof window !== 'undefined' && window.location.port === '51707' // 处理菜单项:清理子菜单结构 const processedMenuItems: MenuItem[] = menuItems.filter(item =>{ // console.log('菜单项:', item.title, 'Icon:', item.icon) // 🔑 优先检查:如果处于系统设置模式,只显示 /settings 及其子路由 if (isSettingsMode) { return item.path === '/settings' || item.path?.startsWith('/settings/'); } // 🔑 重要:非系统设置模式下,隐藏所有 /settings 相关菜单 if (item.path === '/settings' || item.path?.startsWith('/settings/')) { return false; } // 如果是省局访问 // if(isPort51707){ // if (selectedModuleName === '智慧法务大模型'){ // return item.path && item.path.startsWith('/chat-with-llm') // } // return item.path && item.path.startsWith('/cross-checking') // } // 🔑 如果选择了"智慧法务大模型",只显示 /chat-with-llm 相关菜单 if (selectedModuleName === '智慧法务大模型') { return item.path === '/chat-with-llm' || item.path?.startsWith('/chat-with-llm/'); } // 🔑 如果选择了包含"合同"的模块 if (selectedModuleName.includes('合同')) { // 排除智慧法务大模型专属菜单 if (item.path === '/chat-with-llm' || item.path?.startsWith('/chat-with-llm/')) { return false; } // 保留其他所有菜单(包括 /contract-template) return true; } // 🔑 其他模块:排除特殊菜单 // 排除智慧法务大模型专属菜单 if (item.path === '/chat-with-llm' || item.path?.startsWith('/chat-with-llm/')) { return false; } // 排除合同专属菜单 if (item.path === '/contract-template' || item.path?.startsWith('/contract-template/')) { return false; } // 保留其他菜单 return true; }).map((item): MenuItem => { // 处理子菜单:过滤隐藏的子菜单 if (item.children && item.children.length > 0) { // 过滤掉 hideBreadcrumb=true 的子菜单(这些通常是隐藏菜单) const visibleChildren = item.children.filter(child => !child.hideBreadcrumb); // 如果过滤后没有可见的子菜单,返回不带子菜单的父级(变成可点击的单级菜单) if (visibleChildren.length === 0) { const { children, ...itemWithoutChildren } = item; return itemWithoutChildren as MenuItem; } // 如果还有可见的子菜单,返回带过滤后子菜单的项 return { ...item, children: visibleChildren }; } // 处理空 children 数组或 undefined 的情况 if (item.children !== undefined) { // 如果 children 存在但为空数组,移除它(让父级变成可点击的单级菜单) const { children, ...itemWithoutChildren } = item; return itemWithoutChildren as MenuItem; } // 没有子菜单的项直接返回 return item; }); return ( <> {/* 移动端遮罩层 */} {isMobile && !collapsed && (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggle(); } }} /> )}
{ navigate('/'); }} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); navigate('/'); } }} > 智慧法务 {!collapsed &&

智慧法务

}
{/* 显示入口模块的名称 */} {selectedModuleName && (
{selectedModulePicPath && ( {selectedModuleName} )} {!collapsed && ( {selectedModuleName} )}
)}
{isLoadingRoutes ? ( // 加载中状态显示,保留菜单布局结构
{Array(5).fill(0).map((_, index) => (
{!collapsed &&
}
))}
) : ( // 数据加载完成后显示菜单 processedMenuItems.map((item) => (
{!item.children ? ( { // 只阻止冒泡,不阻止默认行为 e.stopPropagation(); // console.log('单级菜单点击:', item.title, '路径:', item.path); }} > {!collapsed && {item.title}} ) : ( <>
{ // console.log('%c父菜单点击 ===> ', 'background: #722ed1; color: white; padding: 2px 4px; border-radius: 2px;', item.title); toggleMenu(item.id, e); }} role="button" tabIndex={0} aria-expanded={expandedMenus[item.id] || false} aria-controls={`submenu-${item.id}`} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleMenu(item.id, (e as unknown) as React.MouseEvent); } }} >
{!collapsed && {item.title}}
{!collapsed && ( )}
{(expandedMenus[item.id] || collapsed) && (
{item.children.map((child) => ( handleSubMenuClick(child, e)} > {!collapsed && {child.title}} ))}
)} )}
)) )}
{/* 操作手册下载按钮 */}
); }