From 4925285cf3fb9ee95ebce9344bfd7b5c4d23abe7 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 1 Jul 2026 14:15:26 +0800 Subject: [PATCH] feat: add frontend auth shell --- frontend/src/app/App.tsx | 30 +++++ frontend/src/components/app/AppSidebar.tsx | 102 ++++++++++------ frontend/src/components/app/AppTopbar.tsx | 11 +- frontend/src/components/ui/alert.tsx | 75 ++++++++++++ frontend/src/components/ui/checkbox.tsx | 30 +++++ frontend/src/components/ui/pagination.tsx | 125 ++++++++++++++++++++ frontend/src/components/ui/switch.tsx | 33 ++++++ frontend/src/features/auth/AuthProvider.tsx | 92 ++++++++++++++ frontend/src/features/auth/LoginPage.tsx | 111 +++++++++++++++++ frontend/src/features/auth/api.ts | 20 ++++ frontend/src/features/auth/types.ts | 38 ++++++ frontend/src/lib/api.ts | 37 +++++- 12 files changed, 664 insertions(+), 40 deletions(-) create mode 100644 frontend/src/components/ui/alert.tsx create mode 100644 frontend/src/components/ui/checkbox.tsx create mode 100644 frontend/src/components/ui/pagination.tsx create mode 100644 frontend/src/components/ui/switch.tsx create mode 100644 frontend/src/features/auth/AuthProvider.tsx create mode 100644 frontend/src/features/auth/LoginPage.tsx create mode 100644 frontend/src/features/auth/api.ts create mode 100644 frontend/src/features/auth/types.ts diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx index 0aa4c3a..4d15e6f 100644 --- a/frontend/src/app/App.tsx +++ b/frontend/src/app/App.tsx @@ -1,7 +1,37 @@ import { AppShell } from './layout/AppShell' import { Router } from './router' +import { AuthProvider, useAuth } from '@/features/auth/AuthProvider' +import { LoginPage } from '@/features/auth/LoginPage' +import { Skeleton } from '@/components/ui/skeleton' export function App() { + return ( + + + + ) +} + +function AppContent() { + const { authenticated, loading } = useAuth() + + if (loading) { + return ( +
+
+ + + + +
+
+ ) + } + + if (!authenticated) { + return + } + return ( diff --git a/frontend/src/components/app/AppSidebar.tsx b/frontend/src/components/app/AppSidebar.tsx index 81940c8..3304a94 100644 --- a/frontend/src/components/app/AppSidebar.tsx +++ b/frontend/src/components/app/AppSidebar.tsx @@ -1,32 +1,66 @@ -import { Bot, BriefcaseBusiness, Database, Home, Library, Settings, ShieldCheck, Ticket } from 'lucide-react' +import * as React from 'react' +import { + Bot, + BriefcaseBusiness, + ClipboardList, + Database, + Home, + Library, + Menu, + ScrollText, + Settings, + ShieldCheck, + Ticket, + UserCog, + Users, + UsersRound +} from 'lucide-react' +import { useAuth } from '@/features/auth/AuthProvider' +import type { AuthMenu } from '@/features/auth/types' import { cn } from '@/lib/utils' -const navGroups = [ - { - label: '工作台', - items: [{ href: '#/', label: '运营总览', icon: Home }] - }, - { - label: '业务处理', - items: [ - { href: '#/tickets', label: '工单中心', icon: Ticket }, - { href: '#/tickets/new', label: '创建工单', icon: BriefcaseBusiness }, - { href: '#/solutions', label: '解决方案库', icon: Library } - ] - }, - { - label: '平台能力', - items: [ - { href: '#/knowledge', label: '知识库', icon: Database }, - { href: '#/agents', label: 'Agent 配置', icon: Bot }, - { href: '#/system', label: '系统治理', icon: ShieldCheck }, - { href: '#/design-system', label: '设计系统', icon: Settings } - ] - } -] +const iconMap = { + audit: ScrollText, + bot: Bot, + briefcase: BriefcaseBusiness, + database: Database, + home: Home, + library: Library, + menu: Menu, + roles: UserCog, + settings: Settings, + shield: ShieldCheck, + ticket: Ticket, + user_groups: UsersRound, + users: Users +} + +function getIcon(icon: string) { + return iconMap[icon as keyof typeof iconMap] ?? ClipboardList +} + +function useCurrentHash() { + const [hash, setHash] = React.useState(() => window.location.hash || '#/') + + React.useEffect(() => { + const handleHashChange = () => setHash(window.location.hash || '#/') + window.addEventListener('hashchange', handleHashChange) + return () => window.removeEventListener('hashchange', handleHashChange) + }, []) + + return hash +} + +function visibleChildren(menu: AuthMenu) { + return menu.children.filter((item) => item.is_visible && item.is_enabled) +} export function AppSidebar() { + const { user } = useAuth() + const currentHash = useCurrentHash() + const groups = (user?.menus ?? []).filter((menu) => menu.is_visible && menu.is_enabled) + return (