AI-accounting-soft-uniApp/pages/index/index.vue
ni ziyi aa8ec6eae4 feat(新增账户管理功能):
在主页右上角点击图标跳转到账户管理页面
2025-12-13 21:58:00 +08:00

337 lines
6.7 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<view class="header">
<view class="header-top">
<text class="title">我的账单</text>
<view class="account-btn" @click="goToAccount">
<text class="account-icon">💰</text>
</view>
</view>
<view class="summary">
<view class="summary-item">
<text class="label">本月支出</text>
<text class="amount expense">¥{{ monthlyExpense.toFixed(2) }}</text>
</view>
<view class="summary-item">
<text class="label">本月收入</text>
<text class="amount income">¥{{ monthlyIncome.toFixed(2) }}</text>
</view>
</view>
</view>
<view class="bill-list">
<view v-for="(group, date) in groupedBills" :key="date" class="bill-group">
<view class="group-header">
<text class="date">{{ date }}</text>
<text class="total">¥{{ getGroupTotal(group).toFixed(2) }}</text>
</view>
<view v-for="bill in group" :key="bill.id" class="bill-item" @click="viewBill(bill)">
<view class="bill-icon">{{ bill.categoryIcon || '📦' }}</view>
<view class="bill-info">
<text class="bill-category">{{ bill.categoryName || '未分类' }}</text>
<text class="bill-desc" v-if="bill.description">{{ bill.description }}</text>
</view>
<text class="bill-amount" :class="bill.amount > 0 ? 'income' : 'expense'">
{{ bill.type > 0 ? '-' : '+' }}¥{{ Math.abs(bill.amount).toFixed(2) }}
</text>
</view>
</view>
<view v-if="bills.length === 0 && !loading" class="empty">
<text>暂无账单记录</text>
<text class="empty-tip">点击下方"记账"按钮添加账单</text>
</view>
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { getBills } from '../../api/bill'
import { getMonthlyStatistics } from '../../api/statistics'
import { formatDate } from '../../utils/date'
import { useUserStore } from '../../store/user'
const userStore = useUserStore()
const bills = ref([])
const monthlyIncome = ref(0)
const monthlyExpense = ref(0)
const loading = ref(false)
const groupedBills = computed(() => {
const groups = {}
bills.value.forEach(bill => {
const date = formatDate(bill.billDate)
if (!groups[date]) {
groups[date] = []
}
groups[date].push(bill)
})
return groups
})
const getGroupTotal = (group) => {
return group.reduce((sum, bill) => sum + parseFloat(bill.amount || 0), 0)
}
const loadBills = async () => {
loading.value = true
try {
const data = await getBills()
bills.value = data || []
} catch (error) {
console.error('加载账单失败', error)
if (error.message && error.message.includes('401')) {
// token过期跳转到登录页
userStore.logout()
uni.reLaunch({
url: '/pages/login/login'
})
}
} finally {
loading.value = false
}
}
const loadMonthlyStatistics = async () => {
try {
const now = new Date()
const data = await getMonthlyStatistics(now.getFullYear(), now.getMonth() + 1)
monthlyIncome.value = data.totalIncome || 0
monthlyExpense.value = data.totalExpense || 0
} catch (error) {
console.error('加载统计失败', error)
}
}
const viewBill = (bill) => {
// 跳转到账单编辑页面
uni.navigateTo({
url: `/pages/bill/detail?id=${bill.id}`
})
}
const goToAccount = () => {
// 跳转到账户管理页面
uni.navigateTo({
url: '/pages/account/account'
})
}
const onPullDownRefresh = () => {
loadBills()
loadMonthlyStatistics()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
}
onMounted(() => {
// 检查登录状态
if (!userStore.isLoggedIn) {
uni.reLaunch({
url: '/pages/login/login'
})
return
}
loadBills()
loadMonthlyStatistics()
})
// 监听刷新事件
uni.$on('refreshBills', () => {
loadBills()
loadMonthlyStatistics()
})
</script>
<script>
// uni-app 页面生命周期钩子
export default {
onShow() {
// 页面显示时刷新 - 通过事件触发
uni.$emit('refreshBills')
},
onPullDownRefresh() {
// 下拉刷新 - 通过事件触发
uni.$emit('refreshBills')
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
}
}
</script>
<style scoped>
.container {
min-height: calc(100vh - 100rpx);
background: #f5f5f5;
padding-bottom: calc(170rpx + env(safe-area-inset-bottom));
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40rpx 30rpx;
color: #fff;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.title {
font-size: 40rpx;
font-weight: bold;
display: block;
}
.account-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
cursor: pointer;
transition: all 0.3s;
}
.account-btn:active {
background: rgba(255, 255, 255, 0.3);
transform: scale(0.95);
}
.account-icon {
font-size: 36rpx;
}
.summary {
display: flex;
justify-content: space-around;
}
.summary-item {
text-align: center;
}
.label {
display: block;
font-size: 24rpx;
opacity: 0.8;
margin-bottom: 10rpx;
}
.amount {
display: block;
font-size: 36rpx;
font-weight: bold;
}
.bill-list {
padding: 20rpx;
}
.bill-group {
background: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.group-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background: #f8f8f8;
border-bottom: 1rpx solid #eee;
}
.date {
font-size: 28rpx;
color: #666;
}
.total {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.bill-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.bill-item:last-child {
border-bottom: none;
}
.bill-icon {
font-size: 48rpx;
margin-right: 20rpx;
}
.bill-info {
flex: 1;
display: flex;
flex-direction: column;
}
.bill-category {
font-size: 30rpx;
color: #333;
margin-bottom: 8rpx;
}
.bill-desc {
font-size: 24rpx;
color: #999;
}
.bill-amount {
font-size: 32rpx;
font-weight: bold;
}
.bill-amount.income {
color: #19be6b;
}
.bill-amount.expense {
color: #fa3534;
}
.empty {
text-align: center;
padding: 100rpx 0;
color: #999;
display: flex;
flex-direction: column;
align-items: center;
}
.empty-tip {
font-size: 24rpx;
margin-top: 20rpx;
color: #ccc;
}
.loading {
text-align: center;
padding: 40rpx 0;
color: #999;
}
</style>