大量的代码格式化
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:
孙诚
2026-01-16 11:15:44 +08:00
parent 9069e3dbcf
commit 319f8f7d7b
54 changed files with 2973 additions and 2200 deletions

View File

@@ -8,34 +8,38 @@
<div class="collapsed-header">
<div class="budget-info">
<slot name="tag">
<van-tag
:type="budget.type === BudgetPeriodType.Year ? 'warning' : 'primary'"
<van-tag
:type="budget.type === BudgetPeriodType.Year ? 'warning' : 'primary'"
plain
class="status-tag"
>
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
</van-tag>
</slot>
<h3 class="card-title">{{ budget.name }}</h3>
<h3 class="card-title">
{{ budget.name }}
</h3>
<span v-if="budget.selectedCategories?.length" class="card-subtitle">
({{ budget.selectedCategories.join('') }})
</span>
</div>
<van-icon name="arrow-down" class="expand-icon" />
</div>
<div class="collapsed-footer">
<div class="collapsed-item">
<span class="compact-label">实际/目标</span>
<span class="compact-value">
<slot name="collapsed-amount">
{{ budget.current !== undefined && budget.limit !== undefined
? `¥${budget.current?.toFixed(0) || 0} / ¥${budget.limit?.toFixed(0) || 0}`
: '--' }}
{{
budget.current !== undefined && budget.limit !== undefined
? `¥${budget.current?.toFixed(0) || 0} / ¥${budget.limit?.toFixed(0) || 0}`
: '--'
}}
</slot>
</span>
</div>
<div class="collapsed-item">
<span class="compact-label">达成率</span>
<span class="compact-value" :class="percentClass">{{ percentage }}%</span>
@@ -45,52 +49,49 @@
<!-- 展开状态 -->
<div v-else class="budget-inner-card">
<div class="card-header" style="margin-bottom: 0;">
<div class="card-header" style="margin-bottom: 0">
<div class="budget-info">
<slot name="tag">
<van-tag
:type="budget.type === BudgetPeriodType.Year ? 'warning' : 'primary'"
<van-tag
:type="budget.type === BudgetPeriodType.Year ? 'warning' : 'primary'"
plain
class="status-tag"
>
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
</van-tag>
</slot>
<h3 class="card-title" style="max-width: 120px;">{{ budget.name }}</h3>
<h3 class="card-title" style="max-width: 120px">
{{ budget.name }}
</h3>
</div>
<div class="header-actions">
<slot name="actions">
<van-button
<van-button
v-if="budget.description"
:icon="showDescription ? 'info' : 'info-o'"
:icon="showDescription ? 'info' : 'info-o'"
size="small"
:type="showDescription ? 'primary' : 'default'"
plain
@click.stop="showDescription = !showDescription"
plain
@click.stop="showDescription = !showDescription"
/>
<van-button
icon="orders-o"
size="small"
plain
<van-button
icon="orders-o"
size="small"
plain
title="查询关联账单"
@click.stop="handleQueryBills"
/>
<template v-if="budget.category !== 2">
<van-button
icon="edit"
size="small"
plain
@click.stop="$emit('click', budget)"
/>
<van-button icon="edit" size="small" plain @click.stop="$emit('click', budget)" />
</template>
</slot>
</div>
</div>
<div class="budget-body">
<div v-if="budget.selectedCategories?.length" class="category-tags">
<van-tag
v-for="cat in budget.selectedCategories"
<van-tag
v-for="cat in budget.selectedCategories"
:key="cat"
size="mini"
class="category-tag"
@@ -101,14 +102,16 @@
</van-tag>
</div>
<div class="amount-info">
<slot name="amount-info"></slot>
<slot name="amount-info" />
</div>
<div class="progress-section">
<slot name="progress-info">
<span class="period-type">{{ periodLabel }}{{ budget.category === 0 ? '使用' : '达成' }}</span>
<van-progress
:percentage="Math.min(percentage, 100)"
<span class="period-type"
>{{ periodLabel }}{{ budget.category === 0 ? '使用' : '达成' }}</span
>
<van-progress
:percentage="Math.min(percentage, 100)"
stroke-width="8"
:color="progressColor"
:show-pivot="false"
@@ -118,8 +121,8 @@
</div>
<div class="progress-section time-progress">
<span class="period-type">时间进度</span>
<van-progress
:percentage="timePercentage"
<van-progress
:percentage="timePercentage"
stroke-width="4"
color="var(--van-gray-6)"
:show-pivot="false"
@@ -129,24 +132,20 @@
<van-collapse-transition>
<div v-if="budget.description && showDescription" class="budget-description">
<div class="description-content rich-html-content" v-html="budget.description"></div>
<div class="description-content rich-html-content" v-html="budget.description" />
</div>
</van-collapse-transition>
</div>
<div class="card-footer">
<slot name="footer"></slot>
<slot name="footer" />
</div>
</div>
</div>
<!-- 关联账单列表弹窗 -->
<PopupContainer
v-model="showBillListModal"
title="关联账单列表"
height="75%"
>
<TransactionList
<PopupContainer v-model="showBillListModal" title="关联账单列表" height="75%">
<TransactionList
:transactions="billList"
:loading="billLoading"
:finished="true"
@@ -166,30 +165,26 @@
<div class="collapsed-header">
<div class="budget-info">
<slot name="tag">
<van-tag
type="success"
plain
class="status-tag"
>
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
<van-tag type="success" plain class="status-tag">
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
</van-tag>
</slot>
<h3 class="card-title">{{ budget.name }}</h3>
<h3 class="card-title">
{{ budget.name }}
</h3>
<span v-if="budget.selectedCategories?.length" class="card-subtitle">
({{ budget.selectedCategories.join('') }})
</span>
</div>
<van-icon name="arrow-down" class="expand-icon" />
</div>
<div class="collapsed-footer no-limit-footer">
<div class="collapsed-item">
<span class="compact-label">实际</span>
<span class="compact-value">
<slot name="collapsed-amount">
{{ budget.current !== undefined
? `¥${budget.current?.toFixed(0) || 0}`
: '--' }}
{{ budget.current !== undefined ? `¥${budget.current?.toFixed(0) || 0}` : '--' }}
</slot>
</span>
</div>
@@ -198,52 +193,45 @@
<!-- 展开状态 -->
<div v-else class="budget-inner-card">
<div class="card-header" style="margin-bottom: 0;">
<div class="card-header" style="margin-bottom: 0">
<div class="budget-info">
<slot name="tag">
<van-tag
type="success"
plain
class="status-tag"
>
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
<van-tag type="success" plain class="status-tag">
{{ budget.type === BudgetPeriodType.Year ? '年度' : '月度' }}
</van-tag>
</slot>
<h3 class="card-title" style="max-width: 120px;">{{ budget.name }}</h3>
<h3 class="card-title" style="max-width: 120px">
{{ budget.name }}
</h3>
</div>
<div class="header-actions">
<slot name="actions">
<van-button
<van-button
v-if="budget.description"
:icon="showDescription ? 'info' : 'info-o'"
:icon="showDescription ? 'info' : 'info-o'"
size="small"
:type="showDescription ? 'primary' : 'default'"
plain
@click.stop="showDescription = !showDescription"
plain
@click.stop="showDescription = !showDescription"
/>
<van-button
icon="orders-o"
size="small"
plain
<van-button
icon="orders-o"
size="small"
plain
title="查询关联账单"
@click.stop="handleQueryBills"
/>
<template v-if="budget.category !== 2">
<van-button
icon="edit"
size="small"
plain
@click.stop="$emit('click', budget)"
/>
<van-button icon="edit" size="small" plain @click.stop="$emit('click', budget)" />
</template>
</slot>
</div>
</div>
<div class="budget-body">
<div v-if="budget.selectedCategories?.length" class="category-tags">
<van-tag
v-for="cat in budget.selectedCategories"
<van-tag
v-for="cat in budget.selectedCategories"
:key="cat"
size="mini"
class="category-tag"
@@ -253,26 +241,28 @@
{{ cat }}
</van-tag>
</div>
<div class="no-limit-amount-info">
<div class="amount-item">
<span>
<span class="label">实际</span>
<span class="value" style="margin-left: 12px;">¥{{ budget.current?.toFixed(0) || 0 }}</span>
<span class="label">实际</span>
<span class="value" style="margin-left: 12px"
>¥{{ budget.current?.toFixed(0) || 0 }}</span
>
</span>
</div>
</div>
<div class="no-limit-notice">
<span>
<van-icon name="info-o" style="margin-right: 4px;" />
<van-icon name="info-o" style="margin-right: 4px" />
不记额预算 - 直接计入存款明细
</span>
</div>
<van-collapse-transition>
<div v-if="budget.description && showDescription" class="budget-description">
<div class="description-content rich-html-content" v-html="budget.description"></div>
<div class="description-content rich-html-content" v-html="budget.description" />
</div>
</van-collapse-transition>
</div>
@@ -280,12 +270,8 @@
</div>
<!-- 关联账单列表弹窗 -->
<PopupContainer
v-model="showBillListModal"
title="关联账单列表"
height="75%"
>
<TransactionList
<PopupContainer v-model="showBillListModal" title="关联账单列表" height="75%">
<TransactionList
:transactions="billList"
:loading="billLoading"
:finished="true"
@@ -339,10 +325,10 @@ const toggleExpand = () => {
const handleQueryBills = async () => {
showBillListModal.value = true
billLoading.value = true
try {
const classify = props.budget.selectedCategories
? props.budget.selectedCategories.join(',')
const classify = props.budget.selectedCategories
? props.budget.selectedCategories.join(',')
: ''
if (classify === '') {
@@ -362,12 +348,11 @@ const handleQueryBills = async () => {
sortByAmount: true
})
if(response.success) {
if (response.success) {
billList.value = response.data || []
} else {
billList.value = []
}
} catch (error) {
console.error('查询账单列表失败:', error)
billList.value = []
@@ -377,19 +362,27 @@ const handleQueryBills = async () => {
}
const percentage = computed(() => {
if (!props.budget.limit) return 0
if (!props.budget.limit) {
return 0
}
return Math.round((props.budget.current / props.budget.limit) * 100)
})
const timePercentage = computed(() => {
if (!props.budget.periodStart || !props.budget.periodEnd) return 0
if (!props.budget.periodStart || !props.budget.periodEnd) {
return 0
}
const start = new Date(props.budget.periodStart).getTime()
const end = new Date(props.budget.periodEnd).getTime()
const now = new Date().getTime()
if (now <= start) return 0
if (now >= end) return 100
if (now <= start) {
return 0
}
if (now >= end) {
return 100
}
return Math.round(((now - start) / (end - start)) * 100)
})
</script>

View File

@@ -1,7 +1,11 @@
<template>
<PopupContainer
v-model="visible"
:title="isEdit ? `编辑${getCategoryName(form.category)}预算` : `新增${getCategoryName(form.category)}预算`"
<PopupContainer
v-model="visible"
:title="
isEdit
? `编辑${getCategoryName(form.category)}预算`
: `新增${getCategoryName(form.category)}预算`
"
height="75%"
>
<div class="add-budget-form">
@@ -17,18 +21,20 @@
<!-- 新增不记额预算复选框 -->
<van-field label="不记额预算">
<template #input>
<van-checkbox v-model="form.noLimit" @update:model-value="onNoLimitChange">不记额预算仅限年度</van-checkbox>
<van-checkbox v-model="form.noLimit" @update:model-value="onNoLimitChange">
不记额预算仅限年度
</van-checkbox>
</template>
</van-field>
<van-field name="type" label="统计周期">
<template #input>
<van-radio-group
v-model="form.type"
<van-radio-group
v-model="form.type"
direction="horizontal"
:disabled="isEdit || form.noLimit"
>
<van-radio :name="BudgetPeriodType.Month"></van-radio>
<van-radio :name="BudgetPeriodType.Year"></van-radio>
<van-radio :name="BudgetPeriodType.Month"> </van-radio>
<van-radio :name="BudgetPeriodType.Year"> </van-radio>
</van-radio-group>
</template>
</van-field>
@@ -48,7 +54,12 @@
</van-field>
<van-field label="相关分类">
<template #input>
<div v-if="form.selectedCategories.length === 0" style="color: var(--van-text-color-3);">可多选分类</div>
<div
v-if="form.selectedCategories.length === 0"
style="color: var(--van-text-color-3)"
>
可多选分类
</div>
<div v-else class="selected-categories">
<span class="ellipsis-text">
{{ form.selectedCategories.join('、') }}
@@ -67,7 +78,7 @@
</van-form>
</div>
<template #footer>
<van-button block round type="primary" @click="onSubmit">保存预算</van-button>
<van-button block round type="primary" @click="onSubmit"> 保存预算 </van-button>
</template>
</PopupContainer>
</template>
@@ -92,15 +103,11 @@ const form = reactive({
category: BudgetCategory.Expense,
limit: '',
selectedCategories: [],
noLimit: false // 新增字段
noLimit: false // 新增字段
})
const open = ({
data,
isEditFlag,
category
}) => {
if(category === undefined) {
const open = ({ data, isEditFlag, category }) => {
if (category === undefined) {
showToast('缺少必要参数category')
return
}
@@ -114,7 +121,7 @@ const open = ({
category: category,
limit: data.limit,
selectedCategories: data.selectedCategories ? [...data.selectedCategories] : [],
noLimit: data.noLimit || false // 新增
noLimit: data.noLimit || false // 新增
})
} else {
Object.assign(form, {
@@ -124,7 +131,7 @@ const open = ({
category: category,
limit: '',
selectedCategories: [],
noLimit: false // 新增
noLimit: false // 新增
})
}
visible.value = true
@@ -135,18 +142,22 @@ defineExpose({
})
const budgetType = computed(() => {
return form.category === BudgetCategory.Expense ? 0 : (form.category === BudgetCategory.Income ? 1 : 2)
return form.category === BudgetCategory.Expense
? 0
: form.category === BudgetCategory.Income
? 1
: 2
})
const onSubmit = async () => {
try {
const data = {
...form,
limit: form.noLimit ? 0 : parseFloat(form.limit), // 不记额时金额为0
limit: form.noLimit ? 0 : parseFloat(form.limit), // 不记额时金额为0
selectedCategories: form.selectedCategories,
noLimit: form.noLimit // 新增
noLimit: form.noLimit // 新增
}
const res = form.id ? await updateBudget(data) : await createBudget(data)
if (res.success) {
showToast('保存成功')
@@ -160,7 +171,7 @@ const onSubmit = async () => {
}
const getCategoryName = (category) => {
switch(category) {
switch (category) {
case BudgetCategory.Expense:
return '支出'
case BudgetCategory.Income:

View File

@@ -1,7 +1,11 @@
<template>
<div class="summary-container">
<transition :name="transitionName" mode="out-in">
<div v-if="stats && (stats.month || stats.year)" :key="dateKey" class="summary-card common-card">
<div
v-if="stats && (stats.month || stats.year)"
:key="dateKey"
class="summary-card common-card"
>
<!-- 左切换按钮 -->
<div class="nav-arrow left" @click.stop="changeMonth(-1)">
<van-icon name="arrow-left" />
@@ -20,13 +24,13 @@
<span class="amount">¥{{ formatMoney(stats[key]?.limit || 0) }}</span>
</div>
</div>
<div v-if="config.showDivider" class="divider"></div>
<div v-if="config.showDivider" class="divider" />
</template>
</div>
<!-- 右切换按钮 -->
<div
class="nav-arrow right"
<div
class="nav-arrow right"
:class="{ disabled: isCurrentMonth }"
@click.stop="!isCurrentMonth && changeMonth(1)"
>
@@ -71,18 +75,17 @@ const dateKey = computed(() => props.date.getFullYear() + '-' + props.date.getMo
const isCurrentMonth = computed(() => {
const now = new Date()
return props.date.getFullYear() === now.getFullYear() &&
props.date.getMonth() === now.getMonth()
return props.date.getFullYear() === now.getFullYear() && props.date.getMonth() === now.getMonth()
})
const periodConfigs = computed(() => ({
month: {
label: isCurrentMonth.value ? '本月' : `${props.date.getMonth() + 1}`,
showDivider: true
month: {
label: isCurrentMonth.value ? '本月' : `${props.date.getMonth() + 1}`,
showDivider: true
},
year: {
label: isCurrentMonth.value ? '年度' : `${props.date.getFullYear()}`,
showDivider: false
year: {
label: isCurrentMonth.value ? '年度' : `${props.date.getFullYear()}`,
showDivider: false
}
}))
@@ -94,7 +97,10 @@ const changeMonth = (delta) => {
}
const formatMoney = (val) => {
return parseFloat(val || 0).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 })
return parseFloat(val || 0).toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 0
})
}
</script>

View File

@@ -1,9 +1,5 @@
<template>
<PopupContainer
v-model="visible"
title="设置存款分类"
height="60%"
>
<PopupContainer v-model="visible" title="设置存款分类" height="60%">
<div class="savings-config-content">
<div class="config-header">
<p class="subtitle">这些分类的统计值将计入存款</p>
@@ -20,9 +16,9 @@
/>
</div>
</div>
<template #footer>
<van-button block round type="primary" @click="onSubmit">保存配置</van-button>
<van-button block round type="primary" @click="onSubmit"> 保存配置 </van-button>
</template>
</PopupContainer>
</template>
@@ -52,7 +48,7 @@ const fetchConfig = async () => {
try {
const res = await getConfig('SavingsCategories')
if (res.success && res.data) {
selectedCategories.value = res.data.split(',').filter(x => x)
selectedCategories.value = res.data.split(',').filter((x) => x)
} else {
selectedCategories.value = []
}