From 1aa23508b15f13332c9e9556e161cb46cf7133cc Mon Sep 17 00:00:00 2001 From: chengkai3 Date: Fri, 1 May 2026 19:39:16 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8C=96=E7=99=BB=E5=BD=95=E9=A1=B5?= =?UTF-8?q?=E5=B9=B6=E6=96=B0=E5=A2=9E=E8=AE=B0=E4=BD=8F=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- memory/2026-05-01.md | 23 ++++++ web/src/app/page.tsx | 190 ++++++++++++++++++------------------------- 2 files changed, 102 insertions(+), 111 deletions(-) diff --git a/memory/2026-05-01.md b/memory/2026-05-01.md index c5f6d18..3fa30de 100644 --- a/memory/2026-05-01.md +++ b/memory/2026-05-01.md @@ -478,3 +478,26 @@ - 风险与影响: - 影响范围限定在前端管理页交互层;后端接口仍保持兼容,可继续返回权限相关字段但前端不再暴露配置入口。 - 若后续需彻底下线该能力(含后端字段/持久化),需单独评估接口契约与历史数据兼容。 + +## Work Log - 登录页面还原最简洁样式并保留记住密码(2026-05-01) + +- 背景: + - Issue `FL-144` 要求将登录页还原为最简洁样式,保留“登录、记住密码”功能,并将标题改为“防雷计算”。 + +- 本次改动(最小闭环): + - 文件:`web/src/app/page.tsx` + - 去除登录页注册模式相关状态与 UI(`mode/register/username/切换按钮`),仅保留登录流程。 + - 页面主标题改为 `防雷计算`。 + - 视觉样式收敛为简洁白底 + 居中卡片布局,移除装饰性图标块、渐变、复杂文案。 + - 新增“记住密码”复选框: + - 勾选后登录成功时将用户 ID 与密码写入 `localStorage`; + - 未勾选时清理本地缓存; + - 页面加载时若已记住则自动回填账号密码并默认勾选。 + +- 验证: + - 代码路径自检:登录仍走 `useAuth().login` 既有链路,未改动鉴权接口与路由跳转逻辑。 + - 按任务约束未执行编译/安装依赖。 + +- 风险与影响: + - 影响范围:仅前端登录页 `web/src/app/page.tsx`。 + - 风险:记住密码当前使用浏览器 `localStorage` 明文存储,存在本机安全暴露风险(符合需求但需知悉)。 diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index d78101d..d61c75f 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -1,23 +1,24 @@ "use client"; -import { IdcardOutlined, LockOutlined, UserOutlined } from "@ant-design/icons"; +import { IdcardOutlined, LockOutlined } from "@ant-design/icons"; import { ChangeEvent, FormEvent, useEffect, useState } from "react"; import { useRouter } from "next/navigation"; -import { Alert, Button, Input, Space, Typography } from "antd"; +import { Alert, Button, Checkbox, Input, Space, Typography } from "antd"; import { useAuth } from "@/components/auth-provider"; import { Card } from "@/components/ui-antd"; -type Mode = "login" | "register"; +const LOGIN_REMEMBER_KEY = "login.remember"; +const LOGIN_USER_ID_KEY = "login.user_id"; +const LOGIN_PASSWORD_KEY = "login.password"; export default function Home() { const router = useRouter(); - const { user, initializing, login, register } = useAuth(); + const { user, initializing, login } = useAuth(); - const [mode, setMode] = useState("login"); const [userId, setUserId] = useState(""); - const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); + const [rememberPassword, setRememberPassword] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); @@ -27,8 +28,20 @@ export default function Home() { } }, [initializing, router, user]); - const formTitle = mode === "login" ? "登录你的工作台" : "创建你的工作台"; - const submitLabel = mode === "login" ? "登录" : "创建账号"; + useEffect(() => { + if (typeof window === "undefined") { + return; + } + + const remembered = window.localStorage.getItem(LOGIN_REMEMBER_KEY) === "1"; + if (!remembered) { + return; + } + + setRememberPassword(true); + setUserId(window.localStorage.getItem(LOGIN_USER_ID_KEY) ?? ""); + setPassword(window.localStorage.getItem(LOGIN_PASSWORD_KEY) ?? ""); + }, []); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); @@ -36,11 +49,21 @@ export default function Home() { setBusy(true); try { - if (mode === "login") { - await login(userId.trim(), password); - } else { - await register(`${username.trim() || userId.trim()}@example.local`, username.trim(), password); + const normalizedUserId = userId.trim(); + await login(normalizedUserId, password); + + if (typeof window !== "undefined") { + if (rememberPassword) { + window.localStorage.setItem(LOGIN_REMEMBER_KEY, "1"); + window.localStorage.setItem(LOGIN_USER_ID_KEY, normalizedUserId); + window.localStorage.setItem(LOGIN_PASSWORD_KEY, password); + } else { + window.localStorage.removeItem(LOGIN_REMEMBER_KEY); + window.localStorage.removeItem(LOGIN_USER_ID_KEY); + window.localStorage.removeItem(LOGIN_PASSWORD_KEY); + } } + setPassword(""); } catch (submitError) { const message = submitError instanceof Error ? submitError.message : "未知错误"; @@ -67,108 +90,53 @@ export default function Home() { } return ( -
-
- - -
-
- D -
-
- - 开发智能平台 - - Development Intelligence Platform -
+
+ + + + 防雷计算 + + +
+ } + placeholder="用户 ID" + onChange={(event: ChangeEvent) => setUserId(event.currentTarget.value)} + autoComplete="username" + required + /> + + } + placeholder="密码" + onChange={(event: ChangeEvent) => setPassword(event.currentTarget.value)} + autoComplete="current-password" + minLength={1} + maxLength={128} + required + /> + +
+ setRememberPassword(event.target.checked)} + > + 记住密码 +
- - {formTitle} - + + -
-
- - 用户 ID - - } - placeholder="请输入用户ID" - onChange={(event: ChangeEvent) => setUserId(event.currentTarget.value)} - className="h-[48px] rounded-[10px]" - autoComplete="username" - required - /> -
- - {mode === "register" && ( -
- - 用户名 - - } - placeholder="请输入用户名" - onChange={(event: ChangeEvent) => setUsername(event.currentTarget.value)} - className="h-[48px] rounded-[10px]" - minLength={3} - maxLength={64} - required - /> -
- )} - -
- - 密码 - - } - placeholder={mode === "login" ? "请输入密码" : "请输入密码(至少 8 位)"} - onChange={(event: ChangeEvent) => setPassword(event.currentTarget.value)} - className="h-[48px] rounded-[10px]" - autoComplete={mode === "login" ? "current-password" : "new-password"} - minLength={mode === "login" ? 1 : 8} - maxLength={128} - required - /> -
- - - - {mode === "login" && ( -
- -
- )} - - -
- - {error && } -
-
-
+ {error && } + +
); }