feat: 更新钱包管理功能,添加银行账户和余额的初始化逻辑,优化API请求和响应处理

This commit is contained in:
2025-12-17 02:09:07 +07:00
parent d375d12583
commit 2f1881cc81
8 changed files with 89 additions and 22 deletions

12
auto-imports.d.ts vendored
View File

@@ -364,6 +364,7 @@ declare module 'vue' {
interface GlobalComponents {} interface GlobalComponents {}
interface ComponentCustomProperties { interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']> readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly computed: UnwrapRef<typeof import('vue')['computed']> readonly computed: UnwrapRef<typeof import('vue')['computed']>
@@ -377,6 +378,7 @@ declare module 'vue' {
readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']> readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']>
readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']> readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']>
readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']> readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']>
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']> readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']>
readonly createRef: UnwrapRef<typeof import('@vueuse/core')['createRef']> readonly createRef: UnwrapRef<typeof import('@vueuse/core')['createRef']>
readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']> readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']>
@@ -388,11 +390,13 @@ declare module 'vue' {
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']> readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']> readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']> readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']> readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']> readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly emailPattern: UnwrapRef<typeof import('./src/utils/pattern')['emailPattern']> readonly emailPattern: UnwrapRef<typeof import('./src/utils/pattern')['emailPattern']>
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']> readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
readonly formatBalance: UnwrapRef<typeof import('./src/utils/helper')['formatBalance']> readonly formatBalance: UnwrapRef<typeof import('./src/utils/helper')['formatBalance']>
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']> readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']> readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']> readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
@@ -407,6 +411,11 @@ declare module 'vue' {
readonly isRef: UnwrapRef<typeof import('vue')['isRef']> readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']> readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']> readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
readonly mapStores: UnwrapRef<typeof import('pinia')['mapStores']>
readonly mapWritableState: UnwrapRef<typeof import('pinia')['mapWritableState']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']> readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']> readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly numberPattern: UnwrapRef<typeof import('./src/utils/pattern')['numberPattern']> readonly numberPattern: UnwrapRef<typeof import('./src/utils/pattern')['numberPattern']>
@@ -450,9 +459,12 @@ declare module 'vue' {
readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']> readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']> readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']> readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']> readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']> readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']> readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']> readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']>
readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']> readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']>
readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']> readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']>

View File

@@ -1,12 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { client, safeClient } from "./api"; const { initializeWallet } = useWalletStore();
const walletStore = useWalletStore(); onMounted(() => {
initializeWallet();
onMounted(async () => {
const { data } = await safeClient(() => client.api.asset.balances.get(), { silent: true });
walletStore.state.balances = data.value;
client.api.rwa.issuance.products.get();
console.log("App mounted successfully"); console.log("App mounted successfully");
}); });
</script> </script>

View File

@@ -8,15 +8,21 @@ const client = treaty<App>(window.location.origin, {
}, },
}); });
export interface SafeClientOptions {
silent?: boolean;
immediate?: boolean;
}
export async function safeClient<T, E>( export async function safeClient<T, E>(
requestPromise: () => Promise<{ data: T; error: E }>, requestPromise: () => Promise<{ data: T; error: E }>,
options: { silent?: boolean; immediate?: boolean } = {}, options: SafeClientOptions = {},
) { ) {
const { immediate = true } = options; const { immediate = true } = options;
const data = ref<T | null>(null); const data = ref<T | null>(null);
const error = ref<E | null>(null); const error = ref<E | null>(null);
let responseCallback: ((data: T, error: E) => void) | null = null;
const executeRequest = async () => { const execute = async () => {
const res = await requestPromise(); const res = await requestPromise();
if (res.error) { if (res.error) {
@@ -34,13 +40,22 @@ export async function safeClient<T, E>(
} }
data.value = res.data; data.value = res.data;
error.value = res.error; error.value = res.error;
// 调用注册的回调函数
if (responseCallback) {
responseCallback(res.data, res.error);
}
}; };
if (immediate) { function onFetchResponse(callback: (data: T, error: E) => void) {
await executeRequest(); responseCallback = callback;
} }
return { data, error, refresh: executeRequest }; if (immediate) {
await execute();
}
return { data, error, refresh: execute, onFetchResponse };
} }
export { client }; export { client };

View File

@@ -1,16 +1,50 @@
import type { BalancesData } from "@/api/types"; import type { BalancesData, BankAccountsData } from "@/api/types";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { client, safeClient } from "@/api";
interface State { interface State {
balances: BalancesData | null; balances: BalancesData;
bankAccounts: BankAccountsData["data"];
} }
export const useWalletStore = defineStore("wallet", () => { export const useWalletStore = defineStore("wallet", () => {
const state = reactive<State>({ const state = reactive<State>({
balances: null, balances: [],
bankAccounts: [],
}); });
const balances = computed(() => state.balances);
const bankAccounts = computed(() => state.bankAccounts);
async function initializeWallet() {
updateBalances();
updateBankAccounts();
}
async function updateBalances(data?: BalancesData) {
if (data) {
state.balances = data;
return;
}
const { data: balances } = await safeClient(() => client.api.asset.balances.get(), { silent: true });
state.balances = balances.value || [];
}
async function updateBankAccounts(data?: BankAccountsData["data"]) {
if (data) {
state.bankAccounts = data;
return;
}
const { data: bankAccounts } = await safeClient(() => client.api.bank_account.get(), { silent: true });
state.bankAccounts = bankAccounts.value?.data || [];
}
return { return {
state, state,
balances,
bankAccounts,
initializeWallet,
updateBalances,
updateBankAccounts,
}; };
}); });

View File

@@ -11,10 +11,15 @@ import { client, safeClient } from "@/api";
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const { updateBankAccounts } = useWalletStore();
const { data, refresh } = await safeClient(() => client.api.bank_account.get()); const { data, refresh, onFetchResponse } = await safeClient(() => client.api.bank_account.get());
const bankCards = computed(() => data.value?.data || []); const bankCards = computed(() => data.value?.data || []);
onFetchResponse((data) => {
data?.data && updateBankAccounts(data.data);
});
function handleAddCard() { function handleAddCard() {
router.push("/trade-settings/bank-management/add"); router.push("/trade-settings/bank-management/add");
} }

View File

@@ -3,7 +3,8 @@ import RechargeChannel from "./recharge-channel.vue";
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const { state } = useWalletStore(); const walletStore = useWalletStore();
const { balances } = storeToRefs(walletStore);
const rechargeInstance = ref<ModalInstance>(); const rechargeInstance = ref<ModalInstance>();
function onCloseModal() { function onCloseModal() {
@@ -12,12 +13,16 @@ function onCloseModal() {
function handleWithdraw() { function handleWithdraw() {
router.push("/withdraw/index"); router.push("/withdraw/index");
} }
onUpdated(() => {
walletStore.updateBalances();
});
</script> </script>
<template> <template>
<div class="mt-5 shadow-md rounded-lg overflow-hidden"> <div class="mt-5 shadow-md rounded-lg overflow-hidden">
<div class="grid grid-cols-1 gap-5 p-5 bg-(--ion-card-background)"> <div class="grid grid-cols-1 gap-5 p-5 bg-(--ion-card-background)">
<div v-for="item in state.balances" :key="item.assetCode" class="flex flex-col gap-1"> <div v-for="item in balances" :key="item.assetCode" class="flex flex-col gap-1">
<div class="uppercase text-xs text-text-400 font-medium tracking-[0.4px]"> <div class="uppercase text-xs text-text-400 font-medium tracking-[0.4px]">
{{ item.assetCode }} {{ item.assetCode }}
</div> </div>

View File

@@ -1,6 +1,5 @@
<script lang='ts' setup> <script lang='ts' setup>
import type { WithdrawBody } from "@/api/types"; import type { WithdrawBody } from "@/api/types";
import { toastController } from "@ionic/vue";
import { client, safeClient } from "@/api"; import { client, safeClient } from "@/api";
import { AssetCodeEnum, ChainEnum, WithdrawMethodEnum } from "@/api/enum"; import { AssetCodeEnum, ChainEnum, WithdrawMethodEnum } from "@/api/enum";
@@ -15,9 +14,10 @@ const [form, resetForm] = useResetRef<WithdrawBody>({
bankAccountId: "", bankAccountId: "",
chain: "BEP20", chain: "BEP20",
}); });
const { state } = useWalletStore(); const walletStore = useWalletStore();
const { balances } = storeToRefs(walletStore);
const maxAmount = computed(() => { const maxAmount = computed(() => {
const balance = state.balances?.find(item => item.assetCode === form.value.assetCode); const balance = balances.value?.find(item => item.assetCode === form.value.assetCode);
return balance ? balance.available : "0"; return balance ? balance.available : "0";
}); });

View File

@@ -27,7 +27,7 @@ export default defineConfig({
tailwindcss(), tailwindcss(),
autoImport({ autoImport({
dirs: ["src/composables", "src/utils", "src/store"], dirs: ["src/composables", "src/utils", "src/store"],
imports: ["vue", "vue-router", "@vueuse/core", "vue-i18n"], imports: ["vue", "vue-router", "@vueuse/core", "vue-i18n", "pinia"],
resolvers: [IonicResolver()], resolvers: [IonicResolver()],
vueTemplate: true, vueTemplate: true,
}), }),