diff --git a/src/views/AgentChat/index.js b/src/views/AgentChat/index.js
index 7446357f..7db61de8 100644
--- a/src/views/AgentChat/index.js
+++ b/src/views/AgentChat/index.js
@@ -3,101 +3,23 @@
// 使用 Framer Motion 物理动画引擎 + 毛玻璃效果
import React, { useState, useEffect, useRef } from 'react';
-import { motion, AnimatePresence } from 'framer-motion';
-import {
- Box,
- Button,
- Input,
- Avatar,
- Badge,
- Divider,
- Spinner,
- Tooltip,
- Checkbox,
- CheckboxGroup,
- Kbd,
- Accordion,
- AccordionItem,
- AccordionButton,
- AccordionPanel,
- AccordionIcon,
- Tabs,
- TabList,
- TabPanels,
- Tab,
- TabPanel,
- useToast,
- VStack,
- HStack,
- Text,
- Flex,
- IconButton,
- useColorMode,
- Card,
- CardBody,
- Tag,
- TagLabel,
- TagCloseButton,
-} from '@chakra-ui/react';
+import { Box, Flex, useToast, useColorMode } from '@chakra-ui/react';
import { useAuth } from '@contexts/AuthContext';
import { logger } from '@utils/logger';
import axios from 'axios';
-// 图标 - 使用 Lucide Icons
-import {
- Send,
- Plus,
- Search,
- MessageSquare,
- Trash2,
- MoreVertical,
- RefreshCw,
- Download,
- Cpu,
- User,
- Zap,
- Clock,
- Settings,
- ChevronLeft,
- ChevronRight,
- Activity,
- Code,
- Database,
- TrendingUp,
- FileText,
- BookOpen,
- Menu,
- X,
- Check,
- Circle,
- Maximize2,
- Minimize2,
- Copy,
- ThumbsUp,
- ThumbsDown,
- Sparkles,
- Brain,
- Rocket,
- Paperclip,
- Image as ImageIcon,
- File,
- Calendar,
- Globe,
- DollarSign,
- Newspaper,
- BarChart3,
- PieChart,
- LineChart,
- Briefcase,
- Users,
-} from 'lucide-react';
+// 注意:图标导入已移至子组件中,主组件不再需要导入
// 常量配置 - 从 TypeScript 模块导入
-import { animations } from './constants/animations';
import { MessageTypes } from './constants/messageTypes';
-import { AVAILABLE_MODELS, DEFAULT_MODEL_ID } from './constants/models';
-import { MCP_TOOLS, TOOL_CATEGORIES, DEFAULT_SELECTED_TOOLS } from './constants/tools';
-import { quickQuestions } from './constants/quickQuestions';
+import { DEFAULT_MODEL_ID } from './constants/models';
+import { DEFAULT_SELECTED_TOOLS } from './constants/tools';
+
+// 拆分后的子组件
+import BackgroundEffects from './components/BackgroundEffects';
+import LeftSidebar from './components/LeftSidebar';
+import ChatArea from './components/ChatArea';
+import RightSidebar from './components/RightSidebar';
/**
* Agent Chat - 主组件(HeroUI v3 深色主题)
@@ -125,7 +47,6 @@ const AgentChat = () => {
const [isProcessing, setIsProcessing] = useState(false);
// UI 状态
- const [searchQuery, setSearchQuery] = useState('');
const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL_ID);
const [selectedTools, setSelectedTools] = useState(DEFAULT_SELECTED_TOOLS);
const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true);
@@ -360,1464 +281,61 @@ const AgentChat = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
- // ==================== 按日期分组会话 ====================
- const groupSessionsByDate = (sessions) => {
- const today = new Date();
- const yesterday = new Date(today);
- yesterday.setDate(yesterday.getDate() - 1);
- const weekAgo = new Date(today);
- weekAgo.setDate(weekAgo.getDate() - 7);
-
- const groups = {
- today: [],
- yesterday: [],
- thisWeek: [],
- older: [],
- };
-
- sessions.forEach(session => {
- const sessionDate = new Date(session.created_at || session.timestamp);
- const daysDiff = Math.floor((today - sessionDate) / (1000 * 60 * 60 * 24));
-
- if (daysDiff === 0) {
- groups.today.push(session);
- } else if (daysDiff === 1) {
- groups.yesterday.push(session);
- } else if (daysDiff <= 7) {
- groups.thisWeek.push(session);
- } else {
- groups.older.push(session);
- }
- });
-
- return groups;
- };
-
- const sessionGroups = groupSessionsByDate(sessions);
- const filteredSessions = searchQuery
- ? sessions.filter((s) =>
- s.title?.toLowerCase().includes(searchQuery.toLowerCase()) ||
- s.session_id?.toLowerCase().includes(searchQuery.toLowerCase())
- )
- : sessions;
-
- // quickQuestions 已移动到 constants/quickQuestions.ts
-
return (
- {/* 背景渐变层 - 移到 Flex 内部 */}
-
+
+ {/* 左侧栏 */}
+ setIsLeftSidebarOpen(false)}
+ sessions={sessions}
+ currentSessionId={currentSessionId}
+ onSessionSwitch={switchSession}
+ onNewSession={createNewSession}
+ isLoadingSessions={isLoadingSessions}
+ user={user}
/>
- {/* 背景装饰光效 */}
-
- setIsLeftSidebarOpen(true)}
+ onToggleRightSidebar={() => setIsRightSidebarOpen(true)}
+ onNewSession={createNewSession}
+ userAvatar={user?.avatar}
+ messagesEndRef={messagesEndRef}
+ inputRef={inputRef}
+ fileInputRef={fileInputRef}
/>
- {/* 左侧栏 - 深色毛玻璃 */}
-
- {isLeftSidebarOpen && (
-
-
-
-
-
-
-
- 对话历史
-
-
-
-
-
- }
- onClick={createNewSession}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.300"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(59, 130, 246, 0.2)",
- borderColor: "blue.400",
- color: "blue.300",
- boxShadow: "0 0 12px rgba(59, 130, 246, 0.3)"
- }}
- />
-
-
-
-
- }
- onClick={() => setIsLeftSidebarOpen(false)}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.300"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- borderColor: "purple.400",
- color: "white"
- }}
- />
-
-
-
-
-
-
-
-
-
- setSearchQuery(e.target.value)}
- size="sm"
- variant="outline"
- pl={10}
- bg="rgba(255, 255, 255, 0.05)"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- color="white"
- _placeholder={{ color: "gray.500" }}
- _hover={{
- borderColor: "rgba(255, 255, 255, 0.2)"
- }}
- _focus={{
- borderColor: "purple.400",
- boxShadow: "0 0 0 1px var(--chakra-colors-purple-400), 0 0 12px rgba(139, 92, 246, 0.3)",
- bg: "rgba(255, 255, 255, 0.08)"
- }}
- />
-
-
-
-
- {/* 按日期分组显示会话 */}
- {sessionGroups.today.length > 0 && (
-
- 今天
-
- {sessionGroups.today.map((session, idx) => (
-
- switchSession(session.session_id)}
- />
-
- ))}
-
-
- )}
-
- {sessionGroups.yesterday.length > 0 && (
-
- 昨天
-
- {sessionGroups.yesterday.map((session) => (
- switchSession(session.session_id)}
- />
- ))}
-
-
- )}
-
- {sessionGroups.thisWeek.length > 0 && (
-
- 本周
-
- {sessionGroups.thisWeek.map((session) => (
- switchSession(session.session_id)}
- />
- ))}
-
-
- )}
-
- {sessionGroups.older.length > 0 && (
-
- 更早
-
- {sessionGroups.older.map((session) => (
- switchSession(session.session_id)}
- />
- ))}
-
-
- )}
-
- {isLoadingSessions && (
-
-
-
- )}
-
- {sessions.length === 0 && !isLoadingSessions && (
-
-
- 还没有对话历史
- 开始一个新对话吧!
-
- )}
-
-
-
-
-
-
-
- {user?.nickname || '未登录'}
-
-
- {user?.subscription_type || 'free'}
-
-
-
-
-
-
- )}
-
-
- {/* 中间主聊天区域 */}
-
- {/* 顶部标题栏 - 深色毛玻璃 */}
-
-
-
- {!isLeftSidebarOpen && (
-
- }
- onClick={() => setIsLeftSidebarOpen(true)}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.400"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- color: "white"
- }}
- />
-
- )}
-
-
- }
- bgGradient="linear(to-br, purple.500, pink.500)"
- boxShadow="0 0 20px rgba(236, 72, 153, 0.5)"
- />
-
-
-
-
- 价小前投研 AI
-
-
-
-
- 智能分析
-
-
- {AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.name}
-
-
-
-
-
-
-
-
- }
- onClick={createNewSession}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.400"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- color: "white",
- borderColor: "purple.400",
- boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
- }}
- />
-
-
- {!isRightSidebarOpen && (
-
- }
- onClick={() => setIsRightSidebarOpen(true)}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.400"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- color: "white"
- }}
- />
-
- )}
-
-
-
-
- {/* 消息列表 */}
-
-
-
-
- {messages.map((message) => (
-
-
-
- ))}
-
-
-
-
-
-
- {/* 快捷问题 */}
-
- {messages.length <= 2 && !isProcessing && (
-
-
-
-
-
- 快速开始
-
-
- {quickQuestions.map((question, idx) => (
-
-
-
- ))}
-
-
-
-
- )}
-
-
- {/* 输入栏 - 深色毛玻璃 */}
-
-
- {/* 已上传文件预览 */}
- {uploadedFiles.length > 0 && (
-
- {uploadedFiles.map((file, idx) => (
-
-
- {file.name}
- removeFile(idx)} color="gray.400" />
-
-
- ))}
-
- )}
-
-
-
-
-
-
- }
- onClick={() => fileInputRef.current?.click()}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.300"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- borderColor: "purple.400",
- color: "white",
- boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
- }}
- />
-
-
-
-
-
- }
- onClick={() => {
- fileInputRef.current?.setAttribute('accept', 'image/*');
- fileInputRef.current?.click();
- }}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.300"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- borderColor: "purple.400",
- color: "white",
- boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
- }}
- />
-
-
-
- setInputValue(e.target.value)}
- onKeyDown={handleKeyPress}
- placeholder="输入你的问题... (Enter 发送, Shift+Enter 换行)"
- isDisabled={isProcessing}
- size="lg"
- variant="outline"
- borderWidth={2}
- bg="rgba(255, 255, 255, 0.05)"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- color="white"
- _placeholder={{ color: "gray.500" }}
- _hover={{
- borderColor: "rgba(255, 255, 255, 0.2)"
- }}
- _focus={{
- borderColor: "purple.400",
- boxShadow: "0 0 0 1px var(--chakra-colors-purple-400), 0 0 12px rgba(139, 92, 246, 0.3)",
- bg: "rgba(255, 255, 255, 0.08)"
- }}
- />
-
-
- }
- onClick={handleSendMessage}
- isLoading={isProcessing}
- isDisabled={!inputValue.trim() || isProcessing}
- bgGradient="linear(to-r, blue.500, purple.600)"
- color="white"
- _hover={{
- bgGradient: "linear(to-r, blue.600, purple.700)",
- boxShadow: "0 8px 20px rgba(139, 92, 246, 0.4)"
- }}
- _active={{
- transform: "translateY(0)",
- boxShadow: "0 4px 12px rgba(139, 92, 246, 0.3)"
- }}
- />
-
-
-
-
-
- Enter
- 发送
-
-
- Shift
- +
- Enter
- 换行
-
-
-
-
-
-
- {/* 右侧栏 - 深色配置中心 */}
-
- {isRightSidebarOpen && (
-
-
-
-
-
-
-
- 配置中心
-
-
-
-
- }
- onClick={() => setIsRightSidebarOpen(false)}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.300"
- backdropFilter="blur(10px)"
- border="1px solid"
- borderColor="rgba(255, 255, 255, 0.1)"
- _hover={{
- bg: "rgba(255, 255, 255, 0.1)",
- borderColor: "purple.400",
- color: "white"
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
- 模型
-
-
-
-
-
- 工具
- {selectedTools.length > 0 && (
-
- {selectedTools.length}
-
- )}
-
-
-
-
-
- 统计
-
-
-
-
-
- {/* 模型选择 */}
-
-
- {AVAILABLE_MODELS.map((model, idx) => (
-
- setSelectedModel(model.id)}
- bg={selectedModel === model.id
- ? 'rgba(139, 92, 246, 0.15)'
- : 'rgba(255, 255, 255, 0.05)'}
- backdropFilter="blur(12px)"
- borderWidth={2}
- borderColor={selectedModel === model.id
- ? 'purple.400'
- : 'rgba(255, 255, 255, 0.1)'}
- _hover={{
- borderColor: selectedModel === model.id
- ? 'purple.400'
- : 'rgba(255, 255, 255, 0.2)',
- boxShadow: selectedModel === model.id
- ? "0 8px 20px rgba(139, 92, 246, 0.4)"
- : "0 4px 12px rgba(0, 0, 0, 0.3)"
- }}
- transition="all 0.3s"
- >
-
-
-
- {model.icon}
-
-
-
- {model.name}
-
-
- {model.description}
-
-
- {selectedModel === model.id && (
-
-
-
- )}
-
-
-
-
- ))}
-
-
-
- {/* 工具选择 */}
-
-
- {Object.entries(TOOL_CATEGORIES).map(([category, tools], catIdx) => (
-
-
-
-
- {category}
-
- {tools.filter(t => selectedTools.includes(t.id)).length}/{tools.length}
-
-
-
-
-
-
-
- {tools.map((tool) => (
-
-
-
- {tool.icon}
-
- {tool.name}
- {tool.description}
-
-
-
-
- ))}
-
-
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
- {/* 统计信息 */}
-
-
-
-
-
-
-
- 对话数
-
- {sessions.length}
-
-
-
-
-
-
-
-
-
-
-
-
-
- 消息数
-
- {messages.length}
-
-
-
-
-
-
-
-
-
-
-
-
-
- 已选工具
-
- {selectedTools.length}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
-
+ {/* 右侧栏 */}
+ setIsRightSidebarOpen(false)}
+ selectedModel={selectedModel}
+ onModelChange={setSelectedModel}
+ selectedTools={selectedTools}
+ onToolsChange={setSelectedTools}
+ sessionsCount={sessions.length}
+ messagesCount={messages.length}
+ />
);
};
export default AgentChat;
-
-/**
- * 会话卡片组件
- */
-const SessionCard = ({ session, isActive, onPress }) => {
- return (
-
-
-
-
-
-
- {session.title || '新对话'}
-
-
- {new Date(session.created_at || session.timestamp).toLocaleString('zh-CN', {
- month: 'numeric',
- day: 'numeric',
- hour: '2-digit',
- minute: '2-digit',
- })}
-
-
- {session.message_count && (
-
- {session.message_count}
-
- )}
-
-
-
-
- );
-};
-
-/**
- * 消息渲染器
- */
-const MessageRenderer = ({ message, userAvatar }) => {
- switch (message.type) {
- case MessageTypes.USER:
- return (
-
-
-
-
-
-
- {message.content}
-
- {message.files && message.files.length > 0 && (
-
- {message.files.map((file, idx) => (
-
-
- {file.name}
-
- ))}
-
- )}
-
-
-
- }
- size="sm"
- bgGradient="linear(to-br, blue.500, purple.600)"
- boxShadow="0 0 12px rgba(139, 92, 246, 0.4)"
- />
-
-
- );
-
- case MessageTypes.AGENT_THINKING:
- return (
-
-
- }
- size="sm"
- bgGradient="linear(to-br, purple.500, pink.500)"
- boxShadow="0 0 12px rgba(236, 72, 153, 0.4)"
- />
-
-
-
-
-
- {message.content}
-
-
-
-
-
-
- );
-
- case MessageTypes.AGENT_RESPONSE:
- return (
-
-
- }
- size="sm"
- bgGradient="linear(to-br, purple.500, pink.500)"
- boxShadow="0 0 12px rgba(236, 72, 153, 0.4)"
- />
-
-
-
-
- {message.content}
-
-
- {message.stepResults && message.stepResults.length > 0 && (
-
-
-
- )}
-
-
-
-
- }
- onClick={() => navigator.clipboard.writeText(message.content)}
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.400"
- _hover={{
- color: "white",
- bg: "rgba(255, 255, 255, 0.1)"
- }}
- />
-
-
-
-
- }
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.400"
- _hover={{
- color: "green.400",
- bg: "rgba(16, 185, 129, 0.1)",
- boxShadow: "0 0 12px rgba(16, 185, 129, 0.3)"
- }}
- />
-
-
-
-
- }
- bg="rgba(255, 255, 255, 0.05)"
- color="gray.400"
- _hover={{
- color: "red.400",
- bg: "rgba(239, 68, 68, 0.1)",
- boxShadow: "0 0 12px rgba(239, 68, 68, 0.3)"
- }}
- />
-
-
-
- {new Date(message.timestamp).toLocaleTimeString('zh-CN', {
- hour: '2-digit',
- minute: '2-digit',
- })}
-
-
-
-
-
-
-
- );
-
- case MessageTypes.ERROR:
- return (
-
-
-
-
- {message.content}
-
-
-
-
- );
-
- default:
- return null;
- }
-};
-
-/**
- * 执行步骤显示组件
- */
-const ExecutionStepsDisplay = ({ steps, plan }) => {
- return (
-
-
-
-
-
- 执行详情
-
- {steps.length} 步骤
-
-
-
-
-
-
- {steps.map((result, idx) => (
-
-
-
-
-
- 步骤 {idx + 1}: {result.tool_name}
-
-
- {result.status}
-
-
-
- {result.execution_time?.toFixed(2)}s
-
- {result.error && (
- ⚠️ {result.error}
- )}
-
-
-
- ))}
-
-
-
-
- );
-};
diff --git a/src/views/AgentChat/index.js.bak b/src/views/AgentChat/index.js.bak
new file mode 100644
index 00000000..60a2ecf8
--- /dev/null
+++ b/src/views/AgentChat/index.js.bak
@@ -0,0 +1,1529 @@
+// src/views/AgentChat/index.js
+// 超炫酷的 AI 投研助手 - HeroUI v3 现代深色主题版本
+// 使用 Framer Motion 物理动画引擎 + 毛玻璃效果
+
+import React, { useState, useEffect, useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import {
+ Box,
+ Button,
+ Input,
+ Avatar,
+ Badge,
+ Divider,
+ Spinner,
+ Tooltip,
+ Checkbox,
+ CheckboxGroup,
+ Kbd,
+ Accordion,
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ AccordionIcon,
+ Tabs,
+ TabList,
+ TabPanels,
+ Tab,
+ TabPanel,
+ useToast,
+ VStack,
+ HStack,
+ Text,
+ Flex,
+ IconButton,
+ useColorMode,
+ Card,
+ CardBody,
+ Tag,
+ TagLabel,
+ TagCloseButton,
+} from '@chakra-ui/react';
+import { useAuth } from '@contexts/AuthContext';
+import { logger } from '@utils/logger';
+import axios from 'axios';
+
+// 图标 - 使用 Lucide Icons
+import {
+ Send,
+ Plus,
+ Search,
+ MessageSquare,
+ Trash2,
+ MoreVertical,
+ RefreshCw,
+ Download,
+ Cpu,
+ User,
+ Zap,
+ Clock,
+ Settings,
+ ChevronLeft,
+ ChevronRight,
+ Activity,
+ Code,
+ Database,
+ TrendingUp,
+ FileText,
+ BookOpen,
+ Menu,
+ X,
+ Check,
+ Circle,
+ Maximize2,
+ Minimize2,
+ Copy,
+ ThumbsUp,
+ ThumbsDown,
+ Sparkles,
+ Brain,
+ Rocket,
+ Paperclip,
+ Image as ImageIcon,
+ File,
+ Calendar,
+ Globe,
+ DollarSign,
+ Newspaper,
+ BarChart3,
+ PieChart,
+ LineChart,
+ Briefcase,
+ Users,
+} from 'lucide-react';
+
+// 常量配置 - 从 TypeScript 模块导入
+import { MessageTypes } from './constants/messageTypes';
+import { DEFAULT_MODEL_ID } from './constants/models';
+import { DEFAULT_SELECTED_TOOLS } from './constants/tools';
+
+// 拆分后的子组件
+import BackgroundEffects from './components/BackgroundEffects';
+import LeftSidebar from './components/LeftSidebar';
+import ChatArea from './components/ChatArea';
+import RightSidebar from './components/RightSidebar';
+
+/**
+ * Agent Chat - 主组件(HeroUI v3 深色主题)
+ *
+ * 注意:所有常量配置已提取到 constants/ 目录:
+ * - animations: constants/animations.ts
+ * - MessageTypes: constants/messageTypes.ts
+ * - AVAILABLE_MODELS: constants/models.ts
+ * - MCP_TOOLS, TOOL_CATEGORIES: constants/tools.ts
+ * - quickQuestions: constants/quickQuestions.ts
+ */
+const AgentChat = () => {
+ const { user } = useAuth();
+ const toast = useToast();
+ const { setColorMode } = useColorMode();
+
+ // 会话管理
+ const [sessions, setSessions] = useState([]);
+ const [currentSessionId, setCurrentSessionId] = useState(null);
+ const [isLoadingSessions, setIsLoadingSessions] = useState(false);
+
+ // 消息管理
+ const [messages, setMessages] = useState([]);
+ const [inputValue, setInputValue] = useState('');
+ const [isProcessing, setIsProcessing] = useState(false);
+
+ // UI 状态
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL_ID);
+ const [selectedTools, setSelectedTools] = useState(DEFAULT_SELECTED_TOOLS);
+ const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true);
+ const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(true);
+
+ // 文件上传
+ const [uploadedFiles, setUploadedFiles] = useState([]);
+ const fileInputRef = useRef(null);
+
+ // Refs
+ const messagesEndRef = useRef(null);
+ const inputRef = useRef(null);
+
+ // ==================== 启用深色模式 ====================
+ useEffect(() => {
+ // 为 AgentChat 页面强制启用深色模式
+ setColorMode('dark');
+ document.documentElement.classList.add('dark');
+
+ return () => {
+ // 组件卸载时不移除,让其他页面自己控制
+ // document.documentElement.classList.remove('dark');
+ };
+ }, [setColorMode]);
+
+ // ==================== 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);
+ }
+ } catch (error) {
+ logger.error('加载会话列表失败', error);
+ } finally {
+ setIsLoadingSessions(false);
+ }
+ };
+
+ 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) => ({
+ id: `${sessionId}-${idx}`,
+ type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
+ content: msg.message,
+ plan: msg.plan ? JSON.parse(msg.plan) : null,
+ stepResults: msg.steps ? JSON.parse(msg.steps) : null,
+ timestamp: msg.timestamp,
+ }));
+ setMessages(formattedMessages);
+ }
+ } catch (error) {
+ logger.error('加载会话历史失败', error);
+ }
+ };
+
+ const createNewSession = () => {
+ setCurrentSessionId(null);
+ setMessages([
+ {
+ id: Date.now(),
+ type: MessageTypes.AGENT_RESPONSE,
+ content: `你好${user?.nickname || ''}!👋\n\n我是**价小前**,你的 AI 投研助手。\n\n**我能做什么?**\n• 📊 全面分析股票基本面和技术面\n• 🔥 追踪市场热点和涨停板块\n• 📈 研究行业趋势和投资机会\n• 📰 汇总最新财经新闻和研报\n\n直接输入你的问题开始探索!`,
+ timestamp: new Date().toISOString(),
+ },
+ ]);
+ };
+
+ const switchSession = (sessionId) => {
+ setCurrentSessionId(sessionId);
+ loadSessionHistory(sessionId);
+ };
+
+ const handleSendMessage = async () => {
+ if (!inputValue.trim() || isProcessing) return;
+
+ const userMessage = {
+ type: MessageTypes.USER,
+ content: inputValue,
+ timestamp: new Date().toISOString(),
+ files: uploadedFiles.length > 0 ? uploadedFiles : undefined,
+ };
+
+ addMessage(userMessage);
+ const userInput = inputValue;
+ setInputValue('');
+ setUploadedFiles([]);
+ setIsProcessing(true);
+
+ try {
+ addMessage({
+ type: MessageTypes.AGENT_THINKING,
+ content: '正在分析你的问题...',
+ timestamp: new Date().toISOString(),
+ });
+
+ const response = await axios.post('/mcp/agent/chat', {
+ message: userInput,
+ conversation_history: messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((m) => ({
+ isUser: m.type === MessageTypes.USER,
+ content: m.content,
+ })),
+ user_id: user?.id || 'anonymous',
+ user_nickname: user?.nickname || '匿名用户',
+ user_avatar: user?.avatar || '',
+ subscription_type: user?.subscription_type || 'free',
+ session_id: currentSessionId,
+ model: selectedModel,
+ tools: selectedTools,
+ files: uploadedFiles.length > 0 ? uploadedFiles : undefined,
+ });
+
+ 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,
+ content: '已制定执行计划',
+ plan: data.plan,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ if (data.steps && data.steps.length > 0) {
+ addMessage({
+ type: MessageTypes.AGENT_EXECUTING,
+ content: '正在执行步骤...',
+ plan: data.plan,
+ stepResults: data.steps,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
+
+ addMessage({
+ type: MessageTypes.AGENT_RESPONSE,
+ content: data.final_answer || data.message || '处理完成',
+ plan: data.plan,
+ stepResults: data.steps,
+ metadata: data.metadata,
+ timestamp: new Date().toISOString(),
+ });
+
+ loadSessions();
+ }
+ } catch (error) {
+ logger.error('Agent chat error', error);
+ setMessages((prev) =>
+ prev.filter(
+ (m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
+ )
+ );
+
+ const errorMessage = error.response?.data?.error || error.message || '处理失败';
+ addMessage({
+ type: MessageTypes.ERROR,
+ content: `处理失败:${errorMessage}`,
+ timestamp: new Date().toISOString(),
+ });
+
+ toast({
+ title: '处理失败',
+ description: errorMessage,
+ status: 'error',
+ duration: 5000,
+ });
+ } finally {
+ setIsProcessing(false);
+ }
+ };
+
+ // 文件上传处理
+ const handleFileSelect = (event) => {
+ const files = Array.from(event.target.files || []);
+ const fileData = files.map(file => ({
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ // 实际上传时需要转换为 base64 或上传到服务器
+ url: URL.createObjectURL(file),
+ }));
+ setUploadedFiles(prev => [...prev, ...fileData]);
+ };
+
+ const removeFile = (index) => {
+ setUploadedFiles(prev => prev.filter((_, i) => i !== index));
+ };
+
+ const addMessage = (message) => {
+ setMessages((prev) => [
+ ...prev,
+ {
+ id: Date.now() + Math.random(),
+ ...message,
+ },
+ ]);
+ };
+
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ useEffect(() => {
+ loadSessions();
+ createNewSession();
+ }, [user]);
+
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages]);
+
+ return (
+
+
+ {/* 背景渐变装饰 */}
+
+
+ {/* 左侧栏 */}
+ setIsLeftSidebarOpen(false)}
+ sessions={sessions}
+ currentSessionId={currentSessionId}
+ onSessionSwitch={switchSession}
+ onNewSession={createNewSession}
+ isLoadingSessions={isLoadingSessions}
+ user={user}
+ />
+
+ {/* 中间主聊天区域 */}
+
+ {/* 顶部标题栏 - 深色毛玻璃 */}
+
+
+
+ {!isLeftSidebarOpen && (
+
+ }
+ onClick={() => setIsLeftSidebarOpen(true)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ color: "white"
+ }}
+ />
+
+ )}
+
+
+ }
+ bgGradient="linear(to-br, purple.500, pink.500)"
+ boxShadow="0 0 20px rgba(236, 72, 153, 0.5)"
+ />
+
+
+
+
+ 价小前投研 AI
+
+
+
+
+ 智能分析
+
+
+ {AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.name}
+
+
+
+
+
+
+
+
+ }
+ onClick={createNewSession}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ color: "white",
+ borderColor: "purple.400",
+ boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+ {!isRightSidebarOpen && (
+
+ }
+ onClick={() => setIsRightSidebarOpen(true)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ color: "white"
+ }}
+ />
+
+ )}
+
+
+
+
+ {/* 消息列表 */}
+
+
+
+
+ {messages.map((message) => (
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* 快捷问题 */}
+
+ {messages.length <= 2 && !isProcessing && (
+
+
+
+
+
+ 快速开始
+
+
+ {quickQuestions.map((question, idx) => (
+
+
+
+ ))}
+
+
+
+
+ )}
+
+
+ {/* 输入栏 - 深色毛玻璃 */}
+
+
+ {/* 已上传文件预览 */}
+ {uploadedFiles.length > 0 && (
+
+ {uploadedFiles.map((file, idx) => (
+
+
+ {file.name}
+ removeFile(idx)} color="gray.400" />
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ }
+ onClick={() => fileInputRef.current?.click()}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white",
+ boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+
+
+
+ }
+ onClick={() => {
+ fileInputRef.current?.setAttribute('accept', 'image/*');
+ fileInputRef.current?.click();
+ }}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white",
+ boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+
+ setInputValue(e.target.value)}
+ onKeyDown={handleKeyPress}
+ placeholder="输入你的问题... (Enter 发送, Shift+Enter 换行)"
+ isDisabled={isProcessing}
+ size="lg"
+ variant="outline"
+ borderWidth={2}
+ bg="rgba(255, 255, 255, 0.05)"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ color="white"
+ _placeholder={{ color: "gray.500" }}
+ _hover={{
+ borderColor: "rgba(255, 255, 255, 0.2)"
+ }}
+ _focus={{
+ borderColor: "purple.400",
+ boxShadow: "0 0 0 1px var(--chakra-colors-purple-400), 0 0 12px rgba(139, 92, 246, 0.3)",
+ bg: "rgba(255, 255, 255, 0.08)"
+ }}
+ />
+
+
+ }
+ onClick={handleSendMessage}
+ isLoading={isProcessing}
+ isDisabled={!inputValue.trim() || isProcessing}
+ bgGradient="linear(to-r, blue.500, purple.600)"
+ color="white"
+ _hover={{
+ bgGradient: "linear(to-r, blue.600, purple.700)",
+ boxShadow: "0 8px 20px rgba(139, 92, 246, 0.4)"
+ }}
+ _active={{
+ transform: "translateY(0)",
+ boxShadow: "0 4px 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+
+
+
+ Enter
+ 发送
+
+
+ Shift
+ +
+ Enter
+ 换行
+
+
+
+
+
+
+ {/* 右侧栏 - 深色配置中心 */}
+
+ {isRightSidebarOpen && (
+
+
+
+
+
+
+
+ 配置中心
+
+
+
+
+ }
+ onClick={() => setIsRightSidebarOpen(false)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white"
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ 模型
+
+
+
+
+
+ 工具
+ {selectedTools.length > 0 && (
+
+ {selectedTools.length}
+
+ )}
+
+
+
+
+
+ 统计
+
+
+
+
+
+ {/* 模型选择 */}
+
+
+ {AVAILABLE_MODELS.map((model, idx) => (
+
+ setSelectedModel(model.id)}
+ bg={selectedModel === model.id
+ ? 'rgba(139, 92, 246, 0.15)'
+ : 'rgba(255, 255, 255, 0.05)'}
+ backdropFilter="blur(12px)"
+ borderWidth={2}
+ borderColor={selectedModel === model.id
+ ? 'purple.400'
+ : 'rgba(255, 255, 255, 0.1)'}
+ _hover={{
+ borderColor: selectedModel === model.id
+ ? 'purple.400'
+ : 'rgba(255, 255, 255, 0.2)',
+ boxShadow: selectedModel === model.id
+ ? "0 8px 20px rgba(139, 92, 246, 0.4)"
+ : "0 4px 12px rgba(0, 0, 0, 0.3)"
+ }}
+ transition="all 0.3s"
+ >
+
+
+
+ {model.icon}
+
+
+
+ {model.name}
+
+
+ {model.description}
+
+
+ {selectedModel === model.id && (
+
+
+
+ )}
+
+
+
+
+ ))}
+
+
+
+ {/* 工具选择 */}
+
+
+ {Object.entries(TOOL_CATEGORIES).map(([category, tools], catIdx) => (
+
+
+
+
+ {category}
+
+ {tools.filter(t => selectedTools.includes(t.id)).length}/{tools.length}
+
+
+
+
+
+
+
+ {tools.map((tool) => (
+
+
+
+ {tool.icon}
+
+ {tool.name}
+ {tool.description}
+
+
+
+
+ ))}
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 统计信息 */}
+
+
+
+
+
+
+
+ 对话数
+
+ {sessions.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 消息数
+
+ {messages.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已选工具
+
+ {selectedTools.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default AgentChat;
+ return (
+
+
+
+
+
+
+ {session.title || '新对话'}
+
+
+ {new Date(session.created_at || session.timestamp).toLocaleString('zh-CN', {
+ month: 'numeric',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ })}
+
+
+ {session.message_count && (
+
+ {session.message_count}
+
+ )}
+
+
+
+
+ );
+};
+
+/**
+ * 消息渲染器
+ */
+const MessageRenderer = ({ message, userAvatar }) => {
+ switch (message.type) {
+ case MessageTypes.USER:
+ return (
+
+
+
+
+
+
+ {message.content}
+
+ {message.files && message.files.length > 0 && (
+
+ {message.files.map((file, idx) => (
+
+
+ {file.name}
+
+ ))}
+
+ )}
+
+
+
+ }
+ size="sm"
+ bgGradient="linear(to-br, blue.500, purple.600)"
+ boxShadow="0 0 12px rgba(139, 92, 246, 0.4)"
+ />
+
+
+ );
+
+ case MessageTypes.AGENT_THINKING:
+ return (
+
+
+ }
+ size="sm"
+ bgGradient="linear(to-br, purple.500, pink.500)"
+ boxShadow="0 0 12px rgba(236, 72, 153, 0.4)"
+ />
+
+
+
+
+
+ {message.content}
+
+
+
+
+
+
+ );
+
+ case MessageTypes.AGENT_RESPONSE:
+ return (
+
+
+ }
+ size="sm"
+ bgGradient="linear(to-br, purple.500, pink.500)"
+ boxShadow="0 0 12px rgba(236, 72, 153, 0.4)"
+ />
+
+
+
+
+ {message.content}
+
+
+ {message.stepResults && message.stepResults.length > 0 && (
+
+
+
+ )}
+
+
+
+
+ }
+ onClick={() => navigator.clipboard.writeText(message.content)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ _hover={{
+ color: "white",
+ bg: "rgba(255, 255, 255, 0.1)"
+ }}
+ />
+
+
+
+
+ }
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ _hover={{
+ color: "green.400",
+ bg: "rgba(16, 185, 129, 0.1)",
+ boxShadow: "0 0 12px rgba(16, 185, 129, 0.3)"
+ }}
+ />
+
+
+
+
+ }
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ _hover={{
+ color: "red.400",
+ bg: "rgba(239, 68, 68, 0.1)",
+ boxShadow: "0 0 12px rgba(239, 68, 68, 0.3)"
+ }}
+ />
+
+
+
+ {new Date(message.timestamp).toLocaleTimeString('zh-CN', {
+ hour: '2-digit',
+ minute: '2-digit',
+ })}
+
+
+
+
+
+
+
+ );
+
+ case MessageTypes.ERROR:
+ return (
+
+
+
+
+ {message.content}
+
+
+
+
+ );
+
+ default:
+ return null;
+ }
+};
+
+/**
+ * 执行步骤显示组件
+ */
+const ExecutionStepsDisplay = ({ steps, plan }) => {
+ return (
+
+
+
+
+
+ 执行详情
+
+ {steps.length} 步骤
+
+
+
+
+
+
+ {steps.map((result, idx) => (
+
+
+
+
+
+ 步骤 {idx + 1}: {result.tool_name}
+
+
+ {result.status}
+
+
+
+ {result.execution_time?.toFixed(2)}s
+
+ {result.error && (
+ ⚠️ {result.error}
+ )}
+
+
+
+ ))}
+
+
+
+
+ );
+};
diff --git a/src/views/AgentChat/index.js.bak2 b/src/views/AgentChat/index.js.bak2
new file mode 100644
index 00000000..071ce7a6
--- /dev/null
+++ b/src/views/AgentChat/index.js.bak2
@@ -0,0 +1,1173 @@
+// src/views/AgentChat/index.js
+// 超炫酷的 AI 投研助手 - HeroUI v3 现代深色主题版本
+// 使用 Framer Motion 物理动画引擎 + 毛玻璃效果
+
+import React, { useState, useEffect, useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import {
+ Box,
+ Button,
+ Input,
+ Avatar,
+ Badge,
+ Divider,
+ Spinner,
+ Tooltip,
+ Checkbox,
+ CheckboxGroup,
+ Kbd,
+ Accordion,
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ AccordionIcon,
+ Tabs,
+ TabList,
+ TabPanels,
+ Tab,
+ TabPanel,
+ useToast,
+ VStack,
+ HStack,
+ Text,
+ Flex,
+ IconButton,
+ useColorMode,
+ Card,
+ CardBody,
+ Tag,
+ TagLabel,
+ TagCloseButton,
+} from '@chakra-ui/react';
+import { useAuth } from '@contexts/AuthContext';
+import { logger } from '@utils/logger';
+import axios from 'axios';
+
+// 图标 - 使用 Lucide Icons
+import {
+ Send,
+ Plus,
+ Search,
+ MessageSquare,
+ Trash2,
+ MoreVertical,
+ RefreshCw,
+ Download,
+ Cpu,
+ User,
+ Zap,
+ Clock,
+ Settings,
+ ChevronLeft,
+ ChevronRight,
+ Activity,
+ Code,
+ Database,
+ TrendingUp,
+ FileText,
+ BookOpen,
+ Menu,
+ X,
+ Check,
+ Circle,
+ Maximize2,
+ Minimize2,
+ Copy,
+ ThumbsUp,
+ ThumbsDown,
+ Sparkles,
+ Brain,
+ Rocket,
+ Paperclip,
+ Image as ImageIcon,
+ File,
+ Calendar,
+ Globe,
+ DollarSign,
+ Newspaper,
+ BarChart3,
+ PieChart,
+ LineChart,
+ Briefcase,
+ Users,
+} from 'lucide-react';
+
+// 常量配置 - 从 TypeScript 模块导入
+import { MessageTypes } from './constants/messageTypes';
+import { DEFAULT_MODEL_ID } from './constants/models';
+import { DEFAULT_SELECTED_TOOLS } from './constants/tools';
+
+// 拆分后的子组件
+import BackgroundEffects from './components/BackgroundEffects';
+import LeftSidebar from './components/LeftSidebar';
+import ChatArea from './components/ChatArea';
+import RightSidebar from './components/RightSidebar';
+
+/**
+ * Agent Chat - 主组件(HeroUI v3 深色主题)
+ *
+ * 注意:所有常量配置已提取到 constants/ 目录:
+ * - animations: constants/animations.ts
+ * - MessageTypes: constants/messageTypes.ts
+ * - AVAILABLE_MODELS: constants/models.ts
+ * - MCP_TOOLS, TOOL_CATEGORIES: constants/tools.ts
+ * - quickQuestions: constants/quickQuestions.ts
+ */
+const AgentChat = () => {
+ const { user } = useAuth();
+ const toast = useToast();
+ const { setColorMode } = useColorMode();
+
+ // 会话管理
+ const [sessions, setSessions] = useState([]);
+ const [currentSessionId, setCurrentSessionId] = useState(null);
+ const [isLoadingSessions, setIsLoadingSessions] = useState(false);
+
+ // 消息管理
+ const [messages, setMessages] = useState([]);
+ const [inputValue, setInputValue] = useState('');
+ const [isProcessing, setIsProcessing] = useState(false);
+
+ // UI 状态
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL_ID);
+ const [selectedTools, setSelectedTools] = useState(DEFAULT_SELECTED_TOOLS);
+ const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true);
+ const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(true);
+
+ // 文件上传
+ const [uploadedFiles, setUploadedFiles] = useState([]);
+ const fileInputRef = useRef(null);
+
+ // Refs
+ const messagesEndRef = useRef(null);
+ const inputRef = useRef(null);
+
+ // ==================== 启用深色模式 ====================
+ useEffect(() => {
+ // 为 AgentChat 页面强制启用深色模式
+ setColorMode('dark');
+ document.documentElement.classList.add('dark');
+
+ return () => {
+ // 组件卸载时不移除,让其他页面自己控制
+ // document.documentElement.classList.remove('dark');
+ };
+ }, [setColorMode]);
+
+ // ==================== 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);
+ }
+ } catch (error) {
+ logger.error('加载会话列表失败', error);
+ } finally {
+ setIsLoadingSessions(false);
+ }
+ };
+
+ 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) => ({
+ id: `${sessionId}-${idx}`,
+ type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
+ content: msg.message,
+ plan: msg.plan ? JSON.parse(msg.plan) : null,
+ stepResults: msg.steps ? JSON.parse(msg.steps) : null,
+ timestamp: msg.timestamp,
+ }));
+ setMessages(formattedMessages);
+ }
+ } catch (error) {
+ logger.error('加载会话历史失败', error);
+ }
+ };
+
+ const createNewSession = () => {
+ setCurrentSessionId(null);
+ setMessages([
+ {
+ id: Date.now(),
+ type: MessageTypes.AGENT_RESPONSE,
+ content: `你好${user?.nickname || ''}!👋\n\n我是**价小前**,你的 AI 投研助手。\n\n**我能做什么?**\n• 📊 全面分析股票基本面和技术面\n• 🔥 追踪市场热点和涨停板块\n• 📈 研究行业趋势和投资机会\n• 📰 汇总最新财经新闻和研报\n\n直接输入你的问题开始探索!`,
+ timestamp: new Date().toISOString(),
+ },
+ ]);
+ };
+
+ const switchSession = (sessionId) => {
+ setCurrentSessionId(sessionId);
+ loadSessionHistory(sessionId);
+ };
+
+ const handleSendMessage = async () => {
+ if (!inputValue.trim() || isProcessing) return;
+
+ const userMessage = {
+ type: MessageTypes.USER,
+ content: inputValue,
+ timestamp: new Date().toISOString(),
+ files: uploadedFiles.length > 0 ? uploadedFiles : undefined,
+ };
+
+ addMessage(userMessage);
+ const userInput = inputValue;
+ setInputValue('');
+ setUploadedFiles([]);
+ setIsProcessing(true);
+
+ try {
+ addMessage({
+ type: MessageTypes.AGENT_THINKING,
+ content: '正在分析你的问题...',
+ timestamp: new Date().toISOString(),
+ });
+
+ const response = await axios.post('/mcp/agent/chat', {
+ message: userInput,
+ conversation_history: messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((m) => ({
+ isUser: m.type === MessageTypes.USER,
+ content: m.content,
+ })),
+ user_id: user?.id || 'anonymous',
+ user_nickname: user?.nickname || '匿名用户',
+ user_avatar: user?.avatar || '',
+ subscription_type: user?.subscription_type || 'free',
+ session_id: currentSessionId,
+ model: selectedModel,
+ tools: selectedTools,
+ files: uploadedFiles.length > 0 ? uploadedFiles : undefined,
+ });
+
+ 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,
+ content: '已制定执行计划',
+ plan: data.plan,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ if (data.steps && data.steps.length > 0) {
+ addMessage({
+ type: MessageTypes.AGENT_EXECUTING,
+ content: '正在执行步骤...',
+ plan: data.plan,
+ stepResults: data.steps,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
+
+ addMessage({
+ type: MessageTypes.AGENT_RESPONSE,
+ content: data.final_answer || data.message || '处理完成',
+ plan: data.plan,
+ stepResults: data.steps,
+ metadata: data.metadata,
+ timestamp: new Date().toISOString(),
+ });
+
+ loadSessions();
+ }
+ } catch (error) {
+ logger.error('Agent chat error', error);
+ setMessages((prev) =>
+ prev.filter(
+ (m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
+ )
+ );
+
+ const errorMessage = error.response?.data?.error || error.message || '处理失败';
+ addMessage({
+ type: MessageTypes.ERROR,
+ content: `处理失败:${errorMessage}`,
+ timestamp: new Date().toISOString(),
+ });
+
+ toast({
+ title: '处理失败',
+ description: errorMessage,
+ status: 'error',
+ duration: 5000,
+ });
+ } finally {
+ setIsProcessing(false);
+ }
+ };
+
+ // 文件上传处理
+ const handleFileSelect = (event) => {
+ const files = Array.from(event.target.files || []);
+ const fileData = files.map(file => ({
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ // 实际上传时需要转换为 base64 或上传到服务器
+ url: URL.createObjectURL(file),
+ }));
+ setUploadedFiles(prev => [...prev, ...fileData]);
+ };
+
+ const removeFile = (index) => {
+ setUploadedFiles(prev => prev.filter((_, i) => i !== index));
+ };
+
+ const addMessage = (message) => {
+ setMessages((prev) => [
+ ...prev,
+ {
+ id: Date.now() + Math.random(),
+ ...message,
+ },
+ ]);
+ };
+
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ useEffect(() => {
+ loadSessions();
+ createNewSession();
+ }, [user]);
+
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages]);
+
+ return (
+
+
+ {/* 背景渐变装饰 */}
+
+
+ {/* 左侧栏 */}
+ setIsLeftSidebarOpen(false)}
+ sessions={sessions}
+ currentSessionId={currentSessionId}
+ onSessionSwitch={switchSession}
+ onNewSession={createNewSession}
+ isLoadingSessions={isLoadingSessions}
+ user={user}
+ />
+
+ {/* 中间主聊天区域 */}
+
+ {/* 顶部标题栏 - 深色毛玻璃 */}
+
+
+
+ {!isLeftSidebarOpen && (
+
+ }
+ onClick={() => setIsLeftSidebarOpen(true)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ color: "white"
+ }}
+ />
+
+ )}
+
+
+ }
+ bgGradient="linear(to-br, purple.500, pink.500)"
+ boxShadow="0 0 20px rgba(236, 72, 153, 0.5)"
+ />
+
+
+
+
+ 价小前投研 AI
+
+
+
+
+ 智能分析
+
+
+ {AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.name}
+
+
+
+
+
+
+
+
+ }
+ onClick={createNewSession}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ color: "white",
+ borderColor: "purple.400",
+ boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+ {!isRightSidebarOpen && (
+
+ }
+ onClick={() => setIsRightSidebarOpen(true)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.400"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ color: "white"
+ }}
+ />
+
+ )}
+
+
+
+
+ {/* 消息列表 */}
+
+
+
+
+ {messages.map((message) => (
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* 快捷问题 */}
+
+ {messages.length <= 2 && !isProcessing && (
+
+
+
+
+
+ 快速开始
+
+
+ {quickQuestions.map((question, idx) => (
+
+
+
+ ))}
+
+
+
+
+ )}
+
+
+ {/* 输入栏 - 深色毛玻璃 */}
+
+
+ {/* 已上传文件预览 */}
+ {uploadedFiles.length > 0 && (
+
+ {uploadedFiles.map((file, idx) => (
+
+
+ {file.name}
+ removeFile(idx)} color="gray.400" />
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ }
+ onClick={() => fileInputRef.current?.click()}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white",
+ boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+
+
+
+ }
+ onClick={() => {
+ fileInputRef.current?.setAttribute('accept', 'image/*');
+ fileInputRef.current?.click();
+ }}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white",
+ boxShadow: "0 0 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+
+ setInputValue(e.target.value)}
+ onKeyDown={handleKeyPress}
+ placeholder="输入你的问题... (Enter 发送, Shift+Enter 换行)"
+ isDisabled={isProcessing}
+ size="lg"
+ variant="outline"
+ borderWidth={2}
+ bg="rgba(255, 255, 255, 0.05)"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ color="white"
+ _placeholder={{ color: "gray.500" }}
+ _hover={{
+ borderColor: "rgba(255, 255, 255, 0.2)"
+ }}
+ _focus={{
+ borderColor: "purple.400",
+ boxShadow: "0 0 0 1px var(--chakra-colors-purple-400), 0 0 12px rgba(139, 92, 246, 0.3)",
+ bg: "rgba(255, 255, 255, 0.08)"
+ }}
+ />
+
+
+ }
+ onClick={handleSendMessage}
+ isLoading={isProcessing}
+ isDisabled={!inputValue.trim() || isProcessing}
+ bgGradient="linear(to-r, blue.500, purple.600)"
+ color="white"
+ _hover={{
+ bgGradient: "linear(to-r, blue.600, purple.700)",
+ boxShadow: "0 8px 20px rgba(139, 92, 246, 0.4)"
+ }}
+ _active={{
+ transform: "translateY(0)",
+ boxShadow: "0 4px 12px rgba(139, 92, 246, 0.3)"
+ }}
+ />
+
+
+
+
+
+ Enter
+ 发送
+
+
+ Shift
+ +
+ Enter
+ 换行
+
+
+
+
+
+
+ {/* 右侧栏 - 深色配置中心 */}
+
+ {isRightSidebarOpen && (
+
+
+
+
+
+
+
+ 配置中心
+
+
+
+
+ }
+ onClick={() => setIsRightSidebarOpen(false)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white"
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ 模型
+
+
+
+
+
+ 工具
+ {selectedTools.length > 0 && (
+
+ {selectedTools.length}
+
+ )}
+
+
+
+
+
+ 统计
+
+
+
+
+
+ {/* 模型选择 */}
+
+
+ {AVAILABLE_MODELS.map((model, idx) => (
+
+ setSelectedModel(model.id)}
+ bg={selectedModel === model.id
+ ? 'rgba(139, 92, 246, 0.15)'
+ : 'rgba(255, 255, 255, 0.05)'}
+ backdropFilter="blur(12px)"
+ borderWidth={2}
+ borderColor={selectedModel === model.id
+ ? 'purple.400'
+ : 'rgba(255, 255, 255, 0.1)'}
+ _hover={{
+ borderColor: selectedModel === model.id
+ ? 'purple.400'
+ : 'rgba(255, 255, 255, 0.2)',
+ boxShadow: selectedModel === model.id
+ ? "0 8px 20px rgba(139, 92, 246, 0.4)"
+ : "0 4px 12px rgba(0, 0, 0, 0.3)"
+ }}
+ transition="all 0.3s"
+ >
+
+
+
+ {model.icon}
+
+
+
+ {model.name}
+
+
+ {model.description}
+
+
+ {selectedModel === model.id && (
+
+
+
+ )}
+
+
+
+
+ ))}
+
+
+
+ {/* 工具选择 */}
+
+
+ {Object.entries(TOOL_CATEGORIES).map(([category, tools], catIdx) => (
+
+
+
+
+ {category}
+
+ {tools.filter(t => selectedTools.includes(t.id)).length}/{tools.length}
+
+
+
+
+
+
+
+ {tools.map((tool) => (
+
+
+
+ {tool.icon}
+
+ {tool.name}
+ {tool.description}
+
+
+
+
+ ))}
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 统计信息 */}
+
+
+
+
+
+
+
+ 对话数
+
+ {sessions.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 消息数
+
+ {messages.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已选工具
+
+ {selectedTools.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default AgentChat;
diff --git a/src/views/AgentChat/index.js.bak3 b/src/views/AgentChat/index.js.bak3
new file mode 100644
index 00000000..324d7229
--- /dev/null
+++ b/src/views/AgentChat/index.js.bak3
@@ -0,0 +1,816 @@
+// src/views/AgentChat/index.js
+// 超炫酷的 AI 投研助手 - HeroUI v3 现代深色主题版本
+// 使用 Framer Motion 物理动画引擎 + 毛玻璃效果
+
+import React, { useState, useEffect, useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import {
+ Box,
+ Button,
+ Input,
+ Avatar,
+ Badge,
+ Divider,
+ Spinner,
+ Tooltip,
+ Checkbox,
+ CheckboxGroup,
+ Kbd,
+ Accordion,
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ AccordionIcon,
+ Tabs,
+ TabList,
+ TabPanels,
+ Tab,
+ TabPanel,
+ useToast,
+ VStack,
+ HStack,
+ Text,
+ Flex,
+ IconButton,
+ useColorMode,
+ Card,
+ CardBody,
+ Tag,
+ TagLabel,
+ TagCloseButton,
+} from '@chakra-ui/react';
+import { useAuth } from '@contexts/AuthContext';
+import { logger } from '@utils/logger';
+import axios from 'axios';
+
+// 图标 - 使用 Lucide Icons
+import {
+ Send,
+ Plus,
+ Search,
+ MessageSquare,
+ Trash2,
+ MoreVertical,
+ RefreshCw,
+ Download,
+ Cpu,
+ User,
+ Zap,
+ Clock,
+ Settings,
+ ChevronLeft,
+ ChevronRight,
+ Activity,
+ Code,
+ Database,
+ TrendingUp,
+ FileText,
+ BookOpen,
+ Menu,
+ X,
+ Check,
+ Circle,
+ Maximize2,
+ Minimize2,
+ Copy,
+ ThumbsUp,
+ ThumbsDown,
+ Sparkles,
+ Brain,
+ Rocket,
+ Paperclip,
+ Image as ImageIcon,
+ File,
+ Calendar,
+ Globe,
+ DollarSign,
+ Newspaper,
+ BarChart3,
+ PieChart,
+ LineChart,
+ Briefcase,
+ Users,
+} from 'lucide-react';
+
+// 常量配置 - 从 TypeScript 模块导入
+import { MessageTypes } from './constants/messageTypes';
+import { DEFAULT_MODEL_ID } from './constants/models';
+import { DEFAULT_SELECTED_TOOLS } from './constants/tools';
+
+// 拆分后的子组件
+import BackgroundEffects from './components/BackgroundEffects';
+import LeftSidebar from './components/LeftSidebar';
+import ChatArea from './components/ChatArea';
+import RightSidebar from './components/RightSidebar';
+
+/**
+ * Agent Chat - 主组件(HeroUI v3 深色主题)
+ *
+ * 注意:所有常量配置已提取到 constants/ 目录:
+ * - animations: constants/animations.ts
+ * - MessageTypes: constants/messageTypes.ts
+ * - AVAILABLE_MODELS: constants/models.ts
+ * - MCP_TOOLS, TOOL_CATEGORIES: constants/tools.ts
+ * - quickQuestions: constants/quickQuestions.ts
+ */
+const AgentChat = () => {
+ const { user } = useAuth();
+ const toast = useToast();
+ const { setColorMode } = useColorMode();
+
+ // 会话管理
+ const [sessions, setSessions] = useState([]);
+ const [currentSessionId, setCurrentSessionId] = useState(null);
+ const [isLoadingSessions, setIsLoadingSessions] = useState(false);
+
+ // 消息管理
+ const [messages, setMessages] = useState([]);
+ const [inputValue, setInputValue] = useState('');
+ const [isProcessing, setIsProcessing] = useState(false);
+
+ // UI 状态
+ const [searchQuery, setSearchQuery] = useState('');
+ const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL_ID);
+ const [selectedTools, setSelectedTools] = useState(DEFAULT_SELECTED_TOOLS);
+ const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true);
+ const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(true);
+
+ // 文件上传
+ const [uploadedFiles, setUploadedFiles] = useState([]);
+ const fileInputRef = useRef(null);
+
+ // Refs
+ const messagesEndRef = useRef(null);
+ const inputRef = useRef(null);
+
+ // ==================== 启用深色模式 ====================
+ useEffect(() => {
+ // 为 AgentChat 页面强制启用深色模式
+ setColorMode('dark');
+ document.documentElement.classList.add('dark');
+
+ return () => {
+ // 组件卸载时不移除,让其他页面自己控制
+ // document.documentElement.classList.remove('dark');
+ };
+ }, [setColorMode]);
+
+ // ==================== 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);
+ }
+ } catch (error) {
+ logger.error('加载会话列表失败', error);
+ } finally {
+ setIsLoadingSessions(false);
+ }
+ };
+
+ 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) => ({
+ id: `${sessionId}-${idx}`,
+ type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
+ content: msg.message,
+ plan: msg.plan ? JSON.parse(msg.plan) : null,
+ stepResults: msg.steps ? JSON.parse(msg.steps) : null,
+ timestamp: msg.timestamp,
+ }));
+ setMessages(formattedMessages);
+ }
+ } catch (error) {
+ logger.error('加载会话历史失败', error);
+ }
+ };
+
+ const createNewSession = () => {
+ setCurrentSessionId(null);
+ setMessages([
+ {
+ id: Date.now(),
+ type: MessageTypes.AGENT_RESPONSE,
+ content: `你好${user?.nickname || ''}!👋\n\n我是**价小前**,你的 AI 投研助手。\n\n**我能做什么?**\n• 📊 全面分析股票基本面和技术面\n• 🔥 追踪市场热点和涨停板块\n• 📈 研究行业趋势和投资机会\n• 📰 汇总最新财经新闻和研报\n\n直接输入你的问题开始探索!`,
+ timestamp: new Date().toISOString(),
+ },
+ ]);
+ };
+
+ const switchSession = (sessionId) => {
+ setCurrentSessionId(sessionId);
+ loadSessionHistory(sessionId);
+ };
+
+ const handleSendMessage = async () => {
+ if (!inputValue.trim() || isProcessing) return;
+
+ const userMessage = {
+ type: MessageTypes.USER,
+ content: inputValue,
+ timestamp: new Date().toISOString(),
+ files: uploadedFiles.length > 0 ? uploadedFiles : undefined,
+ };
+
+ addMessage(userMessage);
+ const userInput = inputValue;
+ setInputValue('');
+ setUploadedFiles([]);
+ setIsProcessing(true);
+
+ try {
+ addMessage({
+ type: MessageTypes.AGENT_THINKING,
+ content: '正在分析你的问题...',
+ timestamp: new Date().toISOString(),
+ });
+
+ const response = await axios.post('/mcp/agent/chat', {
+ message: userInput,
+ conversation_history: messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((m) => ({
+ isUser: m.type === MessageTypes.USER,
+ content: m.content,
+ })),
+ user_id: user?.id || 'anonymous',
+ user_nickname: user?.nickname || '匿名用户',
+ user_avatar: user?.avatar || '',
+ subscription_type: user?.subscription_type || 'free',
+ session_id: currentSessionId,
+ model: selectedModel,
+ tools: selectedTools,
+ files: uploadedFiles.length > 0 ? uploadedFiles : undefined,
+ });
+
+ 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,
+ content: '已制定执行计划',
+ plan: data.plan,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ if (data.steps && data.steps.length > 0) {
+ addMessage({
+ type: MessageTypes.AGENT_EXECUTING,
+ content: '正在执行步骤...',
+ plan: data.plan,
+ stepResults: data.steps,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
+
+ addMessage({
+ type: MessageTypes.AGENT_RESPONSE,
+ content: data.final_answer || data.message || '处理完成',
+ plan: data.plan,
+ stepResults: data.steps,
+ metadata: data.metadata,
+ timestamp: new Date().toISOString(),
+ });
+
+ loadSessions();
+ }
+ } catch (error) {
+ logger.error('Agent chat error', error);
+ setMessages((prev) =>
+ prev.filter(
+ (m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
+ )
+ );
+
+ const errorMessage = error.response?.data?.error || error.message || '处理失败';
+ addMessage({
+ type: MessageTypes.ERROR,
+ content: `处理失败:${errorMessage}`,
+ timestamp: new Date().toISOString(),
+ });
+
+ toast({
+ title: '处理失败',
+ description: errorMessage,
+ status: 'error',
+ duration: 5000,
+ });
+ } finally {
+ setIsProcessing(false);
+ }
+ };
+
+ // 文件上传处理
+ const handleFileSelect = (event) => {
+ const files = Array.from(event.target.files || []);
+ const fileData = files.map(file => ({
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ // 实际上传时需要转换为 base64 或上传到服务器
+ url: URL.createObjectURL(file),
+ }));
+ setUploadedFiles(prev => [...prev, ...fileData]);
+ };
+
+ const removeFile = (index) => {
+ setUploadedFiles(prev => prev.filter((_, i) => i !== index));
+ };
+
+ const addMessage = (message) => {
+ setMessages((prev) => [
+ ...prev,
+ {
+ id: Date.now() + Math.random(),
+ ...message,
+ },
+ ]);
+ };
+
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ useEffect(() => {
+ loadSessions();
+ createNewSession();
+ }, [user]);
+
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ }, [messages]);
+
+ return (
+
+
+ {/* 背景渐变装饰 */}
+
+
+ {/* 左侧栏 */}
+ setIsLeftSidebarOpen(false)}
+ sessions={sessions}
+ currentSessionId={currentSessionId}
+ onSessionSwitch={switchSession}
+ onNewSession={createNewSession}
+ isLoadingSessions={isLoadingSessions}
+ user={user}
+ />
+
+ {/* 中间聊天区 */}
+ setIsLeftSidebarOpen(true)}
+ onToggleRightSidebar={() => setIsRightSidebarOpen(true)}
+ onNewSession={createNewSession}
+ userAvatar={user?.avatar}
+ messagesEndRef={messagesEndRef}
+ inputRef={inputRef}
+ fileInputRef={fileInputRef}
+ />
+
+ {/* 右侧栏 - 深色配置中心 */}
+
+ {isRightSidebarOpen && (
+
+
+
+
+
+
+
+ 配置中心
+
+
+
+
+ }
+ onClick={() => setIsRightSidebarOpen(false)}
+ bg="rgba(255, 255, 255, 0.05)"
+ color="gray.300"
+ backdropFilter="blur(10px)"
+ border="1px solid"
+ borderColor="rgba(255, 255, 255, 0.1)"
+ _hover={{
+ bg: "rgba(255, 255, 255, 0.1)",
+ borderColor: "purple.400",
+ color: "white"
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ 模型
+
+
+
+
+
+ 工具
+ {selectedTools.length > 0 && (
+
+ {selectedTools.length}
+
+ )}
+
+
+
+
+
+ 统计
+
+
+
+
+
+ {/* 模型选择 */}
+
+
+ {AVAILABLE_MODELS.map((model, idx) => (
+
+ setSelectedModel(model.id)}
+ bg={selectedModel === model.id
+ ? 'rgba(139, 92, 246, 0.15)'
+ : 'rgba(255, 255, 255, 0.05)'}
+ backdropFilter="blur(12px)"
+ borderWidth={2}
+ borderColor={selectedModel === model.id
+ ? 'purple.400'
+ : 'rgba(255, 255, 255, 0.1)'}
+ _hover={{
+ borderColor: selectedModel === model.id
+ ? 'purple.400'
+ : 'rgba(255, 255, 255, 0.2)',
+ boxShadow: selectedModel === model.id
+ ? "0 8px 20px rgba(139, 92, 246, 0.4)"
+ : "0 4px 12px rgba(0, 0, 0, 0.3)"
+ }}
+ transition="all 0.3s"
+ >
+
+
+
+ {model.icon}
+
+
+
+ {model.name}
+
+
+ {model.description}
+
+
+ {selectedModel === model.id && (
+
+
+
+ )}
+
+
+
+
+ ))}
+
+
+
+ {/* 工具选择 */}
+
+
+ {Object.entries(TOOL_CATEGORIES).map(([category, tools], catIdx) => (
+
+
+
+
+ {category}
+
+ {tools.filter(t => selectedTools.includes(t.id)).length}/{tools.length}
+
+
+
+
+
+
+
+ {tools.map((tool) => (
+
+
+
+ {tool.icon}
+
+ {tool.name}
+ {tool.description}
+
+
+
+
+ ))}
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 统计信息 */}
+
+
+
+
+
+
+
+ 对话数
+
+ {sessions.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 消息数
+
+ {messages.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已选工具
+
+ {selectedTools.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default AgentChat;