diff --git a/src/api/pmset.ts b/src/api/pmset.ts index 3d6b83d..8b673de 100644 --- a/src/api/pmset.ts +++ b/src/api/pmset.ts @@ -177,8 +177,10 @@ export function amountToUsdcDisplay(raw: number | undefined): string { /** 提现配置(GET /pmset/getWithdrawConfig) */ export interface WithdrawConfig { - /** 手续费,最小精度整数(1U=1e6;系统参数配置口径为 1=1U) */ + /** 固定手续费,最小精度整数(1U=1e6;系统参数配置口径为 1=1U) */ fee: number + /** 比例手续费百分比(1=1%,可与固定手续费同时生效) */ + feePercent: number /** 最低提现金额,最小精度整数(1U=1e6;系统参数配置口径为 1=1U) */ minWithdrawAmount: number } @@ -191,7 +193,7 @@ export interface WithdrawConfigResponse extends ApiResponse { /** * GET /pmset/getWithdrawConfig - * 获取提现手续费与最低提现金额(系统参数 withdrawFee / minWithdrawAmount,配置 1=1U;缺失自动插入) + * 获取提现固定/比例手续费与最低提现金额(withdrawFee / withdrawFeePercent / minWithdrawAmount;缺失自动插入) */ export async function getWithdrawConfig( config?: { headers?: Record }, @@ -199,6 +201,14 @@ export async function getWithdrawConfig( return get('/pmset/getWithdrawConfig', undefined, config) } +/** 计算提现总手续费(展示金额):固定 + 金额×百分比 */ +export function calcWithdrawFeeDisplay(amount: number, fixedFee: number, feePercent: number): number { + const fixed = Number.isFinite(fixedFee) && fixedFee > 0 ? fixedFee : 0 + const percent = Number.isFinite(feePercent) && feePercent > 0 ? feePercent : 0 + const amt = Number.isFinite(amount) && amount > 0 ? amount : 0 + return Math.round((fixed + (amt * percent) / 100) * 1e6) / 1e6 +} + /** 充值配置(GET /pmset/getDepositConfig) */ export interface DepositConfig { /** 最小到账金额,最小精度整数(1U=1e6;系统参数配置口径 1=1U,默认 10) */ diff --git a/src/components/WithdrawDialog.vue b/src/components/WithdrawDialog.vue index 92fff85..725d576 100644 --- a/src/components/WithdrawDialog.vue +++ b/src/components/WithdrawDialog.vue @@ -40,8 +40,16 @@ ${{ minAmountDisplay }}
+ {{ t('withdraw.feeFixed') }} + ${{ feeFixedDisplay }} +
+
+ {{ t('withdraw.feePercent') }} + {{ feePercentDisplay }}% +
+
{{ t('withdraw.fee') }} - ${{ feeDisplay }} + ${{ totalFeeDisplay }}
{{ t('withdraw.receiveAmount') }} @@ -106,6 +114,7 @@ import { usdcToAmount, getWithdrawConfig, amountToUsdcDisplay, + calcWithdrawFeeDisplay, } from '@/api/pmset' import { WALLET_PRIMARY_NETWORK_ID, useWalletNetworkGuard } from '@/composables/useWalletNetworkPolicy' @@ -128,6 +137,7 @@ const connecting = ref(false) const submitting = ref(false) const errorMessage = ref('') const feeRaw = ref(0) +const feePercent = ref(0) const minWithdrawRaw = ref(0) const configLoading = ref(false) @@ -151,12 +161,21 @@ const { onWalletNetworkChange } = useWalletNetworkGuard( const balanceNum = computed(() => parseFloat(props.balance) || 0) const amountNum = computed(() => parseFloat(amount.value) || 0) -const feeDisplayNum = computed(() => feeRaw.value / 1_000_000) +const feeFixedDisplayNum = computed(() => feeRaw.value / 1_000_000) const minAmountDisplayNum = computed(() => minWithdrawRaw.value / 1_000_000) -const feeDisplay = computed(() => amountToUsdcDisplay(feeRaw.value)) +const feeFixedDisplay = computed(() => amountToUsdcDisplay(feeRaw.value)) +const feePercentDisplay = computed(() => { + const p = feePercent.value + if (!Number.isFinite(p) || p <= 0) return '0' + return Number.isInteger(p) ? String(p) : p.toFixed(4).replace(/\.?0+$/, '') +}) const minAmountDisplay = computed(() => amountToUsdcDisplay(minWithdrawRaw.value)) +const totalFeeDisplayNum = computed(() => + calcWithdrawFeeDisplay(amountNum.value, feeFixedDisplayNum.value, feePercent.value), +) +const totalFeeDisplay = computed(() => totalFeeDisplayNum.value.toFixed(2)) const receiveAmountDisplay = computed(() => { - const receive = Math.max(0, amountNum.value - feeDisplayNum.value) + const receive = Math.max(0, amountNum.value - totalFeeDisplayNum.value) return receive.toFixed(2) }) @@ -167,8 +186,8 @@ const amountError = computed(() => { if (minAmountDisplayNum.value > 0 && amountNum.value < minAmountDisplayNum.value) { return t('withdraw.belowMinAmount', { min: minAmountDisplay.value }) } - if (feeDisplayNum.value > 0 && amountNum.value <= feeDisplayNum.value) { - return t('withdraw.amountMustExceedFee', { fee: feeDisplay.value }) + if (totalFeeDisplayNum.value > 0 && amountNum.value <= totalFeeDisplayNum.value) { + return t('withdraw.amountMustExceedFee', { fee: totalFeeDisplay.value }) } return '' }) @@ -219,11 +238,13 @@ async function loadWithdrawConfig() { const res = await getWithdrawConfig() if (res.code === 0 || res.code === 200) { feeRaw.value = Number(res.data?.fee ?? 0) || 0 + feePercent.value = Number(res.data?.feePercent ?? 0) || 0 minWithdrawRaw.value = Number(res.data?.minWithdrawAmount ?? 0) || 0 } } catch (e) { console.error('[loadWithdrawConfig]', e) feeRaw.value = 0 + feePercent.value = 0 minWithdrawRaw.value = 0 } finally { configLoading.value = false diff --git a/src/locales/en.json b/src/locales/en.json index 480efa3..48436bf 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -522,7 +522,9 @@ "loginWalletLabel": "Your login wallet", "connectingWallet": "Connecting wallet…", "walletMismatch": "Switch your browser wallet to the same account you used to sign in.", - "fee": "Fee", + "fee": "Total fee", + "feeFixed": "Fixed fee", + "feePercent": "Fee rate", "minAmount": "Minimum withdrawal", "receiveAmount": "You will receive", "belowMinAmount": "Amount must be at least {min} USDC", diff --git a/src/locales/ja.json b/src/locales/ja.json index b109c7d..5e8635e 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -522,7 +522,9 @@ "loginWalletLabel": "ログイン中のウォレット", "connectingWallet": "ウォレットに接続しています…", "walletMismatch": "ログイン時に使用したアカウントにブラウザのウォレットを切り替えてください。", - "fee": "手数料", + "fee": "手数料合計", + "feeFixed": "固定手数料", + "feePercent": "手数料率", "minAmount": "最低出金額", "receiveAmount": "受取金額", "belowMinAmount": "出金額は {min} USDC 以上である必要があります", diff --git a/src/locales/ko.json b/src/locales/ko.json index 8c118b5..80b1ae7 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -522,7 +522,9 @@ "loginWalletLabel": "로그인 지갑", "connectingWallet": "지갑 연결 중…", "walletMismatch": "로그인에 사용한 계정과 동일한 계정으로 브라우저 지갑을 전환해 주세요.", - "fee": "수수료", + "fee": "총 수수료", + "feeFixed": "고정 수수료", + "feePercent": "수수료율", "minAmount": "최소 출금 금액", "receiveAmount": "실수령액", "belowMinAmount": "출금 금액은 최소 {min} USDC 이상이어야 합니다", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 29b6201..c67357b 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -535,7 +535,9 @@ "loginWalletLabel": "登录钱包(提现地址)", "connectingWallet": "正在连接钱包…", "walletMismatch": "请在浏览器钱包中切换到您登录时使用的账户。", - "fee": "手续费", + "fee": "手续费合计", + "feeFixed": "固定手续费", + "feePercent": "比例手续费", "minAmount": "最低提现金额", "receiveAmount": "实际到账", "belowMinAmount": "提现金额不能低于 {min} USDC", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index fed6d3a..aed69b4 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -522,7 +522,9 @@ "loginWalletLabel": "登入錢包(提現地址)", "connectingWallet": "正在連接錢包…", "walletMismatch": "請在瀏覽器錢包中切換至您登入時使用的帳戶。", - "fee": "手續費", + "fee": "手續費合計", + "feeFixed": "固定手續費", + "feePercent": "比例手續費", "minAmount": "最低提現金額", "receiveAmount": "實際到帳", "belowMinAmount": "提現金額不能低於 {min} USDC",