diff --git a/package.json b/package.json
index 6a33b14d..9a97eb5a 100755
--- a/package.json
+++ b/package.json
@@ -18,8 +18,6 @@
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/react": "^6.1.19",
- "@heroui/react": "^3.0.0-beta.2",
- "@heroui/styles": "^3.0.0-beta.2",
"@reduxjs/toolkit": "^2.9.2",
"@splidejs/react-splide": "^0.7.12",
"@tanstack/react-virtual": "^3.13.12",
@@ -112,7 +110,6 @@
},
"devDependencies": {
"@craco/craco": "^7.1.0",
- "@tailwindcss/postcss": "next",
"@types/node": "^20.19.25",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
@@ -133,7 +130,6 @@
"prettier": "2.2.1",
"react-error-overlay": "6.0.9",
"sharp": "^0.34.4",
- "tailwindcss": "next",
"ts-node": "^10.9.2",
"webpack-bundle-analyzer": "^4.10.2",
"yn": "^5.1.0"
diff --git a/postcss.config.js b/postcss.config.js
deleted file mode 100644
index fa7d5e7d..00000000
--- a/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-// PostCSS 配置 - Tailwind CSS v4
-module.exports = {
- plugins: {
- '@tailwindcss/postcss': {},
- },
-}
diff --git a/src/index.js b/src/index.js
index 5c4d0027..0c8db5ae 100755
--- a/src/index.js
+++ b/src/index.js
@@ -3,9 +3,6 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
-// 导入 HeroUI v3 样式(必须在最前面导入,包含 Tailwind CSS)
-import './styles/heroui.css';
-
// 导入 Brainwave 样式(空文件,保留以避免错误)
import './styles/brainwave.css';
diff --git a/src/styles/heroui.css b/src/styles/heroui.css
deleted file mode 100644
index b9acd7df..00000000
--- a/src/styles/heroui.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * HeroUI v3 + Tailwind CSS v4 配置
- * 参考文档: https://v3.heroui.com/docs/quick-start
- *
- * 重要:HeroUI v3 beta 需要 Tailwind v4
- * 导入顺序:先 tailwindcss,后 @heroui/styles
- */
-
-@import "tailwindcss";
-@import "@heroui/styles";
diff --git a/src/views/AgentChat/index.js b/src/views/AgentChat/index.js
index 468c504c..c79e70c6 100644
--- a/src/views/AgentChat/index.js
+++ b/src/views/AgentChat/index.js
@@ -1,30 +1,43 @@
// src/views/AgentChat/index.js
-// 超炫酷的 AI 投研助手 - Hero UI 深色模式版本
+// 超炫酷的 AI 投研助手 - Chakra UI 深色模式版本
// 使用 Framer Motion 物理动画引擎
import React, { useState, useEffect, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
+ Box,
Button,
- Card,
Input,
Avatar,
- Chip,
- Separator,
+ Badge,
+ Divider,
Spinner,
Tooltip,
Checkbox,
CheckboxGroup,
Kbd,
Accordion,
-} from '@heroui/react';
-import {
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ AccordionIcon,
Tabs,
TabList,
TabPanels,
Tab,
TabPanel,
- useToast
+ 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';
@@ -165,23 +178,23 @@ const AVAILABLE_MODELS = [
{
id: 'kimi-k2-thinking',
name: 'Kimi K2 Thinking',
- description: '深度思考模型,适合复杂分析',
+ description: '深度思考模型,适合复杂分析',
icon: ,
- color: 'secondary',
+ color: 'purple',
},
{
id: 'kimi-k2',
name: 'Kimi K2',
- description: '快速响应模型,适合简单查询',
+ description: '快速响应模型,适合简单查询',
icon: ,
- color: 'primary',
+ color: 'blue',
},
{
id: 'deepmoney',
name: 'DeepMoney',
description: '金融专业模型',
icon: ,
- color: 'success',
+ color: 'green',
},
];
@@ -195,7 +208,7 @@ const MCP_TOOLS = [
name: '全球新闻搜索',
icon: ,
category: '新闻资讯',
- description: '搜索全球新闻,支持关键词和日期过滤'
+ description: '搜索全球新闻,支持关键词和日期过滤'
},
{
id: 'search_china_news',
@@ -248,7 +261,7 @@ const MCP_TOOLS = [
name: '涨停股票搜索',
icon: ,
category: '涨停分析',
- description: '搜索涨停股票,支持多条件筛选'
+ description: '搜索涨停股票,支持多条件筛选'
},
{
id: 'get_daily_stock_analysis',
@@ -264,7 +277,7 @@ const MCP_TOOLS = [
name: '研报搜索',
icon: ,
category: '研报路演',
- description: '搜索研究报告,支持语义搜索'
+ description: '搜索研究报告,支持语义搜索'
},
{
id: 'search_roadshows',
@@ -353,11 +366,12 @@ const TOOL_CATEGORIES = {
};
/**
- * Hero Agent Chat - 主组件(深色模式)
+ * Agent Chat - 主组件(深色模式)
*/
const AgentChat = () => {
const { user } = useAuth();
const toast = useToast();
+ const { setColorMode } = useColorMode();
// 会话管理
const [sessions, setSessions] = useState([]);
@@ -393,13 +407,14 @@ const AgentChat = () => {
// ==================== 启用深色模式 ====================
useEffect(() => {
// 为 AgentChat 页面强制启用深色模式
+ setColorMode('dark');
document.documentElement.classList.add('dark');
return () => {
- // 组件卸载时不移除,让其他页面自己控制
+ // 组件卸载时不移除,让其他页面自己控制
// document.documentElement.classList.remove('dark');
};
- }, []);
+ }, [setColorMode]);
// ==================== API 调用函数 ====================
@@ -449,7 +464,7 @@ const AgentChat = () => {
{
id: Date.now(),
type: MessageTypes.AGENT_RESPONSE,
- content: `你好${user?.nickname || ''}!👋\n\n我是**价小前**,你的 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(),
},
]);
@@ -659,176 +674,200 @@ const AgentChat = () => {
];
return (
-
+
{/* 左侧栏 - 深色毛玻璃 */}
- {isLeftSidebarOpen && (
-
-
-
-
-
- 对话历史
-
-
-
-
-
-
-
+
+
+
+
+ {/* 按日期分组显示会话 */}
+ {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 && (
+
+
+ 还没有对话历史
+ 开始一个新对话吧!
+
+ )}
+
+
+
+
+ setIsLeftSidebarOpen(false)}
- >
-
-
-
-
-
-
-
}
- 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'} 用户
-
-
-
-
- )}
+ bgGradient="linear(to-br, blue.500, purple.600)"
+ />
+
+
+ {user?.nickname || '未登录'}
+
+
+ {user?.subscription_type || 'free'} 用户
+
+
+
+
+
+
+ )}
+
{/* 中间主聊天区域 */}
-
+
{/* 顶部标题栏 - 深色 */}
-
-
-
+
+
+
{!isLeftSidebarOpen && (
-
+ variant="ghost"
+ icon={}
+ onClick={() => setIsLeftSidebarOpen(true)}
+ color="gray.400"
+ />
)}
{
>
}
- classNames={{
- base: 'bg-gradient-to-br from-purple-500 to-pink-500',
- }}
+ bgGradient="linear(to-br, purple.500, pink.500)"
/>
-
-
+
+
价小前投研 AI
-
-
- }
- classNames={{
- base: 'bg-green-500/20',
- content: 'text-green-400',
- }}
+
+
+
+
智能分析
-
-
+
{AVAILABLE_MODELS.find((m) => m.id === selectedModel)?.name}
-
-
-
-
+
+
+
+
-
-
-
+ variant="ghost"
+ icon={}
+ onClick={createNewSession}
+ color="gray.400"
+ _hover={{ color: 'gray.200' }}
+ />
{!isRightSidebarOpen && (
-
+ variant="ghost"
+ icon={}
+ onClick={() => setIsRightSidebarOpen(true)}
+ color="gray.400"
+ _hover={{ color: 'gray.200' }}
+ />
)}
-
-
-
+
+
+
{/* 消息列表 */}
-
+
-
- {messages.map((message, index) => (
-
-
-
- ))}
-
-
+
+
+ {messages.map((message) => (
+
+
+
+ ))}
+
+
+
-
+
{/* 快捷问题 */}
{messages.length <= 2 && !isProcessing && (
-
-
-
- 快速开始
-
-
- {quickQuestions.map((question, idx) => (
-
-
-
- ))}
-
-
+
+
+
+
+ 快速开始
+
+
+ {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',
- }}
+ size="md"
+ variant="subtle"
+ bg="gray.700"
+ borderColor="gray.600"
+ borderWidth={1}
>
- {file.name}
-
+ {file.name}
+ removeFile(idx)} />
+
))}
-
+
)}
-
+
-
-
+ icon={}
+ onClick={() => fileInputRef.current?.click()}
+ bg="gray.700"
+ color="gray.300"
+ _hover={{ bg: 'gray.600' }}
+ />
-
-
+ bg="gray.700"
+ color="gray.300"
+ _hover={{ bg: 'gray.600' }}
+ />
setInputValue(e.target.value)}
onKeyDown={handleKeyPress}
placeholder="输入你的问题... (Enter 发送, Shift+Enter 换行)"
- disabled={isProcessing}
+ isDisabled={isProcessing}
size="lg"
- variant="bordered"
- classNames={{
- input: 'text-base text-gray-100',
- inputWrapper: 'border-2 border-gray-700 bg-gray-800/50 hover:border-blue-500 focus-within:border-blue-500',
- }}
+ variant="outline"
+ borderWidth={2}
+ borderColor="gray.600"
+ bg="rgba(31, 41, 55, 0.5)"
+ color="gray.100"
+ _hover={{ borderColor: 'blue.500' }}
+ _focus={{ borderColor: 'blue.500', boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)' }}
/>
- }
+ onClick={handleSendMessage}
isLoading={isProcessing}
isDisabled={!inputValue.trim() || isProcessing}
- className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-500 hover:to-purple-500"
- >
- {!isProcessing && }
-
+ bgGradient="linear(to-r, blue.600, purple.600)"
+ _hover={{ bgGradient: 'linear(to-r, blue.500, purple.500)' }}
+ />
-
+
-
-
- Enter
- 发送
-
-
- Shift
- +
- Enter
- 换行
-
-
-
-
-
+
+
+ Enter
+ 发送
+
+
+ Shift
+ +
+ Enter
+ 换行
+
+
+
+
+
{/* 右侧栏 - 深色配置中心 */}
- {isRightSidebarOpen && (
-
-
-
-
-
- 配置中心
-
-
-
-
-
-
-
-
-
+ {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 && (
-
- )}
-
-
-
- ))}
-
-
+
+
+
+
+ 配置中心
+
+
+ }
+ onClick={() => setIsRightSidebarOpen(false)}
+ color="gray.400"
+ _hover={{ color: 'gray.200' }}
+ />
+
+
+
- {/* 工具选择 - 按分类显示 */}
-
-
- 工具
- {selectedTools.length > 0 && (
-
- {selectedTools.length}
-
- )}
-
- }
- >
-
-
- {Object.entries(TOOL_CATEGORIES).map(([category, tools]) => (
-
- {category}
-
- {tools.filter(t => selectedTools.includes(t.id)).length}/{tools.length}
-
-
- }
- >
-
+
+
+
+
+
+ 模型
+
+
+
+
+
+ 工具
+ {selectedTools.length > 0 && (
+
+ {selectedTools.length}
+
+ )}
+
+
+
+
+
+ 统计
+
+
+
+
+
+ {/* 模型选择 */}
+
+
+ {AVAILABLE_MODELS.map((model) => (
+ setSelectedModel(model.id)}
+ bg={selectedModel === model.id ? 'rgba(59, 130, 246, 0.2)' : 'rgba(31, 41, 55, 0.5)'}
+ borderWidth={2}
+ borderColor={selectedModel === model.id ? 'blue.500' : 'gray.600'}
+ _hover={{ borderColor: selectedModel === model.id ? 'blue.500' : 'gray.500' }}
+ transition="all 0.2s"
+ >
+
+
+
+ {model.icon}
+
+
+
+ {model.name}
+
+
+ {model.description}
+
+
+ {selectedModel === model.id && (
+
+ )}
+
+
+
+ ))}
+
+
+
+ {/* 工具选择 */}
+
+
+ {Object.entries(TOOL_CATEGORIES).map(([category, tools]) => (
+
+
+
+ {category}
+
+ {tools.filter(t => selectedTools.includes(t.id)).length}/{tools.length}
+
+
+
+
+
+
+
+ {tools.map((tool) => (
+
+
+ {tool.icon}
+
+ {tool.name}
+ {tool.description}
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+
-
- ))}
-
+ 全选
+
+
+
+
-
-
-
-
-
-
+ {/* 统计信息 */}
+
+
+
+
+
+
+ 对话数
+
+ {sessions.length}
+
+
+
+
+
+
- {/* 统计信息 */}
-
-
- 统计
-
- }
- >
-
-
-
-
-
-
对话数
-
{sessions.length}
-
-
-
-
-
+
+
+
+
+ 消息数
+
+ {messages.length}
+
+
+
+
+
+
-
-
-
-
-
消息数
-
{messages.length}
-
-
-
-
-
-
-
-
-
-
-
已选工具
-
{selectedTools.length}
-
-
-
-
-
-
-
-
-
-
- )}
-
+
+
+
+
+ 已选工具
+
+ {selectedTools.length}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
);
};
@@ -1316,44 +1394,44 @@ 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}
-
+
)}
-
-
+
+
);
};
@@ -1365,133 +1443,134 @@ const MessageRenderer = ({ message, userAvatar }) => {
switch (message.type) {
case MessageTypes.USER:
return (
-
-
-
-
- {message.content}
+
+
+
+
+
+ {message.content}
+
{message.files && message.files.length > 0 && (
-
+
{message.files.map((file, idx) => (
-
-
+
+
{file.name}
-
+
))}
-
+
)}
-
+
}
size="sm"
- classNames={{
- base: 'bg-gradient-to-br from-blue-500 to-purple-600',
- }}
+ bgGradient="linear(to-br, blue.500, purple.600)"
/>
-
-
+
+
);
case MessageTypes.AGENT_THINKING:
return (
-
-
+
+
}
size="sm"
- classNames={{
- base: 'bg-gradient-to-br from-purple-500 to-pink-500',
- }}
+ bgGradient="linear(to-br, purple.500, pink.500)"
/>
-
-
-
- {message.content}
-
+
+
+
+
+ {message.content}
+
+
-
-
+
+
);
case MessageTypes.AGENT_RESPONSE:
return (
-
-
+
+
}
size="sm"
- classNames={{
- base: 'bg-gradient-to-br from-purple-500 to-pink-500',
- }}
+ bgGradient="linear(to-br, purple.500, pink.500)"
/>
-
-
-
+
+
+
{message.content}
-
+
{message.stepResults && message.stepResults.length > 0 && (
-
+
-
+
)}
-
-
-
+ variant="ghost"
+ icon={}
+ onClick={() => navigator.clipboard.writeText(message.content)}
+ color="gray.400"
+ _hover={{ color: 'gray.200' }}
+ />
-
-
+ variant="ghost"
+ icon={}
+ color="gray.400"
+ _hover={{ color: 'green.400' }}
+ />
-
-
+ variant="ghost"
+ icon={}
+ color="gray.400"
+ _hover={{ color: 'red.400' }}
+ />
-
+
{new Date(message.timestamp).toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
})}
-
-
-
+
+
+
-
-
+
+
);
case MessageTypes.ERROR:
return (
-
-
-
- {message.content}
-
+
+
+
+ {message.content}
+
-
+
);
default:
@@ -1504,62 +1583,52 @@ const MessageRenderer = ({ message, userAvatar }) => {
*/
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}
}
-
-
- ))}
-
-
+
+
+
+ 执行详情
+
+ {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.heroui-backup b/src/views/AgentChat/index.js.heroui-backup
new file mode 100644
index 00000000..468c504c
--- /dev/null
+++ b/src/views/AgentChat/index.js.heroui-backup
@@ -0,0 +1,1565 @@
+// 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,
+ Input,
+ Avatar,
+ Chip,
+ Separator,
+ Spinner,
+ Tooltip,
+ Checkbox,
+ CheckboxGroup,
+ Kbd,
+ Accordion,
+} from '@heroui/react';
+import {
+ Tabs,
+ TabList,
+ TabPanels,
+ Tab,
+ TabPanel,
+ useToast
+} 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';
+
+/**
+ * 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 && (
+
+ )}
+
+
+
+ ))}
+
+
+
+ {/* 工具选择 - 按分类显示 */}
+
+
+ 工具
+ {selectedTools.length > 0 && (
+
+ {selectedTools.length}
+
+ )}
+
+ }
+ >
+
+
+ {Object.entries(TOOL_CATEGORIES).map(([category, tools]) => (
+
+ {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;
+
+/**
+ * 会话卡片组件
+ */
+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"
+ classNames={{
+ base: 'bg-gradient-to-br from-blue-500 to-purple-600',
+ }}
+ />
+
+
+ );
+
+ case MessageTypes.AGENT_THINKING:
+ return (
+
+
+
}
+ size="sm"
+ classNames={{
+ base: 'bg-gradient-to-br from-purple-500 to-pink-500',
+ }}
+ />
+
+
+
+ {message.content}
+
+
+
+
+ );
+
+ case MessageTypes.AGENT_RESPONSE:
+ return (
+
+
+
}
+ size="sm"
+ classNames={{
+ base: 'bg-gradient-to-br from-purple-500 to-pink-500',
+ }}
+ />
+
+
+
+ {message.content}
+
+
+ {message.stepResults && message.stepResults.length > 0 && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ {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}
}
+
+
+ ))}
+
+
+
+ );
+};