587 lines
13 KiB
Vue
587 lines
13 KiB
Vue
<template>
|
|
<scroll-view scroll-y class="mine-page">
|
|
<view class="mine-container">
|
|
<!-- 用户信息卡片 -->
|
|
<view class="user-card">
|
|
<view class="user-avatar">👤</view>
|
|
<view class="user-info">
|
|
<text class="user-name">{{ userInfo.nickname || userInfo.username || '用户' }}</text>
|
|
<text class="user-id">ID: {{ userInfo.id || '-' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 账户余额卡片 -->
|
|
<view class="balance-card" @click="openBalanceModal">
|
|
<view class="balance-header">
|
|
<text class="balance-title">账户余额</text>
|
|
<text class="balance-arrow">></text>
|
|
</view>
|
|
<text class="balance-amount" :class="account.balance >= 0 ? 'balance-positive' : 'balance-negative'">
|
|
¥{{ account.balance ? account.balance.toFixed(2) : '0.00' }}
|
|
</text>
|
|
<view class="balance-stats">
|
|
<view class="balance-stat-item">
|
|
<text class="balance-stat-label">总收入</text>
|
|
<text class="balance-stat-value income">¥{{ account.totalIncome ? account.totalIncome.toFixed(2) : '0.00' }}</text>
|
|
</view>
|
|
<view class="balance-stat-item">
|
|
<text class="balance-stat-label">总支出</text>
|
|
<text class="balance-stat-value expense">¥{{ account.totalExpense ? account.totalExpense.toFixed(2) : '0.00' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 预算卡片 -->
|
|
<view class="budget-card" @click="openBudgetModal">
|
|
<view class="budget-header">
|
|
<text class="budget-title">本月预算</text>
|
|
<text class="budget-arrow">></text>
|
|
</view>
|
|
<view class="budget-content">
|
|
<view class="budget-progress-wrapper">
|
|
<view class="budget-progress-bar">
|
|
<view class="budget-progress-fill" :style="{ width: budgetProgress + '%' }"></view>
|
|
</view>
|
|
<text class="budget-progress-text">{{ budgetProgress.toFixed(0) }}%</text>
|
|
</view>
|
|
<view class="budget-stats">
|
|
<view class="budget-stat-item">
|
|
<text class="budget-stat-label">预算</text>
|
|
<text class="budget-stat-value">¥{{ budget.amount ? budget.amount.toFixed(2) : '0.00' }}</text>
|
|
</view>
|
|
<view class="budget-stat-item">
|
|
<text class="budget-stat-label">已用</text>
|
|
<text class="budget-stat-value expense">¥{{ budget.usedAmount ? budget.usedAmount.toFixed(2) : '0.00' }}</text>
|
|
</view>
|
|
<view class="budget-stat-item">
|
|
<text class="budget-stat-label">剩余</text>
|
|
<text class="budget-stat-value" :class="budget.remainingAmount >= 0 ? 'positive' : 'negative'">
|
|
¥{{ budget.remainingAmount !== undefined ? budget.remainingAmount.toFixed(2) : '0.00' }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 功能菜单 -->
|
|
<view class="menu-section">
|
|
<view class="menu-item" @click="goToStatistics">
|
|
<view class="menu-icon-wrapper">
|
|
<text class="menu-icon">📊</text>
|
|
</view>
|
|
<text class="menu-text">统计分析</text>
|
|
<text class="menu-arrow">></text>
|
|
</view>
|
|
|
|
<view class="menu-item" @click="goToAccount">
|
|
<view class="menu-icon-wrapper">
|
|
<text class="menu-icon">💰</text>
|
|
</view>
|
|
<text class="menu-text">账户管理</text>
|
|
<text class="menu-arrow">></text>
|
|
</view>
|
|
|
|
<view class="menu-item" @click="goToBudget">
|
|
<view class="menu-icon-wrapper">
|
|
<text class="menu-icon">📝</text>
|
|
</view>
|
|
<text class="menu-text">预算设置</text>
|
|
<text class="menu-arrow">></text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 退出登录按钮 -->
|
|
<button class="logout-button" @click="logout">退出登录</button>
|
|
</view>
|
|
|
|
<!-- 编辑弹窗 -->
|
|
<EditModal
|
|
:visible="modalState.visible"
|
|
:title="modalState.title"
|
|
:placeholder="modalState.placeholder"
|
|
:currentValue="modalState.currentValue"
|
|
@confirm="handleModalConfirm"
|
|
@cancel="handleModalCancel"
|
|
/>
|
|
</scroll-view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { getAccount } from '../../api/account'
|
|
import { getBudget } from '../../api/budget'
|
|
import { updateAccountBalance, updateBudget } from '../../api/edit-modal'
|
|
import EditModal from '../../components/EditModal/EditModal.vue'
|
|
|
|
const userInfo = ref({
|
|
id: null,
|
|
username: '',
|
|
nickname: ''
|
|
})
|
|
|
|
const account = ref({
|
|
balance: 0,
|
|
totalIncome: 0,
|
|
totalExpense: 0
|
|
})
|
|
|
|
const budget = ref({
|
|
amount: 0,
|
|
usedAmount: 0,
|
|
remainingAmount: 0
|
|
})
|
|
|
|
const modalState = ref({
|
|
visible: false,
|
|
title: '',
|
|
placeholder: '',
|
|
currentValue: '',
|
|
type: '' // 'balance' 或 'budget'
|
|
})
|
|
|
|
const budgetProgress = computed(() => {
|
|
if (!budget.value.amount || budget.value.amount === 0) return 0
|
|
const progress = (budget.value.usedAmount / budget.value.amount) * 100
|
|
return Math.min(progress, 100)
|
|
})
|
|
|
|
const loadUserInfo = () => {
|
|
try {
|
|
const info = uni.getStorageSync('userInfo')
|
|
if (info) {
|
|
userInfo.value = typeof info === 'string' ? JSON.parse(info) : info
|
|
}
|
|
} catch (error) {
|
|
console.error('加载用户信息失败', error)
|
|
}
|
|
}
|
|
|
|
const loadAccount = async () => {
|
|
try {
|
|
const data = await getAccount()
|
|
if (data) {
|
|
account.value = data
|
|
}
|
|
} catch (error) {
|
|
console.error('加载账户失败', error)
|
|
}
|
|
}
|
|
|
|
const loadBudget = async () => {
|
|
try {
|
|
const data = await getBudget()
|
|
if (data) {
|
|
budget.value = data
|
|
}
|
|
} catch (error) {
|
|
console.error('加载预算失败', error)
|
|
}
|
|
}
|
|
|
|
const openBalanceModal = () => {
|
|
modalState.value = {
|
|
visible: true,
|
|
title: '编辑账户余额',
|
|
placeholder: '请输入新的账户余额',
|
|
currentValue: account.value.balance || 0,
|
|
type: 'balance'
|
|
}
|
|
}
|
|
|
|
const openBudgetModal = () => {
|
|
modalState.value = {
|
|
visible: true,
|
|
title: '编辑本月预算',
|
|
placeholder: '请输入新的预算金额',
|
|
currentValue: budget.value.amount || 0,
|
|
type: 'budget'
|
|
}
|
|
}
|
|
|
|
const handleModalConfirm = async (value) => {
|
|
try {
|
|
if (modalState.value.type === 'balance') {
|
|
await updateAccountBalance(parseFloat(value))
|
|
account.value.balance = parseFloat(value)
|
|
uni.showToast({
|
|
title: '账户余额已更新',
|
|
icon: 'success',
|
|
duration: 2000
|
|
})
|
|
} else if (modalState.value.type === 'budget') {
|
|
await updateBudget(parseFloat(value))
|
|
budget.value.amount = parseFloat(value)
|
|
uni.showToast({
|
|
title: '预算已更新',
|
|
icon: 'success',
|
|
duration: 2000
|
|
})
|
|
}
|
|
handleModalCancel()
|
|
} catch (error) {
|
|
console.error('更新失败', error)
|
|
uni.showToast({
|
|
title: '更新失败,请重试',
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
}
|
|
}
|
|
|
|
const handleModalCancel = () => {
|
|
modalState.value.visible = false
|
|
}
|
|
|
|
const goToStatistics = () => {
|
|
uni.switchTab({
|
|
url: '/pages/statistics/statistics'
|
|
})
|
|
}
|
|
|
|
const goToAccount = () => {
|
|
uni.navigateTo({
|
|
url: '/pages/account/account'
|
|
})
|
|
}
|
|
|
|
const goToBudget = () => {
|
|
uni.navigateTo({
|
|
url: '/pages/budget/budget'
|
|
})
|
|
}
|
|
|
|
const logout = () => {
|
|
uni.showModal({
|
|
title: '提示',
|
|
content: '确定要退出登录吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
uni.removeStorageSync('token')
|
|
uni.removeStorageSync('userInfo')
|
|
uni.reLaunch({
|
|
url: '/pages/login/login'
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadUserInfo()
|
|
loadAccount()
|
|
loadBudget()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.mine-page {
|
|
height: 100vh;
|
|
background: linear-gradient(135deg, #FFE5CC 0%, #FFD4A8 50%, #FFC08A 100%);
|
|
}
|
|
|
|
.mine-container {
|
|
min-height: 100vh;
|
|
padding: 20rpx;
|
|
padding-bottom: calc(170rpx + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
/* 用户信息卡片 */
|
|
.user-card {
|
|
background: linear-gradient(135deg, #C8956E, #F5D59E, #FFD700);
|
|
border-radius: 25rpx;
|
|
padding: 40rpx 30rpx;
|
|
margin-bottom: 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
border: 4rpx solid #5D4037;
|
|
box-shadow: 0 10rpx 30rpx rgba(93, 64, 55, 0.2);
|
|
}
|
|
|
|
.user-avatar {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 60rpx;
|
|
margin-right: 30rpx;
|
|
border: 3rpx solid #5D4037;
|
|
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.user-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.user-name {
|
|
display: block;
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #5D4037;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.user-id {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #8D6E63;
|
|
}
|
|
|
|
/* 账户余额卡片 */
|
|
.balance-card {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border-radius: 25rpx;
|
|
padding: 35rpx 30rpx;
|
|
margin-bottom: 20rpx;
|
|
border: 3rpx solid #C8956E;
|
|
box-shadow: 0 10rpx 30rpx rgba(200, 149, 110, 0.15);
|
|
}
|
|
|
|
.balance-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.balance-title {
|
|
font-size: 28rpx;
|
|
color: #8D6E63;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.balance-arrow {
|
|
font-size: 28rpx;
|
|
color: #C8956E;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.balance-amount {
|
|
font-size: 56rpx;
|
|
font-weight: bold;
|
|
display: block;
|
|
margin-bottom: 25rpx;
|
|
}
|
|
|
|
.balance-positive {
|
|
color: #FFD700;
|
|
}
|
|
|
|
.balance-negative {
|
|
color: #FA3534;
|
|
}
|
|
|
|
.balance-stats {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.balance-stat-item {
|
|
flex: 1;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
border-radius: 15rpx;
|
|
padding: 20rpx;
|
|
text-align: center;
|
|
border: 2rpx solid #C8956E;
|
|
}
|
|
|
|
.balance-stat-label {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #8D6E63;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.balance-stat-value {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #5D4037;
|
|
}
|
|
|
|
.balance-stat-value.income {
|
|
color: #19BE6B;
|
|
}
|
|
|
|
.balance-stat-value.expense {
|
|
color: #FA3534;
|
|
}
|
|
|
|
/* 预算卡片 */
|
|
.budget-card {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border-radius: 25rpx;
|
|
padding: 35rpx 30rpx;
|
|
margin-bottom: 20rpx;
|
|
border: 3rpx solid #C8956E;
|
|
box-shadow: 0 10rpx 30rpx rgba(200, 149, 110, 0.15);
|
|
}
|
|
|
|
.budget-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 25rpx;
|
|
}
|
|
|
|
.budget-title {
|
|
font-size: 28rpx;
|
|
color: #8D6E63;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.budget-arrow {
|
|
font-size: 28rpx;
|
|
color: #C8956E;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.budget-content {
|
|
|
|
}
|
|
|
|
.budget-progress-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.budget-progress-bar {
|
|
flex: 1;
|
|
height: 20rpx;
|
|
background: rgba(200, 149, 110, 0.2);
|
|
border-radius: 20rpx;
|
|
overflow: hidden;
|
|
margin-right: 15rpx;
|
|
}
|
|
|
|
.budget-progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #C8956E, #FFD700);
|
|
border-radius: 20rpx;
|
|
transition: width 0.5s ease-out;
|
|
}
|
|
|
|
.budget-progress-text {
|
|
font-size: 24rpx;
|
|
color: #5D4037;
|
|
font-weight: bold;
|
|
min-width: 60rpx;
|
|
text-align: right;
|
|
}
|
|
|
|
.budget-stats {
|
|
display: flex;
|
|
gap: 15rpx;
|
|
}
|
|
|
|
.budget-stat-item {
|
|
flex: 1;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
border-radius: 15rpx;
|
|
padding: 20rpx;
|
|
text-align: center;
|
|
border: 2rpx solid #C8956E;
|
|
}
|
|
|
|
.budget-stat-label {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #8D6E63;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.budget-stat-value {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #5D4037;
|
|
}
|
|
|
|
.budget-stat-value.positive {
|
|
color: #19BE6B;
|
|
}
|
|
|
|
.budget-stat-value.negative {
|
|
color: #FA3534;
|
|
}
|
|
|
|
.budget-stat-value.expense {
|
|
color: #FA3534;
|
|
}
|
|
|
|
/* 功能菜单 */
|
|
.menu-section {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border-radius: 25rpx;
|
|
padding: 20rpx 0;
|
|
margin-bottom: 20rpx;
|
|
border: 3rpx solid #C8956E;
|
|
box-shadow: 0 10rpx 30rpx rgba(200, 149, 110, 0.15);
|
|
}
|
|
|
|
.menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 30rpx 30rpx;
|
|
transition: background 0.3s;
|
|
}
|
|
|
|
.menu-item:active {
|
|
background: rgba(255, 215, 0, 0.1);
|
|
}
|
|
|
|
.menu-icon-wrapper {
|
|
width: 80rpx;
|
|
height: 80rpx;
|
|
background: linear-gradient(135deg, rgba(200, 149, 110, 0.2), rgba(255, 215, 0, 0.2));
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 25rpx;
|
|
border: 2rpx solid #C8956E;
|
|
}
|
|
|
|
.menu-icon {
|
|
font-size: 40rpx;
|
|
}
|
|
|
|
.menu-text {
|
|
flex: 1;
|
|
font-size: 30rpx;
|
|
color: #5D4037;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.menu-arrow {
|
|
font-size: 28rpx;
|
|
color: #C8956E;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* 退出登录按钮 */
|
|
.logout-button {
|
|
width: 100%;
|
|
height: 96rpx;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
color: #FA3534;
|
|
border-radius: 50rpx;
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
border: 3rpx solid #FA3534;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 8rpx 20rpx rgba(250, 53, 52, 0.2);
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.logout-button:active {
|
|
transform: scale(0.98);
|
|
background: rgba(250, 53, 52, 0.1);
|
|
}
|
|
</style>
|