import './index.css'; import QRCode from 'qrcode'; import type { ReactNode } from 'react'; import { useCallback, useEffect, useId, useMemo, useRef, useState, } from 'react'; import type { ProfileMembership, ProfileMembershipCycleKind, ProfileMembershipPlan, ProfileMembershipPlanRecord, ProfileMembershipUpgradeQuote, ProfileRechargeCenterResponse, ProfileRechargeProduct, } from '../../contracts/runtime'; import { formatMudPointCount, formatRechargePrice } from '../../utils/format'; import { formatMudPointResetAt, resolveProfileMudPointDisplayTotal, shouldShowProfileDailyFreePool, } from '../../utils/mudPoints'; import { resolveMembershipConcurrencyLabel, resolveMembershipConcurrencyShortLabel, resolveMembershipModelAccessLabel, resolveMembershipPlanCopy, } from './membershipCopy'; import { resolveMembershipProductId } from './membershipProduct'; const WECHAT_NATIVE_PAY_QR_IMAGE_SIZE = 180; /** * roving tabindex / radiogroup 的方向键索引解析:`Home` / `End` 跳首尾,左右键循环, * 其余按键返回 `null`(调用方据此忽略事件)。`currentIndex` 允许为 `-1`(未选中)。 */ function resolveRovingIndex( key: string, currentIndex: number, length: number, ): number | null { if (length <= 0) { return null; } switch (key) { case 'Home': return 0; case 'End': return length - 1; case 'ArrowLeft': return (currentIndex - 1 + length) % length; case 'ArrowRight': return (currentIndex + 1) % length; default: return null; } } type PlatformProfileRechargeTab = 'subscription' | 'recharge'; export type PlatformProfileRechargeNativePaymentState = { orderId: string; productTitle: string; amountCents: number; codeUrl: string; expiresAt: string; isConfirming: boolean; confirmMessage?: string; }; /** 弹层内的结算目标:泥点商品直接带商品行,会员只有「档位 + 周期」。 */ export type PlatformProfileRechargeCheckoutTarget = | { kind: 'points'; product: ProfileRechargeProduct; } | { kind: 'membership'; plan: ProfileMembershipPlan; cycleKind: ProfileMembershipCycleKind; mode: 'purchase' | 'upgrade'; }; export type PlatformProfileRechargeModalProps = { center: ProfileRechargeCenterResponse | null; isLoading: boolean; error: string | null; submittingProductId: string | null; nativePayment: PlatformProfileRechargeNativePaymentState | null; /** 升级报价;仅升级确认页会用到,首次购买不走报价接口。 */ membershipQuote: ProfileMembershipUpgradeQuote | null; isLoadingMembershipQuote: boolean; membershipQuoteError: string | null; onClose: () => void; onRetry: () => void; /** 泥点商品下单(确认页「充值」触发)。 */ onBuy: (product: ProfileRechargeProduct) => void; /** 升级前取服务端报价;只有需要补差的档位才会调用。 */ onRequestMembershipQuote: (plan: ProfileMembershipPlan) => void; /** 会员下单;productId 由档位与周期拼出,复用泥点下单接口。 */ onSubmitMembershipCheckout: ( plan: ProfileMembershipPlan, cycleKind: ProfileMembershipCycleKind, ) => void; onConfirmNativePayment: () => void; onCloseNativePayment: () => void; }; function joinClassNames( ...classNames: Array ) { return classNames.filter(Boolean).join(' '); } function PlatformProfileRechargeModalShell({ title, description, onClose, closeLabel, closeDisabled = false, size = 'sm', zIndexClassName = 'z-[80]', panelClassName, bodyClassName = 'px-5 py-5', children, }: { title: string; description?: ReactNode; onClose: () => void; closeLabel?: string; closeDisabled?: boolean; size?: 'sm' | 'md'; zIndexClassName?: string; panelClassName: string; bodyClassName?: string; children: ReactNode; }) { const titleId = useId(); const descriptionId = useId(); const sizeClassName = size === 'md' ? 'max-w-xl' : 'max-w-md'; return (
event.stopPropagation()} >
{title}
{description ? (
{description}
) : null}
{children}
); } function PlatformRechargeActionButton({ children, className, disabled, ariaLabel, onClick, }: { children: ReactNode; className?: string; disabled?: boolean; ariaLabel?: string; onClick?: () => void; }) { return ( ); } function PlatformRechargeAsyncStatePanel({ errorState, isLoading, loadingState, isEmpty, emptyState, children, }: { errorState?: ReactNode; isLoading?: boolean; loadingState?: ReactNode; isEmpty?: boolean; emptyState?: ReactNode; children: ReactNode; }) { if (errorState !== undefined && errorState !== null) { return <>{errorState}; } if (isLoading) { return <>{loadingState}; } if (isEmpty) { return <>{emptyState}; } return <>{children}; } function PlatformRechargeSkeletonList({ count, containerClassName, itemClassName, }: { count: number; containerClassName?: string; itemClassName?: string; }) { return (
{Array.from({ length: count }).map((_, index) => (
))}
); } function PlatformRechargeEmptyState({ children, className, }: { children: ReactNode; className?: string; }) { return (
{children}
); } function PlatformRechargeStatusMessage({ children, className, }: { children: ReactNode; className?: string; }) { return (
{children}
); } function useWechatNativeQrCode(codeUrl: string | null) { const [qrImageUrl, setQrImageUrl] = useState(null); useEffect(() => { let cancelled = false; setQrImageUrl(null); if (!codeUrl) { return () => { cancelled = true; }; } void QRCode.toDataURL(codeUrl, { errorCorrectionLevel: 'M', margin: 1, width: WECHAT_NATIVE_PAY_QR_IMAGE_SIZE, }).then((dataUrl) => { if (!cancelled) { setQrImageUrl(dataUrl); } }); return () => { cancelled = true; }; }, [codeUrl]); return qrImageUrl; } function formatNativePaymentRemaining(remainingMs: number) { const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000)); const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; return `${minutes}:${seconds.toString().padStart(2, '0')}`; } function useNativePaymentRemaining(expiresAt: string) { const resolveRemainingMs = useCallback(() => { const expiresAtMs = Date.parse(expiresAt); if (!Number.isFinite(expiresAtMs)) { return 0; } return Math.max(0, expiresAtMs - Date.now()); }, [expiresAt]); const [remainingMs, setRemainingMs] = useState(resolveRemainingMs); useEffect(() => { setRemainingMs(resolveRemainingMs()); const timer = window.setInterval(() => { setRemainingMs(resolveRemainingMs()); }, 1000); return () => window.clearInterval(timer); }, [resolveRemainingMs]); return { isExpired: remainingMs <= 0, label: formatNativePaymentRemaining(remainingMs), }; } type MembershipPlanAction = { disabled: boolean; label: string; mode: 'purchase' | 'upgrade'; }; function resolveMembershipPlanAction( plan: ProfileMembershipPlanRecord, membership: ProfileMembership | null, currentPlan: ProfileMembershipPlanRecord | null, ): MembershipPlanAction { const isActive = membership?.status === 'active' && membership.plan !== 'normal'; if (!isActive || !membership || !currentPlan) { return { disabled: false, label: '开通会员', mode: 'purchase' }; } if (plan.plan === membership.plan) { return { disabled: true, label: '当前套餐', mode: 'purchase' }; } if (plan.rank <= currentPlan.rank) { return { disabled: true, label: '不支持降档', mode: 'purchase' }; } return { disabled: false, label: '补差升级', mode: 'upgrade' }; } /** 升级报价的加载 / 失败态;非升级流程不展示报价状态。 */ function resolveMembershipQuoteErrorState({ isUpgrade, error, isLoading, onRetry, }: { isUpgrade: boolean; error: string | null; isLoading: boolean; onRetry: () => void; }): ReactNode { if (!isUpgrade) { return null; } if (error) { return (
{error}
重新获取报价
); } if (isLoading) { return (
正在获取升级报价…
); } return null; } function PlatformMudPointPoolOverview({ center, }: { center: ProfileRechargeCenterResponse | null; }) { const balance = center?.mudPointBalance; const showDailyFree = shouldShowProfileDailyFreePool(balance); const dailyFreeResetsAt = formatMudPointResetAt(balance?.dailyFreeResetsAt); const monthlyResetsAt = formatMudPointResetAt(balance?.monthlyResetsAt); const pools = [ ...(showDailyFree ? [ { key: 'daily', label: '每日免费', points: balance?.dailyFreePoints ?? 0, detail: `每天重置为 ${formatMudPointCount(balance?.dailyFreeResetPoints ?? 0)}`, }, ] : []), { key: 'monthly', label: '月度泥点', points: balance?.monthlyPoints ?? 0, detail: monthlyResetsAt ? `${monthlyResetsAt} 刷新` : '会员每期额度,刷新日清零', }, { key: 'permanent', label: '永久泥点', points: balance?.permanentPoints ?? 0, detail: '不过期,不随会员到期清零', }, ]; return (
可用总额
{formatMudPointCount( resolveProfileMudPointDisplayTotal(balance) ?? 0, )}
{pools.map((pool) => (
{pool.label}
{formatMudPointCount(pool.points)}
{pool.detail}
))}
{showDailyFree && dailyFreeResetsAt ? (

创作扣点顺序:每日免费 → 月度 → 永久,{dailyFreeResetsAt}{' '} 重置每日免费额度。

) : null}
); } function PlatformRechargeTabNav({ activeTab, onChange, }: { activeTab: PlatformProfileRechargeTab; onChange: (tab: PlatformProfileRechargeTab) => void; }) { const tabs: Array<{ id: PlatformProfileRechargeTab; label: string }> = [ { id: 'subscription', label: '会员订阅' }, { id: 'recharge', label: '泥点充值' }, ]; // 方向键移动选中项后把焦点一并移到新页签,保持 roving tabindex 的焦点与选中一致。 const tabRefs = useRef>({}); return (
{ const currentIndex = tabs.findIndex((tab) => tab.id === activeTab); const nextIndex = resolveRovingIndex( event.key, currentIndex, tabs.length, ); if (nextIndex === null) { return; } event.preventDefault(); const nextTab = tabs[nextIndex]; if (nextTab) { onChange(nextTab.id); tabRefs.current[nextTab.id]?.focus(); } }} > {tabs.map((tab) => { const selected = tab.id === activeTab; return ( ); })}
); } function PlatformMembershipStatusCard({ membership, currentPlan, }: { membership: ProfileMembership; currentPlan: ProfileMembershipPlanRecord | null; }) { const planTitle = currentPlan?.title ?? '会员'; const cycleLabel = membership.cycleKind === 'yearly' ? '年付' : '月付'; const expiresAt = formatMudPointResetAt(membership.expiresAt); const cycleIndexLabel = `${Math.max(1, membership.cycleIndex)}/${Math.max( 1, membership.cycleCount, )}`; return (
当前 {planTitle} · {cycleLabel}
{expiresAt ? `会员至 ${expiresAt}` : '会员有效期读取中'} {membership.cycleKind === 'yearly' ? ` · 第 ${cycleIndexLabel} 期` : ''}
可同周期补差升档
周期切换需等到期后
); } function PlatformMembershipCycleSwitch({ cycleKind, locked, onChange, }: { cycleKind: ProfileMembershipCycleKind; locked: boolean; onChange: (cycleKind: ProfileMembershipCycleKind) => void; }) { const options: Array<{ id: ProfileMembershipCycleKind; label: string }> = [ { id: 'monthly', label: '月付' }, { id: 'yearly', label: '年付' }, ]; return (
{options.map((option) => { const selected = option.id === cycleKind; return ( ); })}
{locked ? ( 有效期内不支持月年互换 ) : null}
); } function PlatformMembershipPlanCard({ plan, cycleKind, isCurrentPlan, recommended, actionLabel, disabled, submitting, onCheckout, }: { plan: ProfileMembershipPlanRecord; cycleKind: ProfileMembershipCycleKind; isCurrentPlan: boolean; recommended: boolean; actionLabel: string; disabled: boolean; submitting: boolean; onCheckout: () => void; }) { const copy = resolveMembershipPlanCopy(plan.plan); const annual = cycleKind === 'yearly'; const listPriceCents = annual ? plan.yearPriceCents : plan.monthPriceCents; const perMonthCents = annual ? Math.round(plan.yearPriceCents / 12) : plan.monthPriceCents; const savingCents = annual ? Math.max(0, plan.monthPriceCents * 12 - plan.yearPriceCents) : 0; const annualPriceLine = `年实付 ${formatRechargePrice(plan.yearPriceCents)}${ savingCents > 0 ? ` · 省 ${formatRechargePrice(savingCents)}` : '' }`; // 按钮文案优先级:不可购买时展示业务文案,其次「处理中」,最后默认「选择并确认」。 let actionButtonLabel = '选择并确认'; if (disabled) { actionButtonLabel = actionLabel; } else if (submitting) { actionButtonLabel = '处理中'; } return (
{recommended ? ( 推荐之选 ) : null}

{plan.title}

{copy.estimateAlt}

{formatRechargePrice(perMonthCents)} {annual ? '折合 / 月' : '/ 月'}
{annual ? annualPriceLine : `每次支付 ${formatRechargePrice(listPriceCents)},有效期 1 个月`}
{formatMudPointCount(plan.periodPoints)} 月度泥点
{annual ? '每月刷新 · 全年发放 12 期' : '每月刷新 · 当期有效'}
每月预计可完成约 {copy.estimateCount} · {copy.estimateKind}
  • {resolveMembershipModelAccessLabel(plan.modelAccess)}
  • {resolveMembershipConcurrencyLabel(plan)}
{actionButtonLabel}
); } function PlatformMembershipComparisonTable({ plans, }: { plans: ProfileMembershipPlanRecord[]; }) { const rows: Array<{ label: string; values: string[]; }> = [ { label: '月付价格', values: plans.map((plan) => formatRechargePrice(plan.monthPriceCents)), }, { label: '年付价格', values: plans.map((plan) => formatRechargePrice(plan.yearPriceCents)), }, { label: '每期泥点', values: plans.map((plan) => formatMudPointCount(plan.periodPoints)), }, { label: '模型权限', values: plans.map((plan) => resolveMembershipModelAccessLabel(plan.modelAccess), ), }, { label: '独立任务并发', values: plans.map((plan) => resolveMembershipConcurrencyShortLabel(plan)), }, ]; return (
{plans.map((plan) => ( ))} {rows.map((row) => ( {row.values.map((value, index) => ( ))} ))}
套餐 {plan.title}
{row.label} {value}
); } function PlatformPointProductGrid({ products, selectedProductId, submittingProductId, onSelect, }: { products: ProfileRechargeProduct[]; selectedProductId: string | null; submittingProductId: string | null; onSelect: (product: ProfileRechargeProduct) => void; }) { // 方向键移动选中项后同步移动焦点,保持 roving tabindex 的焦点与选中一致。 const productRefs = useRef>({}); return (
{ const currentIndex = products.findIndex( (product) => product.productId === selectedProductId, ); const nextIndex = resolveRovingIndex( event.key, currentIndex, products.length, ); if (nextIndex === null) { return; } event.preventDefault(); const nextProduct = products[nextIndex]; if (nextProduct) { onSelect(nextProduct); productRefs.current[nextProduct.productId]?.focus(); } }} > {products.map((product) => { const selected = product.productId === selectedProductId; const submitting = submittingProductId === product.productId; return ( ); })}
); } function PlatformRechargeCheckoutDialog({ target, membershipPlans, membership, membershipQuote, isLoadingMembershipQuote, membershipQuoteError, submittingProductId, showDailyFreePool, onClose, onBuy, onRequestMembershipQuote, onSubmitMembershipCheckout, }: { target: PlatformProfileRechargeCheckoutTarget; membershipPlans: ProfileMembershipPlanRecord[]; membership: ProfileMembership | null; membershipQuote: ProfileMembershipUpgradeQuote | null; isLoadingMembershipQuote: boolean; membershipQuoteError: string | null; submittingProductId: string | null; showDailyFreePool: boolean; onClose: () => void; onBuy: (product: ProfileRechargeProduct) => void; onRequestMembershipQuote: (plan: ProfileMembershipPlan) => void; onSubmitMembershipCheckout: ( plan: ProfileMembershipPlan, cycleKind: ProfileMembershipCycleKind, ) => void; }) { if (target.kind === 'points') { const product = target.product; const submitting = submittingProductId === product.productId; return ( { onBuy(product); onClose(); }} lines={[ { label: '充值项目', value: product.title }, { label: '本次到账', value: `${formatMudPointCount(product.pointsAmount)} 永久泥点`, }, ]} note={ showDailyFreePool ? '永久有效,不会随会员到期清零。创作时先扣每日免费与月度泥点,不足部分再扣永久泥点。' : '永久有效,不会随会员到期清零。创作时先扣月度泥点,不足部分再扣永久泥点。' } /> ); } const plan = membershipPlans.find((item) => item.plan === target.plan) ?? null; const annual = target.cycleKind === 'yearly'; const cycleLabel = annual ? '年付' : '月付'; const listPriceCents = annual ? (plan?.yearPriceCents ?? 0) : (plan?.monthPriceCents ?? 0); const isUpgrade = target.mode === 'upgrade'; const currentPlan = membershipPlans.find((item) => item.plan === membership?.plan) ?? null; const productId = resolveMembershipProductId(target.plan, target.cycleKind); const submitting = submittingProductId === productId; const quoteForTarget = isUpgrade && membershipQuote && membershipQuote.targetPlan === target.plan && membershipQuote.cycleKind === target.cycleKind ? membershipQuote : null; // 升级金额只能来自服务端报价:报价未到 / 失败时不能拿新套餐标准价冒充「本次补付」。 const amountPending = isUpgrade && !quoteForTarget; const amountCents = quoteForTarget ? quoteForTarget.amountCents : listPriceCents; const amountText = amountPending ? '—' : formatRechargePrice(amountCents); let ctaLabel: string; if (!isUpgrade) { ctaLabel = `支付 ${amountText}`; } else if (amountPending) { ctaLabel = '补差升级'; } else { ctaLabel = `补差升级 ${amountText}`; } const canSubmit = Boolean(plan) && !submitting && (!isUpgrade || Boolean(quoteForTarget)) && amountCents > 0; const lines: Array<{ label: string; value: string }> = []; if (isUpgrade) { if (currentPlan) { lines.push({ label: `原套餐标准${annual ? '年' : '月'}价`, value: formatRechargePrice( annual ? currentPlan.yearPriceCents : currentPlan.monthPriceCents, ), }); } lines.push({ label: `新套餐标准${annual ? '年' : '月'}价`, value: formatRechargePrice(listPriceCents), }); if (quoteForTarget) { lines.push({ label: '本期补发月度泥点', value: `+${formatMudPointCount(quoteForTarget.grantedPointsDelta)}`, }); lines.push({ label: '补点后月度余额', value: formatMudPointCount(quoteForTarget.monthlyBalanceAfter), }); const expiresAt = formatMudPointResetAt(quoteForTarget.expiresAt); const cycleResetsAt = formatMudPointResetAt(quoteForTarget.cycleResetsAt); if (expiresAt) { lines.push({ label: '会员到期日(不变)', value: expiresAt }); } if (cycleResetsAt) { lines.push({ label: '本期刷新日(不变)', value: cycleResetsAt }); } } } else { lines.push({ label: '到账月度泥点', value: plan ? `${formatMudPointCount(plan.periodPoints)}(首期)` : '—', }); lines.push({ label: '计费周期', value: annual ? '年付 · 共 12 期,本次只到账首期' : '月付 · 本次到账当期额度', }); } return ( { onSubmitMembershipCheckout(target.plan, target.cycleKind); onClose(); }} lines={lines} note={ isUpgrade ? '按剩余比例补差,保留现有余额,已用点不返还;升级后原刷新日与到期日不变。' : `月度泥点按开通日自然月计期,当期未用完部分到期清零,不结转。当前选择 ${cycleLabel}。` } errorState={resolveMembershipQuoteErrorState({ isUpgrade, error: membershipQuoteError, isLoading: isLoadingMembershipQuote, onRetry: () => onRequestMembershipQuote(target.plan), })} /> ); } function PlatformRechargeCheckoutShell({ title, amountCents, amountPending = false, amountLabel = '本次支付', ctaLabel, ctaDisabled = false, submitting, lines, note, errorState, onClose, onSubmit, }: { title: string; amountCents: number; /** 金额未知(升级报价未到 / 失败)时只显示占位符,避免拿标准价冒充补差金额。 */ amountPending?: boolean; amountLabel?: string; ctaLabel: string; ctaDisabled?: boolean; submitting: boolean; lines: Array<{ label: string; value: string }>; note: string; errorState?: ReactNode; onClose: () => void; onSubmit: () => void; }) { return (
{amountLabel}
{amountPending ? '—' : formatRechargePrice(amountCents)}
{errorState ?
{errorState}
: null}
{lines.map((line) => (
{line.label} {line.value}
))}
支付方式
{/* 中文注释:当前只支持微信支付,是纯展示文案,不用 radiogroup / radio 语义(不可交互的 单选项会误导读屏)。 */}
微 微信支付
{submitting ? '处理中' : ctaLabel}

{note}

); } function PlatformProfileWechatNativePaymentModal({ nativePayment, nativeQrImageUrl, onClose, onConfirm, }: { nativePayment: PlatformProfileRechargeNativePaymentState; nativeQrImageUrl: string | null; onClose: () => void; onConfirm: () => void; }) { const remaining = useNativePaymentRemaining(nativePayment.expiresAt); return (
支付项目
{nativePayment.productTitle}
支付金额
{formatRechargePrice(nativePayment.amountCents)}
剩余时间 {remaining.isExpired ? '已过期' : remaining.label}
{nativeQrImageUrl ? ( 微信 Native 支付二维码 ) : ( 生成中 )}
{nativePayment.isConfirming ? '确认中' : remaining.isExpired ? '已过期' : '我已支付'} {nativePayment.confirmMessage ? (
{nativePayment.confirmMessage}
) : null}
); } export function PlatformProfileRechargeModal({ center, isLoading, error, submittingProductId, nativePayment, membershipQuote, isLoadingMembershipQuote, membershipQuoteError, onClose, onRetry, onBuy, onRequestMembershipQuote, onSubmitMembershipCheckout, onConfirmNativePayment, onCloseNativePayment, }: PlatformProfileRechargeModalProps) { const nativeQrImageUrl = useWechatNativeQrCode( nativePayment?.codeUrl ?? null, ); const membership = center?.membership ?? null; const isActiveMembership = membership?.status === 'active' && membership.plan !== 'normal'; const currentBalance = resolveProfileMudPointDisplayTotal( center?.mudPointBalance, ); const showDailyFreePool = shouldShowProfileDailyFreePool( center?.mudPointBalance, ); // 后端目录已按 rank 升序返回,这里只过滤不可购买与占位档。 const membershipPlans = useMemo( () => (center?.membershipPlans ?? []).filter( (plan) => plan.enabled && plan.plan !== 'normal', ), [center?.membershipPlans], ); const pointProducts = useMemo( () => center?.pointProducts ?? [], [center?.pointProducts], ); const currentPlanRecord = useMemo( () => membershipPlans.find((plan) => plan.plan === membership?.plan) ?? null, [membership?.plan, membershipPlans], ); const [activeTab, setActiveTab] = useState('subscription'); const [cycleKind, setCycleKind] = useState('monthly'); const [selectedPointProductId, setSelectedPointProductId] = useState< string | null >(null); const [checkout, setCheckout] = useState(null); // 有效会员期内不允许月付 / 年付互换,切换器锁定在当前周期。 useEffect(() => { if (isActiveMembership && membership?.cycleKind) { setCycleKind(membership.cycleKind); } }, [isActiveMembership, membership?.cycleKind]); useEffect(() => { setSelectedPointProductId((current) => { if ( current && pointProducts.some((product) => product.productId === current) ) { return current; } if (pointProducts.length === 0) { return null; } // 中文注释:默认预选第 3 档(索引 2,不足时退回末档),让首屏落在价格居中的档位; // 充点商品目前同价(¥1 = 10 泥点),这里只影响默认选中项,不改变计费。 const defaultProductIndex = Math.min(2, pointProducts.length - 1); const fallbackProduct = pointProducts[defaultProductIndex]; return fallbackProduct ? fallbackProduct.productId : null; }); }, [pointProducts]); const selectedPointProduct = pointProducts.find( (product) => product.productId === selectedPointProductId, ) ?? null; const openMembershipCheckout = useCallback( (plan: ProfileMembershipPlanRecord) => { const action = resolveMembershipPlanAction( plan, membership, currentPlanRecord, ); if (action.disabled) { return; } setCheckout({ kind: 'membership', plan: plan.plan, cycleKind, mode: action.mode, }); if (action.mode === 'upgrade') { onRequestMembershipQuote(plan.plan); } }, [currentPlanRecord, cycleKind, membership, onRequestMembershipQuote], ); return ( <>
{error}
重新加载 ) : null } isLoading={isLoading} loadingState={ } isEmpty={membershipPlans.length === 0 && pointProducts.length === 0} emptyState={ 暂无可购买套餐 } >
{checkout ? ( setCheckout(null)} onBuy={onBuy} onRequestMembershipQuote={onRequestMembershipQuote} onSubmitMembershipCheckout={onSubmitMembershipCheckout} /> ) : null} {nativePayment ? ( ) : null} ); }