/** * 升级/降级/续费页面 * P1-3: antd Button/Modal/Radio/Spin → 自定义 UI 组件 */ import React, { useState, useEffect } from "react" import { message } from "antd" import { Button, Modal } from "@/components/ui" import { useNavigate } from "react-router-dom" import { getCurrentSubscription, changePlan, toggleAutoRenew, cancelSubscription, } from "@/api/subscription" import type { SubscriptionInfo, PlanType, BillingCycle } from "@/api/subscription" import PageHead from "@/components/layout/PageHead" import "./UpgradeSubscription.css" const PLANS_META: Record = { free: { name: "体验版", price: 0, yearlyPrice: 0 }, standard: { name: "标准版", price: 99, yearlyPrice: 990 }, pro: { name: "专业版", price: 299, yearlyPrice: 2990 }, enterprise: { name: "企业版", price: 0, yearlyPrice: 0 }, } /** 自定义计费周期切换组件 */ const BillingCycleSwitch: React.FC<{ value: BillingCycle onChange: (cycle: BillingCycle) => void monthlyPrice: number yearlyPrice: number }> = ({ value, onChange, monthlyPrice, yearlyPrice }) => (
) /** 自定义 Spinner 组件 */ const Spinner: React.FC<{ size?: "small" | "large" }> = ({ size = "large" }) => (
) const UpgradeSubscription: React.FC = () => { const navigate = useNavigate() const [subscription, setSubscription] = useState(null) const [loading, setLoading] = useState(true) const [submitting, setSubmitting] = useState(false) const [selectedPlan, setSelectedPlan] = useState("standard") const [billingCycle, setBillingCycle] = useState("monthly") useEffect(() => { loadSubscription() }, []) const loadSubscription = async () => { try { const data = await getCurrentSubscription() setSubscription(data) setSelectedPlan(data.plan_id) } catch (err: unknown) { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("获取订阅信息失败") } finally { setLoading(false) } } const handleUpgrade = async () => { if (!subscription) return if (selectedPlan === subscription.plan_id && billingCycle === subscription.billing_cycle) { message.info("当前已是该套餐") return } const plan = PLANS_META[selectedPlan] const price = billingCycle === "yearly" ? plan.yearlyPrice : plan.price Modal.confirm({ title: "确认变更套餐", content: `即将变更为「${plan.name}」(${billingCycle === "monthly" ? "月付" : "年付"}),${price > 0 ? `费用 ¥${price}${billingCycle === "monthly" ? "/月" : "/年"}` : "免费"}。变更立即生效。`, okText: "确认变更", cancelText: "取消", onOk: async () => { try { setSubmitting(true) const res = await changePlan({ target_plan_id: selectedPlan, billing_cycle: billingCycle, }) if (res.success) { message.success(res.message) setSubscription(res.new_subscription ?? null) } else { message.error(res.message) } } catch (err: unknown) { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("套餐变更失败,请重试") } finally { setSubmitting(false) } }, }) } const handleToggleAutoRenew = async (enabled: boolean) => { try { const res = await toggleAutoRenew(enabled) message.success(res.message) if (subscription) { setSubscription({ ...subscription, auto_renew: enabled }) } } catch (err: unknown) { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("操作失败") } } const handleCancel = () => { Modal.confirm({ title: "确认取消订阅", content: "取消后,当前周期结束前仍可正常使用,到期后降级为体验版。", okText: "确认取消", cancelText: "再想想", onOk: async () => { try { const res = await cancelSubscription() message.success(res.message) navigate("/app/subscription") } catch (err: unknown) { if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("取消失败") } }, }) } if (loading) { return (
) } const currentPlan = subscription?.plan_id ?? "free" return (
{(["standard", "pro", "enterprise"] as PlanType[]).map((planId) => { const plan = PLANS_META[planId] const isCurrent = planId === currentPlan return (
setSelectedPlan(planId)} > {isCurrent &&
当前
}

{plan.name}

) })}
{subscription && subscription.status === "active" && (
自动续费:{subscription.auto_renew ? "已开启" : "已关闭"}
)} {subscription && subscription.status === "active" && currentPlan !== "free" && ( )}
) } export default UpgradeSubscription export const Component = UpgradeSubscription