40 KiB
40 KiB
页面、信息架构与跨端静态审查
本文件保留
bbb5c89分域静态审查的原始分级和证据,不再作为当前唯一台账。 2026-07-17 的严重度校准、浏览器行为结论和四层状态以research/findings.md与research/browser-review.md为准;不要直接汇总本文件中的旧 P1/P2 数量。
结论摘要
- 静态覆盖了
client/src/pages/下 75 个 TSX 页面/页面子模块(约 4.15 万行)、client/src/router/index.tsx的 76 个<Route>声明、PC/移动导航、全局壳、共享列表/详情/编辑模板,以及 63 个 Playwright spec 的路由与页面相关资产;对标题动作、移动路由、全局视觉、权限负例和既有 finding 对应 spec 做了重点抽读。 - 本轮只读静态审查没有发现 P0。
- 新增唯一 finding 为 P1 7 条、P2 8 条;其中 1 条 P1 因必须在 768/1024 浏览器中观察实际裁切而标为
LIKELY,其余为CONFIRMED。 - 从
05-31-unified-page-header-actions复核出 P1 3 条、P2 2 条,全部保持DUPLICATE,没有重复创建新问题。 - 若把重复项也计入风险地图,当前页面维度合计为 P0 0 / P1 10 / P2 10;用于后续建修复 task 时,应以唯一新增的 P0 0 / P1 7 / P2 8 为准。
- 本文件只代表当前本地代码静态结论;没有访问公网、真实数据或企业微信真机,也没有运行浏览器。真机 WebView 结论仍属外部准入。
| 状态 | P0 | P1 | P2 | 说明 |
|---|---|---|---|---|
CONFIRMED(新增) |
0 | 6 | 8 | 可由当前代码路径、DOM 结构或测试合同直接证明 |
LIKELY(新增) |
0 | 1 | 0 | 表格在 768/1024 的实际裁切需浏览器量测 |
DUPLICATE(既有) |
0 | 3 | 2 | 已登记在 05-31-unified-page-header-actions |
审查口径
- 合同层:当前 PRD/design、
.trellis/spec/hyb-oa/frontend-react-vite-design.md、docs/产品设计规则.md。 - 实现层:路由、导航、页面元数据、共享模板、页面状态与动作、移动/企微路由转换。
- 证据层:相关 E2E 的路由、视口、断言和覆盖矩阵;未用旧截图代替当前行为。
ui-ux-pro-max只用于补充表格响应式、表单可访问性、错误反馈、焦点、对比度和prefers-reduced-motion检查。其外部配色/风格建议不用于替换当前 OA 视觉基线。
PC、移动与企业微信入口映射
路由资产
| 表面 | 路由族 | 页面/模板归属 | 当前跨端归属 |
|---|---|---|---|
| 认证与兜底 | /login、/forgot-password、/change-password、/403、* |
AuthLayout 或独立全屏状态页 |
普通浏览器与企微共用;不进入 PC/移动业务壳 |
| PC 工作台与协作 | /portal、/announcements、/team、/weekly-reports、/todos、/dashboard、/notifications、/settings/profile |
工作台、台账列表或个人设置 | 移动端分别映射首页、通知、个人设置;其余多折叠回移动首页 |
| PC 业务台账 | /reimbursements、/purchase-requests、/contracts-registry、/projects、/assets、/payment-batches、/ap-operations、/invoice-pool、/budgets、/inventory-checks、/travel-requests、/leave-requests、/attendance、/wecom-approval-archive |
BusinessListTemplate、页面本地台账或记录视图 |
报销、采购、请假、票据、资产/盘点有专用移动表达;项目、合同、付款/AP、预算、差旅等折叠回移动首页 |
| PC 详情/处理 | /projects/:projectId、/reimbursements/:reimbursementId、/reimbursements/:reimbursementId/print、/workflow/tasks/:taskId,以及采购/资产/付款/票据的 ?id= 模式 |
BusinessDetailTemplate、EntityOverviewHero 或显式详情区 |
报销、审批、采购有移动详情;资产只映射扫码页;项目、合同和财务详情没有移动详情 |
| PC 管理 | /audit-logs、/delegations、/payroll、/performance、/organization、/people/**、/system/** |
管理配置页、审计/排障页 | 小屏/企微统一折叠到 /mobile/me,不复制 PC 管理台 |
| 移动工作台 | /mobile、/mobile/me、/mobile/profile、/mobile/notifications、/mobile/wecom-approval-archive、/mobile/capture、/mobile/reimbursements/**、/mobile/leave/**、/mobile/purchase-requests/**、/mobile/assets/scan、/mobile/approvals/** |
12 个 Mobile*Page,部分列表/详情复用同页 |
企业微信 WebView、钉钉容器或触控小屏优先进入该路由族 |
自动转换规则
- 精确映射:工作台/数据看板 → 移动首页;通知 → 移动通知;个人设置 → 移动个人设置;报销/审批/采购/请假 → 对应移动列表或详情;票据 → 移动上传;资产/盘点 → 移动扫码。
- 管理映射:
/system*、/people*、/organization*、/payroll*、/performance*→/mobile/me。 - 折叠映射:项目、合同、差旅、付款、AP、周报、待办、团队、考勤、预算、公告、委派 →
/mobile。 - 触发条件:企业微信/钉钉 UA,或
maxTouchPoints > 0且视口不大于 768px。真机企业微信仍需人工复核,普通桌面浏览器缩窄视口不能替代。 - 移动底栏只固定
首页 / 待办或进度 / 报销 / 我的;请假、采购、通知和资产扫码从首页或“我的”进入。
当前设计中做得较好的部分
- PC 业务列表已普遍分离标题、命令栏、桌面表格和移动卡片;项目、资产、合同、付款、票据、采购和报销不再默认自动选择首条记录填充右侧详情。
- 报销、审批、采购和请假已有独立移动列表/详情;移动壳处理了视觉视口高度、safe area、深层详情隐藏底栏等基础问题。
- 请假/考勤页面总体明确“企微主办、OA 镜像/应急接管”,普通员工的 OA 日常请假表单已隐藏。
- 审批 PC/移动详情的通过、退回和驳回都进入
ConfirmDialog,没有直接从按钮瞬时执行审批动作。 - 共享
BusinessDetailTemplate、BusinessEditTemplate、空状态、骨架屏、状态徽标和确认弹窗已形成可继续治理的基础;问题主要是合同未覆盖权限、未保存、可访问性和响应式边界。
/mobile/wecom-approval-archive 的 375px 专项静态预期
- 该移动路由并非独立移动页,而是直接复用
WecomApprovalArchivePage;路由允许具备leave.view_own或attendance.view_own的普通查看角色进入,并非仅 HR/管理员路径(client/src/router/index.tsx:336、:338、:339)。 - 在 375px 下,四项筛选静态上应由
md:grid-cols-4回落为单列;桌面表格由hidden ... md:block隐藏,md:hidden的卡片列表显示;详情主栅格到lg才切三列,字段摘要和参与人/动作到md才切多列,因此静态结构预期为单列移动表达,而不是压缩的 PC 宽表(client/src/pages/WecomApprovalArchivePage.tsx:176、:227、:246、:276、:297、:307)。 - 列表加载态虽然调用六列
TableSkeleton,但骨架只由可收缩的flex-1短条组成,没有 table 或min-width;静态上不应制造横向滚动,不过六列在 375px 下是否显得过碎仍需截图判断(client/src/pages/WecomApprovalArchivePage.tsx:218;client/src/components/common/TableSkeleton.tsx:11、:12、:18)。 - 该路由不属于移动深层详情,底栏会保持显示;移动壳已按底栏高度、页面纵向间距和 safe area 增加内容底部 padding,静态上末尾详情不应被遮挡(
client/src/layouts/AppShellLayout.tsx:15、:18、:71、:79)。 - 以上仅是 DOM/Tailwind 合同推导,不是浏览器 PASS。必须在 375px 实跑空态、加载态、有记录、打开详情、长文案和长人员名,量测页面
scrollWidth、末尾内容与底栏位置,并分别覆盖普通员工“与我相关”和 HR/管理员范围;真实企业微信 WebView 仍是外部准入。
新增 P1 Finding
PAGE-P1-001:付款批次仍以“粘贴内部 ID”作为首屏主操作
- 维度:功能闭环 / 信息语义 / 财务页面。
- 严重级别:P1。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
client/src/pages/PaymentBatchesPage.tsx:347、:356、:377、:403;client/tests/e2e/reimbursement-payment-batch-flow.spec.ts:107;.trellis/spec/hyb-oa/frontend-react-vite-design.md:777;docs/产品设计规则.md:110。 - 证据:付款页把
新建批次放在标题主操作区;创建面板要求用户输入“待付款单据 ID”,占位文案明确要求粘贴报销或差旅单据 ID。现有 E2E 只能先从 API 取得reimbursementId再填入。当前 spec 同时规定付款首屏应先展示待复核/待付款/异常,批次创建不应成为首屏主操作;普通路径不得要求 UUID/内部 ID。 - 影响:真实财务用户通常只知道报销编号、人员、金额和付款状态,无法可靠取得 UUID;复制错误 ID 可能造成错批次、漏单或批次创建失败。当前自动化能跑通不代表人工可完成。
- 确认方式:用付款管理员打开
/payment-batches,点击新建后确认只有 ID 文本框而没有待付候选列表;尝试输入业务编号验证是否无法完成。静态接口与 E2E 已证明当前合同使用内部 ID。 - 建议:从 AP/待付池按业务编号、对象、人员、金额、证据状态提供可搜索多选;前端内部再提交 ID。批次创建进入待付列表的上下文动作或明确的角色工具,不作为普通首屏主 CTA。
- 修复边界:付款页、待付候选查询/API、付款批次创建 E2E;不改变付款状态机或真实付款边界。
- 是否需用户决策:是——确认以“应付账款待付池发起”还是“付款页候选选择器”为唯一主入口。
PAGE-P1-002:危险动作确认合同没有系统性执行
- 维度:交互安全 / 证据链 / 管理操作。
- 严重级别:P1。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
docs/产品设计规则.md:81;.trellis/spec/hyb-oa/frontend-react-vite-design.md:1189;client/src/pages/PurchaseRequestsPage.tsx:1630;client/src/pages/PaymentBatchesPage.tsx:595;client/src/pages/ReimbursementDetailPage.tsx:1537;client/src/pages/UserManagementPage.tsx:299;client/src/pages/PortalAnnouncementsPage.tsx:190;server/src/routes/reimbursements.js:2660;server/src/routes/paymentBatches.js:1062。 - 证据:采购附件、报销附件和付款回单的“删除”按钮直接调用 delete mutation;后端执行
deleteMany。用户禁用/启用、重置密码、公告停用/归档也从列表按钮直接 mutation,没有对象摘要、后果、理由或确认词。 - 影响:一次误点即可删除财务/采购证据、使用户失去登录能力、停用制度公告或改变配置状态;即使后端记录审计,也不能代替前置防误触。
- 确认方式:在本地 mock 数据中点击上述动作,确认 mutation 前没有
action-confirm-dialog;同时检查后端是否物理删除或可恢复。 - 建议:把持久化附件删除、账号禁用/密码重置、公告发布/停用/归档等接入带类型的共享确认合同;至少展示业务编号/文件名/用户、影响、可逆性和必要理由。附件删除应明确是否物理删除、软删除或仅解除关联。
- 修复边界:共享确认组件及上述核心页面/回归测试;不在页面任务中改变后端权限和审计语义。
- 是否需用户决策:否——现行产品规则已经明确要求二次确认;仅删除/保留策略需要在具体修复 task 内确认。
PAGE-P1-003:长表单没有未保存更改保护
- 维度:表单可靠性 / 数据丢失防护。
- 严重级别:P1。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
.trellis/spec/hyb-oa/frontend-react-vite-design.md:920;client/src/components/patterns/BusinessEditTemplate.tsx:8、:35;client/src/pages/PurchaseRequestsPage.tsx:793、:809;client/src/pages/AssetRegistryPage.tsx:717、:732;client/src/pages/ContractsRegistryPage.tsx:410。 - 证据:全前端没有
useBlocker、beforeunload、dirty-state 或“未保存”实现。共享编辑模板的返回动作是直接<Link>;采购、资产在包含大量字段/明细/材料的编辑状态下点击“返回列表”会直接清空详情/创建状态。 - 影响:采购明细、资产建卡、合同登记、报销编辑等长表单可能因误点返回、侧栏跳转、浏览器返回或刷新而丢失尚未保存的输入,用户无法恢复。
- 确认方式:分别在采购草稿、资产新建和合同编辑中修改字段但不保存,点击返回列表、侧栏或浏览器返回/刷新,观察没有离开确认且输入丢失。
- 建议:新增共享
useUnsavedChanges/dirty 合同,覆盖路由离开、浏览器刷新、弹窗关闭和切换记录;保存成功后清除 dirty。先覆盖采购、报销、资产、合同,再覆盖管理长表单。 - 修复边界:共享 hook/编辑模板与 A/B 类长表单;不把自动保存作为本轮默认扩张。
- 是否需用户决策:否——保护原则已由 spec 明确;自动保存是否引入另行决定。
PAGE-P1-004:移动/企微考勤入口自循环,通知直达对多类对象丢失目标
- 维度:跨端归属 / 企业微信 deeplink / 移动信息架构。
- 严重级别:P1。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
docs/产品设计规则.md:101、:106、:116;client/src/pages/MobilePortalPage.tsx:348、:362;client/src/lib/notificationLinks.ts:52、:67、:75;client/src/lib/runtimeContext.ts:99、:113;client/src/pages/MobileNotificationsPage.tsx:124、:167。 - 证据:移动首页“查看考勤汇总”链接到
/attendance;在企微/触控小屏中该 PC 路由又被resolveMobileEquivalentPath折叠回/mobile,形成自循环。移动通知把考勤修正目标也映射为/attendance;资产/盘点目标统一映射/mobile/assets/scan,不携带资产 ID 或现有的inventoryCheckId参数。 - 影响:企微用户点击考勤入口回到原首页;考勤、资产和盘点通知虽然显示“直达详情”,实际无法定位原对象,用户必须重新查找,破坏消息到业务目标的闭环。
- 确认方式:用 WeCom UA 访问
/mobile点击“查看考勤汇总”;再构造attendance_correction、asset、inventory_check三类通知,核对最终 URL 与目标对象是否保留。 - 建议:为考勤提供明确的移动镜像/摘要路由,或允许该只读页面在企微中响应式打开;资产目标传
id/查询值,盘点传inventoryCheckId;每种通知类型增加“最终 URL + 目标内容”断言。 - 修复边界:移动首页、通知目标解析、移动考勤/资产/盘点入口和相关 E2E;不触碰企微真实发送或写入。
- 是否需用户决策:是——确定考勤汇总采用专用移动页,还是允许现有 PC 只读页在企微 WebView 中打开。
PAGE-P1-005:全局搜索对多类命中只跳列表,不跳命中对象
- 维度:全局导航 / 搜索可用性。
- 严重级别:P1。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
server/src/routes/search.js:173、:179、:201、:206、:341、:349、:378、:386;client/src/components/GlobalSearchDialog.tsx:95、:157;client/src/pages/AssetRegistryPage.tsx:233;client/tests/e2e/global-search.spec.ts:53、:59。 - 证据:项目、报销和采购结果携带对象路由/
?id=,但资产、请假、合同、资料引用、公告、周报和待办结果只返回通用列表路径。搜索弹窗点击时原样导航item.href;资产页本可接收?id=,合同页只有projectId,请假 PC 页没有命中 ID 处理。现有 E2E 只点击并断言报销结果。 - 影响:用户明确点击一个资产、合同或请假搜索结果后,只看到普通列表且无法确认命中项,数据量稍大时“全局搜索”失去直接导航价值。
- 确认方式:搜索一条非首屏资产、合同和请假记录,点击结果后检查是否打开/高亮该对象;当前静态 href 已足以证明目标参数缺失。
- 建议:所有对象型结果统一返回稳定详情路由或
?id=/highlight=;页面接收参数并打开/高亮对象。公告/周报/待办若无详情路由,至少滚动定位并高亮;补每种实体的点击回归。 - 修复边界:搜索 API href 合同、对应页面 deeplink 处理和搜索 E2E;不扩大搜索数据权限。
- 是否需用户决策:否。
PAGE-P1-006:共享业务表格在 768/1024 断点存在右侧列被裁切风险
- 维度:响应式 / 表格可用性。
- 严重级别:P1。
- 置信度:高。
- 状态:
LIKELY。 - 文件与行号:
client/src/components/patterns/BusinessListTemplate.tsx:24、:34;client/src/components/patterns/ListWorkspace.tsx:92;client/src/index.css:318、:547;client/src/layouts/AppShellLayout.tsx:65;client/src/components/navigation/Sidebar.tsx:6。 - 证据:
BusinessTable从md开始显示、内部表格最小宽度 760px;外层.table-container是overflow-hidden而不是横向滚动。768px 下内容区扣除页面边距后不足 760px;1024px 时侧栏开始占 288px,主内容区同样不足 760px。项目、资产、合同、付款、票据、报销六个核心列表复用该结构。 - 影响:常见 768/1024 视口可能裁掉最右“状态/操作”列,且容器没有横向滚动,直接影响查看详情或处理入口。
- 确认方式:在 768×1024 与 1024×768 分别打开
/projects、/assets、/contracts-registry、/payment-batches、/invoice-pool、/reimbursements,量测 table/container 的clientWidth、scrollWidth、overflowX,确认操作列是否可见和可滚动。 - 建议:让共享表格容器
overflow-x-auto,或把桌面表格断点提高到内容真正容得下的宽度并继续显示卡片;在 768/1024 加操作列可见断言。 - 修复边界:共享表格 CSS/模板及六页响应式回归;不逐页打补丁。
- 是否需用户决策:否。
PAGE-P1-007:核心表单标签与动态错误缺少可访问性语义
- 维度:可访问性 / 表单反馈。
- 严重级别:P1。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
client/src/pages/ContractsRegistryPage.tsx:416、:425、:456;client/src/pages/ProjectsPage.tsx:201;client/src/components/patterns/BusinessEditTemplate.tsx:131;client/src/pages/PurchaseRequestsPage.tsx:830、:966;client/src/pages/MobileNotificationsPage.tsx:218。 - 证据:合同和项目等核心编辑表单大量使用独立
<Label>,但没有htmlFor,相邻 Input/select 也没有id,因此视觉标签没有程序化关联。共享验证摘要和采购/移动通知动态错误只渲染红色块,没有role="alert"、aria-live、aria-invalid或字段级aria-describedby。启发式扫描发现页面/组件约 529 个表单控件、375 个 label,但只有 177 个htmlFor;整个页面/组件范围只有登录与忘记密码两处aria-live。数量仅用于说明规模,finding 以列出的确定样例为准。 - 影响:屏幕阅读器无法可靠读出字段名称或获知异步错误;键盘/辅助技术用户在合同、项目、采购和移动流程中可能无法定位失败字段,核心流程并非等价可用。
- 确认方式:对合同/项目创建表单取 accessibility snapshot 或运行 axe;触发采购列表/表单错误,确认 live region 是否产生公告;逐字段核对 accessible name。
- 建议:建立共享 Field/FieldError 合同,强制稳定 id、
htmlFor、aria-invalid、aria-describedby;动态错误使用适当的role=alert/aria-live并把焦点移动到摘要或首个错误字段。 - 修复边界:共享表单/验证组件和核心表单分波迁移;不以批量字符串替换代替逐页验证。
- 是否需用户决策:否。
新增 P2 Finding
PAGE-P2-001:路由元数据、页面标题与浏览器标题存在系统性漂移
- 维度:信息架构 / 标题与导航。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
client/src/features/navigation/schema.ts:5、:53;client/src/components/navigation/Topbar.tsx:56、:168;client/src/layouts/AppShellLayout.tsx:49;client/index.html:7;client/src/pages/ReimbursementDashboardPage.tsx:138;client/src/pages/NotificationsPage.tsx:82;client/src/pages/TeamPresencePage.tsx:262。 - 证据:
PAGE_TITLES只覆盖部分 PC 路由,未命中时把原始 pathname 当标题并写入最近访问。/reimbursements/dashboard被较长的/reimbursements/误识别成“报销详情”;/system/users、/system/departments、/system/master-data、/delegations、/payroll、/performance等会显示原始路径。已有映射也存在“通知/通知中心”“团队/团队状态”“数据看板/仪表盘”等漂移。浏览器<title>始终是“海音达工作台”,没有按路由更新。 - 影响:顶部标题、页面 H1、最近访问和浏览器 tab/back 历史语义不一致;管理页可能直接暴露内部路由文本。
- 确认方式:依次打开统计、系统用户、通知、团队页面,对比 Topbar、H1、最近访问和
document.title。 - 建议:建立唯一 typed route manifest,统一标题、分组、权限、导航和可选详情标题;路由变化时同步
document.title。详情标题可由页面覆写,但不得回落原始 pathname。 - 修复边界:前端路由元数据、Topbar、最近访问和路由标题测试;不改业务路由 URL。
- 是否需用户决策:否。
PAGE-P2-002:403、404 和渲染错误的恢复按钮仍指向隐藏旧仪表盘
- 维度:错误恢复 / 信息架构。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
client/src/pages/ForbiddenPage.tsx:9、:13;client/src/pages/NotFoundPage.tsx:9、:11;client/src/components/common/ErrorBoundary.tsx:33、:50;client/src/router/index.tsx:154;server/src/services/workspaceSettings.js:136。 - 证据:三类兜底都把“返回首页/工作台”链接写成
/dashboard;当前默认工作台是/portal,Dashboard 在默认工作区中为 hidden,且普通用户进入后可能再次看到看板无权限状态。 - 影响:发生 403/404/渲染错误时,用户没有被带回稳定可用入口,容易进入第二个无权限/空错误页面;文案与实际去向也不一致。
- 确认方式:普通员工访问无权限路由、未知路由,并触发 ErrorBoundary 测试 fallback,点击返回按钮核对最终页面。
- 建议:统一使用
/portal或resolveDefaultWorkspacePath();错误页提供“返回上一页 + 回到工作台”,不要显示原始错误 message 作为普通用户主文案。 - 修复边界:共享错误/403/404 页面及聚焦 E2E。
- 是否需用户决策:否。
PAGE-P2-003:冻结的差旅能力仍散落在非差旅页面和入口合同中
- 维度:范围收口 / 兼容残留 / 跨页面一致性。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
docs/产品设计规则.md:151;.trellis/spec/hyb-oa/frontend-react-vite-design.md:189、:241;client/src/router/index.tsx:244;server/src/services/workspaceSettings.js:99、:138;client/src/pages/PaymentBatchesPage.tsx:387;client/src/pages/MobileApprovalsPage.tsx:84;client/src/pages/NotificationsPage.tsx:17;client/src/pages/MobileNotificationsPage.tsx:17。 - 证据:当前事实源要求差旅不进入主线、冻结模块不静默恢复;但差旅路由仍注册为管理/更多入口,付款批次无条件提供“差旅订单付款”,PC/移动通知筛选和移动审批筛选持续显示差旅。
- 影响:冻结能力继续占用普通流程的认知空间,测试和维护容易误把差旅当作启用主线;付款用户甚至能选择一个当前不应扩展的实体类型。
- 确认方式:使用付款管理员和普通移动角色检查上述入口是否在没有独立恢复 task/feature gate 时可见。
- 建议:先移除非差旅页面的差旅选项和筛选;差旅路由保持隐藏或显式冻结边界。若为历史数据查询必须保留,只在独立只读/诊断入口通过明确 feature flag 开放。
- 修复边界:页面入口、导航/feature gate、相关旧 E2E;数据模型/API 删除应另做兼容与迁移评估。
- 是否需用户决策:是——确认“仅隐藏/冻结兼容”还是在独立清理 task 中删除前端和无消费者代码;结合用户已表达偏好,推荐先删用户可见入口,再审计后端消费者。
PAGE-P2-004:普通路径仍暴露内部错误码与技术状态文案
- 维度:文案语义 / 错误恢复。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
.trellis/spec/hyb-oa/frontend-react-vite-design.md:688、:1151;client/src/pages/MobilePortalPage.tsx:36;client/src/pages/MobileApprovalsPage.tsx:194;client/src/pages/PurchaseRequestsPage.tsx:78;client/src/pages/ContractsRegistryPage.tsx:94;client/src/components/GlobalSearchDialog.tsx:118;client/src/components/common/ErrorBoundary.tsx:33;client/src/pages/WecomApprovalArchivePage.tsx:40、:47、:124、:222、:258、:328、:332;client/src/router/index.tsx:336、:338。 - 证据:静态扫描有 56 个页面/组件文件使用
${error.code}: ${mapApiErrorMessage(...)}一类模式,覆盖移动首页、移动审批、采购、合同、搜索等普通路径;ErrorBoundary 直接展示error.message。移动企微审批归档页也在空态暴露PARTIAL_ARCHIVE_NEEDS_DATA,在筛选、卡片和详情中直接使用英文finding,并把findingType、status、severity原值直接输出。该路由允许leave.view_own/attendance.view_own查看角色进入,不是仅管理员可见的排障页。当前 spec 明确普通用户不得看到错误码、内部状态码或技术 dump。 - 影响:异常或对账信息出现时,用户先看到
PERM_001、VALID_001、finding、内部验收状态或原始枚举等平台语言,弱化“原因 + 影响 + 下一步”,也可能暴露不必要的内部实现信息;375px 卡片上的技术文案更直接占用有限空间。 - 确认方式:分别 mock 权限、校验和网络错误,检查普通页面/移动页可见文本;在 375px 企微归档页覆盖空态、含 finding 的卡片和详情,并核对普通员工角色可见文本;管理员审计/排障页可作为允许显示技术摘要的对照组。
- 建议:共享错误解析返回
userMessage与可选diagnosticCode;普通页面只显示自然中文和恢复动作,技术码只在管理/审计折叠区或日志中显示。企微归档页把finding统一翻译为“对账问题”,为类型、状态和严重度建立中文映射,并移除面向验收脚本的空态 token。 - 修复边界:共享错误解析与普通页面调用点分波迁移;不改变服务端错误合同。
- 是否需用户决策:否。
PAGE-P2-005:多处桌面表格行只能鼠标点击,缺少键盘等价入口
- 维度:键盘可达 / 表格交互。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
client/src/pages/InventoryChecksPage.tsx:323;client/src/pages/AssetRegistryPage.tsx:856;client/src/pages/PurchaseRequestsPage.tsx:1046;client/src/pages/PerformancePage.tsx:207;client/src/components/master-data/HolidayCalendarTab.tsx:368。 - 证据:上述
<TableRow>/<tr>直接挂onClick,但没有可聚焦语义、键盘处理或统一行内操作。盘点和部分管理表格依赖点击整行选择记录。 - 影响:只用键盘或开关设备的用户无法选择盘点、资产分类、绩效周期等记录;鼠标悬停样式无法提供等价可达性。
- 确认方式:从页面标题开始只按 Tab/Enter/Space,尝试选择上述行并进入后续操作。
- 建议:优先在操作列放真实
<button>/<Link>;确需整行选择时仍保留显式可聚焦主操作,并给选中状态aria-selected,不要只给tr增加伪按钮语义。 - 修复边界:共享表格行操作约定和列出的五处页面。
- 是否需用户决策:否。
PAGE-P2-006:共享视觉 token 与动效未达到当前可访问性基线
- 维度:视觉设计 / 低视力与动态效果。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
client/src/design/theme.css:8、:10;client/src/index.css:64、:318、:327、:559、:1098。 - 证据:
--color-text-tertiary: #94a3b8在白色背景的 WCAG 对比度约 2.56:1,却被用于 12px 表头/元信息;text-zinc-400也约 2.56:1。所有.page-scaffold固定执行 320ms 位移动画,代码没有prefers-reduced-motion分支。 - 影响:低视力用户难以识别表头和关键元信息;明确请求减少动态效果的用户仍会看到页面位移动画。
- 确认方式:在 100%/200% 缩放下检查表头/元信息对比度,并在系统启用 reduce motion 时记录 computed animation。
- 建议:语义正文/表头至少使用达到 AA 的色阶,浅色只保留非必要装饰;添加
@media (prefers-reduced-motion: reduce)关闭页面位移及非必要过渡。 - 修复边界:共享主题/CSS token 与全局视觉回归,不逐页改色。
- 是否需用户决策:是——若项目尚无可访问性等级,建议把 WCAG 2.1 AA 设为普通文字与交互的验收基线。
PAGE-P2-007:“全页面视觉巡检”矩阵与路由已漂移,且缺关键中间断点
- 维度:视觉验证可信度 / 页面覆盖。
- 严重级别:P2。
- 置信度:高。
- 状态:
CONFIRMED。 - 文件与行号:
docs/trialrun/项目级流程覆盖矩阵.csv:2、:6;client/tests/e2e/project-wide-visual.spec.ts:129、:363、:390、:405;client/tests/e2e/r106-visual-audit.spec.ts:506、:532;client/tests/e2e/r103-cross-page-ui-consistency.spec.ts:339;client/src/router/index.tsx:336;client/tests/e2e/oafront3-pc-readonly-visual.spec.ts:118。 - 证据:项目级覆盖矩阵只有 5 个业务行,系统管理写为
/system/*;runner 只替换少数动态 ID,不展开 wildcard,会把/system/*原样导航成 404,同时只审 1440/390。R106 仍把 PC 请假写成不存在的/leave,审查函数没有校验预期 H1/404,可能给 404 截图记“PC 请假”。R103 只有采购、票据和移动票据三项,未覆盖项目/资产,也没有 768/1024。仓库已注册/mobile/wecom-approval-archive,但当前只找到 PC/wecom-approval-archive的 HR 视觉条目,没有该移动路由的 375/390 覆盖。 - 影响:测试名称和报告容易给出“全页面已看”的错误信心;正好遗漏共享表格最危险的中间断点和多个管理/业务页面。
- 确认方式:运行
--list后打印最终 route plans,断言不得含*,每页必须核对最终 URL、预期 H1 且不得出现 403/404;统计与真实路由清单的差集。 - 建议:从 typed route manifest 生成显式审查矩阵;动态详情由 fixture 解析,冻结/无权限明确标注;增加 768/1024,失败条件包含错误页、4xx 页面和标题不匹配。
- 修复边界:视觉矩阵、项目级/R106/R103 E2E 和报告 schema;不在测试 task 中改页面实现。
- 是否需用户决策:否。
PAGE-P2-008:移动导航和“我的”仍显示无权限或未校验权限的入口
- 维度:权限显隐 / 移动信息架构。
- 严重级别:P2。
- 置信度:中高。
- 状态:
CONFIRMED。 - 文件与行号:
.trellis/spec/hyb-oa/frontend-react-vite-design.md:632、:644;client/src/components/mobile/MobileBottomNav.tsx:39、:46;client/src/pages/MobileMePage.tsx:54、:112;client/src/router/index.tsx:353、:371。 - 证据:移动底栏对无报销权限角色仍渲染 disabled“报销不可用”,而不是隐藏;移动“我的”只检查采购/资产/审批,却无条件显示“请假记录”和“报销”,对应路由本身又有权限守卫。
- 影响:特殊只读、停用或最小权限角色会看到不可用入口,点击“我的”中的未校验链接可能直接进入 403;与“未授权动作通常隐藏”合同不一致。
- 确认方式:用不含
expense.view_*、leave.view_*的最小权限 fixture 打开/mobile与/mobile/me,检查底栏/入口和点击结果。 - 建议:入口清单统一使用同一 permission predicate,未授权业务入口直接隐藏;只有确有申请权限/配置缺失说明时才显示 disabled 状态和真实下一步。
- 修复边界:移动导航 schema、BottomNav、MobileMe 与角色矩阵 E2E。
- 是否需用户决策:否。
既有 Finding 复核与去重
以下 5 条均已在 .trellis/tasks/05-31-unified-page-header-actions/prd.md:108 登记,本轮不重复建账。
PAGE-DUP-P1-001:复核角色仍看到“新建批次”
- 维度:付款权限显隐。
- 严重级别:P1。
- 置信度:高。
- 状态:
DUPLICATE。 - 文件与行号:
client/src/pages/PaymentBatchesPage.tsx:110、:347、:356、:372;既有记录.trellis/tasks/05-31-unified-page-header-actions/prd.md:110。 - 证据:页面已计算
canManageBatch,但列表标题“新建批次”没有按它隐藏;复核用户点击后才看到只读说明。 - 影响:误导复核角色进入无法完成的写流程;服务端 403 虽防止写入,但不修复页面合同。
- 确认方式:仅授予
risk.review.manage的角色打开付款列表,检查标题按钮。 - 建议:按钮和创建面板都以
canManageBatch为条件;增加 review-only 负例。 - 修复边界:独立权限显隐修复 task。
- 是否需用户决策:否。
PAGE-DUP-P1-002:合同只读角色仍看到完整写动作
- 维度:合同权限显隐。
- 严重级别:P1。
- 置信度:高。
- 状态:
DUPLICATE。 - 文件与行号:
client/src/pages/ContractsRegistryPage.tsx:200、:284、:310、:334、:798、:861;既有记录.trellis/tasks/05-31-unified-page-header-actions/prd.md:111。 - 证据:页面只对人员目录做权限判断,没有合同写权限布尔值;新增、编辑、续约和归档对
project.view_own/view_all只读用户可见,最终依赖 API 403。 - 影响:只读用户无法判断真实能力,可能填写整张合同后才失败。
- 确认方式:
project.view_own/project.view_all且无 manage/settings 的角色打开合同页。 - 建议:建立合同 view/manage 权限动作矩阵,写动作默认隐藏,保留只读详情/台账。
- 修复边界:独立合同权限 task,需跨层 reviewer。
- 是否需用户决策:否。
PAGE-DUP-P1-003:票据上传按钮没有使用已计算的上传权限
- 维度:票据权限显隐。
- 严重级别:P1。
- 置信度:高。
- 状态:
DUPLICATE。 - 文件与行号:
client/src/pages/InvoicePoolPage.tsx:56、:59、:475、:485;既有记录.trellis/tasks/05-31-unified-page-header-actions/prd.md:112。 - 证据:
canUploadInvoice已计算,但标题“上传票据”和上传面板没有引用它。 - 影响:只读/管理范围角色可能进入最终失败的上传流程。
- 确认方式:无
invoice.upload、有只读入口权限的 fixture 检查标题动作和面板。 - 建议:按上传权限隐藏标题动作和上传面板,并补直连负例。
- 修复边界:独立票据权限 task。
- 是否需用户决策:否。
PAGE-DUP-P2-001:两条资产 E2E 仍点击旧的直接“资产分类”按钮
- 维度:测试与交互漂移。
- 严重级别:P2。
- 置信度:高。
- 状态:
DUPLICATE。 - 文件与行号:
client/tests/e2e/assets-flow.spec.ts:88;client/tests/e2e/manual/asset1d-public-trial.spec.ts:165;client/src/pages/AssetRegistryPage.tsx:743、:754;既有记录.trellis/tasks/05-31-unified-page-header-actions/prd.md:113。 - 证据:页面已把“资产分类”放入“更多”,两条测试仍按直接按钮点击。
- 影响:自动化不能验证当前真实交互,并可能在恢复公网试录时直接失败。
- 确认方式:运行对应 mock E2E;manual spec 只做静态修复后在另行批准的公网场景验证。
- 建议:测试先打开“更多”再选“资产分类”;不要为旧测试恢复直接按钮。
- 修复边界:测试修复 task;不改资产页面。
- 是否需用户决策:否。
PAGE-DUP-P2-002:标题动作容器没有数量合同,项目/资产视觉覆盖仍缺
- 维度:共享组件合同 / 视觉覆盖。
- 严重级别:P2。
- 置信度:高。
- 状态:
DUPLICATE。 - 文件与行号:
client/src/components/patterns/ListWorkspace.tsx:36;client/tests/e2e/r103-cross-page-ui-consistency.spec.ts:339;既有记录.trellis/tasks/05-31-unified-page-header-actions/prd.md:114。 - 证据:
HeaderActionGroup仅是 flex 布局,不限制“一主操作 + 最多一个更多”;R103 只覆盖采购、票据和移动票据,未覆盖项目、资产或七页面统一动作断言。 - 影响:页面后续可继续在标题区堆按钮,而现有回归不报警。
- 确认方式:静态枚举七个页面标题动作,并在 1440/1024 截图核对。
- 建议:用可测试的 slot/variant 合同表达 primary/more,增加七页面统一断言和项目/资产截图。
- 修复边界:共享标题动作组件及聚焦测试。
- 是否需用户决策:否。
建议主线程优先做的浏览器复核
按风险收益排序,均应使用本地 Vite dev、mock/无凭据数据,不使用公网或真实资料:
- 768/1024 共享表格:
/projects、/assets、/contracts-registry、/payment-batches、/invoice-pool、/reimbursements;检查右侧操作列、横向滚动、标题动作换行和页面整体scrollWidth。 - 付款角色与创建:review-only 与 manage 两种角色;检查“新建批次”显隐、候选来源、内部 ID 文案和表单 accessible name。
- 合同/票据只读角色:复核新增/编辑/续约/归档、上传票据是否隐藏,避免只在 API 403 时才告知权限不足。
- 375px 企微审批归档专项:以 375×812(可补 375×667)和 WeCom UA 打开
/mobile/wecom-approval-archive,分别 mock 空态、加载态、有记录和打开详情;覆盖长页面文案、长人员名、含对账问题记录,断言桌面表格隐藏、卡片/详情单列、documentElement.scrollWidth <= clientWidth、末尾内容不被底栏遮挡。至少使用普通员工查看权限与 HR/管理员两类角色,核对范围选项和可见数据;真机 WebView 另记EXTERNAL_GATED。 - 移动/企微直达:390px WeCom UA 打开
/mobile点击考勤汇总;在/mobile/notifications注入考勤、资产、盘点通知,检查最终目标和返回语义。 - 全局搜索:从
/portal搜索并点击资产、合同、请假、公告、周报和待办;检查是否打开/高亮唯一结果,以及状态是否仍显示in_use/reviewing等原始码。 - 未保存保护:采购、资产、合同、报销分别修改字段后点击返回、侧栏、浏览器返回和刷新。
- 键盘与可访问性:合同/项目表单 accessible name,采购错误 live announcement,盘点/资产分类/绩效表格的 Tab/Enter,移动导航最小权限角色;建议输出 accessibility snapshot 或 axe 结果。
- 错误恢复:普通员工走 403、404、ErrorBoundary 和典型 API 错误,确认回到工作台且不展示内部码。
建议修复波次
- 页面 P1 闭环:付款候选选择、危险确认、未保存保护、移动 deeplink、全局搜索直达、核心表单可访问性。
- 共享响应式与权限显隐:确认 768/1024 后修 BusinessTable;同时处理 3 条既有权限显隐 finding。
- 范围收口与语义:差旅用户可见残留、路由/标题 manifest、错误文案与 recovery 入口。
- 视觉/测试治理:对比度与 reduce motion、键盘表格、显式全路由视觉矩阵和已漂移 E2E。
审查阶段不应在同一 task 中混改上述页面;每波先建独立 Trellis task,并对权限、付款、审批和状态动作安排只读 reviewer。