|
|
|
@ -198,6 +198,10 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan |
|
|
|
const [planList, setPlanList] = useState<ProductPlan[]>([]); |
|
|
|
const [selectedPlanKeyByGroup, setSelectedPlanKeyByGroup] = useState<Record<string, string>>({}); |
|
|
|
const userMemberLevel = Number(user?.memberLevel ?? 0); |
|
|
|
const hasMonthlyPlan = planList.some((plan) => plan.planCategory === "monthly"); |
|
|
|
const hasYearlyPlan = planList.some((plan) => plan.planCategory === "yearly"); |
|
|
|
const activeBillingCycle: SubscribeBillingCycle = billingCycle === "yearly" && hasYearlyPlan ? "yearly" : hasMonthlyPlan ? "monthly" : hasYearlyPlan ? "yearly" : billingCycle; |
|
|
|
const showBillingCycle = hasMonthlyPlan && hasYearlyPlan; |
|
|
|
|
|
|
|
// 拉取套餐列表
|
|
|
|
useEffect(() => { |
|
|
|
@ -234,7 +238,7 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan |
|
|
|
const groupByLevel = new Map<number, PlanGroup>(); |
|
|
|
|
|
|
|
planList.forEach((plan, index) => { |
|
|
|
if (plan.planCategory !== billingCycle) { |
|
|
|
if (plan.planCategory !== activeBillingCycle) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
@ -250,7 +254,7 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan |
|
|
|
}); |
|
|
|
|
|
|
|
return groups; |
|
|
|
}, [billingCycle, planList]); |
|
|
|
}, [activeBillingCycle, planList]); |
|
|
|
|
|
|
|
const showPaySubscribe = useCallback((plan: ProductPlan) => { |
|
|
|
void openPaySubscribeDialog({ type: "subscribe", plan }); |
|
|
|
@ -259,48 +263,50 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan |
|
|
|
const joinCls = (...parts: Array<string | false | undefined | null>) => parts.filter(Boolean).join(" "); |
|
|
|
|
|
|
|
return ( |
|
|
|
<div className={joinCls("plans", embedded && "plans_embedded")}> |
|
|
|
<div className="plans__points-row"> |
|
|
|
<div className="billing_cycle" role="tablist" aria-label={t("subscribe.billing_cycle_tabs_label")}> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
role="tab" |
|
|
|
className={joinCls("billing_cycle__tab", "billing_cycle__tab_with_badge", "billing_cycle__tab_monthly", billingCycle === "monthly" && "billing_cycle__tab_active")} |
|
|
|
aria-selected={billingCycle === "monthly"} |
|
|
|
onClick={() => { |
|
|
|
setBillingCycle("monthly"); |
|
|
|
}} |
|
|
|
> |
|
|
|
<span className="billing_cycle__tab-inner"> |
|
|
|
<span className="billing_cycle__label">{t("subscribe.billing_monthly")}</span> |
|
|
|
<span className="billing_cycle__badge billing_cycle__badge_hot" aria-hidden="true"> |
|
|
|
{t("subscribe.billing_badge_hot")} |
|
|
|
<div className={joinCls("plans", embedded && "plans_embedded", !showBillingCycle && "plans_no-billing-cycle")}> |
|
|
|
{showBillingCycle ? ( |
|
|
|
<div className="plans__points-row"> |
|
|
|
<div className="billing_cycle" role="tablist" aria-label={t("subscribe.billing_cycle_tabs_label")}> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
role="tab" |
|
|
|
className={joinCls("billing_cycle__tab", "billing_cycle__tab_with_badge", "billing_cycle__tab_monthly", activeBillingCycle === "monthly" && "billing_cycle__tab_active")} |
|
|
|
aria-selected={activeBillingCycle === "monthly"} |
|
|
|
onClick={() => { |
|
|
|
setBillingCycle("monthly"); |
|
|
|
}} |
|
|
|
> |
|
|
|
<span className="billing_cycle__tab-inner"> |
|
|
|
<span className="billing_cycle__label">{t("subscribe.billing_monthly")}</span> |
|
|
|
<span className="billing_cycle__badge billing_cycle__badge_hot" aria-hidden="true"> |
|
|
|
{t("subscribe.billing_badge_hot")} |
|
|
|
</span> |
|
|
|
</span> |
|
|
|
</span> |
|
|
|
</button> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
role="tab" |
|
|
|
className={joinCls("billing_cycle__tab", "billing_cycle__tab_with_badge", "billing_cycle__tab_yearly", billingCycle === "yearly" && "billing_cycle__tab_active")} |
|
|
|
aria-selected={billingCycle === "yearly"} |
|
|
|
onClick={() => { |
|
|
|
setBillingCycle("yearly"); |
|
|
|
}} |
|
|
|
> |
|
|
|
<span className="billing_cycle__tab-inner"> |
|
|
|
<span className="billing_cycle__label">{t("subscribe.billing_yearly")}</span> |
|
|
|
<span className="billing_cycle__badge billing_cycle__badge_value" aria-hidden="true"> |
|
|
|
{t("subscribe.billing_badge_value")} |
|
|
|
</button> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
role="tab" |
|
|
|
className={joinCls("billing_cycle__tab", "billing_cycle__tab_with_badge", "billing_cycle__tab_yearly", activeBillingCycle === "yearly" && "billing_cycle__tab_active")} |
|
|
|
aria-selected={activeBillingCycle === "yearly"} |
|
|
|
onClick={() => { |
|
|
|
setBillingCycle("yearly"); |
|
|
|
}} |
|
|
|
> |
|
|
|
<span className="billing_cycle__tab-inner"> |
|
|
|
<span className="billing_cycle__label">{t("subscribe.billing_yearly")}</span> |
|
|
|
<span className="billing_cycle__badge billing_cycle__badge_value" aria-hidden="true"> |
|
|
|
{t("subscribe.billing_badge_value")} |
|
|
|
</span> |
|
|
|
</span> |
|
|
|
</span> |
|
|
|
</button> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
) : null} |
|
|
|
|
|
|
|
<div className="plan-grid-container"> |
|
|
|
<div className="plan-grid"> |
|
|
|
{planGroups.map((planGroup) => { |
|
|
|
const groupKey = `${billingCycle}-${String(planGroup.level)}`; |
|
|
|
const groupKey = `${activeBillingCycle}-${String(planGroup.level)}`; |
|
|
|
const selectedPlanItem = planGroup.plans.find((item) => item.key === selectedPlanKeyByGroup[groupKey]) ?? planGroup.plans[0]; |
|
|
|
const plan = selectedPlanItem.plan; |
|
|
|
const price = Number(plan.price ?? 0) / 100; // 价格单位为分,需要除以100转换为元
|
|
|
|
|