92 lines
3.2 KiB
Svelte
92 lines
3.2 KiB
Svelte
<script lang="ts">
|
|
import * as Card from '$lib/components/ui/card';
|
|
import Flame from '@lucide/svelte/icons/flame';
|
|
import { type UIBill } from '$lib/models/bill';
|
|
import BillDetailDrawer from './BillDetailDrawer.svelte';
|
|
|
|
interface Props {
|
|
records: UIBill[];
|
|
categories: string[]; // 可用的分类列表
|
|
onUpdate?: (record: UIBill) => void;
|
|
}
|
|
|
|
let { records, categories, onUpdate }: Props = $props();
|
|
|
|
let dialogOpen = $state(false);
|
|
let selectedRecord = $state<UIBill | null>(null);
|
|
let selectedRank = $state(0);
|
|
|
|
function openDetail(record: UIBill, rank: number) {
|
|
selectedRecord = record;
|
|
selectedRank = rank;
|
|
dialogOpen = true;
|
|
}
|
|
|
|
function handleRecordUpdated(updated: UIBill, original: UIBill) {
|
|
const idx = records.findIndex(r => r === original);
|
|
if (idx !== -1) records[idx] = updated;
|
|
selectedRecord = updated;
|
|
onUpdate?.(updated);
|
|
}
|
|
</script>
|
|
|
|
<Card.Root class="transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
|
|
<Card.Header>
|
|
<Card.Title class="flex items-center gap-2">
|
|
<Flame class="h-5 w-5 text-orange-500" />
|
|
Top 10 单笔支出
|
|
</Card.Title>
|
|
<Card.Description>最大的单笔支出记录(点击查看详情)</Card.Description>
|
|
</Card.Header>
|
|
<Card.Content>
|
|
<div class="space-y-3">
|
|
{#each records as record, i}
|
|
<button
|
|
class="w-full flex items-center gap-4 p-3 rounded-lg bg-muted/50 transition-all duration-150 hover:bg-muted hover:scale-[1.02] hover:shadow-sm cursor-pointer text-left outline-none focus:outline-none"
|
|
onclick={() => openDetail(record, i + 1)}
|
|
>
|
|
<div class="flex h-8 w-8 items-center justify-center rounded-full font-bold text-sm {
|
|
i === 0 ? 'bg-gradient-to-br from-yellow-400 to-amber-500 text-white shadow-md' :
|
|
i === 1 ? 'bg-gradient-to-br from-slate-300 to-slate-400 text-white shadow-md' :
|
|
i === 2 ? 'bg-gradient-to-br from-orange-400 to-amber-600 text-white shadow-md' :
|
|
'bg-primary/10 text-primary'
|
|
}">
|
|
{i + 1}
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<p class="font-medium truncate">{record.merchant}</p>
|
|
<p class="text-sm text-muted-foreground truncate">
|
|
{record.description || record.category}
|
|
</p>
|
|
</div>
|
|
<div class="font-mono font-bold text-red-600 dark:text-red-400">
|
|
¥{record.amount.toFixed(2)}
|
|
</div>
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
</Card.Content>
|
|
</Card.Root>
|
|
|
|
<BillDetailDrawer
|
|
bind:open={dialogOpen}
|
|
bind:record={selectedRecord}
|
|
{categories}
|
|
title="账单详情"
|
|
viewDescription="查看这笔支出的完整信息"
|
|
editDescription="修改这笔支出的信息"
|
|
onUpdate={handleRecordUpdated}
|
|
>
|
|
{#snippet titleExtra({ isEditing })}
|
|
{#if selectedRank <= 3 && !isEditing}
|
|
<span class="ml-2 px-2 py-0.5 text-xs rounded-full {
|
|
selectedRank === 1 ? 'bg-gradient-to-r from-yellow-400 to-amber-500 text-white' :
|
|
selectedRank === 2 ? 'bg-gradient-to-r from-slate-300 to-slate-400 text-white' :
|
|
'bg-gradient-to-r from-orange-400 to-amber-600 text-white'
|
|
}">
|
|
Top {selectedRank}
|
|
</span>
|
|
{/if}
|
|
{/snippet}
|
|
</BillDetailDrawer>
|