From 4e6e06a603e0cd8290fd165719f309002818ca71 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 31 Jul 2026 05:00:36 +0800 Subject: [PATCH] docs: add transport feedback brand qty design and demo Capture the approved design for site/driver brand quantity feedback, independent storage from archive, and view-tab/button layout changes. Co-authored-by: Cursor --- docs/demos/pl-site-driver-brand-qty-demo.html | 764 ++++++++++++++++++ ...2026-07-31-pl-feedback-brand-qty-design.md | 214 +++++ 2 files changed, 978 insertions(+) create mode 100644 docs/demos/pl-site-driver-brand-qty-demo.html create mode 100644 docs/superpowers/specs/2026-07-31-pl-feedback-brand-qty-design.md diff --git a/docs/demos/pl-site-driver-brand-qty-demo.html b/docs/demos/pl-site-driver-brand-qty-demo.html new file mode 100644 index 000000000..1e573ea94 --- /dev/null +++ b/docs/demos/pl-site-driver-brand-qty-demo.html @@ -0,0 +1,764 @@ + + + + + + 运输计划回传 · 按品牌填量 Demo + + + +
+

运输计划回传 · 按品牌填量 Demo

+

+ 左侧模拟现场链接(入/出库单回执),右侧模拟司机链接。 + 有品牌的产品(烧碱)可填 1~N 条「品牌 + 数量」;无品牌产品(盐酸)只填总实际数量。 + 请先选择下方交互方案,再在手机框内试用。 +

+ +
+ + + +
+ +
+ +
+

现场链接 · 入库单回执

+
+
+ +
+
+
业务类型采购入库
+
往来单位君正化工
+
车牌皖A·12X34
+
计划号JH20260731001
+
+ +
+
产品实际数量
+
+ + +
+
+
烧碱
+ 不限品牌 +
+
+
+
规格型号
+
32%
+
+
+
包装规格
+
散装
+
+
+
计划数量
+
30 吨
+
+
+
+
+ 实际数量 + 由品牌明细合计 + +
+
+ + 吨 +
+
+ +
+
+

按品牌填量

+ 合计 28 = 实际数量 ✓ +
+
+ +
+ + +
+ + +
+
+
盐酸
+ 产品 2/2 +
+
+
+
规格型号
+
31%
+
+
+
包装规格
+
吨袋
+
+
+
计划数量
+
10 吨
+
+
+
+
+ 实际数量 + 无品牌产品,直接填写总量 +
+
+ + 吨 +
+
+
+
+ +
+
+
+ + +
+

司机链接 · 运输回传

+
+
+ +
+
+
计划编号JH20260731001
+
车牌皖A·12X34
+
司机张师傅 138****0001
+
+ +
+
产品明细
+
烧碱 32% · 散装计划 30 吨
+
盐酸 31% · 吨袋计划 10 吨
+
+ + + +
+
回传数量(按产品)
+ +
+
+
烧碱
+ 不限品牌 +
+
+
+ 实际数量 + 由品牌明细合计 +
+
+ + 吨 +
+
+
+
+

按品牌填量

+ 合计 28 = 实际数量 ✓ +
+
+ +
+
+ +
+
+
盐酸
+ 无品牌 +
+
+
+ 实际数量 + 无品牌,直接填总量 +
+
+ + 吨 +
+
+
+
+
+ +
+
+
+
+ +
+ 规则预览: + ① 产品有品牌概念(brandMode = any/required)时展示品牌明细;无品牌则隐藏。 + ② 可填 1 个或多个品牌行,数量 > 0 才计入。 + ③ 各品牌数量合计 = 该产品实际数量;不限品牌时可多选品牌,指定品牌时仅允许该品牌一行。 + ④ 现场回传写入后,归档弹窗可预填品牌明细(与现有归档按品牌库存衔接)。 +

+ 点击上方方案卡片选择偏好(会写入选择事件)。本地文件也可直接打开: + .superpowers/brainstorm/.../content/site-driver-brand-qty-demo.html +
+
+ + + + diff --git a/docs/superpowers/specs/2026-07-31-pl-feedback-brand-qty-design.md b/docs/superpowers/specs/2026-07-31-pl-feedback-brand-qty-design.md new file mode 100644 index 000000000..41500f21c --- /dev/null +++ b/docs/superpowers/specs/2026-07-31-pl-feedback-brand-qty-design.md @@ -0,0 +1,214 @@ +# 运输计划回传按品牌填量设计 + +**日期:** 2026-07-31 +**状态:** 已确认待实现 +**Demo:** `docs/demos/pl-site-driver-brand-qty-demo.html` + +## 背景与目标 + +运输计划安排后生成现场链接(入/出库单公共页)与司机链接。当前回传只记录产品(或全单)总实际数量,无法区分同一产品不同品牌(如烧碱:君正 / 红四方)的实装量。 + +本变更要求: + +1. 现场链接、司机链接均可按产品回传品牌数量(交互:品牌芯片点选再填量)。 +2. 产品无品牌概念时,不展示品牌明细,只填总量。 +3. 现场与司机数据各自独立保存;归档弹窗「产品实际数量」由归档人独立填写最终量并入账(沿用现有 `pl_plan_product_brand_qty`)。 +4. 运输计划 / 归档 / 危化品证上传的查看弹窗「运输信息」Tab 展示品牌数量。 +5. 归档嵌入查看中的两个「补充」弹窗支持品牌数量录入;标题栏按钮布局按下文调整。 + +## 决策摘要 + +| 项 | 决策 | +|----|------| +| 交互 | 方案 B:品牌芯片点选 → 填数量;可多选 | +| 范围 | 现场链接 + 司机链接都按产品填品牌明细 | +| 权威 | 三套独立;仅归档最终量触发库存入账 | +| 存储 | 独立明细子表(不与归档表混用 source) | +| 归档预填 | 不用现场/司机品牌量自动覆盖归档最终量 | + +## 数据模型 + +### 权威边界 + +| 来源 | 数量落点 | 用途 | +|------|----------|------| +| 现场 | 入/出库明细 `actual_qty` + 现场品牌子表 | 留痕、查看展示 | +| 司机 | 按计划产品行回传总量 + 司机品牌子表;保留 `pl_transport.feedback_qty` 作全单合计兼容 | 留痕、查看展示 | +| 归档 | `pl_plan_product.actual_qty` + `pl_plan_product_brand_qty` | **唯一**库存入账来源 | + +三套互不覆盖。归档人员在归档弹窗「产品实际数量」区域独立确认最终信息。 + +### 新表 + +#### 1. 现场:入/出库明细品牌量 + +采用 **入出库各一张表**(与现有 `pl_inbound_order_item` / `pl_outbound_order_item` 对称),结构相同: + +- `pl_inbound_order_item_brand_qty` +- `pl_outbound_order_item_brand_qty` + +字段(业务): + +| 字段 | 说明 | +|------|------| +| `id` | UUID | +| `order_id` | 入/出库单 ID | +| `order_item_id` | 明细 ID | +| `plan_product_id` | 计划产品行 ID(冗余,便于按计划聚合展示) | +| `brand_id` | 品牌 ID,空串表示无品牌键(本场景有品牌时非空) | +| `brand_name` | 品牌名快照 | +| `qty` | 本品牌数量 | + +其后接公共字段:`tenant_no`、`order_num`、`remark`、`create_by`、`create_dept_id`、`create_time`、`update_by`、`update_time`、`del_flag`。 + +软删旧行后按提交结果重插(与归档品牌量持久化模式一致)。 + +#### 2. 司机:按产品回传 + +- `pl_transport_product_feedback` + - `transport_id`、`plan_id`、`plan_product_id` + - `actual_qty`(该产品回传总量) + - 公共字段 + 软删 +- `pl_transport_product_brand_qty` + - `transport_id`、`plan_product_id`、`feedback_id`(可选 FK) + - `brand_id`、`brand_name`、`qty` + - 公共字段 + 软删 + +兼容:保存司机回传时,用各产品 `actual_qty` 之和回写 `pl_transport.feedback_qty`,供旧展示/报表过渡。 + +### 只读辅助字段(API) + +公开页与查看接口对有品牌的产品附带: + +- `brandMode`、`brandId`、`brandName`(计划行约定) +- `brandOptions[]`:`{ brandId, brandName }`(来自 CRM 产品品牌) +- `brandQtys[]`:已填 `{ brandId, brandName, qty }` + +无 `brandMode` 时不返回品牌录入区所需 options。 + +## 录入交互与校验 + +### 何时展示品牌区 + +- 计划产品行存在 `brandMode`(`any` / `required`)→ 展示芯片 + 数量。 +- 否则 → 仅总量输入,不展示品牌区。 + +### 交互(方案 B) + +1. 以芯片展示可选品牌;点选启用,再为已选品牌填数量。 +2. `required`:仅预选指定品牌,不可改选其他;数量可编辑。 +3. 已选品牌数量合计自动回写该产品实际数量(合计只读)。 +4. 未选中或 `qty ≤ 0` 的品牌不落库。 +5. 支持 1 个或多个品牌。 + +### 校验 + +- 有品牌且提交完成态:至少 1 个品牌 `qty > 0`,且各品牌合计等于该产品实际数量。 +- 无品牌:只校验总量规则(与现网入出库/司机回传一致)。 +- 归档弹窗继续使用现有 `validate_brand_qty_rows` + `persist_plan_product_brand_qty`;**不得**用现场/司机数据自动覆盖归档最终量(允许 UI 旁路展示对照,默认不写入表单最终值)。 + +## API 变更(要点) + +### 现场公共 API + +- `GET /public/pl/{inbound|outbound}/{token}`:items 增加 `brandMode`、`brandOptions`、`brandQtys` 等。 +- `POST .../feedback`:items 元素增加可选 `brandQtys[]`;服务端校验并持久化子表,同时更新 `actual_qty`。 + +### 司机公共 API + +- 页面数据:products 增加品牌字段与已填 `brandQtys`;支持按产品 `actualQty`。 +- 保存回传:接收 `products: [{ planProductId, actualQty, brandQtys? }]`(及原有备注/附件);写产品回传表 + 品牌子表,并回写 `feedback_qty` 合计。 + +### 管理端补充 + +- `PATCH .../driver-feedback`:扩展为可提交按产品数量与 `brandQtys`(与公开页同源服务逻辑)。 +- 入/出库现场补充(现有编辑入出库回传路径):同样支持 `brandQtys`。 + +### 查看 + +- 计划详情 / 运输信息 Tab 所用聚合接口返回司机按产品回传、入出库明细品牌摘要(如 `君正 16 / 红四方 12`),与现有归档 `brandSummary` 展示风格一致。 + +## 界面改动 + +### 公开页 + +- `PublicIoOrderPage`:产品卡增加芯片填量区。 +- `views/public/pl/transport`:由「全单一总数量」改为「按产品填量」;有品牌则芯片区。 + +### 管理端补充弹窗 + +- `PlPlanDriverFeedbackDialog`:按产品列表 + 芯片填量(不再仅一个 `feedbackQty`)。 +- 入/出库回传补充/编辑弹窗:产品行支持品牌芯片填量。 +- 归档弹窗「产品实际数量」:最终量仍独立填写;交互统一为芯片方案(替换或并存于当前表格填量,以实现时统一芯片为准)。 + +### 查看 · 运输信息 Tab + +共用组件:`PlPlanViewTabs`(运输计划查看、归档弹窗嵌入、危化品证上传查看)。 + +- 司机回传:按产品展示回传量 + 品牌摘要。 +- 入/出库明细表:增加品牌数量列或摘要列。 + +### 标题栏按钮布局(`feedbackEditable` 时) + +**入/出库回传**标题栏右侧(从左到右): + +1. `补充` / `编辑` — `type="primary"` 高亮 +2. `复制现场链接` + +(原正文底部的补充按钮移除。) + +**司机回传**标题栏右侧(从左到右): + +1. `补充` / `编辑` — `type="primary"` 高亮 +2. `复制司机链接` + +两个补充按钮均高亮展示。 + +## 组件与复用 + +建议抽取公共「产品品牌数量录入」块(芯片 + 数量 + 合计回写),供: + +- 公开现场页、公开司机页 +- 司机补充弹窗、入出库补充弹窗 +- 归档弹窗产品实际数量区 + +校验逻辑与后端 `validate_brand_qty_rows` 语义对齐(前端先拦,后端再验)。 + +## 错误处理 + +- 公开页校验失败:Toast 提示具体产品与原因(合计不符 / 未选品牌等)。 +- 管理端:`ElMessage` / 表单项级提示;提交接口 422 展示后端 detail。 +- 链接失效、计划作废:保持现有只读/拦截行为。 + +## 测试要点 + +1. 无品牌产品:现场/司机仅总量,子表无行。 +2. `any`:多品牌芯片,合计回写实际数量,落库多行。 +3. `required`:仅指定品牌可选,数量可改。 +4. 现场与司机分别保存不同品牌量后,查看 Tab 两侧独立展示;归档表单不被自动覆盖。 +5. 归档提交仍只写 `pl_plan_product_brand_qty` 并按该表入账。 +6. 标题栏:补充高亮、复制现场在补充右侧、司机区复制司机链接在补充右侧。 +7. 旧数据:无品牌子表时查看展示「—」或仅总量;司机旧 `feedback_qty` 仍可读。 + +## 非目标 + +- 不以现场/司机回传自动驱动库存记账。 +- 不改造地磅数量权威链路(地磅预填 actual_qty 规则保持;品牌量仍由人工在现场/归档填写)。 +- 不在本变更中统一 CRM 品牌主数据维护(沿用现有产品品牌)。 + +## 实现顺序建议 + +1. SQL 子表 + models/schemas +2. 现场公共读写 + PublicIoOrderPage +3. 司机公共读写 + 司机公开页 +4. 管理端补充弹窗 + 查看 Tab 展示与标题栏按钮 +5. 归档弹窗芯片交互对齐(最终量仍独立) +6. 回归测试与 `docs/sql` 同步 + +## 参考 + +- 现有归档品牌量:`backend/app/services/pl_brand_archive.py`、`pl_plan_product_brand_qty` +- 现场公开页:`web-admin/src/views/public/pl/_shared/PublicIoOrderPage.vue` +- 司机公开页:`web-admin/src/views/public/pl/transport/index.vue` +- 查看 Tab:`web-admin/src/components/business/pl-plan-view-tabs/index.vue` +- 交互 Demo:`docs/demos/pl-site-driver-brand-qty-demo.html`