🔐 登录 / 注册页

同一页面通过 mode 状态切换「登录」和「注册」两个视图。登录成功后后端返回 JWT,存入 localStorage,下次请求带上 Authorization Header。

页面结构

  • Logo:顶部 Logo 文字
  • Tab 切换:「登录」/「注册」切换按钮
  • 表单区:根据 mode 渲染不同字段
  • 提交按钮:「登录」或「注册」
  • 社交登录:微信登录按钮(仅登录模式显示)
  • 提示文字:Tab 下方切换提示

触发时机详解

登录模式(默认)

1. 输入手机号 + 密码 → POST /api/auth/login

触发条件:填写手机号 + 密码 → 点击"登录"按钮 → 发送 POST 请求
POST /api/auth/login
✅ 点击"登录"按钮触发
参数类型必填说明
phonestring必填手机号(11位数字)
passwordstring必填密码(至少6位)

成功响应:

{"token": "eyJhbG...", "user": {"id": "string", "phone": "...", "nickname": "...", ...}}

错误响应: {"error": "手机号或密码错误"}

2. 登录成功 → 存储 token → 跳转 /home

触发条件:POST /api/auth/login 返回 200 且有 token 字段
  • localStorage.setItem("token", data.token)
  • localStorage.setItem("user", JSON.stringify(data.user))
  • 导航到 /home

3. 登录失败 → 显示错误提示

触发条件:POST /api/auth/login 返回 error 字段
  • 显示红色提示文字 ⚠️ {error}
  • 表单保持可编辑状态,用户可修改后重试

4. 点击「微信登录」→ 跳转微信授权(外部)

触发条件:点击"微信登录"按钮 → window.open 打开微信授权页面
说明:微信 OAuth 流程为外部跳转,需前端自行实现回调逻辑(当前 LoginPage 中微信登录按钮存在,但 callback 处理可能不完整)

注册模式

5. 点击「注册」Tab → 切换到注册视图

触发条件:点击"注册"文字按钮 → setMode("register") → 表单重置,显示注册字段

6. 填写注册信息 → POST /api/auth/register

触发条件:填写手机号 + 验证码 + 密码 + 确认密码 → 点击"注册"按钮
POST /api/auth/register
✅ 点击"注册"按钮触发
参数类型必填说明
phonestring必填手机号(11位数字,唯一)
codestring必填短信验证码(6位数字)
passwordstring必填密码(至少6位)
confirmPasswordstring必填确认密码(需与 password 相同)

成功响应:

{"token": "eyJhbG...", "user": {"id": "...", "phone": "...", ...}}

错误响应: {"error": "手机号已被注册"}

7. 前端校验(无需请求)

校验规则:
  • 手机号 ≠ 11位数字 → 提示"请输入11位手机号"
  • 验证码 ≠ 6位数字 → 提示"请输入6位验证码"
  • 密码长度 < 6 → 提示"密码至少6位"
  • 确认密码 ≠ 密码 → 提示"两次密码不一致"

8. 注册成功 → 自动登录 → 跳转 /home

触发条件:POST /api/auth/register 返回 200 → 自动存储 token → Navigate to /home

9. 发送验证码 → POST /api/auth/send-code

触发条件:注册表单中点击"发送验证码"按钮 → 60秒倒计时按钮禁用
POST /api/auth/send-code
✅ 点击"发送验证码"按钮触发
参数类型必填说明
phonestring必填手机号
typestring必填固定 "register"

响应: {"message": "验证码已发送"}

防抖:点击后按钮进入60秒倒计时(setCooldown(60)),倒计时结束前不可再次点击。

页面跳转规则

10. 已登录用户访问登录页

触发条件:localStorage 中存在 token 时访问 /login → 直接 Navigate to /home(阻止已登录用户访问登录页)

11. App 初始化时的路由守卫

触发条件:App 首次加载 → 检查 localStorage.token → 有 token → redirect to="/home",无 token → redirect to="/login"
// App.jsx 中的路由守卫逻辑
if (token && location.pathname === "/login") {
  return <Navigate to="/home" />
}
if (!token && location.pathname !== "/login") {
  return <Navigate to="/login" />;
}