feat(theme): add dark mode with next-themes and settings toggle

This commit is contained in:
zetaloop
2026-02-25 20:01:52 +08:00
parent c55d533925
commit 336aa36d5a
7 changed files with 91 additions and 8 deletions
+38
View File
@@ -6,13 +6,22 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch"
import { Textarea } from "@/components/ui/textarea"
import { notifySuccess } from "@/lib/toast"
import type { UserRole } from "@/lib/types"
import type { ThemePreference } from "@/store/auth"
import { useAuthStore } from "@/store/auth"
import { Camera } from "lucide-react"
import { useTheme } from "next-themes"
import Link from "next/link"
import { useRef, useState } from "react"
@@ -32,6 +41,14 @@ export default function SettingsPage() {
const fileRef = useRef<HTMLInputElement>(null)
const isRoleVerified = (role: UserRole) => verifiedRoles.includes(role)
const { theme, setTheme } = useTheme()
const setThemePreference = useAuthStore((s) => s.setThemePreference)
function handleThemeChange(value: string) {
const pref = value as ThemePreference
setTheme(pref)
setThemePreference(pref)
}
return (
<div className="container mx-auto max-w-2xl px-4 py-8 space-y-6">
@@ -174,6 +191,27 @@ export default function SettingsPage() {
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center justify-between">
<Label></Label>
<Select value={theme} onValueChange={handleThemeChange}>
<SelectTrigger className="w-32">
<SelectValue placeholder="选择主题" />
</SelectTrigger>
<SelectContent>
<SelectItem value="light"></SelectItem>
<SelectItem value="dark"></SelectItem>
<SelectItem value="system"></SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
+1 -1
View File
@@ -24,7 +24,7 @@ export default function RootLayout({
children: React.ReactNode
}>) {
return (
<html lang="zh-CN">
<html lang="zh-CN" suppressHydrationWarning>
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<Providers>{children}</Providers>
</body>
+10 -5
View File
@@ -1,8 +1,10 @@
"use client"
import { GlobalLoginDialog } from "@/components/global-login-dialog"
import { ThemeSyncEffect } from "@/components/theme-sync"
import { TooltipProvider } from "@/components/ui/tooltip"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ThemeProvider } from "next-themes"
import { useState } from "react"
import { Toaster } from "sonner"
@@ -21,11 +23,14 @@ export function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<TooltipProvider>
{children}
<Toaster richColors position="top-center" />
<GlobalLoginDialog />
</TooltipProvider>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
<TooltipProvider>
<ThemeSyncEffect />
{children}
<Toaster richColors position="top-center" />
<GlobalLoginDialog />
</TooltipProvider>
</ThemeProvider>
</QueryClientProvider>
)
}