20 KiB
20 KiB
B-2b 统一报销工作台迁移
Goal
把报销从“填写详情页、审批详情页、旧只读详情页分别组织同一业务对象”收敛为唯一的交互式报销工作台。工作台以报销内容为主体,通过 edit、readonly、approval 三个模式适配器注入数据、状态和能力;审批只是叠加在报销对象上的流程与决策能力,不再复制一套报销页面壳。
本任务优先服务当前唯一有真实流转的“报销 → 审批 → 付款”链路。请假/补卡继续以企业微信为主,采购/差旅保持冻结的低频能力,不因追求形式一致而同步重构。
Background and Confirmed Facts
- B-2a 已部署到生产并通过公网真实 bundle 的 PC/移动合成验收,解决了审批人必须跳转才能取得明细、票据和附件的问题。
- B-2a 是经批准的过渡交付:先恢复核心内容可达性并产出
ReimbursementItemEvidenceList、快照投影、离线白名单与 fail-closed 合同;B-2b 负责根据真人目视反馈收敛页面结构。 - 真人目视反馈确认:通用审批壳的流程摘要、报销六字段摘要、新证据区和右侧检查计数存在重复;解释性文字按系统内部状态平铺,审批人需要的“哪条明细、什么问题、是否阻断、如何处理”反而不突出。
ReimbursementDetailPage.tsx与MobileReimbursementDetailPage.tsx已是高状态、高 mutation 的大型页面。继续在其中加入if (approvalMode)会把查询、权限、编辑和审批职责耦合成更难维护的巨型组件。- 项目定位决定采用方案 A:只有报销任务进入业务对象工作台的审批模式;其他 workflow 实体继续使用通用审批详情。
/workflow/tasks/:taskId与/mobile/approvals/:taskId仍是审批深链和任务身份入口;报销任务在任务元数据解析后交给报销审批适配器,不要求用户再跳到另一业务路由。- B-2a 的提交时费用标准/预算快照与当前票据事实仍是两类不同时间语义,不因页面合并而混用。
Entry Conditions
- B-2a 已部署并通过生产
/ready、公网 bundle 与合成业务验收。 - 已取得一次真人目视反馈,并据此冻结方案 A、信息收敛规则和壳/适配器边界。
- 本 PRD、
design.md与implement.md经用户复核批准后,B-2b 才可进入实施。 - 三张真实
rejected单据的员工修改与反馈可以并行推进,不是本任务启动或完成的阻塞条件;反馈如改变已批准产品行为,必须回到规划阶段修订。
Scope
- 建立 PC/移动共享内容合同的唯一交互式报销工作台;PC 与移动允许使用各自布局壳,但必须消费同一展示模型、区块顺序、语义组件和 capability 契约。
- 建立
edit、readonly、approval三类模式适配器;查询、业务状态、权限判断、mutation、错误恢复和 fail-closed 均归适配器。 /reimbursements/:reimbursementId的参与人填写态和非参与人只读态迁入工作台。/workflow/tasks/:taskId与/mobile/approvals/:taskId的报销任务进入同一工作台的审批适配器;其他实体继续走通用审批详情。- 复用 B-2a 的证据 projection、capability 契约、票据抽屉、提交前检查和未归属恢复;把嵌套的明细—票据卡片替换为费用明细与材料分区。
- 删除 PC/移动通用审批页中的报销业务分支,以及被工作台替代的旧报销明细/材料布局。
- 收敛顶部摘要、检查提示、右侧动作摘要和退回/驳回解释。
Product Requirements
R1 一个业务对象只保留一个交互式工作台
- 报销详情的主内容顺序在
edit、readonly、approval三种模式中一致:紧凑报销头 → 费用明细 → 票据与附件 → 必要检查/差异 → 处理历史或处理进度。发票归属仍是业务合同,但不再以嵌套卡片占据每条明细的主视觉。 - PC 宽屏使用主列证据 + 右侧单一 sticky 动作区;窄屏和移动采用同一内容顺序的卡片/分区详情布局,动作区转到底部且不遮挡内容。PC 与移动不强求同一份 DOM,不得用密集的
isMobile业务分支把两种布局重新耦合成巨型壳。 /workflow/tasks/:taskId保留为审批入口和浏览器地址。任务解析为reimbursement后由报销审批适配器渲染工作台;不得先渲染通用报销摘要再嵌入工作台,也不得要求用户点击“查看报销单”才能取得核心内容。/reimbursements/:reimbursementId使用同一工作台。参与人处于draft/rejected时获得编辑能力;其他获准查看者只得到只读能力。- “打开完整原始表单”不再作为独立核心内容页面入口;打印/PDF 仍通过专用打印路由提供。
R2 壳与模式适配器必须严格分层
- PC/移动布局壳只接收同一份已经计算完成的展示模型、区块内容和 capability/action slots,分别负责各端布局、响应式结构和语义标题;内容顺序和业务组件合同不得分叉。
- 工作台壳不得调用
useQuery、useMutation或 API;不得持有编辑、审批、权限、状态机、票据上传或错误恢复等业务状态;不得根据mode决定是否发请求。 - 壳内不以
roleCodes、permission code、workflow 状态、报销状态或if (mode === ...)推导业务能力。模式差异由适配器注入了什么数据、capability 和 action slot 体现。 edit适配器拥有参与人判断、draft/rejected状态、表头/明细/票据/附件维护状态和 mutation、提交前检查及 B2 错误恢复。- 任务路由边界唯一获取用于授权和实体分派的 workflow task detail,并把同一份查询结果及受控刷新入口移交给
approval适配器;不得为分派和审批内容各发一次相同 task detail 请求。 approval适配器拥有 workflow task detail 的业务解释、allowed actions、提交快照、审批 mutation、刷新策略、fail-closed、退回引用、处理历史与联网要求;不得获得报销维护 mutation。readonly适配器只执行获准的只读查询和材料查看,不注入新增、编辑、删除、关联、解绑、提交或审批能力。- 共享投影和展示组件接收显式最小字段或纯展示模型,不接收完整 Prisma/API 记录,也不在组件内部判断角色。
R3 通用审批页的报销旧分支必须删除
- PC
WorkflowTaskDetailPage中报销专用的六字段DetailField、报销证据区、提交/当前检查区、报销右栏计数和报销审批动作接线,在新工作台路由生效后全部删除。 - 移动
MobileApprovalsPage中对应的报销详情、证据、检查和报销 fail-closed UI 分支迁入审批适配器后删除。 - 通用审批页只保留请假、补卡、采购、差旅等现有实体的通用投影与动作,不保留休眠报销路径、隐藏兜底或复制文案“以防万一”。
- 最终结构检查必须证明通用审批页不再导入报销证据组件、不再包含报销检查文案,也不再根据
entityType === "reimbursement"渲染业务内容。该检查与壳层禁用 API/权限/模式判断的检查必须实现于scripts/qa/check-ui-design-rules.mjs,由部署 preflight 的--strict门禁执行,不得仅存在于单测。 - 任务类型分派层可以识别
reimbursement并选择适配器;它只负责路由,不得重新实现报销摘要或决策逻辑。
R4 信息必须按审批人决策需要收敛
- 顶部只显示报销单号、状态、报销人、日期、合计;事由作为紧接标题的正文展示。不得再用六张字段卡重复下面已经出现的信息,也不显示“业务类型=报销”等自明字段。
- 主列直接进入费用明细,随后进入独立的“票据与附件”区。PC 明细使用紧凑表格,移动明细使用卡片;表内只呈现票据数量/状态,不嵌套票据卡片或文件操作。发票与补充附件在证据区内分组,避免把两类业务语义合并成同一种数据。
- 正常项不解释、不计数。只有存在异常或人工决策需要时才显示提示,并就近说明“对象、原因、影响、下一步”;不得默认平铺“0 条”状态或仅显示无法定位对象的总数。
- 提交时快照与当前票据事实一致时,不并排展示两个“检查摘要”;只有出现异常、快照缺失或时间语义差异时才显示必要内容。
- 历史提交缺少预算快照时,以低强调度说明“该次提交未留存预算快照”;它不得使用告警视觉冒充当前业务异常,也不得用实时预算结果补写历史。
- PC 右栏只保留当前节点/状态、是否可决策、异常锚点和审批动作。报销合计如已在顶部清晰可见,不在右栏重复;创建/发起/完成等时间进入处理历史或按需展开,不平铺为决策摘要。
- “退回”和“驳回”的后果说明只在用户展开相应动作或进入确认时出现;默认右栏不常驻解释卡。错误和提交结果继续提供明确反馈,不能为了降噪删除必要恢复信息。
- 警告不得只依赖颜色;标题层级、DOM 顺序和读屏顺序必须与视觉顺序一致。
R5 编辑态围绕明细完成票据维护与提交恢复
edit适配器向同一费用明细与材料组件组注入新增、编辑、删除、上传/选择 PDF 发票、关联、解绑和未归属恢复能力。- 员工从费用明细进入单一“编辑”入口维护该条明细;删除收进编辑弹窗或明确的二次确认,不与编辑并排常驻。票据上传、关联、解绑和未归属恢复统一位于“票据与附件”区;未归属票据直接提供选择明细的恢复入口。
- 整单补充附件继续作为独立证据,不伪造与某条明细的结构化关系。
- 填写过程中持续计算可解释的提交前检查;汇总区只提示仍有多少可操作问题并提供锚点,具体错误就近落到明细或票据。
- 服务端 B2 门禁继续为权威。前端不得因工作台迁移放宽
ocr_failed、manual review、金额超票面、缺票或未归属合同。 - 发生日期不取发票日期;系统不自动决定业务是否应拆分,页面继续按既有业务事实源提示员工和财务人工复核。
R6 审批态保留快照、实时事实与 fail-closed
- 费用标准/预算使用 workflow 提交快照;票据归属、金额证据和附件使用当前实时报销详情。两者不得互相替代。
- 完整报销内容、关键当前检查或提交快照解析未就绪时,通过、退回、驳回、转审和报销离线草稿均不可执行;动作入口除禁用外仍有中文原因。
- 审批适配器只注入材料查看及 workflow 决策能力,不注入报销维护能力。明细和票据不常驻“引用到退回意见”;只有展开“退回”动作后,才在意见编辑区提供可选的明细/票据引用清单。
- 报销证据继续排除在移动 IndexedDB workflow cache 之外;其他审批类型的离线草稿能力保持不变。
- 已处理任务查看当前报销版本时保留时间语义提示;历史意见和提交快照不被当前内容覆盖。
R7 路由、权限与回退必须保持稳定
- 通用任务路由以一次已授权 task detail 查询选择报销或非报销渲染器,并把该查询结果移交所选渲染器继续使用。非参与人不能从分派错误、报销加载状态或材料错误推断无权对象信息。
- 报销详情、票据、附件、短时材料 URL 和审批动作继续使用既有后端授权、用途审计和状态机;本任务不以纯前端隐藏替代服务端校验。
- PC/移动从待办列表进入、刷新、浏览器返回、深链打开和已处理任务查看均保持可预测;工作台返回目标由入口上下文决定,不形成“返回审批详情 → 又回到同一工作台”的循环。
- 页面级加载失败显示重试和安全返回;单个材料加载失败只影响该材料,不清空已加载证据。
R8 上线后按国内报销阅读习惯完成 KISS 收敛
- PC “费用明细”使用一张表,固定核心列为序号、费用类型、发生日期、费用说明、金额、票据;票据列只显示“免票 / N 张 / 未关联 / 需核对”等短状态。正常行不显示解释文案或业务按钮,异常在对应行内用文字与图标共同标识。
- PC 表格的审批态和只读态行内操作为 0;编辑态只允许一个“编辑”入口,删除进入编辑弹窗或二次确认。不得恢复“编辑 + 删除 + 引用 + 查看原件”并排动作。
- 移动端保持卡片而不复制 PC 表格;每张卡只显示费用类型、日期、金额、说明和票据状态。正常卡无操作说明,编辑态能力从单一入口进入。
- “票据与附件”是明细之后的独立区块,内部按“发票”和“补充附件”分组。文件名/文件行本身是唯一查看入口并具有完整可访问名称;不得为每个文件重复渲染“查看原件”“查看附件”按钮。只有异常票据显示就地恢复动作。
- 退回引用采用渐进披露:默认页面不显示引用按钮;用户展开“退回”后,意见框附近显示可勾选的异常明细/票据,必要时可展开全部,选择后生成可编辑的引用文本。关闭退回区不改变业务数据。
- “查看完整材料”只在确有批量核对价值时保留为区块级次要入口,不与每个文件的查看入口重复;无材料时不显示空动作。
- 本轮只重排前端展示与能力入口,不改变发票—明细关系、B2 门禁、快照/实时语义、材料授权审计、workflow 状态机或后端数据合同。
Acceptance Criteria
- AC1:PC 与移动的
edit、readonly、approval使用同一展示模型、区块顺序、费用明细/材料语义组件和 capability 契约;PC 可使用右栏 sticky 布局壳,移动可使用卡片 + 底部动作布局壳,不要求同一份 DOM,模式差异只来自适配器注入的数据与能力。 - AC2:PC/移动布局壳中
useQuery、useMutation、API 调用、角色/权限判断、workflow/报销状态机判断和if (mode === ...)业务分支均为 0;scripts/qa/check-ui-design-rules.mjs --strict静态扫描并报 error,且该命令作为部署 preflight 执行。 - AC3:
WorkflowTaskDetailPage与MobileApprovalsPage的报销业务渲染分支、六字段摘要、重复检查区和休眠兜底全部删除;通用审批页仍通过非报销回归。 - AC4:报销审批首屏只有一套紧凑报销头;事由、明细、所属票据、附件、异常和历史各有唯一主展示位置,不再出现两套摘要或四处检查计数。
- AC5:无异常时不显示“0 条”检查卡或常驻退回/驳回解释;异常时每条提示可定位到具体明细/票据,包含原因、影响和下一步。快照/实时不一致时才额外显示差异。
- AC6:PC 右栏只含决策所需状态、异常锚点和单一动作区;窄屏/移动动作区不遮挡内容,触控区域不小于 44×44,键盘和读屏顺序正确。
- AC7:员工可在明细上下文完成新增/编辑/删除、PDF 发票添加、关联/解绑、未归属恢复、提交阻断修复和再次提交;服务端 B2 结果仍为最终权威。
- AC8:审批内容或快照失败时所有报销决策 fail-closed;审批适配器没有任何报销维护能力;readonly 适配器没有编辑或审批能力。
- AC9:报销证据不进入 IndexedDB;非报销审批离线草稿基线、PC/移动非报销详情和 workflow 状态机均无回归。
- AC10:参与人/非参与人、
draft/rejected/reviewing、pending/已处理任务和材料权限矩阵通过;没有新增 schema、migration、flag、seed 或生产数据改写。 - AC11:生产部署后使用公网真实 bundle + 合成 API 完成三模式、失败关闭和离线验收;真实员工重提反馈记录后,父任务才允许归档。
- AC12:PC 审批/只读费用明细为单表且行内业务按钮为 0;编辑态每行至多一个“编辑”入口。表格在 1280px 无横向溢出,键盘与读屏按列顺序读取。
- AC13:移动费用明细保持卡片;PC/移动共享同一明细字段、顺序、异常语义与 capability 合同,不共享同一 DOM。375px 与横屏无内容遮挡或横向溢出。
- AC14:票据和补充附件从明细中拆出并在独立证据区分组;正常文件仅有一个可访问的查看入口,正常明细/票据不出现“引用到退回意见”“查看原件”“查看附件”按钮文案。
- AC15:只有展开退回动作后才出现引用选择;异常对象优先、全部对象按需展开,选择结果进入可编辑意见。B2 未归属恢复、金额异常锚点、材料查看审计和审批 fail-closed 回归通过。
Out of Scope
- 请假、补卡、采购、差旅改造成“业务对象页 + 审批模式”;它们继续使用通用审批详情。
/reimbursements/:id/print打印/PDF 页面重构。该页面是格式化交付视图,不是交互式工作台;本任务有意保留其独立布局,但不得把它当作编辑/只读/审批的第四套交互实现,也不得向其中复制工作台 mutation。- 自动从发票推断发生日期、自动判断业务拆分正确性或替财务作真实性结论。
- 结构化退回原因、统计模型、财务代员工编辑、批量改票或新审批状态。
- 修改 workflow 状态机、B2 flag、付款流程、数据库 schema、生产配置或真人单据。
- 为当前冗余页面单独发布临时删减版;信息收敛随 B-2b 独立候选一次交付。
Test Plan
- 结构边界:
check-ui-design-rules.mjs --strict扫描 PC/移动布局壳,禁止 query/mutation/API/角色/状态机/mode 请求分支,并扫描通用审批页报销分支和旧文案为 0;三适配器能力集合另做组件/类型验证。 - 纯函数/组件:统一报销头、明细行/材料投影、异常过滤、快照/实时差异、异常锚点和 capability 注入覆盖正常/异常/缺失边界。
- PC/移动 Playwright:三模式内容顺序;PC 明细表/移动明细卡;正常行零动作与零解释;独立票据/附件区;文件单一查看入口;异常就近文案;右栏/底部动作;票据抽屉;未归属恢复;B2 阻断后修复并再次提交;退回动作展开后引用;深链/返回。
- 权限/状态:participant/nonparticipant × draft/rejected/reviewing;pending/已处理审批;无权 task、跨单材料、无维护能力和并发关联回归。
- 离线:直接读取 IndexedDB 禁存字段/哨兵值;报销动作和草稿 fail-closed;非报销审批离线草稿仍可形成和同步。
- 后端:原则上不改合同;复跑 B2 门禁、报销详情只读投影、workflow task detail、材料查看审计、票据 bind/unbind/link 和审批动作聚焦测试。
- 前端/治理:typecheck、build、UI strict/self-test、route manifest、相关 Playwright、process contract、Trellis governance、敏感内容检查、docs registry(如改事实源)和
git diff --check。 - 部署:另行批准独立候选;切换前 UI strict;部署后核
/ready、bundle marker、公网文案、三模式合成路径、B2 状态和真人单据状态不变。
Risks and Deferred Items
- 大型旧页面拆分容易在中间阶段形成双实现;允许在开发分支短暂并存,但最终候选必须通过旧分支为 0 的结构门禁。
- task 路由先取元数据再分派可能产生加载闪烁;设计必须用稳定骨架而不是先渲染通用页再切换。
- 打印页继续独立,未来如字段语义漂移可另建“打印投影复用”任务;本轮不扩大交付面。
- 神州 ERP 仅作为国内报销信息层级参考;不复制其旧页面状态管理、过小图标按钮、打印专用边框或数据模型。当前系统继续以自身 B2、权限、审计和可访问性合同为准。
- 全局错误色 token、部署脚本
--no-deps、.claudegitignore 和其他治理债不并入本任务。