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']
|
||||
IonText: typeof import('@ionic/vue')['IonText']
|
||||
IonTitle: typeof import('@ionic/vue')['IonTitle']
|
||||
IonToggle: typeof import('@ionic/vue')['IonToggle']
|
||||
IonToolbar: typeof import('@ionic/vue')['IonToolbar']
|
||||
LayoutDefault: typeof import('./src/components/layout/default.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 IonText: typeof import('@ionic/vue')['IonText']
|
||||
const IonTitle: typeof import('@ionic/vue')['IonTitle']
|
||||
const IonToggle: typeof import('@ionic/vue')['IonToggle']
|
||||
const IonToolbar: typeof import('@ionic/vue')['IonToolbar']
|
||||
const LayoutDefault: typeof import('./src/components/layout/default.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 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>;
|
||||
|
||||
|
||||
@@ -58,11 +58,11 @@
|
||||
"chooseCurrency": "选择货币",
|
||||
"chooseMethod": "选择提现方式",
|
||||
"amount": "金额",
|
||||
"enterAmountMax": "请输入金额(最大:{amount})",
|
||||
"enterAmountMax": "请输入金额(最大可用:{amount})",
|
||||
"validAmountError": "请输入有效的金额。",
|
||||
"bankAccountId": "银行账户ID",
|
||||
"enterBankAccountId": "请输入银行账户ID",
|
||||
"validBankAccountError": "请输入有效的银行账户ID。",
|
||||
"bankAccountId": "银行账户",
|
||||
"enterBankAccountId": "请输入银行账户",
|
||||
"validBankAccountError": "请输入有效的银行账户。",
|
||||
"chooseChain": "选择链",
|
||||
"cryptoAddress": "加密货币地址",
|
||||
"enterCryptoAddress": "请输入加密货币地址",
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
"chooseCurrency": "選擇貨幣",
|
||||
"chooseMethod": "選擇提現方式",
|
||||
"amount": "金額",
|
||||
"enterAmountMax": "請輸入金額(最大:{amount})",
|
||||
"enterAmountMax": "請輸入金額(最大可用:{amount})",
|
||||
"validAmountError": "請輸入有效的金額。",
|
||||
"bankAccountId": "銀行賬戶ID",
|
||||
"bankAccountId": "銀行賬戶",
|
||||
"enterBankAccountId": "請輸入銀行賬戶ID",
|
||||
"validBankAccountError": "請輸入有效的銀行賬戶ID。",
|
||||
"chooseChain": "選擇鏈",
|
||||
|
||||
@@ -7,7 +7,7 @@ interface State {
|
||||
balances: BalancesData;
|
||||
fundingBalances: BalancesData;
|
||||
tradingBalances: BalancesData;
|
||||
bankAccounts: BankAccountsData["data"];
|
||||
bankAccounts: BankAccountsData[];
|
||||
supportBanks: SupportBanksData["data"];
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export const useWalletStore = defineStore("wallet", () => {
|
||||
state.tradingBalances = balances.value || [];
|
||||
}
|
||||
|
||||
async function syncBankAccounts(data?: BankAccountsData["data"]) {
|
||||
async function syncBankAccounts(data?: BankAccountsData[]) {
|
||||
if (data) {
|
||||
state.bankAccounts = data;
|
||||
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>
|
||||
import type { GenericObject } from "vee-validate";
|
||||
import type { WithdrawBody } from "@/api/types";
|
||||
import { loadingController, toastController } from "@ionic/vue";
|
||||
import type { BankAccountsData, WithdrawBody } from "@/api/types";
|
||||
import type { FormInstance } from "@/utils";
|
||||
import { loadingController, modalController, toastController } from "@ionic/vue";
|
||||
import { ErrorMessage, Field, Form } from "vee-validate";
|
||||
import { client, safeClient } from "@/api";
|
||||
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";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const walletStore = useWalletStore();
|
||||
const { fundingBalances, bankAccounts } = storeToRefs(walletStore);
|
||||
await walletStore.syncFundingBalances();
|
||||
const { fundingBalances } = storeToRefs(walletStore);
|
||||
const formInst = useTemplateRef<FormInstance>("formInst");
|
||||
|
||||
const initialValues: WithdrawBody = {
|
||||
assetCode: AssetCodeEnum.USDT,
|
||||
@@ -22,11 +27,42 @@ const initialValues: WithdrawBody = {
|
||||
};
|
||||
|
||||
const maxAmount = computed(() => {
|
||||
const balance = fundingBalances.value?.find(item => item.assetCode === initialValues.assetCode);
|
||||
return balance ? balance.available : "0";
|
||||
const balance = fundingBalances.value?.find(item => item.assetCode === (formInst.value?.getValues().assetCode || initialValues.assetCode));
|
||||
return Number(balance ? balance.available : "0").toFixed(2);
|
||||
});
|
||||
|
||||
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) {
|
||||
const loading = await loadingController.create({
|
||||
@@ -61,6 +97,7 @@ async function onSubmit(values: GenericObject) {
|
||||
</IonHeader>
|
||||
<IonContent :fullscreen="true" class="ion-padding">
|
||||
<Form
|
||||
ref="formInst"
|
||||
:validation-schema="schema"
|
||||
:initial-values="initialValues"
|
||||
@submit="onSubmit"
|
||||
@@ -68,17 +105,13 @@ async function onSubmit(values: GenericObject) {
|
||||
<div class="flex flex-col gap-5">
|
||||
<div>
|
||||
<Field name="assetCode">
|
||||
<template #default="{ field, value }">
|
||||
<ion-radio-group v-bind="field" :model-value="value">
|
||||
<template #default="{ value }">
|
||||
<ion-label class="text-sm">
|
||||
{{ t("withdraw.chooseCurrency") }}
|
||||
</ion-label>
|
||||
<ion-item v-for="item in AssetCodeEnum" :key="item">
|
||||
<ion-radio :value="item" justify="space-between">
|
||||
{{ t(`withdraw.assetCode.${item}`) }}
|
||||
</ion-radio>
|
||||
<ion-item button class="ion-no-border" @click="openSelectCurrency">
|
||||
<ion-label>{{ value }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-radio-group>
|
||||
</template>
|
||||
</Field>
|
||||
<ErrorMessage name="assetCode" class="text-red-500 text-xs mt-1" />
|
||||
@@ -105,21 +138,18 @@ async function onSubmit(values: GenericObject) {
|
||||
<Field name="withdrawMethod">
|
||||
<template #default="{ value: withdrawMethod }">
|
||||
<div v-if="withdrawMethod === WithdrawMethodEnum.BANK">
|
||||
<Field name="bankAccountId" type="text">
|
||||
<template #default="{ field }">
|
||||
<ion-select
|
||||
v-bind="field"
|
||||
class="ui-select"
|
||||
interface="action-sheet"
|
||||
toggle-icon=""
|
||||
:label="t('withdraw.bankAccountId')"
|
||||
:placeholder="t('withdraw.enterBankAccountId')"
|
||||
>
|
||||
<ion-select-option v-for="item in bankAccounts" :key="item.id" :value="item.id">
|
||||
{{ item.bankName }} - {{ item.maskAccountNumber }}
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
<Field name="bankAccountId">
|
||||
<ion-label class="text-sm">
|
||||
{{ t('withdraw.bankAccountId') }}
|
||||
</ion-label>
|
||||
<ion-item button class="ion-no-border" @click="openSelectBankAccount">
|
||||
<template v-if="currentBankAccount">
|
||||
<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]">
|
||||
<span class="text-white">{{ currentBankAccount?.bankCode }}</span>
|
||||
</div>
|
||||
<ion-label>{{ currentBankAccount?.bankName }}</ion-label>
|
||||
</template>
|
||||
</ion-item>
|
||||
</Field>
|
||||
<ErrorMessage name="bankAccountId" class="text-red-500 text-xs mt-1" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user