@@ -100,10 +110,13 @@ export default function ChatBody(props: {
正式回答
{streaming.answerText ?
{streaming.answerText + '▍'} :
等待输出…}
diff --git a/src/pages/chat/components/ChatPageWeb.tsx b/src/pages/chat/components/ChatPageWeb.tsx
index f7063d1..1a57816 100644
--- a/src/pages/chat/components/ChatPageWeb.tsx
+++ b/src/pages/chat/components/ChatPageWeb.tsx
@@ -55,54 +55,8 @@ export default function ChatPageWeb({ logic }: { logic: ChatPageLogicOutput }) {
}
}, [messages.length]);
- // 2. 监听滚动,自动更新大纲激活态
- useEffect(() => {
- const bodyEl = bodyRef.current;
- if (!bodyEl) return;
-
- const handleScroll = () => {
- if (isAutoScrolling.current) return;
-
- const messageEls = bodyEl.querySelectorAll('.message-item-container[data-is-agent="true"]');
- if (messageEls.length === 0) return;
-
- // 检查是否滚动到底部
- const isAtBottom = bodyEl.scrollHeight - bodyEl.scrollTop <= bodyEl.clientHeight + 100;
- if (isAtBottom) {
- const agentMsgs = messages.filter(m => m.role === 'assistant' || m.role === 'agent' || m.speaker?.type === 'agent');
- const lastAgentMsgId = agentMsgs[agentMsgs.length - 1]?.id;
- if (lastAgentMsgId && lastAgentMsgId !== highlightId) {
- setHighlightId(lastAgentMsgId);
- }
- return;
- }
-
- let currentActiveId = highlightId;
- const containerRect = bodyEl.getBoundingClientRect();
-
- // 找到当前视口中占据主要位置的 Agent 消息
- for (let i = 0; i < messageEls.length; i++) {
- const el = messageEls[i] as HTMLElement;
- const rect = el.getBoundingClientRect();
-
- // 判定准则:消息头部进入视口顶部感应区
- if (rect.top >= containerRect.top - 20 && rect.top <= containerRect.top + 150) {
- const idAttr = el.getAttribute('data-msg-id');
- if (idAttr) {
- currentActiveId = idAttr;
- break;
- }
- }
- }
-
- if (currentActiveId && currentActiveId !== highlightId) {
- setHighlightId(currentActiveId);
- }
- };
-
- bodyEl.addEventListener('scroll', handleScroll);
- return () => bodyEl.removeEventListener('scroll', handleScroll);
- }, [messages, highlightId]);
+ // 2. 移除滚动监听逻辑,避免干扰正常滚动
+ // 后续若需恢复,需重新设计非阻塞的判定算法
const handleJump = useCallback((msgId: string) => {
isAutoScrolling.current = true;
diff --git a/src/pages/chat/components/messages/MessageItem.css b/src/pages/chat/components/messages/MessageItem.css
index 65fec0c..374450f 100644
--- a/src/pages/chat/components/messages/MessageItem.css
+++ b/src/pages/chat/components/messages/MessageItem.css
@@ -111,3 +111,45 @@
.bubble.assistant .markdown p {
color: var(--color-text);
}
+
+.message-reasoning-section {
+ display: flex;
+ flex-direction: column;
+}
+
+.reasoning-container {
+ margin: 4px 0;
+ transition: all 0.3s ease;
+}
+
+.reasoning-header {
+ padding: 4px 0;
+ border-radius: 4px;
+ transition: background-color 0.2s;
+}
+
+.reasoning-header:hover {
+ background-color: var(--color-surface-2);
+}
+
+.reasoning-content {
+ margin-top: 8px;
+ animation: reasoningFadeIn 0.3s ease-out;
+}
+
+@keyframes reasoningFadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(-4px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.message-section-label {
+ font-size: 12px;
+ color: var(--color-text-tertiary);
+ margin-bottom: 6px;
+}
diff --git a/src/pages/chat/components/messages/MessageItem.tsx b/src/pages/chat/components/messages/MessageItem.tsx
index fde8031..c7da77e 100644
--- a/src/pages/chat/components/messages/MessageItem.tsx
+++ b/src/pages/chat/components/messages/MessageItem.tsx
@@ -1,5 +1,5 @@
-import { Button, Dropdown, Space, Tag, Tooltip, Avatar } from 'antd';
-import { useMemo } from 'react';
+import { Button, Dropdown, Space, Tag, Tooltip, Avatar, Divider } from 'antd';
+import { useMemo, useState } from 'react';
import { CopyOutlined, SyncOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import type { BranchInfo, ChatMessage } from '../../../../api';
@@ -24,6 +24,8 @@ export default function MessageItem(props: {
}) {
const { message, agentList, currentAgentId, highlighted, branch, busy, onRegenerate, onSwitchBranch, onCopy, isMobile } = props;
+ const [reasoningExpanded, setReasoningExpanded] = useState(false);
+
const formattedContent = useMemo(() => formatMessageContent(message.content), [message.content]);
const speakerType = (message as any)?.speaker?.type as ('user' | 'agent' | undefined);
@@ -85,7 +87,20 @@ export default function MessageItem(props: {
__html: formattedContent.replace(/@([^\s]+)/g, '
@$1')
}} />
) : (
-
{formattedContent}
+
+ {/* 如果有推理过程且不是用户消息,展示推理部分 */}
+ {!isUser && message.meta?.reasoning && (
+
+
setReasoningExpanded(!reasoningExpanded)}
+ />
+ {reasoningExpanded && }
+
+ )}
+
{formattedContent}
+
)}
@@ -124,13 +139,14 @@ export default function MessageItem(props: {
)}