diff --git a/src/components/AppShell.tsx b/src/components/AppShell.tsx index ec66e9b..2207708 100644 --- a/src/components/AppShell.tsx +++ b/src/components/AppShell.tsx @@ -19,10 +19,10 @@ import { ossAssets } from "../data/ossAssets"; import { canManageCommunityCases, canReviewCommunity } from "../features/community-review/communityPermissions"; import type { WebNavItem, WebNotification, WebUsageSummary, WebUserSession, WebViewKey } from "../types"; import NotificationCenter from "./NotificationCenter"; -import { RechargeModal } from "./RechargeModal/RechargeModal"; import { AnimatedPanel } from "./AnimatedPanel"; import AdminMonitor from "./AdminMonitor"; import CookieConsentBanner from "./CookieConsentBanner"; +import { loadRechargeModal, type RechargeModalComponent } from "./RechargeModal/loadRechargeModal"; interface AppShellProps { activeView: WebViewKey; @@ -93,6 +93,7 @@ function AppShell({ const submenuHideTimerRef = useRef(null); const [profileOpen, setProfileOpen] = useState(false); const [rechargeOpen, setRechargeOpen] = useState(false); + const [RechargeModal, setRechargeModal] = useState(null); const [infoOpen, setInfoOpen] = useState(false); const infoRef = useRef(null); const [openSubmenuKey, setOpenSubmenuKey] = useState(null); @@ -192,6 +193,21 @@ function AppShell({ }; }, []); + useEffect(() => { + if (!rechargeOpen || RechargeModal) return; + + let cancelled = false; + void loadRechargeModal().then((component) => { + if (!cancelled) { + setRechargeModal(() => component); + } + }); + + return () => { + cancelled = true; + }; + }, [RechargeModal, rechargeOpen]); + const showSubmenu = (key: WebViewKey) => { if (submenuHideTimerRef.current) { window.clearTimeout(submenuHideTimerRef.current); @@ -491,7 +507,9 @@ function AppShell({ {session?.user.role === "admin" ? : null} - setRechargeOpen(false)} currentBalance={displayedBalanceCents} /> + {rechargeOpen && RechargeModal ? ( + setRechargeOpen(false)} currentBalance={displayedBalanceCents} /> + ) : null} ); diff --git a/src/components/RechargeModal/RechargeModal.tsx b/src/components/RechargeModal/RechargeModal.tsx index 95be2eb..49e01da 100644 --- a/src/components/RechargeModal/RechargeModal.tsx +++ b/src/components/RechargeModal/RechargeModal.tsx @@ -1,5 +1,6 @@ import { CheckCircleOutlined, CloseOutlined, CrownOutlined, RocketOutlined } from "@ant-design/icons"; import { useMemo, useState, type ReactNode } from "react"; +import "../../styles/components/recharge-modal.css"; import { keyServerClient, type RechargeOrderResult } from "../../api/keyServerClient"; import { toast } from "../toast/toastStore"; @@ -116,7 +117,7 @@ const paymentMethods: Array<{ id: PaymentMethod; label: string; hint: string }> { id: "bank", label: "对公转账", hint: "企业客户可联系客服确认" }, ]; -interface RechargeModalProps { +export interface RechargeModalProps { open: boolean; onClose: () => void; currentBalance?: number; diff --git a/src/components/RechargeModal/loadRechargeModal.ts b/src/components/RechargeModal/loadRechargeModal.ts new file mode 100644 index 0000000..1d14aff --- /dev/null +++ b/src/components/RechargeModal/loadRechargeModal.ts @@ -0,0 +1,14 @@ +import type { ComponentType } from "react"; +import type { RechargeModalProps } from "./RechargeModal"; + +export type RechargeModalComponent = ComponentType; + +let rechargeModalPromise: Promise | null = null; + +export function loadRechargeModal(): Promise { + if (!rechargeModalPromise) { + rechargeModalPromise = import("./RechargeModal").then((module) => module.RechargeModal); + } + + return rechargeModalPromise; +} diff --git a/src/features/workbench/WorkbenchPage.tsx b/src/features/workbench/WorkbenchPage.tsx index 765603d..247206c 100644 --- a/src/features/workbench/WorkbenchPage.tsx +++ b/src/features/workbench/WorkbenchPage.tsx @@ -41,7 +41,7 @@ import { claimGenerationSlot, getActiveGenerationTaskCount, getGenerationUserKey import { preUploadReference, resolvePreUploadedUrl } from "../../api/referenceUploadService"; import { assetClient } from "../../api/assetClient"; import { communityClient } from "../../api/communityClient"; -import { RechargeModal } from "../../components/RechargeModal/RechargeModal"; +import { loadRechargeModal, type RechargeModalComponent } from "../../components/RechargeModal/loadRechargeModal"; import { useGenerationTasks } from "../../hooks/useGenerationTasks"; import { conversationClient, type ConversationSummary } from "../../api/conversationClient"; @@ -268,6 +268,7 @@ function WorkbenchPage({ const [isGenerating, setIsGenerating] = useState(false); const [generationStatus, setGenerationStatus] = useState("准备就绪"); const [showRechargeModal, setShowRechargeModal] = useState(false); + const [RechargeModal, setRechargeModal] = useState(null); const [savedAssetMentionItems, setSavedAssetMentionItems] = useState< Pick[] >([]); @@ -291,6 +292,21 @@ function WorkbenchPage({ activeConversationIdRef.current = activeConversationId; }, []); + useEffect(() => { + if (!showRechargeModal || RechargeModal) return; + + let cancelled = false; + void loadRechargeModal().then((component) => { + if (!cancelled) { + setRechargeModal(() => component); + } + }); + + return () => { + cancelled = true; + }; + }, [RechargeModal, showRechargeModal]); + useEffect(() => { if (!isAuthenticated) return; let cancelled = false; @@ -3191,7 +3207,9 @@ function WorkbenchPage({ {renderMessagePreviewOverlay()} {renderDeleteDialog()} - setShowRechargeModal(false)} currentBalance={session?.user?.balanceCents} /> + {showRechargeModal && RechargeModal ? ( + setShowRechargeModal(false)} currentBalance={session?.user?.balanceCents} /> + ) : null} ); } diff --git a/src/styles/index.css b/src/styles/index.css index 4ae1da2..efc0b14 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -3,7 +3,6 @@ @import "./shell/app-shell.css"; @import "./components/primitives.css"; @import "./components/legacy-components.css"; -@import "./components/recharge-modal.css"; @import "./components/toast.css"; @import "./components/page-transition.css"; @import "./components/motion.css";