feat: 添加管理后台页面和功能,优化测试和性能配置

refactor: 重构页面导航和滚动逻辑,提升用户体验

test: 更新测试配置和用例,增加覆盖率和稳定性

perf: 优化性能指标和阈值,适应开发环境需求

ci: 添加Lighthouse CI工作流,集成性能测试

docs: 更新API文档和健康检查端点

fix: 修复登录页面和表单提交问题

style: 调整响应式布局和可访问性改进

chore: 更新依赖项和脚本配置
This commit is contained in:
张翔
2026-03-24 10:11:30 +08:00
parent 08978d38c8
commit f5dec95a83
85 changed files with 12331 additions and 1408 deletions
+13 -8
View File
@@ -13,7 +13,7 @@ import {
X,
Activity
} from 'lucide-react';
import { useState } from 'react';
import { useState, useEffect } from 'react';
const navigation = [
{ name: '仪表盘', href: '/admin', icon: LayoutDashboard },
@@ -31,19 +31,24 @@ export default function AdminLayout({
const { data: session, status } = useSession();
const pathname = usePathname();
const [sidebarOpen, setSidebarOpen] = useState(false);
const [mounted, setMounted] = useState(false);
const isLoginPage = pathname === '/admin/login';
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
if (isLoginPage) {
return <>{children}</>;
return <div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">{children}</div>;
}
if (status === 'loading') {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#C41E3A]"></div>
</div>
);
return null;
}
if (status === 'unauthenticated') {
@@ -151,4 +156,4 @@ export default function AdminLayout({
</main>
</div>
);
}
}
+8 -68
View File
@@ -1,67 +1,37 @@
'use client';
import { useState, useEffect, Suspense } from 'react';
import { useState } from 'react';
import { signIn } from 'next-auth/react';
import { useRouter, useSearchParams } from 'next/navigation';
import { Eye, EyeOff, Mail, Lock, AlertCircle, Loader2 } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { Eye, EyeOff, Mail, Lock, AlertCircle } from 'lucide-react';
function LoginForm() {
export default function LoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get('callbackUrl') || '/admin';
const urlError = searchParams.get('error');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [email, setEmail] = useState('admin@novalon.cn');
const [password, setPassword] = useState('admin123456');
const [showPassword, setShowPassword] = useState(false);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
if (urlError) {
switch (urlError) {
case 'Configuration':
setError('认证配置错误,请联系管理员');
break;
case 'AccessDenied':
setError('访问被拒绝,请检查权限');
break;
case 'Verification':
setError('验证失败,请重试');
break;
case 'CredentialsSignin':
setError('邮箱或密码错误');
break;
default:
setError('登录失败,请稍后重试');
}
}
}, [urlError]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);
try {
console.log('开始登录...', { email, callbackUrl });
const result = await signIn('credentials', {
email,
password,
redirect: false,
});
console.log('登录结果:', result);
if (result?.error) {
console.error('登录错误:', result.error);
setError('邮箱或密码错误');
} else {
console.log('登录成功,准备跳转到:', callbackUrl);
router.push(callbackUrl);
router.push('/admin');
}
} catch (err) {
console.error('登录异常:', err);
setError('登录失败,请稍后重试');
} finally {
setLoading(false);
@@ -150,34 +120,4 @@ function LoginForm() {
</div>
</div>
);
}
function LoginLoading() {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-gray-100 px-4">
<div className="w-full max-w-md">
<div className="bg-white rounded-2xl shadow-xl border border-gray-200 p-8">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-[#C41E3A]"></h1>
<p className="text-gray-600 mt-2"></p>
</div>
<div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-[#C41E3A]" />
<span className="ml-3 text-gray-600">...</span>
</div>
</div>
<p className="text-center text-xs text-gray-500 mt-6">
© {new Date().getFullYear()}
</p>
</div>
</div>
);
}
export default function LoginPage() {
return (
<Suspense fallback={<LoginLoading />}>
<LoginForm />
</Suspense>
);
}
}
+37
View File
@@ -0,0 +1,37 @@
export default function SimpleLoginPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="bg-white p-8 rounded-lg shadow-md">
<h1 className="text-2xl font-bold mb-4"></h1>
<form className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium mb-1"></label>
<input
id="email"
type="email"
name="email"
className="w-full px-3 py-2 border rounded-md"
placeholder="admin@novalon.cn"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium mb-1"></label>
<input
id="password"
type="password"
name="password"
className="w-full px-3 py-2 border rounded-md"
placeholder="admin123456"
/>
</div>
<button
type="submit"
className="w-full bg-[#C41E3A] text-white py-2 rounded-md hover:bg-[#A01828]"
>
</button>
</form>
</div>
</div>
);
}
+10
View File
@@ -0,0 +1,10 @@
export default function SimpleAdminPage() {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-2xl font-bold">Simple Admin Page</h1>
<p className="mt-4">admin页面</p>
</div>
</div>
);
}
+10
View File
@@ -0,0 +1,10 @@
export default function AdminTestPage() {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-2xl font-bold">Admin Test Page</h1>
<p className="mt-4">admin路由是工作的</p>
</div>
</div>
);
}