文件
hyb-oa/.trellis/tasks/09-01-reimbursement-one-page-approval-workbench/design.md
T

7.2 KiB

Design:报销审批一页式工作台

1. Architecture

父任务采用两阶段迁移,不做全页面一次性重写:

B-2a
Workflow task shell
  + submission snapshot adapter
  + live reimbursement detail query
  + shared item/evidence component (review mode)
  + approval decision panel

B-2b
Existing reimbursement form shell
  + same shared item/evidence component (edit mode)
  + invoice drawer
  + submit preflight summary
  - retired legacy item/material duplication

共享边界选在“明细 + 挂在明细下的票据”,而不是复用整个旧 ReimbursementDetailPage。旧页面混有填写 mutation、权限判断、材料上传、处理进度和操作面板,直接嵌入审批页会把报销人维护能力与审批职责再次耦合。

2. Data Authority Matrix

信息 权威来源 时间语义 展示标签
报销头、明细、票据归属、附件 GET /reimbursements/:id 当前事实 当前报销内容
票面金额证据/人工核对 报销详情返回的 amount evidence 当前事实 当前票据检查
费用标准超标 workflowInstance.contextData.standardViolations 提交冻结 提交时检查
预算提示 新增最小 contextData.budgetCheck 提交冻结 提交时预算
审批任务/历史 workflow task/history 流程历史 处理历史
办公电子设备提示 明细类型 + 既有固定阈值纯函数 当前确定性规则 选类提示

不从审批页调用实时 precheck 替代快照。历史 context 缺 budgetCheck 时返回 null 并明确显示缺失,不做当前预算回填。

3. Shared Component Contract

建议建立面向展示模型的共享组件,而不是传整个 workflow 对象:

type ReimbursementItemEvidenceModel = {
  item: ReimbursementItem;
  invoices: ReimbursementInvoiceEvidence[];
  snapshotChecks: SubmissionCheck[];
  liveChecks: LiveEvidenceCheck[];
};

type ReimbursementItemEvidenceListProps = {
  models: ReimbursementItemEvidenceModel[];
  unlinkedInvoices: ReimbursementInvoiceEvidence[];
  mode: "review" | "edit" | "readonly";
  onOpenInvoice: (invoiceId: string) => void;
  onReferenceReturn?: (reference: ReturnReference) => void;
  editCapabilities?: ReimbursementEvidenceEditCapabilities;
};

分组、文案和检查映射由纯函数完成,页面容器只负责查询、mutation、导航和动作面板。editCapabilities 在 B-2a 不启用,B-2b 再接入,避免审批页出现维护分支。

4. Responsive Page Structure

PC

审批详情:报销单号 / 状态 / 当前节点
┌──────────────────────────────────────┬────────────────────┐
│ 报销摘要与事由                       │ 合计               │
│ 明细 1                               │ 当前节点/状态      │
│   ├─ 费用/日期/金额/说明             │ 合并检查摘要       │
│   └─ 发票 A / 发票 B / 原件入口      │ 审批意见           │
│ 明细 2 ...                           │ [通过]             │
│ 尚未对应明细的票据                   │ [退回][驳回][更多] │
│ 补充附件                             │ (sticky)           │
│ 处理历史                             │                    │
└──────────────────────────────────────┴────────────────────┘

右栏只放状态摘要和决策动作,不承载完整审批流。提交快照与当前票据检查默认合并为一块,只有二者不一致时在同一块内显示差异。动作使用一个主按钮、少量横向紧凑次按钮和“更多”,禁止竖向动作卡片。PC 主列底部不复制按钮;低于两栏断点时,同一个动作面板才进入内容底部。

Mobile

状态/单号
报销摘要
明细 1 → 所属票据
明细 2 → 所属票据
未归属票据
补充附件
提交时检查
当前票据检查
处理历史
底部:[通过] [更多]

移动“更多”承载退回、驳回和转审;理由输入与确认保持语义化焦点管理。内容底部预留 safe-area 和固定操作栏高度。

审批页与填写页的动作规则有意不同:审批是只读核验后的一次决策,保持单一固定入口;填写是长表单,B-2b 可基于填写动线另行确认是否需要表单末尾提交入口。这不改变共享业务内容组件。

5. Snapshot Contract

报销提交已经形成 budgetCheck,B-2a 只把其最小、无敏感字段的值同时写入 workflow context_data:预算标识/代码、控制模式、使用比例、是否达到阈值、是否超预算。workflow task detail 对外投影显式字段,不原样返回任意 context JSON。

standardViolations 继续沿用当前快照。两者必须和创建 workflow instance 同一事务写入;退回、规则变更或预算后续变化不改写旧实例。

6. Live Detail Loading and Permissions

B-2a 客户端在 workflow task 已授权后读取现有报销详情 API,复用既有只读投影、材料查看和审计合同,不复制 server select。该双查询把在线证据与 workflow 离线摘要分开,但不能只依赖 DTO 当前形状保证安全;cacheWorkflowTaskDetail 仍必须改为显式允许字段投影,防止未来 workflow DTO 扩展后把证据自动落盘。

若 workflow task 可见但报销详情因既有对象权限失败,页面 fail closed 并记录为权限合同缺口;本任务不得只在前端绕过。实施前用角色/委托矩阵证明当前审批人、财务和已处理查看者的读取基线,发现缺口时只设计“任务关联对象只读”最小授权,不授予 expense.view_all 写能力。

7. Return Reference Without Schema Change

组件生成无 UUID 的可读引用,例如“住宿费|2026-08-25|¥680.00”或“发票|2026-08-25|¥680.00”。点击后把引用加入审批页本地待退回清单,退回确认时合成为普通 comment 文本。删除引用只改本地状态;服务端接口和数据库不变。

8. Compatibility and Migration

  • B-2a 保留完整原始表单入口作为降级路径;核心核验不得依赖它。
  • B-2a 不删除旧填写组件,只新增共享组件的 review/readonly 能力。
  • B-2b 接入 edit 能力后删除被替代布局;不长期维护双实现。
  • 旧 workflow 无预算快照兼容为 null。
  • 当前已处理任务展示实时内容时加版本提示,避免历史误读。

9. UI/UX Review Notes

ui-ux-pro-max 的自动 design-system 搜索把本页误配为 FAQ/夸张极简风,未采用该视觉结果;本任务保留项目现有 OA 设计系统,仅采用其适用的通用规则:错误就近、恢复动作明确、语义 HTML、焦点管理、移动触控区域不小于 44px、警告不只靠颜色、sticky 区不遮挡内容。

布局本身不构成新视觉裁决,而是落实既有 docs/产品设计规则.md:73 与归档统一页面评审:证据居中、历史靠后、PC 右侧或移动底部单一动作面板、多个审批动作不得竖向铺卡片。