新增纯会话页面,独立路由 /pure-chat

- 新增 ChatPagePure 组件:仅保留聊天主区域(section.chat-main),去掉全局 Sidebar 和 AgentSidebar
- 复用原有的 ChatHeader、ChatBody、ChatInput、ChatOutline、ChatDrawers 组件,会话功能完全一致
- 独立路由 /pure-chat 和 /pure-chat/:id,绕过 layout-shell 全局布局,不显示侧边栏
- 新增样式文件 chat-page-pure.css,适配纯会话页面全屏布局
main
sp mac bookpro 2605 2026-07-31 16:14:07 +08:00
parent b9cf3bc78a
commit 1b4812acae
3 changed files with 203 additions and 1 deletions

View File

@ -16,6 +16,7 @@ import StatsPage from './pages/StatsPage';
import ProfilePage from './pages/ProfilePage'; import ProfilePage from './pages/ProfilePage';
import PricingPage from './pages/PricingPage'; import PricingPage from './pages/PricingPage';
import SharedSessionPage from './pages/SharedSessionPage'; import SharedSessionPage from './pages/SharedSessionPage';
import ChatPagePure from './pages/chat/ChatPagePure';
import WorkflowsPage from './pages/WorkflowsPage'; import WorkflowsPage from './pages/WorkflowsPage';
import KnowledgeBasePage from './pages/KnowledgeBase'; import KnowledgeBasePage from './pages/KnowledgeBase';
import { useAuth } from './store/auth'; import { useAuth } from './store/auth';
@ -83,6 +84,12 @@ export default function App() {
<Route path="/login" element={<LoginPage />} /> <Route path="/login" element={<LoginPage />} />
<Route path="*" element={<LoginPage />} /> <Route path="*" element={<LoginPage />} />
</Routes> </Routes>
) : location.pathname.startsWith('/pure-chat') ? (
<Routes>
{/* 纯会话页:无全局 Sidebar无 AgentSidebar仅保留聊天主区域 */}
<Route path="/pure-chat" element={<ChatPagePure />} />
<Route path="/pure-chat/:id" element={<ChatPagePure />} />
</Routes>
) : ( ) : (
<div className="layout-shell"> <div className="layout-shell">
{/* 只有编辑器全屏显示,其他页面均保留侧边栏 */} {/* 只有编辑器全屏显示,其他页面均保留侧边栏 */}

View File

@ -0,0 +1,179 @@
import { useEffect, useState, useCallback, useRef } from 'react';
import { App as AntApp, Empty } from 'antd';
import type { ChatPageLogicOutput } from './ChatPageLogic';
import { markdownToPlainText } from './utils/copy';
import { useChatPageLogic } from './ChatPageLogic';
import ChatBody from './components/ChatBody';
import ChatDrawers from './components/ChatDrawers';
import ChatHeader from './components/ChatHeader';
import ChatInput from './components/ChatInput';
import ChatOutline from './components/ChatOutline';
import type { ModelOverrides } from '../../api';
import './styles/chat-page-pure.css';
/**
*
* section.chat-main Sidebar AgentSidebarchat-side
*
*/
export default function ChatPagePure() {
const logic = useChatPageLogic();
const { message } = AntApp.useApp();
const [outlineCollapsed, setOutlineCollapsed] = useState(true);
const isAutoScrolling = useRef(false);
const {
id,
roomId,
highlightId,
historyDrawerOpen,
mcpDrawerOpen,
paramsDrawerOpen,
tplDrawerOpen,
agent,
agentList,
messages,
branches,
bodyRef,
sender,
navigate,
overrides,
setOverrides,
setRoomId,
setHighlightId,
setHistoryDrawerOpen,
setMcpDrawerOpen,
setParamsDrawerOpen,
setTplDrawerOpen,
handleNewSession,
} = logic;
// 初始化时,如果有历史消息,激活最后一条 Agent 消息
useEffect(() => {
if (messages.length > 0 && !highlightId) {
const agentMsgs = messages.filter(m => m.role === 'assistant' || m.role === 'agent' || m.speaker?.type === 'agent');
if (agentMsgs.length > 0) {
setHighlightId(agentMsgs[agentMsgs.length - 1].id);
}
}
}, [messages.length]);
const handleJump = useCallback((msgId: string) => {
isAutoScrolling.current = true;
setHighlightId(msgId);
const el = document.getElementById('msg-' + msgId);
if (el) {
el.scrollIntoView({ block: 'start', behavior: 'smooth' });
setTimeout(() => {
isAutoScrolling.current = false;
}, 800);
}
}, [setHighlightId]);
return (
<div className="chat-pure-shell">
<section className="chat-main">
{!agent ? (
<div className="chat-empty-state">
<Empty description="请选择一个智能体开始对话" />
</div>
) : (
<div className="chat-content-layout">
<div className="chat-conversation-panel">
<ChatHeader
agent={agent}
useStream={sender.useStream}
setUseStream={sender.setUseStream}
onOpenHistory={() => setHistoryDrawerOpen(true)}
onOpenParams={() => setParamsDrawerOpen(true)}
onOpenMcp={() => setMcpDrawerOpen(true)}
onManageAgent={() => navigate(`/agents/${id}`)}
onClear={sender.handleClear}
/>
<ChatBody
bodyRef={bodyRef}
agent={agent}
agentList={agentList}
currentAgentId={id!}
messages={messages}
branches={branches}
highlightId={highlightId}
sending={sender.sending}
streaming={sender.streaming}
onRegenerate={sender.handleRegenerate}
onSwitchBranch={sender.handleSwitchBranch}
scrollBottom={logic.scrollBottom}
initialScrollDoneRef={logic.initialScrollDoneRef}
onCopy={(text, mode) => {
const content = mode === 'markdown' ? text : markdownToPlainText(text);
navigator.clipboard?.writeText(content).then(() => {
message.success(mode === 'markdown' ? '已复制Markdown' : '已复制(纯文本)');
});
}}
/>
<ChatInput
input={sender.input}
setInput={sender.setInput}
sending={sender.sending}
attachments={sender.attachments}
setAttachments={sender.setAttachments}
imageUrls={sender.imageUrls}
setImageUrls={sender.setImageUrls}
onSend={sender.handleSend}
onStop={sender.handleStop}
onAttach={sender.handleAttach}
onOpenTpl={() => setTplDrawerOpen(true)}
modelOptions={sender.modelOptions}
activeModelValue={sender.activeModelValue}
onChangeModel={(modelId) => {
const picked = sender.modelOptions.find((o) => o.value === modelId);
setOverrides((o: ModelOverrides) => ({
...o,
model_id: modelId,
model: picked?.label ?? o.model
}));
}}
agentList={agentList}
onInsertMention={() => {}}
onOpenHistory={() => setHistoryDrawerOpen(true)}
onNewSession={handleNewSession}
/>
</div>
<ChatOutline
messages={messages}
activeId={highlightId}
collapsed={outlineCollapsed}
onToggleCollapse={() => setOutlineCollapsed(!outlineCollapsed)}
onJump={handleJump}
/>
</div>
)}
</section>
<ChatDrawers
agentId={id}
agent={agent}
input={sender.input}
setInput={(updater) => sender.setInput(updater)}
overrides={overrides}
setOverrides={setOverrides}
roomId={roomId}
setRoomId={setRoomId}
setHighlightId={setHighlightId}
sessionRefresh={sender.sessionRefresh}
mcpDrawerOpen={mcpDrawerOpen}
setMcpDrawerOpen={setMcpDrawerOpen}
tplDrawerOpen={tplDrawerOpen}
setTplDrawerOpen={setTplDrawerOpen}
paramsDrawerOpen={paramsDrawerOpen}
setParamsDrawerOpen={setParamsDrawerOpen}
historyDrawerOpen={historyDrawerOpen}
setHistoryDrawerOpen={setHistoryDrawerOpen}
notify={{ success: (t) => message.success(t) }}
/>
</div>
);
}

View File

@ -0,0 +1,16 @@
/* 纯会话页面样式 - 无侧边栏,仅保留聊天主区域 */
.chat-pure-shell {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-pure-shell .chat-main {
flex: 1;
height: 100vh;
overflow: hidden;
min-width: 0;
}