feat: 金额上限校验、订阅详情展示优化、支付商品名称区分
- 硬编码 MAX_AMOUNT=99999999.99,所有金额输入(API+前端)统一校验上限
- 管理后台订阅列表改为卡片布局,Sub2API 分组信息嵌套只读展示(平台/倍率/限额/模型)
- 用户端套餐卡片和确认页展示平台、倍率、用量限制
- 订阅订单支付商品名改为 "Sub2API 订阅 {分组名}",余额充值保持原格式
This commit is contained in:
@@ -62,11 +62,11 @@ export default function SubscriptionConfirm({
|
||||
{/* Plan info card */}
|
||||
<div
|
||||
className={[
|
||||
'rounded-xl border p-4',
|
||||
'rounded-xl border p-4 space-y-3',
|
||||
isDark ? 'border-slate-700 bg-slate-800/80' : 'border-slate-200 bg-slate-50',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={['text-base font-semibold', isDark ? 'text-slate-100' : 'text-slate-900'].join(' ')}>
|
||||
{plan.name}
|
||||
@@ -81,6 +81,24 @@ export default function SubscriptionConfirm({
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Platform & Rate tags */}
|
||||
{(plan.platform || plan.rateMultiplier != null) && (
|
||||
<div className={['flex flex-wrap gap-2 text-xs', isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
|
||||
{plan.platform && (
|
||||
<span className={['inline-flex items-center rounded-md px-2 py-0.5', isDark ? 'bg-slate-700/60' : 'bg-slate-100'].join(' ')}>
|
||||
{pickLocaleText(locale, '平台', 'Platform')}: {plan.platform}
|
||||
</span>
|
||||
)}
|
||||
{plan.rateMultiplier != null && (
|
||||
<span className={['inline-flex items-center rounded-md px-2 py-0.5', isDark ? 'bg-slate-700/60' : 'bg-slate-100'].join(' ')}>
|
||||
{pickLocaleText(locale, '倍率', 'Rate')}: {plan.rateMultiplier}x
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Features */}
|
||||
{plan.features.length > 0 && (
|
||||
<ul className="space-y-1">
|
||||
{plan.features.map((feature) => (
|
||||
@@ -93,6 +111,24 @@ export default function SubscriptionConfirm({
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* Usage limits */}
|
||||
{plan.limits && (plan.limits.daily_limit_usd != null || plan.limits.weekly_limit_usd != null || plan.limits.monthly_limit_usd != null) && (
|
||||
<div className={['rounded-lg p-2.5 text-xs', isDark ? 'bg-slate-900/60 text-slate-400' : 'bg-white/80 text-slate-500'].join(' ')}>
|
||||
<p className="mb-1 font-medium">{pickLocaleText(locale, '用量限制', 'Usage Limits')}</p>
|
||||
<div className="space-y-0.5">
|
||||
{plan.limits.daily_limit_usd != null && (
|
||||
<p>{pickLocaleText(locale, `每日: $${plan.limits.daily_limit_usd}`, `Daily: $${plan.limits.daily_limit_usd}`)}</p>
|
||||
)}
|
||||
{plan.limits.weekly_limit_usd != null && (
|
||||
<p>{pickLocaleText(locale, `每周: $${plan.limits.weekly_limit_usd}`, `Weekly: $${plan.limits.weekly_limit_usd}`)}</p>
|
||||
)}
|
||||
{plan.limits.monthly_limit_usd != null && (
|
||||
<p>{pickLocaleText(locale, `每月: $${plan.limits.monthly_limit_usd}`, `Monthly: $${plan.limits.monthly_limit_usd}`)}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Payment method selector */}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { formatValidityLabel, formatValiditySuffix, type ValidityUnit } from '@/
|
||||
export interface PlanInfo {
|
||||
id: string;
|
||||
groupId: number;
|
||||
groupName: string | null;
|
||||
name: string;
|
||||
price: number;
|
||||
originalPrice: number | null;
|
||||
@@ -15,6 +16,8 @@ export interface PlanInfo {
|
||||
validityUnit?: ValidityUnit;
|
||||
features: string[];
|
||||
description: string | null;
|
||||
platform: string | null;
|
||||
rateMultiplier: number | null;
|
||||
limits: {
|
||||
daily_limit_usd: number | null;
|
||||
weekly_limit_usd: number | null;
|
||||
@@ -76,6 +79,22 @@ export default function SubscriptionPlanCard({ plan, onSubscribe, isDark, locale
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Platform & Rate */}
|
||||
{(plan.platform || plan.rateMultiplier != null) && (
|
||||
<div className={['mb-3 flex flex-wrap gap-2 text-xs', isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>
|
||||
{plan.platform && (
|
||||
<span className={['inline-flex items-center gap-1 rounded-md px-2 py-0.5', isDark ? 'bg-slate-700/60' : 'bg-slate-100'].join(' ')}>
|
||||
{pickLocaleText(locale, '平台', 'Platform')}: {plan.platform}
|
||||
</span>
|
||||
)}
|
||||
{plan.rateMultiplier != null && (
|
||||
<span className={['inline-flex items-center gap-1 rounded-md px-2 py-0.5', isDark ? 'bg-slate-700/60' : 'bg-slate-100'].join(' ')}>
|
||||
{pickLocaleText(locale, '倍率', 'Rate')}: {plan.rateMultiplier}x
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Features */}
|
||||
{plan.features.length > 0 && (
|
||||
<ul className="mb-4 space-y-2">
|
||||
|
||||
Reference in New Issue
Block a user