import React, { useEffect, useState, useContext } from 'react'; import { API, showError, showInfo, showSuccess, renderQuota, renderQuotaWithAmount, copy, getQuotaPerUnit, } from '../../helpers'; import { Avatar, Typography, Card, Button, Modal, Toast, Input, InputNumber, Banner, Skeleton, Divider, } from '@douyinfe/semi-ui'; import { SiAlipay, SiWechat } from 'react-icons/si'; import { useTranslation } from 'react-i18next'; import { UserContext } from '../../context/User'; import { StatusContext } from '../../context/Status/index.js'; import { useTheme } from '../../context/Theme'; import { CreditCard, Gift, Link as LinkIcon, Copy, Users, User, Coins, } from 'lucide-react'; const { Text, Title } = Typography; const TopUp = () => { const { t } = useTranslation(); const [userState, userDispatch] = useContext(UserContext); const [statusState] = useContext(StatusContext); const theme = useTheme(); const [redemptionCode, setRedemptionCode] = useState(''); const [topUpCode, setTopUpCode] = useState(''); const [amount, setAmount] = useState(0.0); const [minTopUp, setMinTopUp] = useState(statusState?.status?.min_topup || 1); const [topUpCount, setTopUpCount] = useState( statusState?.status?.min_topup || 1, ); const [topUpLink, setTopUpLink] = useState( statusState?.status?.top_up_link || '', ); const [enableOnlineTopUp, setEnableOnlineTopUp] = useState( statusState?.status?.enable_online_topup || false, ); const [priceRatio, setPriceRatio] = useState(statusState?.status?.price || 1); const [stripeAmount, setStripeAmount] = useState(0.0); const [stripeMinTopUp, setStripeMinTopUp] = useState(statusState?.status?.stripe_min_topup || 1); const [stripeTopUpCount, setStripeTopUpCount] = useState(statusState?.status?.stripe_min_topup || 1); const [enableStripeTopUp, setEnableStripeTopUp] = useState(statusState?.status?.enable_stripe_topup || false); const [stripeOpen, setStripeOpen] = useState(false); const [userQuota, setUserQuota] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); const [open, setOpen] = useState(false); const [payWay, setPayWay] = useState(''); const [userDataLoading, setUserDataLoading] = useState(true); const [amountLoading, setAmountLoading] = useState(false); const [paymentLoading, setPaymentLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false); const [payMethods, setPayMethods] = useState([]); // 邀请相关状态 const [affLink, setAffLink] = useState(''); const [openTransfer, setOpenTransfer] = useState(false); const [transferAmount, setTransferAmount] = useState(0); // 预设充值额度选项 const [presetAmounts, setPresetAmounts] = useState([ { value: 5 }, { value: 10 }, { value: 30 }, { value: 50 }, { value: 100 }, { value: 300 }, { value: 500 }, { value: 1000 }, ]); const [selectedPreset, setSelectedPreset] = useState(null); const getUsername = () => { if (userState.user) { return userState.user.username; } else { return 'null'; } }; const getUserRole = () => { if (!userState.user) return t('普通用户'); switch (userState.user.role) { case 100: return t('超级管理员'); case 10: return t('管理员'); case 0: default: return t('普通用户'); } }; const topUp = async () => { if (redemptionCode === '') { showInfo(t('请输入兑换码!')); return; } setIsSubmitting(true); try { const res = await API.post('/api/user/topup', { key: redemptionCode, }); const { success, message, data } = res.data; if (success) { showSuccess(t('兑换成功!')); Modal.success({ title: t('兑换成功!'), content: t('成功兑换额度:') + renderQuota(data), centered: true, }); setUserQuota((quota) => { return quota + data; }); if (userState.user) { const updatedUser = { ...userState.user, quota: userState.user.quota + data, }; userDispatch({ type: 'login', payload: updatedUser }); } setRedemptionCode(''); } else { showError(message); } } catch (err) { showError(t('请求失败')); } finally { setIsSubmitting(false); } }; const openTopUpLink = () => { if (!topUpLink) { showError(t('超级管理员未设置充值链接!')); return; } window.open(topUpLink, '_blank'); }; const preTopUp = async (payment) => { if (!enableOnlineTopUp) { showError(t('管理员未开启在线充值!')); return; } setPayWay(payment); setPaymentLoading(true); try { await getAmount(); if (topUpCount < minTopUp) { showError(t('充值数量不能小于') + minTopUp); return; } setOpen(true); } catch (error) { showError(t('获取金额失败')); } finally { setPaymentLoading(false); } }; const onlineTopUp = async () => { if (amount === 0) { await getAmount(); } if (topUpCount < minTopUp) { showError('充值数量不能小于' + minTopUp); return; } setConfirmLoading(true); try { const res = await API.post('/api/user/pay', { amount: parseInt(topUpCount), top_up_code: topUpCode, payment_method: payWay, }); if (res !== undefined) { const { message, data } = res.data; if (message === 'success') { let params = data; let url = res.data.url; let form = document.createElement('form'); form.action = url; form.method = 'POST'; let isSafari = navigator.userAgent.indexOf('Safari') > -1 && navigator.userAgent.indexOf('Chrome') < 1; if (!isSafari) { form.target = '_blank'; } for (let key in params) { let input = document.createElement('input'); input.type = 'hidden'; input.name = key; input.value = params[key]; form.appendChild(input); } document.body.appendChild(form); form.submit(); document.body.removeChild(form); } else { showError(data); } } else { showError(res); } } catch (err) { console.log(err); showError(t('支付请求失败')); } finally { setOpen(false); setConfirmLoading(false); } }; const stripePreTopUp = async () => { if (!enableStripeTopUp) { showError(t('管理员未开启在线充值!')); return; } setPayWay('stripe'); setPaymentLoading(true); try { await getStripeAmount(); if (stripeTopUpCount < stripeMinTopUp) { showError(t('充值数量不能小于') + stripeMinTopUp); return; } setStripeOpen(true); } catch (error) { showError(t('获取金额失败')); } finally { setPaymentLoading(false); } }; const onlineStripeTopUp = async () => { if (stripeAmount === 0) { await getStripeAmount(); } if (stripeTopUpCount < stripeMinTopUp) { showError(t('充值数量不能小于') + stripeMinTopUp); return; } setConfirmLoading(true); try { const res = await API.post('/api/user/stripe/pay', { amount: parseInt(stripeTopUpCount), payment_method: 'stripe', }); if (res !== undefined) { const { message, data } = res.data; if (message === 'success') { processStripeCallback(data); } else { showError(data); } } else { showError(res); } } catch (err) { console.log(err); showError(t('支付请求失败')); } finally { setStripeOpen(false); setConfirmLoading(false); } } const processStripeCallback = (data) => { window.open(data.pay_link, '_blank'); }; const getUserQuota = async () => { setUserDataLoading(true); let res = await API.get(`/api/user/self`); const { success, message, data } = res.data; if (success) { setUserQuota(data.quota); userDispatch({ type: 'login', payload: data }); } else { showError(message); } setUserDataLoading(false); }; // 获取邀请链接 const getAffLink = async () => { const res = await API.get('/api/user/aff'); const { success, message, data } = res.data; if (success) { let link = `${window.location.origin}/register?aff=${data}`; setAffLink(link); } else { showError(message); } }; // 划转邀请额度 const transfer = async () => { if (transferAmount < getQuotaPerUnit()) { showError(t('划转金额最低为') + ' ' + renderQuota(getQuotaPerUnit())); return; } const res = await API.post(`/api/user/aff_transfer`, { quota: transferAmount, }); const { success, message } = res.data; if (success) { showSuccess(message); setOpenTransfer(false); getUserQuota().then(); } else { showError(message); } }; // 复制邀请链接 const handleAffLinkClick = async () => { await copy(affLink); showSuccess(t('邀请链接已复制到剪切板')); }; useEffect(() => { if (userState?.user?.id) { setUserDataLoading(false); setUserQuota(userState.user.quota); } else { getUserQuota().then(); } getAffLink().then(); setTransferAmount(getQuotaPerUnit()); let payMethods = localStorage.getItem('pay_methods'); try { payMethods = JSON.parse(payMethods); if (payMethods && payMethods.length > 0) { // 检查name和type是否为空 payMethods = payMethods.filter((method) => { return method.name && method.type; }); // 如果没有color,则设置默认颜色 payMethods = payMethods.map((method) => { if (!method.color) { if (method.type === 'zfb') { method.color = 'rgba(var(--semi-blue-5), 1)'; } else if (method.type === 'wx') { method.color = 'rgba(var(--semi-green-5), 1)'; } else { method.color = 'rgba(var(--semi-primary-5), 1)'; } } return method; }); setPayMethods(payMethods); } } catch (e) { console.log(e); showError(t('支付方式配置错误, 请联系管理员')); } }, []); useEffect(() => { if (statusState?.status) { setMinTopUp(statusState.status.min_topup || 1); setTopUpCount(statusState.status.min_topup || 1); setTopUpLink(statusState.status.top_up_link || ''); setEnableOnlineTopUp(statusState.status.enable_online_topup || false); setPriceRatio(statusState.status.price || 1); setStripeMinTopUp(statusState.status.stripe_min_topup || 1); setStripeTopUpCount(statusState.status.stripe_min_topup || 1); setEnableStripeTopUp(statusState.status.enable_stripe_topup || false); } }, [statusState?.status]); const renderAmount = () => { return amount + ' ' + t('元'); }; const renderStripeAmount = () => { return stripeAmount + ' ' + t('元'); }; const getAmount = async (value) => { if (value === undefined) { value = topUpCount; } setAmountLoading(true); try { const res = await API.post('/api/user/amount', { amount: parseFloat(value), top_up_code: topUpCode, }); if (res !== undefined) { const { message, data } = res.data; if (message === 'success') { setAmount(parseFloat(data)); } else { setAmount(0); Toast.error({ content: '错误:' + data, id: 'getAmount' }); } } else { showError(res); } } catch (err) { console.log(err); } setAmountLoading(false); }; const getStripeAmount = async (value) => { if (value === undefined) { value = stripeTopUpCount } setAmountLoading(true); try { const res = await API.post('/api/user/stripe/amount', { amount: parseFloat(value), }); if (res !== undefined) { const { message, data } = res.data; // showInfo(message); if (message === 'success') { setStripeAmount(parseFloat(data)); } else { setStripeAmount(0); Toast.error({ content: '错误:' + data, id: 'getAmount' }); } } else { showError(res); } } catch (err) { console.log(err); } finally { setAmountLoading(false); } } const handleCancel = () => { setOpen(false); }; const handleStripeCancel = () => { setStripeOpen(false); }; const handleTransferCancel = () => { setOpenTransfer(false); }; // 选择预设充值额度 const selectPresetAmount = (preset) => { setTopUpCount(preset.value); setSelectedPreset(preset.value); setAmount(preset.value * priceRatio); setStripeTopUpCount(preset.value); setStripeAmount(preset.value); }; // 格式化大数字显示 const formatLargeNumber = (num) => { return num.toString(); }; return (
{/* 划转模态框 */} {t('划转邀请额度')}
} visible={openTransfer} onOk={transfer} onCancel={handleTransferCancel} maskClosable={false} size='small' centered >
{t('可用邀请额度')}
{t('划转额度')} ({t('最低') + renderQuota(getQuotaPerUnit())}) setTransferAmount(value)} size='large' className='w-full' />
{/* 充值确认模态框 */} {t('充值确认')} } visible={open} onOk={onlineTopUp} onCancel={handleCancel} maskClosable={false} size='small' centered confirmLoading={confirmLoading} >
{t('充值数量')}: {renderQuotaWithAmount(topUpCount)}
{t('实付金额')}: {amountLoading ? ( ) : ( {renderAmount()} )}
{t('支付方式')}: {(() => { const payMethod = payMethods.find( (method) => method.type === payWay, ); if (payMethod) { return (
{payMethod.type === 'zfb' ? ( ) : payMethod.type === 'wx' ? ( ) : ( )} {payMethod.name}
); } else { // 默认充值方式 return payWay === 'zfb' ? (
{t('支付宝')}
) : (
{t('微信')}
); } })()}

{t('充值数量')}:{stripeTopUpCount}

{t('实付金额')}:{renderStripeAmount()}

{t('是否确认充值?')}

{/* 左侧充值区域 */}
{/* 在线充值卡片 */}
{t('在线充值')} {t('快速方便的充值方式')}
{userDataLoading ? ( ) : (
{getUsername()} ({getUserRole()}) {getUsername()}
)}
} >
{/* 账户余额信息 */}
{t('当前余额')} {userDataLoading ? ( ) : (
{renderQuota(userState?.user?.quota || userQuota)}
)}
{t('历史消耗')} {userDataLoading ? ( ) : (
{renderQuota(userState?.user?.used_quota || 0)}
)}
{enableOnlineTopUp && ( <> {/* 预设充值额度卡片网格 */}
{t('选择充值额度')}
{presetAmounts.map((preset, index) => ( selectPresetAmount(preset)} className={`cursor-pointer !rounded-2xl transition-all hover:shadow-md ${selectedPreset === preset.value ? 'border-blue-500' : 'border-gray-200 hover:border-gray-300' }`} bodyStyle={{ textAlign: 'center' }} >
{formatLargeNumber(preset.value)}
{t('实付')} ¥ {(preset.value * priceRatio).toFixed(2)}
))}
{/* 桌面端显示的自定义金额和支付按钮 */}
{t('或输入自定义金额')}
{t('充值数量')} {amountLoading ? ( ) : ( {t('实付金额:') + renderAmount()} )}
{ if (value && value >= 1) { setTopUpCount(value); setSelectedPreset(null); await getAmount(value); } }} onBlur={(e) => { const value = parseInt(e.target.value); if (!value || value < 1) { setTopUpCount(1); getAmount(1); } }} size='large' className='w-full' formatter={(value) => (value ? `${value}` : '')} parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0 } />
{t('选择支付方式')} {payMethods.length === 2 ? (
{payMethods.map((payMethod) => ( ))}
) : payMethods.length === 3 ? (
{payMethods.map((payMethod) => ( ))}
) : payMethods.length > 3 ? (
{payMethods.map((payMethod) => ( preTopUp(payMethod.type)} disabled={!enableOnlineTopUp} className={`cursor-pointer !rounded-xl p-0 transition-all hover:shadow-md ${paymentLoading && payWay === payMethod.type ? 'border-blue-400' : 'border-gray-200 hover:border-gray-300' }`} bodyStyle={{ padding: '10px', textAlign: 'center', opacity: !enableOnlineTopUp ? 0.5 : 1 }} > {paymentLoading && payWay === payMethod.type ? (
{t('处理中')}
) : ( <>
{payMethod.type === 'zfb' ? ( ) : payMethod.type === 'wx' ? ( ) : ( )}
{payMethod.name}
)}
))}
) : (
{payMethods.map((payMethod) => ( ))}
)}
)} {!enableOnlineTopUp && !enableStripeTopUp && ( )} {enableStripeTopUp && ( <> {/* 桌面端显示的自定义金额和支付按钮 */}
{t(!enableOnlineTopUp ? '或输入自定义金额' : 'Stripe')}
{t('充值数量')} {amountLoading ? ( ) : ( {t('实付金额:') + renderStripeAmount()} )}
{ if (value && value >= 1) { setStripeTopUpCount(value); setSelectedPreset(null); await getStripeAmount(value); } }} onBlur={(e) => { const value = parseInt(e.target.value); if (!value || value < 1) { setStripeTopUpCount(1); getStripeAmount(1); } }} size='large' className='w-full' formatter={(value) => (value ? `${value}` : '')} parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0 } />
{t('选择支付方式')}
)} {t('兑换码充值')}
{t('使用兑换码快速充值')}
setRedemptionCode(value)} size='large' />
{topUpLink && ( )}
{/* 右侧邀请信息卡片 */}
{t('邀请奖励')} {t('邀请好友获得额外奖励')}
} >
{t('待使用收益')}
{renderQuota(userState?.user?.aff_quota || 0)}
{t('总收益')}
{renderQuota(userState?.user?.aff_history_quota || 0)}
{t('邀请人数')}
{userState?.user?.aff_count || 0}
{t('邀请链接')} } > {t('复制')} } />
{t('邀请好友注册,好友充值后您可获得相应奖励')}
{t('通过划转功能将奖励额度转入到您的账户余额中')}
{t('邀请的好友越多,获得的奖励越多')}
{/* 移动端底部固定的自定义金额和支付区域 */} {enableOnlineTopUp && (
{t('充值数量')} {amountLoading ? ( ) : ( {t('实付金额:') + renderAmount()} )}
{ if (value && value >= 1) { setTopUpCount(value); setSelectedPreset(null); await getAmount(value); } }} onBlur={(e) => { const value = parseInt(e.target.value); if (!value || value < 1) { setTopUpCount(1); getAmount(1); } }} className='w-full' formatter={(value) => (value ? `${value}` : '')} parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0 } />
{payMethods.length === 2 ? (
{payMethods.map((payMethod) => ( ))}
) : (
{payMethods.map((payMethod) => ( preTopUp(payMethod.type)} disabled={!enableOnlineTopUp} className={`cursor-pointer !rounded-xl p-0 transition-all ${paymentLoading && payWay === payMethod.type ? 'border-blue-400' : 'border-gray-200' }`} bodyStyle={{ padding: '8px', textAlign: 'center', opacity: !enableOnlineTopUp ? 0.5 : 1 }} > {paymentLoading && payWay === payMethod.type ? (
) : ( <>
{payMethod.type === 'zfb' ? ( ) : payMethod.type === 'wx' ? ( ) : ( )}
{payMethod.name}
)}
))}
)}
)} ); }; export default TopUp;