From ab961e669abc229b7c3dbc03550a563f18e8f865 Mon Sep 17 00:00:00 2001 From: erio Date: Thu, 5 Mar 2026 23:08:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20lint=20errors?= =?UTF-8?q?=EF=BC=88hooks=20=E6=9D=A1=E4=BB=B6=E8=B0=83=E7=94=A8=E3=80=81?= =?UTF-8?q?=E6=9C=AA=E8=BD=AC=E4=B9=89=E5=BC=95=E5=8F=B7=E3=80=81effect=20?= =?UTF-8?q?=E5=86=85=20setState=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/pay/page.tsx | 147 ++++++++++++++++++--------------- src/components/PaymentForm.tsx | 79 +++++++++--------- 2 files changed, 122 insertions(+), 104 deletions(-) diff --git a/src/app/pay/page.tsx b/src/app/pay/page.tsx index 39132e5..1c8f73b 100644 --- a/src/app/pay/page.tsx +++ b/src/app/pay/page.tsx @@ -187,6 +187,20 @@ function PayContent() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [userId, token]); + useEffect(() => { + if (step !== 'result' || finalStatus !== 'COMPLETED') return; + // 立即在后台刷新余额,2.2s 显示结果页后再切回表单(届时余额已更新) + loadUserAndOrders(); + const timer = setTimeout(() => { + setStep('form'); + setOrderResult(null); + setFinalStatus(''); + setError(''); + }, 2200); + return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [step, finalStatus]); + if (!effectiveUserId || Number.isNaN(effectiveUserId) || effectiveUserId <= 0) { return (
@@ -290,20 +304,6 @@ function PayContent() { setError(''); }; - useEffect(() => { - if (step !== 'result' || finalStatus !== 'COMPLETED') return; - // 立即在后台刷新余额,2.2s 显示结果页后再切回表单(届时余额已更新) - loadUserAndOrders(); - const timer = setTimeout(() => { - setStep('form'); - setOrderResult(null); - setFinalStatus(''); - setError(''); - }, 2200); - return () => clearTimeout(timer); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [step, finalStatus]); - return ( - - - 我的订单 - - - ) : undefined} + actions={ + !isMobile ? ( + <> + + + 我的订单 + + + ) : undefined + } > - {error && ( -
- {error} -
- )} + {error &&
{error}
} {step === 'form' && isMobile && (
充值 @@ -368,10 +372,12 @@ function PayContent() { className={[ 'rounded-lg px-3 py-2 text-sm font-semibold transition-all duration-200', activeMobileTab === 'orders' - ? (isDark + ? isDark ? 'bg-indigo-500/30 text-indigo-100 ring-1 ring-indigo-300/35 shadow-sm' - : 'bg-white text-slate-900 ring-1 ring-slate-300 shadow-md shadow-slate-300/50') - : (isDark ? 'text-slate-400 hover:text-slate-200' : 'text-slate-500 hover:text-slate-700'), + : 'bg-white text-slate-900 ring-1 ring-slate-300 shadow-md shadow-slate-300/50' + : isDark + ? 'text-slate-400 hover:text-slate-200' + : 'text-slate-500 hover:text-slate-700', ].join(' ')} > 我的订单 @@ -382,9 +388,7 @@ function PayContent() { {step === 'form' && config.enabledPaymentTypes.length === 0 && (
- - 加载中... - + 加载中...
)} @@ -432,31 +436,46 @@ function PayContent() { />
-
+
支付说明
  • 订单完成后会自动到账
  • -
  • 如需历史记录请查看"我的订单"
  • - {config.maxDailyAmount > 0 && ( -
  • 每日最大充值 ¥{config.maxDailyAmount.toFixed(2)}
  • +
  • 如需历史记录请查看「我的订单」
  • + {config.maxDailyAmount > 0 &&
  • 每日最大充值 ¥{config.maxDailyAmount.toFixed(2)}
  • } + {!hasToken && ( +
  • 当前链接无 token,订单查询受限
  • )} - {!hasToken &&
  • 当前链接无 token,订单查询受限
  • }
{hasHelpContent && ( -
+
Support
{helpImageUrl && ( help setHelpImageOpen(true)} - className='mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2' + className="mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2" /> )} {helpText && ( -
+
{helpText.split('\\n').map((line, i) => (

{line}

))} @@ -490,9 +509,7 @@ function PayContent() { /> )} - {step === 'result' && ( - - )} + {step === 'result' && } {helpImageOpen && helpImageUrl && (
help e.stopPropagation()} />
diff --git a/src/components/PaymentForm.tsx b/src/components/PaymentForm.tsx index a9c0942..03915d5 100644 --- a/src/components/PaymentForm.tsx +++ b/src/components/PaymentForm.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import { PAYMENT_TYPE_META } from '@/lib/pay-utils'; export interface MethodLimitInfo { @@ -49,11 +49,9 @@ export default function PaymentForm({ const [customAmount, setCustomAmount] = useState(''); // Reset paymentType when enabledPaymentTypes changes (e.g. after config loads) - useEffect(() => { - if (!enabledPaymentTypes.includes(paymentType)) { - setPaymentType(enabledPaymentTypes[0] || 'stripe'); - } - }, [enabledPaymentTypes, paymentType]); + const effectivePaymentType = enabledPaymentTypes.includes(paymentType) + ? paymentType + : enabledPaymentTypes[0] || 'stripe'; const handleQuickAmount = (val: number) => { setAmount(val); @@ -81,22 +79,23 @@ export default function PaymentForm({ }; const selectedAmount = amount || 0; - const isMethodAvailable = !methodLimits || (methodLimits[paymentType]?.available !== false); - const methodSingleMax = methodLimits?.[paymentType]?.singleMax; - const effectiveMax = (methodSingleMax !== undefined && methodSingleMax > 0) ? methodSingleMax : maxAmount; - const feeRate = methodLimits?.[paymentType]?.feeRate ?? 0; - const feeAmount = feeRate > 0 && selectedAmount > 0 - ? Math.ceil(selectedAmount * feeRate / 100 * 100) / 100 - : 0; - const payAmount = feeRate > 0 && selectedAmount > 0 - ? Math.round((selectedAmount + feeAmount) * 100) / 100 - : selectedAmount; - const isValid = selectedAmount >= minAmount && selectedAmount <= effectiveMax && hasValidCentPrecision(selectedAmount) && isMethodAvailable; + const isMethodAvailable = !methodLimits || methodLimits[effectivePaymentType]?.available !== false; + const methodSingleMax = methodLimits?.[effectivePaymentType]?.singleMax; + const effectiveMax = methodSingleMax !== undefined && methodSingleMax > 0 ? methodSingleMax : maxAmount; + const feeRate = methodLimits?.[effectivePaymentType]?.feeRate ?? 0; + const feeAmount = feeRate > 0 && selectedAmount > 0 ? Math.ceil(((selectedAmount * feeRate) / 100) * 100) / 100 : 0; + const payAmount = + feeRate > 0 && selectedAmount > 0 ? Math.round((selectedAmount + feeAmount) * 100) / 100 : selectedAmount; + const isValid = + selectedAmount >= minAmount && + selectedAmount <= effectiveMax && + hasValidCentPrecision(selectedAmount) && + isMethodAvailable; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!isValid || loading) return; - await onSubmit(selectedAmount, paymentType); + await onSubmit(selectedAmount, effectivePaymentType); }; const renderPaymentIcon = (type: string) => { @@ -215,19 +214,17 @@ export default function PaymentForm({
- {customAmount !== '' && !isValid && (() => { - const num = parseFloat(customAmount); - let msg = '金额需在范围内,且最多支持 2 位小数(精确到分)'; - if (!isNaN(num)) { - if (num < minAmount) msg = `单笔最低充值 ¥${minAmount}`; - else if (num > effectiveMax) msg = `单笔最高充值 ¥${effectiveMax}`; - } - return ( -
- {msg} -
- ); - })()} + {customAmount !== '' && + !isValid && + (() => { + const num = parseFloat(customAmount); + let msg = '金额需在范围内,且最多支持 2 位小数(精确到分)'; + if (!isNaN(num)) { + if (num < minAmount) msg = `单笔最低充值 ¥${minAmount}`; + else if (num > effectiveMax) msg = `单笔最高充值 ¥${effectiveMax}`; + } + return
{msg}
; + })()} {/* Payment Type — only show when multiple types available */} {enabledPaymentTypes.length > 1 && ( @@ -238,7 +235,7 @@ export default function PaymentForm({
{enabledPaymentTypes.map((type) => { const meta = PAYMENT_TYPE_META[type]; - const isSelected = paymentType === type; + const isSelected = effectivePaymentType === type; const limitInfo = methodLimits?.[type]; const isUnavailable = limitInfo !== undefined && !limitInfo.available; @@ -284,7 +281,7 @@ export default function PaymentForm({ {/* 当前选中渠道额度不足时的提示 */} {(() => { - const limitInfo = methodLimits?.[paymentType]; + const limitInfo = methodLimits?.[effectivePaymentType]; if (!limitInfo || limitInfo.available) return null; return (

@@ -311,10 +308,12 @@ export default function PaymentForm({ 手续费({feeRate}%) ¥{feeAmount.toFixed(2)}

-
+
实付金额 ¥{payAmount.toFixed(2)}
@@ -327,7 +326,7 @@ export default function PaymentForm({ disabled={!isValid || loading} className={`w-full rounded-lg py-3 text-center font-medium text-white transition-colors ${ isValid && !loading - ? paymentType === 'stripe' + ? effectivePaymentType === 'stripe' ? 'bg-[#635bff] hover:bg-[#5851db] active:bg-[#4b44c7]' : 'bg-blue-600 hover:bg-blue-700 active:bg-blue-800' : dark @@ -335,7 +334,9 @@ export default function PaymentForm({ : 'cursor-not-allowed bg-gray-300' }`} > - {loading ? '处理中...' : `立即充值 ¥${(feeRate > 0 && selectedAmount > 0 ? payAmount : selectedAmount || 0).toFixed(2)}`} + {loading + ? '处理中...' + : `立即充值 ¥${(feeRate > 0 && selectedAmount > 0 ? payAmount : selectedAmount || 0).toFixed(2)}`} );