feat: 初始化 ChatOA 企业协作 OA 系统
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
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<PageKey[]>(() => [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)
|
||||
const assistantId = activeKey === 'ai-chat' ? location.pathname.split('/')[2] : undefined;
|
||||
const lastAssistantIdRef = useRef<string | undefined>(undefined);
|
||||
if (assistantId) lastAssistantIdRef.current = assistantId;
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderKeys.map((key) => {
|
||||
const active = key === activeKey;
|
||||
let page: ReactNode;
|
||||
switch (key) {
|
||||
case 'chat':
|
||||
page = <ChatPage />;
|
||||
break;
|
||||
case 'contacts':
|
||||
page = <ContactsPage />;
|
||||
break;
|
||||
case 'schedule':
|
||||
page = <SchedulePage />;
|
||||
break;
|
||||
case 'forms':
|
||||
page = <FormsPage />;
|
||||
break;
|
||||
case 'knowledge':
|
||||
page = <KnowledgePage />;
|
||||
break;
|
||||
case 'apps':
|
||||
page = <AppsPage />;
|
||||
break;
|
||||
case 'drive':
|
||||
page = <DrivePage />;
|
||||
break;
|
||||
default:
|
||||
page = <AiChatPage assistantId={assistantId ?? lastAssistantIdRef.current} />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
style={{ flex: 1, minWidth: 0, height: '100%', display: active ? 'flex' : 'none' }}
|
||||
>
|
||||
{page}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user