303 lines
6.1 KiB
Vue
303 lines
6.1 KiB
Vue
<template>
|
|
<scroll-view scroll-y class="page-scroll">
|
|
<view class="container">
|
|
<view class="account-card">
|
|
<view class="card-header">
|
|
<text class="card-title">账户信息</text>
|
|
</view>
|
|
|
|
<view class="card-body">
|
|
<view class="balance-section">
|
|
<text class="balance-label">当前余额</text>
|
|
<text class="balance-amount" :class="account.balance >= 0 ? 'positive' : 'negative'">
|
|
¥{{ account.balance ? account.balance.toFixed(2) : '0.00' }}
|
|
</text>
|
|
</view>
|
|
|
|
<view class="stats-section">
|
|
<view class="stat-item">
|
|
<text class="stat-label">总收入</text>
|
|
<text class="stat-value income">¥{{ account.totalIncome ? account.totalIncome.toFixed(2) : '0.00' }}</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-label">总支出</text>
|
|
<text class="stat-value expense">¥{{ account.totalExpense ? account.totalExpense.toFixed(2) : '0.00' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-section">
|
|
<view class="form-item">
|
|
<text class="label">账户名称</text>
|
|
<input
|
|
v-model="form.name"
|
|
type="text"
|
|
placeholder="请输入账户名称"
|
|
|
|
/>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="label">初始余额</text>
|
|
<input
|
|
v-model="form.initialBalance"
|
|
type="text"
|
|
inputmode="decimal"
|
|
placeholder="请输入初始余额"
|
|
|
|
@input="onInitialBalanceInput"
|
|
/>
|
|
<text class="form-tip">初始余额 + 收入 - 支出 = 当前余额</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="card-footer">
|
|
<button class="save-btn" @click="saveAccount" :loading="saving">保存</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { getAccount, updateAccount } from '../../api/account'
|
|
|
|
const account = ref({
|
|
id: null,
|
|
name: '默认账户',
|
|
initialBalance: 0,
|
|
balance: 0,
|
|
totalIncome: 0,
|
|
totalExpense: 0
|
|
})
|
|
|
|
const form = ref({
|
|
name: '默认账户',
|
|
initialBalance: '0.00'
|
|
})
|
|
|
|
const saving = ref(false)
|
|
|
|
const loadAccount = async () => {
|
|
try {
|
|
const data = await getAccount()
|
|
if (data) {
|
|
account.value = data
|
|
form.value.name = data.name || '默认账户'
|
|
form.value.initialBalance = data.initialBalance ? data.initialBalance.toFixed(2) : '0.00'
|
|
}
|
|
} catch (error) {
|
|
console.error('加载账户失败', error)
|
|
uni.showToast({
|
|
title: '加载账户失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
|
|
const onInitialBalanceInput = (e) => {
|
|
let value = e.detail.value || ''
|
|
value = value.replace(/[^\d.]/g, '')
|
|
const parts = value.split('.')
|
|
if (parts.length > 2) {
|
|
value = parts[0] + '.' + parts.slice(1).join('')
|
|
}
|
|
if (parts.length === 2 && parts[1].length > 2) {
|
|
value = parts[0] + '.' + parts[1].substring(0, 2)
|
|
}
|
|
form.value.initialBalance = value
|
|
}
|
|
|
|
const saveAccount = async () => {
|
|
if (!form.value.name || form.value.name.trim() === '') {
|
|
uni.showToast({
|
|
title: '请输入账户名称',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
const initialBalance = parseFloat(form.value.initialBalance) || 0
|
|
|
|
saving.value = true
|
|
try {
|
|
const data = await updateAccount({
|
|
name: form.value.name.trim(),
|
|
initialBalance: initialBalance
|
|
})
|
|
|
|
if (data) {
|
|
account.value = data
|
|
uni.showToast({
|
|
title: '保存成功',
|
|
icon: 'success'
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error('保存账户失败', error)
|
|
uni.showToast({
|
|
title: error.message || '保存失败',
|
|
icon: 'none'
|
|
})
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadAccount()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-scroll {
|
|
height: 100vh;
|
|
width: 100%;
|
|
padding-bottom: calc(50px + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
.container {
|
|
min-height: calc(100vh - 100rpx);
|
|
background: #f5f5f5;
|
|
padding: 20rpx;
|
|
padding-bottom: calc(170rpx + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
.account-card {
|
|
background: #fff;
|
|
border-radius: 12rpx;
|
|
overflow: hidden;
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.card-header {
|
|
padding: 30rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.card-body {
|
|
padding: 30rpx;
|
|
}
|
|
|
|
.balance-section {
|
|
text-align: center;
|
|
padding: 40rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.balance-label {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.balance-amount {
|
|
font-size: 64rpx;
|
|
font-weight: bold;
|
|
display: block;
|
|
}
|
|
|
|
.balance-amount.positive {
|
|
color: #667eea;
|
|
}
|
|
|
|
.balance-amount.negative {
|
|
color: #fa3534;
|
|
}
|
|
|
|
.stats-section {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin-bottom: 30rpx;
|
|
padding-bottom: 30rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.stat-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-label {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.stat-value {
|
|
display: block;
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.stat-value.income {
|
|
color: #667eea;
|
|
}
|
|
|
|
.stat-value.expense {
|
|
color: #fa3534;
|
|
}
|
|
|
|
.form-section {
|
|
margin-top: 20rpx;
|
|
}
|
|
|
|
.form-item {
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.label {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.input {
|
|
width: 100%;
|
|
padding: 20rpx;
|
|
background: #f8f8f8;
|
|
border-radius: 8rpx;
|
|
font-size: 28rpx;
|
|
box-sizing: border-box;
|
|
border: none;
|
|
outline: none;
|
|
-webkit-appearance: none;
|
|
}
|
|
|
|
.form-tip {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-top: 10rpx;
|
|
}
|
|
|
|
.card-footer {
|
|
padding: 0 30rpx 30rpx;
|
|
}
|
|
|
|
.save-btn {
|
|
width: 100%;
|
|
height: 88rpx;
|
|
background: #667eea;
|
|
color: #fff;
|
|
border-radius: 12rpx;
|
|
font-size: 32rpx;
|
|
border: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
</style>
|
|
|