大量的代码格式化
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Failing after 1m10s
Docker Build & Deploy / Deploy to Production (push) Has been skipped
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Failing after 1m10s
Docker Build & Deploy / Deploy to Production (push) Has been skipped
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<div class="page-container-flex">
|
||||
<van-nav-bar title="预算管理" placeholder>
|
||||
<van-nav-bar
|
||||
title="预算管理"
|
||||
placeholder
|
||||
>
|
||||
<template #right>
|
||||
<van-icon
|
||||
v-if="activeTab !== BudgetCategory.Savings
|
||||
&& uncoveredCategories.length > 0
|
||||
&& !isArchive"
|
||||
name="warning-o"
|
||||
size="20"
|
||||
<van-icon
|
||||
v-if="
|
||||
activeTab !== BudgetCategory.Savings && uncoveredCategories.length > 0 && !isArchive
|
||||
"
|
||||
name="warning-o"
|
||||
size="20"
|
||||
color="var(--van-danger-color)"
|
||||
style="margin-right: 12px"
|
||||
title="查看未覆盖预算的分类"
|
||||
@click="showUncoveredDetails = true"
|
||||
@click="showUncoveredDetails = true"
|
||||
/>
|
||||
<van-icon
|
||||
v-if="isArchive"
|
||||
@@ -22,193 +25,289 @@
|
||||
style="margin-right: 12px"
|
||||
@click="showArchiveSummary()"
|
||||
/>
|
||||
<van-icon
|
||||
<van-icon
|
||||
v-if="activeTab !== BudgetCategory.Savings"
|
||||
name="plus"
|
||||
size="20"
|
||||
name="plus"
|
||||
size="20"
|
||||
title="添加预算"
|
||||
@click="budgetEditRef.open({ category: activeTab })"
|
||||
@click="budgetEditRef.open({ category: activeTab })"
|
||||
/>
|
||||
<van-icon
|
||||
v-else
|
||||
name="setting-o"
|
||||
size="20"
|
||||
name="setting-o"
|
||||
size="20"
|
||||
title="储蓄分类配置"
|
||||
@click="savingsConfigRef.open()"
|
||||
/>
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
|
||||
<van-tabs v-model:active="activeTab" type="card" class="budget-tabs" style="margin: 12px 4px;">
|
||||
<van-tab title="支出" :name="BudgetCategory.Expense">
|
||||
<BudgetSummary
|
||||
<van-tabs
|
||||
v-model:active="activeTab"
|
||||
type="card"
|
||||
class="budget-tabs"
|
||||
style="margin: 12px 4px"
|
||||
>
|
||||
<van-tab
|
||||
title="支出"
|
||||
:name="BudgetCategory.Expense"
|
||||
>
|
||||
<BudgetSummary
|
||||
v-if="activeTab !== BudgetCategory.Savings"
|
||||
v-model:date="selectedDate"
|
||||
:stats="overallStats"
|
||||
:title="activeTabTitle"
|
||||
:get-value-class="getValueClass"
|
||||
:stats="overallStats"
|
||||
:title="activeTabTitle"
|
||||
:get-value-class="getValueClass"
|
||||
/>
|
||||
<van-pull-refresh v-model="isRefreshing" class="scroll-content" @refresh="onRefresh">
|
||||
<van-pull-refresh
|
||||
v-model="isRefreshing"
|
||||
class="scroll-content"
|
||||
@refresh="onRefresh"
|
||||
>
|
||||
<div class="budget-list">
|
||||
<template v-if="expenseBudgets?.length > 0">
|
||||
<van-swipe-cell v-for="budget in expenseBudgets" :key="budget.id">
|
||||
<BudgetCard
|
||||
<van-swipe-cell
|
||||
v-for="budget in expenseBudgets"
|
||||
:key="budget.id"
|
||||
>
|
||||
<BudgetCard
|
||||
:budget="budget"
|
||||
:progress-color="getProgressColor(budget)"
|
||||
:percent-class="{ 'warning': (budget.current / budget.limit) > 0.8 }"
|
||||
:percent-class="{
|
||||
warning: budget.current / budget.limit > 0.8
|
||||
}"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
@click="budgetEditRef.open({
|
||||
data: budget,
|
||||
isEditFlag: true,
|
||||
category: budget.category
|
||||
})"
|
||||
@click="
|
||||
budgetEditRef.open({
|
||||
data: budget,
|
||||
isEditFlag: true,
|
||||
category: budget.category
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #amount-info>
|
||||
<div class="info-item">
|
||||
<div class="label">已支出</div>
|
||||
<div class="value expense">¥{{ formatMoney(budget.current) }}</div>
|
||||
<div class="label">
|
||||
已支出
|
||||
</div>
|
||||
<div class="value expense">
|
||||
¥{{ formatMoney(budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">预算</div>
|
||||
<div class="value">¥{{ formatMoney(budget.limit) }}</div>
|
||||
<div class="label">
|
||||
预算
|
||||
</div>
|
||||
<div class="value">
|
||||
¥{{ formatMoney(budget.limit) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">余额</div>
|
||||
<div class="value" :class="budget.limit - budget.current >= 0 ? 'income' : 'expense'">
|
||||
<div class="label">
|
||||
余额
|
||||
</div>
|
||||
<div
|
||||
class="value"
|
||||
:class="budget.limit - budget.current >= 0 ? 'income' : 'expense'"
|
||||
>
|
||||
¥{{ formatMoney(budget.limit - budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BudgetCard>
|
||||
<template #right>
|
||||
<van-button square text="删除" type="danger" class="delete-button" @click="handleDelete(budget)" />
|
||||
<van-button
|
||||
square
|
||||
text="删除"
|
||||
type="danger"
|
||||
class="delete-button"
|
||||
@click="handleDelete(budget)"
|
||||
/>
|
||||
</template>
|
||||
</van-swipe-cell>
|
||||
</template>
|
||||
<van-empty v-else description="暂无支出预算" />
|
||||
<van-empty
|
||||
v-else
|
||||
description="暂无支出预算"
|
||||
/>
|
||||
</div>
|
||||
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))"></div>
|
||||
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))" />
|
||||
</van-pull-refresh>
|
||||
</van-tab>
|
||||
|
||||
<van-tab title="收入" :name="BudgetCategory.Income">
|
||||
<BudgetSummary
|
||||
<van-tab
|
||||
title="收入"
|
||||
:name="BudgetCategory.Income"
|
||||
>
|
||||
<BudgetSummary
|
||||
v-if="activeTab !== BudgetCategory.Savings"
|
||||
v-model:date="selectedDate"
|
||||
:stats="overallStats"
|
||||
:title="activeTabTitle"
|
||||
:get-value-class="getValueClass"
|
||||
:stats="overallStats"
|
||||
:title="activeTabTitle"
|
||||
:get-value-class="getValueClass"
|
||||
/>
|
||||
<van-pull-refresh v-model="isRefreshing" class="scroll-content" @refresh="onRefresh">
|
||||
<van-pull-refresh
|
||||
v-model="isRefreshing"
|
||||
class="scroll-content"
|
||||
@refresh="onRefresh"
|
||||
>
|
||||
<div class="budget-list">
|
||||
<template v-if="incomeBudgets?.length > 0">
|
||||
<van-swipe-cell v-for="budget in incomeBudgets" :key="budget.id">
|
||||
<BudgetCard
|
||||
<van-swipe-cell
|
||||
v-for="budget in incomeBudgets"
|
||||
:key="budget.id"
|
||||
>
|
||||
<BudgetCard
|
||||
:budget="budget"
|
||||
:progress-color="getProgressColor(budget)"
|
||||
:percent-class="{ 'income': (budget.current / budget.limit) >= 1 }"
|
||||
:percent-class="{
|
||||
income: budget.current / budget.limit >= 1
|
||||
}"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
@click="budgetEditRef.open({
|
||||
data: budget,
|
||||
isEditFlag: true,
|
||||
category: budget.category
|
||||
})"
|
||||
@click="
|
||||
budgetEditRef.open({
|
||||
data: budget,
|
||||
isEditFlag: true,
|
||||
category: budget.category
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #amount-info>
|
||||
<div class="info-item">
|
||||
<div class="label">已收入</div>
|
||||
<div class="value income">¥{{ formatMoney(budget.current) }}</div>
|
||||
<div class="label">
|
||||
已收入
|
||||
</div>
|
||||
<div class="value income">
|
||||
¥{{ formatMoney(budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">目标</div>
|
||||
<div class="value">¥{{ formatMoney(budget.limit) }}</div>
|
||||
<div class="label">
|
||||
目标
|
||||
</div>
|
||||
<div class="value">
|
||||
¥{{ formatMoney(budget.limit) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">差额</div>
|
||||
<div class="value" :class="budget.current >= budget.limit ? 'income' : 'expense'">
|
||||
<div class="label">
|
||||
差额
|
||||
</div>
|
||||
<div
|
||||
class="value"
|
||||
:class="budget.current >= budget.limit ? 'income' : 'expense'"
|
||||
>
|
||||
¥{{ formatMoney(Math.abs(budget.limit - budget.current)) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BudgetCard>
|
||||
<template #right>
|
||||
<van-button square text="删除" type="danger" class="delete-button" @click="handleDelete(budget)" />
|
||||
<van-button
|
||||
square
|
||||
text="删除"
|
||||
type="danger"
|
||||
class="delete-button"
|
||||
@click="handleDelete(budget)"
|
||||
/>
|
||||
</template>
|
||||
</van-swipe-cell>
|
||||
</template>
|
||||
<van-empty v-else description="暂无收入预算" />
|
||||
<van-empty
|
||||
v-else
|
||||
description="暂无收入预算"
|
||||
/>
|
||||
</div>
|
||||
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))"></div>
|
||||
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))" />
|
||||
</van-pull-refresh>
|
||||
</van-tab>
|
||||
|
||||
<van-tab title="存款" :name="BudgetCategory.Savings">
|
||||
<van-pull-refresh v-model="isRefreshing" class="scroll-content" style="padding-top:4px" @refresh="onRefresh">
|
||||
<van-tab
|
||||
title="存款"
|
||||
:name="BudgetCategory.Savings"
|
||||
>
|
||||
<van-pull-refresh
|
||||
v-model="isRefreshing"
|
||||
class="scroll-content"
|
||||
style="padding-top: 4px"
|
||||
@refresh="onRefresh"
|
||||
>
|
||||
<div class="budget-list">
|
||||
<template v-if="savingsBudgets?.length > 0">
|
||||
<BudgetCard
|
||||
v-for="budget in savingsBudgets"
|
||||
<BudgetCard
|
||||
v-for="budget in savingsBudgets"
|
||||
:key="budget.id"
|
||||
:budget="budget"
|
||||
:progress-color="getProgressColor(budget)"
|
||||
:percent-class="{ 'income': (budget.current / budget.limit) >= 1 }"
|
||||
:percent-class="{ income: budget.current / budget.limit >= 1 }"
|
||||
:period-label="getPeriodLabel(budget.type)"
|
||||
style="margin: 0 12px 12px;"
|
||||
style="margin: 0 12px 12px"
|
||||
>
|
||||
<template #amount-info>
|
||||
<div class="info-item">
|
||||
<div class="label">已存</div>
|
||||
<div class="value income">¥{{ formatMoney(budget.current) }}</div>
|
||||
<div class="label">
|
||||
已存
|
||||
</div>
|
||||
<div class="value income">
|
||||
¥{{ formatMoney(budget.current) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">目标</div>
|
||||
<div class="value">¥{{ formatMoney(budget.limit) }}</div>
|
||||
<div class="label">
|
||||
目标
|
||||
</div>
|
||||
<div class="value">
|
||||
¥{{ formatMoney(budget.limit) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="label">还差</div>
|
||||
<div class="label">
|
||||
还差
|
||||
</div>
|
||||
<div class="value expense">
|
||||
¥{{ formatMoney(Math.max(0, budget.limit - budget.current)) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<div class="card-footer-actions">
|
||||
<van-button
|
||||
size="small"
|
||||
icon="arrow-left"
|
||||
plain
|
||||
<van-button
|
||||
size="small"
|
||||
icon="arrow-left"
|
||||
plain
|
||||
type="primary"
|
||||
@click.stop="handleSavingsNav(budget, -1)"
|
||||
>
|
||||
</van-button>
|
||||
/>
|
||||
<span class="current-date-label">
|
||||
{{ getSavingsDateLabel(budget) }}
|
||||
</span>
|
||||
<van-button
|
||||
size="small"
|
||||
icon="arrow"
|
||||
plain
|
||||
<van-button
|
||||
size="small"
|
||||
icon="arrow"
|
||||
plain
|
||||
type="primary"
|
||||
icon-position="right"
|
||||
:disabled="disabledSavingsNextNav(budget)"
|
||||
@click.stop="handleSavingsNav(budget, 1)"
|
||||
>
|
||||
</van-button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</BudgetCard>
|
||||
</template>
|
||||
<van-empty v-else description="暂无存款计划" />
|
||||
<van-empty
|
||||
v-else
|
||||
description="暂无存款计划"
|
||||
/>
|
||||
</div>
|
||||
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))"></div>
|
||||
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))" />
|
||||
</van-pull-refresh>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
|
||||
<BudgetEditPopup
|
||||
<BudgetEditPopup
|
||||
ref="budgetEditRef"
|
||||
@success="fetchBudgetList"
|
||||
@success="fetchBudgetList"
|
||||
/>
|
||||
<SavingsConfigPopup
|
||||
ref="savingsConfigRef"
|
||||
@@ -222,21 +321,37 @@
|
||||
height="60%"
|
||||
>
|
||||
<div class="uncovered-list">
|
||||
<div v-for="item in uncoveredCategories" :key="item.category" class="uncovered-item">
|
||||
<div
|
||||
v-for="item in uncoveredCategories"
|
||||
:key="item.category"
|
||||
class="uncovered-item"
|
||||
>
|
||||
<div class="item-left">
|
||||
<div class="category-name">{{ item.category }}</div>
|
||||
<div class="transaction-count">{{ item.transactionCount }} 笔记录</div>
|
||||
<div class="category-name">
|
||||
{{ item.category }}
|
||||
</div>
|
||||
<div class="transaction-count">
|
||||
{{ item.transactionCount }} 笔记录
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<div class="item-amount" :class="activeTab === BudgetCategory.Expense ? 'expense' : 'income'">
|
||||
<div
|
||||
class="item-amount"
|
||||
:class="activeTab === BudgetCategory.Expense ? 'expense' : 'income'"
|
||||
>
|
||||
¥{{ formatMoney(item.totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<van-button block round type="primary" @click="showUncoveredDetails = false">
|
||||
<van-button
|
||||
block
|
||||
round
|
||||
type="primary"
|
||||
@click="showUncoveredDetails = false"
|
||||
>
|
||||
我知道了
|
||||
</van-button>
|
||||
</template>
|
||||
@@ -248,10 +363,13 @@
|
||||
:subtitle="`${selectedDate.getFullYear()}年${selectedDate.getMonth() + 1}月`"
|
||||
height="70%"
|
||||
>
|
||||
<div style="padding: 16px;">
|
||||
<div style="padding: 16px">
|
||||
<div
|
||||
class="rich-html-content"
|
||||
v-html="archiveSummary || '<p style=\'text-align:center;color:var(--van-text-color-3)\'>暂无总结</p>'"
|
||||
class="rich-html-content"
|
||||
v-html="
|
||||
archiveSummary ||
|
||||
'<p style=\'text-align:center;color:var(--van-text-color-3)\'>暂无总结</p>'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</PopupContainer>
|
||||
@@ -261,7 +379,14 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { showToast, showConfirmDialog } from 'vant'
|
||||
import { getBudgetList, deleteBudget, getCategoryStats, getUncoveredCategories, getArchiveSummary, updateArchiveSummary, getSavingsBudget } from '@/api/budget'
|
||||
import {
|
||||
getBudgetList,
|
||||
deleteBudget,
|
||||
getCategoryStats,
|
||||
getUncoveredCategories,
|
||||
getArchiveSummary,
|
||||
getSavingsBudget
|
||||
} from '@/api/budget'
|
||||
import { BudgetPeriodType, BudgetCategory } from '@/constants/enums'
|
||||
import BudgetCard from '@/components/Budget/BudgetCard.vue'
|
||||
import BudgetSummary from '@/components/Budget/BudgetSummary.vue'
|
||||
@@ -279,7 +404,6 @@ const uncoveredCategories = ref([])
|
||||
|
||||
const showSummaryPopup = ref(false)
|
||||
const archiveSummary = ref('')
|
||||
const isSavingSummary = ref(false)
|
||||
|
||||
const expenseBudgets = ref([])
|
||||
const incomeBudgets = ref([])
|
||||
@@ -290,14 +414,19 @@ const overallStats = ref({
|
||||
})
|
||||
|
||||
const activeTabTitle = computed(() => {
|
||||
if (activeTab.value === BudgetCategory.Expense) return '使用'
|
||||
if (activeTab.value === BudgetCategory.Expense) {
|
||||
return '使用'
|
||||
}
|
||||
return '达成'
|
||||
})
|
||||
|
||||
const isArchive = computed(() => {
|
||||
const now = new Date()
|
||||
return selectedDate.value.getFullYear() < now.getFullYear() ||
|
||||
(selectedDate.value.getFullYear() === now.getFullYear() && selectedDate.value.getMonth() < now.getMonth())
|
||||
return (
|
||||
selectedDate.value.getFullYear() < now.getFullYear() ||
|
||||
(selectedDate.value.getFullYear() === now.getFullYear() &&
|
||||
selectedDate.value.getMonth() < now.getMonth())
|
||||
)
|
||||
})
|
||||
|
||||
watch(activeTab, async () => {
|
||||
@@ -305,23 +434,29 @@ watch(activeTab, async () => {
|
||||
})
|
||||
|
||||
watch(selectedDate, async () => {
|
||||
await Promise.all([
|
||||
fetchBudgetList(),
|
||||
fetchCategoryStats(),
|
||||
fetchUncoveredCategories()
|
||||
])
|
||||
await Promise.all([fetchBudgetList(), fetchCategoryStats(), fetchUncoveredCategories()])
|
||||
})
|
||||
|
||||
const getValueClass = (rate) => {
|
||||
const numRate = parseFloat(rate)
|
||||
if (numRate === 0) return ''
|
||||
if (numRate === 0) {
|
||||
return ''
|
||||
}
|
||||
if (activeTab.value === BudgetCategory.Expense) {
|
||||
if (numRate >= 100) return 'expense'
|
||||
if (numRate >= 80) return 'warning'
|
||||
if (numRate >= 100) {
|
||||
return 'expense'
|
||||
}
|
||||
if (numRate >= 80) {
|
||||
return 'warning'
|
||||
}
|
||||
return 'income'
|
||||
} else {
|
||||
if (numRate >= 100) return 'income'
|
||||
if (numRate >= 80) return 'warning'
|
||||
if (numRate >= 100) {
|
||||
return 'income'
|
||||
}
|
||||
if (numRate >= 80) {
|
||||
return 'warning'
|
||||
}
|
||||
return 'expense'
|
||||
}
|
||||
}
|
||||
@@ -331,9 +466,9 @@ const fetchBudgetList = async () => {
|
||||
const res = await getBudgetList(selectedDate.value.toISOString())
|
||||
if (res.success) {
|
||||
const data = res.data || []
|
||||
expenseBudgets.value = data.filter(b => b.category === BudgetCategory.Expense)
|
||||
incomeBudgets.value = data.filter(b => b.category === BudgetCategory.Income)
|
||||
savingsBudgets.value = data.filter(b => b.category === BudgetCategory.Savings)
|
||||
expenseBudgets.value = data.filter((b) => b.category === BudgetCategory.Expense)
|
||||
incomeBudgets.value = data.filter((b) => b.category === BudgetCategory.Income)
|
||||
savingsBudgets.value = data.filter((b) => b.category === BudgetCategory.Savings)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载预算列表失败', err)
|
||||
@@ -393,18 +528,17 @@ const fetchUncoveredCategories = async () => {
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await Promise.all([
|
||||
fetchBudgetList(),
|
||||
fetchCategoryStats(),
|
||||
fetchUncoveredCategories()
|
||||
])
|
||||
await Promise.all([fetchBudgetList(), fetchCategoryStats(), fetchUncoveredCategories()])
|
||||
} catch (err) {
|
||||
console.error('获取初始化数据失败', err)
|
||||
}
|
||||
})
|
||||
|
||||
const formatMoney = (val) => {
|
||||
return parseFloat(val || 0).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 })
|
||||
return parseFloat(val || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
})
|
||||
}
|
||||
|
||||
const getPeriodLabel = (type) => {
|
||||
@@ -427,10 +561,12 @@ const getPeriodLabel = (type) => {
|
||||
}
|
||||
|
||||
const getProgressColor = (budget) => {
|
||||
if (!budget.limit || budget.limit === 0) return 'var(--van-primary-color)'
|
||||
|
||||
if (!budget.limit || budget.limit === 0) {
|
||||
return 'var(--van-primary-color)'
|
||||
}
|
||||
|
||||
const ratio = Math.min(Math.max(budget.current / budget.limit, 0), 1)
|
||||
|
||||
|
||||
// 颜色插值辅助函数
|
||||
const interpolate = (start, end, t) => {
|
||||
return Math.round(start + (end - start) * t)
|
||||
@@ -441,7 +577,7 @@ const getProgressColor = (budget) => {
|
||||
// 找到当前值所在的区间
|
||||
let startStop = stops[0]
|
||||
let endStop = stops[stops.length - 1]
|
||||
|
||||
|
||||
for (let i = 0; i < stops.length - 1; i++) {
|
||||
if (value >= stops[i].p && value <= stops[i + 1].p) {
|
||||
startStop = stops[i]
|
||||
@@ -449,28 +585,28 @@ const getProgressColor = (budget) => {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 计算区间内的相对比例
|
||||
const range = endStop.p - startStop.p
|
||||
const t = (value - startStop.p) / range
|
||||
|
||||
|
||||
const r = interpolate(startStop.c.r, endStop.c.r, t)
|
||||
const g = interpolate(startStop.c.g, endStop.c.g, t)
|
||||
const b = interpolate(startStop.c.b, endStop.c.b, t)
|
||||
|
||||
|
||||
return `rgb(${r}, ${g}, ${b})`
|
||||
}
|
||||
|
||||
let stops
|
||||
|
||||
|
||||
if (budget.category === BudgetCategory.Expense) {
|
||||
// 支出: 这是一个"安全 -> 警示 -> 危险"的过程
|
||||
// 使用 蓝绿色 -> 黄色 -> 橙红色的渐变,更加自然且具有高级感
|
||||
stops = [
|
||||
{ p: 0, c: { r: 64, g: 169, b: 255 } }, // 0% 清新的蓝色 (Safe/Fresh)
|
||||
{ p: 0.4, c: { r: 54, g: 207, b: 201 } }, // 40% 青色过渡
|
||||
{ p: 0.7, c: { r: 250, g: 173, b: 20 } }, // 70% 温暖的黄色 (Warning)
|
||||
{ p: 1, c: { r: 255, g: 77, b: 79 } } // 100% 柔和的红色 (Danger)
|
||||
{ p: 0, c: { r: 64, g: 169, b: 255 } }, // 0% 清新的蓝色 (Safe/Fresh)
|
||||
{ p: 0.4, c: { r: 54, g: 207, b: 201 } }, // 40% 青色过渡
|
||||
{ p: 0.7, c: { r: 250, g: 173, b: 20 } }, // 70% 温暖的黄色 (Warning)
|
||||
{ p: 1, c: { r: 255, g: 77, b: 79 } } // 100% 柔和的红色 (Danger)
|
||||
]
|
||||
} else {
|
||||
// 收入/存款: 这是一个"开始 -> 积累 -> 达成"的过程
|
||||
@@ -481,17 +617,17 @@ const getProgressColor = (budget) => {
|
||||
// { p: 0.7, c: { r: 115, g: 209, b: 61 } }, // 70% 草绿 (Good)
|
||||
// { p: 1, c: { r: 35, g: 120, b: 4 } } // 100% 深绿 (Excellent)
|
||||
// ]
|
||||
|
||||
|
||||
// 如果用户喜欢"红->蓝"的逻辑,可以尝试这种"红->白->蓝"的冷暖过渡:
|
||||
stops = [
|
||||
{ p: 0, c: { r: 245, g: 34, b: 45 } }, // 深红
|
||||
{ p: 0, c: { r: 245, g: 34, b: 45 } }, // 深红
|
||||
{ p: 0.45, c: { r: 255, g: 204, b: 204 } }, // 浅红
|
||||
{ p: 0.5, c: { r: 240, g: 242, b: 245 } }, // 中性灰白
|
||||
{ p: 0.5, c: { r: 240, g: 242, b: 245 } }, // 中性灰白
|
||||
{ p: 0.55, c: { r: 186, g: 231, b: 255 } }, // 浅蓝
|
||||
{ p: 1, c: { r: 24, g: 144, b: 255 } } // 深蓝
|
||||
{ p: 1, c: { r: 24, g: 144, b: 255 } } // 深蓝
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
return getGradientColor(ratio, stops)
|
||||
}
|
||||
|
||||
@@ -514,7 +650,7 @@ const handleDelete = async (budget) => {
|
||||
title: '删除预算',
|
||||
message: `确定要删除预算 "${budget.name}" 吗?`
|
||||
})
|
||||
|
||||
|
||||
const res = await deleteBudget(budget.id)
|
||||
if (res.success) {
|
||||
showToast('删除成功')
|
||||
@@ -531,7 +667,9 @@ const handleDelete = async (budget) => {
|
||||
}
|
||||
|
||||
const getSavingsDateLabel = (budget) => {
|
||||
if (!budget.periodStart) return ''
|
||||
if (!budget.periodStart) {
|
||||
return ''
|
||||
}
|
||||
const date = new Date(budget.periodStart)
|
||||
if (budget.type === BudgetPeriodType.Year) {
|
||||
return `${date.getFullYear()}年`
|
||||
@@ -541,12 +679,14 @@ const getSavingsDateLabel = (budget) => {
|
||||
}
|
||||
|
||||
const handleSavingsNav = async (budget, offset) => {
|
||||
if (!budget.periodStart) return
|
||||
|
||||
if (!budget.periodStart) {
|
||||
return
|
||||
}
|
||||
|
||||
const date = new Date(budget.periodStart)
|
||||
let year = date.getFullYear()
|
||||
let month = date.getMonth() + 1
|
||||
|
||||
|
||||
if (budget.type === BudgetPeriodType.Year) {
|
||||
year += offset
|
||||
} else {
|
||||
@@ -559,12 +699,12 @@ const handleSavingsNav = async (budget, offset) => {
|
||||
year--
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const res = await getSavingsBudget(year, month, budget.type)
|
||||
if (res.success && res.data) {
|
||||
// 找到并更新对应的 budget 对象
|
||||
const index = savingsBudgets.value.findIndex(b => b.id === budget.id)
|
||||
const index = savingsBudgets.value.findIndex((b) => b.id === budget.id)
|
||||
if (index !== -1) {
|
||||
savingsBudgets.value[index] = res.data
|
||||
}
|
||||
@@ -578,7 +718,9 @@ const handleSavingsNav = async (budget, offset) => {
|
||||
}
|
||||
|
||||
const disabledSavingsNextNav = (budget) => {
|
||||
if (!budget.periodStart) return true
|
||||
if (!budget.periodStart) {
|
||||
return true
|
||||
}
|
||||
const date = new Date(budget.periodStart)
|
||||
const now = new Date()
|
||||
if (budget.type === BudgetPeriodType.Year) {
|
||||
@@ -693,7 +835,9 @@ const disabledSavingsNextNav = (budget) => {
|
||||
.item-amount {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
font-family: DIN Alternate, system-ui;
|
||||
font-family:
|
||||
DIN Alternate,
|
||||
system-ui;
|
||||
}
|
||||
|
||||
/* 设置页面容器背景色 */
|
||||
|
||||
Reference in New Issue
Block a user