import { useEffect, useRef, useState } from 'react'; import type { ReactNode } from 'react'; import { useLocation } from 'react-router-dom'; import ChatPage from '../pages/Chat'; import ContactsPage from '../pages/Contacts'; import SchedulePage from '../pages/Schedule'; import FormsPage from '../pages/Forms'; import KnowledgePage from '../pages/Knowledge'; import AppsPage from '../pages/Apps'; import DrivePage from '../pages/Drive'; import AiChatPage from '../pages/AiChat'; type PageKey = | 'chat' | 'contacts' | 'schedule' | 'forms' | 'knowledge' | 'apps' | 'drive' | 'ai-chat'; function resolveKey(pathname: string): PageKey { if (pathname.startsWith('/chat')) return 'chat'; if (pathname.startsWith('/contacts')) return 'contacts'; if (pathname.startsWith('/schedule')) return 'schedule'; if (pathname.startsWith('/forms')) return 'forms'; if (pathname.startsWith('/knowledge')) return 'knowledge'; if (pathname.startsWith('/apps')) return 'apps'; if (pathname.startsWith('/drive')) return 'drive'; if (pathname.startsWith('/ai-chat')) return 'ai-chat'; return 'chat'; } /** * Keep-Alive 出口:1-7 模块页面(聊天/联系人/日程/表单/笔记/应用/网盘 + AI 对话) * 切换时保持组件挂载,非当前页面用 display:none 隐藏,避免路由切换导致卸载、编辑内容丢失。 * 懒挂载:首次访问某页面时才创建实例,之后一直保留;滚动位置、输入内容等状态均不丢失。 */ export default function KeepAliveOutlet() { const location = useLocation(); const activeKey = resolveKey(location.pathname); const [mountedKeys, setMountedKeys] = useState(() => [activeKey]); // 懒挂载:路由变化时把当前页加入已挂载列表。 // 注意:不能在 render 期间直接 setState(React 18 并发渲染会丢弃 render-phase update, // 导致从 /chat 切换其他页面时停留在旧渲染、内容空白),必须通过 effect 更新。 useEffect(() => { setMountedKeys((prev) => (prev.includes(activeKey) ? prev : [...prev, activeKey])); }, [activeKey]); // 渲染时始终包含当前页(首次切换时立即显示,effect 提交后再持久化) const renderKeys = mountedKeys.includes(activeKey) ? mountedKeys : [...mountedKeys, activeKey]; // AI 对话页的助手 id 从路径解析(常驻后不再依赖 Route 的 useParams) // /ai-chat(无 id)时兜底默认助手 'assistant',与原 Navigate 重定向行为一致 const assistantId = activeKey === 'ai-chat' ? location.pathname.split('/')[2] || 'assistant' : undefined; const lastAssistantIdRef = useRef(undefined); if (assistantId) lastAssistantIdRef.current = assistantId; return ( <> {renderKeys.map((key) => { const active = key === activeKey; let page: ReactNode; switch (key) { case 'chat': page = ; break; case 'contacts': page = ; break; case 'schedule': page = ; break; case 'forms': page = ; break; case 'knowledge': page = ; break; case 'apps': page = ; break; case 'drive': page = ; break; default: page = ; } return (
{page}
); })} ); }