Files
shanxia2.0/docs/superpowers/specs/2026-06-25-pl-plan-archive-design.md
T
2026-06-25 08:06:32 +08:00

15 KiB
Raw Blame History

运输计划归档 — 设计文档

日期: 2026-06-25
状态: 待审阅
前置: docs/运输计划管理-需求说明文档.md、docs/superpowers/specs/2026-06-21-pl-arrange-transport-refine-design.md、openspec/changes/transport-plan-management/specs/transport-plan/spec.md
界面模式: P06 复杂业务列表 + 弹窗表单;归档弹窗上区编辑、下区只读对照(对齐 PlPlanArrangeDialog)

目标

在【运输管理】下新增菜单【运输计划归档】,提供与【运输计划】相同结构的列表与检索;操作列仅 查看 / 编辑 / 归档。归档通过专用弹窗一次性完成:填写产品实际数量 → 调整运费 → 执行归档。原【运输计划】页移除全部归档入口,归档职责集中到本菜单。


已确认决策(2026-06-25)

# 决策 说明
D1 列表展示全部计划 默认不过滤 archiveStatus;已归档计划仍保留在列表中作为历史可查
D2 已归档也可调整 已归档行仍显示「查看 / 编辑 / 调整」;「归档」按钮在已归档时文案改为「调整」,打开同一弹窗修改 actualQty 与运费
D2a 归档页编辑不受归档态限制 本菜单内 PlPlanFormDialog 对已归档计划可打开;后端 update_plan 移除「已归档不可修改」阻断(运输计划页仍通过 UI 隐藏已归档行的编辑入口)
D3 原页移除归档入口 views/pl/plan/index.vue 去掉「批量归档」;views/pl/plan/detail.vue 去掉「归档」按钮;归档只在新区操作
D4 弹窗布局对齐安排运输 上区:归档表单;下区:完整 PlPlanViewTabs(计划概况 / 运输信息含司机·现场链接 / 合同信息)
D5 归档 API 增强 单次请求原子完成:写 actual_qty + 同步入库/出库数与金额 + 更新运费 + 改归档/完成状态
D6 列表/筛选与运输计划一致 列、筛选字段、分页行为与 /#/pl/plan 相同;可额外增加「归档状态」筛选项(展开区)

非目标(本期不做)

  • 跟进状态 12 态完整流转(仍用现有 draft/processing/completed/cancelled 简化枚举;归档时将 follow_status 置为 completed)
  • 归档后允许反归档 / 撤销
  • 运费结算单已生成后的运费修订流程(见约束:有关联结算且非草稿则阻断改运费)
  • OpenSpec 独立 change 归档(可在实现后补 openspec/changes/ 条目)

一、页面与路由

1.1 新菜单

项 值
菜单名 运输计划归档
路由 path plan-archive
Hash /#/pl/plan-archive
组件 web-admin/src/views/pl/plan-archive/index.vue
列表标题 运输计划归档(写入 App.vue → panelTitleBarTextRules)
menuCode pl:plan-archive:list
排序 紧接「运输计划」之后,order_num=10.5 或 11(危化品证上传顺延)

1.2 权限按钮

权限码 用途
pl:plan-archive:list 菜单 + 列表
pl:plan-archive:query 查看弹窗
pl:plan-archive:edit 编辑弹窗(含已归档)
pl:plan-archive:archive 打开归档/调整弹窗 + 提交(含已归档调整)

编辑可复用现有 PlPlanFormDialog + pl:plan:edit 或映射到 pl:plan-archive:edit(实现时二选一,推荐归档页独立权限,后端 update 仍走 /transport/plan/{id})。

1.3 列表行为(D1 / D2)

  • 数据范围:调用同一 planApi.list,默认不传 archiveStatus(全部)。
  • 筛选:与运输计划相同 6 项;展开区增加「归档状态」(未归档 / 已归档 / 全部)。
  • 表格列:与 views/pl/plan/index.vue 一致(计划编号、日期、类型、客户、产品、证书、跟进/运输状态、金额、车牌、司机、跟进人等)。
  • 操作列 min-width 约 260(3 个 link 按钮),fixed="right"。
条件 查看 编辑 归档 / 调整
followStatus !== 'cancelled' ✅ ✅ ✅(未归档文案「归档」,已归档文案「调整」)
followStatus === 'cancelled' ✅ ❌ ❌

1.4 工具栏

按钮 规则
已选 N 项 保留
批量归档 保留;仅对选中行中未归档且未作废的执行;已归档跳过并汇总提示
新增 / 删除 / 证书 / 安排 不出现

1.5 原运输计划页改动(D3)

文件 改动
views/pl/plan/index.vue 删除「批量归档」按钮及 handleBatchArchive;删除 FolderChecked 导入(若无他用)
views/pl/plan/detail.vue 删除「归档」按钮及 handleArchive

二、归档弹窗(PlPlanArchiveDialog)

2.1 布局

┌──────────────────────────────────────────────────────────────┐
│ 运输计划归档 · {planNo}                                 [×]   │
├──────────────────────────────────────────────────────────────┤
│ ▲ 上区:归档操作(max-height ~42vh,可滚动)                  │
│   [产品实际数量]  PlPlanFormProductTable                      │
│                   showActualQty + editableQty(仅 actualQty) │
│   [运费信息]      复用安排运输区块(只读 hasFreight 来自计划)│
│   [归档说明]      可选 remark(写入 plan.remark 或仅展示)   │
├──────────────────────────────────────────────────────────────┤
│ ▼ 下区:计划单查看(flex:1,min-height 280px)               │
│   PlPlanViewTabs embedded — 三 Tab 与查看/安排弹窗一致        │
└──────────────────────────────────────────────────────────────┘
│ 取消 │ 确认归档 / 确认保存 │

模式切换:

模式 标题后缀 主按钮 提交语义
首次归档 运输计划归档 · {planNo} 确认归档 写数量/运费 + 置 archived
已归档调整 调整归档内容 · {planNo} 确认保存 仅写数量/运费,不改变 archive_status / complete_status

尺寸与规范:dialog-form-layout、align-center、width: min(1680px, calc(100vw - 80px))、modal-class: order-module-dialog-mask、backdrop-filter: none、表单 label-position="top";底栏 Close / Check 图标间距 6px。

2.2 上区 — 产品实际数量

  • 使用 PlPlanFormProductTable:show-actual-qty、editable-qty;计划数量列只读(不传 editable 到 quantity 或单独 prop editableActualQtyOnly)。
  • 初始值:actualQty ?? quantity ?? inboundEstimateQty ?? outboundEstimateQty ?? 0(与 usePlPlanViewDetail 一致)。
  • 校验:每行 actualQty >= 0 必填;至少一行有有效数量。
  • 提交后后端按 plan_type 同步:
    • inbound:inbound_qty = actual_qty,inbound_amount = inbound_qty × inbound_price(价缺省用 tax_price)
    • outbound:outbound_qty = actual_qty,outbound_amount = outbound_qty × outbound_price
    • direct:仅写 actual_qty,重算 tax_subtotal 若业务需要(按 quantity 单价比例,或保持计划金额不变 — 本期以 actual_qty 落库为主,金额字段按 inbound/outbound 规则)

2.3 上区 — 运费信息

复用 PlPlanArrangeDialog 运费区块 + usePlFreightCalc:

字段 规则
hasFreight 来自计划,只读
distanceKm 可编辑;预填 transport / plan
freightType by_weight / by_trip
运输量 by_weight 时默认 actualQty 汇总(sumProductActualQuantities)
freightPrice / freightAmount 可编辑;联动计算可手动覆盖
hasInvoice 可编辑

hasFreight=0 时显示「本计划无运输费用」,跳过运费写入。

2.4 下区 — 只读对照

  • 完整嵌入 PlPlanViewTabs(embedded),含:
    • 计划概况(含产品 actualQty 展示列)
    • 运输信息(车辆、物流、运费、司机链接复制、现场链接复制、回传、地磅)
    • 合同信息
  • 提交成功后 tabsRef.reload() 刷新下区。

2.5 入口与禁用

  • followStatus !== 'cancelled' 均可打开(含已归档)。
  • 打开时 planApi.get + 若已安排则读 transport 运费预填;已归档时进入「调整」模式。

三、后端 API

3.1 增强归档接口

方案:扩展 POST /transport/plan/{id}/archive,请求体可选;无 body 时保持向后兼容(仅改状态,供脚本/旧调用),前端归档弹窗必须带 body。

Request PlanArchiveBody:

{
  "products": [
    { "id": "计划产品明细ID", "actualQty": 4.7 }
  ],
  "freight": {
    "distanceKm": 120.5,
    "freightType": "by_weight",
    "freightPrice": 50,
    "freightAmount": 235,
    "hasInvoice": 1
  }
}

处理顺序(单事务):

  1. 校验:计划存在、租户、follow_status != cancelled
  2. 校验:每个 products[].id 属于该计划
  3. 更新 pl_plan_product.actual_qty;按 plan_type 写 inbound_qty/outbound_qty 及金额
  4. 若 plan.has_freight == 1 且 body.freight 存在:
    • 若存在 plan.transport_id → 更新 pl_transport 运费字段 + transport_qty
    • 更新 plan.freight_amount
    • 若运输单已纳入非草稿运费结算 → 400 阻断(查 pl_freight_settlement 关联)
  5. 状态分支:
    • 若 archive_status != archived:置 archive_status=archived、complete_status=yes、follow_status=completed
    • 若 已归档:跳过状态字段,仅更新业务数据(D2)
  6. 写 pl_plan_change:change_type=archive(首次)或 archive_adjust(已归档调整)
  7. commit

update_plan(编辑弹窗):移除 archive_status == archived 时的 400 阻断;已归档计划允许通过 PUT 修改计划主表/产品(与归档弹窗互补:表单改计划信息,归档弹窗改 actualQty/运费)。

Response:PlanWithProducts(与 get 一致)

3.2 Schema 新增

文件:backend/app/schemas/pl/plan.py

  • PlanArchiveProductItem:id, actualQty
  • PlanArchiveFreightBlock:与 transport arrange 运费字段子集一致
  • PlanArchiveBody:products, freight?

3.3 Service 抽取

在 pl_plan_service.py 新增:

  • apply_archive_product_quantities(plan, products_payload) — 写 actual + inbound/outbound
  • apply_archive_freight(plan, transport, freight_payload, products) — 运费同步

便于单测与 archive_plan 路由保持薄。


四、前端文件清单

文件 动作
views/pl/plan-archive/index.vue 新建 — 列表页
components/business/pl-plan-archive-dialog/index.vue 新建 — 归档弹窗
composables/usePlPlanList.ts 新建(推荐) — 从 plan/index 抽取 list/search/pagination
views/pl/plan/index.vue 删除批量归档;可选改用 composable
views/pl/plan/detail.vue 删除归档按钮
router/modules/pl.ts 新增 plan-archive 路由
api/pl/index.ts planApi.archive(id, body) 扩展
utils/router/menu-icon.ts pl:plan-archive:list 图标
App.vue panelTitleBarTextRules 增加「运输计划归档」
docs/sql/shanxia2.0.sql sys_menu 插入归档菜单 + 按钮权限
backend/app/api/pl/plan.py 增强 archive_plan;update_plan 允许已归档编辑
backend/app/schemas/pl/plan.py Archive body schema
backend/app/services/pl_plan_service.py archive 业务函数

4.1 组件复用策略

组件 用途
PlPlanViewDialog 查看
PlPlanFormDialog 编辑
PlPlanViewTabs 归档弹窗下区
PlPlanFormProductTable 归档弹窗 actualQty(可增 prop actualQtyEditableOnly)
usePlFreightCalc 归档弹窗运费

五、数据库与菜单 SQL

-- 菜单(示例 ID,实现时按现有 PL_* 规范)
INSERT INTO sys_menu (...) VALUES
('PL_PLAN_ARCHIVE', '运输计划归档', 'PL_MENU_ROOT', 'C', 'plan-archive', 'pl/plan-archive/index', 'pl:plan-archive:list', ...);

-- 按钮权限(F 类型,parent=PL_PLAN_ARCHIVE)
-- pl:plan-archive:query | edit | archive

六、安全与约束

项 规则
已归档 可继续「编辑」与「调整」actualQty/运费;不可反归档(archive_status 不回退为 unarchived)
已作废 不可归档 / 不可调整
运费结算 运输单若已在「已提交/审核通过/已确认」结算单中,禁止归档时改运费
无 transport has_freight=1 但无 transport_id 时,仅更新 plan.freight_amount,并 warn 日志(或 400 要求先安排运输 — 推荐 400:有运费必须先安排运输)
租户 所有读写 tenant_no
事务 产品 + 运费 + 归档同一 commit

七、验收标准

# 场景 预期
V1 打开归档列表 展示全部计划;筛选与运输计划一致;多「归档状态」筛选项
V2 未归档行 查看 / 编辑 / 归档 均可用
V3 已归档行 查看 / 编辑 / 「调整」均可用
V4 打开归档/调整弹窗 上区 actualQty、运费可编辑;下区三 Tab 完整
V5 首次提交归档 actual_qty、运费、archive/complete 状态一次成功
V6 已归档后再次「调整」 仅更新 actual_qty/运费/金额;archive_status 保持 archived
V6a 已归档「编辑」 PlPlanFormDialog 可保存计划主表/产品变更
V7 原运输计划页 无批量归档、详情无归档按钮
V8 批量归档 仅处理未归档行;已归档跳过有提示
V9 有运费未安排运输 归档 400,提示先安排运输
V10 按重量运费 运输量默认 actualQty 合计,改 actualQty 后运费量联动
V11 下区链接 司机/现场链接可复制(与安排运输下区一致)
V12 权限 无 pl:plan-archive:archive 不显示归档按钮

八、实施分期

Phase 内容
P1 后端:PlanArchiveBody + service + 增强 archive_plan
P2 前端:PlPlanArchiveDialog + plan-archive/index.vue + 路由/菜单/API
P3 原页移除归档入口;抽取 usePlPlanList(可选)
P4 联调验收 V1–V12

九、参考路径

说明 路径
运输计划列表 web-admin/src/views/pl/plan/index.vue
安排运输弹窗(布局基准) web-admin/src/components/business/pl-plan-arrange-dialog/index.vue
下区三 Tab web-admin/src/components/business/pl-plan-view-tabs/index.vue
产品表 web-admin/src/components/business/pl-plan-form-dialog/PlPlanFormProductTable.vue
运费计算 web-admin/src/composables/usePlFreightCalc.ts
现归档 API backend/app/api/pl/plan.py → archive_plan
需求 BR-15/32/42 docs/运输计划管理-需求说明文档.md