更新管理员登录和鉴权逻辑,优化用户体验;重构相关API以支持更安全的身份验证;调整数据库初始化以兼容新字段,确保用户信息安全;修复部分组件样式和功能,提升整体可用性。

This commit is contained in:
乘风
2026-01-31 23:25:14 +08:00
parent c7b125535c
commit bd23273190
22 changed files with 861 additions and 150 deletions

View File

@@ -3,17 +3,43 @@
import type React from "react"
import { useState, useEffect } from "react"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { usePathname, useRouter } from "next/navigation"
import { LayoutDashboard, FileText, Users, CreditCard, Settings, LogOut, Wallet, Globe, BookOpen } 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 = [
@@ -25,8 +51,13 @@ export default function AdminLayout({ children }: { children: React.ReactNode })
{ icon: Settings, label: "系统设置", href: "/admin/settings" },
]
// 避免hydration错误等待客户端mount
if (!mounted) {
// 登录页:不渲染侧栏,只渲染子页面
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" />
@@ -66,12 +97,19 @@ export default function AdminLayout({ children }: { children: React.ReactNode })
})}
</nav>
<div className="p-4 border-t border-gray-700/50">
<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="/"
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"
>
<LogOut className="w-5 h-5" />
<span className="text-sm"></span>
</Link>
</div>

View File

@@ -5,11 +5,9 @@ import { useRouter } from "next/navigation"
import { Lock, User, ShieldCheck } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { useStore } from "@/lib/store"
export default function AdminLoginPage() {
const router = useRouter()
const { adminLogin } = useStore()
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
@@ -18,14 +16,22 @@ export default function AdminLoginPage() {
const handleLogin = async () => {
setError("")
setLoading(true)
await new Promise((resolve) => setTimeout(resolve, 500))
const success = adminLogin(username, password)
if (success) {
router.push("/admin")
} else {
setError("用户名或密码错误")
try {
const res = await fetch("/api/admin", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: username.trim(), password }),
credentials: "include",
})
const data = await res.json()
if (res.ok && data.success) {
router.push("/admin")
return
}
setError(data.error || "用户名或密码错误")
} catch {
setError("网络错误,请重试")
} finally {
setLoading(false)
}
}
@@ -95,12 +101,6 @@ export default function AdminLoginPage() {
</Button>
</div>
<div className="mt-6 pt-6 border-t border-gray-700/50">
<p className="text-gray-500 text-xs text-center">
: <span className="text-gray-300 font-mono">admin</span> /{" "}
<span className="text-gray-300 font-mono">key123456</span>
</p>
</div>
</div>
{/* Footer */}