# 实施计划 — 登录 IP 门禁 + SPA 无感切换 **设计**: [`2026-06-08-login-gate-seamless-nav-design.md`](../specs/2026-06-08-login-gate-seamless-nav-design.md) ## 阶段划分 | 阶段 | 范围 | 估时 | 可独立部署 | |------|------|------|------------| | **P0** | `GET /api/auth/login-access` + LoginPage 门禁 UI | 0.5~1d | 是 | | **P1** | `router-view` key、登录 replace + chunk 预载 | 0.5d | 是 | | **P2** | keep-alive + 各页 `onActivated` 静默刷新 | 2~3d | 是(逐页) | | **P3** | `useCachedQuery` + 统计/分类共享 store | 3~5d | 否(需全链路) | | **P4** | 侧栏预取 + 骨架屏 | 1~2d | 是 | ## P0 实施步骤 1. 新增 `server/utils/login_allowlist.py`:`is_login_allowed(client_ip) -> tuple[bool, str|None]` 2. `auth_service.login` 改为调用上述函数 3. `GET /api/auth/login-access` + `PUBLIC_EXACT_PATHS` 4. `LoginPage`:checking / blocked / form 三态 5. `tests/test_login_access.py` 6. changelog + audit + 部署 ## P1 实施步骤 1. `App.vue`:`route.fullPath` → `route.name`(或移除 key) 2. 回归 `ScriptRunsPage`:`watch(route.params.id)` 3. `LoginPage`:`router.replace` + 动态 import 预载 Dashboard 4. 浏览器:登录 → 仪表盘无闪屏验证 ## P2 实施步骤 1. `App.vue`:`keep-alive` + `include` 列表 2. 各页 `defineOptions({ name: '...' })` 3. 将 `onMounted(load)` 拆为 `onMounted` + `onActivated(() => load({ silent: true }))` 4. **排除** `TerminalPage` 5. E2E:连续切换 10 次菜单,Network 观察无重复全量请求风暴 ## P3/P4(后续迭代) 见设计文档 §4.2;单独开 changelog 与验收报告。 ## 测试要点 - P0:`pytest tests/test_login_access.py`;非白名单 curl 预检与 POST login 结论一致 - P1~P2:Playwright 或手动 — 侧栏往返服务器↔脚本,无整页白屏 - 安全:登出后 keep-alive 内无上一用户数据残留 ## 回滚 各阶段可 git revert 独立 commit;P0 回滚不影响已登录会话。