From c626023250664c4b19700b8825970b353de50545 Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Tue, 13 Oct 2026 20:38:17 +0800 Subject: [PATCH] feat(miniprogram): split driver plan copy product into three lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make copy-to-group text easier to scan: 产品信息 / 计划数量 / 实际数量 as separate lines. Co-authored-by: Cursor --- ...-mini-pl-driver-plan-copy-product-lines.md | 201 ++++++++++++++++++ miniprogram/utils/plDriverPlanCopy.js | 18 +- 2 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 docs/superpowers/plans/2026-10-13-mini-pl-driver-plan-copy-product-lines.md diff --git a/docs/superpowers/plans/2026-10-13-mini-pl-driver-plan-copy-product-lines.md b/docs/superpowers/plans/2026-10-13-mini-pl-driver-plan-copy-product-lines.md new file mode 100644 index 000000000..bdacd9127 --- /dev/null +++ b/docs/superpowers/plans/2026-10-13-mini-pl-driver-plan-copy-product-lines.md @@ -0,0 +1,201 @@ +# 司机运输计划单「复制发群」产品行拆分 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将小程序司机运输计划单「复制发群」中每个产品由单行紧凑格式改为三行(产品信息 / 计划数量 / 实际数量),多产品组间不空行。 + +**Architecture:** 仅改 `miniprogram/utils/plDriverPlanCopy.js`:`formatProductLine` 改为返回三行字符串数组;`appendInfoSection` 展开 push。头信息、区块顺序、空站/无产品边界、`copyDriverPlanToClipboard` 不变。用一次性 Node 断言脚本验证后删除。 + +**Tech Stack:** uni-app 小程序工具 JS(ES module)、Node 临时校验。 + +**Spec:** `docs/superpowers/specs/2026-10-13-mini-pl-driver-plan-copy-product-lines-design.md` + +--- + +## File map + +| 文件 | 动作 | 职责 | +|------|------|------| +| `miniprogram/utils/plDriverPlanCopy.js` | Modify | 产品三行输出;导出签名不变 | +| `/tmp` 临时校验脚本 | Create→Delete | Node 断言定稿示例 | + +--- + +### Task 1: 改产品行为三行 + +**Files:** +- Modify: `miniprogram/utils/plDriverPlanCopy.js` + +- [x] **Step 1: 更新规格注释;将 `formatProductLine` 改为返回三行数组;`appendInfoSection` 展开 push** + +把文件顶部规格注释改为指向新 design。替换产品格式化与追加逻辑为: + +```js +/** + * 司机运输计划单「复制发群」纯文本(不含附件) + * 规格:docs/superpowers/specs/2026-10-13-mini-pl-driver-plan-copy-product-lines-design.md + * (头信息/区块/边界继承 2026-10-12 enrich 设计) + */ + +// ... 保留 DASH、FIELD_GAP、stopTypeOf、customerNameOf、textOrDash、 +// formatQtyWithUnit、planQtyOf、actualQtyOf 不变 ... + +/** 产品三行:产品信息 / 计划数量 / 实际数量 */ +function formatProductLines(product) { + const name = textOrDash(product?.productName ?? product?.product_name) + const model = textOrDash(product?.model) + const pack = textOrDash(product?.packageSpec ?? product?.package_spec) + const unit = product?.unit + const planPart = formatQtyWithUnit(planQtyOf(product), unit) + const actualPart = formatQtyWithUnit(actualQtyOf(product), unit) + return [ + `- 产品信息:${name}${FIELD_GAP}${model}${FIELD_GAP}${pack}`, + `- 计划数量:${planPart}`, + `- 实际数量:${actualPart}` + ] +} + +function appendInfoSection(lines, title, stops) { + lines.push(title) + if (!stops.length) { + lines.push(DASH) + return + } + stops.forEach((stop) => { + lines.push(`${customerNameOf(stop)}:`) + const products = Array.isArray(stop?.products) ? stop.products : [] + if (!products.length) { + lines.push(`- ${DASH}`) + return + } + products.forEach((product) => { + formatProductLines(product).forEach((line) => lines.push(line)) + }) + }) +} +``` + +其余 `buildDriverPlanCopyText` / `copyDriverPlanToClipboard` 保持不变。删除旧的 `formatProductLine`。 + +- [x] **Step 2: 一次性 Node 断言(通过后删除脚本)** + +```bash +cat > /tmp/check-driver-plan-copy-lines.mjs << 'EOF' +import { buildDriverPlanCopyText } from './miniprogram/utils/plDriverPlanCopy.js' + +const page = { + copySeqNo: 128, + planNo: 'YSJH20260409001', + licensePlate: '沪A12345', + stops: [ + { + stopType: 'delivery', + customerName: '甲客户', + products: [ + { productName: '工业酒精', model: '95%', packageSpec: '吨桶', planQty: 12, actualQty: 11.9, unit: '吨' } + ] + }, + { + stopType: 'delivery', + customerName: '乙客户', + products: [ + { productName: '工业酒精', model: '95%', packageSpec: '吨桶', planQty: 8, actualQty: 7.9, unit: '吨' }, + { productName: '甲醇', model: '99.9%', packageSpec: '吨桶', planQty: 2, actualQty: 2, unit: '吨' } + ] + }, + { + stopType: 'load', + customerName: '某某化工厂', + products: [ + { productName: '工业酒精', model: '95%', packageSpec: '吨桶', planQty: 20, actualQty: 19.8, unit: '吨' }, + { productName: '甲醇', model: '99.9%', packageSpec: '吨桶', planQty: 2, actualQty: 2, unit: '吨' } + ] + } + ] +} + +const expected = [ + '序号:128', + '计划编号:YSJH20260409001', + '车辆:沪A12345', + '送货信息:', + '甲客户:', + '- 产品信息:工业酒精 95% 吨桶', + '- 计划数量:12 吨', + '- 实际数量:11.9 吨', + '乙客户:', + '- 产品信息:工业酒精 95% 吨桶', + '- 计划数量:8 吨', + '- 实际数量:7.9 吨', + '- 产品信息:甲醇 99.9% 吨桶', + '- 计划数量:2 吨', + '- 实际数量:2 吨', + '装货信息:', + '某某化工厂:', + '- 产品信息:工业酒精 95% 吨桶', + '- 计划数量:20 吨', + '- 实际数量:19.8 吨', + '- 产品信息:甲醇 99.9% 吨桶', + '- 计划数量:2 吨', + '- 实际数量:2 吨' +].join('\n') + +const actual = buildDriverPlanCopyText(page) +if (actual !== expected) { + console.error('MISMATCH\n--- actual ---\n' + actual + '\n--- expected ---\n' + expected) + process.exit(1) +} + +const emptyish = buildDriverPlanCopyText({ planNo: 'X', stops: [] }) +const emptyExpected = [ + '序号:—', + '计划编号:X', + '车辆:—', + '送货信息:', + '—', + '装货信息:', + '—' +].join('\n') +if (emptyish !== emptyExpected) { + console.error('EMPTY MISMATCH\n' + emptyish) + process.exit(1) +} + +console.log('OK') +EOF +# 在仓库根目录执行;若相对 import 失败则改用 file:// 绝对路径 +cd /Users/zlwu/work/00code/shanxia2.0 && node --input-type=module -e " +import { buildDriverPlanCopyText } from './miniprogram/utils/plDriverPlanCopy.js' +" 2>/dev/null || true +``` + +推荐在仓库根直接跑内嵌断言(路径相对仓库根): + +```bash +cd /Users/zlwu/work/00code/shanxia2.0 +node --input-type=module <<'EOF' +import { buildDriverPlanCopyText } from './miniprogram/utils/plDriverPlanCopy.js' +# ... 同上 page / expected / emptyish 断言 ... +EOF +``` + +Expected stdout: `OK` + +- [x] **Step 3: Commit** + +```bash +git add miniprogram/utils/plDriverPlanCopy.js docs/superpowers/plans/2026-10-13-mini-pl-driver-plan-copy-product-lines.md +git commit -m "feat(miniprogram): split driver plan copy product into three lines" +``` + +--- + +## Spec coverage checklist + +| Spec 项 | Task | +|---------|------| +| D1 只改 plDriverPlanCopy.js | Task 1 | +| D2 多产品不空行 | Task 1 `formatProductLines` 连续 push | +| D3 formatProductLine→三行 | Task 1 | +| D4 / D5 空值与无产品 `- —` | 既有 `appendInfoSection` + `textOrDash` | +| 定稿实例 | Task 1 Step 2 | diff --git a/miniprogram/utils/plDriverPlanCopy.js b/miniprogram/utils/plDriverPlanCopy.js index edd342646..de960b824 100644 --- a/miniprogram/utils/plDriverPlanCopy.js +++ b/miniprogram/utils/plDriverPlanCopy.js @@ -1,6 +1,7 @@ /** * 司机运输计划单「复制发群」纯文本(不含附件) - * 规格:docs/superpowers/specs/2026-10-12-mini-pl-driver-plan-copy-text-enrich-design.md + * 规格:docs/superpowers/specs/2026-10-13-mini-pl-driver-plan-copy-product-lines-design.md + * (头信息/区块/边界继承 2026-10-12 enrich 设计) */ const DASH = '—' @@ -41,18 +42,19 @@ function actualQtyOf(product) { return null } -/** 产品紧凑行:- 名称 型号 包装 计划数量:… 实际数量:… */ -function formatProductLine(product) { +/** 产品三行:产品信息 / 计划数量 / 实际数量(组间不空行) */ +function formatProductLines(product) { const name = textOrDash(product?.productName ?? product?.product_name) const model = textOrDash(product?.model) const pack = textOrDash(product?.packageSpec ?? product?.package_spec) const unit = product?.unit const planPart = formatQtyWithUnit(planQtyOf(product), unit) const actualPart = formatQtyWithUnit(actualQtyOf(product), unit) - return ( - `- ${name}${FIELD_GAP}${model}${FIELD_GAP}${pack}` + - `${FIELD_GAP}计划数量:${planPart}${FIELD_GAP}实际数量:${actualPart}` - ) + return [ + `- 产品信息:${name}${FIELD_GAP}${model}${FIELD_GAP}${pack}`, + `- 计划数量:${planPart}`, + `- 实际数量:${actualPart}` + ] } /** @@ -74,7 +76,7 @@ function appendInfoSection(lines, title, stops) { return } products.forEach((product) => { - lines.push(formatProductLine(product)) + formatProductLines(product).forEach((line) => lines.push(line)) }) }) }