blob: e7f8f52760cef9181127ccac838c6a22bdaf43e1 [file] [log] [blame]
Krishya57cc17b2025-05-26 16:43:34 +08001import { useState } from "react";
2import { Link } from "wouter";
3import "./AuthPage.css";
4import CryptoJS from "crypto-js";
5
Krishya2283d882025-05-27 22:25:19 +08006
Krishya57cc17b2025-05-26 16:43:34 +08007function AuthPage() {
8 const [activeTab, setActiveTab] = useState("login");
9
10 // Login form state
11 const [loginData, setLoginData] = useState({
12 username: "",
13 password: "",
14 rememberMe: false,
15 });
16
17 // Register form state
18 const [registerData, setRegisterData] = useState({
19 username: "",
20 email: "",
21 password: "",
22 confirmPassword: "",
23 inviteCode: "",
Krishya57cc17b2025-05-26 16:43:34 +080024 });
25
26 // Form errors
27 const [errors, setErrors] = useState({
28 login: {},
29 register: {},
30 });
31
32 const hashPassword = (password) => {
33 return CryptoJS.SHA256(password).toString(CryptoJS.enc.Hex);
34 };
35
36 // Handle login form submission
37 const handleLogin = async (e) => {
Krishyaf66d9592025-06-03 17:41:05 +080038 e.preventDefault();
Krishya57cc17b2025-05-26 16:43:34 +080039
Krishyaf66d9592025-06-03 17:41:05 +080040 const newErrors = {};
41 if (!loginData.username) {
42 newErrors.username = "请输入用户名或邮箱";
43 }
44 if (!loginData.password) {
45 newErrors.password = "请输入密码";
46 }
47
48 if (Object.keys(newErrors).length > 0) {
49 setErrors((prev) => ({ ...prev, login: newErrors }));
50 return;
51 }
52
53 try {
54 const response = await fetch("/echo/user/login", {
55 method: 'POST',
56 headers: {
57 'Content-Type': 'application/json',
58 },
59 body: JSON.stringify({
60 username: loginData.username,
61 password: loginData.password,
62 }),
63 });
64
65 const data = await response.json();
66
67 if (data.msg === "登录成功" && data.user) {
68 // 保存用户信息到 localStorage
69 localStorage.setItem("user", JSON.stringify(data.user));
Krishya8f2fec82025-06-04 21:54:46 +080070 localStorage.setItem("userId", data.user.userId); // 如果有 id 字段
Krishya51f611d2025-06-04 22:25:58 +080071 window.location.href = '/forum';
Krishyaf66d9592025-06-03 17:41:05 +080072 } else {
73 throw new Error(data.msg || "登录失败");
Krishya57cc17b2025-05-26 16:43:34 +080074 }
Krishyaf66d9592025-06-03 17:41:05 +080075 } catch (error) {
76 console.error('登录错误:', error.message);
77 setErrors((prev) => ({
78 ...prev,
79 login: { message: error.message },
80 }));
81 }
82};
Krishya57cc17b2025-05-26 16:43:34 +080083
Krishya57cc17b2025-05-26 16:43:34 +080084
85 // Handle register form submission
86 const handleRegister = async (e) => {
Krishyaf66d9592025-06-03 17:41:05 +080087 e.preventDefault();
Krishya57cc17b2025-05-26 16:43:34 +080088
Krishyaf66d9592025-06-03 17:41:05 +080089 const newErrors = {};
90 if (!registerData.username) {
91 newErrors.username = "请输入用户名";
92 }
93 if (!registerData.email) {
94 newErrors.email = "请输入邮箱";
95 } else if (!/\S+@\S+\.\S+/.test(registerData.email)) {
96 newErrors.email = "邮箱格式不正确";
97 }
98 if (!registerData.password) {
99 newErrors.password = "请输入密码";
100 } else if (registerData.password.length < 6) {
101 newErrors.password = "密码长度至少为6位";
102 }
103 if (registerData.password !== registerData.confirmPassword) {
104 newErrors.confirmPassword = "两次输入的密码不一致";
105 }
Krishya57cc17b2025-05-26 16:43:34 +0800106
Krishyaf66d9592025-06-03 17:41:05 +0800107 if (Object.keys(newErrors).length > 0) {
108 setErrors((prev) => ({ ...prev, register: newErrors }));
109 return;
110 }
111
112 const hashedPassword = hashPassword(registerData.password);
113
114 try {
115 const response = await fetch("/echo/user/register", {
116 method: 'POST',
117 headers: {
118 'Content-Type': 'application/json',
119 },
120 body: JSON.stringify({
121 username: registerData.username,
122 email: registerData.email,
123 password: hashedPassword,
124 role: "user", // 默认角色,可根据实际情况调整
125 inviteCode: registerData.inviteCode,
126 }),
127 });
128
129 const data = await response.json();
130
131 if (data.msg === "注册成功") {
132 alert("注册成功,请登录!");
133 setActiveTab("login");
134 } else {
135 throw new Error(data.msg || "注册失败");
Krishya57cc17b2025-05-26 16:43:34 +0800136 }
Krishyaf66d9592025-06-03 17:41:05 +0800137 } catch (error) {
138 console.error("注册错误:", error.message);
139 setErrors((prev) => ({
140 ...prev,
141 register: { message: error.message },
142 }));
143 }
144};
Krishya57cc17b2025-05-26 16:43:34 +0800145
Krishya57cc17b2025-05-26 16:43:34 +0800146
147 // Update login form data
148 const updateLoginData = (field, value) => {
149 setLoginData({
150 ...loginData,
151 [field]: value,
152 });
153 };
154
155 // Update register form data
156 const updateRegisterData = (field, value) => {
157 setRegisterData({
158 ...registerData,
159 [field]: value,
160 });
161 };
Krishya75e43c02025-04-05 21:16:30 +0800162
163 return (
Krishya57cc17b2025-05-26 16:43:34 +0800164 <div className="auth-container">
165 <div className="auth-card">
166 <div className="auth-header">
167 <div className={`auth-tab ${activeTab === "login" ? "active" : ""}`} onClick={() => setActiveTab("login")}>
168 登录
169 </div>
170 <div className={`auth-tab ${activeTab === "register" ? "active" : ""}`} onClick={() => setActiveTab("register")}>
171 注册
172 </div>
Krishyaf66d9592025-06-03 17:41:05 +0800173
Krishya57cc17b2025-05-26 16:43:34 +0800174 </div>
175
176 <div className="auth-content">
177 {activeTab === "login" ? (
178 <form className="auth-form" onSubmit={handleLogin}>
179 {errors.login.message && <div className="error-message">{errors.login.message}</div>}
180 <div className="form-group">
181 <label htmlFor="login-username">用户名/邮箱</label>
182 <input
183 type="text"
184 id="login-username"
185 value={loginData.username}
186 onChange={(e) => updateLoginData("username", e.target.value)}
187 placeholder="请输入用户名或邮箱"
188 required
189 />
190 {errors.login.username && <div className="error-message">{errors.login.username}</div>}
191 </div>
192
193 <div className="form-group">
194 <label htmlFor="login-password">密码</label>
195 <input
196 type="password"
197 id="login-password"
198 value={loginData.password}
199 onChange={(e) => updateLoginData("password", e.target.value)}
200 placeholder="请输入密码"
201 required
202 />
203 {errors.login.password && <div className="error-message">{errors.login.password}</div>}
204 </div>
205
206 <div className="form-group-inline">
207 <div className="checkbox-container">
208 <input
209 type="checkbox"
210 id="remember-me"
211 checked={loginData.rememberMe}
212 onChange={(e) => updateLoginData("rememberMe", e.target.checked)}
213 />
214 <label htmlFor="remember-me">记住我</label>
215 </div>
216 <Link to="/forgot-password" className="forgot-password">
217 忘记密码?
218 </Link>
219 </div>
220
221 <button type="submit" className="auth-button">
222 登录
223 </button>
224 </form>
225 ) : (
226 <form className="auth-form" onSubmit={handleRegister}>
227 {errors.register.message && <div className="error-message">{errors.register.message}</div>}
228 <div className="form-group">
229 <label htmlFor="register-username">用户名</label>
230 <input
231 type="text"
232 id="register-username"
233 value={registerData.username}
234 onChange={(e) => updateRegisterData("username", e.target.value)}
235 placeholder="请输入用户名"
236 required
237 />
238 {errors.register.username && <div className="error-message">{errors.register.username}</div>}
239 </div>
240
241 <div className="form-group">
242 <label htmlFor="register-email">邮箱</label>
243 <input
244 type="email"
245 id="register-email"
246 value={registerData.email}
247 onChange={(e) => updateRegisterData("email", e.target.value)}
248 placeholder="请输入邮箱"
249 required
250 />
251 {errors.register.email && <div className="error-message">{errors.register.email}</div>}
252 </div>
253
254 <div className="form-group">
255 <label htmlFor="register-password">密码</label>
256 <input
257 type="password"
258 id="register-password"
259 value={registerData.password}
260 onChange={(e) => updateRegisterData("password", e.target.value)}
261 placeholder="请输入密码"
262 required
263 />
264 {errors.register.password && <div className="error-message">{errors.register.password}</div>}
265 </div>
266
267 <div className="form-group">
268 <label htmlFor="register-confirm-password">确认密码</label>
269 <input
270 type="password"
271 id="register-confirm-password"
272 value={registerData.confirmPassword}
273 onChange={(e) => updateRegisterData("confirmPassword", e.target.value)}
274 placeholder="请再次输入密码"
275 required
276 />
277 {errors.register.confirmPassword && (
278 <div className="error-message">{errors.register.confirmPassword}</div>
279 )}
280 </div>
281
282 <div className="form-group">
283 <label htmlFor="register-inviteCode">邀请码</label>
284 <input
285 type="text"
286 id="register-inviteCode"
287 value={registerData.inviteCode}
288 onChange={(e) => updateRegisterData("inviteCode", e.target.value)}
289 placeholder="请输入邀请码"
290 />
291 {errors.register.inviteCode && <div className="error-message">{errors.register.inviteCode}</div>}
292 </div>
293
294 <button type="submit" className="auth-button">
295 注册
296 </button>
297 </form>
298 )}
299 </div>
300 </div>
Krishya75e43c02025-04-05 21:16:30 +0800301 </div>
302 );
Krishya57cc17b2025-05-26 16:43:34 +0800303}
Krishya75e43c02025-04-05 21:16:30 +0800304
305export default AuthPage;