update pay function

This commit is contained in:
2025-11-22 10:11:36 +08:00
parent 20c6356842
commit 71f3834b79
8 changed files with 560 additions and 304 deletions

View File

@@ -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<Message[]>([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [currentStreamingContent, setCurrentStreamingContent] = useState('');
const [currentStreamingId, setCurrentStreamingId] = useState<string | null>(null);
const messagesEndRef = useRef<HTMLDivElement>(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 (
<Layout>
<div className="flex items-center justify-center h-96">
<div className="text-lg">...</div>
</div>
</Layout>
);
}
if (!isAuthenticated) {
const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn';
return (
<Layout>
<div className="flex flex-col items-center justify-center h-96">
<h2 className="text-2xl mb-4"></h2>
<a
href={`${mainAppUrl}/auth/sign-in?redirect=/ai-chat/generate-code`}
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700"
>
</a>
</div>
</Layout>
);
}
if (!canAccessChat) {
const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn';
return (
<Layout>
<div className="flex flex-col items-center justify-center h-96">
<h2 className="text-2xl mb-4">使 AI </h2>
<p className="text-gray-600 mb-4"></p>
<a
href={`${mainAppUrl}/subscription`}
className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700"
>
</a>
</div>
</Layout>
);
}
return (
<Layout>
<MCPChat
onInputChange={setInput}
onSend={handleSend}
inputValue={input}
isLoading={isLoading}
>
{messages.length === 0 ? (
<div className="text-center text-gray-500 py-12">
<h3 className="text-xl mb-4">👋 使 AI </h3>
<p></p>
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-3 max-w-2xl mx-auto">
<button
onClick={() => setInput('生成一个响应式的导航栏组件')}
className="text-left p-3 border rounded-lg hover:bg-gray-50 transition-colors"
>
💡
</button>
<button
onClick={() => setInput('创建一个表单验证函数')}
className="text-left p-3 border rounded-lg hover:bg-gray-50 transition-colors"
>
📝
</button>
<button
onClick={() => setInput('写一个排序算法')}
className="text-left p-3 border rounded-lg hover:bg-gray-50 transition-colors"
>
🔢
</button>
<button
onClick={() => setInput('生成 REST API 接口')}
className="text-left p-3 border rounded-lg hover:bg-gray-50 transition-colors"
>
🌐 API
</button>
</div>
</div>
) : (
<>
{messages.map((msg, index) => (
<div key={msg.id}>
{msg.role === 'user' ? (
<Question>{msg.content}</Question>
) : msg.role === 'assistant' ? (
<Answer>
<div className="flex flex-col gap-2">
{msg.isStreaming ? (
<div className="whitespace-pre-wrap">
{msg.id === currentStreamingId ? currentStreamingContent : msg.content}
{msg.id === currentStreamingId && !currentStreamingContent && (
<span className="inline-block animate-pulse">...</span>
)}
</div>
) : (
<>
{msg.content && (
<div className="whitespace-pre-wrap">{msg.content}</div>
)}
{msg.code && (
<CodeEditor
title={msg.code.title || '生成的代码'}
language={msg.code.language}
initialCode={msg.code.content}
onCodeChange={() => {}}
isGenerating={false}
readOnly={false}
/>
)}
</>
)}
</div>
</Answer>
) : (
<div className="px-4 py-2 bg-yellow-50 text-yellow-800 rounded-lg">
{msg.content}
</div>
)}
</div>
))}
<div ref={messagesEndRef} />
</>
)}
</MCPChat>
</Layout>
);
};
export default MCPGenerateCodePage;