docs: add pound QR public link and driver-page entry design
Capture the approved approach for sharing weighbridge slip QR via public inventory-id URLs, multi-line copy text, and a third driver footer button. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
df8c28bd7c
commit
157ad5e29a
2 files changed
+786
No files matched your search
@@ -0,0 +1,599 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>磅单二维码 · 独立模板 & 司机页 Demo</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #10b981;
|
||||
--primary-dark: #059669;
|
||||
--primary-bg: #ecfdf5;
|
||||
--border: #d9e8e2;
|
||||
--text: #1e293b;
|
||||
--muted: #64748b;
|
||||
--page: #f1f5f9;
|
||||
--van-primary: #07c160;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
|
||||
background: var(--page);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
padding: 24px;
|
||||
}
|
||||
h1 { font-size: 22px; margin: 0 0 6px; }
|
||||
.sub { color: var(--muted); font-size: 14px; margin-bottom: 20px; }
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.tab {
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
color: var(--text);
|
||||
}
|
||||
.tab.active {
|
||||
background: var(--primary-bg);
|
||||
border-color: var(--primary);
|
||||
color: var(--primary-dark);
|
||||
font-weight: 600;
|
||||
}
|
||||
.panel {
|
||||
display: none;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.panel.active { display: block; }
|
||||
.panel-head {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.panel-body { padding: 16px; }
|
||||
.hint {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ===== 复制给司机文本 ===== */
|
||||
.copy-box {
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
border-radius: 10px;
|
||||
padding: 14px 16px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 12.5px;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.copy-box .hl {
|
||||
color: #6ee7b7;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ===== 磅单二维码卡片(对齐 DbPoundSlipQrcodeDialog) ===== */
|
||||
.qr-stage {
|
||||
background: rgba(0, 0, 0, 0.92);
|
||||
border-radius: 12px;
|
||||
padding: 18px 14px 22px;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.qr-dialog-title {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
margin: 0 0 10px;
|
||||
padding: 2px 10px 0;
|
||||
}
|
||||
.pound-slip-capture-inline {
|
||||
background: #f5f7fa;
|
||||
padding: 4px 8px;
|
||||
margin: 6px 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.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: var(--text);
|
||||
min-width: 82px;
|
||||
}
|
||||
.capture-row .value {
|
||||
color: var(--text);
|
||||
flex: 1;
|
||||
}
|
||||
.capture-row-plate {
|
||||
width: 100%;
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.vehicle-plate {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 10px 32px;
|
||||
background: #f5d000;
|
||||
border: 3px solid #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
}
|
||||
.vehicle-plate-text {
|
||||
font-size: 32px;
|
||||
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: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.capture-qr-wrap {
|
||||
padding: 8px 0;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.capture-qr-img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
background: #fff;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
.capture-desc {
|
||||
font-size: 9px;
|
||||
font-weight: 500;
|
||||
color: var(--muted);
|
||||
margin-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.url-chip {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: #cbd5e1;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.url-chip code {
|
||||
color: #6ee7b7;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* ===== 司机手机页 mock ===== */
|
||||
.phone {
|
||||
width: 375px;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.12);
|
||||
position: relative;
|
||||
min-height: 640px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.phone-status {
|
||||
height: 28px;
|
||||
background: linear-gradient(135deg, #0a9f6d, #087f57);
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.phone-header {
|
||||
background: linear-gradient(135deg, #0a9f6d, #087f57);
|
||||
color: #fff;
|
||||
padding: 12px 16px 20px;
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
.phone-header h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.phone-header p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.phone-body {
|
||||
flex: 1;
|
||||
padding: 12px 12px 88px;
|
||||
overflow: auto;
|
||||
}
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.04);
|
||||
}
|
||||
.card-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.row .k { color: var(--muted); }
|
||||
.row .v { color: var(--text); font-weight: 500; text-align: right; }
|
||||
.phone-footer {
|
||||
position: absolute;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
background: #fff;
|
||||
border-top: 1px solid #ebedf0;
|
||||
padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 40px;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-ghost {
|
||||
background: #e8f8f0;
|
||||
color: var(--van-primary);
|
||||
}
|
||||
.btn-main {
|
||||
background: var(--van-primary);
|
||||
color: #fff;
|
||||
}
|
||||
.btn-qr {
|
||||
background: #fff7e8;
|
||||
color: #b45309;
|
||||
border: 1px solid #f5d78e;
|
||||
}
|
||||
.btn-row.three .btn { font-size: 13px; padding: 0 4px; }
|
||||
|
||||
/* overlay popup inside phone */
|
||||
.overlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.92);
|
||||
z-index: 20;
|
||||
overflow: auto;
|
||||
padding: 40px 12px 24px;
|
||||
}
|
||||
.overlay.show { display: block; }
|
||||
.overlay .close-x {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 14px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: rgba(255,255,255,0.15);
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--primary-bg);
|
||||
color: var(--primary-dark);
|
||||
font-weight: 600;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
margin-top: 10px;
|
||||
}
|
||||
.legend span::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 2px;
|
||||
margin-right: 6px;
|
||||
vertical-align: -1px;
|
||||
}
|
||||
.legend .new::before { background: #fbbf24; }
|
||||
.legend .exist::before { background: var(--van-primary); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>磅单二维码 · 独立公共模板 & 司机页 Demo</h1>
|
||||
<p class="sub">
|
||||
对齐现有「复制磅单二维码」弹窗样式(黄牌 + 往来单位/物料 + 出入库单 ID 二维码)。
|
||||
本页演示:① 独立 URL 模板页 ② 司机填写页底部新增按钮 + 弹窗 ③ 复制给司机文案追加链接。
|
||||
</p>
|
||||
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="tpl" type="button">1. 独立模板页</button>
|
||||
<button class="tab" data-tab="driver" type="button">2. 司机页 + 弹窗</button>
|
||||
<button class="tab" data-tab="copy" type="button">3. 复制给司机文案</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab 1: Independent template -->
|
||||
<section id="panel-tpl" class="panel active">
|
||||
<div class="panel-head">
|
||||
独立公共页
|
||||
<span class="badge">样式与管理端弹窗一致</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="hint">
|
||||
拟议路由示例:
|
||||
<code>#/public/pl/pound-qrcode/:inventoryId</code>
|
||||
(或 token 版,待需求确认)。传入出入库单 id 后渲染下方界面。
|
||||
</p>
|
||||
<div class="qr-stage">
|
||||
<div class="qr-dialog-title">出库磅单二维码</div>
|
||||
<div class="pound-slip-capture-inline">
|
||||
<div class="capture-card">
|
||||
<div class="capture-row capture-row-plate">
|
||||
<div class="vehicle-plate">
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-lt"></span>
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-lb"></span>
|
||||
<span class="vehicle-plate-text">皖AM8516</span>
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-rt"></span>
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-rb"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="capture-row capture-row-large">
|
||||
<span class="label">往来单位:</span>
|
||||
<span class="value">合肥德泽化工科技有限公司</span>
|
||||
</div>
|
||||
<div class="capture-row capture-row-large">
|
||||
<span class="label">物料名称:</span>
|
||||
<span class="value">30% 工业硫酸(30%)(10.000 吨)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="capture-card capture-qrcode">
|
||||
<div class="capture-id">2026-09-12 出库单ID:a1b2c3d4-e5f6-7890</div>
|
||||
<div class="capture-qr-wrap">
|
||||
<img
|
||||
class="capture-qr-img"
|
||||
alt="二维码"
|
||||
src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=a1b2c3d4-e5f6-7890"
|
||||
/>
|
||||
</div>
|
||||
<div class="capture-desc">出入库单标识(扫码核验)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="url-chip">
|
||||
链接示例:<br />
|
||||
<code>http://host/#/public/pl/pound-qrcode/a1b2c3d4-e5f6-7890</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tab 2: Driver page -->
|
||||
<section id="panel-driver" class="panel">
|
||||
<div class="panel-head">
|
||||
司机链接填写页底部
|
||||
<span class="badge">新增「磅单二维码」</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="hint">
|
||||
当前底栏为「运输记录 / 保存回执」。本 Demo 增加第三颗「磅单二维码」;点击后深色遮罩弹层,内容与上方模板同款。
|
||||
(若你口头说的「现场记录」指现场人员页,可另开;本 Demo 按司机页 <code>/public/pl/transport/:token</code>。)
|
||||
</p>
|
||||
<div class="phone" id="driverPhone">
|
||||
<div class="phone-status"><span>9:05</span><span>LTE</span></div>
|
||||
<div class="phone-header">
|
||||
<h2>皖AM8516 · 运输计划</h2>
|
||||
<p>PO20260912008 · 2026-09-12</p>
|
||||
</div>
|
||||
<div class="phone-body">
|
||||
<div class="card">
|
||||
<div class="card-title">装货</div>
|
||||
<div class="row"><span class="k">单位</span><span class="v">安庆市山峡化工有限公司</span></div>
|
||||
<div class="row"><span class="k">产品</span><span class="v">30% 工业硫酸,10 吨</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">送货 #1</div>
|
||||
<div class="row"><span class="k">单位</span><span class="v">合肥德泽化工科技有限公司</span></div>
|
||||
<div class="row"><span class="k">产品</span><span class="v">30% 工业硫酸,10 吨</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">备注</div>
|
||||
<div class="row"><span class="k">填写运输备注…</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="phone-footer">
|
||||
<div class="btn-row three">
|
||||
<button class="btn btn-ghost" type="button">运输记录</button>
|
||||
<button class="btn btn-qr" type="button" onclick="openQrOverlay()">磅单二维码</button>
|
||||
<button class="btn btn-main" type="button">保存回执</button>
|
||||
</div>
|
||||
<div class="legend">
|
||||
<span class="exist">已有按钮</span>
|
||||
<span class="new">本次新增</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overlay" id="qrOverlay">
|
||||
<button class="close-x" type="button" onclick="closeQrOverlay()" aria-label="关闭">×</button>
|
||||
<div class="qr-dialog-title">出库磅单二维码</div>
|
||||
<div class="pound-slip-capture-inline">
|
||||
<div class="capture-card">
|
||||
<div class="capture-row capture-row-plate">
|
||||
<div class="vehicle-plate">
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-lt"></span>
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-lb"></span>
|
||||
<span class="vehicle-plate-text">皖AM8516</span>
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-rt"></span>
|
||||
<span class="vehicle-plate-dot vehicle-plate-dot-rb"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="capture-row capture-row-large">
|
||||
<span class="label">往来单位:</span>
|
||||
<span class="value">合肥德泽化工科技有限公司</span>
|
||||
</div>
|
||||
<div class="capture-row capture-row-large">
|
||||
<span class="label">物料名称:</span>
|
||||
<span class="value">30% 工业硫酸(30%)(10.000 吨)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="capture-card capture-qrcode">
|
||||
<div class="capture-id">2026-09-12 出库单ID:a1b2c3d4-e5f6-7890</div>
|
||||
<div class="capture-qr-wrap">
|
||||
<img
|
||||
class="capture-qr-img"
|
||||
alt="二维码"
|
||||
src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=a1b2c3d4-e5f6-7890"
|
||||
/>
|
||||
</div>
|
||||
<div class="capture-desc">出入库单标识(扫码核验)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Tab 3: Copy text -->
|
||||
<section id="panel-copy" class="panel">
|
||||
<div class="panel-head">复制给司机 · 文案增量</div>
|
||||
<div class="panel-body">
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
<p class="hint">现状(末行仅司机链接)</p>
|
||||
<div class="copy-box">【皖AM8516 - 运输计划】
|
||||
计划号:PO20260912008
|
||||
车牌:皖AM8516
|
||||
时间:2026-09-12
|
||||
|
||||
【装货 · 安庆市山峡化工有限公司】
|
||||
时间:2026-09-12
|
||||
导航:https://www.amap.com/search?query=装货
|
||||
产品:30% 工业硫酸(30%),10.000吨
|
||||
|
||||
【送货 #1 · 合肥德泽化工科技有限公司】
|
||||
时间:2026-09-12
|
||||
导航:https://www.amap.com/search?query=送货%20%231
|
||||
产品:30% 工业硫酸(30%),10.000吨
|
||||
|
||||
司机链接:http://121.43.228.102:9911/#/public/pl/transport/Ve-rB5Q3…</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="hint">目标(追加磅单二维码链接)</p>
|
||||
<div class="copy-box">【皖AM8516 - 运输计划】
|
||||
计划号:PO20260912008
|
||||
车牌:皖AM8516
|
||||
时间:2026-09-12
|
||||
|
||||
【装货 · 安庆市山峡化工有限公司】
|
||||
时间:2026-09-12
|
||||
导航:https://www.amap.com/search?query=装货
|
||||
产品:30% 工业硫酸(30%),10.000吨
|
||||
|
||||
【送货 #1 · 合肥德泽化工科技有限公司】
|
||||
时间:2026-09-12
|
||||
导航:https://www.amap.com/search?query=送货%20%231
|
||||
产品:30% 工业硫酸(30%),10.000吨
|
||||
|
||||
司机链接:http://121.43.228.102:9911/#/public/pl/transport/Ve-rB5Q3…
|
||||
<span class="hl">磅单二维码链接:http://121.43.228.102:9911/#/public/pl/pound-qrcode/a1b2c3d4-e5f6-7890</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint" style="margin-top:14px">
|
||||
多张地磅出入库单时:文案可列多行链接,或先弹选择器再复制对应链接(与现有「选择磅单二维码」一致)。待确认。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
document.querySelectorAll('.tab').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.tab').forEach((t) => t.classList.remove('active'))
|
||||
document.querySelectorAll('.panel').forEach((p) => p.classList.remove('active'))
|
||||
btn.classList.add('active')
|
||||
document.getElementById('panel-' + btn.dataset.tab).classList.add('active')
|
||||
})
|
||||
})
|
||||
|
||||
function openQrOverlay() {
|
||||
document.getElementById('qrOverlay').classList.add('show')
|
||||
}
|
||||
function closeQrOverlay() {
|
||||
document.getElementById('qrOverlay').classList.remove('show')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,187 @@
|
||||
# 磅单二维码公开链接与司机页入口 — 设计文档
|
||||
|
||||
> **日期**: 2026-09-13
|
||||
> **状态**: 待实现
|
||||
> **方案**: 方案 1 — 抽公共展示组件 + 公开只读 API + 独立路由 + 司机页弹层
|
||||
> **Demo**: `docs/superpowers/demos/2026-09-13-pound-qrcode-public-demo.html`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
1. 「复制给司机」文案末尾追加**磅单二维码链接**(可多行);链接打开独立公共页,样式与管理端「复制磅单二维码」弹窗一致。
|
||||
2. 司机填写页(`/#/public/pl/transport/:token`)底栏增加「磅单二维码」;点击后弹层展示同款界面(多单先选再弹)。
|
||||
|
||||
## 2. 已确认决策
|
||||
|
||||
| # | 项 | 决策 |
|
||||
|---|-----|------|
|
||||
| D1 | 多单 · 复制文案 | 多行链接;格式见 D4 |
|
||||
| D2 | 多单 · 司机页 | 先选一张再弹窗(ActionSheet) |
|
||||
| D3 | 公开 URL | `#/public/pl/pound-qrcode/{inventoryId}`,免登录按 id 查公开字段 |
|
||||
| D4 | 文案格式 | 先一行标题 `磅单二维码链接:`,下列 `{业务类型} · {往来单位}:{url}` |
|
||||
| D5 | 无地磅关联单 | 复制不加磅单段;司机页隐藏第三钮 |
|
||||
| D6 | 底栏布局 | 同一行三钮:`运输记录 \| 磅单二维码 \| 保存回执` |
|
||||
| D7 | 实现路径 | 方案 1(共享 View + 公开 API + 扩展司机页 payload) |
|
||||
| D8 | 作废/已完成单据 | 仍允许只读展示二维码(核验场景) |
|
||||
|
||||
## 3. 非目标
|
||||
|
||||
- 不做公开 URL token 化 / 访问限流(已知 id 枚举风险,后续可单独立项)
|
||||
- 不改管理端「复制磅单二维码」图片复制主流程(仍弹窗复制图)
|
||||
- 不改现场页 `transport-site` 底栏
|
||||
- 不做小程序端对本能力的移植
|
||||
- 不扩展公开接口返回现场回执、金额、租户号等非展示字段
|
||||
|
||||
## 4. 架构
|
||||
|
||||
```text
|
||||
管理端「复制给司机」
|
||||
→ arrange / inventoryLinks(已有)
|
||||
→ buildPlanSummaryText 追加磅单链接块
|
||||
→ 剪贴板
|
||||
|
||||
独立公开页
|
||||
/#/public/pl/pound-qrcode/:inventoryId
|
||||
→ GET /api/public/db/inventory/{id}/pound-qr
|
||||
→ PoundSlipQrcodeView
|
||||
|
||||
司机页
|
||||
/#/public/pl/transport/:token
|
||||
→ GET /api/public/pl/transport/{token} 含 poundQrcodeItems[]
|
||||
→ 有 items 显示第三钮
|
||||
→ 1 条直接弹层 / 多条 ActionSheet
|
||||
→ 同 pound-qr API → PoundSlipQrcodeView
|
||||
|
||||
管理端「复制磅单二维码」
|
||||
→ DbPoundSlipQrcodeDialog(鉴权 get)
|
||||
→ 内嵌同一 PoundSlipQrcodeView(样式不漂移)
|
||||
```
|
||||
|
||||
### 4.1 组件边界
|
||||
|
||||
| 单元 | 职责 | 依赖 |
|
||||
|------|------|------|
|
||||
| `PoundSlipQrcodeView` | 纯展示:黄牌、往来单位、物料、备注、日期+单号、二维码图、说明 | 已解析的公开字段 + 图片 URL;样式类 `.pound-slip-capture-inline` |
|
||||
| `DbPoundSlipQrcodeDialog` | 管理端弹窗壳、鉴权加载、复制图片 | View + `inventoryApi.get` |
|
||||
| 公共页 `pound-qrcode/index.vue` | 路由 id → 公开 API → View / 空态 | `publicPlGet` |
|
||||
| 司机页弹层 | `van-popup` 深色底 + View;多选 ActionSheet | `poundQrcodeItems` + 公开 API |
|
||||
|
||||
## 5. 接口
|
||||
|
||||
### 5.1 新增 `GET /api/public/db/inventory/{inventoryId}/pound-qr`
|
||||
|
||||
- 挂在现有 `public/db/inventory` 路由模块;该前缀已在 `API_ENCRYPT_EXCLUDE_PATHS`。
|
||||
- 条件:`id` 匹配且 `del_flag=0`;**不**做登录/租户校验。
|
||||
- 响应(camelCase)仅含:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 出入库单 id(二维码内容) |
|
||||
| `date` | 业务日期展示串 |
|
||||
| `businessType` | `入库` / `出库` |
|
||||
| `vehicleNo` | 车牌 |
|
||||
| `partnerCompany` | 往来单位 |
|
||||
| `product` | 物料名 |
|
||||
| `specification` | 规格(可空) |
|
||||
| `quantity` | 数量 |
|
||||
| `quantityUnit` | 单位文案,缺省「吨」 |
|
||||
| `remarks` | 备注(可空;空则不渲染行) |
|
||||
|
||||
- 404:不存在或已删。
|
||||
- Schema 建议:`PublicPoundQrRead`(`app/schemas/db/public_inventory.py`)。
|
||||
|
||||
### 5.2 扩展司机页 `PublicTransportPage`
|
||||
|
||||
增加:
|
||||
|
||||
```ts
|
||||
poundQrcodeItems?: {
|
||||
inventoryId: string
|
||||
businessType: string
|
||||
partnerCompany: string
|
||||
label: string // 「出库 · 合肥德泽化工科技有限公司」
|
||||
}[]
|
||||
```
|
||||
|
||||
来源:运输对应计划下有效 `PlTransportDbLink` → `DbInboundOutbound`(`del_flag=0`)。无则 `[]`。
|
||||
|
||||
## 6. 前端交互
|
||||
|
||||
### 6.1 路由
|
||||
|
||||
`staticRoutes.ts` 增加:
|
||||
|
||||
- path: `/public/pl/pound-qrcode/:inventoryId`
|
||||
- name: `PublicPlPoundQrcode`
|
||||
- meta: `{ title: '磅单二维码', isHideTab: true, isPublicMobile: true }`
|
||||
- 加载成功后可将 document/title 改为「入库/出库磅单二维码」。
|
||||
|
||||
### 6.2 司机页底栏
|
||||
|
||||
- `poundQrcodeItems.length > 0` → 三钮:`运输记录 | 磅单二维码 | 保存回执`。
|
||||
- 否则保持现有两钮。
|
||||
- 计划作废/归档导致仅「运输记录」时:若仍有 items,**仍显示「磅单二维码」**(只读核验);「保存回执」按原规则隐藏即可。
|
||||
- 多条:`showActionSheet({ actions: items.map(i => ({ name: i.label, … })) })`。
|
||||
|
||||
### 6.3 复制给司机文案
|
||||
|
||||
改 `web-admin/src/utils/pl-driver-summary.ts` 的 `buildPlanSummaryText`:
|
||||
|
||||
- 新增可选参数 `poundQrcodeLinks?: { label: string; url: string }[]`。
|
||||
- 有数据时,在「司机链接」行之后追加:
|
||||
|
||||
```text
|
||||
磅单二维码链接:
|
||||
出库 · 合肥德泽化工科技有限公司:http://host/#/public/pl/pound-qrcode/{id}
|
||||
入库 · 安庆市山峡化工有限公司:http://host/#/public/pl/pound-qrcode/{id}
|
||||
```
|
||||
|
||||
- `url` = `{window.location.origin}/#/public/pl/pound-qrcode/{inventoryId}`。
|
||||
- 调用方(`usePlPlanArrangeActions` / `pl-plan-view-tabs` 等)从已有 `dibangInventoryRecords` 组装 links;无记录则不传。
|
||||
|
||||
### 6.4 二维码图片
|
||||
|
||||
与现 `DbPoundSlipQrcodeDialog` 一致:对 `id` 编码生成二维码图(当前为 qrserver);产品标签拼接规则(规格后缀去重)复用现函数。
|
||||
|
||||
## 7. 错误处理
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 公开页 id 无效/已删 | 404 → 「链接无效或单据不存在」 |
|
||||
| 公开页网络失败 | 「加载失败,请稍后重试」 |
|
||||
| 司机弹层加载失败 | Toast「加载二维码失败」 |
|
||||
| items 为空 | 不渲染第三钮 |
|
||||
| 无 dibang 关联复制 | 文案无磅单段 |
|
||||
| 二维码图 CDN 失败 | 公共页保留错误提示;管理端保持现有提示行为 |
|
||||
|
||||
## 8. 测试要点
|
||||
|
||||
1. 单张地磅单:复制文案含标题 + 一行 label:url;打开样式与管理端弹窗一致。
|
||||
2. 多张:文案多行;司机页 ActionSheet → 弹层对应正确单。
|
||||
3. 未勾选生成地磅单:无磅单段、无第三钮。
|
||||
4. 管理端「复制磅单二维码」复制图片仍可用,样式不回退。
|
||||
5. 计划作废后:有 items 时可只读打开二维码;保存回执按原规则。
|
||||
6. 删单后旧链接:公开页 404 文案。
|
||||
|
||||
## 9. 主要改动文件(预估)
|
||||
|
||||
**Backend**
|
||||
|
||||
- `app/api/db/public_inventory.py` — 新增 `pound-qr`
|
||||
- `app/schemas/db/public_inventory.py` — `PublicPoundQrRead`
|
||||
- `app/api/pl/public_transport.py` — `_build_public_page` 填 `poundQrcodeItems`
|
||||
- `app/schemas/pl/...` — `PublicTransportPageRead` 字段
|
||||
|
||||
**web-admin**
|
||||
|
||||
- `components/business/db/PoundSlipQrcodeView.vue`(新建)
|
||||
- `components/business/db/DbPoundSlipQrcodeDialog.vue`(改用 View)
|
||||
- `views/public/pl/pound-qrcode/index.vue`(新建)
|
||||
- `router/routes/staticRoutes.ts`
|
||||
- `views/public/pl/transport/index.vue`
|
||||
- `api/pl/public-transport.ts`、`api/db/public-inventory.ts`(或并列 pound-qr API)
|
||||
- `utils/pl-driver-summary.ts`
|
||||
- `composables/usePlPlanArrangeActions.ts`(及复制入口调用链)
|
||||
|
||||
## 10. 验收标准
|
||||
|
||||
司机通过复制文案中的磅单链接,或在司机页点击「磅单二维码」,看到的界面与后台「复制磅单二维码」弹窗同款;多单可选;无关联单时链接与按钮均不可见。
|
||||
Reference in new issue
Block a user