Files
vf_react/src/views/AgentChat/neuratalk/components/Layout/MCPLayout.tsx
2025-11-22 10:37:15 +08:00

181 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from "react";
import Sidebar from "@/components/Sidebar";
import Tools from "@/components/Tools";
import Header from "@/components/Header";
import PythonRunner from "@/components/PythonRunner";
import Calculator from "@/components/Calculator";
import Browser from "@/components/Browser";
import WebDesign from "@/components/WebDesign";
import FileConverter from "@/components/FileConverter";
import LanguageTranslator from "@/components/LanguageTranslator";
import ApiIntegrator from "@/components/ApiIntegrator";
import { useAuth } from '@/hooks/useAuth';
import MCPImage from "@/components/Image/MCPImage";
type Props = {
children: React.ReactNode;
};
const MCPLayout = ({ children }: Props) => {
const [activeId, setActiveId] = useState<string | null>(null);
const [visibleTools, setVisibleTools] = useState(true);
const [visibleSidebar, setVisibleSidebar] = useState(false);
const { user, isAuthenticated, canAccessChat, loading: authLoading } = useAuth();
console.log('[MCPLayout] Auth state:', {
user,
isAuthenticated,
canAccessChat,
authLoading
});
// 权限检查
if (authLoading) {
return (
<div className="flex items-center justify-center h-screen bg-gray-50">
<div className="text-center">
<div className="w-16 h-16 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-lg text-gray-600">...</p>
</div>
</div>
);
}
if (!isAuthenticated) {
const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn';
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-50">
<div className="text-center p-8 bg-white rounded-lg shadow-lg">
<h2 className="text-2xl mb-4"></h2>
<p className="text-gray-600 mb-6">使 AI </p>
<a
href={`${mainAppUrl}/auth/sign-in?redirect=/ai-chat`}
className="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
>
</a>
</div>
</div>
);
}
if (!canAccessChat) {
const mainAppUrl = process.env.NEXT_PUBLIC_MAIN_APP_URL || 'https://valuefrontier.cn';
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-50">
<div className="text-center p-8 bg-white rounded-lg shadow-lg">
<h2 className="text-2xl mb-4">使 AI </h2>
<p className="text-gray-600 mb-2"></p>
<p className="text-sm text-gray-500 mb-6">
{user?.subscription_type || '未订阅'}
</p>
<div className="space-y-3">
<a
href={`${mainAppUrl}/subscription`}
className="block bg-green-600 text-white px-6 py-3 rounded-lg hover:bg-green-700 transition-colors"
>
</a>
<button
onClick={() => window.location.reload()}
className="block w-full bg-gray-200 text-gray-700 px-6 py-3 rounded-lg hover:bg-gray-300 transition-colors"
>
</button>
</div>
</div>
</div>
);
}
return (
<div
className={`pl-90 overflow-hidden transition-all max-3xl:pl-75 max-lg:pl-5 max-md:pl-4 ${
visibleTools
? "pr-90 max-3xl:pr-75 max-2xl:pr-5 max-md:pr-4"
: "pr-5 max-md:pr-4"
}`}
>
<Sidebar
visible={visibleSidebar}
onClose={() => setVisibleSidebar(false)}
onClickNewChat={() => setActiveId(null)}
/>
{/* 用户信息显示 */}
{user && (
<div className="fixed bottom-4 left-4 z-50 bg-white rounded-lg shadow-lg p-3 flex items-center space-x-3 max-w-xs">
{user.avatar_url ? (
<img
src={user.avatar_url}
alt={user.username || user.nickname}
className="w-10 h-10 rounded-full"
/>
) : (
<div className="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center text-white font-semibold">
{(user.username || user.nickname || 'U')[0].toUpperCase()}
</div>
)}
<div className="flex-1 min-w-0">
<p className="font-medium text-sm text-gray-900 truncate">
{user.username || user.nickname}
</p>
<p className="text-xs text-gray-500">
{user.subscription_type === 'max' ? '🌟 MAX会员' :
user.subscription_type === 'premium' ? '💎 高级会员' :
user.subscription_type === 'pro' ? '🚀 专业版' : '免费版'}
</p>
{user.subscription_days_left && user.subscription_days_left > 0 && (
<p className="text-xs text-green-600">
{user.subscription_days_left}
</p>
)}
</div>
</div>
)}
<div className="pt-9.5 pb-5 max-2xl:pt-5 max-md:pt-3 max-md:pb-4">
<Header
onOpenSidebar={() => setVisibleSidebar(true)}
onToggleTools={() => setVisibleTools(!visibleTools)}
/>
{activeId === "python" ? (
<PythonRunner />
) : activeId === "calculator" ? (
<Calculator />
) : activeId === "browser" ? (
<Browser />
) : activeId === "web-design" ? (
<WebDesign />
) : activeId === "exchange" ? (
<FileConverter />
) : activeId === "language" ? (
<LanguageTranslator />
) : activeId === "api" ? (
<ApiIntegrator />
) : (
children
)}
</div>
<Tools
activeId={activeId}
setActiveId={setActiveId}
visible={visibleTools}
onClose={() => setVisibleTools(!visibleTools)}
/>
<div
className={`fixed inset-0 z-10 hidden bg-overlay backdrop-blur-sm transition-all max-lg:block max-md:hidden ${
visibleSidebar || !visibleTools
? "visible opacity-100"
: "invisible opacity-0"
}`}
onClick={() => {
setVisibleSidebar(false);
setVisibleTools(true);
}}
></div>
</div>
);
};
export default MCPLayout;