3.5 KiB
3.5 KiB
设计说明
1. 架构边界
本任务只迁移采购、资产已有详情态。列表数据、详情 API、mutation、权限和表单字段保持原合同,不为路由化新增后端接口或数据库字段。
目标路由:
| 模块 | 列表 | 详情 |
|---|---|---|
| 采购 | /purchase-requests |
/purchase-requests/:purchaseRequestId |
| 资产 | /assets |
/assets/:assetId |
2. 页面拆分
PurchaseRequestsPage与AssetRegistryPage收敛为纯列表页,只持有筛选、分页、列表 query、创建入口和行级“查看详情”。- 新建
PurchaseRequestDetailPage与AssetDetailPage,承接现有详情 query、表单 hydration、附件/动作、右侧状态动作和 dirty 状态。 - 先提取页面内纯函数、类型和可复用 section,避免复制大段表单逻辑;不为了抽象而建立通用“万能详情页”。
- 创建行为保持现有 API 合同:创建成功后导航到新详情路由;本任务不新增独立新建 URL。
3. 导航与返回
- 列表进入详情时通过 React Router history push,并在 location state 保存安全的当前列表 URL;浏览器后退自然恢复原筛选和滚动历史。
- 详情显式“返回”优先使用经过校验的同模块
state.from,无来源或刷新后回退到模块列表。 - 项目关联、全局搜索、审批/通知等入口直接生成新详情 URL;不允许把任意外部字符串作为 return URL。
features/navigation/schema.ts添加采购详情、资产详情元数据,并确保长路径优先匹配。
4. 旧链接兼容
/purchase-requests?id=<uuid>与/assets?id=<uuid>在列表路由入口执行 replace 重定向到新详情路径。- 重定向移除
id,保留其余无关查询参数;无效或空 ID 不重定向,列表正常展示。 - 仓库内消费者全部改为新 URL,旧格式只作为外部收藏、历史通知和人工 witness 的兼容层。
5. Dirty guard 与错误态
- 详情页面继续注册
useUnsavedChangesGuard;返回按钮、浏览器后退、跨模块链接和直接路由离开都由 provider 阻断。 - 用户取消离开时保留当前 ID 和所有输入;确认后才执行导航。
- 服务端 403/404 使用普通中文错误态。为避免资源枚举,详情页不区分“无权限”与“记录不存在”的敏感摘要,只提供安全返回列表入口。
- 列表路由不发详情 query;详情路由不为构造页面偷偷加载完整列表。
6. 权限与兼容
- 新动态路由复制对应列表路由的
RequirePermissionguard;页面内现有细粒度动作权限继续保留。 - API endpoint、请求 payload、审计、高风险确认、附件下载原因和状态矩阵均不改变。
- 移动采购现有
/mobile/purchase-requests/:id不受影响;本任务不新增移动资产详情路由。
7. 测试策略
- 为两个模块建立同构的路由合同测试:列表纯净、进入详情、直接深链、刷新、返回恢复、旧链 replace、无效/无权限、dirty 取消/确认。
- 更新现有
procurement-flow.spec.ts、assets-flow.spec.ts、r103-cross-page-ui-consistency.spec.ts、pc-detail-mobile-visual-wave3.spec.ts和人工 witness 中的旧链接。 - 测试应使用显式 route tag/URL 断言,不再用父列表路径字符串误认动态详情证据。
8. 回滚
- 新路由、页面拆分、链接消费者和兼容 redirect 同一任务提交;回滚时整体恢复
?id=,避免一半消费者指向不存在的路由。 - 无 schema、API 或数据迁移。应用回滚不需要处理数据库。