blob: 1bc18c2171c2aadb3833b88a1bff7b9716787db4 [file] [log] [blame]
San3yuan8166d1b2025-06-05 23:15:53 +08001import React, { useState } from 'react';
San3yuan6f2ed692025-04-16 20:24:49 +08002import { useApi } from '@/hooks/request';
3import request from '@/utils/request';
San3yuan8166d1b2025-06-05 23:15:53 +08004import { postUserLogin } from '@/api/auth';
San3yuan6f2ed692025-04-16 20:24:49 +08005import { useAppDispatch } from '@/hooks/store';
San3yuan6f2ed692025-04-16 20:24:49 +08006import style from './login.module.css';
San3yuan6f2ed692025-04-16 20:24:49 +08007import { useNavigate } from 'react-router';
San3yuan03ab0642025-04-29 18:00:25 +08008import logo from '&/assets/logo.png';
San3yuan6f2ed692025-04-16 20:24:49 +08009import { getUserInfo } from '@/api/user';
San3yuan03ab0642025-04-29 18:00:25 +080010import debounce from 'lodash/debounce';
San3yuan8166d1b2025-06-05 23:15:53 +080011import { message } from 'antd';
San3yuan6f2ed692025-04-16 20:24:49 +080012
13const Login: React.FC = () => {
14 const [email, setEmail] = useState('');
15 const [password, setPassword] = useState('');
San3yuan8166d1b2025-06-05 23:15:53 +080016 const [showRegister, setShowRegister] = useState(false);
17 const [inviteCode, setInviteCode] = useState('');
18 const [registerEmail, setRegisterEmail] = useState('');
19 const [registerPassword, setRegisterPassword] = useState('');
20 const [emailCode, setEmailCode] = useState('');
21 const [codeBtnDisabled, setCodeBtnDisabled] = useState(false);
22 const [codeBtnText, setCodeBtnText] = useState('发送验证码');
23 const [codeTimer, setCodeTimer] = useState<NodeJS.Timeout | null>(null);
San3yuan292794c2025-06-08 20:02:46 +080024 const [userName, setUserName] = useState('');
San3yuan6f2ed692025-04-16 20:24:49 +080025
San3yuan8166d1b2025-06-05 23:15:53 +080026 const dispatch = useAppDispatch();
27 const [messageApi, contextHolder] = message.useMessage();
阳菜,放晴!7e1e3a52025-06-05 23:00:51 +080028 const { refresh: postUserLoginRefresh } = useApi(
29 () => request.post(postUserLogin, { email, password}), false);
30 const { refresh: getUserInfoRefresh } = useApi(
31 () => request.get(getUserInfo), false);
San3yuan6f2ed692025-04-16 20:24:49 +080032 const nav = useNavigate();
San3yuan8166d1b2025-06-05 23:15:53 +080033 const showErrorMessage = async (msg: string) => {
34 messageApi.error(msg);
San3yuana2ee30b2025-06-05 21:20:17 +080035 };
San3yuan8166d1b2025-06-05 23:15:53 +080036
37 // 登录逻辑
San3yuan03ab0642025-04-29 18:00:25 +080038 const handleLogin = debounce(async () => {
San3yuan6f2ed692025-04-16 20:24:49 +080039 try {
阳菜,放晴!7e1e3a52025-06-05 23:00:51 +080040 const res =await postUserLoginRefresh({email, password});
41 console.log("res", res);
San3yuan6f2ed692025-04-16 20:24:49 +080042 if (res==null ||(res as any).error) {
43 alert('Login failed. Please check your credentials.');
44 return;
45 }
46 dispatch({ type: "user/login", payload: res });
San3yuana2ee30b2025-06-05 21:20:17 +080047
San3yuan6f2ed692025-04-16 20:24:49 +080048 const userInfo = await getUserInfoRefresh();
San3yuana2ee30b2025-06-05 21:20:17 +080049 if (userInfo == null || (userInfo as any).error) {
50 throw new Error('获取用户信息失败');
San3yuan6f2ed692025-04-16 20:24:49 +080051 }
阳菜,放晴!2f987042025-06-08 14:54:50 +080052
53 dispatch({ type: "user/getUserInfo", payload: userInfo.userInfo });
54
55 //设置宠物初始化参数
56 localStorage.setItem('petState', JSON.stringify({
57 mood: 30,
58 hunger: 50,
59 timestamp: Date.now()
60 }));
61
San3yuan6f2ed692025-04-16 20:24:49 +080062 nav('/');
63 } catch (error) {
San3yuana2ee30b2025-06-05 21:20:17 +080064 showErrorMessage('登录失败,请检查您的用户名和密码');
San3yuan6f2ed692025-04-16 20:24:49 +080065 }
San3yuan03ab0642025-04-29 18:00:25 +080066 }, 1000) as () => void;
San3yuan6f2ed692025-04-16 20:24:49 +080067
San3yuan8166d1b2025-06-05 23:15:53 +080068 // 发送验证码逻辑
69 const handleSendCode = async () => {
70 if (!registerEmail) {
71 showErrorMessage('请填写邮箱');
72 return;
73 }
74 setCodeBtnDisabled(true);
75 let seconds = 60;
76 setCodeBtnText(`已发送(${seconds}s)`);
77 const timer = setInterval(() => {
78 seconds -= 1;
79 setCodeBtnText(`已发送(${seconds}s)`);
80 if (seconds <= 0) {
81 clearInterval(timer);
82 setCodeBtnDisabled(false);
83 setCodeBtnText('发送验证码');
84 }
85 }, 1000);
86 setCodeTimer(timer);
87 // TODO: 调用发送验证码接口
88 message.success('验证码已发送');
89 };
90
91 // 切换回登录
92 const handleBackToLogin = () => {
93 setShowRegister(false);
94 setInviteCode('');
95 setRegisterEmail('');
96 setRegisterPassword('');
97 setEmailCode('');
98 if (codeTimer) clearInterval(codeTimer);
99 setCodeBtnDisabled(false);
100 setCodeBtnText('发送验证码');
101 };
102
103 // 注册逻辑(仅前端校验,实际应调用注册接口)
104 const handleRegister = async () => {
105 if (!inviteCode || !registerEmail || !registerPassword || !emailCode) {
106 showErrorMessage('请填写完整信息');
107 return;
108 }
109 // TODO: 调用注册接口
110 message.success('注册成功,请登录');
111 handleBackToLogin();
112 };
113
San3yuan03ab0642025-04-29 18:00:25 +0800114 const handleLogoClick = () => {
115 nav('/');
San3yuan8166d1b2025-06-05 23:15:53 +0800116 };
117
San3yuan6f2ed692025-04-16 20:24:49 +0800118 return (
119 <div className={style.form}>
San3yuana2ee30b2025-06-05 21:20:17 +0800120 {contextHolder}
San3yuan8166d1b2025-06-05 23:15:53 +0800121 <img className={style.logo} src={logo} alt="logo" onClick={handleLogoClick} />
122 {!showRegister ? (
123 <>
124 <input
125 type="email"
126 value={email}
127 onChange={(e) => setEmail(e.target.value)}
128 className={style.email}
129 placeholder="Enter your email"
130 />
131 <input
132 type="password"
133 value={password}
134 onChange={(e) => setPassword(e.target.value)}
135 className={style.password}
136 placeholder="Enter your password"
137 />
138 <button className={style.submit} onClick={() => handleLogin()}>登录</button>
139 <button className={style.register} onClick={() => setShowRegister(true)}>注册</button>
140 <button className={style.forget}>忘记密码</button>
141 </>
142 ) : (
143 <>
144 <input
San3yuan292794c2025-06-08 20:02:46 +0800145 type='text'
146 value={userName}
147 onChange={(e)=>setUserName(e.target.value)}
148 className={style.invite}
149 placeholder="用户名"
150 />
151 <input
San3yuan8166d1b2025-06-05 23:15:53 +0800152 type="text"
153 value={inviteCode}
154 onChange={(e) => setInviteCode(e.target.value)}
155 className={style.invite}
156 placeholder="邀请码"
157 />
158 <input
159 type="email"
160 value={registerEmail}
161 onChange={(e) => setRegisterEmail(e.target.value)}
162 className={style.email}
163 placeholder="邮箱"
164 />
165 <input
166 type="password"
167 value={registerPassword}
168 onChange={(e) => setRegisterPassword(e.target.value)}
169 className={style.password}
170 placeholder="密码"
171 />
172 <div style={{ display: 'flex',width:'80%', alignItems: 'center', gap: 8, padding:'10px' }}>
173 <input
174 type="text"
175 value={emailCode}
176 onChange={(e) => setEmailCode(e.target.value)}
177 className={style.code}
178 placeholder="邮箱验证码"
179 style={{ flex: 1 }}
180 />
181 <button
182 className={style.sendCode}
183 onClick={handleSendCode}
184 disabled={codeBtnDisabled}
185 style={{
186 background: codeBtnDisabled ? '#ccc' : undefined,
187 color: codeBtnDisabled ? '#888' : undefined,
188 cursor: codeBtnDisabled ? 'not-allowed' : 'pointer'
189 }}
190 >
191 {codeBtnText}
192 </button>
193 </div>
194 <button className={style.submit} onClick={handleRegister}>注册</button>
195 <button className={style.back} onClick={handleBackToLogin}>返回登录</button>
196 </>
197 )}
San3yuan6f2ed692025-04-16 20:24:49 +0800198 </div>
199 );
200};
201
202export default Login;