feat: 更新 '@capp/eden' 依赖至 0.0.9,添加资产中心页面,优化钱包相关逻辑

This commit is contained in:
2026-01-18 20:48:02 +07:00
parent 7a17102141
commit afc806381e
14 changed files with 174 additions and 99 deletions

View File

@@ -61,8 +61,8 @@ export function safeClient<T, E>(
isPending.value = false;
});
if (res.error && res.status === 418) {
if (!options.silent) {
if (res.error) {
if (!options.silent && res.status === 418) {
const toast = await toastController.create({
message: i18n.global.t((res.error as any).value.code, {
...(res.error as any).value.context,
@@ -73,6 +73,10 @@ export function safeClient<T, E>(
});
await toast.present();
}
else if (res.status === 401) {
localStorage.removeItem("user-token");
window.location.reload();
}
throw res.error;
}

View File

@@ -119,6 +119,11 @@ const routes: Array<RouteRecordRaw> = [
component: () => import("@/views/team/index.vue"),
meta: { requiresAuth: true },
},
{
path: "/asset_center",
component: () => import("@/views/asset_center/index.vue"),
meta: { requiresAuth: true },
},
];
const router = createRouter({

View File

@@ -5,21 +5,20 @@ export type Wallet = Treaty.Data<typeof client.api.wallet.wallets.get>[number];
export const useWalletStore = defineStore("wallet", () => {
const wallets = ref<Wallet[]>([]);
const balanceWallet = ref<Wallet | null>(null);
async function syncWallets() {
const { data } = await safeClient(client.api.wallet.wallets.get(), { silent: true });
wallets.value = data.value || [];
}
async function syncBalanceWallet() {
const { data } = await safeClient(client.api.wallet.wallet_by_code({ walletTypeCode: "balance" }).get(), { silent: true });
balanceWallet.value = data.value || null;
async function getWalletByType(type: Wallet["walletType"]["code"]) {
const { data } = await safeClient(client.api.wallet.wallet_by_code({ walletTypeCode: type }).get(), { silent: true });
return data;
}
return {
wallets,
balanceWallet,
syncWallets,
syncBalanceWallet,
getWalletByType,
};
});

View File

@@ -63,75 +63,9 @@
--ion-color-dark-tint: #444444;
}
/*
:root {
--ion-color-primary: #2065c3;
--ion-color-primary-rgb: 32,101,195;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255,255,255;
--ion-color-primary-shade: #1c59ac;
--ion-color-primary-tint: #3674c9;
--ion-color-secondary: #0163aa;
--ion-color-secondary-rgb: 1,99,170;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255,255,255;
--ion-color-secondary-shade: #015796;
--ion-color-secondary-tint: #1a73b3;
--ion-color-tertiary: #6030ff;
--ion-color-tertiary-rgb: 96,48,255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255,255,255;
--ion-color-tertiary-shade: #542ae0;
--ion-color-tertiary-tint: #7045ff;
--ion-color-success: #2dd55b;
--ion-color-success-rgb: 45,213,91;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0,0,0;
--ion-color-success-shade: #28bb50;
--ion-color-success-tint: #42d96b;
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255,196,9;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0,0,0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
--ion-color-danger: #c5000f;
--ion-color-danger-rgb: 197,0,15;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255,255,255;
--ion-color-danger-shade: #ad000d;
--ion-color-danger-tint: #cb1a27;
--ion-color-light: #f6f8fc;
--ion-color-light-rgb: 246,248,252;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0,0,0;
--ion-color-light-shade: #d8dade;
--ion-color-light-tint: #f7f9fc;
--ion-color-medium: #5f5f5f;
--ion-color-medium-rgb: 95,95,95;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255,255,255;
--ion-color-medium-shade: #545454;
--ion-color-medium-tint: #6f6f6f;
--ion-color-dark: #2f2f2f;
--ion-color-dark-rgb: 47,47,47;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255,255,255;
--ion-color-dark-shade: #292929;
--ion-color-dark-tint: #444444;
} */
.ion-toolbar {
--background: var(--ion-color-primary);
--background: #c32120;
--min-height: 58px;
--color: white;
}

View File

@@ -0,0 +1,107 @@
<script lang='ts' setup>
import { arrowDownOutline, arrowUpOutline, cardOutline, swapHorizontalOutline, syncOutline } from "ionicons/icons";
const router = useRouter();
const walletStore = useWalletStore();
const { wallets } = storeToRefs(walletStore);
onMounted(async () => {
await walletStore.syncWallets();
});
function formatAmount(amount: number | string) {
const num = Number(amount);
return num.toLocaleString("zh-CN", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
// 操作按钮配置
const actions = [
{ id: "recharge", name: "充值", icon: arrowDownOutline, color: "#c32120" },
{ id: "withdraw", name: "提现", icon: arrowUpOutline, color: "#c32120" },
{ id: "transfer", name: "转账", icon: swapHorizontalOutline, color: "#c32120" },
{ id: "exchange", name: "兑换", icon: syncOutline, color: "#c32120" },
];
function handleAction(actionId: string) {
router.push(`/${actionId}`);
}
</script>
<template>
<ion-page>
<ion-header class="ion-no-border">
<ion-toolbar class="ion-toolbar">
<ion-buttons slot="start">
<back-button />
</ion-buttons>
<ion-title>资产中心</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<div class="ion-padding">
<!-- 操作按钮 -->
<section class="my-5 grid grid-cols-4 gap-4">
<div
v-for="action in actions"
:key="action.id"
class="flex flex-col items-center gap-2 cursor-pointer transition-transform active:scale-95"
@click="handleAction(action.id)"
>
<div
class="w-12 h-12 rounded-xl flex-center shadow-lg"
:style="{ background: action.color }"
>
<ion-icon :icon="action.icon" class="text-xl text-white" />
</div>
<span class="text-xs text-[#333] font-medium text-center">{{ action.name }}</span>
</div>
</section>
<!-- 钱包列表 -->
<section class="mt-8 mb-5">
<div class="flex items-center gap-2 mb-4">
<img src="@/assets/images/icon.png" class="size-7">
<div class="text-xl font-bold text-[#1a1a1a]">
我的钱包
</div>
</div>
<empty v-if="wallets.length === 0" class="my-10" />
<div v-else class="flex flex-col gap-4">
<div
v-for="wallet in wallets"
:key="wallet.id"
class="card rounded-2xl overflow-hidden shadow-sm p-5"
>
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="text-sm text-[#666] mb-1 flex items-center gap-2 font-bold">
<ion-icon :icon="cardOutline" class="text-xl text-primary" />
{{ wallet.walletType.name }}
</div>
<div class="text-2xl font-bold text-[#1a1a1a]">
¥ {{ formatAmount(wallet.available) }}
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</ion-content>
</ion-page>
</template>
<style lang='css' scoped>
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.card {
/* background: linear-gradient(45deg, #ffffff, #ffdbdb 100%); */
}
</style>

View File

@@ -2,7 +2,7 @@
import { arrowDownCircleOutline, arrowUpCircleOutline, listOutline, walletOutline } from "ionicons/icons";
const walletStore = useWalletStore();
const { balanceWallet } = storeToRefs(walletStore);
const balanceWallet = await walletStore.getWalletByType("balance");
// 当前选中的标签
const selectedTab = ref<"all" | "income" | "investment">("all");
@@ -137,8 +137,6 @@ function getTypeName(type: string) {
function formatAmount(amount: number) {
return amount >= 0 ? `+${amount.toFixed(2)}` : amount.toFixed(2);
}
walletStore.syncBalanceWallet();
</script>
<template>

View File

@@ -39,6 +39,15 @@ async function handleSignup() {
<template>
<ion-page>
<ion-header class="ion-no-border">
<ion-toolbar class="ion-toolbar">
<ion-buttons slot="start">
<back-button />
</ion-buttons>
<!-- <ion-title>签到</ion-title> -->
</ion-toolbar>
</ion-header>
<ion-content>
<!-- 顶部横幅 -->
<img src="@/assets/images/signup-banner.jpg" class="h-50 w-full object-cover" alt="服务页横幅">

View File

@@ -70,6 +70,15 @@ function handleDownloadQR() {
<template>
<ion-page>
<ion-header class="ion-no-border">
<ion-toolbar class="ion-toolbar">
<ion-buttons slot="start">
<back-button />
</ion-buttons>
<ion-title>邀请</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<!-- 顶部横幅 -->
<img src="@/assets/images/invite-banner.jpg" class="w-full object-cover" alt="邀请横幅">

View File

@@ -9,9 +9,9 @@ const router = useRouter();
const userStore = useUserStore();
const walletStore = useWalletStore();
const { userProfile } = storeToRefs(userStore);
const { balanceWallet } = storeToRefs(walletStore);
walletStore.syncBalanceWallet();
const balanceWallet = await walletStore.getWalletByType("balance");
const profitWallet = await walletStore.getWalletByType("profit");
function handleRecharge() {
router.push("/recharge");
@@ -72,7 +72,7 @@ async function handleLogout() {
手机号{{ userProfile?.user.username }}
</div>
<div class="text-primary text-sm font-semibold opacity-90">
邀请码{{ userProfile?.inviteCode }}
邀请码{{ userProfile?.referralCode }}
</div>
</div>
</div>
@@ -96,7 +96,7 @@ async function handleLogout() {
收益钱包
</div>
<div class="text-2xl font-bold text-[#c41e3a] mb-1">
¥{{ balanceWallet?.available }}
¥{{ Number(profitWallet?.available).toString() }}
</div>
</div>
<div class="bg-linear-to-br from-[#fff7f0] to-[#ffe8e8] rounded-xl p-4">
@@ -104,11 +104,20 @@ async function handleLogout() {
账户余额
</div>
<div class="text-2xl font-bold text-[#c41e3a] mb-1">
¥{{ balanceWallet?.available }}
¥{{ Number(balanceWallet?.available).toString() }}
</div>
</div>
</div>
<ion-button
expand="block"
class="flex-1 recharge-btn mb-5"
@click="router.push('/asset_center')"
>
<ion-icon slot="start" :icon="walletOutline" class="mr-2" />
资产中心
</ion-button>
<!-- 快捷操作按钮 -->
<div class="flex gap-3">
<ion-button

View File

@@ -3,7 +3,7 @@ import { alertController, toastController } from "@ionic/vue";
import { cardOutline, checkmarkCircleOutline, documentTextOutline, logoAlipay, logoWechat, walletOutline } from "ionicons/icons";
const walletStore = useWalletStore();
const { balanceWallet } = storeToRefs(walletStore);
const balanceWallet = await walletStore.getWalletByType("balance");
interface PaymentMethod {
id: number;
@@ -144,12 +144,11 @@ async function handleSubmit() {
handler: async () => {
// TODO: 调用充值 API
await showToast("充值成功");
// 重置表单
rechargeAmount.value = "";
selectedPaymentType.value = null;
selectedPaymentId.value = null;
// 刷新余额
walletStore.syncBalanceWallet();
const data = await walletStore.getWalletByType("balance");
balanceWallet.value = data.value;
},
},
],
@@ -157,8 +156,6 @@ async function handleSubmit() {
await alert.present();
}
walletStore.syncBalanceWallet();
</script>
<template>
@@ -185,7 +182,7 @@ walletStore.syncBalanceWallet();
<span>当前账户余额</span>
</div>
<div class="balance-amount">
¥{{ balanceWallet?.balance?.toFixed(2) || '0.00' }}
¥{{ Number(balanceWallet?.available)?.toFixed(2) || '0.00' }}
</div>
</div>

View File

@@ -18,7 +18,7 @@ const [depth3Query] = useResetRef({
pageSize: 10,
});
// 统计数据
const { data: statistics } = safeClient(client.api.team.stats.get({ query: { currentTime: new Date().toISOString() } }));
const { data: statistics } = safeClient(client.api.team.stats.get());
const { data: depth1, execute: executeDepth1 } = safeClient(() => client.api.team.members.get({ query: { ...depth1Query.value } }));
const { data: depth2, execute: executeDepth2 } = safeClient(() => client.api.team.members.get({ query: { ...depth2Query.value } }));
const { data: depth3, execute: executeDepth3 } = safeClient(() => client.api.team.members.get({ query: { ...depth3Query.value } }));