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