'use client'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { apiFetch } from '../../../lib/auth'; import { Skeleton } from '@/components/ui/skeleton'; import { useT } from '@/i18n'; interface Subscription { id: number; plan: string; startDate: string; endDate: string; status: string; } interface Order { id: number; orderNo: string; amount: number; planType: string; status: string; createdAt: string; } const PLANS = [ { id: 'FREE', nameKey: 'planFree' as const, price: 0, period: '', popular: false, features: ['featureSandboxFree', 'featureModelsFree', 'featurePromptsFree', 'featureCoursesFree', 'featureAdsFree'] as const }, { id: 'MONTHLY', nameKey: 'planMonthly' as const, price: 29.9, period: 'perMonth', popular: true, features: ['featureSandboxPro', 'featureModelsPro', 'featurePromptsPro', 'featureCoursesPro', 'featureAdsPro'] as const }, { id: 'YEARLY', nameKey: 'planYearly' as const, price: 199, period: 'perYear', popular: false, features: ['featureSandboxUnlimited', 'featureModelsPremium', 'featurePromptsPremium', 'featureCoursesPremium', 'featureAdsPremium'] as const }, ]; const FEATURE_LABELS = ['featureSandbox', 'featureModels', 'featurePrompts', 'featureCourses', 'featureAds'] as const; export default function MemberPage() { const t = useT(); const [subscription, setSubscription] = useState(null); const [orders, setOrders] = useState([]); const [quota, setQuota] = useState<{ used: number; remaining: number; dailyLimit: number } | null>(null); const [loading, setLoading] = useState(true); const [payLoading, setPayLoading] = useState(null); useEffect(() => { loadData(); }, []); async function loadData() { try { const [subRes, ordersRes, quotaRes] = await Promise.all([ apiFetch('/subscriptions/current').catch(() => ({ ok: false })), apiFetch('/orders'), apiFetch('/sandbox/quota'), ]); if (subRes.ok) setSubscription(await subRes.json()); const ordersData = await ordersRes.json(); setOrders(ordersData.items || []); const quotaData = await quotaRes.json(); if (quotaData.remaining !== undefined) setQuota(quotaData); } catch (e) { console.error(e) } setLoading(false); } async function handleSubscribe(planType: string) { setPayLoading(planType); try { const res = await apiFetch('/orders/create', { method: 'POST', body: JSON.stringify({ amount: planType === 'MONTHLY' ? 29.9 : 199, planType, payChannel: 'wxpay', }), }); const data = await res.json(); if (data.order && data.payResult) { alert('订单创建成功,请扫码支付(模拟模式)'); loadData(); } } catch (e) { console.error(e) } setPayLoading(null); } if (loading) return (
{[1,2,3].map(i => )}
); return (
← 返回我的

{t.member.title}

{t.member.desc}

{quota && (
{t.member.dailyQuota} {t.member.used.replace('{n}', String(quota.used))} / {quota.dailyLimit}
)}
{PLANS.map(plan => { const isCurrent = subscription?.plan === plan.id; return (
{plan.popular && !isCurrent && ( {t.member.popular} )} {isCurrent && ( {t.member.currentPlan_badge} )}

{t.member[plan.nameKey]}

{plan.price > 0 ? ( {plan.price === 29.9 ? t.member.priceMonthly : t.member.priceYearly}{t.member[plan.period]} ) : ( ¥0 )}
{(plan.id === 'FREE' ? FEATURE_LABELS : FEATURE_LABELS).map((f, fi) => (
{t.member[f]} {t.member[plan.features[fi]]}
))}
{plan.id !== 'FREE' && ( )}
); })}

{t.member.orderHistory}

{orders.length === 0 ? (

{t.member.noOrders}

) : (
{orders.map(order => (
{t.member[order.planType === 'YEARLY' ? 'yearly' : 'monthly']}
{new Date(order.createdAt).toLocaleDateString()}
¥{order.amount}
{order.status}
))}
)}
); }