fix: 暗色主题二轮修复 - 支付页面、订单状态、Stripe弹窗、Fallback组件

- PaymentForm: 快速金额选中暗色态、禁用按钮暗色态
- PaymentQRCode: Stripe错误框、成功图标、取消按钮暗色适配
- MobileOrderList: 底部文字对比度修复(slate-600→slate-400)
- OrderStatus: getStatusConfig增加isDark参数,所有状态色适配暗色
- result/page: getStatusConfig传isDark、链接按钮暗色、Fallback暗色背景
- stripe-popup/page: 金额/成功图标/链接/禁用按钮/Fallback暗色适配
- pay/page: 帮助图片容器、错误提示、Fallback暗色背景
- orders/page: 错误提示、Fallback暗色背景
- README: 补充lang参数说明
This commit is contained in:
erio
2026-03-14 03:27:24 +08:00
parent 3b5a3ba5df
commit 48e94c205a
9 changed files with 85 additions and 70 deletions

View File

@@ -308,6 +308,7 @@ Sub2API **v0.1.88** 及以上版本会自动拼接以下参数,无需手动添
| `user_id` | Sub2API 用户 ID | | `user_id` | Sub2API 用户 ID |
| `token` | 用户登录 Token有 token 才能查看订单历史) | | `token` | 用户登录 Token有 token 才能查看订单历史) |
| `theme` | `light`(默认)或 `dark` | | `theme` | `light`(默认)或 `dark` |
| `lang` | 界面语言,`zh`(默认)或 `en` |
| `ui_mode` | `standalone`(默认)或 `embedded`iframe 嵌入) | | `ui_mode` | `standalone`(默认)或 `embedded`iframe 嵌入) |
--- ---

View File

@@ -177,7 +177,7 @@ function OrdersContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}> <div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500"> <div className="text-center text-red-500">
<p className="text-lg font-medium">{text.missingAuth}</p> <p className="text-lg font-medium">{text.missingAuth}</p>
<p className="mt-2 text-sm text-gray-500">{text.visitOrders}</p> <p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>{text.visitOrders}</p>
</div> </div>
</div> </div>
); );
@@ -238,10 +238,11 @@ function OrdersContent() {
function OrdersPageFallback() { function OrdersPageFallback() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang')); const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div> <div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div> </div>
); );
} }

View File

@@ -110,7 +110,7 @@ function PayContent() {
{pickLocaleText(locale, '帮助', 'Support')} {pickLocaleText(locale, '帮助', 'Support')}
</div> </div>
{helpImageUrl && ( {helpImageUrl && (
<img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className="mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2" /> <img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className={`mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain p-2 ${isDark ? 'bg-slate-700/50' : 'bg-white/70'}`} />
)} )}
{helpText && ( {helpText && (
<div className={['mt-3 space-y-1 text-sm leading-6', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}> <div className={['mt-3 space-y-1 text-sm leading-6', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}>
@@ -301,7 +301,7 @@ function PayContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}> <div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500"> <div className="text-center text-red-500">
<p className="text-lg font-medium">{pickLocaleText(locale, '缺少认证信息', 'Missing authentication info')}</p> <p className="text-lg font-medium">{pickLocaleText(locale, '缺少认证信息', 'Missing authentication info')}</p>
<p className="mt-2 text-sm text-gray-500"> <p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>
{pickLocaleText(locale, '请从 Sub2API 平台正确访问充值页面', 'Please open the recharge page from the Sub2API platform')} {pickLocaleText(locale, '请从 Sub2API 平台正确访问充值页面', 'Please open the recharge page from the Sub2API platform')}
</p> </p>
</div> </div>
@@ -314,7 +314,7 @@ function PayContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}> <div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500"> <div className="text-center text-red-500">
<p className="text-lg font-medium">{pickLocaleText(locale, '用户不存在', 'User not found')}</p> <p className="text-lg font-medium">{pickLocaleText(locale, '用户不存在', 'User not found')}</p>
<p className="mt-2 text-sm text-gray-500"> <p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>
{pickLocaleText(locale, '请检查链接是否正确,或联系管理员', 'Please check whether the link is correct or contact the administrator')} {pickLocaleText(locale, '请检查链接是否正确,或联系管理员', 'Please check whether the link is correct or contact the administrator')}
</p> </p>
</div> </div>
@@ -852,7 +852,7 @@ function PayContent() {
{pickLocaleText(locale, '帮助', 'Support')} {pickLocaleText(locale, '帮助', 'Support')}
</div> </div>
{helpImageUrl && ( {helpImageUrl && (
<img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className="mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2" /> <img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className={`mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain p-2 ${isDark ? 'bg-slate-700/50' : 'bg-white/70'}`} />
)} )}
{helpText && ( {helpText && (
<div className={['mt-3 space-y-1 text-sm leading-6', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}> <div className={['mt-3 space-y-1 text-sm leading-6', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}>
@@ -935,9 +935,10 @@ function PayContent() {
function PayPageFallback() { function PayPageFallback() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang')); const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div> <div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div> </div>
); );
} }

View File

@@ -54,12 +54,12 @@ function closeCurrentWindow() {
}, 250); }, 250);
} }
function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale, hasAccessToken: boolean) { function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale, hasAccessToken: boolean, isDark = false) {
if (!order) { if (!order) {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Payment Error', label: 'Payment Error',
color: 'text-red-600', color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗', icon: '✗',
message: hasAccessToken message: hasAccessToken
? 'Unable to load the order status. Please try again later.' ? 'Unable to load the order status. Please try again later.'
@@ -67,7 +67,7 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
} }
: { : {
label: '支付异常', label: '支付异常',
color: 'text-red-600', color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗', icon: '✗',
message: hasAccessToken ? '未查询到订单状态,请稍后重试。' : '订单访问凭证缺失,请返回原充值页查看订单结果。', message: hasAccessToken ? '未查询到订单状态,请稍后重试。' : '订单访问凭证缺失,请返回原充值页查看订单结果。',
}; };
@@ -77,11 +77,11 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
return locale === 'en' return locale === 'en'
? { ? {
label: 'Recharge Successful', label: 'Recharge Successful',
color: 'text-green-600', color: isDark ? 'text-green-400' : 'text-green-600',
icon: '✓', icon: '✓',
message: 'Your balance has been credited successfully.', message: 'Your balance has been credited successfully.',
} }
: { label: '充值成功', color: 'text-green-600', icon: '✓', message: '余额已成功到账!' }; : { label: '充值成功', color: isDark ? 'text-green-400' : 'text-green-600', icon: '✓', message: '余额已成功到账!' };
} }
if (order.paymentSuccess) { if (order.paymentSuccess) {
@@ -89,25 +89,25 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
return locale === 'en' return locale === 'en'
? { ? {
label: 'Top-up Processing', label: 'Top-up Processing',
color: 'text-blue-600', color: isDark ? 'text-blue-400' : 'text-blue-600',
icon: '⟳', icon: '⟳',
message: 'Payment succeeded, and the balance top-up is being processed.', message: 'Payment succeeded, and the balance top-up is being processed.',
} }
: { label: '充值处理中', color: 'text-blue-600', icon: '⟳', message: '支付成功,余额正在充值中...' }; : { label: '充值处理中', color: isDark ? 'text-blue-400' : 'text-blue-600', icon: '⟳', message: '支付成功,余额正在充值中...' };
} }
if (order.rechargeStatus === 'failed') { if (order.rechargeStatus === 'failed') {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Payment Successful', label: 'Payment Successful',
color: 'text-amber-600', color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!', icon: '!',
message: message:
'Payment succeeded, but the balance top-up has not completed yet. Please check again later or contact the administrator.', 'Payment succeeded, but the balance top-up has not completed yet. Please check again later or contact the administrator.',
} }
: { : {
label: '支付成功', label: '支付成功',
color: 'text-amber-600', color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!', icon: '!',
message: '支付成功,但余额充值暂未完成,请稍后查看订单结果或联系管理员。', message: '支付成功,但余额充值暂未完成,请稍后查看订单结果或联系管理员。',
}; };
@@ -116,30 +116,30 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
if (order.status === 'PENDING') { if (order.status === 'PENDING') {
return locale === 'en' return locale === 'en'
? { label: 'Awaiting Payment', color: 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' } ? { label: 'Awaiting Payment', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' }
: { label: '等待支付', color: 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' }; : { label: '等待支付', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' };
} }
if (order.status === 'EXPIRED') { if (order.status === 'EXPIRED') {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Order Expired', label: 'Order Expired',
color: 'text-gray-500', color: isDark ? 'text-slate-400' : 'text-gray-500',
icon: '⏰', icon: '⏰',
message: 'This order has expired. Please create a new order.', message: 'This order has expired. Please create a new order.',
} }
: { label: '订单已超时', color: 'text-gray-500', icon: '⏰', message: '订单已超时,请重新充值。' }; : { label: '订单已超时', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '⏰', message: '订单已超时,请重新充值。' };
} }
if (order.status === 'CANCELLED') { if (order.status === 'CANCELLED') {
return locale === 'en' return locale === 'en'
? { label: 'Order Cancelled', color: 'text-gray-500', icon: '✗', message: 'This order has been cancelled.' } ? { label: 'Order Cancelled', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: 'This order has been cancelled.' }
: { label: '订单已取消', color: 'text-gray-500', icon: '✗', message: '订单已被取消。' }; : { label: '订单已取消', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: '订单已被取消。' };
} }
return locale === 'en' return locale === 'en'
? { label: 'Payment Error', color: 'text-red-600', icon: '✗', message: 'Please contact the administrator.' } ? { label: 'Payment Error', color: isDark ? 'text-red-400' : 'text-red-600', icon: '✗', message: 'Please contact the administrator.' }
: { label: '支付异常', color: 'text-red-600', icon: '✗', message: '请联系管理员处理。' }; : { label: '支付异常', color: isDark ? 'text-red-400' : 'text-red-600', icon: '✗', message: '请联系管理员处理。' };
} }
function ResultContent() { function ResultContent() {
@@ -233,7 +233,7 @@ function ResultContent() {
); );
} }
const display = getStatusConfig(orderState, locale, Boolean(accessToken)); const display = getStatusConfig(orderState, locale, Boolean(accessToken), isDark);
return ( return (
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}> <div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
@@ -254,14 +254,14 @@ function ResultContent() {
<button <button
type="button" type="button"
onClick={closeCurrentWindow} onClick={closeCurrentWindow}
className="text-sm text-blue-600 underline hover:text-blue-700" className={`text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
> >
{text.closeNow} {text.closeNow}
</button> </button>
</div> </div>
) )
) : ( ) : (
<button type="button" onClick={goBack} className="mt-4 text-sm text-blue-600 underline hover:text-blue-700"> <button type="button" onClick={goBack} className={`mt-4 text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}>
{text.back} {text.back}
</button> </button>
)} )}
@@ -277,10 +277,11 @@ function ResultContent() {
function ResultPageFallback() { function ResultPageFallback() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang')); const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return ( return (
<div className="flex min-h-screen items-center justify-center bg-slate-50"> <div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div> <div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div> </div>
); );
} }

View File

@@ -195,7 +195,7 @@ function StripePopupContent() {
className={`w-full max-w-md space-y-4 rounded-2xl border p-6 ${isDark ? 'border-slate-700 bg-slate-900' : 'border-slate-200 bg-white'} shadow-lg`} className={`w-full max-w-md space-y-4 rounded-2xl border p-6 ${isDark ? 'border-slate-700 bg-slate-900' : 'border-slate-200 bg-white'} shadow-lg`}
> >
<div className="text-center"> <div className="text-center">
<div className="text-3xl font-bold text-blue-600"> <div className={`text-3xl font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>
{'¥'} {'¥'}
{amount.toFixed(2)} {amount.toFixed(2)}
</div> </div>
@@ -213,7 +213,7 @@ function StripePopupContent() {
<button <button
type="button" type="button"
onClick={() => window.close()} onClick={() => window.close()}
className="w-full text-sm text-blue-600 underline hover:text-blue-700" className={`w-full text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
> >
{text.closeWindow} {text.closeWindow}
</button> </button>
@@ -235,7 +235,7 @@ function StripePopupContent() {
className={`w-full max-w-md space-y-4 rounded-2xl border p-6 ${isDark ? 'border-slate-700 bg-slate-900' : 'border-slate-200 bg-white'} shadow-lg`} className={`w-full max-w-md space-y-4 rounded-2xl border p-6 ${isDark ? 'border-slate-700 bg-slate-900' : 'border-slate-200 bg-white'} shadow-lg`}
> >
<div className="text-center"> <div className="text-center">
<div className="text-3xl font-bold text-blue-600"> <div className={`text-3xl font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>
{'¥'} {'¥'}
{amount.toFixed(2)} {amount.toFixed(2)}
</div> </div>
@@ -251,12 +251,12 @@ function StripePopupContent() {
</div> </div>
) : stripeSuccess ? ( ) : stripeSuccess ? (
<div className="py-6 text-center"> <div className="py-6 text-center">
<div className="text-5xl text-green-600">{'✓'}</div> <div className={`text-5xl ${isDark ? 'text-green-400' : 'text-green-600'}`}>{'✓'}</div>
<p className={`mt-3 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>{text.successClosing}</p> <p className={`mt-3 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>{text.successClosing}</p>
<button <button
type="button" type="button"
onClick={() => window.close()} onClick={() => window.close()}
className="mt-4 text-sm text-blue-600 underline hover:text-blue-700" className={`mt-4 text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
> >
{text.closeWindowManually} {text.closeWindowManually}
</button> </button>
@@ -280,7 +280,9 @@ function StripePopupContent() {
onClick={handleSubmit} onClick={handleSubmit}
className={[ className={[
'w-full rounded-lg py-3 font-medium text-white shadow-md transition-colors', 'w-full rounded-lg py-3 font-medium text-white shadow-md transition-colors',
stripeSubmitting ? 'bg-gray-400 cursor-not-allowed' : getPaymentMeta('stripe').buttonClass, stripeSubmitting
? isDark ? 'bg-slate-700 text-slate-400 cursor-not-allowed' : 'bg-gray-400 cursor-not-allowed'
: getPaymentMeta('stripe').buttonClass,
].join(' ')} ].join(' ')}
> >
{stripeSubmitting ? ( {stripeSubmitting ? (
@@ -302,10 +304,11 @@ function StripePopupContent() {
function StripePopupFallback() { function StripePopupFallback() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang')); const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div> <div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div> </div>
); );
} }

View File

@@ -134,7 +134,7 @@ export default function MobileOrderList({
{locale === 'en' ? 'Loading...' : '加载中...'} {locale === 'en' ? 'Loading...' : '加载中...'}
</span> </span>
) : ( ) : (
<span className={['text-xs', isDark ? 'text-slate-600' : 'text-slate-300'].join(' ')}> <span className={['text-xs', isDark ? 'text-slate-400' : 'text-slate-400'].join(' ')}>
{locale === 'en' ? 'Scroll up to load more' : '上滑加载更多'} {locale === 'en' ? 'Scroll up to load more' : '上滑加载更多'}
</span> </span>
)} )}
@@ -142,7 +142,7 @@ export default function MobileOrderList({
)} )}
{!hasMore && orders.length > 0 && ( {!hasMore && orders.length > 0 && (
<div className={['py-2 text-center text-xs', isDark ? 'text-slate-600' : 'text-slate-400'].join(' ')}> <div className={['py-2 text-center text-xs', isDark ? 'text-slate-400' : 'text-slate-400'].join(' ')}>
{locale === 'en' ? 'All orders loaded' : '已显示全部订单'} {locale === 'en' ? 'All orders loaded' : '已显示全部订单'}
</div> </div>
)} )}

View File

@@ -15,16 +15,16 @@ interface OrderStatusProps {
locale?: Locale; locale?: Locale;
} }
function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) { function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale, isDark = false) {
if (order.rechargeSuccess) { if (order.rechargeSuccess) {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Recharge Successful', label: 'Recharge Successful',
color: 'text-green-600', color: isDark ? 'text-green-400' : 'text-green-600',
icon: '✓', icon: '✓',
message: 'Your balance has been credited. Thank you for your payment.', message: 'Your balance has been credited. Thank you for your payment.',
} }
: { label: '充值成功', color: 'text-green-600', icon: '✓', message: '余额已到账,感谢您的充值!' }; : { label: '充值成功', color: isDark ? 'text-green-400' : 'text-green-600', icon: '✓', message: '余额已到账,感谢您的充值!' };
} }
if (order.paymentSuccess) { if (order.paymentSuccess) {
@@ -32,25 +32,25 @@ function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Recharging', label: 'Recharging',
color: 'text-blue-600', color: isDark ? 'text-blue-400' : 'text-blue-600',
icon: '⟳', icon: '⟳',
message: 'Payment received. Recharging your balance...', message: 'Payment received. Recharging your balance...',
} }
: { label: '充值中', color: 'text-blue-600', icon: '⟳', message: '支付成功,正在充值余额中,请稍候...' }; : { label: '充值中', color: isDark ? 'text-blue-400' : 'text-blue-600', icon: '⟳', message: '支付成功,正在充值余额中,请稍候...' };
} }
if (order.rechargeStatus === 'failed') { if (order.rechargeStatus === 'failed') {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Payment Successful', label: 'Payment Successful',
color: 'text-amber-600', color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!', icon: '!',
message: message:
'Payment completed, but the balance top-up has not finished yet. The system may retry automatically. Please check the order list later or contact the administrator if it remains unresolved.', 'Payment completed, but the balance top-up has not finished yet. The system may retry automatically. Please check the order list later or contact the administrator if it remains unresolved.',
} }
: { : {
label: '支付成功', label: '支付成功',
color: 'text-amber-600', color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!', icon: '!',
message: message:
'支付已完成,但余额充值暂未完成。系统可能会自动重试,请稍后在订单列表查看;如长时间未到账请联系管理员。', '支付已完成,但余额充值暂未完成。系统可能会自动重试,请稍后在订单列表查看;如长时间未到账请联系管理员。',
@@ -62,14 +62,14 @@ function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Payment Failed', label: 'Payment Failed',
color: 'text-red-600', color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗', icon: '✗',
message: message:
'Payment was not completed. Please try again. If funds were deducted but not credited, contact the administrator.', 'Payment was not completed. Please try again. If funds were deducted but not credited, contact the administrator.',
} }
: { : {
label: '支付失败', label: '支付失败',
color: 'text-red-600', color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗', icon: '✗',
message: '支付未完成,请重新发起支付;如已扣款未到账,请联系管理员处理。', message: '支付未完成,请重新发起支付;如已扣款未到账,请联系管理员处理。',
}; };
@@ -77,35 +77,35 @@ function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
if (order.status === 'PENDING') { if (order.status === 'PENDING') {
return locale === 'en' return locale === 'en'
? { label: 'Awaiting Payment', color: 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' } ? { label: 'Awaiting Payment', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' }
: { label: '等待支付', color: 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' }; : { label: '等待支付', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' };
} }
if (order.status === 'EXPIRED') { if (order.status === 'EXPIRED') {
return locale === 'en' return locale === 'en'
? { ? {
label: 'Order Expired', label: 'Order Expired',
color: 'text-gray-500', color: isDark ? 'text-slate-400' : 'text-gray-500',
icon: '⏰', icon: '⏰',
message: 'This order has expired. Please create a new one.', message: 'This order has expired. Please create a new one.',
} }
: { label: '订单超时', color: 'text-gray-500', icon: '⏰', message: '订单已超时,请重新创建订单。' }; : { label: '订单超时', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '⏰', message: '订单已超时,请重新创建订单。' };
} }
if (order.status === 'CANCELLED') { if (order.status === 'CANCELLED') {
return locale === 'en' return locale === 'en'
? { label: 'Cancelled', color: 'text-gray-500', icon: '✗', message: 'The order has been cancelled.' } ? { label: 'Cancelled', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: 'The order has been cancelled.' }
: { label: '已取消', color: 'text-gray-500', icon: '✗', message: '订单已取消。' }; : { label: '已取消', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: '订单已取消。' };
} }
return locale === 'en' return locale === 'en'
? { ? {
label: 'Payment Error', label: 'Payment Error',
color: 'text-red-600', color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗', icon: '✗',
message: 'Payment status is abnormal. Please contact the administrator.', message: 'Payment status is abnormal. Please contact the administrator.',
} }
: { label: '支付异常', color: 'text-red-600', icon: '✗', message: '支付状态异常,请联系管理员处理。' }; : { label: '支付异常', color: isDark ? 'text-red-400' : 'text-red-600', icon: '✗', message: '支付状态异常,请联系管理员处理。' };
} }
export default function OrderStatus({ export default function OrderStatus({
@@ -156,7 +156,7 @@ export default function OrderStatus({
}; };
}, [orderId, currentOrder.paymentSuccess, currentOrder.rechargeSuccess, statusAccessToken]); }, [orderId, currentOrder.paymentSuccess, currentOrder.rechargeSuccess, statusAccessToken]);
const config = getStatusConfig(currentOrder, locale); const config = getStatusConfig(currentOrder, locale, dark);
const doneLabel = locale === 'en' ? 'Done' : '完成'; const doneLabel = locale === 'en' ? 'Done' : '完成';
const backLabel = locale === 'en' ? 'Back to Recharge' : '返回充值'; const backLabel = locale === 'en' ? 'Back to Recharge' : '返回充值';

View File

@@ -195,7 +195,9 @@ export default function PaymentForm({
onClick={() => handleQuickAmount(val)} onClick={() => handleQuickAmount(val)}
className={`rounded-lg border-2 px-4 py-3 text-center font-medium transition-colors ${ className={`rounded-lg border-2 px-4 py-3 text-center font-medium transition-colors ${
amount === val amount === val
? 'border-blue-500 bg-blue-50 text-blue-700' ? dark
? 'border-blue-500 bg-blue-900/40 text-blue-300'
: 'border-blue-500 bg-blue-50 text-blue-700'
: dark : dark
? 'border-slate-700 bg-slate-900 text-slate-200 hover:border-slate-500' ? 'border-slate-700 bg-slate-900 text-slate-200 hover:border-slate-500'
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300' : 'border-gray-200 bg-white text-gray-700 hover:border-gray-300'
@@ -373,12 +375,12 @@ export default function PaymentForm({
<button <button
type="submit" type="submit"
disabled={!isValid || loading || pendingBlocked} disabled={!isValid || loading || pendingBlocked}
className={`w-full rounded-lg py-3 text-center font-medium text-white transition-colors ${ className={`w-full rounded-lg py-3 text-center font-medium transition-colors ${
isValid && !loading && !pendingBlocked isValid && !loading && !pendingBlocked
? getPaymentMeta(effectivePaymentType).buttonClass ? `text-white ${getPaymentMeta(effectivePaymentType).buttonClass}`
: dark : dark
? 'cursor-not-allowed bg-slate-700 text-slate-300' ? 'cursor-not-allowed bg-slate-700 text-slate-400'
: 'cursor-not-allowed bg-gray-300' : 'cursor-not-allowed bg-gray-300 text-gray-500'
}`} }`}
> >
{loading {loading

View File

@@ -391,14 +391,17 @@ export default function PaymentQRCode({
if (cancelBlocked) { if (cancelBlocked) {
return ( return (
<div className="flex flex-col items-center space-y-4 py-8"> <div className="flex flex-col items-center space-y-4 py-8">
<div className="text-6xl text-green-600">{'✓'}</div> <div className={dark ? 'text-6xl text-green-400' : 'text-6xl text-green-600'}>{'✓'}</div>
<h2 className="text-xl font-bold text-green-600">{t.paid}</h2> <h2 className={['text-xl font-bold', dark ? 'text-green-400' : 'text-green-600'].join(' ')}>{t.paid}</h2>
<p className={['text-center text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}> <p className={['text-center text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>
{t.paidCancelBlocked} {t.paidCancelBlocked}
</p> </p>
<button <button
onClick={onBack} onClick={onBack}
className="mt-4 w-full rounded-lg bg-blue-600 py-3 font-medium text-white hover:bg-blue-700" className={[
'mt-4 w-full rounded-lg py-3 font-medium text-white',
dark ? 'bg-blue-600/90 hover:bg-blue-600' : 'bg-blue-600 hover:bg-blue-700',
].join(' ')}
> >
{t.backToRecharge} {t.backToRecharge}
</button> </button>
@@ -409,7 +412,7 @@ export default function PaymentQRCode({
return ( return (
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
<div className="text-center"> <div className="text-center">
<div className="text-4xl font-bold text-blue-600"> <div className={['text-4xl font-bold', dark ? 'text-blue-400' : 'text-blue-600'].join(' ')}>
{'¥'} {'¥'}
{displayAmount.toFixed(2)} {displayAmount.toFixed(2)}
</div> </div>
@@ -465,13 +468,16 @@ export default function PaymentQRCode({
].join(' ')} ].join(' ')}
/> />
{stripeError && ( {stripeError && (
<div className="rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600"> <div className={[
'rounded-lg border p-3 text-sm',
dark ? 'border-red-700/50 bg-red-900/30 text-red-400' : 'border-red-200 bg-red-50 text-red-600',
].join(' ')}>
{stripeError} {stripeError}
</div> </div>
)} )}
{stripeSuccess ? ( {stripeSuccess ? (
<div className="text-center"> <div className="text-center">
<div className="text-4xl text-green-600">{'✓'}</div> <div className={dark ? 'text-4xl text-green-400' : 'text-4xl text-green-600'}>{'✓'}</div>
<p className={['mt-2 text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}> <p className={['mt-2 text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>
{t.successProcessing} {t.successProcessing}
</p> </p>