feat(analysis): 增强图表交互功能
- 分类支出排行: 饼图支持点击类别切换显示/隐藏,百分比动态重新计算 - 每日支出趋势: 图例支持点击切换类别显示,隐藏类别不参与堆叠计算 - Dialog列表: 添加列排序功能(时间/商家/描述/金额) - Dialog列表: 添加分页功能,每页10条(分类)/8条(每日) - 饼图hover效果: 扇形放大、阴影增强、中心显示详情
This commit is contained in:
235
web/src/routes/+layout.svelte
Normal file
235
web/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,235 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { page } from '$app/stores';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import * as Avatar from '$lib/components/ui/avatar';
|
||||
import { Separator } from '$lib/components/ui/separator';
|
||||
import Upload from '@lucide/svelte/icons/upload';
|
||||
import ClipboardCheck from '@lucide/svelte/icons/clipboard-check';
|
||||
import FileText from '@lucide/svelte/icons/file-text';
|
||||
import BarChart3 from '@lucide/svelte/icons/bar-chart-3';
|
||||
import Settings from '@lucide/svelte/icons/settings';
|
||||
import HelpCircle from '@lucide/svelte/icons/help-circle';
|
||||
import Search from '@lucide/svelte/icons/search';
|
||||
import Moon from '@lucide/svelte/icons/moon';
|
||||
import Sun from '@lucide/svelte/icons/sun';
|
||||
import ChevronsUpDown from '@lucide/svelte/icons/chevrons-up-down';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let darkMode = $state(false);
|
||||
|
||||
function toggleDarkMode() {
|
||||
darkMode = !darkMode;
|
||||
if (darkMode) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
const mainNavItems = [
|
||||
{ href: '/', label: '上传', icon: Upload },
|
||||
{ href: '/review', label: '复核', icon: ClipboardCheck },
|
||||
{ href: '/bills', label: '账单', icon: FileText },
|
||||
{ href: '/analysis', label: '分析', icon: BarChart3 },
|
||||
];
|
||||
|
||||
function isActive(href: string, pathname: string): boolean {
|
||||
if (href === '/') return pathname === '/';
|
||||
return pathname.startsWith(href);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Sidebar.Provider>
|
||||
<Sidebar.Root>
|
||||
<Sidebar.Header>
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Sidebar.MenuButton
|
||||
{...props}
|
||||
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<div class="flex aspect-square size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground">
|
||||
<span class="text-lg">💰</span>
|
||||
</div>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold">BillAI</span>
|
||||
<span class="truncate text-xs text-muted-foreground">智能账单分析</span>
|
||||
</div>
|
||||
<ChevronsUpDown class="ml-auto" />
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-[--bits-dropdown-menu-anchor-width] min-w-56 rounded-lg"
|
||||
side="bottom"
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenu.Label class="text-xs text-muted-foreground">
|
||||
版本信息
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Item>
|
||||
<span class="mr-2">📦</span>
|
||||
v0.1.0 Beta
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Header>
|
||||
|
||||
<Sidebar.Content>
|
||||
<Sidebar.Group>
|
||||
<Sidebar.GroupLabel>主要功能</Sidebar.GroupLabel>
|
||||
<Sidebar.GroupContent>
|
||||
<Sidebar.Menu>
|
||||
{#each mainNavItems as item}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton
|
||||
isActive={isActive(item.href, $page.url.pathname)}
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
<a href={item.href} {...props}>
|
||||
<item.icon class="size-4" />
|
||||
<span>{item.label}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
{/each}
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.GroupContent>
|
||||
</Sidebar.Group>
|
||||
|
||||
<Sidebar.Group>
|
||||
<Sidebar.GroupLabel>系统</Sidebar.GroupLabel>
|
||||
<Sidebar.GroupContent>
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet child({ props })}
|
||||
<button {...props} onclick={toggleDarkMode}>
|
||||
{#if darkMode}
|
||||
<Sun class="size-4" />
|
||||
<span>浅色模式</span>
|
||||
{:else}
|
||||
<Moon class="size-4" />
|
||||
<span>深色模式</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet child({ props })}
|
||||
<a href="/settings" {...props}>
|
||||
<Settings class="size-4" />
|
||||
<span>设置</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet child({ props })}
|
||||
<a href="/help" {...props}>
|
||||
<HelpCircle class="size-4" />
|
||||
<span>帮助</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.GroupContent>
|
||||
</Sidebar.Group>
|
||||
</Sidebar.Content>
|
||||
|
||||
<Sidebar.Footer>
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Sidebar.MenuButton
|
||||
{...props}
|
||||
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<Avatar.Root class="h-8 w-8 rounded-lg">
|
||||
<Avatar.Fallback class="rounded-lg bg-gradient-to-br from-primary to-chart-1 text-primary-foreground">
|
||||
U
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold">用户</span>
|
||||
<span class="truncate text-xs text-muted-foreground">user@example.com</span>
|
||||
</div>
|
||||
<ChevronsUpDown class="ml-auto size-4" />
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-[--bits-dropdown-menu-anchor-width] min-w-56 rounded-lg"
|
||||
side="bottom"
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenu.Label class="p-0 font-normal">
|
||||
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar.Root class="h-8 w-8 rounded-lg">
|
||||
<Avatar.Fallback class="rounded-lg bg-gradient-to-br from-primary to-chart-1 text-primary-foreground">
|
||||
U
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold">用户</span>
|
||||
<span class="truncate text-xs text-muted-foreground">user@example.com</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<Settings class="mr-2 size-4" />
|
||||
账户设置
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Footer>
|
||||
<Sidebar.Rail />
|
||||
</Sidebar.Root>
|
||||
|
||||
<Sidebar.Inset>
|
||||
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
||||
<Sidebar.Trigger class="-ml-1" />
|
||||
<Separator orientation="vertical" class="mr-2 h-4" />
|
||||
<div class="flex items-center gap-2">
|
||||
<Search class="size-4 text-muted-foreground" />
|
||||
<span class="text-sm text-muted-foreground">搜索...</span>
|
||||
</div>
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<div class="flex items-center gap-1.5 text-sm">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
||||
</span>
|
||||
<span class="text-muted-foreground">服务运行中</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex-1 p-6">
|
||||
{@render children()}
|
||||
</main>
|
||||
</Sidebar.Inset>
|
||||
</Sidebar.Provider>
|
||||
Reference in New Issue
Block a user