mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-30 05:39:26 +00:00
Merge pull request #2838 from QuantumNous/fix/subscription-epay
✨ fix: Improve subscription payment handling and card layout consistency
This commit is contained in:
@@ -108,7 +108,7 @@ func SubscriptionRequestEpay(c *gin.Context) {
|
||||
common.ApiErrorMsg(c, "拉起支付失败")
|
||||
return
|
||||
}
|
||||
common.ApiSuccess(c, gin.H{"data": params, "url": uri})
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success", "data": params, "url": uri})
|
||||
}
|
||||
|
||||
func SubscriptionEpayNotify(c *gin.Context) {
|
||||
|
||||
@@ -128,7 +128,11 @@ const SubscriptionPlansCard = ({
|
||||
showSuccess(t('已打开支付页面'));
|
||||
closeBuy();
|
||||
} else {
|
||||
showError(res.data?.data || res.data?.message || t('支付失败'));
|
||||
const errorMsg =
|
||||
typeof res.data?.data === 'string'
|
||||
? res.data.data
|
||||
: res.data?.message || t('支付失败');
|
||||
showError(errorMsg);
|
||||
}
|
||||
} catch (e) {
|
||||
showError(t('支付请求失败'));
|
||||
@@ -152,7 +156,11 @@ const SubscriptionPlansCard = ({
|
||||
showSuccess(t('已打开支付页面'));
|
||||
closeBuy();
|
||||
} else {
|
||||
showError(res.data?.data || res.data?.message || t('支付失败'));
|
||||
const errorMsg =
|
||||
typeof res.data?.data === 'string'
|
||||
? res.data.data
|
||||
: res.data?.message || t('支付失败');
|
||||
showError(errorMsg);
|
||||
}
|
||||
} catch (e) {
|
||||
showError(t('支付请求失败'));
|
||||
@@ -177,7 +185,11 @@ const SubscriptionPlansCard = ({
|
||||
showSuccess(t('已发起支付'));
|
||||
closeBuy();
|
||||
} else {
|
||||
showError(res.data?.data || res.data?.message || t('支付失败'));
|
||||
const errorMsg =
|
||||
typeof res.data?.data === 'string'
|
||||
? res.data.data
|
||||
: res.data?.message || t('支付失败');
|
||||
showError(errorMsg);
|
||||
}
|
||||
} catch (e) {
|
||||
showError(t('支付请求失败'));
|
||||
@@ -269,9 +281,13 @@ const SubscriptionPlansCard = ({
|
||||
</div>
|
||||
</Card>
|
||||
{/* 套餐列表骨架屏 */}
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4'>
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-5 w-full'>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<Card key={i} className='!rounded-xl' bodyStyle={{ padding: 16 }}>
|
||||
<Card
|
||||
key={i}
|
||||
className='!rounded-xl w-full h-full'
|
||||
bodyStyle={{ padding: 16 }}
|
||||
>
|
||||
<Skeleton.Title
|
||||
active
|
||||
style={{ width: '60%', height: 24, marginBottom: 8 }}
|
||||
@@ -435,7 +451,7 @@ const SubscriptionPlansCard = ({
|
||||
|
||||
{/* 可购买套餐 - 标准定价卡片 */}
|
||||
{plans.length > 0 ? (
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4'>
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-5 w-full'>
|
||||
{plans.map((p, index) => {
|
||||
const plan = p?.plan;
|
||||
const totalAmount = Number(plan?.total_amount || 0);
|
||||
@@ -477,15 +493,15 @@ const SubscriptionPlansCard = ({
|
||||
return (
|
||||
<Card
|
||||
key={plan?.id}
|
||||
className={`!rounded-xl transition-all hover:shadow-lg ${
|
||||
className={`!rounded-xl transition-all hover:shadow-lg w-full h-full ${
|
||||
isPopular ? 'ring-2 ring-purple-500' : ''
|
||||
}`}
|
||||
bodyStyle={{ padding: 0 }}
|
||||
>
|
||||
<div className='p-4'>
|
||||
<div className='p-4 h-full flex flex-col'>
|
||||
{/* 推荐标签 */}
|
||||
{isPopular && (
|
||||
<div className='text-center mb-2'>
|
||||
<div className='mb-2'>
|
||||
<Tag color='purple' shape='circle' size='small'>
|
||||
<Sparkles size={10} className='mr-1' />
|
||||
{t('推荐')}
|
||||
@@ -493,7 +509,7 @@ const SubscriptionPlansCard = ({
|
||||
</div>
|
||||
)}
|
||||
{/* 套餐名称 */}
|
||||
<div className='text-center mb-3'>
|
||||
<div className='mb-3'>
|
||||
<Typography.Title
|
||||
heading={5}
|
||||
ellipsis={{ rows: 1, showTooltip: true }}
|
||||
@@ -514,8 +530,8 @@ const SubscriptionPlansCard = ({
|
||||
</div>
|
||||
|
||||
{/* 价格区域 */}
|
||||
<div className='text-center py-2'>
|
||||
<div className='flex items-baseline justify-center'>
|
||||
<div className='py-2'>
|
||||
<div className='flex items-baseline justify-start'>
|
||||
<span className='text-xl font-bold text-purple-600'>
|
||||
{symbol}
|
||||
</span>
|
||||
@@ -526,7 +542,7 @@ const SubscriptionPlansCard = ({
|
||||
</div>
|
||||
|
||||
{/* 套餐权益描述 */}
|
||||
<div className='flex flex-col items-center gap-1 pb-2'>
|
||||
<div className='flex flex-col items-start gap-1 pb-2'>
|
||||
{planBenefits.map((item) => {
|
||||
const content = (
|
||||
<div className='flex items-center gap-2 text-xs text-gray-500'>
|
||||
@@ -538,7 +554,7 @@ const SubscriptionPlansCard = ({
|
||||
return (
|
||||
<div
|
||||
key={item.label}
|
||||
className='w-full flex justify-center'
|
||||
className='w-full flex justify-start'
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
@@ -546,7 +562,7 @@ const SubscriptionPlansCard = ({
|
||||
}
|
||||
return (
|
||||
<Tooltip key={item.label} content={item.tooltip}>
|
||||
<div className='w-full flex justify-center'>
|
||||
<div className='w-full flex justify-start'>
|
||||
{content}
|
||||
</div>
|
||||
</Tooltip>
|
||||
@@ -554,36 +570,38 @@ const SubscriptionPlansCard = ({
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Divider margin={12} />
|
||||
<div className='mt-auto'>
|
||||
<Divider margin={12} />
|
||||
|
||||
{/* 购买按钮 */}
|
||||
{(() => {
|
||||
const count = getPlanPurchaseCount(p?.plan?.id);
|
||||
const reached = limit > 0 && count >= limit;
|
||||
const tip = reached
|
||||
? t('已达到购买上限') + ` (${count}/${limit})`
|
||||
: '';
|
||||
const buttonEl = (
|
||||
<Button
|
||||
theme='outline'
|
||||
type='tertiary'
|
||||
block
|
||||
disabled={reached}
|
||||
onClick={() => {
|
||||
if (!reached) openBuy(p);
|
||||
}}
|
||||
>
|
||||
{reached ? t('已达上限') : t('立即订阅')}
|
||||
</Button>
|
||||
);
|
||||
return reached ? (
|
||||
<Tooltip content={tip} position='top'>
|
||||
{buttonEl}
|
||||
</Tooltip>
|
||||
) : (
|
||||
buttonEl
|
||||
);
|
||||
})()}
|
||||
{/* 购买按钮 */}
|
||||
{(() => {
|
||||
const count = getPlanPurchaseCount(p?.plan?.id);
|
||||
const reached = limit > 0 && count >= limit;
|
||||
const tip = reached
|
||||
? t('已达到购买上限') + ` (${count}/${limit})`
|
||||
: '';
|
||||
const buttonEl = (
|
||||
<Button
|
||||
theme='outline'
|
||||
type='tertiary'
|
||||
block
|
||||
disabled={reached}
|
||||
onClick={() => {
|
||||
if (!reached) openBuy(p);
|
||||
}}
|
||||
>
|
||||
{reached ? t('已达上限') : t('立即订阅')}
|
||||
</Button>
|
||||
);
|
||||
return reached ? (
|
||||
<Tooltip content={tip} position='top'>
|
||||
{buttonEl}
|
||||
</Tooltip>
|
||||
) : (
|
||||
buttonEl
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -249,7 +249,9 @@ const TopUp = () => {
|
||||
document.body.removeChild(form);
|
||||
}
|
||||
} else {
|
||||
showError(data);
|
||||
const errorMsg =
|
||||
typeof data === 'string' ? data : message || t('支付失败');
|
||||
showError(errorMsg);
|
||||
}
|
||||
} else {
|
||||
showError(res);
|
||||
@@ -293,7 +295,9 @@ const TopUp = () => {
|
||||
if (message === 'success') {
|
||||
processCreemCallback(data);
|
||||
} else {
|
||||
showError(data);
|
||||
const errorMsg =
|
||||
typeof data === 'string' ? data : message || t('支付失败');
|
||||
showError(errorMsg);
|
||||
}
|
||||
} else {
|
||||
showError(res);
|
||||
|
||||
@@ -170,21 +170,21 @@ export const getModelCategories = (() => {
|
||||
gemini: {
|
||||
label: 'Gemini',
|
||||
icon: <Gemini.Color />,
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('gemini') ||
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('gemini') ||
|
||||
model.model_name.toLowerCase().includes('gemma') ||
|
||||
model.model_name.toLowerCase().includes('learnlm') ||
|
||||
model.model_name.toLowerCase().includes('learnlm') ||
|
||||
model.model_name.toLowerCase().startsWith('embedding-') ||
|
||||
model.model_name.toLowerCase().includes('text-embedding-004') ||
|
||||
model.model_name.toLowerCase().includes('imagen-4') ||
|
||||
model.model_name.toLowerCase().includes('veo-') ||
|
||||
model.model_name.toLowerCase().includes('aqa') ,
|
||||
model.model_name.toLowerCase().includes('imagen-4') ||
|
||||
model.model_name.toLowerCase().includes('veo-') ||
|
||||
model.model_name.toLowerCase().includes('aqa'),
|
||||
},
|
||||
moonshot: {
|
||||
label: 'Moonshot',
|
||||
icon: <Moonshot />,
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('moonshot') ||
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('moonshot') ||
|
||||
model.model_name.toLowerCase().includes('kimi'),
|
||||
},
|
||||
zhipu: {
|
||||
@@ -192,8 +192,8 @@ export const getModelCategories = (() => {
|
||||
icon: <Zhipu.Color />,
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('chatglm') ||
|
||||
model.model_name.toLowerCase().includes('glm-') ||
|
||||
model.model_name.toLowerCase().includes('cogview') ||
|
||||
model.model_name.toLowerCase().includes('glm-') ||
|
||||
model.model_name.toLowerCase().includes('cogview') ||
|
||||
model.model_name.toLowerCase().includes('cogvideo'),
|
||||
},
|
||||
qwen: {
|
||||
@@ -209,8 +209,8 @@ export const getModelCategories = (() => {
|
||||
minimax: {
|
||||
label: 'MiniMax',
|
||||
icon: <Minimax.Color />,
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('abab') ||
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('abab') ||
|
||||
model.model_name.toLowerCase().includes('minimax'),
|
||||
},
|
||||
baidu: {
|
||||
@@ -236,7 +236,7 @@ export const getModelCategories = (() => {
|
||||
cohere: {
|
||||
label: 'Cohere',
|
||||
icon: <Cohere.Color />,
|
||||
filter: (model) =>
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('command') ||
|
||||
model.model_name.toLowerCase().includes('c4ai-') ||
|
||||
model.model_name.toLowerCase().includes('embed-'),
|
||||
@@ -259,7 +259,7 @@ export const getModelCategories = (() => {
|
||||
mistral: {
|
||||
label: 'Mistral AI',
|
||||
icon: <Mistral.Color />,
|
||||
filter: (model) =>
|
||||
filter: (model) =>
|
||||
model.model_name.toLowerCase().includes('mistral') ||
|
||||
model.model_name.toLowerCase().includes('codestral') ||
|
||||
model.model_name.toLowerCase().includes('pixtral') ||
|
||||
|
||||
Reference in New Issue
Block a user