更新预算归档功能,添加归档总结和更新归档总结接口,优化预算统计逻辑,调整相关样式
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 34s
Docker Build & Deploy / Deploy to Production (push) Successful in 9s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 34s
Docker Build & Deploy / Deploy to Production (push) Successful in 9s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
This commit is contained in:
@@ -3,23 +3,36 @@
|
||||
<van-nav-bar title="预算管理" placeholder>
|
||||
<template #right>
|
||||
<van-icon
|
||||
v-if="activeTab !== BudgetCategory.Savings && uncoveredCategories.length > 0"
|
||||
v-if="activeTab !== BudgetCategory.Savings
|
||||
&& uncoveredCategories.length > 0
|
||||
&& !isArchive"
|
||||
name="warning-o"
|
||||
size="20"
|
||||
color="#ee0a24"
|
||||
style="margin-right: 12px"
|
||||
title="查看未覆盖预算的分类"
|
||||
@click="showUncoveredDetails = true"
|
||||
/>
|
||||
<van-icon
|
||||
v-if="isArchive"
|
||||
name="records-o"
|
||||
size="20"
|
||||
title="已归档月份总结"
|
||||
style="margin-right: 12px"
|
||||
@click="showArchiveSummary()"
|
||||
/>
|
||||
<van-icon
|
||||
v-if="activeTab !== BudgetCategory.Savings"
|
||||
name="plus"
|
||||
size="20"
|
||||
title="添加预算"
|
||||
@click="budgetEditRef.open({ category: activeTab })"
|
||||
/>
|
||||
<van-icon
|
||||
v-else
|
||||
name="setting-o"
|
||||
size="20"
|
||||
title="储蓄分类配置"
|
||||
@click="savingsConfigRef.open()"
|
||||
/>
|
||||
</template>
|
||||
@@ -43,7 +56,6 @@
|
||||
:progress-color="getProgressColor(budget)"
|
||||
:percent-class="{ 'warning': (budget.current / budget.limit) > 0.8 }"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
@switch-period="(dir) => handleSwitchPeriod(budget, dir)"
|
||||
@click="budgetEditRef.open({
|
||||
data: budget,
|
||||
isEditFlag: true,
|
||||
@@ -95,7 +107,6 @@
|
||||
:progress-color="getIncomeProgressColor(budget)"
|
||||
:percent-class="{ 'income': (budget.current / budget.limit) >= 1 }"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
@switch-period="(dir) => handleSwitchPeriod(budget, dir)"
|
||||
@click="budgetEditRef.open({
|
||||
data: budget,
|
||||
isEditFlag: true,
|
||||
@@ -142,7 +153,6 @@
|
||||
:percent-class="{ 'income': (budget.current / budget.limit) >= 1 }"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
style="margin: 0 12px 12px;"
|
||||
@switch-period="(dir) => handleSwitchPeriod(budget, dir)"
|
||||
>
|
||||
<template #amount-info>
|
||||
<div class="info-item">
|
||||
@@ -204,13 +214,44 @@
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
|
||||
<PopupContainer
|
||||
v-model="showSummaryPopup"
|
||||
title="月份归档总结"
|
||||
:subtitle="`${selectedDate.getFullYear()}年${selectedDate.getMonth() + 1}月`"
|
||||
height="50%"
|
||||
>
|
||||
<div style="padding: 16px;">
|
||||
<van-field
|
||||
v-model="archiveSummary"
|
||||
rows="6"
|
||||
autosize
|
||||
label="总结语"
|
||||
type="textarea"
|
||||
:placeholder="`请输入${selectedDate.getFullYear()}年${selectedDate.getMonth() + 1}月预算执行的总结或感悟...`"
|
||||
show-word-limit
|
||||
maxlength="500"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<van-button
|
||||
block
|
||||
round
|
||||
type="primary"
|
||||
:loading="isSavingSummary"
|
||||
@click="handleSaveSummary"
|
||||
>
|
||||
保存总结
|
||||
</van-button>
|
||||
</template>
|
||||
</PopupContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { showToast, showConfirmDialog } from 'vant'
|
||||
import { getBudgetList, deleteBudget, getBudgetStatistics, getCategoryStats, getUncoveredCategories } from '@/api/budget'
|
||||
import { getBudgetList, deleteBudget, getCategoryStats, getUncoveredCategories, getArchiveSummary, updateArchiveSummary } from '@/api/budget'
|
||||
import { BudgetPeriodType, BudgetCategory } from '@/constants/enums'
|
||||
import BudgetCard from '@/components/Budget/BudgetCard.vue'
|
||||
import BudgetSummary from '@/components/Budget/BudgetSummary.vue'
|
||||
@@ -226,6 +267,10 @@ const isRefreshing = ref(false)
|
||||
const showUncoveredDetails = ref(false)
|
||||
const uncoveredCategories = ref([])
|
||||
|
||||
const showSummaryPopup = ref(false)
|
||||
const archiveSummary = ref('')
|
||||
const isSavingSummary = ref(false)
|
||||
|
||||
const expenseBudgets = ref([])
|
||||
const incomeBudgets = ref([])
|
||||
const savingsBudgets = ref([])
|
||||
@@ -239,6 +284,12 @@ const activeTabTitle = computed(() => {
|
||||
return '达成'
|
||||
})
|
||||
|
||||
const isArchive = computed(() => {
|
||||
const now = new Date()
|
||||
return selectedDate.value.getFullYear() < now.getFullYear() ||
|
||||
(selectedDate.value.getFullYear() === now.getFullYear() && selectedDate.value.getMonth() < now.getMonth())
|
||||
})
|
||||
|
||||
watch(activeTab, async () => {
|
||||
await Promise.all([fetchCategoryStats(), fetchUncoveredCategories()])
|
||||
})
|
||||
@@ -378,30 +429,6 @@ const getIncomeProgressColor = (budget) => {
|
||||
return '#1989fa'
|
||||
}
|
||||
|
||||
const refDateMap = {}
|
||||
|
||||
const handleSwitchPeriod = async (budget, direction) => {
|
||||
let currentRefDate = refDateMap[budget.id] || new Date()
|
||||
const date = new Date(currentRefDate)
|
||||
|
||||
if (budget.type === BudgetPeriodType.Month) {
|
||||
date.setMonth(date.getMonth() + direction)
|
||||
} else if (budget.type === BudgetPeriodType.Year) {
|
||||
date.setFullYear(date.getFullYear() + direction)
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getBudgetStatistics(budget.id, date.toISOString())
|
||||
if (res.success) {
|
||||
refDateMap[budget.id] = date
|
||||
Object.assign(budget, res.data)
|
||||
}
|
||||
} catch (err) {
|
||||
showToast('加载历史统计失败')
|
||||
console.error('加载预算历史统计失败', err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = (budget) => {
|
||||
showConfirmDialog({
|
||||
title: '确认删除',
|
||||
@@ -419,6 +446,39 @@ const handleDelete = (budget) => {
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const showArchiveSummary = async () => {
|
||||
try {
|
||||
const res = await getArchiveSummary(selectedDate.value.toISOString())
|
||||
if (res.success) {
|
||||
archiveSummary.value = res.data || ''
|
||||
showSummaryPopup.value = true
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取总结失败', err)
|
||||
showToast('获取总结失败')
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveSummary = async () => {
|
||||
if (isSavingSummary.value) return
|
||||
isSavingSummary.value = true
|
||||
try {
|
||||
const res = await updateArchiveSummary({
|
||||
referenceDate: selectedDate.value.toISOString(),
|
||||
summary: archiveSummary.value
|
||||
})
|
||||
if (res.success) {
|
||||
showToast('已保存')
|
||||
showSummaryPopup.value = false
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('保存总结失败', err)
|
||||
showToast('保存总结失败')
|
||||
} finally {
|
||||
isSavingSummary.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user