aura-web/src/pages/chat/ChatPagePure.tsx

194 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { useEffect, useState, useCallback, useRef } from 'react';
import { App as AntApp, Empty, Modal } 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 { useAuth } from '../../store/auth';
import './styles/chat-page-pure.css';
/**
* 纯会话页面组件
* 仅保留聊天主区域section.chat-main去掉全局 Sidebar 和 AgentSidebarchat-side
* 会话功能与原聊天页面完全一致
*/
export default function ChatPagePure() {
const logic = useChatPageLogic();
const { message } = AntApp.useApp();
const { logout } = useAuth();
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]);
// 退出登录
const handleLogout = () => {
Modal.confirm({
title: '确定要退出登录吗?',
onOk: async () => {
await logout();
window.location.href = '/login';
},
});
};
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}
onLogout={handleLogout}
/>
<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>
);
}