diff --git a/docs/fresh-mint-pl-plan-contract-complete-demo.html b/docs/fresh-mint-pl-plan-contract-complete-demo.html new file mode 100644 index 000000000..dddec0ba7 --- /dev/null +++ b/docs/fresh-mint-pl-plan-contract-complete-demo.html @@ -0,0 +1,803 @@ + + + + + + Fresh Mint · 运输计划合同完善 Demo + + + + + + +
+
+

运输计划 · 合同完善 界面 Demo

+

+ 仅展示「缺合同」的运输计划。判定规则:供需双方若非内置公司 + (安庆市山峡化工有限公司 / 安徽云栋化工有限公司),该侧至少需关联 1 份合同; + 内置公司该侧合同可免。本页只做关联已有合同,不支持新增/编辑合同。 +

+
+ Fresh Mint · web-admin + 参考模式:P02 复杂列表 + 关联弹窗 + 可点击交互 Demo + 菜单建议:运输计划模块下 +
+
+ +
+
+

业务目标

+
    +
  • 创建计划时合同改为非必填,事后用本页快速补齐
  • +
  • 列表默认只显示缺口:缺供方 / 缺需方 / 两侧都缺
  • +
  • 行内操作「补充合同」→ 打开选择器关联 CRM 已有合同
  • +
  • 补齐后自动从本列表消失(或刷新后消失)
  • +
+
+
+

本 Demo 两种交互

+
    +
  • 方案 A(推荐):列表 +「补充合同」弹窗,两侧分区关联
  • +
  • 方案 B:列表行展开,直接在行内补两侧合同
  • +
  • 下方可切换方案,并点击按钮体验弹窗
  • +
+
+
+ +
+ + +
+ + +
+

+ 推荐:与现有「危化品证上传」「运输计划」列表一致;弹窗内可专注选合同, + 不打断表格滚动;便于后续加菜单角标(待完善数量)。 +

+ +
+
+

运输计划合同完善

+ 待完善 3 条 +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+ 仅展示仍缺合同的运输计划 · 内置公司侧已自动豁免 + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
计划编号计划日期供方需方合同缺口跟进状态操作
YP202608200012026-08-20 +
+ 合肥某某商贸有限公司 + 供方合同:未关联 +
+
+
+ 安庆市山峡化工有限公司 + 内置 · 合同可免 +
+
+
+ 缺供方合同 +
+
已安排 +
+ + +
+
YP202608190882026-08-19 +
+ 安庆市山峡化工有限公司 + 内置 · 合同可免 +
+
+
+ 芜湖某某材料有限公司 + 需方合同:未关联 +
+
+
+ 缺需方合同 +
+
待安排 +
+ + +
+
YP202608180422026-08-18 +
+ 马鞍山某某化工有限公司 + 供方合同:未关联 +
+
+
+ 铜陵某某实业有限公司 + 需方合同:未关联 +
+
+
+ 两侧均缺 +
+
运输中 +
+ + +
+
+
+
共 3 条 · 每页 20 条 · 1 / 1
+
+
+ + +
+

+ 备选:适合一次补几条、不想频繁开弹窗的场景;表格变高,窄屏体验略差。 +

+ +
+
+

运输计划合同完善

+ 待完善 2 条(示例) +
+
+ 点击「展开补充」在行内关联合同 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
计划编号计划日期供方需方合同缺口操作
YP202608200012026-08-20合肥某某商贸有限公司安庆市山峡化工有限公司缺供方合同
+
+
+ + +
+
+
YP202608180422026-08-18马鞍山某某化工有限公司铜陵某某实业有限公司两侧均缺
+
+
+
+ +
+
+

缺口判定(与内置名单一致)

+
    +
  • 名称 ∈ {山峡, 云栋} → 该侧不要求合同
  • +
  • 否则 → 该侧 contracts.length ≥ 1
  • +
  • 任一侧不满足 → 进入本列表
  • +
+
+
+

本页能力边界

+
    +
  • ✅ 筛选、列表、关联已有合同、保存
  • +
  • ✅ 跳转查看运输计划(只读)
  • +
  • ❌ 不在此页新增 / 编辑 / 审批合同
  • +
+
+
+

待你确认(下一步提问)

+
    +
  • 是否含已作废计划?归档计划?
  • +
  • 菜单角标是否显示待完善数量?
  • +
  • 关联时是否允许多份合同?
  • +
+
+
+
+ + + + +
已保存合同关联(Demo)
+ + + + diff --git a/docs/superpowers/specs/2026-08-20-pl-plan-contract-complete-design.md b/docs/superpowers/specs/2026-08-20-pl-plan-contract-complete-design.md new file mode 100644 index 000000000..d9ebec435 --- /dev/null +++ b/docs/superpowers/specs/2026-08-20-pl-plan-contract-complete-design.md @@ -0,0 +1,218 @@ +# 运输计划合同完善 · 设计说明 + +> Demo:`docs/fresh-mint-pl-plan-contract-complete-demo.html`(方案 B 为采纳交互) +> 日期:2026-08-20 +> 状态:待实现 + +## 1. 背景与目标 + +运输计划新增/编辑时,供需双方合同已改为**非必填**,便于先建计划再补合同。业务上:非内置公司一侧仍应关联合同;内置公司该侧合同可免。 + +**内置公司(合同可免):** + +- 安庆市山峡化工有限公司 +- 安徽云栋化工有限公司 + +判定逻辑复用现有工具: + +- 前端:`web-admin/src/utils/pl-internal-customer.ts` → `isPlInternalCustomerName` +- 后端:`backend/app/services/pl_internal_customer.py` → `is_internal_customer_name` + +**目标:** 提供独立的「运输计划合同完善」界面,只列出仍缺合同的未作废计划,并支持在列表行内直接关联已有 CRM 合同(不支持在本页新增/编辑合同)。 + +## 2. 已确认决策 + +| 项 | 结论 | +|---|---| +| 实现路径 | 独立菜单页 + 专用列表/保存 API(路径 1) | +| 交互 | 方案 B:行内展开补充合同 | +| 计划范围 | 仅未作废(`follow_status != cancelled`),含待安排/已安排/运输中等;**已归档但未作废的计划仍入列**(未采用「排除归档」) | +| 合同数量 | 每侧可多选(与计划新增/编辑一致) | +| 保存方式 | 两侧各自「保存」;补完一侧即可落库 | +| 菜单角标 | 要;显示当前缺合同条数(对齐危化品证) | +| 非目标 | 本页不新增/编辑/审批合同;不改计划其它字段;不做小程序页 | + +## 3. 信息架构与入口 + +### 3.1 菜单 + +挂在 **运输管理 → 运输计划** 分组下,与「运输计划」「运输计划归档」并列: + +| 项 | 值 | +|---|---| +| 菜单名 | 运输计划合同完善 | +| 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 菜单脚本风格一致)。 + +### 3.2 UI 模式 + +**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 +``` + +## 4. 缺口判定规则 + +对每条未删除、未作废的运输计划: + +1. **供方侧** + - 若 `is_internal_customer_name(supplier_name)` → 该侧**不要求**合同(可免) + - 否则 → 至少 1 条有效供方侧 `pl_plan_contract`(`side=supplier`, `del_flag=0`) +2. **需方侧** + - 对 `customer_name` 同理(`side=demander`) +3. **入列表条件**:供方侧缺口 **或** 需方侧缺口(任一侧不满足要求) + +列表/角标口径必须与上述规则一致(前后端同一工具函数)。 + +## 5. 列表与行内展开 + +### 5.1 列表列 + +| 列 | 说明 | +|---|---| +| 计划编号 | 主色链接 → 打开现有计划只读查看 | +| 计划日期 | | +| 供方 | 名称;副文案:已关联 N 份 / 未关联 / 内置可免 | +| 需方 | 同上 | +| 合同缺口 | Tag:`缺供方合同` / `缺需方合同` / `两侧均缺` | +| 跟进状态 | 现有跟进状态 Tag | +| 操作 | 「展开补充」/「收起」;「查看计划」 | + +### 5.2 筛选与排序 + +- 筛选:计划编号、缺口类型(全部 / 仅缺供方 / 仅缺需方 / 两侧均缺)、供方或需方关键词、计划日期区间 +- 工具栏:日期快捷 + 快速搜索(计划编号/供方/需方) +- 默认排序:`plan_date DESC`,其次 `create_time DESC` +- 数据权限:与运输计划列表相同(`apply_pl_scope`) + +### 5.3 行内展开交互 + +- 同时仅展开一行;展开另一行时收起当前行 +- 展开区左右两栏:供方合同 | 需方合同 + +| 侧状态 | UI | +|---|---| +| 待补充 | 已选合同卡片(可移除)+「选择合同」+ 该侧「保存」 | +| 已齐 | 展示已关联合同;仍允许改选后保存 | +| 可免(内置) | 灰态说明;无选择器、无保存按钮 | + +- 「选择合同」复用 `ContractSelector`(多选;可选状态与计划页一致:已审批 / 执行中);按该侧 `customerId` 过滤 +- **不提供**新建/编辑合同入口 +- 保存只提交**该侧**合同列表;成功后刷新该行;若两侧均已满足缺口规则,该行从列表移除并刷新角标 +- 有未保存变更时收起:二次确认 + +组件复用:优先复用 `PlPlanContractMultiSelect` / `ContractSelector` 的选择与卡片展示能力。 + +## 6. API 设计 + +建议挂在运输计划模块下(路径示例,实现时可微调前缀,须注册到 `app/api` + `main`): + +### 6.1 缺合同分页列表 + +`GET /api/pl/plans/contract-gaps` + +Query:分页 + `planNo`、`gapType`(`supplier` | `demander` | `both`)、客户关键词、`planDate` 起止、快速搜索等。 + +固定过滤:`del_flag=0`、`follow_status != cancelled`、存在合同缺口(**不**按 `archive_status` 排除)。 + +响应行至少包含: + +- 计划基础字段(编号、日期、供需方 id/name、跟进状态等) +- `gapSides: ("supplier" | "demander")[]` +- `supplierContractExempt` / `demanderContractExempt` +- `supplierContracts` / `demanderContracts`(`PlanContractLink[]`,展开回填) + +### 6.2 单侧保存合同 + +`PUT /api/pl/plans/{planId}/contracts/{side}` + +- `side` ∈ `supplier` | `demander` +- Body:`{ "contracts": PlanContractLink[] }`(可多份;空数组表示清空该侧) + +服务端: + +1. 校验计划存在、未作废、租户与数据权限 +2. 校验合同可选(归属该侧客户、状态允许等),复用 `assert_contract_selectable_for_plan` / `validate_plan_party_contracts` 中与合同相关的能力;**本接口不因「非内置且空列表」直接 422**(允许先清空;列表是否仍展示由缺口规则决定) +3. 加载对侧现有合同 + 本侧请求列表 → 调用现有 `replace_plan_contracts` 整体替换关联表 +4. 返回:更新后两侧合同 + 最新 `gapSides`(及豁免标记) + +**不走**完整 `PlanUpdate`;不修改产品、运输、客户等字段。 + +### 6.3 与现有「合同非必填」的关系 + +计划创建/全量更新已不再强制非内置侧必须有合同。本页是事后补齐通道;主计划编辑页仍可改合同,行为保持一致。 + +## 7. 菜单角标 + +- `menuCode`:`pl:plan-contract:list` +- 在 `backend/app/services/pl_menu_badge_service.py` 增加计数函数,口径与列表默认条件一致 +- 加入 `SUPPORTED_MENU_CODES`;前端侧栏展示数字(对齐 `pl:plan-cert:list`) + +## 8. 权限 + +| 权限码 | 用途 | +|---|---| +| `pl:plan-contract:list` | 进入页面、拉列表、角标 | +| `pl:plan-contract:update` | 单侧保存合同 | + +「查看计划」复用现有计划查看能力(与 `plan-cert` 跳转查看一致,通常依赖 `pl:plan:list` 或已有详情权限)。 + +## 9. 前端文件(预期) + +| 文件 | 职责 | +|---|---| +| `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 | +| (可选)小组件 | 行内展开双侧合同区,避免单文件过大 | + +## 10. 后端文件(预期) + +| 文件 | 职责 | +|---|---| +| `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/*` | 菜单与权限 | + +## 11. 错误与边界 + +- 已作废计划:列表不出现;若 ID 直调保存 → 400/422,提示不可修改 +- 合同不属于该侧客户或状态不可选 → 422,明确文案 +- 并发:单侧保存以「读对侧最新 + 写本侧」再 `replace_plan_contracts`;后写覆盖该次替换结果(可接受;不做乐观锁,除非后续有冲突投诉) +- 内置侧误传合同:允许关联(不强制拒绝),UI 默认不展示选择器 + +## 12. 测试要点 + +- 缺口判定:双非内置无合同 → 入列;一侧内置一侧外部无合同 → 仅缺外部侧;双侧内置 → 不入列 +- 单侧保存后对侧合同不变 +- 补齐后行消失、角标减一 +- 作废计划不出现在列表与角标 +- 数据权限用户看不到范围外计划 +- 前端:展开互斥、未保存收起确认、无新建合同入口 + +## 13. 非目标(明确不做) + +- 合同 CRUD / 审批 +- 修改运输计划主数据、产品、运输安排 +- 小程序「合同完善」页 +- 主计划列表增加「缺合同」Tab(本设计采用独立页)