This commit is contained in:
ryan
2026-06-07 19:47:24 +08:00
parent 48d414e197
commit 360a26f109
60 changed files with 10063 additions and 637 deletions
+55 -39
View File
@@ -1,13 +1,13 @@
"use client"
import { motion } from "motion/react"
import { usePathname } from "next/navigation"
import { useState } from "react"
import { AppSidebar } from "@/components/layout/sidebar"
import { SiteHeader } from "@/components/layout/header"
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"
import { UserProvider } from "@/contexts/user-context"
import {motion} from "motion/react"
import {usePathname, useRouter} from "next/navigation"
import {useEffect, useState} from "react"
import {AppSidebar} from "@/components/layout/sidebar"
import {SiteHeader} from "@/components/layout/header"
import {SidebarInset, SidebarProvider} from "@/components/ui/sidebar"
import {LoadingPage} from "@/components/layout/loading"
import {useUser} from "@/contexts/user-context"
export default function MainLayout({
@@ -15,40 +15,56 @@ export default function MainLayout({
}: {
children: React.ReactNode
}) {
const router = useRouter()
const pathname = usePathname()
const {user, loading} = useUser()
const [isFullWidth, setIsFullWidth] = useState(false)
useEffect(() => {
if (loading || user) return
const queryString = window.location.search
const callbackUrl = queryString ? `${pathname}${queryString}` : pathname
const loginUrl = new URL("/login", window.location.origin)
loginUrl.searchParams.set("callbackUrl", callbackUrl)
sessionStorage.setItem("redirect_after_login", callbackUrl)
router.replace(loginUrl.toString())
}, [loading, pathname, router, user])
if (loading || !user) {
return <LoadingPage text="登录状态" badgeText="Auth" />
}
return (
<UserProvider>
<SidebarProvider
className="h-screen"
style={
{
"--header-height": "60px",
} as React.CSSProperties
}
>
<AppSidebar />
<SidebarInset className="flex flex-col min-w-0 h-screen">
<SiteHeader isFullWidth={isFullWidth} onToggleFullWidth={setIsFullWidth} />
<div className="flex flex-1 flex-col bg-background overflow-y-auto overflow-x-hidden min-w-0 hide-scrollbar">
<div className={`w-full mx-auto px-4 sm:px-6 md:px-8 lg:px-12 min-w-0 transition-all duration-300 ease-in-out ${!isFullWidth ? "max-w-[1320px]" : "max-w-full"}`}>
<motion.div
key={pathname}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
duration: 0.5,
ease: "easeOut",
}}
className="w-full"
>
{children}
</motion.div>
</div>
</div>
</SidebarInset>
</SidebarProvider>
</UserProvider>
<SidebarProvider
className="h-screen"
style={
{
"--header-height": "60px",
} as React.CSSProperties
}
>
<AppSidebar />
<SidebarInset className="flex flex-col min-w-0 h-screen">
<SiteHeader isFullWidth={isFullWidth} onToggleFullWidth={setIsFullWidth} />
<div className="flex flex-1 flex-col bg-background overflow-y-auto overflow-x-hidden min-w-0 hide-scrollbar">
<div className={`w-full mx-auto px-4 sm:px-6 md:px-8 lg:px-12 min-w-0 transition-all duration-300 ease-in-out ${!isFullWidth ? "max-w-[1320px]" : "max-w-full"}`}>
<motion.div
key={pathname}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
duration: 0.5,
ease: "easeOut",
}}
className="w-full"
>
{children}
</motion.div>
</div>
</div>
</SidebarInset>
</SidebarProvider>
)
}
+26 -6
View File
@@ -1,7 +1,9 @@
import Link from "next/link"
import { Card, CardTitle, CardDescription, CardContent } from "@/components/ui/card"
import { UserRound, Bell, Shield, Palette } from "lucide-react"
"use client"
import Link from "next/link"
import {Card, CardContent, CardDescription, CardTitle} from "@/components/ui/card"
import {Bell, Loader2, Palette, Shield, UserRound} from "lucide-react"
import {useAuth} from "@/components/providers/auth-provider"
/* 设置项 */
const settingsItems = [
@@ -28,7 +30,7 @@ const settingsItems = [
},
{
title: "外观设置",
description: "自定义界面主题和显示",
description: "自定义界面主题 and 显示",
icon: Palette,
href: "/settings/appearance",
category: "个人设置",
@@ -36,7 +38,25 @@ const settingsItems = [
]
export default function SettingsPage() {
const groupedSettings = settingsItems.reduce((acc, item) => {
const { user, loading } = useAuth()
if (loading) {
return (
<div className="flex items-center justify-center min-h-[200px]">
<Loader2 className="size-6 animate-spin text-indigo-500" />
</div>
)
}
// 非管理员过滤掉安全设置
const filteredItems = settingsItems.filter((item) => {
if (item.href === "/settings/security") {
return !!user?.is_admin
}
return true
})
const groupedSettings = filteredItems.reduce((acc, item) => {
if (!acc[item.category]) {
acc[item.category] = []
}
@@ -46,7 +66,7 @@ export default function SettingsPage() {
return (
<div className="space-y-6 py-6">
<div className="font-semibold">设置</div>
<div className="font-semibold text-lg">设置</div>
{Object.entries(groupedSettings).map(([category, items]) => (
<div key={category} className="space-y-4">
+18 -12
View File
@@ -1,9 +1,11 @@
import type { Metadata } from "next";
import { Toaster } from "@/components/ui/sonner";
import { ThemeProvider } from "@/components/layout/theme-provider";
import { CustomThemeProvider } from "@/lib/theme";
import { BellRingProvider } from "@/contexts/bell-ring-context";
import { NotificationSettingsProvider } from "@/contexts/notification-settings-context";
import type {Metadata} from "next";
import {Toaster} from "@/components/ui/sonner";
import {ThemeProvider} from "@/components/layout/theme-provider";
import {CustomThemeProvider} from "@/lib/theme";
import {BellRingProvider} from "@/contexts/bell-ring-context";
import {NotificationSettingsProvider} from "@/contexts/notification-settings-context";
import {UserProvider} from "@/contexts/user-context";
import {AppQueryProvider} from "@/components/providers/query-provider";
import "./globals.css";
export const metadata: Metadata = {
@@ -32,12 +34,16 @@ export default function RootLayout({
disableTransitionOnChange
>
<CustomThemeProvider>
<NotificationSettingsProvider>
<BellRingProvider>
{children}
<Toaster position="top-center" />
</BellRingProvider>
</NotificationSettingsProvider>
<AppQueryProvider>
<UserProvider>
<NotificationSettingsProvider>
<BellRingProvider>
{children}
<Toaster position="top-center" />
</BellRingProvider>
</NotificationSettingsProvider>
</UserProvider>
</AppQueryProvider>
</CustomThemeProvider>
</ThemeProvider>
</body>