diff --git a/web/src/lib/components/analysis/CategoryRanking.svelte b/web/src/lib/components/analysis/CategoryRanking.svelte index 60a7825..3dda463 100644 --- a/web/src/lib/components/analysis/CategoryRanking.svelte +++ b/web/src/lib/components/analysis/CategoryRanking.svelte @@ -16,9 +16,25 @@ totalExpense: number; records: UIBill[]; categories?: string[]; + onUpdate?: (updated: UIBill, original: UIBill) => void; } - let { categoryStats, pieChartData, totalExpense, records = $bindable(), categories = [] }: Props = $props(); + let { categoryStats, pieChartData, totalExpense, records = $bindable(), categories = [], onUpdate }: Props = $props(); + + function handleRecordUpdated(updated: UIBill, original: UIBill) { + // 更新本地 records 数组 + const idx = records.findIndex(r => + r === original || + (r.time === original.time && r.merchant === original.merchant && r.amount === original.amount) + ); + if (idx !== -1) { + records[idx] = updated; + records = [...records]; + } + + // 传播到父组件 + onUpdate?.(updated, original); + } let mode = $state<'bar' | 'pie'>('bar'); let dialogOpen = $state(false); @@ -213,7 +229,7 @@
- +
diff --git a/web/src/lib/components/analysis/DailyTrendChart.svelte b/web/src/lib/components/analysis/DailyTrendChart.svelte index 7f5be61..db5d95b 100644 --- a/web/src/lib/components/analysis/DailyTrendChart.svelte +++ b/web/src/lib/components/analysis/DailyTrendChart.svelte @@ -17,9 +17,35 @@ interface Props { records: UIBill[]; categories?: string[]; + onUpdate?: (updated: UIBill, original: UIBill) => void; } - let { records = $bindable(), categories = [] }: Props = $props(); + let { records = $bindable(), categories = [], onUpdate }: Props = $props(); + + function handleRecordUpdated(updated: UIBill, original: UIBill) { + // 更新 records 数组 + const idx = records.findIndex(r => + r === original || + (r.time === original.time && r.merchant === original.merchant && r.amount === original.amount) + ); + if (idx !== -1) { + records[idx] = updated; + records = [...records]; + } + + // 更新 selectedDateRecords(如果账单在当前选中的日期记录中) + const dateIdx = selectedDateRecords.findIndex(r => + r === original || + (r.time === original.time && r.merchant === original.merchant && r.amount === original.amount) + ); + if (dateIdx !== -1) { + selectedDateRecords[dateIdx] = updated; + selectedDateRecords = [...selectedDateRecords]; + } + + // 传播到父组件 + onUpdate?.(updated, original); + } // Dialog 状态 let dialogOpen = $state(false); @@ -896,6 +922,7 @@ showDescription={false} pageSize={8} {categories} + onUpdate={handleRecordUpdated} /> {:else} diff --git a/web/src/lib/components/analysis/TopExpenses.svelte b/web/src/lib/components/analysis/TopExpenses.svelte index d22204c..e31895f 100644 --- a/web/src/lib/components/analysis/TopExpenses.svelte +++ b/web/src/lib/components/analysis/TopExpenses.svelte @@ -24,7 +24,11 @@ function handleRecordUpdated(updated: UIBill, original: UIBill) { const idx = records.findIndex(r => r === original); - if (idx !== -1) records[idx] = updated; + if (idx !== -1) { + records[idx] = updated; + // 触发响应式更新 + records = [...records]; + } selectedRecord = updated; onUpdate?.(updated); } diff --git a/web/src/routes/analysis/+page.svelte b/web/src/routes/analysis/+page.svelte index a7a7b3a..da4ca04 100644 --- a/web/src/routes/analysis/+page.svelte +++ b/web/src/routes/analysis/+page.svelte @@ -91,6 +91,42 @@ let pieChartData = $derived(calculatePieChartData(categoryStats, totalStats.expense)); let topExpenses = $derived(getTopExpenses(analysisRecords, 10)); + // 账单更新处理 + function handleBillUpdated(updated: UIBill) { + // 在 records 中查找并更新对应的账单 + const idx = records.findIndex(r => + r.id === (updated as unknown as { id?: string }).id || + (r.time === updated.time && r.merchant === updated.merchant && r.amount === updated.amount) + ); + + if (idx !== -1) { + // 更新后端格式的记录 + records[idx] = { + ...records[idx], + time: updated.time, + category: updated.category, + merchant: updated.merchant, + description: updated.description || '', + amount: updated.amount, + pay_method: updated.paymentMethod || '', + status: updated.status || records[idx].status, + remark: updated.remark || records[idx].remark, + }; + // 触发响应式更新 + records = [...records]; + + // 同时更新 allRecords(如果账单在全部数据中) + const allIdx = allRecords.findIndex(r => + r.id === (updated as unknown as { id?: string }).id || + (r.time === updated.time && r.merchant === updated.merchant) + ); + if (allIdx !== -1) { + allRecords[allIdx] = records[idx]; + allRecords = [...allRecords]; + } + } + } + // 分类列表按数据中出现次数排序 let sortedCategories = $derived(() => { const categoryCounts = new Map(); @@ -253,7 +289,7 @@ - +
@@ -263,6 +299,7 @@ totalExpense={totalStats.expense} records={analysisRecords} categories={sortedCategories()} + onUpdate={handleBillUpdated} /> @@ -270,7 +307,7 @@
- + {:else}