From 71f3834b79ff276bdbfdd87a4a479440d04c530b Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Sat, 22 Nov 2025 10:11:36 +0800 Subject: [PATCH] update pay function --- .../ai-chat/generate-code-template/page.tsx | 6 + .../app/ai-chat/generate-code/page.tsx | 6 + .../AgentChat/neuratalk/app/chat/page.tsx | 7 + .../AgentChat/neuratalk/app/demo/page.tsx | 75 ++++ src/views/AgentChat/neuratalk/app/page.tsx | 22 +- .../neuratalk/components/Chat/MCPChat.tsx | 343 +++--------------- .../PanelMessage/MCPPanelMessage.tsx | 131 +++++++ .../GenerateCodePage/MCPIntegrated.tsx | 274 ++++++++++++++ 8 files changed, 560 insertions(+), 304 deletions(-) create mode 100644 src/views/AgentChat/neuratalk/app/ai-chat/generate-code-template/page.tsx create mode 100644 src/views/AgentChat/neuratalk/app/ai-chat/generate-code/page.tsx create mode 100644 src/views/AgentChat/neuratalk/app/chat/page.tsx create mode 100644 src/views/AgentChat/neuratalk/app/demo/page.tsx create mode 100644 src/views/AgentChat/neuratalk/components/PanelMessage/MCPPanelMessage.tsx create mode 100644 src/views/AgentChat/neuratalk/templates/GenerateCodePage/MCPIntegrated.tsx diff --git a/src/views/AgentChat/neuratalk/app/ai-chat/generate-code-template/page.tsx b/src/views/AgentChat/neuratalk/app/ai-chat/generate-code-template/page.tsx new file mode 100644 index 00000000..9bbc4fa8 --- /dev/null +++ b/src/views/AgentChat/neuratalk/app/ai-chat/generate-code-template/page.tsx @@ -0,0 +1,6 @@ +// app/ai-chat/generate-code-template/page.tsx - 原始模板页面(仅演示) +import GenerateCodePage from "@/templates/GenerateCodePage"; + +export default function Page() { + return ; +} \ No newline at end of file diff --git a/src/views/AgentChat/neuratalk/app/ai-chat/generate-code/page.tsx b/src/views/AgentChat/neuratalk/app/ai-chat/generate-code/page.tsx new file mode 100644 index 00000000..6f820f73 --- /dev/null +++ b/src/views/AgentChat/neuratalk/app/ai-chat/generate-code/page.tsx @@ -0,0 +1,6 @@ +// app/ai-chat/generate-code/page.tsx - MCP 集成的代码生成页面 +import MCPGenerateCodePage from "@/templates/GenerateCodePage/MCPIntegrated"; + +export default function Page() { + return ; +} \ No newline at end of file diff --git a/src/views/AgentChat/neuratalk/app/chat/page.tsx b/src/views/AgentChat/neuratalk/app/chat/page.tsx new file mode 100644 index 00000000..0bbfc58c --- /dev/null +++ b/src/views/AgentChat/neuratalk/app/chat/page.tsx @@ -0,0 +1,7 @@ +// app/chat/page.tsx - 使用模板的漂亮聊天界面 +import GenerateCodePage from "@/templates/GenerateCodePage"; + +export default function ChatPage() { + // 使用代码生成页面模板(最接近聊天功能) + return ; +} \ No newline at end of file diff --git a/src/views/AgentChat/neuratalk/app/demo/page.tsx b/src/views/AgentChat/neuratalk/app/demo/page.tsx new file mode 100644 index 00000000..69bf2443 --- /dev/null +++ b/src/views/AgentChat/neuratalk/app/demo/page.tsx @@ -0,0 +1,75 @@ +// app/demo/page.tsx - 所有页面演示 +'use client'; + +export default function DemoPage() { + const pages = [ + { name: '🏠 主页', path: '/ai-chat', desc: '欢迎页面,功能卡片展示' }, + { name: '🤖 AI 代码助手', path: '/ai-chat/generate-code', desc: '✨ 漂亮界面 + MCP 集成(推荐)' }, + { name: '💬 模板代码生成', path: '/ai-chat/generate-code-template', desc: '原始模板界面(仅演示)' }, + { name: '🎨 图像生成', path: '/ai-chat/image-generation', desc: '图像生成界面' }, + { name: '📊 数据分析', path: '/ai-chat/data-analytics', desc: '数据分析和图表' }, + { name: '📝 文档生成', path: '/ai-chat/document-generation', desc: '文档生成界面' }, + { name: '📁 文档管理', path: '/ai-chat/documents', desc: '文档列表管理' }, + { name: '🔍 研究', path: '/ai-chat/research', desc: '研究和语音功能' }, + { name: '📋 模板', path: '/ai-chat/templates', desc: '模板库' }, + { name: '⏰ 历史', path: '/ai-chat/history', desc: '聊天历史' }, + { name: '🚀 简单聊天', path: '/ai-chat/chat-direct', desc: '简化版聊天(已集成 MCP)' } + ]; + + return ( +
+
+

AI Chat 页面导航

+

选择您想查看的页面模板

+ + + +
+

推荐查看

+
    +
  • + 最漂亮: + 主页 + - 精美的欢迎界面和功能卡片 +
  • +
  • + 🔥 最新集成: + AI 代码助手 + - 漂亮界面 + MCP 功能 +
  • +
  • + 最全功能: + 研究 + - 包含语音和多种工具 +
  • +
  • + 已集成 MCP: + 直接聊天 + - 简化版但已连接 MCP +
  • +
+
+ +
+

+ 当前用户:星野源 (Max) +

+
+
+
+ ); +} \ No newline at end of file diff --git a/src/views/AgentChat/neuratalk/app/page.tsx b/src/views/AgentChat/neuratalk/app/page.tsx index 72729129..fefe2982 100644 --- a/src/views/AgentChat/neuratalk/app/page.tsx +++ b/src/views/AgentChat/neuratalk/app/page.tsx @@ -1,15 +1,15 @@ -// 可以选择使用原始主页或直接进入聊天 -// import HomePage from "@/templates/HomePage"; -import MCPChat from "@/components/Chat/MCPChat"; +// 使用原始模板的漂亮界面 +import HomePage from "@/templates/HomePage"; +// import MCPChat from "@/components/Chat/MCPChat"; export default function Page() { - // 直接显示聊天界面 - return ( -
- -
- ); + // 使用原始主页 - 漂亮的界面 + return ; - // 或者使用原始主页 - // return ; + // 简单聊天界面(备用) + // return ( + //
+ // + //
+ // ); } diff --git a/src/views/AgentChat/neuratalk/components/Chat/MCPChat.tsx b/src/views/AgentChat/neuratalk/components/Chat/MCPChat.tsx index 5f644e1a..374deca9 100644 --- a/src/views/AgentChat/neuratalk/components/Chat/MCPChat.tsx +++ b/src/views/AgentChat/neuratalk/components/Chat/MCPChat.tsx @@ -1,305 +1,62 @@ -// components/Chat/MCPChat.tsx - 集成 MCP 的聊天组件 -'use client'; +import MCPPanelMessage from "@/components/PanelMessage/MCPPanelMessage"; +import Head from "./Head"; +import { useState } from "react"; -import React, { useState, useRef, useEffect } from 'react'; -import { mcpService } from '../../services/mcp-real'; -import { useAuth } from '../../hooks/useAuth'; -import styles from './MCPChat.module.css'; +type Props = { + titleHead?: React.ReactNode; + hidePanelMessage?: boolean; + children: React.ReactNode; + onInputChange?: (value: string) => void; + onSend?: () => void; + inputValue?: string; + isLoading?: boolean; +}; -interface Message { - id: string; - role: 'user' | 'assistant' | 'system'; - content: string; - timestamp: Date; - isStreaming?: boolean; -} +const MCPChat = ({ + titleHead, + hidePanelMessage, + children, + onInputChange, + onSend, + inputValue, + isLoading +}: Props) => { + const [localInput, setLocalInput] = useState(''); -export default function MCPChat() { - const [messages, setMessages] = useState([]); - const [input, setInput] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const [streamingMessage, setStreamingMessage] = useState(''); - const [tools, setTools] = useState([]); - const messagesEndRef = useRef(null); - const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth(); - - // 加载可用工具 - useEffect(() => { - if (isAuthenticated && canAccessChat) { - loadTools(); - } - }, [isAuthenticated, canAccessChat]); - - // 自动滚动到底部 - useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [messages, streamingMessage]); - - const loadTools = async () => { - try { - const availableTools = await mcpService.getTools(); - setTools(availableTools); - console.log('Available tools:', availableTools); - } catch (error) { - console.error('Failed to load tools:', error); - } - }; - - const handleSend = async () => { - if (!input.trim() || isLoading) return; - - const userMessage: Message = { - id: Date.now().toString(), - role: 'user', - content: input, - timestamp: new Date(), + const handleInputChange = (value: string) => { + if (onInputChange) { + onInputChange(value); + } else { + setLocalInput(value); + } }; - setMessages(prev => [...prev, userMessage]); - setInput(''); - setIsLoading(true); - setStreamingMessage(''); + const handleSend = () => { + if (onSend) { + onSend(); + } + }; - try { - // 创建助手消息占位符 - const assistantMessage: Message = { - id: (Date.now() + 1).toString(), - role: 'assistant', - content: '', - timestamp: new Date(), - isStreaming: true, - }; - - setMessages(prev => [...prev, assistantMessage]); - - // 流式接收回复 - let fullContent = ''; - for await (const chunk of mcpService.streamMessage(input)) { - fullContent += chunk; - setStreamingMessage(fullContent); - } - - // 更新最终消息 - setMessages(prev => - prev.map(msg => - msg.id === assistantMessage.id - ? { ...msg, content: fullContent, isStreaming: false } - : msg - ) - ); - setStreamingMessage(''); - } catch (error) { - console.error('Send message error:', error); - setMessages(prev => [ - ...prev, - { - id: Date.now().toString(), - role: 'system', - content: '抱歉,发送消息时出错了。请稍后重试。', - timestamp: new Date(), - }, - ]); - } finally { - setIsLoading(false); - } - }; - - const handleToolCall = async (toolName: string) => { - // 示例:调用工具 - try { - setIsLoading(true); - const result = await mcpService.callTool(toolName, {}); - console.log('Tool result:', result); - - // 显示工具结果 - setMessages(prev => [ - ...prev, - { - id: Date.now().toString(), - role: 'system', - content: `工具 ${toolName} 执行结果:${JSON.stringify(result.result, null, 2)}`, - timestamp: new Date(), - }, - ]); - } catch (error) { - console.error('Tool call error:', error); - } finally { - setIsLoading(false); - } - }; - - // 权限检查 - if (authLoading) { return ( -
-
加载中...
-
- ); - } - - if (!isAuthenticated) { - const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn'; - return ( -
-

请先登录

- - 前往登录 - -
- ); - } - - if (!canAccessChat) { - const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn'; - return ( -
-

需要订阅才能使用 AI 助手

-

升级到高级版解锁所有功能

-

- 当前订阅等级:{user?.subscription_type || user?.subscription_tier || '未知'} - {user?.is_subscription_active && ' (活跃)'} -

-
- - 查看订阅方案 - - -
-
- ); - } - - return ( -
- {/* 侧边栏 - 工具列表 */} -
-

可用工具

-
- {tools.map(tool => ( - - ))} -
- - {/* 用户信息 */} -
-
-
- {user?.username?.[0]?.toUpperCase()} -
-
-
{user?.username || user?.nickname}
-
- {user?.subscription_type || user?.subscription_tier || 'Free'} - {user?.is_subscription_active && ' ✓'} -
-
-
-
-
- - {/* 主聊天区域 */} -
- {/* 消息列表 */} -
- {messages.map(msg => ( +
+
-
- {msg.isStreaming ? ( -
- {streamingMessage || ( - 思考中... - )} -
- ) : ( -
{msg.content}
- )} -
- {msg.timestamp.toLocaleTimeString()} -
-
+ > + {children}
- ))} -
+ {!hidePanelMessage && ( + + )}
+ ); +}; - {/* 输入区域 */} -
-
- setInput(e.target.value)} - onKeyPress={e => e.key === 'Enter' && handleSend()} - disabled={isLoading} - placeholder="输入消息..." - className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white" - /> - -
- - {/* 快捷操作 */} -
- - - -
-
-
-
- ); -} - -// CSS 模块文件需要单独创建 \ No newline at end of file +export default MCPChat; \ No newline at end of file diff --git a/src/views/AgentChat/neuratalk/components/PanelMessage/MCPPanelMessage.tsx b/src/views/AgentChat/neuratalk/components/PanelMessage/MCPPanelMessage.tsx new file mode 100644 index 00000000..0a1d68bb --- /dev/null +++ b/src/views/AgentChat/neuratalk/components/PanelMessage/MCPPanelMessage.tsx @@ -0,0 +1,131 @@ +import { useState, useEffect, KeyboardEvent } from "react"; +import Link from "next/link"; +import TextareaAutosize from "react-textarea-autosize"; +import Icon from "@/components/Icon"; +import Image from "@/components/Image"; +import Note from "./Note"; +import Speed from "./Speed"; +import Search from "./Search"; +import Menu from "./Menu"; + +type ButtonProps = { + className?: string; + icon: string; + onClick: () => void; +}; + +const Button = ({ className, icon, onClick }: ButtonProps) => { + return ( + + ); +}; + +type MCPPanelMessageProps = { + value?: string; + onInputChange?: (value: string) => void; + onSend?: () => void; + isLoading?: boolean; + placeholder?: string; +}; + +const MCPPanelMessage = ({ + value = '', + onInputChange, + onSend, + isLoading = false, + placeholder = "输入消息,与 AI 对话..." +}: MCPPanelMessageProps) => { + const [localMessage, setLocalMessage] = useState(""); + + // 同步外部值 + useEffect(() => { + if (value !== undefined) { + setLocalMessage(value); + } + }, [value]); + + const handleChange = (newValue: string) => { + setLocalMessage(newValue); + if (onInputChange) { + onInputChange(newValue); + } + }; + + const handleSend = () => { + if (!localMessage.trim() || isLoading) return; + + if (onSend) { + onSend(); + } + }; + + const handleKeyPress = (e: KeyboardEvent) => { + // Shift+Enter 换行,Enter 发送 + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + return ( +
+ +
+
+ handleChange(e.target.value)} + onKeyDown={handleKeyPress} + placeholder={placeholder} + disabled={isLoading} + /> +
+
+ +
+ + +
+ {/* 快捷提示 */} +
+ 按 Enter 发送,Shift+Enter 换行 +
+
+
+ ); +}; + +export default MCPPanelMessage; \ No newline at end of file diff --git a/src/views/AgentChat/neuratalk/templates/GenerateCodePage/MCPIntegrated.tsx b/src/views/AgentChat/neuratalk/templates/GenerateCodePage/MCPIntegrated.tsx new file mode 100644 index 00000000..d46591f7 --- /dev/null +++ b/src/views/AgentChat/neuratalk/templates/GenerateCodePage/MCPIntegrated.tsx @@ -0,0 +1,274 @@ +"use client"; + +import { useState, useRef, useEffect } from "react"; +import Layout from "@/components/Layout"; +import MCPChat from "@/components/Chat/MCPChat"; +import Question from "@/components/Question"; +import Answer from "@/components/Answer"; +import CodeEditor from "@/components/CodeEditor"; +import { mcpService } from '@/services/mcp-real'; +import { useAuth } from '@/hooks/useAuth'; + +interface Message { + id: string; + role: 'user' | 'assistant' | 'system'; + content: string; + timestamp: Date; + code?: { + language: string; + content: string; + title?: string; + }; + isStreaming?: boolean; +} + +const MCPGenerateCodePage = () => { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [currentStreamingContent, setCurrentStreamingContent] = useState(''); + const [currentStreamingId, setCurrentStreamingId] = useState(null); + const messagesEndRef = useRef(null); + const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth(); + + // 自动滚动到底部 + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages, currentStreamingContent]); + + const extractCodeFromResponse = (content: string) => { + // 匹配 markdown 代码块 + const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g; + const matches = [...content.matchAll(codeBlockRegex)]; + + if (matches.length > 0) { + // 返回第一个代码块 + const [, language = 'javascript', code] = matches[0]; + return { + hasCode: true, + language, + code: code.trim(), + textBefore: content.substring(0, matches[0].index), + textAfter: content.substring((matches[0].index || 0) + matches[0][0].length) + }; + } + + return { hasCode: false, content }; + }; + + const handleSend = async () => { + if (!input.trim() || isLoading) return; + + const userMessage: Message = { + id: Date.now().toString(), + role: 'user', + content: input, + timestamp: new Date(), + }; + + setMessages(prev => [...prev, userMessage]); + setInput(''); + setIsLoading(true); + setCurrentStreamingContent(''); + + try { + // 创建助手消息占位符 + const assistantMessageId = (Date.now() + 1).toString(); + setCurrentStreamingId(assistantMessageId); + + const assistantMessage: Message = { + id: assistantMessageId, + role: 'assistant', + content: '', + timestamp: new Date(), + isStreaming: true, + }; + + setMessages(prev => [...prev, assistantMessage]); + + // 流式接收回复 + let fullContent = ''; + for await (const chunk of mcpService.streamMessage(input)) { + fullContent += chunk; + setCurrentStreamingContent(fullContent); + } + + // 检查回复中是否包含代码 + const codeInfo = extractCodeFromResponse(fullContent); + + // 更新最终消息 + setMessages(prev => + prev.map(msg => + msg.id === assistantMessageId + ? { + ...msg, + content: codeInfo.hasCode + ? (codeInfo.textBefore || '这是生成的代码:') + : fullContent, + code: codeInfo.hasCode + ? { + language: codeInfo.language!, + content: codeInfo.code!, + title: '生成的代码' + } + : undefined, + isStreaming: false + } + : msg + ) + ); + + setCurrentStreamingContent(''); + setCurrentStreamingId(null); + } catch (error) { + console.error('Send message error:', error); + setMessages(prev => [ + ...prev, + { + id: Date.now().toString(), + role: 'system', + content: '抱歉,发送消息时出错了。请稍后重试。', + timestamp: new Date(), + }, + ]); + } finally { + setIsLoading(false); + } + }; + + // 权限检查 + if (authLoading) { + return ( + +
+
加载中...
+
+
+ ); + } + + if (!isAuthenticated) { + const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn'; + return ( + +
+

请先登录

+ + 前往登录 + +
+
+ ); + } + + if (!canAccessChat) { + const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn'; + return ( + +
+

需要订阅才能使用 AI 助手

+

升级到高级版解锁所有功能

+ + 查看订阅方案 + +
+
+ ); + } + + return ( + + + {messages.length === 0 ? ( +
+

👋 欢迎使用 AI 代码生成助手

+

输入您的需求,我会帮您生成代码

+
+ + + + +
+
+ ) : ( + <> + {messages.map((msg, index) => ( +
+ {msg.role === 'user' ? ( + {msg.content} + ) : msg.role === 'assistant' ? ( + +
+ {msg.isStreaming ? ( +
+ {msg.id === currentStreamingId ? currentStreamingContent : msg.content} + {msg.id === currentStreamingId && !currentStreamingContent && ( + 思考中... + )} +
+ ) : ( + <> + {msg.content && ( +
{msg.content}
+ )} + {msg.code && ( + {}} + isGenerating={false} + readOnly={false} + /> + )} + + )} +
+
+ ) : ( +
+ {msg.content} +
+ )} +
+ ))} +
+ + )} + + + ); +}; + +export default MCPGenerateCodePage; \ No newline at end of file