修改论坛、促销、登录,增加测试

Change-Id: I71883fc1da46a94db47f90a4cd61474c274a5b2c
diff --git a/src/pages/AuthPage/AuthPage.css b/src/pages/AuthPage/AuthPage.css
index cb9e470..98e5532 100644
--- a/src/pages/AuthPage/AuthPage.css
+++ b/src/pages/AuthPage/AuthPage.css
@@ -1,11 +1,11 @@
-.auth-container {
+/* .auth-container {
   display: flex;
   align-items: center;
-  justify-content: flex-end; /* 使卡片靠右 */
+  justify-content: flex-end;
   min-height: 100vh;
   font-family: Arial, sans-serif;
-  background: linear-gradient(180deg, #5F4437, #823c3c);
-  padding: 0 2rem; /* 添加左右内边距 */
+  background: #333;
+  padding: 0 2rem;
 }
 
 .auth-container img {
@@ -13,13 +13,13 @@
 }
 
 .auth-form-section {
-  background: #E4D8C9; /* 米白色 */
+  background: #E4D8C9;
   padding: 2rem;
   border-radius: 10px;
   box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
   width: 300px;
   margin: 0 auto;
-  max-width: 400px; /* 限制卡片最大宽度 */
+  max-width: 400px;
 }
 
 .form-group {
@@ -32,7 +32,7 @@
   display: block;
   font-size: 0.9rem;
   margin-bottom: 0.5rem;
-  color: #4A3B34; /* 棕色 */
+  color: #4A3B34;
 }
 
 .form-input {
@@ -46,8 +46,8 @@
 
 .auth-button {
   padding: 0.8rem 8.4rem;
-  background: #BA929A; /* 粉色 */
-  color: #4A3B34; /* 棕色 */
+  background: #BA929A;
+  color: #4A3B34;
   border: none;
   border-radius: 5px;
   cursor: pointer;
@@ -56,21 +56,20 @@
 }
 
 .verify-button {
-  padding: 0.5rem 1rem; /* 更小的内边距 */
-  background: #BA929A; /* 粉色 */
-  color: #4A3B34; /* 棕色 */
+  padding: 0.5rem 1rem;
+  background: #BA929A;
+  color: #4A3B34;
   border: none;
   border-radius: 5px;
   cursor: pointer;
-  font-size: 0.8rem; /* 更小的字体 */
+  font-size: 0.8rem;
   display: inline-block;
 }
 
 .link-button {
   background: none;
   border: none;
-  color: #4A3B34; /* 棕色 */
-  /* text-decoration: underline; */
+  color: #4A3B34;
   cursor: pointer;
   font-size: 0.8rem;
   padding: 0;
@@ -78,19 +77,251 @@
 
 .forgot-password {
   position: absolute;
-  right: 10px; /* 让按钮靠右 */
-  bottom: 5px; /* 调整到底部 */
+  right: 10px;
+  bottom: 5px;
   font-size: 12px;
   background: none;
   border: none;
-  color: #4A3B34; /* 颜色与 "点击注册" 一致 */
+  color: #4A3B34;
   cursor: pointer;
   text-decoration: underline;
 }
 
-
 .register-link, .login-link {
   text-align: center;
   font-size: 0.8rem;
   margin-top: 1rem;
-}
\ No newline at end of file
+} */
+
+
+  .auth-container {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end; /* 使卡片靠右 */
+  min-height: 100vh;
+  font-family: Arial, sans-serif;
+  background: #333; 
+  /* background: linear-gradient(180deg, #5F4437, #823c3c) */
+  padding: 0 2rem; /* 添加左右内边距 */
+}
+  
+  .auth-card {
+    width: 100%;
+    max-width: 480px;
+    background-color: #E4D8C9;
+    border-radius: 8px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+    overflow: hidden;
+  }
+  
+  .auth-header {
+    display: flex;
+    border-bottom: 1px solid #eee;
+  }
+  
+  .auth-tab {
+    flex: 1;
+    padding: 16px;
+    text-align: center;
+    font-size: 18px;
+    font-weight: 500;
+    cursor: pointer;
+    transition: all 0.3s ease;
+  }
+  
+  .auth-tab.active {
+    color: #BA929A;
+    border-bottom: 2px solid #BA929A;
+  }
+  
+  .auth-tab:not(.active) {
+    color: #888;
+  }
+  
+  .auth-tab:hover:not(.active) {
+    background-color: #f9f9f9;
+  }
+  
+  .auth-content {
+    padding: 30px;
+  }
+  
+  .auth-form {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+  }
+  
+  .form-group {
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+  }
+  
+  .form-group label {
+    font-weight: 500;
+    color: #333;
+    text-align: left;
+    align-self: flex-start;
+  }
+  
+  .form-group input[type="text"],
+  .form-group input[type="email"],
+  .form-group input[type="password"] {
+    padding: 12px 16px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    font-size: 16px;
+    transition: border-color 0.3s;
+  }
+  
+  .form-group input:focus {
+    border-color: #BA929A;
+    outline: none;
+  }
+  
+  .form-group-inline {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  
+  .checkbox-container {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+  
+  .checkbox-container input[type="checkbox"] {
+    width: 18px;
+    height: 18px;
+    cursor: pointer;
+  }
+  
+  .forgot-password {
+    color: #BA929A;
+    text-decoration: none;
+    font-size: 14px;
+  }
+  
+  .forgot-password:hover {
+    text-decoration: underline;
+  }
+  
+  .auth-button {
+    background-color: #BA929A;
+    color: white;
+    border: none;
+    border-radius: 4px;
+    padding: 14px;
+    font-size: 16px;
+    font-weight: 500;
+    cursor: pointer;
+    transition: background-color 0.3s;
+  }
+  
+  .auth-button:hover {
+    background-color: #BA929A;
+  }
+  
+  .social-login {
+    margin-top: 20px;
+    text-align: center;
+  }
+  
+  .social-login p {
+    color: #666;
+    margin-bottom: 15px;
+    position: relative;
+  }
+  
+  .social-login p::before,
+  .social-login p::after {
+    content: "";
+    position: absolute;
+    top: 50%;
+    width: 30%;
+    height: 1px;
+    background-color: #ddd;
+  }
+  
+  .social-login p::before {
+    left: 0;
+  }
+  
+  .social-login p::after {
+    right: 0;
+  }
+  
+  .social-icons {
+    display: flex;
+    justify-content: center;
+    gap: 20px;
+  }
+  
+  .social-icon {
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: white;
+    font-size: 12px;
+    cursor: pointer;
+  }
+  
+  .social-icon.wechat {
+    background-color: #07c160;
+  }
+  
+  .social-icon.weibo {
+    background-color: #e6162d;
+  }
+  
+  .social-icon.qq {
+    background-color: #12b7f5;
+  }
+  
+  .terms-container {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+  
+  .terms-link {
+    color: #BA929A;
+    text-decoration: none;
+  }
+  
+  .terms-link:hover {
+    text-decoration: underline;
+  }
+  
+  .error-message {
+    color: #e53e3e;
+    font-size: 14px;
+    margin-top: 4px;
+  }
+  
+  /* Responsive adjustments */
+  @media (max-width: 576px) {
+    .auth-card {
+      box-shadow: none;
+      border-radius: 0;
+    }
+  
+    .auth-content {
+      padding: 20px;
+    }
+  
+    .form-group-inline {
+      flex-direction: column;
+      align-items: flex-start;
+      gap: 10px;
+    }
+  
+    .social-icons {
+      flex-wrap: wrap;
+    }
+  }
+  
\ No newline at end of file
diff --git a/src/pages/AuthPage/AuthPage.jsx b/src/pages/AuthPage/AuthPage.jsx
index 4238214..bfc8e43 100644
--- a/src/pages/AuthPage/AuthPage.jsx
+++ b/src/pages/AuthPage/AuthPage.jsx
@@ -1,19 +1,342 @@
-import React, { useState } from 'react';
-import Login from '../../components/Auth/Login';
-import Register from '../../components/Auth/Register';
+// import React, { useState } from 'react';
+// import Login from '../../components/Auth/Login';
+// import Register from '../../components/Auth/Register';
 
-const AuthPage = () => {
-  const [isRegister, setIsRegister] = useState(false);
+// const AuthPage = () => {
+//   const [isRegister, setIsRegister] = useState(false);
+
+//   return (
+//     <div>
+//       {isRegister ? (
+//         <Register onLoginClick={() => setIsRegister(false)} />
+//       ) : (
+//         <Login onRegisterClick={() => setIsRegister(true)} />
+//       )}
+//     </div>
+//   );
+// };
+
+// export default AuthPage;
+
+
+
+import { useState } from "react";
+import { Link } from "wouter";
+import "./AuthPage.css";
+import CryptoJS from "crypto-js";
+
+const API_BASE = process.env.REACT_APP_API_BASE;
+function AuthPage() {
+  const [activeTab, setActiveTab] = useState("login");
+
+  // Login form state
+  const [loginData, setLoginData] = useState({
+    username: "",
+    password: "",
+    rememberMe: false,
+  });
+
+  // Register form state
+  const [registerData, setRegisterData] = useState({
+    username: "",
+    email: "",
+    password: "",
+    confirmPassword: "",
+    inviteCode: "",
+    agreeTerms: false,
+  });
+
+  // Form errors
+  const [errors, setErrors] = useState({
+    login: {},
+    register: {},
+  });
+
+  const hashPassword = (password) => {
+    return CryptoJS.SHA256(password).toString(CryptoJS.enc.Hex);
+  };
+
+  // Handle login form submission
+  const handleLogin = async (e) => {
+    e.preventDefault();
+
+    // 客户端验证
+    const newErrors = {};
+    if (!loginData.username) {
+      newErrors.username = "请输入用户名或邮箱";
+    }
+    if (!loginData.password) {
+      newErrors.password = "请输入密码";
+    }
+
+    if (Object.keys(newErrors).length > 0) {
+      setErrors((prev) => ({ ...prev, login: newErrors }));
+      return;
+    }
+
+    // 加密密码
+    const hashedPassword = hashPassword(loginData.password);
+
+    try {
+      const response = await fetch(`${API_BASE}/echo/user/login`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          username: loginData.username,
+          password: hashedPassword,
+        }),
+      });
+
+      if (!response.ok) {
+        throw new Error('登录失败');
+      }
+
+      const data = await response.json();
+      
+      if (data.code !== 0 || !data.result) {
+        throw new Error(data.msg || '登录失败');
+      }
+
+      console.log('登录成功:', data);
+      localStorage.setItem('token', data.rows.token);
+      localStorage.setItem('userId', data.rows.userId);
+      window.location.href = '/';
+    } catch (error) {
+      console.error('登录错误:', error.message);
+      setErrors((prev) => ({
+        ...prev,
+        login: { message: error.message },
+      }));
+      throw error;
+    }
+  };
+
+  // Handle register form submission
+  const handleRegister = async (e) => {
+    e.preventDefault();
+
+    // 客户端验证
+    const newErrors = {};
+    if (!registerData.username) {
+      newErrors.username = "请输入用户名";
+    }
+    if (!registerData.email) {
+      newErrors.email = "请输入邮箱";
+    } else if (!/\S+@\S+\.\S+/.test(registerData.email)) {
+      newErrors.email = "邮箱格式不正确";
+    }
+    if (!registerData.password) {
+      newErrors.password = "请输入密码";
+    } else if (registerData.password.length < 6) {
+      newErrors.password = "密码长度至少为6位";
+    }
+    if (registerData.password !== registerData.confirmPassword) {
+      newErrors.confirmPassword = "两次输入的密码不一致";
+    }
+    if (!registerData.agreeTerms) {
+      newErrors.agreeTerms = "请同意用户协议和隐私政策";
+    }
+
+    if (Object.keys(newErrors).length > 0) {
+      setErrors({ ...errors, register: newErrors });
+      return;
+    }
+
+    // 加密密码
+    const hashedPassword = hashPassword(registerData.password);
+
+    try {
+      const response = await fetch(`${API_BASE}/echo/user/register`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          username: registerData.username,
+          email: registerData.email,
+          password: hashedPassword, 
+          inviteCode: registerData.inviteCode,
+        }),
+      });
+
+      if (!response.ok) {
+        throw new Error('注册失败');
+      }
+
+      const data = await response.json();
+      if (data.code !== 0 || !data.result) {
+        throw new Error(data.msg || '注册失败');
+      }
+
+      console.log('注册成功:', data);
+      setActiveTab('login');
+      alert('注册成功,请登录!');
+    } catch (error) {
+      console.error('注册错误:', error.message);
+      setErrors((prev) => ({
+        ...prev,
+        register: { message: error.message },
+      }));
+    }
+  };
+
+  // Update login form data
+  const updateLoginData = (field, value) => {
+    setLoginData({
+      ...loginData,
+      [field]: value,
+    });
+  };
+
+  // Update register form data
+  const updateRegisterData = (field, value) => {
+    setRegisterData({
+      ...registerData,
+      [field]: value,
+    });
+  };
 
   return (
-    <div>
-      {isRegister ? (
-        <Register onLoginClick={() => setIsRegister(false)} />
-      ) : (
-        <Login onRegisterClick={() => setIsRegister(true)} />
-      )}
+    <div className="auth-container">
+      <div className="auth-card">
+        <div className="auth-header">
+          <div className={`auth-tab ${activeTab === "login" ? "active" : ""}`} onClick={() => setActiveTab("login")}>
+            登录
+          </div>
+          <div className={`auth-tab ${activeTab === "register" ? "active" : ""}`} onClick={() => setActiveTab("register")}>
+            注册
+          </div>
+        </div>
+
+        <div className="auth-content">
+          {activeTab === "login" ? (
+            <form className="auth-form" onSubmit={handleLogin}>
+              {errors.login.message && <div className="error-message">{errors.login.message}</div>}
+              <div className="form-group">
+                <label htmlFor="login-username">用户名/邮箱</label>
+                <input
+                  type="text"
+                  id="login-username"
+                  value={loginData.username}
+                  onChange={(e) => updateLoginData("username", e.target.value)}
+                  placeholder="请输入用户名或邮箱"
+                  required
+                />
+                {errors.login.username && <div className="error-message">{errors.login.username}</div>}
+              </div>
+
+              <div className="form-group">
+                <label htmlFor="login-password">密码</label>
+                <input
+                  type="password"
+                  id="login-password"
+                  value={loginData.password}
+                  onChange={(e) => updateLoginData("password", e.target.value)}
+                  placeholder="请输入密码"
+                  required
+                />
+                {errors.login.password && <div className="error-message">{errors.login.password}</div>}
+              </div>
+
+              <div className="form-group-inline">
+                <div className="checkbox-container">
+                  <input
+                    type="checkbox"
+                    id="remember-me"
+                    checked={loginData.rememberMe}
+                    onChange={(e) => updateLoginData("rememberMe", e.target.checked)}
+                  />
+                  <label htmlFor="remember-me">记住我</label>
+                </div>
+                <Link to="/forgot-password" className="forgot-password">
+                  忘记密码?
+                </Link>
+              </div>
+
+              <button type="submit" className="auth-button">
+                登录
+              </button>
+            </form>
+          ) : (
+            <form className="auth-form" onSubmit={handleRegister}>
+              {errors.register.message && <div className="error-message">{errors.register.message}</div>}
+              <div className="form-group">
+                <label htmlFor="register-username">用户名</label>
+                <input
+                  type="text"
+                  id="register-username"
+                  value={registerData.username}
+                  onChange={(e) => updateRegisterData("username", e.target.value)}
+                  placeholder="请输入用户名"
+                  required
+                />
+                {errors.register.username && <div className="error-message">{errors.register.username}</div>}
+              </div>
+
+              <div className="form-group">
+                <label htmlFor="register-email">邮箱</label>
+                <input
+                  type="email"
+                  id="register-email"
+                  value={registerData.email}
+                  onChange={(e) => updateRegisterData("email", e.target.value)}
+                  placeholder="请输入邮箱"
+                  required
+                />
+                {errors.register.email && <div className="error-message">{errors.register.email}</div>}
+              </div>
+
+              <div className="form-group">
+                <label htmlFor="register-password">密码</label>
+                <input
+                  type="password"
+                  id="register-password"
+                  value={registerData.password}
+                  onChange={(e) => updateRegisterData("password", e.target.value)}
+                  placeholder="请输入密码"
+                  required
+                />
+                {errors.register.password && <div className="error-message">{errors.register.password}</div>}
+              </div>
+
+              <div className="form-group">
+                <label htmlFor="register-confirm-password">确认密码</label>
+                <input
+                  type="password"
+                  id="register-confirm-password"
+                  value={registerData.confirmPassword}
+                  onChange={(e) => updateRegisterData("confirmPassword", e.target.value)}
+                  placeholder="请再次输入密码"
+                  required
+                />
+                {errors.register.confirmPassword && (
+                  <div className="error-message">{errors.register.confirmPassword}</div>
+                )}
+              </div>
+
+              <div className="form-group">
+                <label htmlFor="register-inviteCode">邀请码</label>
+                <input
+                  type="text"
+                  id="register-inviteCode"
+                  value={registerData.inviteCode}
+                  onChange={(e) => updateRegisterData("inviteCode", e.target.value)}
+                  placeholder="请输入邀请码"
+                />
+                {errors.register.inviteCode && <div className="error-message">{errors.register.inviteCode}</div>}
+              </div>
+
+              <button type="submit" className="auth-button">
+                注册
+              </button>
+            </form>
+          )}
+        </div>
+      </div>
     </div>
   );
-};
+}
 
 export default AuthPage;
\ No newline at end of file
diff --git a/src/pages/AuthPage/AuthPage.test.jsx b/src/pages/AuthPage/AuthPage.test.jsx
deleted file mode 100644
index e398536..0000000
--- a/src/pages/AuthPage/AuthPage.test.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import { render, screen, fireEvent } from '@testing-library/react';
-import AuthPage from './AuthPage';
-
-describe('AuthPage component', () => {
-  test('switches between login and register forms', () => {
-    render(<AuthPage />);
-
-    const registerButton = screen.getByText('点击注册');
-    fireEvent.click(registerButton);
-
-    const usernameInput = screen.getByPlaceholderText('请输入用户名');
-    const passwordInput = screen.getByPlaceholderText('请输入密码');
-    const emailInput = screen.getByPlaceholderText('请输入邮箱');
-
-    expect(usernameInput).toBeInTheDocument();
-    expect(passwordInput).toBeInTheDocument();
-    expect(emailInput).toBeInTheDocument();
-
-    const loginButton = screen.getByText('点击登录');
-    fireEvent.click(loginButton);
-
-    const loginUsernameInput = screen.getByPlaceholderText('请输入用户名');
-    const loginPasswordInput = screen.getByPlaceholderText('请输入密码');
-    const loginSubmitButton = screen.getByText('登录');
-
-    expect(loginUsernameInput).toBeInTheDocument();
-    expect(loginPasswordInput).toBeInTheDocument();
-    expect(loginSubmitButton).toBeInTheDocument();
-  });
-});
\ No newline at end of file
diff --git a/src/pages/AuthPage/AuthPage.test.jsx.txt b/src/pages/AuthPage/AuthPage.test.jsx.txt
new file mode 100644
index 0000000..a622c13
--- /dev/null
+++ b/src/pages/AuthPage/AuthPage.test.jsx.txt
@@ -0,0 +1,31 @@
+// import React from 'react';
+// import { render, screen, fireEvent } from '@testing-library/react';
+// import AuthPage from './AuthPage';
+
+// describe('AuthPage component', () => {
+//   test('switches between login and register forms', () => {
+//     render(<AuthPage />);
+
+//     const registerButton = screen.getByText('点击注册');
+//     fireEvent.click(registerButton);
+
+//     const usernameInput = screen.getByPlaceholderText('请输入用户名');
+//     const passwordInput = screen.getByPlaceholderText('请输入密码');
+//     const emailInput = screen.getByPlaceholderText('请输入邮箱');
+
+//     expect(usernameInput).toBeInTheDocument();
+//     expect(passwordInput).toBeInTheDocument();
+//     expect(emailInput).toBeInTheDocument();
+
+//     const loginButton = screen.getByText('点击登录');
+//     fireEvent.click(loginButton);
+
+//     const loginUsernameInput = screen.getByPlaceholderText('请输入用户名');
+//     const loginPasswordInput = screen.getByPlaceholderText('请输入密码');
+//     const loginSubmitButton = screen.getByText('登录');
+
+//     expect(loginUsernameInput).toBeInTheDocument();
+//     expect(loginPasswordInput).toBeInTheDocument();
+//     expect(loginSubmitButton).toBeInTheDocument();
+//   });
+// });
\ No newline at end of file