import { Suspense, useCallback, useEffect, useMemo, useState } from "react"; import { BugOutlined, CheckCircleFilled, CloseOutlined, HomeOutlined, IdcardOutlined, LockOutlined, LoadingOutlined, LoginOutlined, LogoutOutlined, MailOutlined, MobileOutlined, PictureOutlined, SafetyOutlined, UserOutlined, VideoCameraOutlined, WalletOutlined, } from "@ant-design/icons"; import ErrorBoundary from "./components/ErrorBoundary"; import ToastContainer from "./components/toast/ToastContainer"; import { toast } from "./components/toast/toastStore"; import EcommercePage from "./features/ecommerce/EcommercePage"; import { flushPendingGenerationRecords } from "./api/generationRecordClient"; import { ossAssets } from "./data/ossAssets"; import { keyServerClient } from "./api/keyServerClient"; import { setUserMaxConcurrency } from "./api/generationConcurrency"; import { SERVER_SESSION_EXPIRED_EVENT, SERVER_SESSION_REPLACED_EVENT, clearAllUserStorage, type ServerSessionReplacedDetail, } from "./api/serverConnection"; import { initNotificationPermission } from "./utils/generationNotifier"; import { reportError } from "./utils/errorReporting"; import { loadDarkGreenTheme } from "./styles/loadDarkGreenTheme"; import { useAppStore, useSessionStore } from "./stores"; import type { WebUserSession } from "./types"; import "./styles/ecommerce-standalone.css"; type AuthMode = "login" | "register"; type AuthMethod = "account" | "email" | "phone"; interface LocalProfilePageProps { session: WebUserSession; balance: number; imageCount: number; videoCount: number; onBack: () => void; onBugFeedback: () => void; onLogout: () => void; } const profileWorks = [ { title: "主图套图生成", desc: "电商主图与场景图自动生成", image: ossAssets.ecommerce.templateCases[0], type: "图像", time: "6/9 18:13" }, { title: "A+详情页设计", desc: "产品卖点与长图详情版式", image: ossAssets.ecommerce.templateCases[1], type: "图像", time: "6/9 10:11" }, { title: "短视频广告", desc: "产品展示短视频脚本与画面", image: ossAssets.ecommerce.productSet.hosting, type: "视频", time: "6/9 10:05" }, { title: "模特图生成", desc: "服饰商品真人上身展示", image: ossAssets.ecommerce.tryOn.tryA, type: "图像", time: "6/9 10:03" }, { title: "商品场景图", desc: "按平台比例输出营销素材", image: ossAssets.ecommerce.detail.gridA, type: "图像", time: "6/9 10:01" }, { title: "高度复刻", desc: "参考图结构复刻与商品替换", image: ossAssets.ecommerce.detail.gridB, type: "图像", time: "6/9 09:39" }, { title: "详情模块", desc: "功能卖点、参数和包装模块", image: ossAssets.ecommerce.detail.gridC, type: "图像", time: "6/8 21:20" }, { title: "平台素材", desc: "淘宝/天猫投放图批量生成", image: ossAssets.ecommerce.detail.gridD, type: "图像", time: "6/8 18:26" }, ]; function LocalAvatar({ session, size = "md" }: { session: WebUserSession; size?: "sm" | "md" | "lg" }) { const displayName = session.user.displayName || session.user.username || "用户"; const label = displayName.trim().slice(0, 1).toUpperCase() || "用"; const avatarUrl = session.user.avatarUrl; return ( {avatarUrl ? {displayName} : {label}} ); } function LocalProfilePage({ session, balance, imageCount, videoCount, onBack, onBugFeedback, onLogout }: LocalProfilePageProps) { const displayName = session.user.displayName || session.user.username || "用户"; const workCount = Math.max(imageCount + videoCount, profileWorks.length); const projectCount = Math.max(1, Math.round(workCount / 18)); const assetCount = Math.max(1, Math.round(workCount / 20)); return (
{["我的作品", "我的项目", "我的资产", "社区发布"].map((item, index) => ( ))}
代表作 最近完成的高质量生成内容
{workCount} 项
{profileWorks.map((work) => (
{work.type} {work.title}

{work.desc}

已完成 · {work.time}
))}
); } function App() { const session = useSessionStore((s) => s.session); const setSession = useSessionStore((s) => s.setSession); const clearSessionState = useSessionStore((s) => s.clearSession); const usage = useAppStore((s) => s.usage); const setUsage = useAppStore((s) => s.setUsage); const [authOpen, setAuthOpen] = useState(false); const [authMode, setAuthMode] = useState("login"); const [authMethod, setAuthMethod] = useState("account"); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [betaCode, setBetaCode] = useState(""); const [authSubmitting, setAuthSubmitting] = useState(false); const [authError, setAuthError] = useState(null); const [sessionNotice, setSessionNotice] = useState(null); const [profileMenuOpen, setProfileMenuOpen] = useState(false); const [currentPage, setCurrentPage] = useState<"workspace" | "profile">("workspace"); const [workspaceKey, setWorkspaceKey] = useState(0); useEffect(() => { void loadDarkGreenTheme(); document.documentElement.dataset.theme = "dark"; document.documentElement.dataset.uiTheme = "dark-green"; document.documentElement.style.colorScheme = "dark"; document.body.classList.add("ecommerce-standalone-body"); return () => { document.body.classList.remove("ecommerce-standalone-body"); }; }, []); useEffect(() => { const splash = document.getElementById("app-boot-splash"); if (splash) { splash.style.opacity = "0"; const timer = window.setTimeout(() => splash.remove(), 350); return () => window.clearTimeout(timer); } }, []); useEffect(() => { initNotificationPermission(); }, []); useEffect(() => { if (!session) return; void flushPendingGenerationRecords(); }, [session]); useEffect(() => { const handleUnhandled = (event: ErrorEvent) => { reportError(event.error || new Error(event.message), "unhandled"); }; const handleRejection = (event: PromiseRejectionEvent) => { reportError(event.reason instanceof Error ? event.reason : new Error(String(event.reason)), "rejection"); }; window.addEventListener("error", handleUnhandled); window.addEventListener("unhandledrejection", handleRejection); return () => { window.removeEventListener("error", handleUnhandled); window.removeEventListener("unhandledrejection", handleRejection); }; }, []); const refreshUsage = useCallback(async () => { try { setUsage(await keyServerClient.getUsageSummary()); } catch { // Usage is helpful but should not block the standalone generator. } }, [setUsage]); const completeAuth = useCallback( async (nextSession: WebUserSession) => { setSession(nextSession); setUserMaxConcurrency(nextSession.user.maxConcurrency); setAuthOpen(false); setAuthError(null); await refreshUsage(); if (nextSession.user.email && !nextSession.user.emailVerified) { toast.info("邮箱尚未验证,部分功能可能受限"); } }, [refreshUsage, setSession], ); const clearAuthenticatedState = useCallback(() => { clearAllUserStorage(); clearSessionState(); setUserMaxConcurrency(null); setUsage({ balanceCents: 0, imageUsed: 0, videoUsed: 0, textUsed: 0, source: "preview", }); }, [clearSessionState, setUsage]); useEffect(() => { let cancelled = false; const loadSession = async () => { try { const nextSession = await keyServerClient.getCurrentSession(); if (cancelled) return; setSession(nextSession); setUserMaxConcurrency(nextSession?.user.maxConcurrency); if (nextSession) await refreshUsage(); } catch { if (!cancelled) clearAuthenticatedState(); } }; void loadSession(); return () => { cancelled = true; }; }, [clearAuthenticatedState, refreshUsage, setSession]); useEffect(() => { const handleSessionInvalid = (event: Event) => { const detail = (event as CustomEvent).detail; clearAuthenticatedState(); setSessionNotice(detail?.message || "登录状态已失效,请重新登录。"); setAuthOpen(true); }; window.addEventListener(SERVER_SESSION_REPLACED_EVENT, handleSessionInvalid); window.addEventListener(SERVER_SESSION_EXPIRED_EVENT, handleSessionInvalid); return () => { window.removeEventListener(SERVER_SESSION_REPLACED_EVENT, handleSessionInvalid); window.removeEventListener(SERVER_SESSION_EXPIRED_EVENT, handleSessionInvalid); }; }, [clearAuthenticatedState]); const openAuth = useCallback((mode: AuthMode = "login") => { setAuthMode(mode); setAuthError(null); setSessionNotice(null); setAuthOpen(true); }, []); const handleSubmitAuth = async () => { if (!username.trim() || !password) { setAuthError(authMethod === "email" ? "请输入邮箱和密码" : authMethod === "phone" ? "请输入手机号和验证码/密码" : "请输入用户名和密码"); return; } setAuthSubmitting(true); setAuthError(null); try { const nextSession = authMode === "login" ? await keyServerClient.login({ username, password }) : await keyServerClient.register({ username, password, betaCode }); await completeAuth(nextSession); } catch (error) { setAuthError(error instanceof Error ? error.message : "登录失败,请稍后重试。"); } finally { setAuthSubmitting(false); } }; const handleLogout = () => { setProfileMenuOpen(false); setCurrentPage("workspace"); clearAuthenticatedState(); toast.info("已退出登录"); }; const balance = Math.max(usage.balanceCents, 0) / 100; const displayName = session?.user.displayName || session?.user.username || "用户"; const actualWorkCount = Math.max(usage.imageUsed + usage.videoUsed, 0); const shownWorkCount = Math.max(actualWorkCount, profileWorks.length); const avatarMenuStats = useMemo( () => [ { icon: , label: "UID", value: session?.user.id ?? "-" }, { icon: , label: "积分", value: `${balance.toFixed(2)} 积分` }, { icon: , label: "图片", value: usage.imageUsed }, { icon: , label: "视频", value: usage.videoUsed }, { icon: , label: "作品", value: shownWorkCount }, ], [balance, session?.user.id, shownWorkCount, usage.imageUsed, usage.videoUsed], ); const handleOpenProfile = () => { setProfileMenuOpen(false); setCurrentPage("profile"); }; const handleOpenWorkspace = () => { setProfileMenuOpen(false); setCurrentPage("workspace"); setWorkspaceKey((k) => k + 1); }; const handleBugFeedback = () => { setProfileMenuOpen(false); toast.info("Bug 反馈入口已保留,后续可接入反馈页面。"); }; return (
{session ? (
{(Math.max(usage.balanceCents, 0) / 100).toFixed(2)} 积分 {profileMenuOpen ? ( <>
) : null}
) : ( )}
{currentPage === "profile" && session ? ( ) : (
加载中...
} > undefined} onOpenProject={() => undefined} onDeleteProject={() => undefined} onImportWorkflow={() => undefined} onCreateTask={() => undefined} onRequireLogin={() => openAuth("login")} initialTemplate={null} onInitialTemplateConsumed={() => undefined} />
)}
{authOpen ? (

{authMode === "login" ? "欢迎回来" : "创建账号"}

{authMode === "login" ? "登录后继续你的 AI 创作之旅" : "注册即可免费体验全部功能"}

{sessionNotice ?

{sessionNotice}

: null}
{authMode === "register" ? ( ) : null} {authMethod === "phone" ? ( <> ) : ( <> )} {authError ?

{authError}

: null} {authMode === "login" && authMethod !== "phone" ? : null}

{authMode === "login" ? "登录" : "注册"}即表示同意 《用户协议》《隐私政策》

其他方式
) : null} ); } export default App;