3v324v23's picture
chore: 彻底清理项目,符合 Hugging Face 部署规范
ae4ceef
import React, { useState, useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useStore } from '@/store/useStore';
import { useTranslation } from 'react-i18next';
import { motion } from 'framer-motion';
import { Mail, Lock, User, Loader2, LogIn, ArrowLeft } from 'lucide-react';
export default function AuthPage() {
const { t } = useTranslation();
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { setUser, setToken, token } = useStore();
const initialMode = searchParams.get('mode') === 'register' ? false : true;
const [isLogin, setIsLogin] = useState(initialMode);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const [formData, setFormData] = useState({
email: '',
password: '',
name: ''
});
// 如果已经登录,直接跳转到 dashboard 或指定的 redirect
useEffect(() => {
if (token) {
const redirect = searchParams.get('redirect') || '/dashboard';
const openPaymentModal = searchParams.get('openPaymentModal');
let targetUrl = redirect;
if (openPaymentModal) {
const url = new URL(redirect, window.location.origin);
url.searchParams.set('openPaymentModal', openPaymentModal);
targetUrl = url.pathname + url.search;
}
navigate(targetUrl, { replace: true });
}
}, [token, navigate, searchParams]);
// 监听 URL 参数变化
useEffect(() => {
const mode = searchParams.get('mode');
if (mode === 'register') setIsLogin(false);
else if (mode === 'login') setIsLogin(true);
}, [searchParams]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError('');
const endpoint = isLogin ? '/api/auth/login' : '/api/auth/register';
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
const data = await response.json();
if (data.success) {
if (isLogin) {
setUser(data.user);
setToken(data.token);
const redirect = searchParams.get('redirect') || '/dashboard';
const openPaymentModal = searchParams.get('openPaymentModal');
let targetUrl = redirect;
if (openPaymentModal) {
const url = new URL(redirect, window.location.origin);
url.searchParams.set('openPaymentModal', openPaymentModal);
targetUrl = url.pathname + url.search;
}
navigate(targetUrl);
} else {
setIsLogin(true);
setFormData({ ...formData, password: '' });
alert('注册成功,请登录');
}
} else {
setError(data.error || '认证失败');
}
} catch (err) {
setError('网络错误,请稍后再试');
} finally {
setIsLoading(false);
}
};
return (
<div className="min-h-screen bg-zinc-50 flex flex-col items-center justify-center p-4">
<motion.button
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
onClick={() => navigate('/')}
className="mb-8 flex items-center gap-2 text-zinc-500 hover:text-zinc-900 transition-colors group"
>
<ArrowLeft size={18} className="group-hover:-translate-x-1 transition-transform" />
返回首页
</motion.button>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="w-full max-w-md bg-white rounded-2xl shadow-xl border border-zinc-200 p-8"
>
<div className="flex justify-center mb-8">
<div className="w-12 h-12 bg-blue-600 rounded-xl flex items-center justify-center text-white shadow-lg shadow-blue-200">
<LogIn size={24} />
</div>
</div>
<h2 className="text-2xl font-bold text-center text-zinc-900 mb-2">
{isLogin ? '欢迎回来' : '开启 AI 之旅'}
</h2>
<p className="text-zinc-500 text-center mb-8 text-sm">
{isLogin ? '请登录您的账号以访问工作台' : '只需几秒钟即可创建您的账号'}
</p>
<form onSubmit={handleSubmit} className="space-y-4">
{!isLogin && (
<div className="relative">
<User className="absolute left-3 top-3 text-zinc-400" size={18} />
<input
type="text"
placeholder="您的姓名"
required
className="w-full pl-10 pr-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
</div>
)}
<div className="relative">
<Mail className="absolute left-3 top-3 text-zinc-400" size={18} />
<input
type="email"
placeholder="电子邮箱"
required
className="w-full pl-10 pr-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
/>
</div>
<div className="relative">
<Lock className="absolute left-3 top-3 text-zinc-400" size={18} />
<input
type="password"
placeholder="密码"
required
className="w-full pl-10 pr-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
/>
</div>
{error && (
<p className="text-red-500 text-xs mt-1 text-center">{error}</p>
)}
<button
type="submit"
disabled={isLoading}
className="w-full py-3 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition-colors shadow-lg shadow-blue-200 disabled:opacity-50 flex items-center justify-center gap-2"
>
{isLoading ? <Loader2 size={18} className="animate-spin" /> : (isLogin ? '登录' : '注册')}
</button>
</form>
<div className="mt-6 text-center">
<button
onClick={() => setIsLogin(!isLogin)}
className="text-sm text-zinc-500 hover:text-blue-600 transition-colors"
>
{isLogin ? '还没有账号?立即注册' : '已有账号?返回登录'}
</button>
</div>
</motion.div>
</div>
);
}