blob: c315b7dc46e17c8061033bf0a6fb82f4b6adaa72 [file] [log] [blame]
Raverafc93da2025-06-15 18:12:49 +08001import React, { useState, useEffect } from 'react';
2import { Link } from 'react-router-dom';
3import { Input, Checkbox, Modal, Alert } from 'antd';
4import { MailOutlined, LockOutlined, ExclamationCircleOutlined, CheckCircleOutlined } from '@ant-design/icons';
5import {
6 getRememberedLoginInfo,
7 saveRememberedLoginInfo,
8 saveAuthInfo,
9 isLoggedIn
10} from '../../utils/auth';
11import { hashPassword } from '../../utils/crypto';
12import './LoginPage.css';
13
14const baseURL = 'http://10.126.59.25:8082';
15
16const LoginPage = () => {
17 const [formData, setFormData] = useState({
18 email: '',
19 password: ''
20 });
21
22 const [rememberMe, setRememberMe] = useState(false);
23 const [isLoading, setIsLoading] = useState(false);
24 const [errors, setErrors] = useState({
25 email: '',
26 password: ''
27 });
28 const [errorModal, setErrorModal] = useState({
29 visible: false,
30 title: '',
31 content: ''
32 });
33 const [successAlert, setSuccessAlert] = useState({
34 visible: false,
35 message: ''
36 });
37
38 // 显示错误弹窗
39 const showErrorModal = (title, content) => {
40 setErrorModal({
41 visible: true,
42 title: title,
43 content: content
44 });
45 };
46
47 // 关闭错误弹窗
48 const closeErrorModal = () => {
49 setErrorModal({
50 visible: false,
51 title: '',
52 content: ''
53 });
54 };
55
56 // 显示成功提示
57 const showSuccessAlert = (message) => {
58 setSuccessAlert({
59 visible: true,
60 message: message
61 });
62
63 // 3秒后自动隐藏
64 setTimeout(() => {
65 setSuccessAlert({
66 visible: false,
67 message: ''
68 });
69 }, 3000);
70 };
71
72 // 页面加载时检查是否有记住的登录信息
73 useEffect(() => {
74 // 检查是否已经登录
75 if (isLoggedIn()) {
76 // 如果已经有token,可以选择直接跳转到主页面
77 // window.location.href = '/test-dashboard';
78 console.log('用户已登录');
79 }
80
81 // 获取记住的登录信息
82 const rememberedInfo = getRememberedLoginInfo();
83 if (rememberedInfo.rememberMe && rememberedInfo.email) {
84 setFormData({
85 email: rememberedInfo.email,
86 password: rememberedInfo.password
87 });
88 setRememberMe(true);
89 }
90 }, []);
91
92 const handleEmailChange = (e) => {
93 const value = e.target.value;
94 setFormData(prev => ({
95 ...prev,
96 email: value
97 }));
98
99 // 清除邮箱错误提示
100 if (errors.email) {
101 setErrors(prev => ({
102 ...prev,
103 email: ''
104 }));
105 }
106 };
107
108 const handlePasswordChange = (e) => {
109 const value = e.target.value;
110 setFormData(prev => ({
111 ...prev,
112 password: value
113 }));
114
115 // 清除密码错误提示
116 if (errors.password) {
117 setErrors(prev => ({
118 ...prev,
119 password: ''
120 }));
121 }
122 };
123
124 const handleRememberMeChange = (e) => {
125 const checked = e.target.checked;
126 setRememberMe(checked);
127
128 // 如果取消记住我,清除已保存的登录信息
129 if (!checked) {
130 saveRememberedLoginInfo('', '', false);
131 }
132 };
133
134 const validateForm = () => {
135 const newErrors = {
136 email: '',
137 password: ''
138 };
139
140 let hasError = false;
141
142 // 验证邮箱
143 if (!formData.email || typeof formData.email !== 'string' || !formData.email.trim()) {
144 newErrors.email = '请输入邮箱地址';
145 hasError = true;
146 } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
147 newErrors.email = '请输入有效的邮箱地址';
148 hasError = true;
149 }
150
151 // 验证密码
152 if (!formData.password || typeof formData.password !== 'string' || !formData.password.trim()) {
153 newErrors.password = '请输入密码';
154 hasError = true;
155 } else if (formData.password.length < 6) {
156 newErrors.password = '密码长度至少6位';
157 hasError = true;
158 }
159
160 setErrors(newErrors);
161 return !hasError;
162 };
163
164 const handleSubmit = async (e) => {
165 e.preventDefault();
166
167 // 验证表单
168 if (!validateForm()) {
169 return;
170 }
171
172 setIsLoading(true);
173
174 try {
175 // 发送登录请求到后端
176 const response = await fetch(baseURL + '/login', {
177 method: 'POST',
178 headers: {
179 'Content-Type': 'application/json',
180 },
181 body: JSON.stringify({
182 email: formData.email, // 后端支持邮箱登录
183 password: hashPassword(formData.password) // 前端加密密码
184 })
185 });
186
187 const result = await response.json();
188
189 if (result.success) {
190 // 显示成功提示
191 showSuccessAlert('登录成功!正在跳转...');
192
193 // 保存认证信息
194 saveAuthInfo(result.token, result.user, rememberMe);
195
196 // 保存或清除记住的登录信息
197 saveRememberedLoginInfo(formData.email, formData.password, rememberMe);
198
199 // 延迟跳转,让用户看到成功提示
200 setTimeout(() => {
201 window.location.href = '/test-dashboard';
202 }, 1500);
203 } else {
204 // 登录失败,显示错误信息
205 let errorTitle = '登录失败';
206 let errorContent = result.message || '登录失败,请检查您的邮箱和密码';
207
208 // 根据错误类型提供更详细的信息
209 if (result.message) {
210 if (result.message.includes('邮箱') || result.message.includes('email')) {
211 errorTitle = '邮箱验证失败';
212 errorContent = '您输入的邮箱地址不存在或格式不正确,请检查后重试。';
213 } else if (result.message.includes('密码') || result.message.includes('password')) {
214 errorTitle = '密码验证失败';
215 errorContent = '您输入的密码不正确,请检查后重试。如果忘记密码,请点击"忘记密码"进行重置。';
216 } else if (result.message.includes('用户不存在')) {
217 errorTitle = '用户不存在';
218 errorContent = '该邮箱尚未注册,请先注册账户或检查邮箱地址是否正确。';
219 } else if (result.message.includes('账户被锁定') || result.message.includes('locked')) {
220 errorTitle = '账户被锁定';
221 errorContent = '您的账户因安全原因被暂时锁定,请联系客服或稍后重试。';
222 }
223 }
224
225 showErrorModal(errorTitle, errorContent);
226 }
227 } catch (error) {
228 console.error('登录请求失败:', error);
229
230 // 根据错误类型显示不同的错误信息
231 if (error.name === 'TypeError' && error.message.includes('fetch')) {
232 showErrorModal('网络连接失败', '无法连接到服务器,请检查您的网络连接后重试。如果问题持续存在,请联系客服。');
233 } else if (error.name === 'AbortError') {
234 showErrorModal('请求超时', '请求超时,请检查网络连接后重试。');
235 } else {
236 showErrorModal('登录失败', '网络连接失败,请检查网络或稍后重试。如果问题持续存在,请联系客服。');
237 }
238 } finally {
239 setIsLoading(false);
240 }
241 };
242
243 return (
244 <div className="login-container">
245 <div className="login-background"></div>
246
247 {isLoading && (
248 <div className="loading-overlay">
249 <div className="loading-content">
250 <div className="loading-spinner-large"></div>
251 <p className="loading-text">正在登录...</p>
252 </div>
253 </div>
254 )}
255
256 <div className="login-content">
257 <div className="login-card">
258 {/* 成功提示 */}
259 {successAlert.visible && (
260 <div style={{ marginBottom: '16px' }}>
261 <Alert
262 message={successAlert.message}
263 type="success"
264 icon={<CheckCircleOutlined />}
265 showIcon
266 closable
267 onClose={() => setSuccessAlert({ visible: false, message: '' })}
268 style={{
269 borderRadius: '8px',
270 border: '1px solid #b7eb8f',
271 backgroundColor: '#f6ffed'
272 }}
273 />
274 </div>
275 )}
276
277 <div className="login-header">
278 <h1 className="login-title">欢迎来到小红书</h1>
279 <p className="login-subtitle">标记我的生活</p>
280 </div>
281
282 <form className="login-form" onSubmit={handleSubmit}>
283 <div className="form-group">
284 <Input
285 type="email"
286 id="email"
287 name="email"
288 className={`form-input ${errors.email ? 'input-error' : ''}`}
289 placeholder="请输入您的邮箱"
290 value={formData.email}
291 onChange={handleEmailChange}
292 prefix={<MailOutlined />}
293 size="large"
294 title=""
295 status={errors.email ? 'error' : ''}
296 />
297 {errors.email && (
298 <div className="error-message">
299 {errors.email}
300 </div>
301 )}
302 </div>
303
304 <div className="form-group">
305 <Input.Password
306 id="password"
307 name="password"
308 className={`form-input ${errors.password ? 'input-error' : ''}`}
309 placeholder="请输入您的密码"
310 value={formData.password}
311 onChange={handlePasswordChange}
312 prefix={<LockOutlined />}
313 size="large"
314 title=""
315 status={errors.password ? 'error' : ''}
316 />
317 {errors.password && (
318 <div className="error-message">
319 {errors.password}
320 </div>
321 )}
322 </div>
323
324 <div className="form-options">
325 <Checkbox
326 checked={rememberMe}
327 onChange={handleRememberMeChange}
328 >
329 记住我
330 </Checkbox>
331 <Link to="/forgot-password" className="forgot-password">忘记密码?</Link>
332 </div>
333
334 <button
335 type="submit"
336 className={`login-button ${isLoading ? 'loading' : ''}`}
337 disabled={isLoading}
338 >
339 {isLoading ? (
340 <>
341 <div className="loading-spinner"></div>
342 登录中...
343 </>
344 ) : (
345 '登录'
346 )}
347 </button>
348 </form>
349
350 <div className="signup-link">
351 <p>还没有账户? <Link to="/register">立即注册</Link></p>
352 </div>
353 </div>
354 </div>
355
356 {/* 错误弹窗 */}
357 <Modal
358 title={
359 <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
360 <ExclamationCircleOutlined style={{ color: '#ff4d4f', fontSize: '18px' }} />
361 {errorModal.title}
362 </div>
363 }
364 open={errorModal.visible}
365 onOk={closeErrorModal}
366 onCancel={closeErrorModal}
367 okText="我知道了"
368 cancelButtonProps={{ style: { display: 'none' } }}
369 centered
370 className="error-modal"
371 >
372 <div style={{ padding: '16px 0', fontSize: '14px', lineHeight: '1.6' }}>
373 {errorModal.content}
374 </div>
375 </Modal>
376 </div>
377 );
378};
379
380export default LoginPage;