feat: 添加 dayjs 依赖并优化钱包视图,增强日期格式化和交易记录显示

This commit is contained in:
2025-12-30 23:39:55 +08:00
parent 798be672bc
commit a6b870bc8f
3 changed files with 77 additions and 15 deletions

View File

@@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@primevue/themes": "^4.5.4", "@primevue/themes": "^4.5.4",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"dayjs": "^1.11.19",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primevue": "^4.5.4", "primevue": "^4.5.4",
@@ -1685,6 +1686,12 @@
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
"license": "MIT"
},
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",

View File

@@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"@primevue/themes": "^4.5.4", "@primevue/themes": "^4.5.4",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"dayjs": "^1.11.19",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primevue": "^4.5.4", "primevue": "^4.5.4",

View File

@@ -10,7 +10,7 @@
<div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-6"> <div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-6">
<div> <div>
<div class="text-blue-100 text-sm font-medium mb-1">账户余额 ()</div> <div class="text-blue-100 text-sm font-medium mb-1">账户余额 ()</div>
<div class="text-5xl font-bold font-mono">128.50</div> <div class="text-5xl font-bold font-mono">{{ balance.toFixed(2) }}</div>
</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 @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">
立即充值 立即充值
@@ -44,15 +44,28 @@
<h3 class="font-bold text-slate-900 mb-4">支付方式</h3> <h3 class="font-bold text-slate-900 mb-4">支付方式</h3>
<div class="flex gap-4 mb-8"> <div class="flex gap-4 mb-8">
<button class="flex items-center gap-2 px-6 py-3 border-2 border-blue-600 bg-blue-50 text-blue-700 rounded-lg font-medium cursor-pointer active:scale-95 transition-transform"> <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> 微信支付 <i class="pi pi-wechat text-xl text-green-600"></i> 微信支付
</button> </button>
<button class="flex items-center gap-2 px-6 py-3 border-2 border-slate-200 text-slate-600 rounded-lg hover:bg-white hover:border-slate-300 font-medium cursor-pointer active:scale-95 transition-all"> <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> 支付宝 <i class="pi pi-alipay text-xl text-blue-500"></i> 支付宝
</button> </button>
</div> </div>
<button 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]"> <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 }} 确认支付 ¥ {{ displayAmount }}
</button> </button>
</div> </div>
@@ -63,15 +76,15 @@
<i class="pi pi-history text-slate-400"></i> 交易明细 <i class="pi pi-history text-slate-400"></i> 交易明细
</h3> </h3>
<div class="space-y-4"> <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 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="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'"> <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> <i class="pi" :class="item.type === 'income' ? 'pi-plus' : 'pi-minus'"></i>
</div> </div>
<div> <div>
<div class="font-bold text-slate-900">{{ item.title }}</div> <div class="font-bold text-slate-900">{{ item.title || (item.type === 'income' ? '充值' : '消费') }}</div>
<div class="text-xs text-slate-400">{{ item.date }}</div> <div class="text-xs text-slate-400">{{ formatDate(item.date) }}</div>
</div> </div>
</div> </div>
<div class="font-mono font-bold text-lg" :class="item.type === 'income' ? 'text-green-600' : 'text-slate-900'"> <div class="font-mono font-bold text-lg" :class="item.type === 'income' ? 'text-green-600' : 'text-slate-900'">
@@ -79,6 +92,9 @@
</div> </div>
</div> </div>
</div> </div>
<div v-else class="text-center py-10 text-slate-400">
暂无交易记录
</div>
</div> </div>
<Toast /> <Toast />
@@ -86,24 +102,62 @@
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import Toast from 'primevue/toast'; import Toast from 'primevue/toast';
import { useToast } from 'primevue/usetoast'; import { useToast } from 'primevue/usetoast';
import { userApi } from '../../api/user';
import dayjs from 'dayjs';
const toast = useToast(); const toast = useToast();
const showRecharge = ref(false); const showRecharge = ref(false);
const selectedAmount = ref(50); const selectedAmount = ref(50);
const customAmount = ref(''); const customAmount = ref('');
const balance = ref(0);
const transactions = ref([]);
const loading = ref(false);
const paymentMethod = ref('wechat');
const displayAmount = computed(() => { const displayAmount = computed(() => {
return customAmount.value ? Number(customAmount.value).toFixed(2) : selectedAmount.value.toFixed(2); return customAmount.value ? Number(customAmount.value).toFixed(2) : selectedAmount.value.toFixed(2);
}); });
// Mock Data const formatDate = (date) => {
const transactions = ref([ return dayjs(date).format('YYYY-MM-DD HH:mm');
{ id: 1, title: '购买《霸王别姬》全本', date: '2025-12-24 14:30', amount: 9.90, type: 'expense' }, };
{ id: 2, title: '账户充值', date: '2025-12-20 10:00', amount: 100.00, type: 'income' },
{ id: 3, title: '购买周边商品', date: '2025-12-15 18:20', amount: 45.00, type: 'expense' }, const fetchWallet = async () => {
{ id: 4, title: '付费专栏订阅', date: '2025-12-10 09:15', amount: 19.90, type: 'expense' }, 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> </script>