feat: 添加银行账户和货币选择组件,优化提现页面交互体验
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -58,6 +58,7 @@ declare module 'vue' {
|
|||||||
IonTabs: typeof import('@ionic/vue')['IonTabs']
|
IonTabs: typeof import('@ionic/vue')['IonTabs']
|
||||||
IonText: typeof import('@ionic/vue')['IonText']
|
IonText: typeof import('@ionic/vue')['IonText']
|
||||||
IonTitle: typeof import('@ionic/vue')['IonTitle']
|
IonTitle: typeof import('@ionic/vue')['IonTitle']
|
||||||
|
IonToggle: typeof import('@ionic/vue')['IonToggle']
|
||||||
IonToolbar: typeof import('@ionic/vue')['IonToolbar']
|
IonToolbar: typeof import('@ionic/vue')['IonToolbar']
|
||||||
LayoutDefault: typeof import('./src/components/layout/default.vue')['default']
|
LayoutDefault: typeof import('./src/components/layout/default.vue')['default']
|
||||||
PwaInstallButton: typeof import('./src/components/pwa-install-button/index.vue')['default']
|
PwaInstallButton: typeof import('./src/components/pwa-install-button/index.vue')['default']
|
||||||
@@ -117,6 +118,7 @@ declare global {
|
|||||||
const IonTabs: typeof import('@ionic/vue')['IonTabs']
|
const IonTabs: typeof import('@ionic/vue')['IonTabs']
|
||||||
const IonText: typeof import('@ionic/vue')['IonText']
|
const IonText: typeof import('@ionic/vue')['IonText']
|
||||||
const IonTitle: typeof import('@ionic/vue')['IonTitle']
|
const IonTitle: typeof import('@ionic/vue')['IonTitle']
|
||||||
|
const IonToggle: typeof import('@ionic/vue')['IonToggle']
|
||||||
const IonToolbar: typeof import('@ionic/vue')['IonToolbar']
|
const IonToolbar: typeof import('@ionic/vue')['IonToolbar']
|
||||||
const LayoutDefault: typeof import('./src/components/layout/default.vue')['default']
|
const LayoutDefault: typeof import('./src/components/layout/default.vue')['default']
|
||||||
const PwaInstallButton: typeof import('./src/components/pwa-install-button/index.vue')['default']
|
const PwaInstallButton: typeof import('./src/components/pwa-install-button/index.vue')['default']
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export type RwaIssuanceProductBody = TreatyBody<typeof client.api.rwa.issuance.p
|
|||||||
|
|
||||||
export type RwaIssuanceCategoriesData = Treaty.Data<typeof client.api.rwa.category.categories.get>;
|
export type RwaIssuanceCategoriesData = Treaty.Data<typeof client.api.rwa.category.categories.get>;
|
||||||
|
|
||||||
export type BankAccountsData = Treaty.Data<typeof client.api.bank_account.get>;
|
export type BankAccountsData = Treaty.Data<typeof client.api.bank_account.get>["data"][number];
|
||||||
|
|
||||||
export type BankAccountBody = TreatyBody<typeof client.api.bank_account.post>;
|
export type BankAccountBody = TreatyBody<typeof client.api.bank_account.post>;
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,11 @@
|
|||||||
"chooseCurrency": "选择货币",
|
"chooseCurrency": "选择货币",
|
||||||
"chooseMethod": "选择提现方式",
|
"chooseMethod": "选择提现方式",
|
||||||
"amount": "金额",
|
"amount": "金额",
|
||||||
"enterAmountMax": "请输入金额(最大:{amount})",
|
"enterAmountMax": "请输入金额(最大可用:{amount})",
|
||||||
"validAmountError": "请输入有效的金额。",
|
"validAmountError": "请输入有效的金额。",
|
||||||
"bankAccountId": "银行账户ID",
|
"bankAccountId": "银行账户",
|
||||||
"enterBankAccountId": "请输入银行账户ID",
|
"enterBankAccountId": "请输入银行账户",
|
||||||
"validBankAccountError": "请输入有效的银行账户ID。",
|
"validBankAccountError": "请输入有效的银行账户。",
|
||||||
"chooseChain": "选择链",
|
"chooseChain": "选择链",
|
||||||
"cryptoAddress": "加密货币地址",
|
"cryptoAddress": "加密货币地址",
|
||||||
"enterCryptoAddress": "请输入加密货币地址",
|
"enterCryptoAddress": "请输入加密货币地址",
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
"chooseCurrency": "選擇貨幣",
|
"chooseCurrency": "選擇貨幣",
|
||||||
"chooseMethod": "選擇提現方式",
|
"chooseMethod": "選擇提現方式",
|
||||||
"amount": "金額",
|
"amount": "金額",
|
||||||
"enterAmountMax": "請輸入金額(最大:{amount})",
|
"enterAmountMax": "請輸入金額(最大可用:{amount})",
|
||||||
"validAmountError": "請輸入有效的金額。",
|
"validAmountError": "請輸入有效的金額。",
|
||||||
"bankAccountId": "銀行賬戶ID",
|
"bankAccountId": "銀行賬戶",
|
||||||
"enterBankAccountId": "請輸入銀行賬戶ID",
|
"enterBankAccountId": "請輸入銀行賬戶ID",
|
||||||
"validBankAccountError": "請輸入有效的銀行賬戶ID。",
|
"validBankAccountError": "請輸入有效的銀行賬戶ID。",
|
||||||
"chooseChain": "選擇鏈",
|
"chooseChain": "選擇鏈",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface State {
|
|||||||
balances: BalancesData;
|
balances: BalancesData;
|
||||||
fundingBalances: BalancesData;
|
fundingBalances: BalancesData;
|
||||||
tradingBalances: BalancesData;
|
tradingBalances: BalancesData;
|
||||||
bankAccounts: BankAccountsData["data"];
|
bankAccounts: BankAccountsData[];
|
||||||
supportBanks: SupportBanksData["data"];
|
supportBanks: SupportBanksData["data"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ export const useWalletStore = defineStore("wallet", () => {
|
|||||||
state.tradingBalances = balances.value || [];
|
state.tradingBalances = balances.value || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncBankAccounts(data?: BankAccountsData["data"]) {
|
async function syncBankAccounts(data?: BankAccountsData[]) {
|
||||||
if (data) {
|
if (data) {
|
||||||
state.bankAccounts = data;
|
state.bankAccounts = data;
|
||||||
return;
|
return;
|
||||||
|
|||||||
39
src/views/withdraw/components/select-bank-account.vue
Normal file
39
src/views/withdraw/components/select-bank-account.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<script lang='ts' setup>
|
||||||
|
import type { BankAccountsData } from "@/api/types";
|
||||||
|
import { modalController } from "@ionic/vue";
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
select: [id: BankAccountsData];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const walletStore = useWalletStore();
|
||||||
|
const { bankAccounts } = storeToRefs(walletStore);
|
||||||
|
|
||||||
|
function handleSelect(item: BankAccountsData) {
|
||||||
|
emit("select", item);
|
||||||
|
modalController.dismiss(item);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ion-header class="ion-no-border">
|
||||||
|
<ion-toolbar class="ion-toolbar">
|
||||||
|
<ion-title>选择银行账户</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<div class="mt-2 flex flex-col">
|
||||||
|
<div v-for="item in bankAccounts" :key="item.id" class="flex items-center px-3 my-3 w-full" @click="handleSelect(item)">
|
||||||
|
<div class="ml-2 inline-flex gap-2 items-baseline">
|
||||||
|
<div class="w-10 h-10 rounded-xl flex items-center justify-center text-white font-bold text-xs shadow-sm bg-[#0F5AA6]">
|
||||||
|
<span class="text-white">{{ item.bankCode }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="font-medium text-md">{{ item.bankName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='css' scoped></style>
|
||||||
40
src/views/withdraw/components/select-currency.vue
Normal file
40
src/views/withdraw/components/select-currency.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script lang='ts' setup>
|
||||||
|
import { modalController } from "@ionic/vue";
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
select: [code: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const walletStore = useWalletStore();
|
||||||
|
const { fundingBalances } = storeToRefs(walletStore);
|
||||||
|
|
||||||
|
function handleSelect(code: string) {
|
||||||
|
emit("select", code);
|
||||||
|
modalController.dismiss(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
walletStore.syncFundingBalances();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ion-header class="ion-no-border">
|
||||||
|
<ion-toolbar class="ion-toolbar">
|
||||||
|
<ion-title>选择货币</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<div class="mt-2 flex flex-col">
|
||||||
|
<div v-for="item in fundingBalances" :key="item.assetCode" class="flex items-center px-3 my-3 w-full" @click="handleSelect(item.assetCode)">
|
||||||
|
<Icon :icon="item.asset.iconUrl || ''" class="text-3xl" />
|
||||||
|
<div class="ml-2 inline-flex gap-2 items-baseline">
|
||||||
|
<span class="font-medium text-md">{{ item.assetCode }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='css' scoped></style>
|
||||||
@@ -1,16 +1,21 @@
|
|||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
import type { GenericObject } from "vee-validate";
|
import type { GenericObject } from "vee-validate";
|
||||||
import type { WithdrawBody } from "@/api/types";
|
import type { BankAccountsData, WithdrawBody } from "@/api/types";
|
||||||
import { loadingController, toastController } from "@ionic/vue";
|
import type { FormInstance } from "@/utils";
|
||||||
|
import { loadingController, modalController, toastController } from "@ionic/vue";
|
||||||
import { ErrorMessage, Field, Form } from "vee-validate";
|
import { ErrorMessage, Field, Form } from "vee-validate";
|
||||||
import { client, safeClient } from "@/api";
|
import { client, safeClient } from "@/api";
|
||||||
import { AssetCodeEnum, ChainEnum, WithdrawMethodEnum } from "@/api/enum";
|
import { AssetCodeEnum, ChainEnum, WithdrawMethodEnum } from "@/api/enum";
|
||||||
|
import SelectBankAccount from "./components/select-bank-account.vue";
|
||||||
|
import SelectCurrency from "./components/select-currency.vue";
|
||||||
import { createWithdrawSchema } from "./rules";
|
import { createWithdrawSchema } from "./rules";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const walletStore = useWalletStore();
|
const walletStore = useWalletStore();
|
||||||
const { fundingBalances, bankAccounts } = storeToRefs(walletStore);
|
await walletStore.syncFundingBalances();
|
||||||
|
const { fundingBalances } = storeToRefs(walletStore);
|
||||||
|
const formInst = useTemplateRef<FormInstance>("formInst");
|
||||||
|
|
||||||
const initialValues: WithdrawBody = {
|
const initialValues: WithdrawBody = {
|
||||||
assetCode: AssetCodeEnum.USDT,
|
assetCode: AssetCodeEnum.USDT,
|
||||||
@@ -22,11 +27,42 @@ const initialValues: WithdrawBody = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const maxAmount = computed(() => {
|
const maxAmount = computed(() => {
|
||||||
const balance = fundingBalances.value?.find(item => item.assetCode === initialValues.assetCode);
|
const balance = fundingBalances.value?.find(item => item.assetCode === (formInst.value?.getValues().assetCode || initialValues.assetCode));
|
||||||
return balance ? balance.available : "0";
|
return Number(balance ? balance.available : "0").toFixed(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
const schema = computed(() => createWithdrawSchema(t, maxAmount.value));
|
const schema = computed(() => createWithdrawSchema(t, maxAmount.value));
|
||||||
|
const currentBankAccount = ref<BankAccountsData | null>(null);
|
||||||
|
|
||||||
|
async function openSelectCurrency() {
|
||||||
|
const modal = await modalController.create({
|
||||||
|
component: SelectCurrency,
|
||||||
|
componentProps: {
|
||||||
|
onSelect: (code: string) => {
|
||||||
|
formInst.value?.setFieldValue("assetCode", code);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
breakpoints: [0, 0.8],
|
||||||
|
initialBreakpoint: 0.8,
|
||||||
|
handle: true,
|
||||||
|
});
|
||||||
|
await modal.present();
|
||||||
|
}
|
||||||
|
async function openSelectBankAccount() {
|
||||||
|
const modal = await modalController.create({
|
||||||
|
component: SelectBankAccount,
|
||||||
|
componentProps: {
|
||||||
|
onSelect: (item: BankAccountsData) => {
|
||||||
|
currentBankAccount.value = item;
|
||||||
|
formInst.value?.setFieldValue("bankAccountId", item.id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
breakpoints: [0, 0.8],
|
||||||
|
initialBreakpoint: 0.8,
|
||||||
|
handle: true,
|
||||||
|
});
|
||||||
|
await modal.present();
|
||||||
|
}
|
||||||
|
|
||||||
async function onSubmit(values: GenericObject) {
|
async function onSubmit(values: GenericObject) {
|
||||||
const loading = await loadingController.create({
|
const loading = await loadingController.create({
|
||||||
@@ -61,6 +97,7 @@ async function onSubmit(values: GenericObject) {
|
|||||||
</IonHeader>
|
</IonHeader>
|
||||||
<IonContent :fullscreen="true" class="ion-padding">
|
<IonContent :fullscreen="true" class="ion-padding">
|
||||||
<Form
|
<Form
|
||||||
|
ref="formInst"
|
||||||
:validation-schema="schema"
|
:validation-schema="schema"
|
||||||
:initial-values="initialValues"
|
:initial-values="initialValues"
|
||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
@@ -68,17 +105,13 @@ async function onSubmit(values: GenericObject) {
|
|||||||
<div class="flex flex-col gap-5">
|
<div class="flex flex-col gap-5">
|
||||||
<div>
|
<div>
|
||||||
<Field name="assetCode">
|
<Field name="assetCode">
|
||||||
<template #default="{ field, value }">
|
<template #default="{ value }">
|
||||||
<ion-radio-group v-bind="field" :model-value="value">
|
|
||||||
<ion-label class="text-sm">
|
<ion-label class="text-sm">
|
||||||
{{ t("withdraw.chooseCurrency") }}
|
{{ t("withdraw.chooseCurrency") }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-item v-for="item in AssetCodeEnum" :key="item">
|
<ion-item button class="ion-no-border" @click="openSelectCurrency">
|
||||||
<ion-radio :value="item" justify="space-between">
|
<ion-label>{{ value }}</ion-label>
|
||||||
{{ t(`withdraw.assetCode.${item}`) }}
|
|
||||||
</ion-radio>
|
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-radio-group>
|
|
||||||
</template>
|
</template>
|
||||||
</Field>
|
</Field>
|
||||||
<ErrorMessage name="assetCode" class="text-red-500 text-xs mt-1" />
|
<ErrorMessage name="assetCode" class="text-red-500 text-xs mt-1" />
|
||||||
@@ -105,21 +138,18 @@ async function onSubmit(values: GenericObject) {
|
|||||||
<Field name="withdrawMethod">
|
<Field name="withdrawMethod">
|
||||||
<template #default="{ value: withdrawMethod }">
|
<template #default="{ value: withdrawMethod }">
|
||||||
<div v-if="withdrawMethod === WithdrawMethodEnum.BANK">
|
<div v-if="withdrawMethod === WithdrawMethodEnum.BANK">
|
||||||
<Field name="bankAccountId" type="text">
|
<Field name="bankAccountId">
|
||||||
<template #default="{ field }">
|
<ion-label class="text-sm">
|
||||||
<ion-select
|
{{ t('withdraw.bankAccountId') }}
|
||||||
v-bind="field"
|
</ion-label>
|
||||||
class="ui-select"
|
<ion-item button class="ion-no-border" @click="openSelectBankAccount">
|
||||||
interface="action-sheet"
|
<template v-if="currentBankAccount">
|
||||||
toggle-icon=""
|
<div class="w-8 h-8 rounded-md mr-2 flex items-center justify-center text-white font-bold text-xs shadow-sm bg-[#0F5AA6]">
|
||||||
:label="t('withdraw.bankAccountId')"
|
<span class="text-white">{{ currentBankAccount?.bankCode }}</span>
|
||||||
:placeholder="t('withdraw.enterBankAccountId')"
|
</div>
|
||||||
>
|
<ion-label>{{ currentBankAccount?.bankName }}</ion-label>
|
||||||
<ion-select-option v-for="item in bankAccounts" :key="item.id" :value="item.id">
|
|
||||||
{{ item.bankName }} - {{ item.maskAccountNumber }}
|
|
||||||
</ion-select-option>
|
|
||||||
</ion-select>
|
|
||||||
</template>
|
</template>
|
||||||
|
</ion-item>
|
||||||
</Field>
|
</Field>
|
||||||
<ErrorMessage name="bankAccountId" class="text-red-500 text-xs mt-1" />
|
<ErrorMessage name="bankAccountId" class="text-red-500 text-xs mt-1" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user