From a4617097fb8da374d1ad2e63302d6c772ffba4eb Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Thu, 5 Feb 2026 01:34:04 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20fix:=20Improve=20subscription=20pay?= =?UTF-8?q?ment=20handling=20and=20card=20layout=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify Epay subscription response format with top-up flow, and harden frontend error handling to avoid object-to-string issues. Refine subscription plan cards layout to be wider, left-aligned, and visually consistent across breakpoints. --- controller/subscription_payment_epay.go | 2 +- .../topup/SubscriptionPlansCard.jsx | 106 ++++++++++-------- web/src/components/topup/index.jsx | 8 +- web/src/helpers/render.jsx | 28 ++--- 4 files changed, 83 insertions(+), 61 deletions(-) diff --git a/controller/subscription_payment_epay.go b/controller/subscription_payment_epay.go index c0a5162a5..528ab5d2d 100644 --- a/controller/subscription_payment_epay.go +++ b/controller/subscription_payment_epay.go @@ -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) { diff --git a/web/src/components/topup/SubscriptionPlansCard.jsx b/web/src/components/topup/SubscriptionPlansCard.jsx index a3a223ba4..e9d25e543 100644 --- a/web/src/components/topup/SubscriptionPlansCard.jsx +++ b/web/src/components/topup/SubscriptionPlansCard.jsx @@ -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 = ({ {/* 套餐列表骨架屏 */} -
+
{[1, 2, 3].map((i) => ( - + 0 ? ( -
+
{plans.map((p, index) => { const plan = p?.plan; const totalAmount = Number(plan?.total_amount || 0); @@ -477,15 +493,15 @@ const SubscriptionPlansCard = ({ return ( -
+
{/* 推荐标签 */} {isPopular && ( -
+
{t('推荐')} @@ -493,7 +509,7 @@ const SubscriptionPlansCard = ({
)} {/* 套餐名称 */} -
+
{/* 价格区域 */} -
-
+
+
{symbol} @@ -526,7 +542,7 @@ const SubscriptionPlansCard = ({
{/* 套餐权益描述 */} -
+
{planBenefits.map((item) => { const content = (
@@ -538,7 +554,7 @@ const SubscriptionPlansCard = ({ return (
{content}
@@ -546,7 +562,7 @@ const SubscriptionPlansCard = ({ } return ( -
+
{content}
@@ -554,36 +570,38 @@ const SubscriptionPlansCard = ({ })}
- +
+ - {/* 购买按钮 */} - {(() => { - const count = getPlanPurchaseCount(p?.plan?.id); - const reached = limit > 0 && count >= limit; - const tip = reached - ? t('已达到购买上限') + ` (${count}/${limit})` - : ''; - const buttonEl = ( - - ); - return reached ? ( - - {buttonEl} - - ) : ( - buttonEl - ); - })()} + {/* 购买按钮 */} + {(() => { + const count = getPlanPurchaseCount(p?.plan?.id); + const reached = limit > 0 && count >= limit; + const tip = reached + ? t('已达到购买上限') + ` (${count}/${limit})` + : ''; + const buttonEl = ( + + ); + return reached ? ( + + {buttonEl} + + ) : ( + buttonEl + ); + })()} +
); diff --git a/web/src/components/topup/index.jsx b/web/src/components/topup/index.jsx index 03e12a941..8f0b67a21 100644 --- a/web/src/components/topup/index.jsx +++ b/web/src/components/topup/index.jsx @@ -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); diff --git a/web/src/helpers/render.jsx b/web/src/helpers/render.jsx index d3a7350a9..5c58ff0e4 100644 --- a/web/src/helpers/render.jsx +++ b/web/src/helpers/render.jsx @@ -170,21 +170,21 @@ export const getModelCategories = (() => { gemini: { label: 'Gemini', icon: , - 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: , - 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: , 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: , - 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: , - 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: , - filter: (model) => + filter: (model) => model.model_name.toLowerCase().includes('mistral') || model.model_name.toLowerCase().includes('codestral') || model.model_name.toLowerCase().includes('pixtral') ||