Procházet zdrojové kódy

Merge branch 'v1.1.0' into test

0es před 1 měsícem
rodič
revize
f50694ba2c

+ 9 - 1
app/api/wallet.ts

@@ -4,7 +4,7 @@
  */
 
 import { http } from '~/utils/request'
-import type { NextVOWalletRecordVo, ResultVOString, WalletRechargeConfigVO, WalletRechargeOrderStateVo, WalletRecordDTO, WalletWithdrawCalculateVo, WalletWithdrawInfoVo, WalletWithdrawRealNameAuthDTO, WalletWithdrawRealInfoAuthApplyVo, WalletWithdrawRealInfoAuthStatusVo } from '~/types/api'
+import type { NextVOWalletRecordVo, ResultVOString, WalletRechargeConfigVO, WalletRechargeOrderStateVo, WalletRecordDTO, WalletWithdrawApplyDTO, WalletWithdrawCalculateVo, WalletWithdrawInfoVo, WalletWithdrawRealNameAuthDTO, WalletWithdrawRealInfoAuthApplyVo, WalletWithdrawRealInfoAuthStatusVo } from '~/types/api'
 
 export const walletApi = {
   /**
@@ -90,4 +90,12 @@ export const walletApi = {
   withdrawCalculate(amount: number) {
     return http.post<WalletWithdrawCalculateVo>('/wallet/withdraw/calculate', { amount })
   },
+
+  /**
+   * 金豆提现申请提交
+   * 对应后端接口:POST /wallet/withdraw/submit
+   */
+  withdrawSubmit(data: WalletWithdrawApplyDTO) {
+    return http.post<Record<string, never>>('/wallet/withdraw/submit', data)
+  },
 }

+ 58 - 30
app/pages/wallet/withdraw/apply.vue

@@ -20,6 +20,7 @@ const router = useRouter()
 // Check auth status on mount
 const checkingAuth = ref(true)
 const loadingInfo = ref(true)
+const submitting = ref(false)
 
 const checkAuthStatus = async (): Promise<boolean> => {
   try {
@@ -44,24 +45,26 @@ const checkAuthStatus = async (): Promise<boolean> => {
   }
 }
 
+const initPage = async (resetInput = false) => {
+  const ok = await checkAuthStatus()
+  if (!ok) return
+
+  try {
+    loadingInfo.value = true
+    const info = await walletApi.getWithdrawInfo()
+    applyWithdrawInfo(info, resetInput)
+  }
+  catch (error) {
+    console.error('Failed to fetch withdraw info:', error)
+    showToast({ message: t('wallet.withdraw.apply.toast.fetchInfoFailed') })
+  }
+  finally {
+    loadingInfo.value = false
+  }
+}
+
 onMounted(() => {
-  (async () => {
-    const ok = await checkAuthStatus()
-    if (!ok) return
-
-    try {
-      loadingInfo.value = true
-      const info = await walletApi.getWithdrawInfo()
-      applyWithdrawInfo(info)
-    }
-    catch (error) {
-      console.error('Failed to fetch withdraw info:', error)
-      showToast({ message: 'Failed to fetch withdraw info' })
-    }
-    finally {
-      loadingInfo.value = false
-    }
-  })()
+  void initPage()
 })
 
 const withdrawInfo = ref<WalletWithdrawInfoVo | null>(null)
@@ -126,7 +129,9 @@ const canInputBeans = computed(() => {
 })
 
 const beansPlaceholder = computed(() => {
-  return canInputBeans.value ? 'Please fill in' : '不满足最低提现金额'
+  return canInputBeans.value
+    ? t('wallet.withdraw.apply.input.placeholder')
+    : t('wallet.withdraw.apply.input.placeholderDisabled')
 })
 
 const fromBeansMeasureText = computed(() => {
@@ -135,7 +140,7 @@ const fromBeansMeasureText = computed(() => {
   return v
 })
 
-const applyWithdrawInfo = (info: WalletWithdrawInfoVo) => {
+const applyWithdrawInfo = (info: WalletWithdrawInfoVo, resetInput = false) => {
   withdrawInfo.value = info
 
   balanceBeans.value = Number(info.availableBeanAmount) || 0
@@ -146,7 +151,7 @@ const applyWithdrawInfo = (info: WalletWithdrawInfoVo) => {
   withdrawalFeePercent.value = Number(info.config?.feeRate) || 0
 
   // If balance is below minimum, clear any existing input
-  if (!canInputBeans.value) {
+  if (resetInput || !canInputBeans.value) {
     fromBeans.value = ''
   }
 
@@ -308,6 +313,7 @@ const calibrateWithdrawCalculate = async (beans: number) => {
 }
 
 const isSubmitDisabled = computed(() => {
+  if (checkingAuth.value || loadingInfo.value || submitting.value) return true
   if (!canInputBeans.value) return true
   const v = Number(fromBeans.value)
   if (!fromBeans.value || !Number.isFinite(v) || v <= 0) return true
@@ -317,11 +323,16 @@ const isSubmitDisabled = computed(() => {
 const tipText = computed(() => {
   const minWithdrawalIdr = Number(withdrawInfo.value?.config?.minnum) || 0
   const feeText = withdrawFeeType.value === 0
-    ? `Withdrawal fee: ${idrFormat(withdrawalFeeAmount.value, { withSymbol: true })}`
-    : `Withdrawal fee: ${withdrawalFeePercent.value}%`
+    ? t('wallet.withdraw.apply.fee.fixed', { fee: idrFormat(withdrawalFeeAmount.value, { withSymbol: true }) })
+    : t('wallet.withdraw.apply.fee.rate', { rate: withdrawalFeePercent.value })
   const { bean, idr } = beanToIdrExchange.value
 
-  return `Exchange rate from Beans to IDR: ${bean}:${idr}, Minimum withdrawal amount: ${idrFormat(minWithdrawalIdr, { withSymbol: true })}, ${feeText}`
+  return t('wallet.withdraw.apply.tip', {
+    bean,
+    idr,
+    minAmount: idrFormat(minWithdrawalIdr, { withSymbol: true }),
+    feeText,
+  })
 })
 
 const onBack = () => {
@@ -391,16 +402,33 @@ const validateBeforeSubmit = (): boolean => {
   return true
 }
 
-const onSubmit = () => {
+const onSubmit = async () => {
   if (!validateBeforeSubmit()) return
-  // Submit API is not provided in the spec; only do local validation for now.
+  if (submitting.value) return
+
+  const v = Number(fromBeans.value)
+  if (!Number.isFinite(v) || v <= 0) return
+
+  try {
+    submitting.value = true
+    await walletApi.withdrawSubmit({ amount: Math.floor(v) })
+    showToast({ message: t('wallet.withdraw.apply.toast.submitSuccess') })
+    await initPage(true)
+  }
+  catch (error) {
+    console.error('Failed to submit withdraw apply:', error)
+    // Error message is usually handled by request interceptor toast already
+  }
+  finally {
+    submitting.value = false
+  }
 }
 </script>
 
 <template>
   <div class="withdraw-apply-page">
     <CommonHeader
-      title="Income Swap"
+      :title="t('wallet.withdraw.apply.title')"
       :disable-default-back="true"
       @back="onBack"
     />
@@ -444,7 +472,7 @@ const onSubmit = () => {
           class="withdraw-apply-balance__all"
           @click="onWithdrawAll"
         >
-          全部提现
+          {{ t('wallet.withdraw.apply.actions.withdrawAll') }}
         </button>
       </section>
 
@@ -452,7 +480,7 @@ const onSubmit = () => {
       <section class="withdraw-apply-card">
         <div class="withdraw-apply-card__row withdraw-apply-card__row--top">
           <p class="withdraw-apply-card__label">
-            From Beans
+            {{ t('wallet.withdraw.apply.labels.fromBeans') }}
           </p>
 
           <div class="withdraw-apply-card__right">
@@ -486,7 +514,7 @@ const onSubmit = () => {
 
         <div class="withdraw-apply-card__row withdraw-apply-card__row--bottom">
           <p class="withdraw-apply-card__label">
-            To
+            {{ t('wallet.withdraw.apply.labels.to') }}
           </p>
 
           <div class="withdraw-apply-card__to">
@@ -512,7 +540,7 @@ const onSubmit = () => {
         :disabled="isSubmitDisabled"
         @click="onSubmit"
       >
-        With Draw
+        {{ submitting ? t('wallet.withdraw.apply.submitting') : t('wallet.withdraw.apply.submit') }}
       </button>
     </div>
   </div>

+ 9 - 0
app/types/api/wallet.ts

@@ -198,3 +198,12 @@ export interface WalletWithdrawCalculateVo {
   /** 手续费金额 */
   feeAmount: number
 }
+
+/**
+ * 金豆提现申请提交 - 请求参数
+ * 对应后端:POST /wallet/withdraw/submit
+ */
+export interface WalletWithdrawApplyDTO {
+  /** 要提现的金豆金额 */
+  amount: number
+}

+ 25 - 0
i18n/locales/en.json

@@ -327,6 +327,31 @@
         "success": "Verification submitted successfully",
         "failed": "Submission failed, please try again",
         "partialSuccess": "Submission partially successful"
+      },
+      "apply": {
+        "title": "Income Swap",
+        "actions": {
+          "withdrawAll": "Withdraw all"
+        },
+        "labels": {
+          "fromBeans": "From Beans",
+          "to": "To"
+        },
+        "input": {
+          "placeholder": "Please fill in...",
+          "placeholderDisabled": "Below minimum"
+        },
+        "fee": {
+          "fixed": "Withdrawal fee: {fee}",
+          "rate": "Withdrawal fee: {rate}%"
+        },
+        "tip": "Exchange rate from Beans to IDR: {bean}:{idr}, Minimum withdrawal amount: {minAmount}, {feeText}",
+        "submit": "Withdraw",
+        "submitting": "Submitting...",
+        "toast": {
+          "fetchInfoFailed": "Failed to fetch withdraw info",
+          "submitSuccess": "Withdrawal request submitted successfully. Please wait for review."
+        }
       }
     }
   },

+ 25 - 0
i18n/locales/id.json

@@ -328,6 +328,31 @@
         "success": "Verifikasi berhasil dikirim",
         "failed": "Gagal mengirim, silakan coba lagi",
         "partialSuccess": "Sebagian berhasil dikirim"
+      },
+      "apply": {
+        "title": "Tukar Penghasilan",
+        "actions": {
+          "withdrawAll": "Tarik semua"
+        },
+        "labels": {
+          "fromBeans": "Dari Beans",
+          "to": "Ke"
+        },
+        "input": {
+          "placeholder": "Silakan isi...",
+          "placeholderDisabled": "Di bawah minimum"
+        },
+        "fee": {
+          "fixed": "Biaya penarikan: {fee}",
+          "rate": "Biaya penarikan: {rate}%"
+        },
+        "tip": "Nilai tukar Beans ke IDR: {bean}:{idr}, Minimum penarikan: {minAmount}, {feeText}",
+        "submit": "Tarik",
+        "submitting": "Mengirim...",
+        "toast": {
+          "fetchInfoFailed": "Gagal memuat info penarikan",
+          "submitSuccess": "Pengajuan penarikan berhasil dikirim, mohon tunggu peninjauan"
+        }
       }
     }
   },

+ 25 - 0
i18n/locales/zh.json

@@ -328,6 +328,31 @@
         "success": "认证信息提交成功",
         "failed": "提交失败,请重试",
         "partialSuccess": "提交部分成功"
+      },
+      "apply": {
+        "title": "收益兑换",
+        "actions": {
+          "withdrawAll": "全部提现"
+        },
+        "labels": {
+          "fromBeans": "From Beans",
+          "to": "To"
+        },
+        "input": {
+          "placeholder": "请填写...",
+          "placeholderDisabled": "低于最低额"
+        },
+        "fee": {
+          "fixed": "手续费:{fee}",
+          "rate": "手续费:{rate}%"
+        },
+        "tip": "金豆兑印尼盾汇率:{bean}:{idr},最低提现金额:{minAmount},{feeText}",
+        "submit": "提现",
+        "submitting": "提交中...",
+        "toast": {
+          "fetchInfoFailed": "获取提现信息失败",
+          "submitSuccess": "提现申请成功,请等待审核"
+        }
       }
     }
   },