diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx
index 4d15e6f..99c569e 100644
--- a/frontend/src/app/App.tsx
+++ b/frontend/src/app/App.tsx
@@ -1,14 +1,17 @@
import { AppShell } from './layout/AppShell'
import { Router } from './router'
+import { AppThemeProvider } from '@/components/app/AppThemeProvider'
import { AuthProvider, useAuth } from '@/features/auth/AuthProvider'
import { LoginPage } from '@/features/auth/LoginPage'
import { Skeleton } from '@/components/ui/skeleton'
export function App() {
return (
-
-
-
+
+
+
+
+
)
}
diff --git a/frontend/src/components/app/AppThemeProvider.tsx b/frontend/src/components/app/AppThemeProvider.tsx
new file mode 100644
index 0000000..6c5061c
--- /dev/null
+++ b/frontend/src/components/app/AppThemeProvider.tsx
@@ -0,0 +1,14 @@
+import type * as React from 'react'
+import { ThemeProvider } from 'next-themes'
+
+type AppThemeProviderProps = {
+ children: React.ReactNode
+}
+
+export function AppThemeProvider({ children }: AppThemeProviderProps) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/frontend/src/components/app/AppThemeToggle.tsx b/frontend/src/components/app/AppThemeToggle.tsx
new file mode 100644
index 0000000..0387103
--- /dev/null
+++ b/frontend/src/components/app/AppThemeToggle.tsx
@@ -0,0 +1,53 @@
+import { Laptop, Moon, Sun } from 'lucide-react'
+import { useTheme } from 'next-themes'
+
+import { Button } from '@/components/ui/button'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger
+} from '@/components/ui/dropdown-menu'
+
+const themeOptions = [
+ { value: 'light', label: '亮色', icon: Sun },
+ { value: 'dark', label: '暗色', icon: Moon },
+ { value: 'system', label: '跟随系统', icon: Laptop }
+]
+
+export function AppThemeToggle() {
+ const { theme = 'system', setTheme } = useTheme()
+ const activeTheme = themeOptions.find((option) => option.value === theme) ?? themeOptions[2]
+ const ActiveIcon = activeTheme.icon
+
+ return (
+
+
+
+
+
+ 界面主题
+
+
+
+ {themeOptions.map((option) => {
+ const Icon = option.icon
+ return (
+
+
+ {option.label}
+
+ )
+ })}
+
+
+
+
+ )
+}
diff --git a/frontend/src/components/app/AppTopbar.tsx b/frontend/src/components/app/AppTopbar.tsx
index 86213aa..a90d54d 100644
--- a/frontend/src/components/app/AppTopbar.tsx
+++ b/frontend/src/components/app/AppTopbar.tsx
@@ -1,5 +1,6 @@
import { Bell, ChevronRight, LogOut, Menu, UserRound } from 'lucide-react'
+import { AppThemeToggle } from '@/components/app/AppThemeToggle'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
@@ -34,6 +35,7 @@ export function AppTopbar({ onMobileMenuClick }: AppTopbarProps) {
+