2026-06-01-FE-04.md
6.22 KB
FE-04 用户信息单据 — AI 代码评审报告(第 1 轮)
- 规格:
docs/superpowers/specs/2026-06-01-FE-04.md - 阶段:前端(frontend)
- 关联 REQ:REQ-USR-001(增加用户)/ REQ-USR-002(修改用户)
- 关联原型:
prototype/erp.html<section id="screen-userdetail"> - 裁决:request-changes
一、作用域核查(通过)
本轮 diff 实现文件全部落在 frontend/ 下(src/api/、src/pages/usr/UserDetail/、src/router/index.tsx、tests/),未触碰 backend/ / sql/ / scripts/,符合前端阶段路径作用域硬约束。
二、阻断项(must-fix)
B1. edit 态预填按主键查「用户号」字段,正常导航流必然取不到记录(blocker)
- 位置:
frontend/src/pages/usr/UserDetail/useUserDetail.ts:124(getUserDetail({ queryField: '用户号', queryValue: String(userId) }));连带frontend/src/pages/usr/UserDetail/index.tsx:43(userId = Number(params.id))。 - 事实依据:
- 路由
/usr/users/:id的:id是用户主键——docs/05 § REQ-USR-002「路径参数id(用户主键)」,且 FE-03 双击行入口为frontend/src/pages/usr/UserList/index.tsx:36navigate('/usr/users/' + row.id)(row.id即UserVO.id主键),未经 router state 传presetUser。 - 因此 edit 态
presetUser恒为null,必走getUserDetail分支;该分支把主键值当作「用户号」去GET /api/usr/users?queryField=用户号&matchType=等于&queryValue=<主键>。 - 主键
id与业务字段「用户号」sUserNo是不同字段,正常情况下二者取值不相等 → 列表精确匹配返回空records→getUserDetail返回null→ 进入notFound(40401)分支,页面显示「该用户不存在或已被删除」。 - 真实后端下,REQ-USR-002 验收「编辑预填该用户原值(基本字段 + 已授权权限回勾)」/ spec BR17 将无法满足。
- 路由
- 附加事实:docs/05 § REQ-USR-003 列表端点的
queryField合法取值为用户名/员工名/用户号/部门/用户类型/作废/登录日期/制单人,不含主键,因此无法用列表端点按:id主键定位单条。这是 spec D4「复用列表端点按 :id 定位」未消解的跨阶段数据流缺口;当前用「用户号=主键值」的实现并未解决该缺口,反而引入了确定性错误。 - 修复方向(任选其一,实现期定):
- FE-03 双击进入时经
navigate('/usr/users/' + row.id, { state: { user: row } })携带行数据,使 edit 态走presetUser分支(spec D4 备注已给此先例),避免二次取数;并相应为/new之外的 edit 直链场景兜底;或 - 与后端对齐补「单用户详情」读端点 / 列表端点按主键定位的能力(跨阶段对齐项,记入 decisions),不要把主键塞进「用户号」查询字段。
- FE-03 双击进入时经
该项单独成立即触发 request-changes。
三、建议项(非 must-fix,可在本轮或后续消化)
- S1.
frontend/tests/e2e/userdetail.spec.ts:103-110的 GET 存根对任意/api/usr/users**请求都返回makeUser(7,'zhangsan'),与请求 query 参数无关,因而掩盖了 B1(存根没有校验queryField/queryValue是否真的命中目标用户)。建议在 edit 预填用例里断言实际发出的 query 参数,避免再次掩盖此类数据流缺陷。 - S2.
frontend/src/pages/usr/UserDetail/UserDetail.module.css:25/29/34工具栏按钮与齿轮用硬编码#ffffff。tokens.css 无「深色条上的白色前景」语义 token;该白色前景与.toolbar的#2c2f36深色装饰底强绑定,属 spec § 7 D10 已豁免的「工具条局部装饰」同一范畴,故不作 must-fix。若希望严格收敛,可在 tokens.css 登记一个工具条前景/底色 token 一并替换。 - S3.
frontend/src/pages/usr/UserDetail/useUserDetail.ts:150catch 分支用于错误文案选择的permissions.length读取的是闭包旧值(runLoad的eslint-disable exhaustive-deps),仅影响 loadError 文案精度,不影响功能;可改读permissionsRef或不区分文案。
四、对照确认(通过项,留痕)
- 原型一致性:工具栏(保存/取消/新增 + 5 占位按钮 + 齿轮)、3 列
form-grid(创建时间/制单人只读 + 员工名/用户名/类型/语言/用户号 + 单据修改权限复选框)、tabs-row(权限组 active + 5 占位页签 disabled)、perm-list(表头全选 indeterminate + 逐行复选框)结构与#screen-userdetail一致;主操作「保存」置于工具栏左侧主按钮,无结构性偏移。 - Design Tokens:表单/权限区语义色均走
var(--color-*);工具栏深色底#2c2f36为 D10 豁免装饰;未发现语义色硬编码(白色前景见 S2)。 - 业务校验前端复刻:用户名必填 + 3-20 位字母数字下划线正则(BR3)、用户号必填(BR4)、类型/语言必填枚举(BR6/BR7)、单据修改权限默认否(BR8)、员工名联动带出(BR5)、密码不采集(BR9)、权限全量覆盖语义(BR11)均已实现;错误码 40001/40901/40401/40301 文案与就近高亮(用户名冲突
form.setFields)对齐 spec § 4。 - API 调用一致性:
createUser(POST/usr/users)、updateUser(PUT/usr/users/{id})、listEmployees/listPermissions/getUserDetail全部经frontend/src/api/request.ts统一实例,无裸fetch/axios;请求体字段(sUserName/sUserNo/iEmployeeId/sUserType/sLanguage/iCanModifyBill/permissionIds、PUT 含iIsVoid不含sUserName)对齐 docs/05 § REQ-USR-001/002。 - 状态机覆盖:loading(
initialLoading+Spin)/ empty(权限空态perm-empty)/ error(loadError重试入口 +notFound返回列表)/ 正常(editing)/ 提交中(submitting置loading+disabled防重)五态齐备。 - a11y(客观项):表单控件均经 AntD
Form.Item label关联;危险/导航类「取消」有未保存改动二次确认(Modal.confirm)。颜色对比度/响应式为 best-effort,未见明显失败,不单独触发 request-changes。
五、裁决
存在 1 项 blocker(B1:edit 预填数据流错误,正常导航流必然 40401),故本轮裁决 request-changes。其余为通过项与建议项。