Files
soul-yongping/app/admin/layout.tsx

124 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import type React from "react"
import { useState, useEffect } from "react"
import Link from "next/link"
import { usePathname, useRouter } from "next/navigation"
import { LayoutDashboard, FileText, Users, CreditCard, Settings, LogOut, Wallet, Globe, BookOpen, Banknote } from "lucide-react"
export default function AdminLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
const router = useRouter()
const [mounted, setMounted] = useState(false)
const [authChecked, setAuthChecked] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
// 非登录页时校验 Cookie未登录则跳转登录页
useEffect(() => {
if (!mounted || pathname === "/admin/login") return
setAuthChecked(false)
let cancelled = false
fetch("/api/admin", { credentials: "include" })
.then((res) => {
if (cancelled) return
if (res.status === 401) router.replace("/admin/login")
else setAuthChecked(true)
})
.catch(() => {
if (!cancelled) setAuthChecked(true)
})
return () => {
cancelled = true
}
}, [mounted, pathname, router])
const handleLogout = async () => {
await fetch("/api/admin/logout", { method: "POST", credentials: "include" })
router.replace("/admin/login")
}
// 简化菜单:按功能归类,保留核心功能
// PDF需求分账管理、分销管理、订单管理三合一 → 交易中心
const menuItems = [
{ icon: LayoutDashboard, label: "数据概览", href: "/admin" },
{ icon: BookOpen, label: "内容管理", href: "/admin/content" },
{ icon: Users, label: "用户管理", href: "/admin/users" },
{ icon: Wallet, label: "交易中心", href: "/admin/distribution" }, // 合并:分销+订单+提现
{ icon: CreditCard, label: "推广设置", href: "/admin/referral-settings" },
{ icon: Banknote, label: "支付配置", href: "/admin/payment" },
{ icon: Settings, label: "系统设置", href: "/admin/settings" },
]
// 登录页:不渲染侧栏,只渲染子页面
if (pathname === "/admin/login") {
return <div className="min-h-screen bg-[#0a1628]">{children}</div>
}
// 避免 hydration 错误,等待客户端 mount 并完成鉴权
if (!mounted || !authChecked) {
return (
<div className="flex min-h-screen bg-[#0a1628]">
<div className="w-64 bg-[#0f2137] border-r border-gray-700/50" />
<div className="flex-1 flex items-center justify-center">
<div className="text-[#38bdac]">...</div>
</div>
</div>
)
}
return (
<div className="flex min-h-screen bg-[#0a1628]">
{/* Sidebar - 深色侧边栏 */}
<div className="w-64 bg-[#0f2137] flex flex-col border-r border-gray-700/50 shadow-xl">
<div className="p-6 border-b border-gray-700/50">
<h1 className="text-xl font-bold text-[#38bdac]"></h1>
<p className="text-xs text-gray-400 mt-1">Soul创业派对</p>
</div>
<nav className="flex-1 p-4 space-y-1">
{menuItems.map((item) => {
const isActive = pathname === item.href
return (
<Link
key={item.href}
href={item.href}
className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${
isActive
? "bg-[#38bdac]/20 text-[#38bdac] font-medium"
: "text-gray-400 hover:bg-gray-700/50 hover:text-white"
}`}
>
<item.icon className="w-5 h-5" />
<span className="text-sm">{item.label}</span>
</Link>
)
})}
</nav>
<div className="p-4 border-t border-gray-700/50 space-y-1">
<button
type="button"
onClick={handleLogout}
className="w-full flex items-center gap-3 px-4 py-3 text-gray-400 hover:text-white rounded-lg hover:bg-gray-700/50 transition-colors"
>
<LogOut className="w-5 h-5" />
<span className="text-sm">退</span>
</button>
<Link
href="/view"
className="flex items-center gap-3 px-4 py-3 text-gray-400 hover:text-white rounded-lg hover:bg-gray-700/50 transition-colors"
>
<span className="text-sm"></span>
</Link>
</div>
</div>
{/* Main Content - 深色背景 */}
<div className="flex-1 overflow-auto bg-[#0a1628]">{children}</div>
</div>
)
}