Files
EmailBill/Web/src/components/Budget/BudgetEditPopup.vue
孙诚 c74ce24727
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 39s
Docker Build & Deploy / Deploy to Production (push) Successful in 12s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s
fix
2026-01-16 23:18:04 +08:00

276 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<PopupContainer
v-model="visible"
:title="
isEdit
? `编辑${getCategoryName(form.category)}预算`
: `新增${getCategoryName(form.category)}预算`
"
height="75%"
>
<div class="add-budget-form">
<van-form>
<van-cell-group inset>
<van-field
v-model="form.name"
name="name"
label="预算名称"
placeholder="例如:每月餐饮、年度奖金"
:rules="[{ required: true, message: '请填写预算名称' }]"
/>
<!-- 新增不记额预算复选框 -->
<van-field label="不记额预算">
<template #input>
<van-checkbox
v-model="form.noLimit"
@update:model-value="onNoLimitChange"
>
不记额预算
</van-checkbox>
</template>
</van-field>
<!-- 新增硬性消费复选框 -->
<van-field label="硬性消费">
<template #input>
<div class="mandatory-wrapper">
<van-checkbox
v-model="form.isMandatoryExpense"
:disabled="form.noLimit"
>
硬性消费
<span class="mandatory-tip">
当前周期 / 按天数自动累加
</span>
</van-checkbox>
</div>
</template>
</van-field>
<van-field
name="type"
label="统计周期"
>
<template #input>
<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-group>
</template>
</van-field>
<!-- 仅当未选中"不记额预算"时显示预算金额 -->
<van-field
v-if="!form.noLimit"
v-model="form.limit"
type="number"
name="limit"
label="预算金额"
placeholder="0.00"
:rules="[{ required: true, message: '请填写预算金额' }]"
>
<template #extra>
<span></span>
</template>
</van-field>
<van-field label="相关分类">
<template #input>
<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('、') }}
</span>
</div>
</template>
</van-field>
<ClassifySelector
v-model="form.selectedCategories"
:type="budgetType"
multiple
:show-add="false"
:show-clear="false"
/>
</van-cell-group>
</van-form>
</div>
<template #footer>
<van-button
block
round
type="primary"
@click="onSubmit"
>
保存预算
</van-button>
</template>
</PopupContainer>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import { showToast } from 'vant'
import { createBudget, updateBudget } from '@/api/budget'
import { BudgetPeriodType, BudgetCategory } from '@/constants/enums'
import PopupContainer from '@/components/PopupContainer.vue'
import ClassifySelector from '@/components/ClassifySelector.vue'
const emit = defineEmits(['success'])
const visible = ref(false)
const isEdit = ref(false)
const form = reactive({
id: undefined,
name: '',
type: BudgetPeriodType.Month,
category: BudgetCategory.Expense,
limit: '',
selectedCategories: [],
noLimit: false, // 新增字段
isMandatoryExpense: false // 新增:硬性消费
})
const open = ({ data, isEditFlag, category }) => {
if (category === undefined) {
showToast('缺少必要参数category')
return
}
isEdit.value = isEditFlag
if (data) {
Object.assign(form, {
id: data.id,
name: data.name,
type: data.type,
category: category,
limit: data.limit,
selectedCategories: data.selectedCategories ? [...data.selectedCategories] : [],
noLimit: data.noLimit || false, // 新增
isMandatoryExpense: data.isMandatoryExpense || false // 新增:硬性消费
})
} else {
Object.assign(form, {
id: undefined,
name: '',
type: BudgetPeriodType.Month,
category: category,
limit: '',
selectedCategories: [],
noLimit: false, // 新增
isMandatoryExpense: false // 新增:硬性消费
})
}
visible.value = true
}
defineExpose({
open
})
const budgetType = computed(() => {
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
selectedCategories: form.selectedCategories,
noLimit: form.noLimit, // 新增
isMandatoryExpense: form.isMandatoryExpense // 新增:硬性消费
}
const res = form.id ? await updateBudget(data) : await createBudget(data)
if (res.success) {
showToast('保存成功')
visible.value = false
emit('success')
}
} catch (err) {
showToast(err.message || '保存失败')
console.error('保存预算失败', err)
}
}
const getCategoryName = (category) => {
switch (category) {
case BudgetCategory.Expense:
return '支出'
case BudgetCategory.Income:
return '收入'
case BudgetCategory.Savings:
return '存款'
default:
return ''
}
}
const onNoLimitChange = (value) => {
if (value) {
// 选中不记额时,自动设为年度预算
form.type = BudgetPeriodType.Year
// 选中不记额时,清除硬性消费选择
form.isMandatoryExpense = false
}
}
</script>
<style scoped>
.add-budget-form {
padding: 20px 0;
}
.selected-categories {
display: flex;
align-items: center;
padding: 4px 0;
width: 100%;
overflow: hidden;
}
.ellipsis-text {
font-size: 14px;
color: var(--van-text-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
.no-data {
font-size: 13px;
color: var(--van-text-color-2);
padding: 8px 16px;
}
.mandatory-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.mandatory-tip {
font-size: 11px;
color: var(--van-text-color-3);
margin-left: 6px;
}
</style>