7.8 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
Authorized task/reimbursement route dispatch
-> edit / readonly / approval adapters
-> one stateless reimbursement workspace shell
+ same shared item/evidence component
+ invoice drawer + submit preflight
- generic workflow reimbursement branches
- retired legacy item/material duplication
共享边界在 B-2a 先选为“明细 + 挂在明细下的票据”;真人目视反馈后,B-2b 把它上升为唯一交互式报销工作台。旧 ReimbursementDetailPage 的查询、mutation、权限判断和编辑状态进入 edit adapter,workflow task、审批 mutation 与 fail-closed 进入 approval adapter,只读查询进入 readonly adapter;工作台壳只负责编排已经计算好的展示模型和 capability/action slots。
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[];
onOpenInvoice: (invoiceId: string) => void;
onReferenceReturn?: (reference: ReturnReference) => void;
editCapabilities?: ReimbursementEvidenceEditCapabilities;
};
分组、文案和检查映射由纯函数完成。B-2a 页面容器暂时负责查询、mutation、导航和动作面板;B-2b 将这些职责迁入三种适配器,唯一工作台壳不得拥有 query/mutation/角色/状态机判断。editCapabilities 在 B-2a 不启用,B-2b 只由 edit adapter 接入,避免审批/只读视角出现维护分支。
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 接入三种适配器后删除被替代布局,并删除 PC/移动通用审批页的报销业务分支;不长期维护双实现或运行时 feature flag。
/reimbursements/:id/print保持专用打印布局,不纳入三模式交互壳,只做不误伤回归。- 旧 workflow 无预算快照兼容为 null。
- 当前已处理任务展示实时内容时加版本提示,避免历史误读。
9. UI/UX Review Notes
ui-ux-pro-max 的自动 design-system 搜索把本页误配为 FAQ/夸张极简风,未采用该视觉结果;本任务保留项目现有 OA 设计系统,仅采用其适用的通用规则:错误就近、恢复动作明确、语义 HTML、焦点管理、移动触控区域不小于 44px、警告不只靠颜色、sticky 区不遮挡内容。
布局本身不构成新视觉裁决,而是落实既有 docs/产品设计规则.md:73 与归档统一页面评审:证据居中、历史靠后、PC 右侧或移动底部单一动作面板、多个审批动作不得竖向铺卡片。