update pay function

This commit is contained in:
2025-11-22 11:49:20 +08:00
parent 42b7d2ee63
commit 47be4584f9

View File

@@ -1,6 +1,6 @@
// src/views/AgentChat/index.js // src/views/AgentChat/index.js
// 超炫酷的 AI 投研助手 - Hero UI 版本 // 超炫酷的 AI 投研助手 - Hero UI 版本
// 模仿 Google AI Studio 风格 // 使用 CSS 动画替代 Framer Motion避免版本冲突
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { import {
@@ -22,20 +22,15 @@ import {
Tab, Tab,
ScrollShadow, ScrollShadow,
Kbd, Kbd,
Snippet,
Accordion, Accordion,
AccordionItem, AccordionItem,
} from '@heroui/react'; } from '@heroui/react';
import { motion, AnimatePresence } from 'framer-motion';
import { useAuth } from '@contexts/AuthContext'; import { useAuth } from '@contexts/AuthContext';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import axios from 'axios'; import axios from 'axios';
import { useToast } from '@chakra-ui/react'; // 继续使用 Chakra 的 Toast import { useToast } from '@chakra-ui/react';
// Framer Motion 包装组件 // 图标 - 使用 Lucide Icons
const MotionDiv = motion.div;
// 图标 - 使用 Hero UI 推荐的 Lucide Icons
import { import {
Send, Send,
Plus, Plus,
@@ -156,16 +151,13 @@ const AgentChat = () => {
// ==================== API 调用函数 ==================== // ==================== API 调用函数 ====================
// 加载会话列表
const loadSessions = async () => { const loadSessions = async () => {
if (!user?.id) return; if (!user?.id) return;
setIsLoadingSessions(true); setIsLoadingSessions(true);
try { try {
const response = await axios.get('/mcp/agent/sessions', { const response = await axios.get('/mcp/agent/sessions', {
params: { user_id: user.id, limit: 50 }, params: { user_id: user.id, limit: 50 },
}); });
if (response.data.success) { if (response.data.success) {
setSessions(response.data.data); setSessions(response.data.data);
} }
@@ -176,15 +168,12 @@ const AgentChat = () => {
} }
}; };
// 加载会话历史
const loadSessionHistory = async (sessionId) => { const loadSessionHistory = async (sessionId) => {
if (!sessionId) return; if (!sessionId) return;
try { try {
const response = await axios.get(`/mcp/agent/history/${sessionId}`, { const response = await axios.get(`/mcp/agent/history/${sessionId}`, {
params: { limit: 100 }, params: { limit: 100 },
}); });
if (response.data.success) { if (response.data.success) {
const history = response.data.data; const history = response.data.data;
const formattedMessages = history.map((msg, idx) => ({ const formattedMessages = history.map((msg, idx) => ({
@@ -195,7 +184,6 @@ const AgentChat = () => {
stepResults: msg.steps ? JSON.parse(msg.steps) : null, stepResults: msg.steps ? JSON.parse(msg.steps) : null,
timestamp: msg.timestamp, timestamp: msg.timestamp,
})); }));
setMessages(formattedMessages); setMessages(formattedMessages);
} }
} catch (error) { } catch (error) {
@@ -203,7 +191,6 @@ const AgentChat = () => {
} }
}; };
// 创建新会话
const createNewSession = () => { const createNewSession = () => {
setCurrentSessionId(null); setCurrentSessionId(null);
setMessages([ setMessages([
@@ -216,13 +203,11 @@ const AgentChat = () => {
]); ]);
}; };
// 切换会话
const switchSession = (sessionId) => { const switchSession = (sessionId) => {
setCurrentSessionId(sessionId); setCurrentSessionId(sessionId);
loadSessionHistory(sessionId); loadSessionHistory(sessionId);
}; };
// 发送消息
const handleSendMessage = async () => { const handleSendMessage = async () => {
if (!inputValue.trim() || isProcessing) return; if (!inputValue.trim() || isProcessing) return;
@@ -238,14 +223,12 @@ const AgentChat = () => {
setIsProcessing(true); setIsProcessing(true);
try { try {
// 显示思考状态
addMessage({ addMessage({
type: MessageTypes.AGENT_THINKING, type: MessageTypes.AGENT_THINKING,
content: '正在分析你的问题...', content: '正在分析你的问题...',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}); });
// 调用后端 API
const response = await axios.post('/mcp/agent/chat', { const response = await axios.post('/mcp/agent/chat', {
message: userInput, message: userInput,
conversation_history: messages conversation_history: messages
@@ -262,17 +245,14 @@ const AgentChat = () => {
tools: selectedTools, tools: selectedTools,
}); });
// 移除思考消息
setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_THINKING)); setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_THINKING));
if (response.data.success) { if (response.data.success) {
const data = response.data; const data = response.data;
if (data.session_id && !currentSessionId) { if (data.session_id && !currentSessionId) {
setCurrentSessionId(data.session_id); setCurrentSessionId(data.session_id);
} }
// 显示执行计划
if (data.plan) { if (data.plan) {
addMessage({ addMessage({
type: MessageTypes.AGENT_PLAN, type: MessageTypes.AGENT_PLAN,
@@ -282,7 +262,6 @@ const AgentChat = () => {
}); });
} }
// 显示执行步骤
if (data.steps && data.steps.length > 0) { if (data.steps && data.steps.length > 0) {
addMessage({ addMessage({
type: MessageTypes.AGENT_EXECUTING, type: MessageTypes.AGENT_EXECUTING,
@@ -293,10 +272,8 @@ const AgentChat = () => {
}); });
} }
// 移除执行中消息
setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING)); setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
// 显示最终结果
addMessage({ addMessage({
type: MessageTypes.AGENT_RESPONSE, type: MessageTypes.AGENT_RESPONSE,
content: data.final_answer || data.message || '处理完成', content: data.final_answer || data.message || '处理完成',
@@ -310,7 +287,6 @@ const AgentChat = () => {
} }
} catch (error) { } catch (error) {
logger.error('Agent chat error', error); logger.error('Agent chat error', error);
setMessages((prev) => setMessages((prev) =>
prev.filter( prev.filter(
(m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING (m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
@@ -318,7 +294,6 @@ const AgentChat = () => {
); );
const errorMessage = error.response?.data?.error || error.message || '处理失败'; const errorMessage = error.response?.data?.error || error.message || '处理失败';
addMessage({ addMessage({
type: MessageTypes.ERROR, type: MessageTypes.ERROR,
content: `处理失败:${errorMessage}`, content: `处理失败:${errorMessage}`,
@@ -338,12 +313,10 @@ const AgentChat = () => {
} }
}; };
// 添加消息
const addMessage = (message) => { const addMessage = (message) => {
setMessages((prev) => [...prev, { ...message, id: Date.now() + Math.random() }]); setMessages((prev) => [...prev, { ...message, id: Date.now() + Math.random() }]);
}; };
// 自动滚动到底部
const scrollToBottom = () => { const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}; };
@@ -352,7 +325,6 @@ const AgentChat = () => {
scrollToBottom(); scrollToBottom();
}, [messages]); }, [messages]);
// 处理键盘事件
const handleKeyPress = (e) => { const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) { if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault(); e.preventDefault();
@@ -360,7 +332,6 @@ const AgentChat = () => {
} }
}; };
// 初始化
useEffect(() => { useEffect(() => {
if (user) { if (user) {
loadSessions(); loadSessions();
@@ -368,13 +339,11 @@ const AgentChat = () => {
} }
}, [user]); }, [user]);
// 筛选会话
const filteredSessions = sessions.filter( const filteredSessions = sessions.filter(
(session) => (session) =>
!searchQuery || session.last_message?.toLowerCase().includes(searchQuery.toLowerCase()) !searchQuery || session.last_message?.toLowerCase().includes(searchQuery.toLowerCase())
); );
// 快捷问题
const quickQuestions = [ const quickQuestions = [
{ text: '全面分析贵州茅台', emoji: '📊' }, { text: '全面分析贵州茅台', emoji: '📊' },
{ text: '今日涨停股票分析', emoji: '🔥' }, { text: '今日涨停股票分析', emoji: '🔥' },
@@ -384,17 +353,9 @@ const AgentChat = () => {
return ( return (
<div className="flex h-screen bg-gradient-to-br from-gray-50 to-blue-50 dark:from-gray-900 dark:to-gray-800 overflow-hidden"> <div className="flex h-screen bg-gradient-to-br from-gray-50 to-blue-50 dark:from-gray-900 dark:to-gray-800 overflow-hidden">
{/* 左侧栏 - 历史记录 */} {/* 左侧栏 */}
<AnimatePresence>
{isLeftSidebarOpen && ( {isLeftSidebarOpen && (
<MotionDiv <div className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-r border-gray-200 dark:border-gray-700 shadow-xl animate-slide-in-left">
initial={{ x: -320, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: -320, opacity: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-r border-gray-200 dark:border-gray-700 shadow-xl"
>
{/* 侧边栏头部 */}
<div className="p-4 border-b border-gray-200 dark:border-gray-700"> <div className="p-4 border-b border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -431,7 +392,6 @@ const AgentChat = () => {
/> />
</div> </div>
{/* 会话列表 */}
<ScrollShadow className="flex-1 p-2"> <ScrollShadow className="flex-1 p-2">
{isLoadingSessions ? ( {isLoadingSessions ? (
<div className="flex items-center justify-center h-40"> <div className="flex items-center justify-center h-40">
@@ -444,18 +404,13 @@ const AgentChat = () => {
</div> </div>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
{filteredSessions.map((session, index) => ( {filteredSessions.map((session) => (
<MotionDiv
key={session.session_id}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: index * 0.05 }}
>
<Card <Card
key={session.session_id}
isPressable isPressable
isHoverable isHoverable
onPress={() => switchSession(session.session_id)} onPress={() => switchSession(session.session_id)}
className={`${ className={`transition-all ${
currentSessionId === session.session_id currentSessionId === session.session_id
? 'bg-primary-50 dark:bg-primary-900/20 border-primary-500' ? 'bg-primary-50 dark:bg-primary-900/20 border-primary-500'
: '' : ''
@@ -488,13 +443,11 @@ const AgentChat = () => {
</div> </div>
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
))} ))}
</div> </div>
)} )}
</ScrollShadow> </ScrollShadow>
{/* 用户信息 */}
<div className="p-4 border-t border-gray-200 dark:border-gray-700"> <div className="p-4 border-t border-gray-200 dark:border-gray-700">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Avatar src={user?.avatar} name={user?.nickname} size="sm" /> <Avatar src={user?.avatar} name={user?.nickname} size="sm" />
@@ -504,18 +457,12 @@ const AgentChat = () => {
</div> </div>
</div> </div>
</div> </div>
</MotionDiv> </div>
)} )}
</AnimatePresence>
{/* 中间主聊天区域 */} {/* 中间主聊天区域 */}
<div className="flex-1 flex flex-col"> <div className="flex-1 flex flex-col">
{/* 聊天头部 */} <div className="bg-white/70 dark:bg-gray-800/70 backdrop-blur-xl border-b border-gray-200 dark:border-gray-700 px-6 py-4 shadow-sm">
<MotionDiv
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
className="bg-white/70 dark:bg-gray-800/70 backdrop-blur-xl border-b border-gray-200 dark:border-gray-700 px-6 py-4 shadow-sm"
>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{!isLeftSidebarOpen && ( {!isLeftSidebarOpen && (
@@ -529,17 +476,12 @@ const AgentChat = () => {
</Button> </Button>
)} )}
<MotionDiv
animate={{ rotate: [0, 360] }}
transition={{ duration: 3, repeat: Infinity, ease: 'linear' }}
>
<Avatar <Avatar
icon={<Cpu className="w-6 h-6" />} icon={<Cpu className="w-6 h-6" />}
classNames={{ classNames={{
base: 'bg-gradient-to-br from-purple-500 to-pink-500', base: 'bg-gradient-to-br from-purple-500 to-pink-500 animate-spin-slow',
}} }}
/> />
</MotionDiv>
<div> <div>
<h1 className="text-xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"> <h1 className="text-xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
@@ -579,35 +521,21 @@ const AgentChat = () => {
)} )}
</div> </div>
</div> </div>
</MotionDiv> </div>
{/* 消息列表 */}
<ScrollShadow className="flex-1 p-6"> <ScrollShadow className="flex-1 p-6">
<div className="max-w-4xl mx-auto space-y-4"> <div className="max-w-4xl mx-auto space-y-4">
<AnimatePresence> {messages.map((message) => (
{messages.map((message, index) => ( <div key={message.id} className="animate-fade-in">
<MotionDiv
key={message.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ delay: index * 0.1 }}
>
<MessageRenderer message={message} userAvatar={user?.avatar} /> <MessageRenderer message={message} userAvatar={user?.avatar} />
</MotionDiv> </div>
))} ))}
</AnimatePresence>
<div ref={messagesEndRef} /> <div ref={messagesEndRef} />
</div> </div>
</ScrollShadow> </ScrollShadow>
{/* 快捷问题 */}
{messages.length <= 2 && !isProcessing && ( {messages.length <= 2 && !isProcessing && (
<MotionDiv <div className="px-6 py-3 animate-fade-in">
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
className="px-6 py-3"
>
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<p className="text-xs text-gray-500 mb-2 font-medium flex items-center gap-1"> <p className="text-xs text-gray-500 mb-2 font-medium flex items-center gap-1">
<Sparkles className="w-3 h-3" /> <Sparkles className="w-3 h-3" />
@@ -615,11 +543,11 @@ const AgentChat = () => {
</p> </p>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
{quickQuestions.map((question, idx) => ( {quickQuestions.map((question, idx) => (
<MotionDiv key={idx} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}>
<Button <Button
key={idx}
variant="bordered" variant="bordered"
color="primary" color="primary"
className="w-full justify-start h-auto py-3" className="w-full justify-start h-auto py-3 hover:scale-105 transition-transform"
onPress={() => { onPress={() => {
setInputValue(question.text); setInputValue(question.text);
inputRef.current?.focus(); inputRef.current?.focus();
@@ -628,19 +556,13 @@ const AgentChat = () => {
<span className="mr-2">{question.emoji}</span> <span className="mr-2">{question.emoji}</span>
{question.text} {question.text}
</Button> </Button>
</MotionDiv>
))} ))}
</div> </div>
</div> </div>
</MotionDiv> </div>
)} )}
{/* 输入框 */} <div className="bg-white/70 dark:bg-gray-800/70 backdrop-blur-xl border-t border-gray-200 dark:border-gray-700 px-6 py-4">
<MotionDiv
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
className="bg-white/70 dark:bg-gray-800/70 backdrop-blur-xl border-t border-gray-200 dark:border-gray-700 px-6 py-4"
>
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<div className="flex gap-2"> <div className="flex gap-2">
<Input <Input
@@ -654,10 +576,9 @@ const AgentChat = () => {
variant="bordered" variant="bordered"
classNames={{ classNames={{
input: 'text-base', input: 'text-base',
inputWrapper: 'border-2 hover:border-primary-500', inputWrapper: 'border-2 hover:border-primary-500 transition-colors',
}} }}
/> />
<MotionDiv whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<Button <Button
isIconOnly isIconOnly
color="primary" color="primary"
@@ -666,14 +587,12 @@ const AgentChat = () => {
onPress={handleSendMessage} onPress={handleSendMessage}
isLoading={isProcessing} isLoading={isProcessing}
isDisabled={!inputValue.trim() || isProcessing} isDisabled={!inputValue.trim() || isProcessing}
className="bg-gradient-to-r from-blue-500 to-purple-500" className="bg-gradient-to-r from-blue-500 to-purple-500 hover:scale-105 transition-transform"
> >
{!isProcessing && <Send className="w-5 h-5" />} {!isProcessing && <Send className="w-5 h-5" />}
</Button> </Button>
</MotionDiv>
</div> </div>
{/* 键盘提示 */}
<div className="flex items-center gap-4 mt-2 text-xs text-gray-500"> <div className="flex items-center gap-4 mt-2 text-xs text-gray-500">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Kbd keys={['enter']}>Enter</Kbd> <Kbd keys={['enter']}>Enter</Kbd>
@@ -687,20 +606,12 @@ const AgentChat = () => {
</div> </div>
</div> </div>
</div> </div>
</MotionDiv> </div>
</div> </div>
{/* 右侧栏 - 模型和工具配置 */} {/* 右侧栏 */}
<AnimatePresence>
{isRightSidebarOpen && ( {isRightSidebarOpen && (
<MotionDiv <div className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-l border-gray-200 dark:border-gray-700 shadow-xl animate-slide-in-right">
initial={{ x: 320, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: 320, opacity: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
className="w-80 flex flex-col bg-white/80 dark:bg-gray-800/80 backdrop-blur-xl border-l border-gray-200 dark:border-gray-700 shadow-xl"
>
{/* 侧边栏头部 */}
<div className="p-4 border-b border-gray-200 dark:border-gray-700"> <div className="p-4 border-b border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -718,21 +629,16 @@ const AgentChat = () => {
</div> </div>
</div> </div>
{/* 配置面板 */}
<ScrollShadow className="flex-1 p-4"> <ScrollShadow className="flex-1 p-4">
<Tabs aria-label="配置选项" variant="underlined"> <Tabs aria-label="配置选项" variant="underlined">
<Tab key="model" title="模型"> <Tab key="model" title="模型">
<div className="space-y-3 mt-4"> <div className="space-y-3 mt-4">
{AVAILABLE_MODELS.map((model) => ( {AVAILABLE_MODELS.map((model) => (
<MotionDiv
key={model.id}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<Card <Card
key={model.id}
isPressable isPressable
onPress={() => setSelectedModel(model.id)} onPress={() => setSelectedModel(model.id)}
className={`${ className={`hover:scale-102 transition-transform ${
selectedModel === model.id selectedModel === model.id
? 'border-2 border-' + model.color + '-500 bg-' + model.color + '-50' ? 'border-2 border-' + model.color + '-500 bg-' + model.color + '-50'
: 'border-2 border-transparent' : 'border-2 border-transparent'
@@ -753,7 +659,6 @@ const AgentChat = () => {
</div> </div>
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
))} ))}
</div> </div>
</Tab> </Tab>
@@ -764,14 +669,12 @@ const AgentChat = () => {
<CheckboxGroup value={selectedTools} onValueChange={setSelectedTools}> <CheckboxGroup value={selectedTools} onValueChange={setSelectedTools}>
<div className="space-y-2"> <div className="space-y-2">
{AVAILABLE_TOOLS.map((tool) => ( {AVAILABLE_TOOLS.map((tool) => (
<MotionDiv key={tool.id} whileHover={{ x: 4 }}> <Checkbox key={tool.id} value={tool.id} size="sm">
<Checkbox value={tool.id} size="sm">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{tool.icon} {tool.icon}
<span className="text-sm">{tool.name}</span> <span className="text-sm">{tool.name}</span>
</div> </div>
</Checkbox> </Checkbox>
</MotionDiv>
))} ))}
</div> </div>
</CheckboxGroup> </CheckboxGroup>
@@ -806,15 +709,82 @@ const AgentChat = () => {
</Tab> </Tab>
</Tabs> </Tabs>
</ScrollShadow> </ScrollShadow>
</MotionDiv> </div>
)} )}
</AnimatePresence>
<style>{`
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slide-in-left {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-right {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes spin-slow {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
.animate-slide-in-left {
animation: slide-in-left 0.3s ease-out;
}
.animate-slide-in-right {
animation: slide-in-right 0.3s ease-out;
}
.animate-spin-slow {
animation: spin-slow 3s linear infinite;
}
.hover\\:scale-102:hover {
transform: scale(1.02);
}
.hover\\:scale-105:hover {
transform: scale(1.05);
}
`}</style>
</div> </div>
); );
}; };
/** /**
* 消息渲染器组件 * 消息渲染器
*/ */
const MessageRenderer = ({ message, userAvatar }) => { const MessageRenderer = ({ message, userAvatar }) => {
switch (message.type) { switch (message.type) {
@@ -822,13 +792,11 @@ const MessageRenderer = ({ message, userAvatar }) => {
return ( return (
<div className="flex justify-end"> <div className="flex justify-end">
<div className="flex items-start gap-3 max-w-[75%]"> <div className="flex items-start gap-3 max-w-[75%]">
<MotionDiv whileHover={{ scale: 1.02 }}> <Card className="bg-gradient-to-br from-blue-500 to-purple-500 hover:scale-102 transition-transform" shadow="lg">
<Card className="bg-gradient-to-br from-blue-500 to-purple-500" shadow="lg">
<CardBody className="px-5 py-3"> <CardBody className="px-5 py-3">
<p className="text-sm text-white whitespace-pre-wrap">{message.content}</p> <p className="text-sm text-white whitespace-pre-wrap">{message.content}</p>
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
<Avatar src={userAvatar} icon={<User className="w-4 h-4" />} size="sm" /> <Avatar src={userAvatar} icon={<User className="w-4 h-4" />} size="sm" />
</div> </div>
</div> </div>
@@ -840,15 +808,9 @@ const MessageRenderer = ({ message, userAvatar }) => {
<div className="flex items-start gap-3 max-w-[75%]"> <div className="flex items-start gap-3 max-w-[75%]">
<Avatar <Avatar
icon={<Cpu className="w-4 h-4" />} icon={<Cpu className="w-4 h-4" />}
classNames={{ classNames={{ base: 'bg-gradient-to-br from-purple-500 to-pink-500' }}
base: 'bg-gradient-to-br from-purple-500 to-pink-500',
}}
size="sm" size="sm"
/> />
<MotionDiv
animate={{ scale: [1, 1.02, 1] }}
transition={{ duration: 1.5, repeat: Infinity }}
>
<Card shadow="sm" variant="bordered"> <Card shadow="sm" variant="bordered">
<CardBody className="px-5 py-3"> <CardBody className="px-5 py-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@@ -859,7 +821,6 @@ const MessageRenderer = ({ message, userAvatar }) => {
</div> </div>
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
</div> </div>
</div> </div>
); );
@@ -870,18 +831,14 @@ const MessageRenderer = ({ message, userAvatar }) => {
<div className="flex items-start gap-3 max-w-[85%]"> <div className="flex items-start gap-3 max-w-[85%]">
<Avatar <Avatar
icon={<Cpu className="w-4 h-4" />} icon={<Cpu className="w-4 h-4" />}
classNames={{ classNames={{ base: 'bg-gradient-to-br from-blue-500 to-purple-500' }}
base: 'bg-gradient-to-br from-blue-500 to-purple-500',
}}
size="sm" size="sm"
/> />
<div className="flex-1 space-y-3"> <div className="flex-1 space-y-3">
<MotionDiv whileHover={{ scale: 1.01 }}> <Card shadow="lg" className="hover:scale-101 transition-transform">
<Card shadow="lg">
<CardBody className="px-5 py-4"> <CardBody className="px-5 py-4">
<p className="text-sm whitespace-pre-wrap leading-relaxed">{message.content}</p> <p className="text-sm whitespace-pre-wrap leading-relaxed">{message.content}</p>
{/* 操作按钮 */}
<div className="flex items-center gap-2 mt-3 pt-3 border-t border-gray-200"> <div className="flex items-center gap-2 mt-3 pt-3 border-t border-gray-200">
<Tooltip content="复制回答"> <Tooltip content="复制回答">
<Button <Button
@@ -904,7 +861,6 @@ const MessageRenderer = ({ message, userAvatar }) => {
</Button> </Button>
</Tooltip> </Tooltip>
{/* 元数据 */}
{message.metadata && ( {message.metadata && (
<div className="flex-1 flex items-center justify-end gap-3 text-xs text-gray-500"> <div className="flex-1 flex items-center justify-end gap-3 text-xs text-gray-500">
<span>步骤: {message.metadata.total_steps}</span> <span>步骤: {message.metadata.total_steps}</span>
@@ -918,9 +874,7 @@ const MessageRenderer = ({ message, userAvatar }) => {
</div> </div>
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
{/* 执行步骤(可展开) */}
{message.stepResults && message.stepResults.length > 0 && ( {message.stepResults && message.stepResults.length > 0 && (
<StepResultsPanel stepResults={message.stepResults} /> <StepResultsPanel stepResults={message.stepResults} />
)} )}
@@ -938,16 +892,11 @@ const MessageRenderer = ({ message, userAvatar }) => {
classNames={{ base: 'bg-danger-500' }} classNames={{ base: 'bg-danger-500' }}
size="sm" size="sm"
/> />
<MotionDiv
animate={{ x: [-2, 2, -2, 2, 0] }}
transition={{ duration: 0.4 }}
>
<Card className="border-2 border-danger-500 bg-danger-50"> <Card className="border-2 border-danger-500 bg-danger-50">
<CardBody className="px-5 py-3"> <CardBody className="px-5 py-3">
<p className="text-sm text-danger-700">{message.content}</p> <p className="text-sm text-danger-700">{message.content}</p>
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
</div> </div>
</div> </div>
); );
@@ -958,7 +907,7 @@ const MessageRenderer = ({ message, userAvatar }) => {
}; };
/** /**
* 步骤结果面板组件 * 步骤结果面板
*/ */
const StepResultsPanel = ({ stepResults }) => { const StepResultsPanel = ({ stepResults }) => {
return ( return (
@@ -975,13 +924,8 @@ const StepResultsPanel = ({ stepResults }) => {
> >
<div className="space-y-2"> <div className="space-y-2">
{stepResults.map((result, idx) => ( {stepResults.map((result, idx) => (
<MotionDiv
key={idx}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: idx * 0.1 }}
>
<Card <Card
key={idx}
className={`border-l-4 ${ className={`border-l-4 ${
result.status === 'success' result.status === 'success'
? 'border-l-success-500 bg-success-50' ? 'border-l-success-500 bg-success-50'
@@ -1012,12 +956,9 @@ const StepResultsPanel = ({ stepResults }) => {
</div> </div>
<p className="text-xs text-gray-500">{result.execution_time?.toFixed(2)}s</p> <p className="text-xs text-gray-500">{result.execution_time?.toFixed(2)}s</p>
</div> </div>
{result.error && ( {result.error && <p className="text-xs text-danger-600 mt-1"> {result.error}</p>}
<p className="text-xs text-danger-600 mt-1"> {result.error}</p>
)}
</CardBody> </CardBody>
</Card> </Card>
</MotionDiv>
))} ))}
</div> </div>
</AccordionItem> </AccordionItem>