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.tsxtests/),未触碰 backend/ / sql/ / scripts/,符合前端阶段路径作用域硬约束。

二、阻断项(must-fix)

B1. edit 态预填按主键查「用户号」字段,正常导航流必然取不到记录(blocker)

  • 位置:frontend/src/pages/usr/UserDetail/useUserDetail.ts:124getUserDetail({ queryField: '用户号', queryValue: String(userId) }));连带 frontend/src/pages/usr/UserDetail/index.tsx:43userId = Number(params.id))。
  • 事实依据:
    • 路由 /usr/users/:id:id用户主键——docs/05 § REQ-USR-002「路径参数 id(用户主键)」,且 FE-03 双击行入口为 frontend/src/pages/usr/UserList/index.tsx:36 navigate('/usr/users/' + row.id)row.idUserVO.id 主键),经 router state 传 presetUser
    • 因此 edit 态 presetUser 恒为 null,必走 getUserDetail 分支;该分支把主键值当作「用户号」去 GET /api/usr/users?queryField=用户号&matchType=等于&queryValue=<主键>
    • 主键 id 与业务字段「用户号」sUserNo 是不同字段,正常情况下二者取值不相等 → 列表精确匹配返回空 recordsgetUserDetail 返回 null → 进入 notFound(40401)分支,页面显示「该用户不存在或已被删除」。
    • 真实后端下,REQ-USR-002 验收「编辑预填该用户原值(基本字段 + 已授权权限回勾)」/ spec BR17 将无法满足。
  • 附加事实:docs/05 § REQ-USR-003 列表端点的 queryField 合法取值为 用户名/员工名/用户号/部门/用户类型/作废/登录日期/制单人不含主键,因此无法用列表端点按 :id 主键定位单条。这是 spec D4「复用列表端点按 :id 定位」未消解的跨阶段数据流缺口;当前用「用户号=主键值」的实现并未解决该缺口,反而引入了确定性错误。
  • 修复方向(任选其一,实现期定):
    1. FE-03 双击进入时经 navigate('/usr/users/' + row.id, { state: { user: row } }) 携带行数据,使 edit 态走 presetUser 分支(spec D4 备注已给此先例),避免二次取数;并相应为 /new 之外的 edit 直链场景兜底;或
    2. 与后端对齐补「单用户详情」读端点 / 列表端点按主键定位的能力(跨阶段对齐项,记入 decisions),不要把主键塞进「用户号」查询字段。

该项单独成立即触发 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:150 catch 分支用于错误文案选择的 permissions.length 读取的是闭包旧值(runLoadeslint-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)/ 提交中(submittingloading+disabled 防重)五态齐备。
  • a11y(客观项):表单控件均经 AntD Form.Item label 关联;危险/导航类「取消」有未保存改动二次确认(Modal.confirm)。颜色对比度/响应式为 best-effort,未见明显失败,不单独触发 request-changes。

五、裁决

存在 1 项 blocker(B1:edit 预填数据流错误,正常导航流必然 40401),故本轮裁决 request-changes。其余为通过项与建议项。