feat: server connect mongo
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
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';
|
||||
|
||||
// Icons
|
||||
import Upload from '@lucide/svelte/icons/upload';
|
||||
import ClipboardCheck from '@lucide/svelte/icons/clipboard-check';
|
||||
import FileText from '@lucide/svelte/icons/file-text';
|
||||
@@ -13,30 +15,65 @@
|
||||
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';
|
||||
import Wallet from '@lucide/svelte/icons/wallet';
|
||||
import LogOut from '@lucide/svelte/icons/log-out';
|
||||
import User from '@lucide/svelte/icons/user';
|
||||
import Bell from '@lucide/svelte/icons/bell';
|
||||
import Sparkles from '@lucide/svelte/icons/sparkles';
|
||||
|
||||
// Theme
|
||||
import {
|
||||
type ThemeMode,
|
||||
themeConfig,
|
||||
getNextTheme,
|
||||
applyThemeToDocument,
|
||||
loadThemeFromStorage,
|
||||
saveThemeToStorage
|
||||
} from '$lib/config/theme';
|
||||
|
||||
let { children } = $props();
|
||||
let { children } = $props();
|
||||
|
||||
let darkMode = $state(false);
|
||||
let themeMode = $state<ThemeMode>('system');
|
||||
|
||||
function toggleDarkMode() {
|
||||
darkMode = !darkMode;
|
||||
if (darkMode) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
onMount(() => {
|
||||
themeMode = loadThemeFromStorage();
|
||||
applyThemeToDocument(themeMode);
|
||||
|
||||
// 监听系统主题变化
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const handleChange = () => applyThemeToDocument(themeMode);
|
||||
mediaQuery.addEventListener('change', handleChange);
|
||||
return () => mediaQuery.removeEventListener('change', handleChange);
|
||||
});
|
||||
|
||||
function cycleTheme() {
|
||||
themeMode = getNextTheme(themeMode);
|
||||
saveThemeToStorage(themeMode);
|
||||
applyThemeToDocument(themeMode);
|
||||
}
|
||||
|
||||
const mainNavItems = [
|
||||
{ href: '/', label: '上传', icon: Upload },
|
||||
{ href: '/review', label: '复核', icon: ClipboardCheck },
|
||||
{ href: '/bills', label: '账单', icon: FileText },
|
||||
{ href: '/analysis', label: '分析', icon: BarChart3 },
|
||||
// 主导航
|
||||
const navMain = [
|
||||
{ href: '/', label: '上传账单', icon: Upload },
|
||||
{ href: '/review', label: '智能复核', icon: ClipboardCheck },
|
||||
{ href: '/bills', label: '账单管理', icon: FileText },
|
||||
{ href: '/analysis', label: '数据分析', icon: BarChart3 },
|
||||
];
|
||||
|
||||
// 次级导航(底部)
|
||||
const navSecondary = [
|
||||
{ href: '/settings', label: '设置', icon: Settings },
|
||||
{ href: '/help', label: '帮助', icon: HelpCircle },
|
||||
];
|
||||
|
||||
// 用户数据
|
||||
const user = {
|
||||
name: '用户',
|
||||
email: 'user@example.com',
|
||||
avatar: ''
|
||||
};
|
||||
|
||||
function isActive(href: string, pathname: string): boolean {
|
||||
if (href === '/') return pathname === '/';
|
||||
return pathname.startsWith(href);
|
||||
@@ -44,57 +81,33 @@
|
||||
</script>
|
||||
|
||||
<Sidebar.Provider>
|
||||
<Sidebar.Root>
|
||||
<Sidebar.Root collapsible="offcanvas">
|
||||
<!-- Header: Logo + App Name -->
|
||||
<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.MenuButton class="!p-1.5">
|
||||
{#snippet child({ props })}
|
||||
<a href="/" {...props} class="flex items-center gap-2">
|
||||
<div class="flex size-8 items-center justify-center rounded-lg bg-gradient-to-br from-orange-500 to-amber-500 text-white">
|
||||
<Wallet class="size-5" />
|
||||
</div>
|
||||
<span class="text-base font-semibold">BillAI</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Header>
|
||||
|
||||
<Sidebar.Content>
|
||||
<!-- 主导航 -->
|
||||
<Sidebar.Group>
|
||||
<Sidebar.GroupLabel>主要功能</Sidebar.GroupLabel>
|
||||
<Sidebar.GroupContent>
|
||||
<Sidebar.Menu>
|
||||
{#each mainNavItems as item}
|
||||
{#each navMain as item}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton
|
||||
isActive={isActive(item.href, $page.url.pathname)}
|
||||
>
|
||||
<Sidebar.MenuButton isActive={isActive(item.href, $page.url.pathname)}>
|
||||
{#snippet child({ props })}
|
||||
<a href={item.href} {...props}>
|
||||
<item.icon class="size-4" />
|
||||
@@ -108,50 +121,41 @@
|
||||
</Sidebar.GroupContent>
|
||||
</Sidebar.Group>
|
||||
|
||||
<Sidebar.Group>
|
||||
<Sidebar.GroupLabel>系统</Sidebar.GroupLabel>
|
||||
<!-- 次级导航 (底部) -->
|
||||
<Sidebar.Group class="mt-auto">
|
||||
<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}
|
||||
{@const theme = themeConfig[themeMode]}
|
||||
<button {...props} onclick={cycleTheme}>
|
||||
<theme.icon class="size-4" />
|
||||
<span>{theme.label}</span>
|
||||
</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>
|
||||
|
||||
{#each navSecondary as item}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#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.Content>
|
||||
|
||||
<!-- Footer: 用户信息 -->
|
||||
<Sidebar.Footer>
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
@@ -163,13 +167,13 @@
|
||||
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 class="rounded-lg bg-gradient-to-br from-violet-500 to-purple-600 text-white font-medium">
|
||||
{user.name.charAt(0).toUpperCase()}
|
||||
</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>
|
||||
<span class="truncate font-semibold">{user.name}</span>
|
||||
<span class="truncate text-xs text-muted-foreground">{user.email}</span>
|
||||
</div>
|
||||
<ChevronsUpDown class="ml-auto size-4" />
|
||||
</Sidebar.MenuButton>
|
||||
@@ -184,23 +188,43 @@
|
||||
<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 class="rounded-lg bg-gradient-to-br from-violet-500 to-purple-600 text-white font-medium">
|
||||
{user.name.charAt(0).toUpperCase()}
|
||||
</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>
|
||||
<span class="truncate font-semibold">{user.name}</span>
|
||||
<span class="truncate text-xs text-muted-foreground">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<Settings class="mr-2 size-4" />
|
||||
账户设置
|
||||
<Sparkles class="mr-2 size-4" />
|
||||
升级到 Pro
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<User class="mr-2 size-4" />
|
||||
账户
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<Settings class="mr-2 size-4" />
|
||||
设置
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<Bell class="mr-2 size-4" />
|
||||
通知
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item>
|
||||
<LogOut class="mr-2 size-4" />
|
||||
退出登录
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
@@ -210,7 +234,7 @@
|
||||
</Sidebar.Root>
|
||||
|
||||
<Sidebar.Inset>
|
||||
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
||||
<header class="flex h-14 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">
|
||||
@@ -229,7 +253,7 @@
|
||||
</header>
|
||||
|
||||
<main class="flex-1 p-6">
|
||||
{@render children()}
|
||||
{@render children()}
|
||||
</main>
|
||||
</Sidebar.Inset>
|
||||
</Sidebar.Provider>
|
||||
|
||||
Reference in New Issue
Block a user