From f95687bc2d4af2e790f5f233cb618247a56176f3 Mon Sep 17 00:00:00 2001 From: abingyyds Date: Wed, 8 Apr 2026 14:02:28 +0800 Subject: [PATCH] feat: add dist-site commission withdraw --- src/i18n/locales/en.json | 16 ++++ src/i18n/locales/zh.json | 16 ++++ src/pages/Dashboard.jsx | 177 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 205 insertions(+), 4 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 47dc24c..0e8bc6c 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -298,6 +298,22 @@ "topup.transferPlaceholder": "Enter transfer amount (quota)", "topup.transfer": "Transfer", "topup.transferSuccess": "Transfer successful", + "topup.withdraw": "Withdraw", + "topup.withdrawTitle": "Request Withdrawal", + "topup.withdrawSubtitle": "Submit a payout request with your preferred payment method", + "topup.withdrawAvailable": "Withdrawable Balance", + "topup.withdrawAmount": "Withdrawal Amount", + "topup.withdrawAll": "Withdraw All", + "topup.withdrawMethod": "Payment Method", + "topup.withdrawMethodPlaceholder": "e.g. Alipay, WeChat, bank card", + "topup.withdrawRemark": "Account / Note", + "topup.withdrawRemarkPlaceholder": "Enter your payout account or extra notes", + "topup.submitWithdraw": "Submit Withdrawal", + "topup.withdrawSuccess": "Withdrawal request submitted", + "topup.invalidWithdrawAmount": "Please enter a valid withdrawal amount", + "topup.withdrawExceedsBalance": "Withdrawal amount cannot exceed your withdrawable balance", + "topup.enterWithdrawMethod": "Please enter a payment method", + "topup.currentCommissionRateLabel": "Commission Rate", "dashboard.topupDesc": "Recharge account balance", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 9fec394..843c175 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -298,6 +298,22 @@ "topup.transferPlaceholder": "输入划转额度 (quota)", "topup.transfer": "划转", "topup.transferSuccess": "划转成功", + "topup.withdraw": "提现", + "topup.withdrawTitle": "申请提现", + "topup.withdrawSubtitle": "填写收款方式后即可提交提现申请", + "topup.withdrawAvailable": "可提现余额", + "topup.withdrawAmount": "提现金额", + "topup.withdrawAll": "全部提现", + "topup.withdrawMethod": "收款方式", + "topup.withdrawMethodPlaceholder": "如:支付宝、微信、银行卡", + "topup.withdrawRemark": "收款账号 / 备注", + "topup.withdrawRemarkPlaceholder": "请输入收款账号或补充说明", + "topup.submitWithdraw": "提交提现", + "topup.withdrawSuccess": "提现申请已提交", + "topup.invalidWithdrawAmount": "请输入有效的提现金额", + "topup.withdrawExceedsBalance": "提现金额不能超过可提现余额", + "topup.enterWithdrawMethod": "请输入收款方式", + "topup.currentCommissionRateLabel": "当前返佣比例", "dashboard.topupDesc": "充值账户余额", diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index e6bf076..86564a0 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../context/AuthContext'; -import { getUserUsage, redeemCode, getAffCode, transferAffQuota, getAffEarnings, Q, quotaToDollar } from '../api'; +import { getUserUsage, redeemCode, getAffCode, transferAffQuota, getAffEarnings, requestAffWithdraw, Q } from '../api'; import { useCurrency, useSite } from '../context/SiteContext'; import CountUp from '../components/bits/CountUp'; import toast from 'react-hot-toast'; @@ -23,6 +23,11 @@ export default function Dashboard() { const [affEarningsLoading, setAffEarningsLoading] = useState(false); const [transferAmount, setTransferAmount] = useState(''); const [transferring, setTransferring] = useState(false); + const [showWithdrawModal, setShowWithdrawModal] = useState(false); + const [withdrawAmount, setWithdrawAmount] = useState(''); + const [withdrawMethod, setWithdrawMethod] = useState(''); + const [withdrawRemark, setWithdrawRemark] = useState(''); + const [withdrawing, setWithdrawing] = useState(false); const loadData = useCallback(async () => { try { @@ -92,10 +97,61 @@ export default function Dashboard() { setTransferring(false); }; + const resetWithdrawForm = () => { + setWithdrawAmount(''); + setWithdrawMethod(''); + setWithdrawRemark(''); + }; + + const handleOpenWithdraw = () => { + resetWithdrawForm(); + setShowWithdrawModal(true); + }; + + const handleCloseWithdraw = () => { + if (withdrawing) return; + resetWithdrawForm(); + setShowWithdrawModal(false); + }; + const quota = usage?.quota ?? user?.quota ?? 0; const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0; const requestCount = usage?.request_count ?? user?.request_count ?? 0; const balanceDollars = quota / Q * rate; + const availableAffAmount = ((user?.aff_quota || 0) / Q) * rate; + const currentCommissionRate = Number(user?.commission_rate || 0); + + const handleWithdraw = async () => { + const amount = Number.parseFloat(withdrawAmount); + if (!Number.isFinite(amount) || amount <= 0) { + toast.error(t('topup.invalidWithdrawAmount')); + return; + } + if (amount - availableAffAmount > 1e-8) { + toast.error(t('topup.withdrawExceedsBalance')); + return; + } + if (!withdrawMethod.trim()) { + toast.error(t('topup.enterWithdrawMethod')); + return; + } + + setWithdrawing(true); + try { + const res = await requestAffWithdraw({ + amount: amount / rate, + payment_method: withdrawMethod.trim(), + remark: withdrawRemark.trim(), + }); + if (res.data.success) { + toast.success(res.data.message || t('topup.withdrawSuccess')); + setShowWithdrawModal(false); + resetWithdrawForm(); + await Promise.all([loadData(), refreshUser()]); + } + } catch (err) { /* interceptor */ } + setWithdrawing(false); + }; return (
@@ -189,8 +245,16 @@ export default function Dashboard() { {/* Invitation / Affiliate */} {affLink && (
-

{t('topup.inviteTitle')}

-

{t('topup.inviteSubtitle')}

+
+
+

{t('topup.inviteTitle')}

+

{t('topup.inviteSubtitle')}

+
+
+ {t('topup.currentCommissionRateLabel')} + {(currentCommissionRate * 100).toFixed(1)}% +
+
{/* Stats */}
@@ -234,7 +298,16 @@ export default function Dashboard() { {/* Transfer to balance */} {(user?.aff_quota || 0) > 0 && (
- +
+ + +
)} + + {showWithdrawModal && ( +
+
e.stopPropagation()} + > +
+

{t('topup.withdrawTitle')}

+

{t('topup.withdrawSubtitle')}

+
+ +
+
+ + +
+ +
+ +
+
+ + {symbol} + + setWithdrawAmount(e.target.value)} + placeholder="0.00" + className="input pl-8" + /> +
+ +
+
+ +
+ + setWithdrawMethod(e.target.value)} + placeholder={t('topup.withdrawMethodPlaceholder')} + className="input" + /> +
+ +
+ +