import React, { useState, useEffect } from 'react'; import { Sidebar } from './Sidebar'; // import { Header } from './Header'; import { Breadcrumb } from './Breadcrumb'; import { useMatches, useLocation } from '@remix-run/react'; import type { UserRole } from '~/root'; interface LayoutProps { children: React.ReactNode; userRole?: UserRole; frontendJWT?: string; isMobile?: boolean; // 是否为移动端设备(服务端通过 User-Agent 检测) } // 添加一个接口表示路由handle可能包含的属性 interface RouteHandle { hideBreadcrumb?: boolean; collapseSidebar?: boolean; noPadding?: boolean; [key: string]: unknown; } interface Match { handle?: RouteHandle; pathname: string; data: unknown; } type RulesTestDetailData = { pack?: { documentType?: string; mainType?: string; fields?: unknown[]; subDocuments?: unknown[]; visualElements?: unknown[]; }; }; export function Layout({ children, userRole = 'developer' as UserRole, frontendJWT = '', isMobile = false }: LayoutProps) { const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [effectiveUserRole, setEffectiveUserRole] = useState(userRole); const [effectiveFrontendJWT, setEffectiveFrontendJWT] = useState(frontendJWT); const matches = useMatches() as Match[]; const location = useLocation(); // 检查当前路径是否应该隐藏侧边栏 const noLayoutPaths = ['/login', '/']; // 移动端设备强制隐藏侧边栏 const shouldHideSidebar = isMobile || noLayoutPaths.includes(location.pathname); // 检查当前路由是否应该隐藏默认面包屑 // 移动端设备强制隐藏面包屑(避免显示首页链接) const shouldHideBreadcrumb = isMobile || shouldHideSidebar || matches.some(match => match.handle && match.handle.hideBreadcrumb === true ); // 检查当前路由是否要求无 padding const shouldNoPadding = matches.some(match => match.handle && match.handle.noPadding === true ); // 从 localStorage 读取用户信息和 JWT 作为备用方案 useEffect(() => { if (typeof window === 'undefined') return; try { // 如果服务端没有传递 userRole,从 localStorage 读取 if (!userRole || userRole === '') { const storedUserInfoStr = localStorage.getItem('user_info'); if (storedUserInfoStr) { const storedUserInfo = JSON.parse(storedUserInfoStr); const storedUserRole = storedUserInfo.user_role || 'common'; console.log('📖 [Layout] 从 localStorage 读取用户角色:', storedUserRole); setEffectiveUserRole(storedUserRole as UserRole); } } else { setEffectiveUserRole(userRole); } // 如果服务端没有传递 frontendJWT,从 localStorage 读取 if (!frontendJWT || frontendJWT === '') { const storedToken = localStorage.getItem('access_token'); if (storedToken) { console.log('📖 [Layout] 从 localStorage 读取 JWT token'); setEffectiveFrontendJWT(storedToken); } } else { setEffectiveFrontendJWT(frontendJWT); } } catch (error) { console.error('❌ [Layout] 读取 localStorage 失败:', error); } }, [userRole, frontendJWT]); // 从localStorage中获取侧边栏状态 useEffect(() => { // 检查是否为移动端 const isMobile = window.innerWidth <= 768; // 检查当前路由是否要求收缩侧边栏 const shouldCollapse = matches.some(match => match.handle && match.handle.collapseSidebar === true ); if (isMobile) { setSidebarCollapsed(true); } else if (shouldCollapse) { setSidebarCollapsed(true); } else { // 从localStorage获取侧边栏状态 const savedState = localStorage.getItem('sidebarCollapsed'); if (savedState) { setSidebarCollapsed(savedState === 'true'); } } }, [location.pathname]); const toggleSidebar = () => { const newState = !sidebarCollapsed; setSidebarCollapsed(newState); localStorage.setItem('sidebarCollapsed', String(newState)); }; // 切换应用模块 // const changeAppModule = (appId: AppModule) => { // setSelectedApp(appId); // localStorage.setItem('selectedApp', appId); // }; // 如果是无布局页面,只渲染内容 if (shouldHideSidebar) { return <>{children}; } const isRulesTestDetail = location.pathname.startsWith('/rulesTest/detail'); const isRulesTestTopbarPage = isRulesTestDetail; const rulesTestDetailData = matches.find(match => match.pathname.startsWith('/rulesTest/detail'))?.data as RulesTestDetailData | undefined; const detailPack = rulesTestDetailData?.pack; const detailPackFilterMainType = detailPack?.businessType || detailPack?.mainType || ''; const isContractDetail = !!detailPack?.documentType?.includes('合同'); const isCaseFileDetail = !!detailPack?.documentType?.includes('案卷'); const showFieldNav = isContractDetail && (detailPack?.fields?.length || 0) > 0; const showSubDocumentNav = isCaseFileDetail && (detailPack?.subDocuments?.length || 0) > 0; const showVisualNav = (detailPack?.visualElements?.length || 0) > 0; const rulesListHref = detailPack?.documentType ? `/rulesTest/list?documentType=${encodeURIComponent(detailPack.documentType)}${detailPackFilterMainType ? `&mainType=${encodeURIComponent(detailPackFilterMainType)}` : ''}` : '/rulesTest/list'; return (
{/* 侧边栏始终保留,不再使用条件渲染 */} {/* 规则详情页顶部栏 */} {isRulesTestDetail && (

规则配置详情

规则列表 / 配置详情
{showFieldNav && 字段抽取} {showSubDocumentNav && 案卷文书} {showVisualNav && 视觉要素} 评查规则
)}
{/* 应用模块选择器 */} {/*
{APP_MODULES.map(app => ( ))}
*/}
{!shouldHideBreadcrumb && !isRulesTestTopbarPage && } {children}
); }