Demo:
docs/fresh-mint-pl-plan-contract-complete-demo.html(方案 B 为采纳交互)
日期:2026-08-20
状态:已实现(待联调验收)
运输计划新增/编辑时,供需双方合同已改为非必填,便于先建计划再补合同。业务上:非内置公司一侧仍应关联合同;内置公司该侧合同可免。
内置公司(合同可免):
判定逻辑复用现有工具:
web-admin/src/utils/pl-internal-customer.ts → isPlInternalCustomerNamebackend/app/services/pl_internal_customer.py → is_internal_customer_name目标: 提供独立的「运输计划合同完善」界面,只列出仍缺合同的未作废计划,并支持在列表行内直接关联已有 CRM 合同(不支持在本页新增/编辑合同)。
| 项 | 结论 |
|---|---|
| 实现路径 | 独立菜单页 + 专用列表/保存 API(路径 1) |
| 交互 | 列表「补充」打开弹窗关联本段合同(已由行内展开改为弹窗,见 docs/fresh-mint-pl-plan-contract-supplement-dialog-demo.html) |
| 计划范围 | 仅未作废(follow_status != cancelled),含待安排/已安排/运输中等;已归档但未作废的计划仍入列(未采用「排除归档」) |
| 合同数量 | 每侧可多选(与计划新增/编辑一致) |
| 保存方式 | 两侧各自「保存」;补完一侧即可落库 |
| 菜单角标 | 要;显示当前缺合同条数(对齐危化品证) |
| 非目标 | 本页不新增/编辑/审批合同;不改计划其它字段;不做小程序页 |
挂在 运输管理 → 运输计划 分组下,与「运输计划」「运输计划归档」并列:
| 项 | 值 |
|---|---|
| 菜单名 | 运输计划合同完善 |
| Hash 路由 | /#/pl/plan-module/plan-contract |
| 组件 | web-admin/src/views/pl/plan-contract/index.vue |
| menuCode | pl:plan-contract:list |
| 更新权限 | pl:plan-contract:update |
路由注册:web-admin/src/router/modules/pl.ts(plan-module children)。
菜单/权限 SQL:写入 docs/sql(与现有 pl 菜单脚本风格一致)。
P02 复杂业务列表,参考:
web-admin/src/views/pl/plan-cert/index.vue(待办型衍生列表 + 角标)web-admin/src/views/pl/plan/index.vue(列表骨架、筛选、日期快捷)视觉遵循 docs/web-admin-ui-spec-fresh-mint.md。
页面骨架:
panel
├─ panel-top:标题 + 待完善数量
├─ filter-block
├─ list-toolbar(日期快捷 + 快速搜索 + 刷新;无「新增」)
├─ list-table(可展开行)
└─ pagination
对每条未删除、未作废的运输计划:
is_internal_customer_name(supplier_name) → 该侧不要求合同(可免)pl_plan_contract(side=supplier, del_flag=0)customer_name 同理(side=demander)列表/角标口径必须与上述规则一致(前后端同一工具函数)。
| 列 | 说明 |
|---|---|
| 计划编号 | 主色链接 → 打开现有计划只读查看 |
| 计划日期 | |
| 供方 | 名称;副文案:已关联 N 份 / 未关联 / 内置可免 |
| 需方 | 同上 |
| 合同缺口 | Tag:缺供方合同 / 缺需方合同 / 两侧均缺 |
| 跟进状态 | 现有跟进状态 Tag |
| 操作 | 「展开补充」/「收起」;「查看计划」 |
plan_date DESC,其次 create_time DESCapply_pl_scope)| 侧状态 | UI |
|---|---|
| 待补充 | 已选合同卡片(可移除)+「选择合同」+ 该侧「保存」 |
| 已齐 | 展示已关联合同;仍允许改选后保存 |
| 可免(内置) | 灰态说明;无选择器、无保存按钮 |
ContractSelector(多选;可选状态与计划页一致:已审批 / 执行中);按该侧 customerId 过滤组件复用:优先复用 PlPlanContractMultiSelect / ContractSelector 的选择与卡片展示能力。
建议挂在运输计划模块下(路径示例,实现时可微调前缀,须注册到 app/api + main):
GET /api/pl/plans/contract-gaps
Query:分页 + planNo、gapType(supplier | demander | both)、客户关键词、planDate 起止、快速搜索等。
固定过滤:del_flag=0、follow_status != cancelled、存在合同缺口(不按 archive_status 排除)。
响应行至少包含:
gapSides: ("supplier" | "demander")[]supplierContractExempt / demanderContractExemptsupplierContracts / demanderContracts(PlanContractLink[],展开回填)PUT /api/pl/plans/{planId}/contracts/{side}
side ∈ supplier | demander{ "contracts": PlanContractLink[] }(可多份;空数组表示清空该侧)服务端:
assert_contract_selectable_for_plan / validate_plan_party_contracts 中与合同相关的能力;本接口不因「非内置且空列表」直接 422(允许先清空;列表是否仍展示由缺口规则决定)replace_plan_contracts 整体替换关联表gapSides(及豁免标记)不走完整 PlanUpdate;不修改产品、运输、客户等字段。
计划创建/全量更新已不再强制非内置侧必须有合同。本页是事后补齐通道;主计划编辑页仍可改合同,行为保持一致。
menuCode:pl:plan-contract:listbackend/app/services/pl_menu_badge_service.py 增加计数函数,口径与列表默认条件一致SUPPORTED_MENU_CODES;前端侧栏展示数字(对齐 pl:plan-cert:list)| 权限码 | 用途 |
|---|---|
pl:plan-contract:list |
进入页面、拉列表、角标 |
pl:plan-contract:update |
单侧保存合同 |
「查看计划」复用现有计划查看能力(与 plan-cert 跳转查看一致,通常依赖 pl:plan:list 或已有详情权限)。
| 文件 | 职责 |
|---|---|
web-admin/src/views/pl/plan-contract/index.vue |
页面 |
web-admin/src/router/modules/pl.ts |
路由 |
web-admin/src/api/pl/* |
list gaps / save side |
| (可选)小组件 | 行内展开双侧合同区,避免单文件过大 |
| 文件 | 职责 |
|---|---|
app/api/pl/plan.py 或独立 router |
两个端点 |
app/services/pl_plan_service.py(或小服务模块) |
缺口查询、单侧保存编排 |
app/schemas/pl/plan.py |
请求/响应模型 |
app/services/pl_menu_badge_service.py |
角标 |
docs/sql/* |
菜单与权限 |
replace_plan_contracts;后写覆盖该次替换结果(可接受;不做乐观锁,除非后续有冲突投诉)