返回 AiToEarn
index.tsx
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
120 lines Plain Text