208 lines
7.2 KiB
TypeScript
Raw Normal View History

import { useState } from 'react';
import { useRouter } from 'next/router';
import Head from 'next/head';
import Link from 'next/link';
import { toast } from 'react-hot-toast';
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
import { auth } from '@/lib/supabase';
interface LoginForm {
email: string;
password: string;
}
export default function Login() {
const router = useRouter();
const [form, setForm] = useState<LoginForm>({
email: '',
password: '',
});
const [loading, setLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setForm(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!form.email || !form.password) {
toast.error('请填写所有必填字段');
return;
}
setLoading(true);
try {
// 检查是否为演示模式
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const isDemoMode = !supabaseUrl || supabaseUrl === 'https://demo.supabase.co' || supabaseUrl === '';
if (isDemoMode) {
// 演示模式:检查测试账号
if (form.email === 'admin@demo.com' && form.password === 'admin123') {
toast.success('登录成功!');
// 在演示模式下直接跳转到仪表盘
router.push('/dashboard');
} else {
toast.error('演示模式:请使用测试账号 admin@demo.com / admin123');
}
} else {
// 真实模式:使用 Supabase 认证
try {
await auth.signIn(form.email, form.password);
toast.success('登录成功!');
router.push('/dashboard');
} catch (authError: any) {
console.error('Supabase auth error:', authError);
toast.error(authError.message || '登录失败,请检查邮箱和密码');
}
}
} catch (error: any) {
console.error('Login error:', error);
toast.error('登录过程中发生错误,请稍后重试');
} finally {
setLoading(false);
}
};
// 填入测试账号
const fillTestAccount = () => {
setForm({
email: 'admin@demo.com',
password: 'admin123'
});
};
return (
<>
<Head>
<title> - </title>
</Head>
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
</p>
</div>
{/* 测试账号提示 */}
<div className="bg-blue-50 border border-blue-200 rounded-md p-4">
<div className="flex">
<div className="ml-3">
<h3 className="text-sm font-medium text-blue-800">
</h3>
<div className="mt-2 text-sm text-blue-700">
<p>admin@demo.com</p>
<p>admin123</p>
<button
type="button"
onClick={fillTestAccount}
className="mt-2 text-xs text-blue-600 hover:text-blue-500 underline"
>
</button>
</div>
</div>
</div>
</div>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="email" className="sr-only">
</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="管理员邮箱"
value={form.email}
onChange={handleInputChange}
/>
</div>
<div className="relative">
<label htmlFor="password" className="sr-only">
</label>
<input
id="password"
name="password"
type={showPassword ? 'text' : 'password'}
autoComplete="current-password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 pr-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="管理员密码"
value={form.password}
onChange={handleInputChange}
/>
<button
type="button"
className="absolute inset-y-0 right-0 pr-3 flex items-center"
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? (
<EyeSlashIcon className="h-5 w-5 text-gray-400" />
) : (
<EyeIcon className="h-5 w-5 text-gray-400" />
)}
</button>
</div>
</div>
<div className="flex items-center justify-between">
<div className="text-sm">
<Link
href="/"
className="font-medium text-blue-600 hover:text-blue-500"
>
</Link>
</div>
<div className="text-sm">
<a
href="#"
className="font-medium text-blue-600 hover:text-blue-500"
>
</a>
</div>
</div>
<div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? (
<div className="flex items-center">
<div className="loading-spinner-sm mr-2"></div>
...
</div>
) : (
'登录'
)}
</button>
</div>
</form>
</div>
</div>
</>
);
}