163 lines
7.1 KiB
Vue
163 lines
7.1 KiB
Vue
<template>
|
|
<div class="bg-white rounded-xl shadow-sm border border-slate-100 min-h-[600px] p-8">
|
|
<h1 class="text-2xl font-bold text-slate-900 mb-8">我的钱包</h1>
|
|
|
|
<!-- Balance Card -->
|
|
<div class="bg-gradient-to-r from-blue-600 to-blue-500 rounded-2xl p-8 text-white shadow-lg mb-10 relative overflow-hidden">
|
|
<!-- Background Decor -->
|
|
<div class="absolute -right-10 -bottom-10 w-48 h-48 bg-white/10 rounded-full blur-2xl"></div>
|
|
|
|
<div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-6">
|
|
<div>
|
|
<div class="text-blue-100 text-sm font-medium mb-1">账户余额 (元)</div>
|
|
<div class="text-5xl font-bold font-mono">{{ balance.toFixed(2) }}</div>
|
|
</div>
|
|
<button @click="showRecharge = !showRecharge" class="px-8 py-3 bg-white text-blue-600 font-bold rounded-full shadow-sm hover:bg-blue-50 transition-all active:scale-95 cursor-pointer">
|
|
立即充值
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recharge Section (Collapsible) -->
|
|
<div v-if="showRecharge" class="mb-10 p-6 bg-slate-50 rounded-xl border border-slate-200 animate-in fade-in slide-in-from-top-4 duration-300">
|
|
<h3 class="font-bold text-slate-900 mb-4">充值金额</h3>
|
|
<div class="grid grid-cols-3 sm:grid-cols-4 gap-4 mb-6">
|
|
<button
|
|
v-for="amount in [10, 30, 50, 100, 200, 500]"
|
|
:key="amount"
|
|
@click="selectedAmount = amount; customAmount = ''"
|
|
class="h-14 rounded-lg border-2 font-bold text-lg transition-all cursor-pointer active:scale-95"
|
|
:class="selectedAmount === amount ? 'border-blue-600 bg-blue-50 text-blue-600' : 'border-slate-200 bg-white text-slate-600 hover:border-blue-300'"
|
|
>
|
|
{{ amount }}元
|
|
</button>
|
|
<div class="col-span-2 sm:col-span-2 relative">
|
|
<input
|
|
v-model="customAmount"
|
|
@focus="selectedAmount = null"
|
|
type="number"
|
|
placeholder="自定义金额"
|
|
class="w-full h-14 pl-4 pr-4 rounded-lg border-2 border-slate-200 focus:border-blue-600 focus:outline-none text-lg font-bold transition-colors"
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 class="font-bold text-slate-900 mb-4">支付方式</h3>
|
|
<div class="flex gap-4 mb-8">
|
|
<button
|
|
@click="paymentMethod = 'wechat'"
|
|
class="flex items-center gap-2 px-6 py-3 border-2 rounded-lg font-medium cursor-pointer active:scale-95 transition-transform"
|
|
:class="paymentMethod === 'wechat' ? 'border-blue-600 bg-blue-50 text-blue-700' : 'border-slate-200 text-slate-600 hover:bg-white hover:border-slate-300'"
|
|
>
|
|
<i class="pi pi-wechat text-xl text-green-600"></i> 微信支付
|
|
</button>
|
|
<button
|
|
@click="paymentMethod = 'alipay'"
|
|
class="flex items-center gap-2 px-6 py-3 border-2 rounded-lg font-medium cursor-pointer active:scale-95 transition-transform"
|
|
:class="paymentMethod === 'alipay' ? 'border-blue-600 bg-blue-50 text-blue-700' : 'border-slate-200 text-slate-600 hover:bg-white hover:border-slate-300'"
|
|
>
|
|
<i class="pi pi-alipay text-xl text-blue-500"></i> 支付宝
|
|
</button>
|
|
</div>
|
|
|
|
<button
|
|
@click="handleRecharge"
|
|
:disabled="loading"
|
|
class="w-full py-4 bg-blue-600 text-white rounded-xl font-bold text-lg hover:bg-blue-700 transition-all shadow-lg shadow-blue-200 cursor-pointer active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
<i v-if="loading" class="pi pi-spin pi-spinner mr-2"></i>
|
|
确认支付 ¥ {{ displayAmount }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Transaction History -->
|
|
<div>
|
|
<h3 class="text-xl font-bold text-slate-900 mb-6 flex items-center gap-2">
|
|
<i class="pi pi-history text-slate-400"></i> 交易明细
|
|
</h3>
|
|
|
|
<div v-if="transactions.length > 0" class="space-y-4">
|
|
<div v-for="item in transactions" :key="item.id" class="flex items-center justify-between p-4 border-b border-slate-50 hover:bg-slate-50 rounded-lg transition-colors">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-10 h-10 rounded-full flex items-center justify-center" :class="item.type === 'income' ? 'bg-green-100 text-green-600' : 'bg-slate-100 text-slate-500'">
|
|
<i class="pi" :class="item.type === 'income' ? 'pi-plus' : 'pi-minus'"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-bold text-slate-900">{{ item.title || (item.type === 'income' ? '充值' : '消费') }}</div>
|
|
<div class="text-xs text-slate-400">{{ formatDate(item.date) }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="font-mono font-bold text-lg" :class="item.type === 'income' ? 'text-green-600' : 'text-slate-900'">
|
|
{{ item.type === 'income' ? '+' : '-' }} {{ item.amount.toFixed(2) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="text-center py-10 text-slate-400">
|
|
暂无交易记录
|
|
</div>
|
|
</div>
|
|
|
|
<Toast />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue';
|
|
import Toast from 'primevue/toast';
|
|
import { useToast } from 'primevue/usetoast';
|
|
import { userApi } from '../../api/user';
|
|
import dayjs from 'dayjs';
|
|
|
|
const toast = useToast();
|
|
const showRecharge = ref(false);
|
|
const selectedAmount = ref(50);
|
|
const customAmount = ref('');
|
|
const balance = ref(0);
|
|
const transactions = ref([]);
|
|
const loading = ref(false);
|
|
const paymentMethod = ref('wechat');
|
|
|
|
const displayAmount = computed(() => {
|
|
return customAmount.value ? Number(customAmount.value).toFixed(2) : selectedAmount.value.toFixed(2);
|
|
});
|
|
|
|
const formatDate = (date) => {
|
|
return dayjs(date).format('YYYY-MM-DD HH:mm');
|
|
};
|
|
|
|
const fetchWallet = async () => {
|
|
try {
|
|
const res = await userApi.getWallet();
|
|
balance.value = res.balance || 0;
|
|
transactions.value = res.transactions || [];
|
|
} catch (error) {
|
|
console.error('Failed to fetch wallet:', error);
|
|
toast.add({ severity: 'error', summary: '错误', detail: '获取钱包信息失败', life: 3000 });
|
|
}
|
|
};
|
|
|
|
const handleRecharge = async () => {
|
|
const amount = Number(displayAmount.value);
|
|
if (!amount || amount <= 0) {
|
|
toast.add({ severity: 'warn', summary: '提示', detail: '请输入有效的充值金额', life: 3000 });
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
try {
|
|
await userApi.recharge({ amount: amount, method: paymentMethod.value });
|
|
toast.add({ severity: 'success', summary: '成功', detail: '充值成功', life: 3000 });
|
|
showRecharge.value = false;
|
|
fetchWallet(); // Refresh balance
|
|
} catch (error) {
|
|
console.error('Recharge failed:', error);
|
|
toast.add({ severity: 'error', summary: '错误', detail: error.message || '充值失败', life: 3000 });
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
fetchWallet();
|
|
});
|
|
</script> |