+
}
- classNames={{ base: 'bg-gradient-to-br from-blue-500 to-purple-500' }}
size="sm"
+ classNames={{
+ base: 'bg-gradient-to-br from-purple-500 to-pink-500',
+ }}
/>
-
-
-
- {message.content}
+
+
+
+ {message.content}
+
-
-
-
-
-
-
-
-
-
-
-
- {message.metadata && (
-
- 步骤: {message.metadata.total_steps}
- ✓ {message.metadata.successful_steps}
- {message.metadata.failed_steps > 0 && (
- ✗ {message.metadata.failed_steps}
- )}
- ⏱ {message.metadata.total_execution_time?.toFixed(1)}s
-
- )}
+ {message.stepResults && message.stepResults.length > 0 && (
+
+
-
-
+ )}
- {message.stepResults && message.stepResults.length > 0 && (
-
- )}
-
+
+
+
+
+
+
+
+
+
+
+
+ {new Date(message.timestamp).toLocaleTimeString('zh-CN', {
+ hour: '2-digit',
+ minute: '2-digit',
+ })}
+
+
+
+
);
case MessageTypes.ERROR:
return (
-
-
-
}
- classNames={{ base: 'bg-danger-500' }}
- size="sm"
- />
-
-
- {message.content}
-
-
-
+
+
+
+ {message.content}
+
+
);
@@ -957,56 +1504,61 @@ const MessageRenderer = ({ message, userAvatar }) => {
};
/**
- * 步骤结果面板
+ * 执行步骤显示组件
*/
-const StepResultsPanel = ({ stepResults }) => {
+const ExecutionStepsDisplay = ({ steps, plan }) => {
return (
-
+
-
- 执行详情 ({stepResults.length} 个步骤)
+
+ 执行详情
+
+ {steps.length} 步骤
+
}
>
- {stepResults.map((result, idx) => (
-
+ {steps.map((result, idx) => (
+
-
-
-
- 步骤 {idx + 1}: {result.tool}
-
-
- {result.status}
-
-
-
{result.execution_time?.toFixed(2)}s
+
+
+ 步骤 {idx + 1}: {result.tool_name}
+
+
+ {result.status}
+
- {result.error &&
⚠️ {result.error}
}
+
{result.execution_time?.toFixed(2)}s
+ {result.error &&
⚠️ {result.error}
}
))}
diff --git a/src/views/AgentChat/index_old_chakra.js b/src/views/AgentChat/index_old_chakra.js
index 29d7097c..39cdefa4 100644
--- a/src/views/AgentChat/index_old_chakra.js
+++ b/src/views/AgentChat/index_old_chakra.js
@@ -1,63 +1,147 @@
-// src/views/AgentChat/index_v3.js
-// Agent聊天页面 V3 - 带左侧会话列表和用户信息集成
+// src/views/AgentChat/index.js
+// 超炫酷的 AI 投研助手 - Hero UI 版本
+// 使用 Framer Motion 物理动画引擎
import React, { useState, useEffect, useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
import {
- Box,
- Flex,
- VStack,
- HStack,
- Text,
- Input,
- IconButton,
Button,
+ Card,
+ CardHeader,
+ CardBody,
+ CardFooter,
+ Input,
Avatar,
- Heading,
+ Chip,
Divider,
Spinner,
- Badge,
- useColorModeValue,
- useToast,
- Progress,
- Fade,
- Collapse,
- useDisclosure,
- InputGroup,
- InputLeftElement,
- Menu,
- MenuButton,
- MenuList,
- MenuItem,
- Modal,
- ModalOverlay,
- ModalContent,
- ModalHeader,
- ModalBody,
- ModalCloseButton,
Tooltip,
-} from '@chakra-ui/react';
-import {
- FiSend,
- FiSearch,
- FiPlus,
- FiMessageSquare,
- FiTrash2,
- FiMoreVertical,
- FiRefreshCw,
- FiDownload,
- FiCpu,
- FiUser,
- FiZap,
- FiClock,
-} from 'react-icons/fi';
+ Badge,
+ Checkbox,
+ CheckboxGroup,
+ Tabs,
+ Tab,
+ ScrollShadow,
+ Kbd,
+ Accordion,
+ AccordionItem,
+} from '@heroui/react';
import { useAuth } from '@contexts/AuthContext';
-import { PlanCard } from '@components/ChatBot/PlanCard';
-import { StepResultCard } from '@components/ChatBot/StepResultCard';
import { logger } from '@utils/logger';
import axios from 'axios';
+import { useToast } from '@chakra-ui/react';
+
+// 图标 - 使用 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,
+} from 'lucide-react';
/**
- * Agent消息类型
+ * Framer Motion 动画变体配置
+ */
+const animations = {
+ // 侧边栏滑入动画(带弹性效果)
+ slideInLeft: {
+ initial: { x: -320, opacity: 0 },
+ animate: {
+ x: 0,
+ opacity: 1,
+ transition: {
+ type: 'spring',
+ stiffness: 300,
+ damping: 30,
+ },
+ },
+ exit: {
+ x: -320,
+ opacity: 0,
+ transition: { duration: 0.2 },
+ },
+ },
+ slideInRight: {
+ initial: { x: 320, opacity: 0 },
+ animate: {
+ x: 0,
+ opacity: 1,
+ transition: {
+ type: 'spring',
+ stiffness: 300,
+ damping: 30,
+ },
+ },
+ exit: {
+ x: 320,
+ opacity: 0,
+ transition: { duration: 0.2 },
+ },
+ },
+ // 消息淡入动画(带上移效果)
+ fadeInUp: {
+ initial: { opacity: 0, y: 20 },
+ animate: {
+ opacity: 1,
+ y: 0,
+ transition: {
+ type: 'spring',
+ stiffness: 400,
+ damping: 25,
+ },
+ },
+ },
+ // 列表项淡入(错开延迟)
+ staggerItem: {
+ initial: { opacity: 0, y: 10 },
+ animate: { opacity: 1, y: 0 },
+ },
+ // 父容器错开动画
+ staggerContainer: {
+ animate: {
+ transition: {
+ staggerChildren: 0.05,
+ },
+ },
+ },
+ // 按钮点击动画
+ pressScale: {
+ whileTap: { scale: 0.95 },
+ whileHover: { scale: 1.05 },
+ },
+};
+
+/**
+ * 消息类型
*/
const MessageTypes = {
USER: 'user',
@@ -69,84 +153,102 @@ const MessageTypes = {
};
/**
- * Agent聊天页面 V3
+ * 可用模型配置
*/
-const AgentChatV3 = () => {
- const { user } = useAuth(); // 获取当前用户信息
+const AVAILABLE_MODELS = [
+ {
+ id: 'kimi-k2-thinking',
+ name: 'Kimi K2 Thinking',
+ description: '深度思考模型,适合复杂分析',
+ icon:
,
+ color: 'secondary',
+ },
+ {
+ id: 'kimi-k2',
+ name: 'Kimi K2',
+ description: '快速响应模型,适合简单查询',
+ icon:
,
+ color: 'primary',
+ },
+ {
+ id: 'deepmoney',
+ name: 'DeepMoney',
+ description: '金融专业模型',
+ icon:
,
+ color: 'success',
+ },
+];
+
+/**
+ * 可用工具配置
+ */
+const AVAILABLE_TOOLS = [
+ { id: 'search_news', name: '新闻搜索', icon:
},
+ { id: 'search_limit_up', name: '涨停分析', icon:
},
+ { id: 'search_concept', name: '概念板块', icon:
},
+ { id: 'search_research_report', name: '研报搜索', icon:
},
+ { id: 'search_roadshow', name: '路演信息', icon:
},
+];
+
+/**
+ * Hero Agent Chat - 主组件
+ */
+const AgentChat = () => {
+ const { user } = useAuth();
const toast = useToast();
- // 会话相关状态
+ // 会话管理
const [sessions, setSessions] = useState([]);
const [currentSessionId, setCurrentSessionId] = useState(null);
- const [isLoadingSessions, setIsLoadingSessions] = useState(true);
+ const [isLoadingSessions, setIsLoadingSessions] = useState(false);
- // 消息相关状态
+ // 消息管理
const [messages, setMessages] = useState([]);
const [inputValue, setInputValue] = useState('');
const [isProcessing, setIsProcessing] = useState(false);
- const [currentProgress, setCurrentProgress] = useState(0);
// UI 状态
const [searchQuery, setSearchQuery] = useState('');
- const { isOpen: isSidebarOpen, onToggle: toggleSidebar } = useDisclosure({ defaultIsOpen: true });
+ const [selectedModel, setSelectedModel] = useState('kimi-k2-thinking');
+ const [selectedTools, setSelectedTools] = useState([
+ 'search_news',
+ 'search_limit_up',
+ 'search_concept',
+ ]);
+ const [isLeftSidebarOpen, setIsLeftSidebarOpen] = useState(true);
+ const [isRightSidebarOpen, setIsRightSidebarOpen] = useState(true);
// Refs
const messagesEndRef = useRef(null);
const inputRef = useRef(null);
- // 颜色主题
- const bgColor = useColorModeValue('gray.50', 'gray.900');
- const sidebarBg = useColorModeValue('white', 'gray.800');
- const chatBg = useColorModeValue('white', 'gray.800');
- const inputBg = useColorModeValue('white', 'gray.700');
- const borderColor = useColorModeValue('gray.200', 'gray.600');
- const hoverBg = useColorModeValue('gray.100', 'gray.700');
- const activeBg = useColorModeValue('blue.50', 'blue.900');
- const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
- const agentBubbleBg = useColorModeValue('white', 'gray.700');
+ // ==================== 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);
- logger.info('会话列表加载成功', response.data.data);
}
} catch (error) {
logger.error('加载会话列表失败', error);
- toast({
- title: '加载失败',
- description: '无法加载会话列表',
- status: 'error',
- duration: 3000,
- });
} 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,
@@ -155,83 +257,33 @@ const AgentChatV3 = () => {
stepResults: msg.steps ? JSON.parse(msg.steps) : null,
timestamp: msg.timestamp,
}));
-
setMessages(formattedMessages);
- logger.info('会话历史加载成功', formattedMessages);
}
} catch (error) {
logger.error('加载会话历史失败', error);
- toast({
- title: '加载失败',
- description: '无法加载会话历史',
- status: 'error',
- duration: 3000,
- });
}
};
- // 创建新会话
const createNewSession = () => {
setCurrentSessionId(null);
setMessages([
{
id: Date.now(),
type: MessageTypes.AGENT_RESPONSE,
- content: `你好${user?.nickname || ''}!我是价小前,北京价值前沿科技公司的AI投研助手。\n\n我会通过多步骤分析来帮助你深入了解金融市场。\n\n你可以问我:\n• 全面分析某只股票\n• 某个行业的投资机会\n• 今日市场热点\n• 某个概念板块的表现`,
+ 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);
};
- // 删除会话(需要后端API支持)
- const deleteSession = async (sessionId) => {
- // TODO: 实现删除会话的后端API
- toast({
- title: '删除会话',
- description: '此功能尚未实现',
- status: 'info',
- duration: 2000,
- });
- };
-
- // ==================== 消息处理函数 ====================
-
- // 自动滚动到底部
- const scrollToBottom = () => {
- messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
- };
-
- useEffect(() => {
- scrollToBottom();
- }, [messages]);
-
- // 添加消息
- const addMessage = (message) => {
- setMessages((prev) => [...prev, { ...message, id: Date.now() }]);
- };
-
- // 发送消息
const handleSendMessage = async () => {
if (!inputValue.trim() || isProcessing) return;
- // 权限检查
- if (user?.id !== 'max') {
- toast({
- title: '权限不足',
- description: '「价小前投研」功能目前仅对特定用户开放。如需使用,请联系管理员。',
- status: 'warning',
- duration: 5000,
- isClosable: true,
- });
- return;
- }
-
const userMessage = {
type: MessageTypes.USER,
content: inputValue,
@@ -242,22 +294,14 @@ const AgentChatV3 = () => {
const userInput = inputValue;
setInputValue('');
setIsProcessing(true);
- setCurrentProgress(0);
-
- let currentPlan = null;
- let stepResults = [];
try {
- // 1. 显示思考状态
addMessage({
type: MessageTypes.AGENT_THINKING,
content: '正在分析你的问题...',
timestamp: new Date().toISOString(),
});
- setCurrentProgress(10);
-
- // 2. 调用后端API(非流式)
const response = await axios.post('/mcp/agent/chat', {
message: userInput,
conversation_history: messages
@@ -269,67 +313,54 @@ const AgentChatV3 = () => {
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,
});
- // 移除思考消息
setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_THINKING));
if (response.data.success) {
const data = response.data;
-
- // 更新 session_id(如果是新会话)
if (data.session_id && !currentSessionId) {
setCurrentSessionId(data.session_id);
}
- // 显示执行计划
if (data.plan) {
- currentPlan = data.plan;
addMessage({
type: MessageTypes.AGENT_PLAN,
content: '已制定执行计划',
plan: data.plan,
timestamp: new Date().toISOString(),
});
- setCurrentProgress(30);
}
- // 显示执行步骤
if (data.steps && data.steps.length > 0) {
- stepResults = data.steps;
addMessage({
type: MessageTypes.AGENT_EXECUTING,
content: '正在执行步骤...',
- plan: currentPlan,
- stepResults: stepResults,
+ plan: data.plan,
+ stepResults: data.steps,
timestamp: new Date().toISOString(),
});
- setCurrentProgress(70);
}
- // 移除执行中消息
setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
- // 显示最终结果
addMessage({
type: MessageTypes.AGENT_RESPONSE,
content: data.final_answer || data.message || '处理完成',
- plan: currentPlan,
- stepResults: stepResults,
+ plan: data.plan,
+ stepResults: data.steps,
metadata: data.metadata,
timestamp: new Date().toISOString(),
});
- setCurrentProgress(100);
-
- // 重新加载会话列表
loadSessions();
}
} catch (error) {
logger.error('Agent chat error', error);
-
- // 移除思考/执行中消息
setMessages((prev) =>
prev.filter(
(m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
@@ -337,7 +368,6 @@ const AgentChatV3 = () => {
);
const errorMessage = error.response?.data?.error || error.message || '处理失败';
-
addMessage({
type: MessageTypes.ERROR,
content: `处理失败:${errorMessage}`,
@@ -353,12 +383,22 @@ const AgentChatV3 = () => {
});
} finally {
setIsProcessing(false);
- setCurrentProgress(0);
inputRef.current?.focus();
}
};
- // 处理键盘事件
+ const addMessage = (message) => {
+ setMessages((prev) => [...prev, { ...message, id: Date.now() + Math.random() }]);
+ };
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages]);
+
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
@@ -366,29 +406,6 @@ const AgentChatV3 = () => {
}
};
- // 清空对话
- const handleClearChat = () => {
- createNewSession();
- };
-
- // 导出对话
- const handleExportChat = () => {
- const chatText = messages
- .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
- .map((msg) => `[${msg.type === MessageTypes.USER ? '用户' : '价小前'}] ${msg.content}`)
- .join('\n\n');
-
- const blob = new Blob([chatText], { type: 'text/plain' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = `chat_${new Date().toISOString().slice(0, 10)}.txt`;
- a.click();
- URL.revokeObjectURL(url);
- };
-
- // ==================== 初始化 ====================
-
useEffect(() => {
if (user) {
loadSessions();
@@ -396,457 +413,542 @@ const AgentChatV3 = () => {
}
}, [user]);
- // ==================== 渲染 ====================
-
- // 快捷问题
- const quickQuestions = [
- '全面分析贵州茅台这只股票',
- '今日涨停股票有哪些亮点',
- '新能源概念板块的投资机会',
- '半导体行业最新动态',
- ];
-
- // 筛选会话
const filteredSessions = sessions.filter(
(session) =>
- !searchQuery ||
- session.last_message?.toLowerCase().includes(searchQuery.toLowerCase())
+ !searchQuery || session.last_message?.toLowerCase().includes(searchQuery.toLowerCase())
);
+ const quickQuestions = [
+ { text: '全面分析贵州茅台', emoji: '📊' },
+ { text: '今日涨停股票分析', emoji: '🔥' },
+ { text: '新能源概念机会', emoji: '⚡' },
+ { text: '半导体行业动态', emoji: '💾' },
+ ];
+
return (
-
- {/* 左侧会话列表 */}
-
-
+ {/* 左侧栏 */}
+ {isLeftSidebarOpen && (
+
- {/* 侧边栏头部 */}
-
+
+
+
+
+
对话历史
+
+
+
+
}
- colorScheme="blue"
- w="100%"
- onClick={createNewSession}
- size="sm"
+ color="primary"
+ variant="shadow"
+ startContent={
}
+ onPress={createNewSession}
+ className="w-full mb-3"
>
新建对话
- {/* 搜索框 */}
-
-
-
-
- setSearchQuery(e.target.value)}
- />
-
-
-
- {/* 会话列表 */}
-
- {isLoadingSessions ? (
-
-
-
- ) : filteredSessions.length === 0 ? (
-
-
-
- {searchQuery ? '没有找到匹配的对话' : '暂无对话记录'}
-
-
- ) : (
- filteredSessions.map((session) => (
- switchSession(session.session_id)}
- >
-
-
-
- {session.last_message || '新对话'}
-
-
-
-
- {new Date(session.last_timestamp).toLocaleDateString('zh-CN', {
- month: 'numeric',
- day: 'numeric',
- hour: 'numeric',
- minute: 'numeric',
- })}
-
-
- {session.message_count} 条
-
-
-
-
-
-
-
- ))
- )}
-
-
- {/* 用户信息 */}
-
-
-
-
-
- {user?.nickname || '未登录'}
-
-
- {user?.id || 'anonymous'}
-
-
-
-
-
-
-
- {/* 主聊天区域 */}
-
- {/* 聊天头部 */}
-
-
-
- }
- size="sm"
- variant="ghost"
- aria-label="切换侧边栏"
- onClick={toggleSidebar}
- />
- } />
-
- 价小前投研
-
-
-
-
- 智能分析
-
-
-
- 多步骤深度研究
-
-
-
-
-
-
- }
- size="sm"
- variant="ghost"
- aria-label="清空对话"
- onClick={handleClearChat}
- />
- }
- size="sm"
- variant="ghost"
- aria-label="导出对话"
- onClick={handleExportChat}
- />
-
-
-
- {/* 进度条 */}
- {isProcessing && (
-
- )}
-
-
- {/* 消息列表 */}
-
-
- {messages.map((message) => (
-
-
-
- ))}
-
-
-
-
- {/* 快捷问题 */}
- {messages.length <= 2 && !isProcessing && (
-
-
- 💡 试试这些问题:
-
-
- {quickQuestions.map((question, idx) => (
-
- ))}
-
-
- )}
-
- {/* 输入框 */}
-
-
setInputValue(e.target.value)}
- onKeyPress={handleKeyPress}
- placeholder="输入你的问题,我会进行深度分析..."
- bg={inputBg}
- border="1px"
- borderColor={borderColor}
- _focus={{ borderColor: 'blue.500', boxShadow: '0 0 0 1px #3182CE' }}
- mr={2}
- disabled={isProcessing}
- size="lg"
+ placeholder="搜索对话..."
+ value={searchQuery}
+ onChange={(e) => setSearchQuery(e.target.value)}
+ startContent={}
+ size="sm"
+ variant="bordered"
/>
- : }
- colorScheme="blue"
- aria-label="发送"
- onClick={handleSendMessage}
- isLoading={isProcessing}
- disabled={!inputValue.trim() || isProcessing}
- size="lg"
- />
-
-
-
-
+
+
+
+ {isLoadingSessions ? (
+
+
+
+ ) : filteredSessions.length === 0 ? (
+
+
+
{searchQuery ? '没有找到匹配的对话' : '暂无对话记录'}
+
+ ) : (
+
+ {filteredSessions.map((session) => (
+
switchSession(session.session_id)}
+ className={`transition-all ${
+ currentSessionId === session.session_id
+ ? 'bg-primary-50 dark:bg-primary-900/20 border-primary-500'
+ : ''
+ }`}
+ >
+
+
+
+
+ {session.last_message || '新对话'}
+
+
+
+
+ {new Date(session.last_timestamp).toLocaleDateString('zh-CN', {
+ month: 'numeric',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ })}
+
+
+ {session.message_count}
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
{user?.nickname || '未登录'}
+
{user?.id || 'anonymous'}
+
+
+
+
+ )}
+
+ {/* 中间主聊天区域 */}
+
+
+
+
+ {!isLeftSidebarOpen && (
+
+ )}
+
+
+ }
+ classNames={{
+ base: 'bg-gradient-to-br from-purple-500 to-pink-500',
+ }}
+ />
+
+
+
+
+ 价小前投研 AI
+
+
+ }
+ >
+ 智能分析
+
+
+ {AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.name}
+
+
+
+
+
+
+
+
+
+ {!isRightSidebarOpen && (
+
+ )}
+
+
+
+
+
+
+
+ {messages.map((message, index) => (
+
+
+
+ ))}
+
+
+
+
+
+
+ {messages.length <= 2 && !isProcessing && (
+
+
+
+
+ 快速开始
+
+
+ {quickQuestions.map((question, idx) => (
+
+
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter
+ 发送
+
+
+ Shift
+ +
+ Enter
+ 换行
+
+
+
+
+
+
+ {/* 右侧栏 */}
+ {isRightSidebarOpen && (
+
+
+
+
+
+
配置中心
+
+
+
+
+
+
+
+
+
+ {AVAILABLE_MODELS.map((model) => (
+
setSelectedModel(model.id)}
+ className={`hover:scale-102 transition-transform ${
+ selectedModel === model.id
+ ? 'border-2 border-' + model.color + '-500 bg-' + model.color + '-50'
+ : 'border-2 border-transparent'
+ }`}
+ >
+
+
+
+
{model.icon}
+
+
{model.name}
+
{model.description}
+
+
+ {selectedModel === model.id && (
+
+ )}
+
+
+
+ ))}
+
+
+
+
+
+
选择 AI 可以使用的工具
+
+
+ {AVAILABLE_TOOLS.map((tool) => (
+
+
+ {tool.icon}
+ {tool.name}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ 历史会话
+
+ {sessions.length}
+
+
+
+
+ 当前消息
+
+ {messages.length}
+
+
+
+
+ 启用工具
+
+ {selectedTools.length}
+
+
+
+
+
+
+
+
+ )}
+
);
};
+export default AgentChat;
+
/**
* 消息渲染器
*/
const MessageRenderer = ({ message, userAvatar }) => {
- const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
- const agentBubbleBg = useColorModeValue('white', 'gray.700');
- const borderColor = useColorModeValue('gray.200', 'gray.600');
-
switch (message.type) {
case MessageTypes.USER:
return (
-
-
-
-
- {message.content}
-
-
- } />
-
-
+
+
+
+
+ {message.content}
+
+
+
} size="sm" />
+
+
);
case MessageTypes.AGENT_THINKING:
return (
-
-
- } />
-
-
-
-
- {message.content}
-
-
-
-
-
- );
-
- case MessageTypes.AGENT_PLAN:
- return (
-
-
- } />
-
-
-
-
-
- );
-
- case MessageTypes.AGENT_EXECUTING:
- return (
-
-
- } />
-
-
- {message.stepResults?.map((result, idx) => (
-
- ))}
-
-
-
+
+
+
}
+ classNames={{ base: 'bg-gradient-to-br from-purple-500 to-pink-500' }}
+ size="sm"
+ />
+
+
+
+
+
+ {message.content}
+
+
+
+
+
+
);
case MessageTypes.AGENT_RESPONSE:
return (
-
-
- } />
-
- {/* 最终总结 */}
-
-
- {message.content}
-
+
+
+
}
+ classNames={{ base: 'bg-gradient-to-br from-blue-500 to-purple-500' }}
+ size="sm"
+ />
+
+
+
+ {message.content}
- {/* 元数据 */}
- {message.metadata && (
-
- 总步骤: {message.metadata.total_steps}
- ✓ {message.metadata.successful_steps}
- {message.metadata.failed_steps > 0 && (
- ✗ {message.metadata.failed_steps}
+
+
+
+
+
+
+
+
+
+
+
+ {message.metadata && (
+
+ 步骤: {message.metadata.total_steps}
+ ✓ {message.metadata.successful_steps}
+ {message.metadata.failed_steps > 0 && (
+ ✗ {message.metadata.failed_steps}
+ )}
+ ⏱ {message.metadata.total_execution_time?.toFixed(1)}s
+
)}
-
耗时: {message.metadata.total_execution_time?.toFixed(1)}s
-
- )}
-
+
+
+
- {/* 执行详情(可选) */}
- {message.plan && message.stepResults && message.stepResults.length > 0 && (
-
-
-
- 📊 执行详情(点击展开查看)
-
- {message.stepResults.map((result, idx) => (
-
- ))}
-
+ {message.stepResults && message.stepResults.length > 0 && (
+
)}
-
-
-
+
+
+
);
case MessageTypes.ERROR:
return (
-
-
- } />
-
- {message.content}
-
-
-
+
+
+
}
+ classNames={{ base: 'bg-danger-500' }}
+ size="sm"
+ />
+
+
+ {message.content}
+
+
+
+
);
default:
@@ -854,4 +956,62 @@ const MessageRenderer = ({ message, userAvatar }) => {
}
};
-export default AgentChatV3;
+/**
+ * 步骤结果面板
+ */
+const StepResultsPanel = ({ stepResults }) => {
+ return (
+
+
+
+ 执行详情 ({stepResults.length} 个步骤)
+
+ }
+ >
+
+ {stepResults.map((result, idx) => (
+
+
+
+
+
+ 步骤 {idx + 1}: {result.tool}
+
+
+ {result.status}
+
+
+
{result.execution_time?.toFixed(2)}s
+
+ {result.error && ⚠️ {result.error}
}
+
+
+ ))}
+
+
+
+ );
+};
diff --git a/src/views/AgentChat/index_v2.js b/src/views/AgentChat/index_v2.js
new file mode 100644
index 00000000..050c4944
--- /dev/null
+++ b/src/views/AgentChat/index_v2.js
@@ -0,0 +1,1569 @@
+// src/views/AgentChat/index.js
+// 超炫酷的 AI 投研助手 - Hero UI 深色模式版本
+// 使用 Framer Motion 物理动画引擎
+
+import React, { useState, useEffect, useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import {
+ Button,
+ Card,
+ CardHeader,
+ CardBody,
+ CardFooter,
+ Input,
+ Avatar,
+ Chip,
+ Divider,
+ Spinner,
+ Tooltip,
+ Badge,
+ Checkbox,
+ CheckboxGroup,
+ Tabs,
+ Tab,
+ ScrollShadow,
+ Kbd,
+ Accordion,
+ AccordionItem,
+} from '@heroui/react';
+import { useAuth } from '@contexts/AuthContext';
+import { logger } from '@utils/logger';
+import axios from 'axios';
+import { useToast } from '@chakra-ui/react';
+
+// 图标 - 使用 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';
+
+/**
+ * Framer Motion 动画变体配置
+ */
+const animations = {
+ slideInLeft: {
+ initial: { x: -320, opacity: 0 },
+ animate: {
+ x: 0,
+ opacity: 1,
+ transition: {
+ type: 'spring',
+ stiffness: 300,
+ damping: 30,
+ },
+ },
+ exit: {
+ x: -320,
+ opacity: 0,
+ transition: { duration: 0.2 },
+ },
+ },
+ slideInRight: {
+ initial: { x: 320, opacity: 0 },
+ animate: {
+ x: 0,
+ opacity: 1,
+ transition: {
+ type: 'spring',
+ stiffness: 300,
+ damping: 30,
+ },
+ },
+ exit: {
+ x: 320,
+ opacity: 0,
+ transition: { duration: 0.2 },
+ },
+ },
+ fadeInUp: {
+ initial: { opacity: 0, y: 20 },
+ animate: {
+ opacity: 1,
+ y: 0,
+ transition: {
+ type: 'spring',
+ stiffness: 400,
+ damping: 25,
+ },
+ },
+ },
+ staggerItem: {
+ initial: { opacity: 0, y: 10 },
+ animate: { opacity: 1, y: 0 },
+ },
+ staggerContainer: {
+ animate: {
+ transition: {
+ staggerChildren: 0.05,
+ },
+ },
+ },
+ pressScale: {
+ whileTap: { scale: 0.95 },
+ whileHover: { scale: 1.05 },
+ },
+};
+
+/**
+ * 消息类型
+ */
+const MessageTypes = {
+ USER: 'user',
+ AGENT_THINKING: 'agent_thinking',
+ AGENT_PLAN: 'agent_plan',
+ AGENT_EXECUTING: 'agent_executing',
+ AGENT_RESPONSE: 'agent_response',
+ ERROR: 'error',
+};
+
+/**
+ * 可用模型配置
+ */
+const AVAILABLE_MODELS = [
+ {
+ id: 'kimi-k2-thinking',
+ name: 'Kimi K2 Thinking',
+ description: '深度思考模型,适合复杂分析',
+ icon:
,
+ color: 'secondary',
+ },
+ {
+ id: 'kimi-k2',
+ name: 'Kimi K2',
+ description: '快速响应模型,适合简单查询',
+ icon:
,
+ color: 'primary',
+ },
+ {
+ id: 'deepmoney',
+ name: 'DeepMoney',
+ description: '金融专业模型',
+ icon:
,
+ color: 'success',
+ },
+];
+
+/**
+ * MCP 工具配置(完整列表)
+ */
+const MCP_TOOLS = [
+ // 新闻搜索类
+ {
+ id: 'search_news',
+ name: '全球新闻搜索',
+ icon:
,
+ category: '新闻资讯',
+ description: '搜索全球新闻,支持关键词和日期过滤'
+ },
+ {
+ id: 'search_china_news',
+ name: '中国新闻搜索',
+ icon:
,
+ category: '新闻资讯',
+ description: 'KNN语义搜索中国新闻'
+ },
+ {
+ id: 'search_medical_news',
+ name: '医疗健康新闻',
+ icon:
,
+ category: '新闻资讯',
+ description: '医药、医疗设备、生物技术新闻'
+ },
+
+ // 概念板块类
+ {
+ id: 'search_concepts',
+ name: '概念板块搜索',
+ icon:
,
+ category: '概念板块',
+ description: '搜索股票概念板块及相关股票'
+ },
+ {
+ id: 'get_concept_details',
+ name: '概念详情',
+ icon:
,
+ category: '概念板块',
+ description: '获取概念板块详细信息'
+ },
+ {
+ id: 'get_stock_concepts',
+ name: '股票概念',
+ icon:
,
+ category: '概念板块',
+ description: '查询股票相关概念板块'
+ },
+ {
+ id: 'get_concept_statistics',
+ name: '概念统计',
+ icon:
,
+ category: '概念板块',
+ description: '涨幅榜、跌幅榜、活跃榜等'
+ },
+
+ // 涨停分析类
+ {
+ id: 'search_limit_up_stocks',
+ name: '涨停股票搜索',
+ icon:
,
+ category: '涨停分析',
+ description: '搜索涨停股票,支持多条件筛选'
+ },
+ {
+ id: 'get_daily_stock_analysis',
+ name: '涨停日报',
+ icon:
,
+ category: '涨停分析',
+ description: '每日涨停股票分析报告'
+ },
+
+ // 研报路演类
+ {
+ id: 'search_research_reports',
+ name: '研报搜索',
+ icon:
,
+ category: '研报路演',
+ description: '搜索研究报告,支持语义搜索'
+ },
+ {
+ id: 'search_roadshows',
+ name: '路演活动',
+ icon:
,
+ category: '研报路演',
+ description: '上市公司路演、投资者交流活动'
+ },
+
+ // 股票数据类
+ {
+ id: 'get_stock_basic_info',
+ name: '股票基本信息',
+ icon:
,
+ category: '股票数据',
+ description: '公司名称、行业、主营业务等'
+ },
+ {
+ id: 'get_stock_financial_index',
+ name: '财务指标',
+ icon:
,
+ category: '股票数据',
+ description: 'EPS、ROE、营收增长率等'
+ },
+ {
+ id: 'get_stock_trade_data',
+ name: '交易数据',
+ icon:
,
+ category: '股票数据',
+ description: '价格、成交量、涨跌幅等'
+ },
+ {
+ id: 'get_stock_balance_sheet',
+ name: '资产负债表',
+ icon:
,
+ category: '股票数据',
+ description: '资产、负债、所有者权益'
+ },
+ {
+ id: 'get_stock_cashflow',
+ name: '现金流量表',
+ icon:
,
+ category: '股票数据',
+ description: '经营、投资、筹资现金流'
+ },
+ {
+ id: 'search_stocks_by_criteria',
+ name: '条件选股',
+ icon:
,
+ category: '股票数据',
+ description: '按行业、地区、市值筛选'
+ },
+ {
+ id: 'get_stock_comparison',
+ name: '股票对比',
+ icon:
,
+ category: '股票数据',
+ description: '多只股票财务指标对比'
+ },
+
+ // 用户数据类
+ {
+ id: 'get_user_watchlist',
+ name: '自选股列表',
+ icon:
,
+ category: '用户数据',
+ description: '用户关注的股票及行情'
+ },
+ {
+ id: 'get_user_following_events',
+ name: '关注事件',
+ icon:
,
+ category: '用户数据',
+ description: '用户关注的重大事件'
+ },
+];
+
+// 按类别分组工具
+const TOOL_CATEGORIES = {
+ '新闻资讯': MCP_TOOLS.filter(t => t.category === '新闻资讯'),
+ '概念板块': MCP_TOOLS.filter(t => t.category === '概念板块'),
+ '涨停分析': MCP_TOOLS.filter(t => t.category === '涨停分析'),
+ '研报路演': MCP_TOOLS.filter(t => t.category === '研报路演'),
+ '股票数据': MCP_TOOLS.filter(t => t.category === '股票数据'),
+ '用户数据': MCP_TOOLS.filter(t => t.category === '用户数据'),
+};
+
+/**
+ * Hero Agent Chat - 主组件(深色模式)
+ */
+const AgentChat = () => {
+ const { user } = useAuth();
+ const toast = useToast();
+
+ // 会话管理
+ 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('kimi-k2-thinking');
+ const [selectedTools, setSelectedTools] = useState([
+ 'search_news',
+ 'search_china_news',
+ 'search_concepts',
+ 'search_limit_up_stocks',
+ 'search_research_reports',
+ ]);
+ 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 页面强制启用深色模式
+ document.documentElement.classList.add('dark');
+
+ return () => {
+ // 组件卸载时不移除,让其他页面自己控制
+ // document.documentElement.classList.remove('dark');
+ };
+ }, []);
+
+ // ==================== 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]);
+
+ // ==================== 按日期分组会话 ====================
+ 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;
+
+ const quickQuestions = [
+ { text: '今日涨停板块分析', emoji: '🔥' },
+ { text: '新能源概念机会', emoji: '⚡' },
+ { text: '半导体行业动态', emoji: '💾' },
+ { text: '本周热门研报', emoji: '📊' },
+ ];
+
+ return (
+
+ {/* 左侧栏 - 深色毛玻璃 */}
+ {isLeftSidebarOpen && (
+
+
+
+
+
+ 对话历史
+
+
+
+
+
+
+
+
+
+
+
+
}
+ size="sm"
+ variant="bordered"
+ classNames={{
+ input: 'text-sm text-gray-100',
+ inputWrapper: 'border-gray-700 bg-gray-800/50 hover:border-gray-600',
+ }}
+ />
+
+
+
+ {/* 按日期分组显示会话 */}
+ {sessionGroups.today.length > 0 && (
+
+
今天
+
+ {sessionGroups.today.map((session) => (
+ 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 && (
+
+ )}
+
+
+ }
+ classNames={{
+ base: 'bg-gradient-to-br from-purple-500 to-pink-500',
+ }}
+ />
+
+
+
+
+ 价小前投研 AI
+
+
+ }
+ classNames={{
+ base: 'bg-green-500/20',
+ content: 'text-green-400',
+ }}
+ >
+ 智能分析
+
+
+ {AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.name}
+
+
+
+
+
+
+
+
+
+ {!isRightSidebarOpen && (
+
+ )}
+
+
+
+
+ {/* 消息列表 */}
+
+
+
+ {messages.map((message, index) => (
+
+
+
+ ))}
+
+
+
+
+
+ {/* 快捷问题 */}
+
+ {messages.length <= 2 && !isProcessing && (
+
+
+
+
+ 快速开始
+
+
+ {quickQuestions.map((question, idx) => (
+
+
+
+ ))}
+
+
+
+ )}
+
+
+ {/* 输入栏 - 深色 */}
+
+
+ {/* 已上传文件预览 */}
+ {uploadedFiles.length > 0 && (
+
+ {uploadedFiles.map((file, idx) => (
+ removeFile(idx)}
+ variant="flat"
+ classNames={{
+ base: 'bg-gray-800 border border-gray-700',
+ content: 'text-gray-300',
+ }}
+ >
+ {file.name}
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter
+ 发送
+
+
+ Shift
+ +
+ Enter
+ 换行
+
+
+
+
+
+
+ {/* 右侧栏 - 深色配置中心 */}
+ {isRightSidebarOpen && (
+
+
+
+
+
+ 配置中心
+
+
+
+
+
+
+
+
+
+ {/* 模型选择 */}
+
+
+ 模型
+
+ }
+ >
+
+ {AVAILABLE_MODELS.map((model) => (
+
setSelectedModel(model.id)}
+ className={`transition-all ${
+ selectedModel === model.id
+ ? 'bg-blue-500/20 border-2 border-blue-500'
+ : 'bg-gray-800/50 border-2 border-gray-700 hover:border-gray-600'
+ }`}
+ >
+
+
+
+ {model.icon}
+
+
+
{model.name}
+
{model.description}
+
+ {selectedModel === model.id && (
+
+ )}
+
+
+
+ ))}
+
+
+
+ {/* 工具选择 - 按分类显示 */}
+
+
+ 工具
+
+
+
+ ))}
+
+
+