feat: 添加划转功能,更新相关路由和国际化支持
This commit is contained in:
261
src/views/wallet/transfer.vue
Normal file
261
src/views/wallet/transfer.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<script lang='ts' setup>
|
||||
import type { GenericObject } from "vee-validate";
|
||||
import type { FormInstance } from "@/utils";
|
||||
import { loadingController, toastController } from "@ionic/vue";
|
||||
import { swapVerticalOutline } from "ionicons/icons";
|
||||
import { ErrorMessage, Field, Form } from "vee-validate";
|
||||
import * as yup from "yup";
|
||||
import { client, safeClient } from "@/api";
|
||||
import { AssetCodeEnum } from "@/api/enum";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const walletStore = useWalletStore();
|
||||
const { fundingBalances, tradingBalances } = storeToRefs(walletStore);
|
||||
const formRef = useTemplateRef<FormInstance>("formRef");
|
||||
|
||||
type AccountType = "funding" | "trading";
|
||||
|
||||
interface TransferForm {
|
||||
assetCode: AssetCodeEnum;
|
||||
amount: string;
|
||||
fromAccount: AccountType;
|
||||
toAccount: AccountType;
|
||||
}
|
||||
|
||||
const initialValues: TransferForm = {
|
||||
assetCode: AssetCodeEnum.USDT,
|
||||
amount: "",
|
||||
fromAccount: "funding",
|
||||
toAccount: "trading",
|
||||
};
|
||||
|
||||
// 可用余额
|
||||
const availableBalance = computed(() => {
|
||||
const form = formRef.value?.values as TransferForm | undefined;
|
||||
if (!form)
|
||||
return "0";
|
||||
|
||||
const balances = form.fromAccount === "funding" ? fundingBalances.value : tradingBalances.value;
|
||||
const balance = balances?.find(item => item.assetCode === form.assetCode);
|
||||
return balance ? balance.available : "0";
|
||||
});
|
||||
|
||||
// 验证规则
|
||||
const schema = computed(() => yup.object({
|
||||
assetCode: yup.string().required(t("transfer.assetCodeRequired")),
|
||||
amount: yup
|
||||
.string()
|
||||
.required(t("transfer.amountRequired"))
|
||||
.test("min", t("transfer.amountMinError"), value => Number(value) > 0)
|
||||
.test("max", t("transfer.amountMaxError", { amount: availableBalance.value }), value => Number(value) <= Number(availableBalance.value)),
|
||||
fromAccount: yup.string().required(t("transfer.fromAccountRequired")),
|
||||
toAccount: yup.string().required(t("transfer.toAccountRequired")),
|
||||
}));
|
||||
|
||||
// 交换账户
|
||||
function swapAccounts() {
|
||||
const form = formRef.value as any;
|
||||
if (!form)
|
||||
return;
|
||||
|
||||
const currentFrom = form.values.fromAccount;
|
||||
const currentTo = form.values.toAccount;
|
||||
|
||||
form.setFieldValue("fromAccount", currentTo);
|
||||
form.setFieldValue("toAccount", currentFrom);
|
||||
}
|
||||
|
||||
// 设置全部金额
|
||||
function setMaxAmount() {
|
||||
const form = formRef.value as any;
|
||||
if (!form)
|
||||
return;
|
||||
|
||||
form.setFieldValue("amount", availableBalance.value);
|
||||
}
|
||||
|
||||
// 提交划转
|
||||
async function onSubmit(values: GenericObject) {
|
||||
const loading = await loadingController.create({
|
||||
message: t("transfer.submitting"),
|
||||
});
|
||||
await loading.present();
|
||||
|
||||
const transferData = values as TransferForm;
|
||||
const { error } = await safeClient(() => client.api.account_transfer.post({
|
||||
assetCode: transferData.assetCode,
|
||||
amount: String(transferData.amount),
|
||||
fromAccountType: transferData.fromAccount,
|
||||
toAccountType: transferData.toAccount,
|
||||
}));
|
||||
|
||||
await loading.dismiss();
|
||||
|
||||
if (!error.value) {
|
||||
const toast = await toastController.create({
|
||||
message: t("transfer.successMessage"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "success",
|
||||
});
|
||||
|
||||
await toast.present();
|
||||
|
||||
// 刷新余额
|
||||
walletStore.syncFundingBalances();
|
||||
walletStore.syncTradingBalances();
|
||||
|
||||
router.back();
|
||||
}
|
||||
}
|
||||
|
||||
// 账户类型显示名称
|
||||
function getAccountTypeName(type: AccountType) {
|
||||
return type === "funding" ? t("transfer.fundingAccount") : t("transfer.tradingAccount");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header>
|
||||
<ion-toolbar class="ui-toolbar">
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button default-href="/wallet/index" />
|
||||
</ion-buttons>
|
||||
<ion-title>{{ t("transfer.title") }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content :fullscreen="true" class="ion-padding">
|
||||
<Form
|
||||
ref="formRef"
|
||||
:validation-schema="schema"
|
||||
:initial-values="initialValues"
|
||||
@submit="onSubmit"
|
||||
>
|
||||
<div class="flex flex-col gap-5">
|
||||
<!-- 币种选择 -->
|
||||
<div>
|
||||
<Field name="assetCode">
|
||||
<template #default="{ field, value }">
|
||||
<ion-radio-group v-bind="field" :model-value="value">
|
||||
<ion-label class="block text-sm font-medium mb-3">
|
||||
{{ t("transfer.chooseCurrency") }}
|
||||
</ion-label>
|
||||
<div class="flex gap-3">
|
||||
<ion-item
|
||||
v-for="item in AssetCodeEnum"
|
||||
:key="item"
|
||||
class="flex-1"
|
||||
lines="none"
|
||||
>
|
||||
<ion-radio :value="item">
|
||||
{{ item }}
|
||||
</ion-radio>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-radio-group>
|
||||
</template>
|
||||
</Field>
|
||||
<ErrorMessage name="assetCode" class="text-red-500 text-xs mt-1" />
|
||||
</div>
|
||||
|
||||
<!-- 划转账户 -->
|
||||
<div class="relative flex flex-col">
|
||||
<Field name="fromAccount">
|
||||
<template #default="{ field, value }">
|
||||
<ion-select
|
||||
v-bind="field"
|
||||
:model-value="value"
|
||||
class="ui-select"
|
||||
interface="action-sheet"
|
||||
toggle-icon=""
|
||||
:label="t('transfer.from')"
|
||||
>
|
||||
<ion-select-option value="funding">
|
||||
{{ getAccountTypeName("funding") }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="trading">
|
||||
{{ getAccountTypeName("trading") }}
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
</template>
|
||||
</Field>
|
||||
|
||||
<div class="absolute left-7.5 top-1/2 -translate-y-1/2 z-10">
|
||||
<ion-button
|
||||
color="primary"
|
||||
@click="swapAccounts"
|
||||
>
|
||||
<ion-icon slot="icon-only" :icon="swapVerticalOutline" />
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
<Field name="toAccount">
|
||||
<template #default="{ field, value }">
|
||||
<ion-select
|
||||
v-bind="field"
|
||||
:model-value="value"
|
||||
class="ui-select"
|
||||
interface="action-sheet"
|
||||
toggle-icon=""
|
||||
:label="t('transfer.to')"
|
||||
>
|
||||
<ion-select-option value="funding">
|
||||
{{ getAccountTypeName("funding") }}
|
||||
</ion-select-option>
|
||||
<ion-select-option value="trading">
|
||||
{{ getAccountTypeName("trading") }}
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
</template>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<!-- 划转数量 -->
|
||||
<div>
|
||||
<Field name="amount">
|
||||
<template #default="{ field }">
|
||||
<div class="relative">
|
||||
<ui-input-label
|
||||
v-bind="field"
|
||||
:label="t('transfer.amount')"
|
||||
:placeholder="t('transfer.enterAmount')"
|
||||
type="number"
|
||||
inputmode="decimal"
|
||||
/>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
size="small"
|
||||
class="absolute right-0 top-8 text-sm font-semibold"
|
||||
@click="setMaxAmount"
|
||||
>
|
||||
{{ t("transfer.all") }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</template>
|
||||
</Field>
|
||||
<ErrorMessage name="amount" class="text-red-500 text-xs mt-1" />
|
||||
</div>
|
||||
|
||||
<!-- 可用余额 -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-(--ion-color-medium)">{{ t("transfer.available") }}</span>
|
||||
<span class="text-sm font-medium">{{ Number(availableBalance).toFixed(2) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<ion-button
|
||||
expand="block"
|
||||
type="submit"
|
||||
shape="round"
|
||||
class="mt-4 h-12 font-semibold"
|
||||
>
|
||||
{{ t("transfer.submit") }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</Form>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
Reference in New Issue
Block a user