feat: 添加划转功能,更新相关路由和国际化支持
This commit is contained in:
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@@ -10,7 +10,7 @@ Riwa-Ionic 项目提示词
|
|||||||
项目技术栈
|
项目技术栈
|
||||||
核心框架:
|
核心框架:
|
||||||
|
|
||||||
Vue 3.5.25 (Composition API with <script setup>)
|
Vue 3.5.25 (Composition API with <script lang="ts" setup>)
|
||||||
Ionic Vue 8.7.11 (移动端 UI 框架)
|
Ionic Vue 8.7.11 (移动端 UI 框架)
|
||||||
TypeScript 5.9.3
|
TypeScript 5.9.3
|
||||||
Capacitor 8.0.0 (原生功能桥接)
|
Capacitor 8.0.0 (原生功能桥接)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"@elysiajs/eden": "^1.4.5",
|
"@elysiajs/eden": "^1.4.5",
|
||||||
"@ionic/vue": "^8.7.11",
|
"@ionic/vue": "^8.7.11",
|
||||||
"@ionic/vue-router": "^8.7.11",
|
"@ionic/vue-router": "^8.7.11",
|
||||||
"@riwa/api-types": "http://192.168.1.27:9527/api/riwa-api-types-0.0.63.tgz",
|
"@riwa/api-types": "http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz",
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
"@vee-validate/yup": "^4.15.1",
|
"@vee-validate/yup": "^4.15.1",
|
||||||
"@vueuse/core": "^14.1.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^6.6.1",
|
"@antfu/eslint-config": "^6.6.1",
|
||||||
"@capacitor/cli": "8.0.0",
|
"@capacitor/cli": "8.0.0",
|
||||||
|
"@iconify-json/bx": "^1.2.2",
|
||||||
"@iconify-json/circle-flags": "^1.2.10",
|
"@iconify-json/circle-flags": "^1.2.10",
|
||||||
"@iconify-json/cryptocurrency-color": "^1.2.4",
|
"@iconify-json/cryptocurrency-color": "^1.2.4",
|
||||||
"@iconify-json/ic": "^1.2.4",
|
"@iconify-json/ic": "^1.2.4",
|
||||||
|
|||||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -57,8 +57,8 @@ importers:
|
|||||||
specifier: ^8.7.11
|
specifier: ^8.7.11
|
||||||
version: 8.7.11(@stencil/core@4.39.0)(vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))
|
version: 8.7.11(@stencil/core@4.39.0)(vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))
|
||||||
'@riwa/api-types':
|
'@riwa/api-types':
|
||||||
specifier: http://192.168.1.27:9527/api/riwa-api-types-0.0.63.tgz
|
specifier: http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz
|
||||||
version: http://192.168.1.27:9527/api/riwa-api-types-0.0.63.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
version: http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.18
|
specifier: ^4.1.18
|
||||||
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||||
@@ -120,6 +120,9 @@ importers:
|
|||||||
'@capacitor/cli':
|
'@capacitor/cli':
|
||||||
specifier: 8.0.0
|
specifier: 8.0.0
|
||||||
version: 8.0.0
|
version: 8.0.0
|
||||||
|
'@iconify-json/bx':
|
||||||
|
specifier: ^1.2.2
|
||||||
|
version: 1.2.2
|
||||||
'@iconify-json/circle-flags':
|
'@iconify-json/circle-flags':
|
||||||
specifier: ^1.2.10
|
specifier: ^1.2.10
|
||||||
version: 1.2.10
|
version: 1.2.10
|
||||||
@@ -1204,6 +1207,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
|
|
||||||
|
'@iconify-json/bx@1.2.2':
|
||||||
|
resolution: {integrity: sha512-hZVx6LMEkYckScdRdUuQWcmv8Lm2au6Cnf799TLoR6YgiAfFvaJ4M5ElwcnExvCu8ntsS7jW89r0W5LwBAfZXQ==}
|
||||||
|
|
||||||
'@iconify-json/circle-flags@1.2.10':
|
'@iconify-json/circle-flags@1.2.10':
|
||||||
resolution: {integrity: sha512-sZRxs689a281RtZvuAiKtV7pQHv8Tk0HkinSM7QvLgdLEK8xgGRCcbDvL09Rq+/KtemmsMzGhS9/qt+r3ca+Ug==}
|
resolution: {integrity: sha512-sZRxs689a281RtZvuAiKtV7pQHv8Tk0HkinSM7QvLgdLEK8xgGRCcbDvL09Rq+/KtemmsMzGhS9/qt+r3ca+Ug==}
|
||||||
|
|
||||||
@@ -1389,9 +1395,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
||||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||||
|
|
||||||
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.63.tgz':
|
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz':
|
||||||
resolution: {tarball: http://192.168.1.27:9527/api/riwa-api-types-0.0.63.tgz}
|
resolution: {tarball: http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz}
|
||||||
version: 0.0.63
|
version: 0.0.64
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@elysiajs/eden': ^1.4.5
|
'@elysiajs/eden': ^1.4.5
|
||||||
|
|
||||||
@@ -6300,6 +6306,10 @@ snapshots:
|
|||||||
|
|
||||||
'@humanwhocodes/retry@0.4.3': {}
|
'@humanwhocodes/retry@0.4.3': {}
|
||||||
|
|
||||||
|
'@iconify-json/bx@1.2.2':
|
||||||
|
dependencies:
|
||||||
|
'@iconify/types': 2.0.0
|
||||||
|
|
||||||
'@iconify-json/circle-flags@1.2.10':
|
'@iconify-json/circle-flags@1.2.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@iconify/types': 2.0.0
|
'@iconify/types': 2.0.0
|
||||||
@@ -6624,7 +6634,7 @@ snapshots:
|
|||||||
|
|
||||||
'@pkgr/core@0.2.9': {}
|
'@pkgr/core@0.2.9': {}
|
||||||
|
|
||||||
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.63.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
'@riwa/api-types@http://192.168.1.27:9527/api/riwa-api-types-0.0.64.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@elysiajs/eden': 1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
'@elysiajs/eden': 1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
||||||
|
|
||||||
|
|||||||
@@ -365,5 +365,26 @@
|
|||||||
"themeLight": "Light",
|
"themeLight": "Light",
|
||||||
"themeDark": "Dark",
|
"themeDark": "Dark",
|
||||||
"themeAuto": "Auto"
|
"themeAuto": "Auto"
|
||||||
|
},
|
||||||
|
"transfer": {
|
||||||
|
"title": "Transfer",
|
||||||
|
"chooseCurrency": "Choose Currency",
|
||||||
|
"from": "From",
|
||||||
|
"to": "To",
|
||||||
|
"fundingAccount": "Funding Account",
|
||||||
|
"tradingAccount": "Trading Account",
|
||||||
|
"available": "Available",
|
||||||
|
"amount": "Amount",
|
||||||
|
"enterAmount": "Enter amount",
|
||||||
|
"all": "All",
|
||||||
|
"submit": "Confirm Transfer",
|
||||||
|
"submitting": "Transferring...",
|
||||||
|
"successMessage": "Transfer successful!",
|
||||||
|
"assetCodeRequired": "Please select a currency",
|
||||||
|
"amountRequired": "Please enter amount",
|
||||||
|
"amountMinError": "Amount must be greater than 0",
|
||||||
|
"amountMaxError": "Amount cannot exceed available balance {amount}",
|
||||||
|
"fromAccountRequired": "Please select from account",
|
||||||
|
"toAccountRequired": "Please select to account"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,5 +371,26 @@
|
|||||||
"themeLight": "浅色",
|
"themeLight": "浅色",
|
||||||
"themeDark": "深色",
|
"themeDark": "深色",
|
||||||
"themeAuto": "跟随系统"
|
"themeAuto": "跟随系统"
|
||||||
|
},
|
||||||
|
"transfer": {
|
||||||
|
"title": "划转",
|
||||||
|
"chooseCurrency": "选择币种",
|
||||||
|
"from": "从",
|
||||||
|
"to": "到",
|
||||||
|
"fundingAccount": "资金账户",
|
||||||
|
"tradingAccount": "交易账户",
|
||||||
|
"available": "可用",
|
||||||
|
"amount": "划转数量",
|
||||||
|
"enterAmount": "请输入划转数量",
|
||||||
|
"all": "全部",
|
||||||
|
"submit": "确认划转",
|
||||||
|
"submitting": "划转中...",
|
||||||
|
"successMessage": "划转成功!",
|
||||||
|
"assetCodeRequired": "请选择币种",
|
||||||
|
"amountRequired": "请输入划转数量",
|
||||||
|
"amountMinError": "划转数量必须大于0",
|
||||||
|
"amountMaxError": "划转数量不能超过可用余额 {amount}",
|
||||||
|
"fromAccountRequired": "请选择转出账户",
|
||||||
|
"toAccountRequired": "请选择转入账户"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () => import("@/views/wallet/bill.vue"),
|
component: () => import("@/views/wallet/bill.vue"),
|
||||||
meta: { requiresAuth: true },
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/wallet/transfer",
|
||||||
|
component: () => import("@/views/wallet/transfer.vue"),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/user/settings",
|
path: "/user/settings",
|
||||||
component: () => import("@/views/user-settings/outlet.vue"),
|
component: () => import("@/views/user-settings/outlet.vue"),
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
import { eyeOffOutline, eyeOutline } from "ionicons/icons";
|
import { eyeOffOutline, eyeOutline } from "ionicons/icons";
|
||||||
|
import BxTransferAlt from "~icons/bx/transfer-alt";
|
||||||
import IcBaselineBlurCircular from "~icons/ic/baseline-blur-circular";
|
import IcBaselineBlurCircular from "~icons/ic/baseline-blur-circular";
|
||||||
import IcRoundArrowCircleDown from "~icons/ic/round-arrow-circle-down";
|
import IcRoundArrowCircleDown from "~icons/ic/round-arrow-circle-down";
|
||||||
import IcRoundArrowCircleUp from "~icons/ic/round-arrow-circle-up";
|
import IcRoundArrowCircleUp from "~icons/ic/round-arrow-circle-up";
|
||||||
import RechargeChannel from "./recharge-channel.vue";
|
import RechargeChannel from "./recharge-channel.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
|
||||||
const walletStore = useWalletStore();
|
const walletStore = useWalletStore();
|
||||||
const { totalAssetValue } = storeToRefs(walletStore);
|
const { totalAssetValue } = storeToRefs(walletStore);
|
||||||
const rechargeInstance = ref<ModalInstance>();
|
const rechargeInstance = ref<ModalInstance>();
|
||||||
@@ -16,12 +16,6 @@ const totalAsset = computed(() => Number(totalAssetValue.value.totalValueUsd).to
|
|||||||
function onCloseModal() {
|
function onCloseModal() {
|
||||||
rechargeInstance.value?.$el.dismiss(null, "confirm");
|
rechargeInstance.value?.$el.dismiss(null, "confirm");
|
||||||
}
|
}
|
||||||
function handleWithdraw() {
|
|
||||||
router.push("/withdraw/index");
|
|
||||||
}
|
|
||||||
function handleBill() {
|
|
||||||
router.push("/wallet/bill");
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
walletStore.syncFundingBalances();
|
walletStore.syncFundingBalances();
|
||||||
@@ -45,21 +39,42 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-5 w-full">
|
<div class="flex gap-2 w-full justify-around">
|
||||||
<ion-button id="open-recharge-modal" expand="full" color="success" shape="round" class="w-full min-h-10 h-10">
|
<div id="open-recharge-modal" class="flex-col-center">
|
||||||
<IcRoundArrowCircleDown slot="start" />
|
<ion-button expand="full" color="success" shape="round" class="w-12 h-12">
|
||||||
|
<IcRoundArrowCircleDown slot="icon-only" />
|
||||||
|
</ion-button>
|
||||||
|
<div class="text-sm font-medium mt-1">
|
||||||
{{ t("wallet.recharge") }}
|
{{ t("wallet.recharge") }}
|
||||||
</ion-button>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ion-button expand="full" color="success" shape="round" class="w-full min-h-10 h-10" @click="handleWithdraw">
|
<div class="flex-col-center" @click="$router.push('/withdraw/index')">
|
||||||
<IcRoundArrowCircleUp slot="start" />
|
<ion-button expand="full" color="success" shape="round" class="w-12 h-12">
|
||||||
|
<IcRoundArrowCircleUp slot="icon-only" />
|
||||||
|
</ion-button>
|
||||||
|
<div class="text-sm font-medium mt-1">
|
||||||
{{ t("wallet.withdraw") }}
|
{{ t("wallet.withdraw") }}
|
||||||
</ion-button>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ion-button expand="full" color="success" shape="round" class="w-full min-h-10 h-10" @click="handleBill">
|
<div class="flex-col-center" @click="$router.push('/wallet/transfer')">
|
||||||
<IcBaselineBlurCircular slot="start" />
|
<ion-button expand="full" color="success" shape="round" class="w-12 h-12">
|
||||||
账单
|
<BxTransferAlt slot="icon-only" />
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
<div class="text-sm font-medium mt-1">
|
||||||
|
划转
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-col-center" @click="$router.push('/wallet/bill')">
|
||||||
|
<ion-button expand="full" color="success" shape="round" class="w-12 h-12">
|
||||||
|
<IcBaselineBlurCircular slot="icon-only" />
|
||||||
|
</ion-button>
|
||||||
|
<div class="text-sm font-medium mt-1">
|
||||||
|
账单
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- <div id="open-recharge-modal" class="flex-col-center">
|
<!-- <div id="open-recharge-modal" class="flex-col-center">
|
||||||
<ion-ripple-effect />
|
<ion-ripple-effect />
|
||||||
<ion-button shape="round" color="success" size="large">
|
<ion-button shape="round" color="success" size="large">
|
||||||
|
|||||||
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