fix: 修复账单编辑后刷新才生效的问题
- 在分析页面添加 handleBillUpdated 回调,编辑后同步更新 records 和 allRecords - 为 TopExpenses、CategoryRanking、DailyTrendChart 组件添加 onUpdate prop - 修复 TopExpenses 组件内响应式更新,使用新数组引用触发更新 - 建立完整的更新传播链:BillDetailDrawer -> BillRecordsTable -> 各分析组件 -> 分析页面 - 确保所有派生数据(topExpenses、categoryStats、pieChartData)自动刷新
This commit is contained in:
@@ -16,9 +16,25 @@
|
|||||||
totalExpense: number;
|
totalExpense: number;
|
||||||
records: UIBill[];
|
records: UIBill[];
|
||||||
categories?: string[];
|
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 mode = $state<'bar' | 'pie'>('bar');
|
||||||
let dialogOpen = $state(false);
|
let dialogOpen = $state(false);
|
||||||
@@ -213,7 +229,7 @@
|
|||||||
</Drawer.Header>
|
</Drawer.Header>
|
||||||
|
|
||||||
<div class="flex-1 overflow-auto px-4 md:px-0">
|
<div class="flex-1 overflow-auto px-4 md:px-0">
|
||||||
<BillRecordsTable records={selectedRecords} showDescription={true} {categories} />
|
<BillRecordsTable records={selectedRecords} showDescription={true} {categories} onUpdate={handleRecordUpdated} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer.Footer>
|
<Drawer.Footer>
|
||||||
|
|||||||
@@ -17,9 +17,35 @@
|
|||||||
interface Props {
|
interface Props {
|
||||||
records: UIBill[];
|
records: UIBill[];
|
||||||
categories?: string[];
|
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 状态
|
// Dialog 状态
|
||||||
let dialogOpen = $state(false);
|
let dialogOpen = $state(false);
|
||||||
@@ -896,6 +922,7 @@
|
|||||||
showDescription={false}
|
showDescription={false}
|
||||||
pageSize={8}
|
pageSize={8}
|
||||||
{categories}
|
{categories}
|
||||||
|
onUpdate={handleRecordUpdated}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -24,7 +24,11 @@
|
|||||||
|
|
||||||
function handleRecordUpdated(updated: UIBill, original: UIBill) {
|
function handleRecordUpdated(updated: UIBill, original: UIBill) {
|
||||||
const idx = records.findIndex(r => r === original);
|
const idx = records.findIndex(r => r === original);
|
||||||
if (idx !== -1) records[idx] = updated;
|
if (idx !== -1) {
|
||||||
|
records[idx] = updated;
|
||||||
|
// 触发响应式更新
|
||||||
|
records = [...records];
|
||||||
|
}
|
||||||
selectedRecord = updated;
|
selectedRecord = updated;
|
||||||
onUpdate?.(updated);
|
onUpdate?.(updated);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,42 @@
|
|||||||
let pieChartData = $derived(calculatePieChartData(categoryStats, totalStats.expense));
|
let pieChartData = $derived(calculatePieChartData(categoryStats, totalStats.expense));
|
||||||
let topExpenses = $derived(getTopExpenses(analysisRecords, 10));
|
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(() => {
|
let sortedCategories = $derived(() => {
|
||||||
const categoryCounts = new Map<string, number>();
|
const categoryCounts = new Map<string, number>();
|
||||||
@@ -253,7 +289,7 @@
|
|||||||
<OverviewCards {totalStats} records={analysisRecords} />
|
<OverviewCards {totalStats} records={analysisRecords} />
|
||||||
|
|
||||||
<!-- 每日支出趋势图(按分类堆叠) - 使用全部数据 -->
|
<!-- 每日支出趋势图(按分类堆叠) - 使用全部数据 -->
|
||||||
<DailyTrendChart records={allAnalysisRecords} categories={sortedCategories()} />
|
<DailyTrendChart records={allAnalysisRecords} categories={sortedCategories()} onUpdate={handleBillUpdated} />
|
||||||
|
|
||||||
<div class="grid gap-6 lg:grid-cols-2">
|
<div class="grid gap-6 lg:grid-cols-2">
|
||||||
<!-- 分类支出排行 -->
|
<!-- 分类支出排行 -->
|
||||||
@@ -263,6 +299,7 @@
|
|||||||
totalExpense={totalStats.expense}
|
totalExpense={totalStats.expense}
|
||||||
records={analysisRecords}
|
records={analysisRecords}
|
||||||
categories={sortedCategories()}
|
categories={sortedCategories()}
|
||||||
|
onUpdate={handleBillUpdated}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 月度趋势 -->
|
<!-- 月度趋势 -->
|
||||||
@@ -270,7 +307,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Top 10 支出 -->
|
<!-- Top 10 支出 -->
|
||||||
<TopExpenses records={topExpenses} categories={sortedCategories()} />
|
<TopExpenses records={topExpenses} categories={sortedCategories()} onUpdate={handleBillUpdated} />
|
||||||
{:else}
|
{:else}
|
||||||
<!-- 空状态:服务器不可用或没有数据时显示示例按钮 -->
|
<!-- 空状态:服务器不可用或没有数据时显示示例按钮 -->
|
||||||
<Card.Root>
|
<Card.Root>
|
||||||
|
|||||||
Reference in New Issue
Block a user