日期: 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)
在【运输管理】下新增菜单【运输计划归档】,提供与【运输计划】相同结构的列表与检索;操作列仅 查看 / 编辑 / 归档。归档通过专用弹窗一次性完成:填写产品实际数量 → 调整运费 → 执行归档。原【运输计划】页移除全部归档入口,归档职责集中到本菜单。
| # | 决策 | 说明 |
|---|---|---|
| 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 相同;可额外增加「归档状态」筛选项(展开区) |
draft/processing/completed/cancelled 简化枚举;归档时将 follow_status 置为 completed)openspec/changes/ 条目)| 项 | 值 |
|---|---|
| 菜单名 | 运输计划归档 |
| 路由 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(危化品证上传顺延) |
| 权限码 | 用途 |
|---|---|
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})。
planApi.list,默认不传 archiveStatus(全部)。views/pl/plan/index.vue 一致(计划编号、日期、类型、客户、产品、证书、跟进/运输状态、金额、车牌、司机、跟进人等)。min-width 约 260(3 个 link 按钮),fixed="right"。| 条件 | 查看 | 编辑 | 归档 / 调整 |
|---|---|---|---|
followStatus !== 'cancelled' |
✅ | ✅ | ✅(未归档文案「归档」,已归档文案「调整」) |
followStatus === 'cancelled' |
✅ | ❌ | ❌ |
| 按钮 | 规则 |
|---|---|
| 已选 N 项 | 保留 |
| 批量归档 | 保留;仅对选中行中未归档且未作废的执行;已归档跳过并汇总提示 |
| 新增 / 删除 / 证书 / 安排 | 不出现 |
| 文件 | 改动 |
|---|---|
views/pl/plan/index.vue |
删除「批量归档」按钮及 handleBatchArchive;删除 FolderChecked 导入(若无他用) |
views/pl/plan/detail.vue |
删除「归档」按钮及 handleArchive |
PlPlanArchiveDialog)┌──────────────────────────────────────────────────────────────┐
│ 运输计划归档 · {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。
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_pricedirect:仅写 actual_qty,重算 tax_subtotal 若业务需要(按 quantity 单价比例,或保持计划金额不变 — 本期以 actual_qty 落库为主,金额字段按 inbound/outbound 规则)复用 PlPlanArrangeDialog 运费区块 + usePlFreightCalc:
| 字段 | 规则 |
|---|---|
hasFreight |
来自计划,只读 |
distanceKm |
可编辑;预填 transport / plan |
freightType |
by_weight / by_trip |
| 运输量 | by_weight 时默认 actualQty 汇总(sumProductActualQuantities) |
freightPrice / freightAmount |
可编辑;联动计算可手动覆盖 |
hasInvoice |
可编辑 |
hasFreight=0 时显示「本计划无运输费用」,跳过运费写入。
PlPlanViewTabs(embedded),含:
tabsRef.reload() 刷新下区。followStatus !== 'cancelled' 均可打开(含已归档)。planApi.get + 若已安排则读 transport 运费预填;已归档时进入「调整」模式。方案:扩展 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
}
}
处理顺序(单事务):
follow_status != cancelledproducts[].id 属于该计划pl_plan_product.actual_qty;按 plan_type 写 inbound_qty/outbound_qty 及金额plan.has_freight == 1 且 body.freight 存在:
plan.transport_id → 更新 pl_transport 运费字段 + transport_qtyplan.freight_amountpl_freight_settlement 关联)archive_status != archived:置 archive_status=archived、complete_status=yes、follow_status=completedpl_plan_change:change_type=archive(首次)或 archive_adjust(已归档调整)update_plan(编辑弹窗):移除 archive_status == archived 时的 400 阻断;已归档计划允许通过 PUT 修改计划主表/产品(与归档弹窗互补:表单改计划信息,归档弹窗改 actualQty/运费)。
Response:PlanWithProducts(与 get 一致)
文件:backend/app/schemas/pl/plan.py
PlanArchiveProductItem:id, actualQtyPlanArchiveFreightBlock:与 transport arrange 运费字段子集一致PlanArchiveBody:products, freight?在 pl_plan_service.py 新增:
apply_archive_product_quantities(plan, products_payload) — 写 actual + inbound/outboundapply_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 业务函数 |
| 组件 | 用途 |
|---|---|
PlPlanViewDialog |
查看 |
PlPlanFormDialog |
编辑 |
PlPlanViewTabs |
归档弹窗下区 |
PlPlanFormProductTable |
归档弹窗 actualQty(可增 prop actualQtyEditableOnly) |
usePlFreightCalc |
归档弹窗运费 |
-- 菜单(示例 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 |