Files
pad_scanner/docs/single-code-boxing-ui-states.html
2026-05-18 14:59:53 +08:00

2105 lines
68 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单码装箱模式 - UI 状态全览</title>
<style>
:root {
--blue: #1565C0;
--green: #2E7D32;
--red: #C62828;
--amber: #F57F1F;
--orange: #E65100;
--yellow: #F9A825;
--grey50: #FAFAFA;
--grey100: #F5F5F5;
--grey200: #EEEEEE;
--grey300: #E0E0E0;
--grey400: #BDBDBD;
--grey500: #9E9E9E;
--grey600: #757575;
--grey700: #616161;
--grey800: #424242;
--radius: 8px;
--device-w: 360px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: #ECEFF1;
color: #212121;
padding: 24px;
}
h1 {
text-align: center;
font-size: 24px;
margin-bottom: 8px;
}
.subtitle {
text-align: center;
font-size: 13px;
color: var(--grey600);
margin-bottom: 32px;
}
.subtitle code {
background: #CFD8DC;
padding: 1px 5px;
border-radius: 3px;
font-size: 12px;
}
/* ── Section headers ── */
.section {
margin-bottom: 40px;
}
.section-title {
font-size: 18px;
font-weight: 700;
padding-bottom: 8px;
border-bottom: 2px solid var(--blue);
margin-bottom: 20px;
}
.section-desc {
font-size: 13px;
color: var(--grey600);
margin-bottom: 16px;
line-height: 1.6;
}
/* ── Phone mockup ── */
.phone-row {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-bottom: 24px;
}
.phone-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
overflow: hidden;
width: var(--device-w);
}
.phone-label {
background: #37474F;
color: #fff;
padding: 8px 12px;
font-size: 12px;
font-weight: 600;
display: flex;
align-items: center;
gap: 6px;
}
.phone-label .tag {
background: rgba(255,255,255,0.2);
padding: 1px 6px;
border-radius: 3px;
font-size: 10px;
}
.phone-screen {
width: 100%;
border-top: 1px solid var(--grey200);
}
/* ── App chrome ── */
.app-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px 12px 16px;
background: #fff;
border-bottom: 1px solid var(--grey200);
}
.app-bar h2 {
font-size: 18px;
font-weight: 500;
}
.mode-pill {
background: var(--blue);
color: #fff;
font-size: 11px;
font-weight: 700;
padding: 3px 9px;
border-radius: 12px;
display: flex;
align-items: center;
gap: 4px;
}
.mode-pill .pill-icon {
font-size: 14px;
}
.mode-pill .pill-key {
background: rgba(255,255,255,0.22);
padding: 0 4px;
border-radius: 4px;
font-size: 10px;
}
/* ── Banners ── */
.banner {
padding: 6px 12px;
font-size: 12px;
display: flex;
align-items: center;
gap: 6px;
}
.banner.orange {
background: #FFF3E0;
color: #E65100;
}
.banner.amber {
background: #FFF8E1;
color: #E65100;
}
.banner.blue {
background: #E3F2FD;
color: #1565C0;
}
.banner.red {
background: #FFEBEE;
color: #C62828;
}
.spinner {
width: 12px; height: 12px;
border: 1.5px solid #E65100;
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.8s linear infinite;
display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* ── Scan area ── */
.scan-area {
margin: 12px 12px 0;
padding: 14px 16px;
border-radius: var(--radius);
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.scan-area.waiting {
background: var(--grey100);
color: var(--grey500);
justify-content: center;
}
.scan-area.scanned {
background: #fff;
border: 2px solid var(--green);
color: #212121;
}
.scan-area .scan-label {
font-size: 18px;
font-weight: 700;
flex: 1;
}
.scan-area .scan-tag {
font-size: 12px;
color: var(--green);
}
/* ── Info area ── */
.info-area {
margin: 0 12px;
padding-top: 10px;
}
.info-main {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 10px;
}
.info-main .paichan {
flex: 5;
font-size: 20px;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.info-main .work-order {
flex: 4;
font-size: 18px;
font-weight: 700;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.info-main .qty {
flex: 3;
font-size: 18px;
font-weight: 700;
text-align: right;
}
.info-meta {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 13px;
}
.info-meta .meta-left {
display: flex;
gap: 16px;
color: var(--grey700);
}
.info-meta .meta-left .meta-label {
color: var(--grey600);
}
.info-meta .meta-left .meta-value {
font-weight: 600;
}
.info-meta .meta-left .max-qty {
color: var(--amber);
}
.detail-link {
font-size: 13px;
color: var(--blue);
background: none;
border: none;
cursor: pointer;
padding: 0;
}
.detail-link.disabled {
color: var(--grey400);
pointer-events: none;
}
.info-placeholder {
color: var(--grey400);
}
/* ── Divider ── */
.section-divider {
border: none;
border-top: 1px solid var(--grey300);
margin: 12px 0;
}
/* ── Assigned boxes ── */
.assigned-section {
margin: 0 12px;
}
.assigned-label {
font-size: 13px;
color: var(--grey600);
margin-bottom: 6px;
}
.assigned-row {
margin-bottom: 6px;
padding: 6px 8px;
border-radius: 6px;
background: var(--grey50);
border: 1px solid var(--grey300);
}
.assigned-row.editing {
background: #FFF3E0;
border-color: var(--amber);
}
.assigned-row .ar-top {
display: flex;
align-items: center;
gap: 4px;
}
.assigned-row .ar-box-no {
flex: 1;
font-size: 13px;
font-weight: 600;
}
.assigned-row .ar-qty {
font-size: 13px;
}
.assigned-row .ar-actions {
display: flex;
gap: 0;
}
.assigned-row .ar-actions .ar-btn {
width: 28px; height: 28px;
display: flex; align-items: center; justify-content: center;
background: none; border: none; cursor: pointer;
font-size: 16px; padding: 0;
}
.assigned-row .ar-actions .ar-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.delete-confirm {
margin-top: 4px;
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.delete-confirm .btn-yes {
background: none; border: none;
color: var(--red); cursor: pointer;
font-weight: 600;
}
.delete-confirm .btn-no {
background: none; border: none;
color: var(--grey600); cursor: pointer;
}
/* ── Input area ── */
.input-area {
margin: 0 12px 12px;
display: flex;
gap: 8px;
align-items: flex-end;
}
.input-group {
flex: 1;
}
.input-group label {
display: block;
font-size: 13px;
color: var(--grey600);
margin-bottom: 4px;
}
.input-group input {
width: 100%;
height: 40px;
padding: 0 10px;
border: 1px solid var(--grey400);
border-radius: 0;
font-size: 16px;
outline: none;
}
.input-group input:focus {
border-color: var(--blue);
box-shadow: 0 0 0 1px var(--blue);
}
.input-group input.disabled {
background: var(--grey100);
color: var(--grey400);
border-color: var(--grey300);
}
.input-group input.warn-amber {
border-color: var(--amber);
}
.input-group input.warn-red {
border-color: var(--red);
}
.btn-confirm {
height: 40px;
padding: 0 16px;
background: var(--blue);
color: #fff;
border: none;
border-radius: 4px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
}
.btn-confirm:disabled {
background: var(--grey300);
color: var(--grey600);
cursor: not-allowed;
}
.btn-confirm.saving {
background: var(--blue);
}
/* ── Action buttons ── */
.action-bar {
display: flex;
gap: 12px;
padding: 4px 12px;
}
.btn-back {
flex: 1;
height: 40px;
background: #fff;
border: 1px solid var(--grey400);
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
.btn-cancel {
flex: 1;
height: 40px;
background: var(--blue);
color: #fff;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
/* ── Status bar ── */
.status-bar {
display: flex;
align-items: center;
gap: 7px;
padding: 6px 12px;
background: var(--grey200);
font-size: 12px;
}
.status-dot {
width: 7px; height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
.status-dot.blue { background: var(--blue); }
.status-dot.green { background: var(--green); }
.status-dot.red { background: var(--red); }
.status-dot.orange { background: var(--orange); }
.status-dot.yellow { background: var(--yellow); }
.status-dot.amber { background: var(--amber); }
/* ── State explanation ── */
.state-note {
background: #fff;
border-left: 3px solid var(--blue);
padding: 10px 14px;
margin: 12px 0 20px;
font-size: 13px;
line-height: 1.6;
color: var(--grey700);
}
.state-note strong {
color: #212121;
}
.state-note code {
background: var(--grey100);
padding: 0 4px;
border-radius: 2px;
font-size: 12px;
}
/* ── Logic table ── */
.logic-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
margin: 12px 0 20px;
background: #fff;
}
.logic-table th {
background: var(--grey100);
text-align: left;
padding: 8px 10px;
border: 1px solid var(--grey300);
font-weight: 600;
}
.logic-table td {
padding: 6px 10px;
border: 1px solid var(--grey300);
vertical-align: top;
}
/* ── Diagram ── */
.flow-diagram {
background: #fff;
border: 1px solid var(--grey300);
border-radius: 8px;
padding: 20px;
margin: 12px 0 20px;
font-size: 13px;
line-height: 2;
}
.flow-node {
display: inline-block;
padding: 4px 10px;
border-radius: 4px;
font-weight: 600;
font-size: 12px;
}
.flow-node.idle { background: #E3F2FD; color: var(--blue); }
.flow-node.scanned { background: #E8F5E9; color: var(--green); }
.flow-node.submit { background: #FFF3E0; color: var(--orange); }
.flow-node.error { background: #FFEBEE; color: var(--red); }
.flow-node.success { background: #E8F5E9; color: var(--green); }
.arrow { color: var(--grey500); margin: 0 4px; }
/* ── Legend ── */
.legend {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin: 8px 0 16px;
font-size: 12px;
}
.legend-item {
display: flex;
align-items: center;
gap: 4px;
}
.legend-dot {
width: 10px; height: 10px;
border-radius: 50%;
}
</style>
</head>
<body>
<h1>单码装箱模式 — UI 状态全览</h1>
<p class="subtitle">
源码:<code>lib/pages/boxing_page.dart</code><em>_BoxingPageState</em><br>
覆盖 <strong>BoxingMode.singleCode</strong> 下所有 UI 状态、状态机流转与交互细节
</p>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 0. 颜色图例 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">0. 状态栏颜色图例</div>
<div class="legend">
<div class="legend-item"><div class="legend-dot" style="background:var(--blue)"></div> 蓝色 — 等待/空闲</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--green)"></div> 绿色 — 成功</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--red)"></div> 红色 — 错误(无效码/数量超限/提交失败)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--orange)"></div> 橙色 — 进行中(提交中/同步数据)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--amber)"></div> 琥珀 — 警告(箱号重复/提交重复)</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--yellow)"></div> 黄色 — 网络异常</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 1. 状态机 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">1. 状态机_Phase 枚举)</div>
<div class="flow-diagram">
<span class="flow-node idle">waiting<br>等待扫码</span>
<span class="arrow">→ 扫码成功 →</span>
<span class="flow-node scanned">scanned<br>已扫码,已自动填充</span>
<span class="arrow">→ 用户点击确认 →</span>
<span class="flow-node submit">submitting<br>正在提交…</span>
<span class="arrow"></span><br><br>
&nbsp;&nbsp;成功,尚有剩余 →
<span class="flow-node success">回到 scanned箱号+1数量=剩余)</span>
<span class="arrow"> / </span>
<span class="flow-node success">回到 waiting全部装箱完毕</span><br>
&nbsp;&nbsp;失败 →
<span class="flow-node error">停留在 scanned显示错误覆盖</span>
</div>
<table class="logic-table">
<thead>
<tr>
<th>_Phase</th>
<th>触发条件</th>
<th>主要 UI 效果</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>waiting</code></td>
<td>初始化 / 重置 / 全部装箱完毕</td>
<td>扫码区灰色占位;信息区灰色 "--";输入区禁用</td>
</tr>
<tr>
<td><code>scanned</code></td>
<td>扫码成功且 API 返回有效数据</td>
<td>扫码区绿色边框+总排号;信息区展示数据;输入区可编辑,自动填充箱号/数量</td>
</tr>
<tr>
<td><em>没有 submitted</em></td>
<td></td>
<td>单码模式中,提交成功后直接进入新的 scanned 或 resetting waiting不经 submitted 阶段</td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 2. UI 状态可视化 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">2. UI 状态可视化</div>
<p class="section-desc">每个手机模型展示一个具体状态。底部状态栏颜色参考上方图例。</p>
<!-- ── 2.1 等待扫码 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.1 等待扫码 (waiting) <span class="tag">初始状态</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area waiting">
📷 请扫描执行卡二维码
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan info-placeholder">--</span>
<span class="work-order info-placeholder">--</span>
<span class="qty info-placeholder">--</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="info-placeholder">--</span></span>
<span><span class="meta-label">最大箱号:</span><span class="info-placeholder">--</span></span>
</div>
<button class="detail-link disabled">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input class="disabled" disabled value="">
</div>
<div class="input-group">
<label>数量</label>
<input class="disabled" disabled value="">
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot blue"></div>
等待扫码
</div>
</div>
</div>
<!-- ── 2.2 已扫码-正常可装箱 ── -->
<div class="phone-card">
<div class="phone-label">2.2 已扫码-正常 (scanned, can submit) <span class="tag">最常见状态</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">5</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="6">
</div>
<div class="input-group">
<label>数量</label>
<input value="150" style="border-color:var(--blue);box-shadow:0 0 0 1px var(--blue);">
</div>
<button class="btn-confirm">确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot blue"></div>
数量已填入,请确认或修改
</div>
</div>
</div>
</div>
<!-- ── 2.3 已扫码+已分配列表 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.3 已扫码+已分配 (with assigned boxes)</div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">3箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">7</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="assigned-section">
<div class="assigned-label">已分配</div>
<div class="assigned-row">
<div class="ar-top">
<span class="ar-box-no">3号箱</span>
<span class="ar-qty">数量 50</span>
<div class="ar-actions">
<button class="ar-btn" title="修改">✏️</button>
<button class="ar-btn" title="删除">🗑️</button>
</div>
</div>
</div>
<div class="assigned-row">
<div class="ar-top">
<span class="ar-box-no">7号箱</span>
<span class="ar-qty">数量 100</span>
<div class="ar-actions">
<button class="ar-btn" title="修改">✏️</button>
<button class="ar-btn" title="删除">🗑️</button>
</div>
</div>
</div>
</div>
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="8">
</div>
<div class="input-group">
<label>数量</label>
<input value="50" style="border-color:var(--blue);box-shadow:0 0 0 1px var(--blue);">
</div>
<button class="btn-confirm">确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot blue"></div>
数量已填入,请确认或修改
</div>
</div>
</div>
<!-- ── 2.4 已分配行 - 编辑状态 ── -->
<div class="phone-card">
<div class="phone-label">2.4 已分配行 - 编辑中 (editing assigned) <span class="tag">行内编辑</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">3箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">7</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="assigned-section">
<div class="assigned-label">已分配</div>
<div class="assigned-row">
<div class="ar-top">
<span class="ar-box-no">3号箱</span>
<span class="ar-qty">数量 50</span>
<div class="ar-actions">
<button class="ar-btn" title="修改">✏️</button>
<button class="ar-btn" title="删除">🗑️</button>
</div>
</div>
</div>
<div class="assigned-row editing">
<div class="ar-top">
<span class="ar-box-no">7号箱</span>
<input style="width:56px;height:32px;font-size:13px;text-align:center;padding:0 4px;border:1px solid var(--amber);border-radius:4px;" value="80">
<div class="ar-actions">
<button class="ar-btn" title="保存" style="color:var(--green);"></button>
<button class="ar-btn" title="取消" style="color:var(--grey600);"></button>
</div>
</div>
</div>
</div>
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="8">
</div>
<div class="input-group">
<label>数量</label>
<input value="50">
</div>
<button class="btn-confirm">保存</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
<button class="btn-cancel" style="flex:1;background:#fff;color:#212121;border:1px solid var(--grey400);">取消</button>
</div>
<div class="status-bar">
<div class="status-dot blue"></div>
数量已填入,请确认或修改
</div>
</div>
</div>
</div>
<!-- ── 2.5 已分配行 - 删除待确认 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.5 已分配行 - 删除确认 (delete confirm) <span class="tag">行内确认</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">5</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="assigned-section">
<div class="assigned-label">已分配</div>
<div class="assigned-row">
<div class="ar-top">
<span class="ar-box-no">3号箱</span>
<span class="ar-qty">数量 50</span>
<div class="ar-actions">
<button class="ar-btn" title="修改">✏️</button>
<button class="ar-btn" title="删除">🗑️</button>
</div>
</div>
</div>
<div class="assigned-row">
<div class="ar-top">
<span class="ar-box-no">5号箱</span>
<span class="ar-qty">数量 100</span>
<div class="ar-actions">
<button class="ar-btn" title="修改">✏️</button>
<button class="ar-btn" title="删除">🗑️</button>
</div>
</div>
<div class="delete-confirm">
<span>确认删除此条记录?</span>
<button class="btn-yes"></button>
<button class="btn-no"></button>
</div>
</div>
</div>
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="6">
</div>
<div class="input-group">
<label>数量</label>
<input value="50">
</div>
<button class="btn-confirm">确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot blue"></div>
数量已填入,请确认或修改
</div>
</div>
</div>
<!-- ── 2.6 提交中 ── -->
<div class="phone-card">
<div class="phone-label">2.6 正在提交 (submitting) <span class="tag">按钮 loading</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">5</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="6">
</div>
<div class="input-group">
<label>数量</label>
<input value="50">
</div>
<button class="btn-confirm saving" disabled>
<span class="spinner"></span>
</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot orange"></div>
正在提交…
</div>
</div>
</div>
</div>
<!-- ── 2.7 箱号重复 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.7 箱号重复 (duplicate box no) <span class="tag">琥珀色警告</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<!-- 重复箱号 banner -->
<div class="banner amber">
⚠️ 箱号 5 已存在,请重新输入
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">5</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input class="warn-amber" value="5">
</div>
<div class="input-group">
<label>数量</label>
<input value="50">
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot amber"></div>
箱号 5 已存在,请重新输入
</div>
</div>
</div>
<!-- ── 2.8 数量超限 ── -->
<div class="phone-card">
<div class="phone-label">2.8 数量超限 (quantity too high) <span class="tag">红色警告</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">5</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="6">
</div>
<div class="input-group">
<label>数量</label>
<input class="warn-red" value="200">
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot red"></div>
超出可装数量上限
</div>
</div>
</div>
</div>
<!-- ── 2.9 已全部装箱完毕 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.9 已全部装箱完毕 (fully packed on scan) <span class="tag">红色覆盖</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180002</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-B202</span>
<span class="work-order">WO-2026-0901</span>
<span class="qty">100件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">3箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">3</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="4">
</div>
<div class="input-group">
<label>数量</label>
<input value="">
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot red"></div>
该总排号已全部装箱完毕
</div>
</div>
</div>
<!-- ── 2.10 无效码 ── -->
<div class="phone-card">
<div class="phone-label">2.10 无效码 (invalid code) <span class="tag">红色覆盖 2s</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner red">
❌ 无效码,请重新扫描
</div>
<div class="scan-area waiting">
📷 请扫描执行卡二维码
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan info-placeholder">--</span>
<span class="work-order info-placeholder">--</span>
<span class="qty info-placeholder">--</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="info-placeholder">--</span></span>
<span><span class="meta-label">最大箱号:</span><span class="info-placeholder">--</span></span>
</div>
<button class="detail-link disabled">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input class="disabled" disabled>
</div>
<div class="input-group">
<label>数量</label>
<input class="disabled" disabled>
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot red"></div>
无效码,请重新扫描
</div>
</div>
</div>
</div>
<!-- ── 2.11 网络异常 / 查询失败 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.11 网络异常 (network error) <span class="tag">黄色覆盖 2s</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner" style="background:#FFFDE7;color:#F9A825;">
⚠️ 网络异常,请检查网络连接
</div>
<div class="scan-area waiting">
📷 请扫描执行卡二维码
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan info-placeholder">--</span>
<span class="work-order info-placeholder">--</span>
<span class="qty info-placeholder">--</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="info-placeholder">--</span></span>
<span><span class="meta-label">最大箱号:</span><span class="info-placeholder">--</span></span>
</div>
<button class="detail-link disabled">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input class="disabled" disabled>
</div>
<div class="input-group">
<label>数量</label>
<input class="disabled" disabled>
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot yellow"></div>
网络异常,请检查网络连接
</div>
</div>
</div>
<!-- ── 2.12 提交失败 ── -->
<div class="phone-card">
<div class="phone-label">2.12 提交失败 (submit failure) <span class="tag">红色覆盖 2s</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner red">
❌ 服务器响应异常,请稍后重试
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">5</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="6">
</div>
<div class="input-group">
<label>数量</label>
<input value="50">
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot red"></div>
服务器响应异常,请稍后重试
</div>
</div>
</div>
</div>
<!-- ── 2.13 提交重复箱号 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.13 提交重复 (duplicate on submit) <span class="tag">琥珀覆盖 2s</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner amber">
⚠️ 该总排号在箱号 5 已存在,请重新输入
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180001</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-A101</span>
<span class="work-order">WO-2026-0882</span>
<span class="qty">200件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">2箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">7</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="5">
</div>
<div class="input-group">
<label>数量</label>
<input value="50">
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot amber"></div>
该总排号在箱号 5 已存在,请重新输入
</div>
</div>
</div>
<!-- ── 2.14 未配置 API ── -->
<div class="phone-card">
<div class="phone-label">2.14 未配置 API (no API configured) <span class="tag">红色覆盖 2s</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner red">
❌ 未配置 API 地址,请前往设置
</div>
<div class="scan-area waiting">
📷 请扫描执行卡二维码
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan info-placeholder">--</span>
<span class="work-order info-placeholder">--</span>
<span class="qty info-placeholder">--</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="info-placeholder">--</span></span>
<span><span class="meta-label">最大箱号:</span><span class="info-placeholder">--</span></span>
</div>
<button class="detail-link disabled">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input class="disabled" disabled>
</div>
<div class="input-group">
<label>数量</label>
<input class="disabled" disabled>
</div>
<button class="btn-confirm" disabled>确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot red"></div>
未配置 API 地址,请前往设置
</div>
</div>
</div>
</div>
<!-- ── 2.15 同步转运数据 ── -->
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">2.15 同步转运数据中 (auto processing) <span class="tag">橙色横幅</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner orange">
<span class="spinner"></span> 正在同步转运数据...
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180003</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-C303</span>
<span class="work-order">WO-2026-0920</span>
<span class="qty">300件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">0箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value">0</span></span>
</div>
<button class="detail-link disabled">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="1">
</div>
<div class="input-group">
<label>数量</label>
<input value="300">
</div>
<button class="btn-confirm">确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot orange"></div>
正在同步转运数据...
</div>
</div>
</div>
<!-- ── 2.16 自动装箱警告 ── -->
<div class="phone-card">
<div class="phone-label">2.16 自动装箱警告 (auto warnings) <span class="tag">琥珀横幅</span></div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱编号</h2>
<div class="mode-pill">
<span class="pill-icon"></span> 单码装箱 <span class="pill-key">P2</span>
</div>
</div>
<div class="banner amber">
⚠️ 总排号 ZP-INVALID 未找到排产号信息,已忽略
</div>
<div class="scan-area scanned">
<span class="scan-label">ZP202605180004</span> <span class="scan-tag">已识别</span>
</div>
<div class="info-area">
<div class="info-main">
<span class="paichan">PC-2605-D404</span>
<span class="work-order">WO-2026-0931</span>
<span class="qty">150件</span>
</div>
<div class="info-meta">
<div class="meta-left">
<span><span class="meta-label">已有箱数:</span><span class="meta-value">1箱</span></span>
<span><span class="meta-label">最大箱号:</span><span class="meta-value max-qty">3</span></span>
</div>
<button class="detail-link">详情 →</button>
</div>
</div>
<hr class="section-divider">
<div class="input-area">
<div class="input-group">
<label>箱号</label>
<input value="4">
</div>
<div class="input-group">
<label>数量</label>
<input value="100" style="border-color:var(--blue);box-shadow:0 0 0 1px var(--blue);">
</div>
<button class="btn-confirm">确认</button>
</div>
<div class="action-bar">
<button class="btn-back">返回</button>
</div>
<div class="status-bar">
<div class="status-dot blue"></div>
数量已填入,请确认或修改
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 3. _canSubmit 判定条件 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">3. 提交按钮启用条件_canSubmit</div>
<div class="state-note">
确认按钮的 enabled 状态由 <code>_canSubmit</code> 决定。当以下<strong>所有条件</strong>同时满足时,按钮启用:
</div>
<table class="logic-table">
<thead>
<tr>
<th>#</th>
<th>条件</th>
<th>代码表达式</th>
<th>不满足时效果</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>未处于提交中</td>
<td><code>!_isSubmitting</code></td>
<td>按钮显示 loading 转圈</td>
</tr>
<tr>
<td>2</td>
<td>阶段为 scanned</td>
<td><code>_phase == _Phase.scanned</code></td>
<td>按钮灰色禁用</td>
</tr>
<tr>
<td>3</td>
<td>已有总排号</td>
<td><code>_zongpaiNo != null</code></td>
<td>按钮灰色禁用</td>
</tr>
<tr>
<td>4</td>
<td>箱号有效</td>
<td><code>boxNo != null &amp;&amp; boxNo &gt; 0</code></td>
<td>按钮灰色禁用</td>
</tr>
<tr>
<td>5</td>
<td>数量有效</td>
<td><code>qty != null &amp;&amp; qty &gt; 0</code></td>
<td>按钮灰色禁用</td>
</tr>
<tr>
<td>6</td>
<td>数量未超限</td>
<td><code>!_quantityTooHigh</code>qty &le; remaining</td>
<td>数量输入红色边框;状态栏红色 "超出可装数量上限"</td>
</tr>
<tr>
<td>7</td>
<td>箱号不重复</td>
<td><code>!_isDuplicateBoxNo</code></td>
<td>箱号输入琥珀边框;琥珀横幅;状态栏琥珀色 "箱号 X 已存在"</td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 4. _showStatusOverride 状态覆盖机制 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">4. 状态栏覆盖机制_showStatusOverride</div>
<div class="state-note">
方法签名:<code>void _showStatusOverride(String text, StatusDotColor dot, Duration duration)</code><br><br>
大多数反馈状态通过此方法展示。调用后立即替换底部状态栏的 <strong>文字</strong><strong>圆点颜色</strong>,并在 <code>duration</code> 到期后自动恢复为基础状态(由 <code>_updateBaseStatus()</code> 计算)。
</div>
<table class="logic-table">
<thead>
<tr>
<th>场景</th>
<th>文字</th>
<th>颜色</th>
<th>持续时间</th>
</tr>
</thead>
<tbody>
<tr>
<td>无效码</td>
<td>无效码,请重新扫描</td>
<td>红色</td>
<td>2s</td>
</tr>
<tr>
<td>网络异常</td>
<td>网络异常,请检查网络连接</td>
<td>黄色</td>
<td>2s</td>
</tr>
<tr>
<td>查询失败</td>
<td>查询失败(或具体消息)</td>
<td>红色</td>
<td>2s</td>
</tr>
<tr>
<td>提交重复</td>
<td>该总排号在箱号 X 已存在,请重新输入</td>
<td>琥珀</td>
<td>2s</td>
</tr>
<tr>
<td>提交失败</td>
<td>提交失败(或具体消息)</td>
<td>红色</td>
<td>2s</td>
</tr>
<tr>
<td>未配置 API</td>
<td>未配置 API 地址,请前往设置</td>
<td>红色</td>
<td>2s</td>
</tr>
<tr>
<td>装箱成功-继续</td>
<td>请继续完成剩余数量装箱</td>
<td>绿色</td>
<td>1.5s</td>
</tr>
<tr>
<td>装箱成功-全部</td>
<td>装箱成功</td>
<td>绿色</td>
<td>1.5s</td>
</tr>
<tr>
<td>全部装箱完毕</td>
<td>该总排号已全部装箱完毕</td>
<td>红色</td>
<td>常驻_statusOverride</td>
</tr>
<tr>
<td>删除成功</td>
<td>删除成功</td>
<td>绿色</td>
<td>1.5s</td>
</tr>
<tr>
<td>修改成功</td>
<td>修改成功</td>
<td>绿色</td>
<td>1.5s</td>
</tr>
<tr>
<td>保存编辑-数量无效</td>
<td>请输入有效数量</td>
<td>红色</td>
<td>2s</td>
</tr>
<tr>
<td>保存编辑-数量超限</td>
<td>超出可装数量上限</td>
<td>红色</td>
<td>2s</td>
</tr>
<tr>
<td>同步转运数据</td>
<td>正在同步转运数据...</td>
<td>橙色</td>
<td>常驻(直到处理结束)</td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 5. 自动填充逻辑 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">5. 自动填充逻辑_applyAutoFill</div>
<div class="state-note">
扫码成功并获取 API 数据后自动触发。核心逻辑:<strong>填充箱号 = lastBoxNo+1 或 maxBoxNo+1</strong><strong>填充数量 = 剩余可装数量</strong>;数量输入框自动全选并聚焦。
</div>
<table class="logic-table">
<thead>
<tr>
<th>情况</th>
<th>箱号填充</th>
<th>数量填充</th>
</tr>
</thead>
<tbody>
<tr>
<td>首次扫码(无历史箱号)</td>
<td><code>maxBoxNo + 1</code>(通常为 1</td>
<td><code>erpQuantity - packedQuantity</code>(全部剩余)</td>
</tr>
<tr>
<td>上次装箱后有继续</td>
<td><code>lastBoxNo + 1</code></td>
<td>剩余可装数量</td>
</tr>
<tr>
<td>已全部装箱完毕</td>
<td>清空</td>
<td>清空</td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 6. 已分配行操作 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">6. 已分配行操作详解</div>
<div class="state-note">
每个 <code>CurrentZongpaiBoxData</code> 渲染为一行 <code>_buildAssignedBoxRow</code>。操作按钮在 <strong>view/edit 模式</strong><strong>delete 确认模式</strong> 间切换。
</div>
<table class="logic-table">
<thead>
<tr>
<th>操作</th>
<th>入口</th>
<th>交互过程</th>
<th>成功后效果</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>修改</strong></td>
<td>点击 ✏️</td>
<td>行变橙色背景;数量文字→文本输入;显示 ✅保存 / ❌取消 按钮</td>
<td>调用 <code>updateBoxRecord</code>;状态栏绿 "修改成功" 1.5s;自动刷新输入区</td>
</tr>
<tr>
<td><strong>删除</strong></td>
<td>点击 🗑️</td>
<td>行下方出现红色确认条 "确认删除此条记录?" [是] [否]</td>
<td>调用 <code>deleteBoxRecord</code>;状态栏绿 "删除成功" 1.5s;行移除此数据</td>
</tr>
<tr>
<td><strong>取消编辑</strong></td>
<td>点击 ❌ 或 "否"</td>
<td>恢复 view 模式,不做 API 调用</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 7. 模式切换 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">7. 模式切换_cycleMode</div>
<div class="state-note">
触发方式:<strong>点击 Pill</strong><strong>按下 P2 物理按键</strong>Android KeyCode R1 / LogicalKey gameButtonRight1 / LogicalKey select<br>
切换时调用 <code>_resetState()</code> 清空所有状态,包括阶段、总排号、箱号输入等。
</div>
<table class="logic-table">
<thead>
<tr>
<th>方向</th>
<th>Pill 背景色</th>
<th>标签</th>
</tr>
</thead>
<tbody>
<tr>
<td>单码 → 多码</td>
<td>橙色 <code>Colors.orange.shade700</code></td>
<td>多码凑箱</td>
</tr>
<tr>
<td>多码 → 单码</td>
<td>蓝色 <code>Colors.blue.shade700</code></td>
<td>单码装箱</td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 8. 装箱详情页 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">8. 装箱详情页BoxingDetailPage</div>
<div class="state-note">
源码:<code>lib/pages/boxing_detail_page.dart</code><br>
通过点击主页面 "详情 →" 进入。传入 <code>paichanNo</code><code>existingBoxes</code>,按箱号分组展示每个箱内的总排号明细(总排号、工单号、数量/总量)。
</div>
<div class="phone-row">
<div class="phone-card">
<div class="phone-label">装箱详情(导航页)</div>
<div class="phone-screen">
<div class="app-bar">
<h2>装箱详情&nbsp;&nbsp;PC-2605-A101</h2>
</div>
<div style="padding:12px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;">
<span style="font-size:14px;font-weight:700;">箱号 3</span>
<span style="font-size:14px;font-weight:600;color:var(--blue);">共 50 件</span>
</div>
<div style="border:1px solid var(--grey300);border-radius:6px;padding:8px;">
<div style="display:flex;gap:8px;font-size:14px;margin:2px 0;">
<span style="flex:3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">ZP202605180001</span>
<span style="flex:2;color:var(--grey600);">WO-2026-0882</span>
<span>50/--</span>
</div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin:12px 0 4px;">
<span style="font-size:14px;font-weight:700;">箱号 7</span>
<span style="font-size:14px;font-weight:600;color:var(--blue);">共 100 件</span>
</div>
<div style="border:1px solid var(--grey300);border-radius:6px;padding:8px;">
<div style="display:flex;gap:8px;font-size:14px;margin:2px 0;">
<span style="flex:3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">ZP202605180001</span>
<span style="flex:2;color:var(--grey600);">WO-2026-0882</span>
<span>100/--</span>
</div>
</div>
</div>
<div style="padding:12px 16px;background:var(--grey200);font-size:14px;">
共 2 箱
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 9. 关键状态字段速查表 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">9. 关键状态字段速查表</div>
<table class="logic-table">
<thead>
<tr>
<th>字段</th>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>_mode</code></td>
<td>BoxingMode</td>
<td>singleCode / multiCode</td>
</tr>
<tr>
<td><code>_phase</code></td>
<td>_Phase (enum)</td>
<td>waiting / scanned单码无 submitted</td>
</tr>
<tr>
<td><code>_zongpaiNo</code></td>
<td>String?</td>
<td>当前总排号null = 未扫码</td>
</tr>
<tr>
<td><code>_paichanNo</code></td>
<td>String?</td>
<td>来自 API 的排产号</td>
</tr>
<tr>
<td><code>_workOrderNo</code></td>
<td>String?</td>
<td>来自 API 的工单号</td>
</tr>
<tr>
<td><code>_erpQuantity</code></td>
<td>int?</td>
<td>来自 API 的 ERP 总数量</td>
</tr>
<tr>
<td><code>_currentZongpaiBoxes</code></td>
<td>List&lt;CurrentZongpaiBoxData&gt;</td>
<td>当前总排号已分配的箱号列表("已分配" 区数据源)</td>
</tr>
<tr>
<td><code>_existingBoxes</code></td>
<td>List&lt;BoxDetailData&gt;</td>
<td>该排产号所有已有箱号(详情页数据源)</td>
</tr>
<tr>
<td><code>_maxBoxNo</code></td>
<td>int</td>
<td>已有最大箱号,用于填充下一个箱号</td>
</tr>
<tr>
<td><code>_lastBoxNo</code></td>
<td>int?</td>
<td>上次提交的箱号(用于 continue 时 boxNo+1</td>
</tr>
<tr>
<td><code>_editingBox</code></td>
<td>CurrentZongpaiBoxData?</td>
<td>当前正在编辑的已分配行null = 非编辑状态)</td>
</tr>
<tr>
<td><code>_isSubmitting</code></td>
<td>bool</td>
<td>正在提交 API 请求(禁用按钮、显示 loading 转圈)</td>
</tr>
<tr>
<td><code>_isDuplicateBoxNo</code></td>
<td>bool</td>
<td>当前箱号是否和已有箱号重复</td>
</tr>
<tr>
<td><code>_editingAssignedItemId</code></td>
<td>int?</td>
<td>正在编辑的已分配行 ID</td>
</tr>
<tr>
<td><code>_deletingAssignedItemId</code></td>
<td>int?</td>
<td>待删除的已分配行 ID显示确认条</td>
</tr>
<tr>
<td><code>_statusOverrideText</code></td>
<td>String?</td>
<td>覆盖状态栏文字null = 使用基础状态文字)</td>
</tr>
<tr>
<td><code>_statusOverrideDot</code></td>
<td>StatusDotColor?</td>
<td>覆盖状态栏圆点颜色</td>
</tr>
<tr>
<td><code>_isAutoProcessing</code></td>
<td>bool</td>
<td>是否正在自动同步/装箱转运数据</td>
</tr>
</tbody>
</table>
</div>
<!-- ═══════════════════════════════════════════════════════ -->
<!-- 10. 数据来源 -->
<!-- ═══════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">10. API 数据依赖</div>
<div class="state-note">
所有核心信息来自 <code>BoxInfoResult</code>方法fetchBoxInfo装箱操作依赖 saveBoxRecord / updateBoxRecord / deleteBoxRecord。
</div>
<table class="logic-table">
<thead>
<tr>
<th>API</th>
<th>触发时机</th>
<th>返回关键字段</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>fetchBoxInfo(zongpaiNo)</code></td>
<td>扫码时</td>
<td>paichanNo, workOrderNo, quantity, currentZongpaiBoxes, existingBoxes, maxBoxNo</td>
</tr>
<tr>
<td><code>saveBoxRecord(zongpaiNo, boxNo, quantity)</code></td>
<td>首次添加装箱记录</td>
<td>success, isDuplicate, boxItemId</td>
</tr>
<tr>
<td><code>updateBoxRecord(boxItemId, boxNo, quantity)</code></td>
<td>修改已分配行 / 保存编辑</td>
<td>success, isDuplicate</td>
</tr>
<tr>
<td><code>deleteBoxRecord(boxItemId)</code></td>
<td>删除已分配行确认</td>
<td>success</td>
</tr>
</tbody>
</table>
</div>
<div style="text-align:center;padding:24px;font-size:12px;color:var(--grey500);">
生成时间2026-05-18 &middot; 源码:<code>lib/pages/boxing_page.dart</code>2545行<br>
相关模块:<code>lib/pages/boxing_detail_page.dart</code> · <code>lib/widgets/status_bar.dart</code> · <code>lib/services/boxing_context.dart</code>
</div>
</body>
</html>