feat: 添加账单软删除功能
- 新增删除按钮(带二次确认)到账单详情抽屉 - 后端实现软删除(设置 is_deleted 标记) - 所有查询方法自动过滤已删除记录 - 账单列表和复核页面都支持删除 - 版本更新至 1.2.0
This commit is contained in:
@@ -11,13 +11,15 @@
|
||||
import Pencil from '@lucide/svelte/icons/pencil';
|
||||
import Save from '@lucide/svelte/icons/save';
|
||||
import X from '@lucide/svelte/icons/x';
|
||||
import Check from '@lucide/svelte/icons/check';
|
||||
import Trash2 from '@lucide/svelte/icons/trash-2';
|
||||
import Calendar from '@lucide/svelte/icons/calendar';
|
||||
import Store from '@lucide/svelte/icons/store';
|
||||
import Tag from '@lucide/svelte/icons/tag';
|
||||
import FileText from '@lucide/svelte/icons/file-text';
|
||||
import CreditCard from '@lucide/svelte/icons/credit-card';
|
||||
|
||||
import { updateBill } from '$lib/api';
|
||||
import { updateBill, deleteBill } from '$lib/api';
|
||||
import { cleanedBillToUIBill, type UIBill } from '$lib/models/bill';
|
||||
|
||||
interface Props {
|
||||
@@ -33,7 +35,14 @@
|
||||
|
||||
contentClass?: string;
|
||||
|
||||
/** 保存时是否清除 review_level(用于复核场景) */
|
||||
clearReviewLevel?: boolean;
|
||||
|
||||
/** 是否允许删除 */
|
||||
allowDelete?: boolean;
|
||||
|
||||
onUpdate?: (updated: UIBill, original: UIBill) => void;
|
||||
onDelete?: (deleted: UIBill) => void;
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -45,11 +54,17 @@
|
||||
editDescription = '修改这笔支出的信息',
|
||||
titleExtra,
|
||||
contentClass,
|
||||
onUpdate
|
||||
clearReviewLevel = false,
|
||||
allowDelete = false,
|
||||
onUpdate,
|
||||
onDelete
|
||||
}: Props = $props();
|
||||
|
||||
let isEditing = $state(false);
|
||||
let isSaving = $state(false);
|
||||
let isConfirming = $state(false);
|
||||
let isDeleting = $state(false);
|
||||
let showDeleteConfirm = $state(false);
|
||||
|
||||
let editForm = $state({
|
||||
amount: '',
|
||||
@@ -62,6 +77,7 @@
|
||||
$effect(() => {
|
||||
if (!open) return;
|
||||
isEditing = false;
|
||||
showDeleteConfirm = false;
|
||||
});
|
||||
|
||||
function startEdit() {
|
||||
@@ -84,6 +100,52 @@
|
||||
if (value) editForm.category = value;
|
||||
}
|
||||
|
||||
// 确认正确(仅清除 review_level,不修改其他字段)
|
||||
async function confirmCorrect() {
|
||||
if (!record || isConfirming) return;
|
||||
|
||||
isConfirming = true;
|
||||
const original = { ...record };
|
||||
|
||||
try {
|
||||
const billId = record.id;
|
||||
if (billId) {
|
||||
const resp = await updateBill(billId, { review_level: '' });
|
||||
|
||||
if (resp.result && resp.data) {
|
||||
const updated = cleanedBillToUIBill(resp.data);
|
||||
record = updated;
|
||||
onUpdate?.(updated, original);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isConfirming = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 删除账单
|
||||
async function handleDelete() {
|
||||
if (!record || isDeleting) return;
|
||||
|
||||
isDeleting = true;
|
||||
const deleted = { ...record };
|
||||
|
||||
try {
|
||||
const billId = record.id;
|
||||
if (billId) {
|
||||
const resp = await deleteBill(billId);
|
||||
|
||||
if (resp.result) {
|
||||
open = false;
|
||||
onDelete?.(deleted);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isDeleting = false;
|
||||
showDeleteConfirm = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveEdit() {
|
||||
if (!record) return;
|
||||
if (isSaving) return;
|
||||
@@ -108,7 +170,9 @@
|
||||
category: editForm.category,
|
||||
amount: Number(editForm.amount),
|
||||
description: editForm.description,
|
||||
pay_method: editForm.payment_method
|
||||
pay_method: editForm.payment_method,
|
||||
// 复核模式下清除 review_level
|
||||
...(clearReviewLevel ? { review_level: '' } : {})
|
||||
});
|
||||
|
||||
if (resp.result && resp.data) {
|
||||
@@ -199,8 +263,10 @@
|
||||
{:else}
|
||||
<div>
|
||||
<div class="text-center mb-6">
|
||||
<div class="text-3xl font-bold text-red-600 dark:text-red-400 font-mono">¥{record.amount.toFixed(2)}</div>
|
||||
<div class="text-sm text-muted-foreground mt-1">支出金额</div>
|
||||
<div class="text-3xl font-bold font-mono {record.incomeExpense === '收入' ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}">
|
||||
¥{record.amount.toFixed(2)}
|
||||
</div>
|
||||
<div class="text-sm text-muted-foreground mt-1">{record.incomeExpense || '支出'}金额</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@@ -263,10 +329,35 @@
|
||||
<Save class="h-4 w-4 mr-2" />
|
||||
{isSaving ? '保存中…' : '保存'}
|
||||
</Button>
|
||||
{:else if showDeleteConfirm}
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<span class="text-sm text-muted-foreground">确定要删除这条账单吗?</span>
|
||||
<div class="flex-1"></div>
|
||||
<Button variant="outline" onclick={() => (showDeleteConfirm = false)} disabled={isDeleting}>
|
||||
取消
|
||||
</Button>
|
||||
<Button variant="destructive" onclick={handleDelete} disabled={isDeleting}>
|
||||
<Trash2 class="h-4 w-4 mr-2" />
|
||||
{isDeleting ? '删除中…' : '确认删除'}
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
{#if allowDelete}
|
||||
<Button variant="outline" class="text-red-600 hover:text-red-700 hover:bg-red-50" onclick={() => (showDeleteConfirm = true)}>
|
||||
<Trash2 class="h-4 w-4 mr-2" />
|
||||
删除
|
||||
</Button>
|
||||
{/if}
|
||||
<div class="flex-1"></div>
|
||||
<Button variant="outline" onclick={() => (open = false)}>
|
||||
关闭
|
||||
</Button>
|
||||
{#if clearReviewLevel}
|
||||
<Button class="bg-green-600 hover:bg-green-700 text-white" onclick={confirmCorrect} disabled={isConfirming}>
|
||||
<Check class="h-4 w-4 mr-2" />
|
||||
{isConfirming ? '确认中…' : '确认正确'}
|
||||
</Button>
|
||||
{/if}
|
||||
<Button onclick={startEdit}>
|
||||
<Pencil class="h-4 w-4 mr-2" />
|
||||
编辑
|
||||
|
||||
Reference in New Issue
Block a user