feat: 添加账单软删除功能

- 新增删除按钮(带二次确认)到账单详情抽屉
- 后端实现软删除(设置 is_deleted 标记)
- 所有查询方法自动过滤已删除记录
- 账单列表和复核页面都支持删除
- 版本更新至 1.2.0
This commit is contained in:
clz
2026-01-25 18:49:07 +08:00
parent a97a8d6a20
commit bacbabc0a5
12 changed files with 373 additions and 8 deletions

View File

@@ -5,6 +5,8 @@
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
import * as Table from '$lib/components/ui/table';
import BillDetailDrawer from '$lib/components/analysis/BillDetailDrawer.svelte';
import { cleanedBillToUIBill, type UIBill } from '$lib/models/bill';
import Loader2 from '@lucide/svelte/icons/loader-2';
import AlertCircle from '@lucide/svelte/icons/alert-circle';
import AlertTriangle from '@lucide/svelte/icons/alert-triangle';
@@ -68,6 +70,64 @@
let totalCount = $derived(reviewStats?.total || 0);
let highCount = $derived(reviewStats?.high || 0);
let lowCount = $derived(reviewStats?.low || 0);
// 账单详情抽屉
let drawerOpen = $state(false);
let selectedBill = $state<UIBill | null>(null);
// 分类列表(用于编辑选择)
const categories = [
'餐饮美食', '交通出行', '生活服务', '日用百货',
'服饰美容', '医疗健康', '通讯话费', '住房缴费',
'文化娱乐', '金融理财', '教育培训', '人情往来', '其他'
];
// 点击行打开详情
function handleRowClick(record: CleanedBill) {
selectedBill = cleanedBillToUIBill(record);
drawerOpen = true;
}
// 复核完成后从列表中移除该记录
function handleBillUpdate(updated: UIBill, original: UIBill) {
// 更新后 review_level 已被清除,从列表中移除
const index = allBills.findIndex(r => r.id === updated.id);
if (index !== -1) {
allBills.splice(index, 1);
// 触发响应式更新
allBills = [...allBills];
}
// 更新统计数据(根据原始的 review_level 减少计数)
if (reviewStats) {
reviewStats = {
...reviewStats,
total: Math.max(0, reviewStats.total - 1),
high: original.reviewLevel === 'HIGH' ? Math.max(0, reviewStats.high - 1) : reviewStats.high,
low: original.reviewLevel === 'LOW' ? Math.max(0, reviewStats.low - 1) : reviewStats.low
};
}
// 关闭抽屉
drawerOpen = false;
}
// 删除账单后从列表中移除该记录
function handleBillDelete(deleted: UIBill) {
// 从列表中移除对应的记录
const index = allBills.findIndex(r => r.id === deleted.id);
if (index !== -1) {
allBills.splice(index, 1);
allBills = [...allBills];
}
// 更新统计数据
if (reviewStats) {
reviewStats = {
...reviewStats,
total: Math.max(0, reviewStats.total - 1),
high: deleted.reviewLevel === 'HIGH' ? Math.max(0, reviewStats.high - 1) : reviewStats.high,
low: deleted.reviewLevel === 'LOW' ? Math.max(0, reviewStats.low - 1) : reviewStats.low
};
}
}
</script>
<svelte:head>
@@ -198,7 +258,10 @@
</Table.Header>
<Table.Body>
{#each filteredRecords as record}
<Table.Row>
<Table.Row
class="cursor-pointer hover:bg-muted/50 transition-colors"
onclick={() => handleRowClick(record)}
>
<Table.Cell class="text-muted-foreground text-sm">
{record.time ? new Date(record.time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) : '-'}
</Table.Cell>
@@ -240,3 +303,17 @@
</Card.Root>
{/if}
</div>
<!-- 账单详情抽屉(复核模式) -->
<BillDetailDrawer
bind:open={drawerOpen}
bind:record={selectedBill}
categories={categories}
title="复核账单"
viewDescription="确认或修改这笔账单的分类"
editDescription="修改这笔账单的分类信息"
clearReviewLevel={true}
allowDelete={true}
onUpdate={handleBillUpdate}
onDelete={handleBillDelete}
/>