修复认证页 Tab 焦点顺序 05-26-oa-auth-layout-wecom-login-ux

这个提交包含在:
2026-05-27 12:04:46 +08:00
父节点 b511515bc6
当前提交 2a11ab9da8
共修改 5 个文件,包含 51 行新增和 10 行删除
@@ -138,6 +138,10 @@ onNeedRefresh() {
should feel like an OA workstation entry, not a marketing split hero.
- Place `忘记密码?` beside the password label or directly below the password
input on the right; do not bury it in help text or page footer.
- Keep the DOM/tab order aligned with the main form flow even when CSS places
`忘记密码?` visually beside the password label: username/email input -> password
input -> secondary controls. Do not render the forgot-password link before the
password input in DOM order.
- Use password visibility icon buttons with accessible labels and preserve the
current input value.
@@ -158,6 +162,8 @@ onNeedRefresh() {
- Frontend typecheck and build.
- Focused auth E2E covering:
- password visibility and forgot-password navigation on `/login`;
- keyboard Tab from the username/email field lands on the password input, not
the forgot-password link;
- password recovery request-code and reset flow on `/forgot-password`;
- normal-browser Enterprise WeChat entry as secondary CTA;
- WeCom WebView Enterprise WeChat entry as primary CTA with password fallback.
@@ -186,3 +192,19 @@ onNeedRefresh() {
<SecondaryLoginMethods />
</AuthPanel>
```
#### Wrong
```tsx
<Label htmlFor="login-password">密码</Label>
<Link to="/forgot-password">忘记密码?</Link>
<PasswordInput id="login-password" />
```
#### Correct
```tsx
<Label htmlFor="login-password">密码</Label>
<PasswordInput id="login-password" />
<Link className="row-start-1" to="/forgot-password">忘记密码?</Link>
```
@@ -5,6 +5,7 @@
- 新增共享认证页组件 `AuthPanel`,统一品牌、标题、副标题、内容区和页脚样式。
- 调整 `AuthLayout`,仅保留认证页面背景、居中容器、PWA 更新提示和路由出口。
- 重构登录页,移除卡片套卡片;普通浏览器以账号密码登录为主,企业微信/SSO 放入次级登录区。
- 修正登录页键盘 Tab 顺序:用户名输入后先进入密码输入,“忘记密码?”视觉仍在密码行右侧但不打断主表单输入流。
- 重构企业微信 WebView 登录页层级,企业微信一键进入为主 CTA,账号密码保留为兜底入口。
- 重构找回密码页和修改密码页,复用同一认证面板,不改后端认证接口。
- 收窄企业微信入口 E2E,避免前端 UI 用例依赖真实登录和个人设置页跳转。
@@ -27,6 +28,16 @@
- `git diff --check`:已通过。
- 登录/找回密码 focused E2E:已通过,2 passed。
- 企业微信入口 E2E:已通过,2 passed。
- 2026-05-27 登录页 Tab 顺序回归:
- `cd client && npm run typecheck`:已通过。
- `cd client && npm run build`:已通过。
- `E2E_BASE_URL=http://127.0.0.1:3000 npx playwright test tests/e2e/login-portal-ux.spec.ts -g "登录页提供找回密码" --workers=1 --reporter=list`:已通过,1 passed。
- `E2E_BASE_URL=http://127.0.0.1:3000 npx playwright test tests/e2e/login-portal-ux.spec.ts -g "登录页提供|邮箱验证码登录|邮件服务不可用|找回密码流程" --workers=1 --reporter=list`:已通过,4 passed。
- `E2E_BASE_URL=http://127.0.0.1:3000 npx playwright test tests/e2e/wecom-entry.spec.ts --workers=1 --reporter=list`:已通过,2 passed。
- `node scripts/qa/check-ui-design-rules.mjs --strict`:已通过。
- `python3 ./.trellis/scripts/task.py validate 05-26-oa-auth-layout-wecom-login-ux`:已通过。
- `node scripts/qa/check-trellis-governance.mjs`:已通过;保留既有 implement/check 非 spec 上下文 warning。
- `git diff --check`:已通过。
- 完整 `login-portal-ux.spec.ts`:认证页、找回密码、企业微信 WebView 相关 5 passed;Portal 历史文案断言 3 failed,见下方已知漂移。
- 视觉截图已生成:
- `output/playwright/oa-auth-layout-wecom-login-ux/desktop-login.png`
@@ -57,6 +57,7 @@
- [ ] 普通浏览器下账号密码为主,企业微信登录为次级入口。
- [ ] 企业微信 WebView 下企业微信一键进入为主,账号密码为兜底入口。
- [ ] 忘记密码入口位于密码 label 右侧,密码显隐控件仍可用。
- [ ] 键盘 Tab 顺序符合表单习惯:用户名输入后进入密码输入,不被“忘记密码?”链接打断。
- [ ] 找回密码页和改密页复用同一认证页视觉模板。
- [ ] 原有登录、找回密码、企业微信入口 E2E 仍通过。
- [ ] 前端 typecheck、build、设计规则检查通过。
+14 -10
查看文件
@@ -239,21 +239,25 @@ export function LoginPage() {
/>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between gap-3">
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-3 gap-y-2">
<div className="col-start-1 row-start-1">
<Label htmlFor="login-password">密码</Label>
</div>
<div className="col-span-2 row-start-2">
<PasswordInput
autoComplete="current-password"
data-testid="login-password"
id="login-password"
onChange={(event) => setPassword(event.target.value)}
placeholder="请输入密码"
value={password}
/>
</div>
<div className="col-start-2 row-start-1 justify-self-end">
<Link className="text-sm font-medium text-primary hover:underline" to="/forgot-password">
忘记密码?
</Link>
</div>
<PasswordInput
autoComplete="current-password"
data-testid="login-password"
id="login-password"
onChange={(event) => setPassword(event.target.value)}
placeholder="请输入密码"
value={password}
/>
</div>
{error ? <div className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600">{error}</div> : null}
+3
查看文件
@@ -348,6 +348,9 @@ test("登录页提供找回密码、密码显隐,PWA 更新不会清空正在
await page.goto("/login");
await page.getByTestId("login-identifier").fill("[email protected]");
await page.getByTestId("login-identifier").focus();
await page.keyboard.press("Tab");
await expect(page.getByTestId("login-password")).toBeFocused();
await page.getByTestId("login-password").fill("PasswordA1234");
await expect(page.getByTestId("login-password")).toHaveAttribute("type", "password");