| 1 | /** |
| 2 | * LoginContent - 登录页面内容组件 |
| 3 | * 邮箱验证码登录 |
| 4 | */ |
| 5 | |
| 6 | 'use client' |
| 7 | |
| 8 | import { motion } from 'framer-motion' |
| 9 | import Image from 'next/image' |
| 10 | import Link from 'next/link' |
| 11 | import { useRouter } from 'next/navigation' |
| 12 | import { useEffect } from 'react' |
| 13 | |
| 14 | import { useTransClient } from '@/app/i18n/client' |
| 15 | import logo from '@/assets/images/logo.png' |
| 16 | import { useUserStore } from '@/store/user' |
| 17 | |
| 18 | import { EmailLoginForm } from './EmailLoginForm' |
| 19 | |
| 20 | const fadeInUp = { |
| 21 | initial: { opacity: 0, y: 10 }, |
| 22 | animate: { opacity: 1, y: 0 }, |
| 23 | exit: { opacity: 0, y: -10 }, |
| 24 | } |
| 25 | |
| 26 | export default function LoginContent() { |
| 27 | const router = useRouter() |
| 28 | const { token, _hasHydrated } = useUserStore() |
| 29 | const { t } = useTransClient('login') |
| 30 | |
| 31 | // 已登录用户重定向 |
| 32 | useEffect(() => { |
| 33 | if (_hasHydrated && token) { |
| 34 | router.replace('/') |
| 35 | } |
| 36 | }, [_hasHydrated, token, router]) |
| 37 | |
| 38 | // 在 hydration 完成前或已登录时显示空白(避免闪烁) |
| 39 | if (!_hasHydrated || token) { |
| 40 | return null |
| 41 | } |
| 42 | |
| 43 | return ( |
| 44 | <div className="relative min-h-screen w-full overflow-hidden bg-muted"> |
| 45 | {/* 点状网格背景 */} |
| 46 | <div |
| 47 | className="pointer-events-none absolute inset-0" |
| 48 | style={{ |
| 49 | backgroundImage: `radial-gradient(circle, var(--border) 1px, transparent 1px)`, |
| 50 | backgroundSize: '24px 24px', |
| 51 | }} |
| 52 | /> |
| 53 | |
| 54 | {/* 左上角 Logo */} |
| 55 | <div className="absolute left-6 top-6 z-20"> |
| 56 | <Link |
| 57 | href="/" |
| 58 | className="flex items-center gap-2 text-foreground no-underline hover:opacity-80 transition-opacity" |
| 59 | > |
| 60 | <Image src={logo} alt="AiToEarn" width={28} height={28} /> |
| 61 | <span className="text-lg font-semibold tracking-tight">AiToEarn</span> |
| 62 | </Link> |
| 63 | </div> |
| 64 | |
| 65 | {/* 主内容区域 */} |
| 66 | <div className="relative z-10 flex min-h-screen flex-col items-center justify-center px-4 py-20"> |
| 67 | <motion.div |
| 68 | variants={fadeInUp} |
| 69 | initial="initial" |
| 70 | animate="animate" |
| 71 | transition={{ duration: 0.2 }} |
| 72 | className="w-full max-w-[400px]" |
| 73 | > |
| 74 | {/* 中心 Logo */} |
| 75 | <div className="mb-8 flex flex-col items-center"> |
| 76 | <Link |
| 77 | href="/" |
| 78 | className="mb-6 flex h-20 w-20 items-center justify-center hover:opacity-80 transition-opacity" |
| 79 | > |
| 80 | <Image |
| 81 | src={logo} |
| 82 | alt="AiToEarn" |
| 83 | width={72} |
| 84 | height={72} |
| 85 | className="drop-shadow-md" |
| 86 | /> |
| 87 | </Link> |
| 88 | <h1 className="text-2xl font-semibold text-foreground">{t('welcomeBack')}</h1> |
| 89 | <p className="mt-2 text-muted-foreground">{t('loginSubtitle')}</p> |
| 90 | </div> |
| 91 | |
| 92 | {/* 登录表单 */} |
| 93 | <EmailLoginForm /> |
| 94 | </motion.div> |
| 95 | |
| 96 | {/* 底部条款 */} |
| 97 | <p className="mt-10 text-center text-xs text-muted-foreground/70"> |
| 98 | {t('termsText')} |
| 99 | {' '} |
| 100 | <Link |
| 101 | href="/websit/terms-of-service" |
| 102 | className="text-muted-foreground underline hover:text-foreground" |
| 103 | > |
| 104 | {t('termsOfService')} |
| 105 | </Link> |
| 106 | {' '} |
| 107 | {t('and')} |
| 108 | {' '} |
| 109 | <Link |
| 110 | href="/websit/privacy-policy" |
| 111 | className="text-muted-foreground underline hover:text-foreground" |
| 112 | > |
| 113 | {t('privacyPolicy')} |
| 114 | </Link> |
| 115 | </p> |
| 116 | </div> |
| 117 | </div> |
| 118 | ) |
| 119 | } |
| 120 |