update pay function
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user