| | |
| | | import Image from 'next/image'; |
| | | import { motion } from 'framer-motion'; |
| | | import Link from 'next/link'; |
| | | import { useRouter } from 'next/navigation'; |
| | | import { useUser } from '@/context/UserContext'; |
| | | import ApiService from '@/utils/api'; |
| | | |
| | | // 定义User接口 |
| | | interface User { |
| | | id: number; |
| | | username: string; |
| | | email: string; |
| | | // 其他用户属性... |
| | | } |
| | | |
| | | export default function LoginPage() { |
| | | const [email, setEmail] = useState(''); |
| | |
| | | const [isLoading, setIsLoading] = useState(false); |
| | | const [mounted, setMounted] = useState(false); |
| | | const [loginMethod, setLoginMethod] = useState<'password' | 'sms'>('password'); |
| | | const [error, setError] = useState(''); |
| | | const router = useRouter(); |
| | | const { setUser } = useUser(); |
| | | |
| | | // 确保组件挂载后再显示动画效果 |
| | | useEffect(() => { |
| | | setMounted(true); |
| | | }, []); |
| | | |
| | | const handleSubmit = (e: React.FormEvent) => { |
| | | const handleSubmit = async (e: React.FormEvent) => { |
| | | e.preventDefault(); |
| | | setIsLoading(true); |
| | | setError(''); // 清除之前的错误信息 |
| | | |
| | | // 模拟登录请求 |
| | | setTimeout(() => { |
| | | try { |
| | | const response = await ApiService.post<string>('/users/login', { |
| | | accountName: email, |
| | | password, |
| | | }); |
| | | |
| | | if (response.code === 200) { |
| | | // 获取token |
| | | const token = response.data; |
| | | // 使用ApiService的方法设置token |
| | | ApiService.setToken(token); |
| | | |
| | | // 使用新token获取用户信息 |
| | | try { |
| | | const userData = await ApiService.get<User>('/users/info', token); |
| | | |
| | | if (userData.code === 200) { |
| | | // 保存用户信息到全局状态 |
| | | setUser(userData.data); |
| | | router.push('/'); // 登录成功后跳转到首页 |
| | | } else { |
| | | setError('获取用户信息失败'); |
| | | } |
| | | } catch (error) { |
| | | setError('获取用户信息失败'); |
| | | } |
| | | } else { |
| | | setError(response.message || '登录失败,请检查账号密码'); |
| | | } |
| | | } catch (err) { |
| | | setError('网络错误,请稍后重试'); |
| | | } finally { |
| | | setIsLoading(false); |
| | | // 这里应该添加实际登录逻辑 |
| | | }, 2000); |
| | | } |
| | | }; |
| | | |
| | | return ( |
| | |
| | | |
| | | {/* 账号登录表单 */} |
| | | <form onSubmit={handleSubmit} className="space-y-4"> |
| | | {error && ( |
| | | <motion.div |
| | | initial={{ opacity: 0, y: -10 }} |
| | | animate={{ opacity: 1, y: 0 }} |
| | | className="relative overflow-hidden backdrop-blur-sm bg-[#FF6A88]/5 border border-[#FF6A88]/20 rounded-lg p-3" |
| | | > |
| | | <div className="absolute top-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-[#FF6A88]/50 to-transparent"></div> |
| | | <div className="absolute bottom-0 left-0 right-0 h-[1px] bg-gradient-to-r from-transparent via-[#FF6A88]/50 to-transparent"></div> |
| | | <div className="absolute top-0 bottom-0 left-0 w-[1px] bg-gradient-to-b from-transparent via-[#FF6A88]/50 to-transparent"></div> |
| | | <div className="absolute top-0 bottom-0 right-0 w-[1px] bg-gradient-to-b from-transparent via-[#FF6A88]/50 to-transparent"></div> |
| | | |
| | | <div className="flex items-center space-x-2"> |
| | | <svg className="w-4 h-4 text-[#FF6A88]" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
| | | <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /> |
| | | </svg> |
| | | <span className="text-xs text-[#FF6A88]">{error}</span> |
| | | </div> |
| | | |
| | | {/* 动态扫描线 */} |
| | | <div className="absolute top-0 -left-full w-[200%] h-full bg-gradient-to-r from-transparent via-[#FF6A88]/10 to-transparent animate-error-scan"></div> |
| | | </motion.div> |
| | | )} |
| | | |
| | | <div className="space-y-3"> |
| | | {loginMethod === 'password' ? ( |
| | | <> |
| | | <motion.div |
| | | className="relative group" |
| | | initial={{ opacity: 0, x: -20 }} |
| | | initial={{ opacity: 1, x: 0 }} |
| | | animate={{ opacity: 1, x: 0 }} |
| | | exit={{ opacity: 0, x: 20 }} |
| | | transition={{ duration: 0.3 }} |
| | |
| | | |
| | | <motion.div |
| | | className="relative group" |
| | | initial={{ opacity: 0, x: -20 }} |
| | | initial={{ opacity: 1, x: 0 }} |
| | | animate={{ opacity: 1, x: 0 }} |
| | | exit={{ opacity: 0, x: 20 }} |
| | | transition={{ duration: 0.3, delay: 0.1 }} |
| | |
| | | </div> |
| | | </form> |
| | | |
| | | {/* 分割线 */} |
| | | <div className="mt-6 relative"> |
| | | <div className="absolute inset-0 flex items-center"> |
| | | <div className="w-full border-t border-[#6ADBFF]/20"></div> |
| | | </div> |
| | | <div className="relative flex justify-center text-xs"> |
| | | <span className="px-3 bg-[#1E2B63]/40 text-[#6ADBFF]/70">其他方式</span> |
| | | </div> |
| | | </div> |
| | | |
| | | {/* 社交登录 - 简化 */} |
| | | <div className="mt-4 flex justify-center space-x-6"> |
| | | <button className="inline-flex justify-center items-center w-8 h-8 border border-[#6ADBFF]/30 rounded-full bg-[#131C41]/60 hover:bg-[#131C41] hover:border-[#6ADBFF]/60 hover:text-[#6ADBFF] transition-all duration-300 group"> |
| | | <svg className="w-4 h-4 text-[#6ADBFF]/70 group-hover:text-[#6ADBFF] transition-colors duration-300" fill="currentColor" viewBox="0 0 24 24"> |
| | | <path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" /> |
| | | </svg> |
| | | </button> |
| | | <button className="inline-flex justify-center items-center w-8 h-8 border border-[#6ADBFF]/30 rounded-full bg-[#131C41]/60 hover:bg-[#131C41] hover:border-[#6ADBFF]/60 hover:text-[#6ADBFF] transition-all duration-300 group"> |
| | | <svg className="w-4 h-4 text-[#6ADBFF]/70 group-hover:text-[#6ADBFF] transition-colors duration-300" fill="currentColor" viewBox="0 0 24 24"> |
| | | <path d="M12 0C5.373 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.6.11.819-.26.819-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61-.546-1.387-1.332-1.756-1.332-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.418-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.468-2.38 1.235-3.22-.123-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.3 1.23A11.51 11.51 0 0112 5.803c1.02.005 2.046.138 3.005.404 2.29-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.91 1.235 3.22 0 4.61-2.805 5.625-5.475 5.92.43.372.824 1.102.824 2.222 0 1.605-.015 2.896-.015 3.286 0 .32.217.694.825.577C20.565 21.795 24 17.298 24 12c0-6.627-5.373-12-12-12z" /> |
| | | </svg> |
| | | </button> |
| | | <button className="inline-flex justify-center items-center w-8 h-8 border border-[#6ADBFF]/30 rounded-full bg-[#131C41]/60 hover:bg-[#131C41] hover:border-[#6ADBFF]/60 hover:text-[#6ADBFF] transition-all duration-300 group"> |
| | | <svg className="w-4 h-4 text-[#6ADBFF]/70 group-hover:text-[#6ADBFF] transition-colors duration-300" fill="currentColor" viewBox="0 0 24 24"> |
| | | <path d="M12 2C6.48 2 2 6.48 2 12C2 16.42 4.87 20.17 8.84 21.5C9.34 21.58 9.5 21.27 9.5 21C9.5 20.77 9.5 20.14 9.5 19.31C6.73 19.91 6.14 17.97 6.14 17.97C5.68 16.81 5.03 16.5 5.03 16.5C4.12 15.88 5.1 15.9 5.1 15.9C6.1 15.97 6.63 16.93 6.63 16.93C7.5 18.45 8.97 18 9.54 17.76C9.63 17.11 9.89 16.67 10.17 16.42C7.95 16.17 5.62 15.31 5.62 11.5C5.62 10.39 6 9.5 6.65 8.79C6.55 8.54 6.2 7.5 6.75 6.15C6.75 6.15 7.59 5.88 9.5 7.17C10.29 6.95 11.15 6.84 12 6.84C12.85 6.84 13.71 6.95 14.5 7.17C16.41 5.88 17.25 6.15 17.25 6.15C17.8 7.5 17.45 8.54 17.35 8.79C18 9.5 18.38 10.39 18.38 11.5C18.38 15.32 16.04 16.16 13.81 16.41C14.17 16.72 14.5 17.33 14.5 18.26C14.5 19.6 14.5 20.68 14.5 21C14.5 21.27 14.66 21.59 15.17 21.5C19.14 20.16 22 16.42 22 12C22 6.48 17.52 2 12 2Z" /> |
| | | </svg> |
| | | </button> |
| | | </div> |
| | | |
| | | {/* 注册链接 */} |
| | | <div className="mt-4 text-center"> |
| | | <p className="text-xs text-white/70"> |
| | | 还没有帐号? |
| | | <motion.a |
| | | href="#" |
| | | href="/register" |
| | | className="ml-1 text-[#6ADBFF] hover:text-[#6ADBFF] transition-colors duration-200 relative group" |
| | | whileHover={{ scale: 1.03 }} |
| | | whileTap={{ scale: 0.97 }} |