docs: add implementation plan for inventory pound QR popup.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
71f3fde31c
commit
a6e0c4481f
1 file changed
+677
@@ -0,0 +1,677 @@
|
||||
# 小程序出入库列表 · 磅单二维码弹窗 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:** 出入库管理列表去掉「生成地磅单」,改为主色「磅单二维码」按钮,弹出与运输计划一致的居中磅单二维码卡片(本地生成 QR,内容为出入库单 `id`)。
|
||||
|
||||
**Architecture:** 抽取纯函数工具(标题/物料行/备注归一)便于单测;新建居中弹窗组件 `db-pound-qrcode-popup`(样式对齐 `PoundSlipQrcodeView`);列表页只负责开/关与传 `inventoryId`。二维码用 `uqrcodejs` 画到隐藏 canvas 再导出临时路径展示,不依赖外网 QR 图床。
|
||||
|
||||
**Tech Stack:** uni-app Vue3 小程序、`uqrcodejs`、现有 `inventoryApi` / `db-btn`
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-09-24-mp-inventory-pound-qrcode-popup-design.md`
|
||||
**视觉参考:** `web-admin/src/components/business/db/PoundSlipQrcodeView.vue`、`web-admin/src/styles/db-module.scss`(`.pound-slip-capture-inline`)、运输计划 webview `DbPoundSlipQrcodeDialog`
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `miniprogram/utils/dbPoundQr.js` | 纯函数:弹窗标题、物料行文案、备注字段归一 |
|
||||
| `miniprogram/scripts/test_db_pound_qr.mjs` | Node 断言跑上述纯函数(小程序无 jest) |
|
||||
| `miniprogram/utils/makeQrTempFile.js` | 用 `uqrcodejs` + canvas 生成临时图片路径 |
|
||||
| `miniprogram/components/db-ui/db-pound-qrcode-popup.vue` | 居中弹窗 UI + 拉详情 + 展示 QR |
|
||||
| `miniprogram/pages/db/inventory/list.vue` | 按钮替换、挂载弹窗、删除 `genWeighing` |
|
||||
| `miniprogram/package.json` | 增加依赖 `uqrcodejs` |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 纯函数工具 + 单测
|
||||
|
||||
**Files:**
|
||||
- Create: `miniprogram/utils/dbPoundQr.js`
|
||||
- Create: `miniprogram/scripts/test_db_pound_qr.mjs`
|
||||
|
||||
- [ ] **Step 1: 写失败用例脚本(先测再实现)**
|
||||
|
||||
`miniprogram/scripts/test_db_pound_qr.mjs`:
|
||||
|
||||
```js
|
||||
import assert from 'node:assert/strict'
|
||||
import {
|
||||
poundQrDialogTitle,
|
||||
formatPoundQrProductLine,
|
||||
normalizePoundQrRemarks
|
||||
} from '../utils/dbPoundQr.js'
|
||||
|
||||
assert.equal(poundQrDialogTitle('出库'), '出库磅单二维码')
|
||||
assert.equal(poundQrDialogTitle('入库'), '入库磅单二维码')
|
||||
assert.equal(poundQrDialogTitle(''), '入库磅单二维码')
|
||||
|
||||
assert.equal(formatPoundQrProductLine({ product: '尿素', specification: '', quantity: 32 }, '吨'), '尿素(32 吨)')
|
||||
assert.equal(
|
||||
formatPoundQrProductLine({ product: '尿素颗粒', specification: '颗粒', quantity: 10 }, '吨'),
|
||||
'尿素 颗粒(10 吨)'
|
||||
)
|
||||
assert.equal(
|
||||
formatPoundQrProductLine({ product: '尿素', specification: '46%', quantity: 1 }, '吨'),
|
||||
'尿素 46%(1 吨)'
|
||||
)
|
||||
|
||||
assert.equal(normalizePoundQrRemarks({ remarks: '急单' }), '急单')
|
||||
assert.equal(normalizePoundQrRemarks({ remark: '备货' }), '备货')
|
||||
assert.equal(normalizePoundQrRemarks({ remarks: ' ', remark: '' }), '')
|
||||
|
||||
console.log('test_db_pound_qr: ok')
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd miniprogram && node scripts/test_db_pound_qr.mjs
|
||||
```
|
||||
|
||||
Expected: `ERR_MODULE_NOT_FOUND` 或类似(`dbPoundQr.js` 尚不存在)。
|
||||
|
||||
- [ ] **Step 3: 实现纯函数**
|
||||
|
||||
`miniprogram/utils/dbPoundQr.js`:
|
||||
|
||||
```js
|
||||
/**
|
||||
* 磅单二维码弹窗文案工具(与 web-admin PoundSlipQrcodeView / DbPoundSlipQrcodeDialog 对齐)
|
||||
*/
|
||||
|
||||
export function poundQrDialogTitle(businessType) {
|
||||
return String(businessType || '').trim() === '出库' ? '出库磅单二维码' : '入库磅单二维码'
|
||||
}
|
||||
|
||||
/** 与 DbPoundSlipQrcodeDialog.formatQrcodeProductLabel 一致 */
|
||||
export function formatQrcodeProductLabel(row) {
|
||||
const product = String(row?.product || '').trim()
|
||||
const specification = String(row?.specification || '').trim()
|
||||
if (!specification) return product
|
||||
if (product.endsWith(specification)) {
|
||||
const base = product.slice(0, -specification.length).trimEnd()
|
||||
return base ? `${base} ${specification}` : specification
|
||||
}
|
||||
return `${product} ${specification}`.trim()
|
||||
}
|
||||
|
||||
export function formatPoundQrProductLine(row, quantityUnit = '吨') {
|
||||
const unit = String(quantityUnit || '').trim() || '吨'
|
||||
const label = formatQrcodeProductLabel(row)
|
||||
const qty = row?.quantity
|
||||
const qtyText = qty === 0 || qty ? String(qty) : '--'
|
||||
return `${label}(${qtyText} ${unit})`
|
||||
}
|
||||
|
||||
export function normalizePoundQrRemarks(row) {
|
||||
const a = String(row?.remarks ?? '').trim()
|
||||
if (a) return a
|
||||
return String(row?.remark ?? '').trim()
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 再跑测试确认通过**
|
||||
|
||||
```bash
|
||||
cd miniprogram && node scripts/test_db_pound_qr.mjs
|
||||
```
|
||||
|
||||
Expected: `test_db_pound_qr: ok`
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add miniprogram/utils/dbPoundQr.js miniprogram/scripts/test_db_pound_qr.mjs
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(miniprogram): add pound QR label helpers and node checks.
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 安装 uqrcodejs + 生成临时二维码图
|
||||
|
||||
**Files:**
|
||||
- Modify: `miniprogram/package.json`
|
||||
- Create: `miniprogram/utils/makeQrTempFile.js`
|
||||
|
||||
- [ ] **Step 1: 安装依赖**
|
||||
|
||||
在 `miniprogram` 目录:
|
||||
|
||||
```bash
|
||||
cd miniprogram && npm install uqrcodejs --save
|
||||
```
|
||||
|
||||
确认 `package.json` 的 `dependencies` 含 `"uqrcodejs"`。
|
||||
|
||||
- [ ] **Step 2: 实现生成工具**
|
||||
|
||||
`miniprogram/utils/makeQrTempFile.js`:
|
||||
|
||||
```js
|
||||
import UQRCode from 'uqrcodejs'
|
||||
|
||||
/**
|
||||
* 在组件内隐藏 canvas 上绘制二维码,导出临时文件路径供 <image> 展示。
|
||||
* @param {object} opts
|
||||
* @param {string} opts.text 二维码内容(出入库单 id)
|
||||
* @param {string} opts.canvasId canvas-id
|
||||
* @param {object} opts.componentInstance 组件实例(getCurrentInstance().proxy)
|
||||
* @param {number} [opts.size=200]
|
||||
* @returns {Promise<string>} tempFilePath
|
||||
*/
|
||||
export function makeQrTempFile({ text, canvasId, componentInstance, size = 200 }) {
|
||||
const content = String(text || '').trim()
|
||||
if (!content) {
|
||||
return Promise.reject(new Error('二维码内容为空'))
|
||||
}
|
||||
if (!componentInstance) {
|
||||
return Promise.reject(new Error('缺少组件实例'))
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const qr = new UQRCode()
|
||||
qr.data = content
|
||||
qr.size = size
|
||||
qr.margin = 10
|
||||
qr.make()
|
||||
const ctx = uni.createCanvasContext(canvasId, componentInstance)
|
||||
qr.canvasContext = ctx
|
||||
qr.drawCanvas()
|
||||
// drawCanvas 内部会 ctx.draw;稍延迟再导出
|
||||
setTimeout(() => {
|
||||
uni.canvasToTempFilePath(
|
||||
{
|
||||
canvasId,
|
||||
width: size,
|
||||
height: size,
|
||||
destWidth: size,
|
||||
destHeight: size,
|
||||
success: (res) => {
|
||||
if (res.tempFilePath) resolve(res.tempFilePath)
|
||||
else reject(new Error('二维码导出失败'))
|
||||
},
|
||||
fail: (err) => reject(err || new Error('二维码导出失败'))
|
||||
},
|
||||
componentInstance
|
||||
)
|
||||
}, 120)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
说明:若真机 `drawCanvas` 与导出时序不稳,实现时可改为 `uQRCode` 文档中的 `make({ canvasId, componentInstance, text, size, success })` 成功回调拿 path/base64,但**禁止**改回外网 `api.qrserver.com`。
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add miniprogram/package.json miniprogram/package-lock.json miniprogram/utils/makeQrTempFile.js
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(miniprogram): add local QR temp-file helper via uqrcodejs.
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
(若无 lock 文件则只 add 实际存在的依赖清单文件。)
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 新建 `db-pound-qrcode-popup` 组件
|
||||
|
||||
**Files:**
|
||||
- Create: `miniprogram/components/db-ui/db-pound-qrcode-popup.vue`
|
||||
- Test: 手工打开弹窗(Task 5);纯函数已在 Task 1 覆盖
|
||||
|
||||
- [ ] **Step 1: 创建组件**
|
||||
|
||||
`miniprogram/components/db-ui/db-pound-qrcode-popup.vue`(完整实现):
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view v-if="show" class="pound-qr-mask" @click="close">
|
||||
<view class="pound-qr-panel" @click.stop>
|
||||
<view class="pound-qr-panel__title">{{ title }}</view>
|
||||
<view v-if="loading" class="pound-qr-panel__loading">加载中...</view>
|
||||
<view v-else-if="row" class="pound-slip-capture-inline">
|
||||
<view class="capture-card">
|
||||
<view class="capture-row capture-row-plate">
|
||||
<view class="vehicle-plate">
|
||||
<view class="vehicle-plate-dot vehicle-plate-dot-lt" />
|
||||
<view class="vehicle-plate-dot vehicle-plate-dot-lb" />
|
||||
<text class="vehicle-plate-text">{{ row.vehicleNo || '未填车牌' }}</text>
|
||||
<view class="vehicle-plate-dot vehicle-plate-dot-rt" />
|
||||
<view class="vehicle-plate-dot vehicle-plate-dot-rb" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="capture-row capture-row-large">
|
||||
<text class="label">往来单位:</text>
|
||||
<text class="value">{{ row.partnerCompany || '--' }}</text>
|
||||
</view>
|
||||
<view class="capture-row capture-row-large">
|
||||
<text class="label">物料名称:</text>
|
||||
<text class="value">{{ productLine }}</text>
|
||||
</view>
|
||||
<view v-if="remarksText" class="capture-row">
|
||||
<text class="label">备注:</text>
|
||||
<text class="value">{{ remarksText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="capture-card capture-qrcode">
|
||||
<view class="capture-id">
|
||||
{{ row.date || '' }}
|
||||
{{ row.businessType === '出库' ? '出库单ID:' : '入库单ID:' }}{{ row.id }}
|
||||
</view>
|
||||
<view class="capture-qr-wrap">
|
||||
<image v-if="qrPath" class="capture-qr-img" :src="qrPath" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="capture-desc">出入库单标识(扫码核验)</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 隐藏 canvas,供 uqrcodejs 绘制 -->
|
||||
<canvas
|
||||
canvas-id="poundQrCanvas"
|
||||
id="poundQrCanvas"
|
||||
class="pound-qr-canvas"
|
||||
:style="{ width: canvasSize + 'px', height: canvasSize + 'px' }"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, ref, watch } from 'vue'
|
||||
import { inventoryApi } from '@/pages/db/api/db'
|
||||
import {
|
||||
formatPoundQrProductLine,
|
||||
normalizePoundQrRemarks,
|
||||
poundQrDialogTitle
|
||||
} from '@/utils/dbPoundQr'
|
||||
import { makeQrTempFile } from '@/utils/makeQrTempFile'
|
||||
|
||||
const props = defineProps({
|
||||
show: Boolean,
|
||||
inventoryId: { type: String, default: '' },
|
||||
/** 列表行快照(可选);仍会按需 get 补全 */
|
||||
snapshot: { type: Object, default: null }
|
||||
})
|
||||
const emit = defineEmits(['close', 'update:show'])
|
||||
|
||||
const loading = ref(false)
|
||||
const row = ref(null)
|
||||
const qrPath = ref('')
|
||||
const canvasSize = 200
|
||||
const instance = getCurrentInstance()
|
||||
|
||||
const title = computed(() => poundQrDialogTitle(row.value?.businessType))
|
||||
const productLine = computed(() =>
|
||||
row.value ? formatPoundQrProductLine(row.value, '吨') : ''
|
||||
)
|
||||
const remarksText = computed(() => (row.value ? normalizePoundQrRemarks(row.value) : ''))
|
||||
|
||||
function close() {
|
||||
emit('update:show', false)
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function reset() {
|
||||
row.value = null
|
||||
qrPath.value = ''
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
async function load() {
|
||||
const id = String(props.inventoryId || '').trim()
|
||||
if (!id) {
|
||||
uni.showToast({ title: '缺少单据ID', icon: 'none' })
|
||||
close()
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
row.value = null
|
||||
qrPath.value = ''
|
||||
try {
|
||||
let data = props.snapshot && props.snapshot.id === id ? { ...props.snapshot } : null
|
||||
if (!data || data.quantity == null || !data.partnerCompany) {
|
||||
const res = await inventoryApi.get(id)
|
||||
data = res?.data ?? res
|
||||
}
|
||||
if (!data || !data.id) throw new Error('单据不存在')
|
||||
row.value = data
|
||||
await new Promise((r) => setTimeout(r, 50))
|
||||
const proxy = instance?.proxy
|
||||
qrPath.value = await makeQrTempFile({
|
||||
text: data.id,
|
||||
canvasId: 'poundQrCanvas',
|
||||
componentInstance: proxy,
|
||||
size: canvasSize
|
||||
})
|
||||
} catch (e) {
|
||||
uni.showToast({ title: e?.message || '加载二维码失败', icon: 'none' })
|
||||
close()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.show, props.inventoryId],
|
||||
([show]) => {
|
||||
if (show) load()
|
||||
else reset()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pound-qr-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1100;
|
||||
background: rgba(0, 0, 0, 0.92);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.pound-qr-panel {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
max-height: 90vh;
|
||||
overflow: auto;
|
||||
background: #f5f7fa;
|
||||
border-radius: 12px;
|
||||
position: relative;
|
||||
}
|
||||
.pound-qr-panel__title {
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
padding: 14px 12px 6px;
|
||||
}
|
||||
.pound-qr-panel__loading {
|
||||
text-align: center;
|
||||
color: #9ca3af;
|
||||
padding: 40px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.pound-qr-canvas {
|
||||
position: fixed;
|
||||
left: -9999px;
|
||||
top: 0;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.pound-slip-capture-inline {
|
||||
background: #f5f7fa;
|
||||
padding: 4px 8px 12px;
|
||||
margin: 0 6px 10px;
|
||||
}
|
||||
.capture-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
margin: 10px;
|
||||
padding: 6px 8px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.capture-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.capture-row-large {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.capture-row .label {
|
||||
color: #1e293b;
|
||||
min-width: 82px;
|
||||
}
|
||||
.capture-row .value {
|
||||
color: #1e293b;
|
||||
flex: 1;
|
||||
}
|
||||
.capture-row-plate {
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.vehicle-plate {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 10px 32px;
|
||||
box-sizing: border-box;
|
||||
background: #f5d000;
|
||||
border: 3px solid #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
}
|
||||
.vehicle-plate-text {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.vehicle-plate-dot {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
.vehicle-plate-dot-lt {
|
||||
left: 6px;
|
||||
top: 8px;
|
||||
}
|
||||
.vehicle-plate-dot-lb {
|
||||
left: 6px;
|
||||
bottom: 8px;
|
||||
}
|
||||
.vehicle-plate-dot-rt {
|
||||
right: 6px;
|
||||
top: 8px;
|
||||
}
|
||||
.vehicle-plate-dot-rb {
|
||||
right: 6px;
|
||||
bottom: 8px;
|
||||
}
|
||||
.capture-qrcode {
|
||||
text-align: center;
|
||||
}
|
||||
.capture-id {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 6px;
|
||||
word-break: break-all;
|
||||
padding: 0 4px;
|
||||
}
|
||||
.capture-qr-wrap {
|
||||
padding: 8px 0;
|
||||
min-height: 200px;
|
||||
}
|
||||
.capture-qr-img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
}
|
||||
.capture-desc {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
margin-top: 4px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add miniprogram/components/db-ui/db-pound-qrcode-popup.vue
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(miniprogram): add centered pound QR popup component.
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 改造出入库列表页
|
||||
|
||||
**Files:**
|
||||
- Modify: `miniprogram/pages/db/inventory/list.vue`
|
||||
|
||||
- [ ] **Step 1: 替换卡片底部按钮**
|
||||
|
||||
将:
|
||||
|
||||
```vue
|
||||
<view class="db-card__foot" @click.stop>
|
||||
<db-btn size="small" type="primary" plain @click="viewRelated(item)">关联</db-btn>
|
||||
<db-btn size="small" type="primary" @click="genWeighing(item)">生成地磅单</db-btn>
|
||||
</view>
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```vue
|
||||
<view class="db-card__foot" @click.stop>
|
||||
<db-btn size="small" type="primary" plain @click="viewRelated(item)">关联</db-btn>
|
||||
<db-btn size="small" type="primary" @click="openPoundQr(item)">磅单二维码</db-btn>
|
||||
</view>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在关联弹窗后增加磅单弹窗**
|
||||
|
||||
在 `</db-popup>`(关联弹窗)之后增加:
|
||||
|
||||
```vue
|
||||
<db-pound-qrcode-popup
|
||||
:show="poundQrOpen"
|
||||
:inventory-id="poundQrId"
|
||||
:snapshot="poundQrSnapshot"
|
||||
@close="poundQrOpen = false"
|
||||
@update:show="(v) => (poundQrOpen = v)"
|
||||
/>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: script 调整**
|
||||
|
||||
1. 增加 import:
|
||||
|
||||
```js
|
||||
import DbPoundQrcodePopup from '@/components/db-ui/db-pound-qrcode-popup.vue'
|
||||
```
|
||||
|
||||
2. 增加状态:
|
||||
|
||||
```js
|
||||
const poundQrOpen = ref(false)
|
||||
const poundQrId = ref('')
|
||||
const poundQrSnapshot = ref(null)
|
||||
```
|
||||
|
||||
3. 增加方法:
|
||||
|
||||
```js
|
||||
function openPoundQr(item) {
|
||||
poundQrId.value = item?.id || ''
|
||||
poundQrSnapshot.value = item || null
|
||||
poundQrOpen.value = true
|
||||
}
|
||||
```
|
||||
|
||||
4. **删除**整个 `genWeighing` 函数(约 263–277 行)及对 `PAGE_PATHS.dbWeighingDetail` 的仅服务于该函数的用法(若 `PAGE_PATHS` 仍被 `goBack`/`viewItem` 使用则保留 import)。
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add miniprogram/pages/db/inventory/list.vue
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat(miniprogram): replace inventory list weigh-slip create with pound QR.
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 验收与图谱更新
|
||||
|
||||
**Files:**
|
||||
- 无强制代码变更;若改了文件则跑 graphify
|
||||
|
||||
- [ ] **Step 1: 再跑纯函数测试**
|
||||
|
||||
```bash
|
||||
cd miniprogram && node scripts/test_db_pound_qr.mjs
|
||||
```
|
||||
|
||||
Expected: `test_db_pound_qr: ok`
|
||||
|
||||
- [ ] **Step 2: 微信开发者工具手工验收**
|
||||
|
||||
1. 打开「出入库管理」列表:卡片底部仅「关联」「磅单二维码」。
|
||||
2. 点「磅单二维码」:深色遮罩居中弹窗,黄牌车牌、往来单位、物料(含吨)、有备注则显示、日期+单 ID、二维码、「出入库单标识(扫码核验)」;标题为入/出库磅单二维码。
|
||||
3. 扫码内容 = 该行出入库单 `id`。
|
||||
4. 「关联」、点卡片进详情行为不变。
|
||||
5. 断网或屏蔽外网图床时仍能出二维码(本地 canvas)。
|
||||
|
||||
- [ ] **Step 3: 更新知识图谱(若本会话改了代码)**
|
||||
|
||||
```bash
|
||||
./scripts/update-graph.sh frontend
|
||||
```
|
||||
|
||||
或按仓库惯例对 miniprogram 目录执行对应 graphify 更新。
|
||||
|
||||
- [ ] **Step 4: 若 Step 3 有产物,单独 commit**
|
||||
|
||||
```bash
|
||||
git add miniprogram/graphify-out graphify-out
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore: update graphify after inventory pound QR popup.
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
(仅 add 实际变更的图谱路径。)
|
||||
|
||||
---
|
||||
|
||||
## Spec 覆盖自检
|
||||
|
||||
| Spec 要求 | 任务 |
|
||||
|-----------|------|
|
||||
| 去掉「生成地磅单」 | Task 4 |
|
||||
| 「关联」+「磅单二维码」主色 | Task 4 |
|
||||
| 居中深色弹窗、视觉对齐 PoundSlip | Task 3 |
|
||||
| QR 内容 = inventory id | Task 2 + 3 |
|
||||
| 本地生成、禁外网 QR 服务 | Task 2 |
|
||||
| 列表快照 + get 补全 | Task 3 |
|
||||
| 失败 toast 关弹窗 | Task 3 |
|
||||
| 不改后端 | 全计划无 backend 文件 |
|
||||
|
||||
无 TBD / 无「类似 Task N」占位。
|
||||
Reference in new issue
Block a user