2026-06-01-FE-04.md 6.12 KB

FE-04 用户信息单据 — AI 代码评审报告(第 2 轮)

  • 规格:docs/superpowers/specs/2026-06-01-FE-04.md
  • 阶段:前端(frontend)
  • 关联 REQ:REQ-USR-001(增加用户)/ REQ-USR-002(修改用户)
  • 关联原型:prototype/erp.html <section id="screen-userdetail">
  • 本轮 fix commit:96e88d3(编辑预填走 navigate state + 补 loadError 返回列表入口)
  • 裁决:approve

一、作用域核查(通过)

本轮 diff 实现文件全部落在 frontend/ 下(src/pages/usr/UserDetail/src/pages/usr/UserList/index.tsxtests/unit/),未触碰 backend/ / sql/ / scripts/,符合前端阶段路径作用域硬约束。工作树干净(fix 已 commit)。

二、上轮 must-fix 复验(核心维度)

B1(上轮 blocker)— 已修复 ✔

上轮判定:edit 态预填把路由主键 :id 当「用户号」去 GET /api/usr/users?queryField=用户号 精确匹配,真实后端必然返回空 → 误报 40401,无法满足 REQ-USR-002 编辑预填(BR17)。

本轮 fix(commit 96e88d3)按上轮修复方向 #1 落地,复验确认到位:

  • frontend/src/pages/usr/UserList/index.tsx:36 双击行改为 navigate('/usr/users/' + row.id, { state: { user: row } }),把列表行 UserVO 经 navigate state 透传。
  • frontend/src/pages/usr/UserDetail/index.tsx:43location.state.userpresetUser 传入 hook。
  • frontend/src/pages/usr/UserDetail/useUserDetail.ts:116-131 edit 分支改为:有 presetUserinitFromVo 回填;缺 presetUser(直链访问 / 刷新丢 state)→ 进入 loadErrorloadFailed=true + MSG_LOAD_DETAIL_FAIL),不再getUserDetail、不再把主键塞进「用户号」查询。原 getUserDetail import 与 notFound 状态已彻底移除。
  • frontend/src/pages/usr/UserDetail/index.tsx:108-128 loadError 整页态在 edit 模式额外提供「返回列表」入口(spec § 4「edit 详情失败给整页重试或返回列表」),替代原误导性的 40401「该用户不存在」页。
  • 残留核查(grep):src/ 内已无 getUserDetail 调用方(仅 usrApi.ts 保留导出,见 S1)、无 notFound 状态、无「按主键查用户号」路径;MSG_ERR_USER_NOT_FOUND 仅在 submit 路径(useUserDetail.ts:225,PUT 返回 40401)保留使用,符合 spec § 4「edit submit 可命中 40401」,非误用。

测试同步更新且通过:

  • tests/unit/useUserDetail.test.tsx:新增「edit 缺 presetUser → loadFailed 且不调 getUserDetail」、保留「edit 带 presetUser 跳过 getUserDetail」。
  • tests/unit/UserDetailPage.test.tsx:edit 预填用例改为经 navigate state 注入 presetUser 并断言 mockedDetail 未被调用;新增「edit 缺 state → loadError 提供 点击重试 + 返回列表」。
  • tests/e2e/userdetail.spec.ts:97 edit user prefill then save:经列表双击(携带 state)预填→改语言→PUT 保存→回流列表,通过。

结论:B1 已被真实修复(非掩盖),数据流自洽,且测试改造正确反映了「主键无 by-id 读端点、预填依赖列表行 state」这一事实约束。

三、本轮门禁证据(复跑确认)

  • npm run lint:exit 0(无 error/warning)。
  • npx tsc --noEmit:exit 0(含 renderShell.tsx 新增 ShellInitialEntry 本地类型,类型自洽)。
  • npm run test:unit:40 文件 / 193 用例全通过(FE-04 相关 UserDetailPage 10、useUserDetail、UserDetailToolbar 等全绿)。
  • npm run test:e2e -- userdetail.spec.ts:5 passed / 0 failed。ECONNREFUSED 为 route 层 mock 下的预期 vite proxy 噪声,不影响判定。

四、对照确认(七维 + 通用维,沿用上轮通过项,本轮 diff 未改动者不再赘述)

  • 原型一致性:工具栏 / 3 列 form-grid / tabs-row(权限组 active + 5 占位页签 disabled)/ perm-list 结构与 #screen-userdetail 一致,主操作「保存」置工具栏,无结构性偏移;本轮 diff 未触碰布局。
  • Design Tokens:语义色仍走 var(--color-*);本轮 diff 未引入新色值。
  • a11y(客观项):表单控件经 AntD Form.Item label 关联;「取消」脏表单二次确认(Modal.confirm)保留;loadError「返回列表」为标准 Button,键盘可达。
  • 业务校验前端复刻:BR3/BR4/BR6/BR7/BR8/BR5/BR9/BR11 与错误码 40001/40901/40401/40301 文案及就近高亮均保持。
  • API 调用一致性:create/update/listEmployees/listPermissions 经统一 request.ts 实例,无裸 fetch/axios;请求体字段对齐 docs/05。本轮去掉了 edit 预填对列表端点的误用,API 调用面更收敛。
  • 状态机覆盖:loading / empty / error(loadError 重试 + edit 返回列表)/ 正常 / 提交中 五态齐备;edit 缺 state 归入 loadError,语义比上轮 40401 更准确。

五、建议项(非 must-fix,可后续消化,不影响本轮 approve)

  • S1(沿上轮 S1,已部分自然消解):frontend/src/api/usrApi.ts:107 getUserDetail 现已无生产调用方(仅测试 mock 中作为占位导出)。可在后续清理为「删除该函数」或「待后端补单用户详情/按主键定位端点后复用」。当前保留不构成缺陷(lint/tsc 均不报未用导出)。
  • S2:userVoToFormValuesconstants.ts:146)因 UserVO(FE-03 列表 VO)不含 iEmployeeId / iCanModifyBill / 已授权权限 id,edit 预填时这三项被置默认(iEmployeeId=null / iCanModifyBill=0 / checkedPermissionIds=[])。这是 spec D4 已登记的数据模型限制(列表 VO 为唯一可用源),非本轮回归;待后端补单用户详情读端点后可完整回勾。已属 spec 决策范畴,不作 must-fix。
  • S3(沿上轮 S3):useUserDetail.ts:143 catch 文案分支读 permissions.length 为闭包旧值,仅影响 loadError 文案精度,不影响功能。

六、裁决

上轮唯一 blocker(B1)已在本轮 diff 中真实修复,数据流自洽、测试改造正确、门禁四项(lint / tsc / unit / e2e)全绿,未引入新缺陷、未回归既有通过项。其余为可选建议项,均不构成 must-fix。

verdict: approve