单码装箱模式 — UI 状态全览

源码:lib/pages/boxing_page.dart_BoxingPageState
覆盖 BoxingMode.singleCode 下所有 UI 状态、状态机流转与交互细节

0. 状态栏颜色图例
蓝色 — 等待/空闲
绿色 — 成功
红色 — 错误(无效码/数量超限/提交失败)
橙色 — 进行中(提交中/同步数据)
琥珀 — 警告(箱号重复/提交重复)
黄色 — 网络异常
1. 状态机(_Phase 枚举)
waiting
等待扫码
→ 扫码成功 → scanned
已扫码,已自动填充
→ 用户点击确认 → submitting
正在提交…


  成功,尚有剩余 → 回到 scanned(箱号+1,数量=剩余) / 回到 waiting(全部装箱完毕)
  失败 → 停留在 scanned,显示错误覆盖
_Phase 触发条件 主要 UI 效果
waiting 初始化 / 重置 / 全部装箱完毕 扫码区灰色占位;信息区灰色 "--";输入区禁用
scanned 扫码成功且 API 返回有效数据 扫码区绿色边框+总排号;信息区展示数据;输入区可编辑,自动填充箱号/数量
没有 submitted 单码模式中,提交成功后直接进入新的 scanned 或 resetting waiting,不经 submitted 阶段
2. UI 状态可视化

每个手机模型展示一个具体状态。底部状态栏颜色参考上方图例。

2.1 等待扫码 (waiting) 初始状态

装箱编号

单码装箱 P2
📷 请扫描执行卡二维码
-- -- --
已有箱数:-- 最大箱号:--

等待扫码
2.2 已扫码-正常 (scanned, can submit) 最常见状态

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:5

数量已填入,请确认或修改
2.3 已扫码+已分配 (with assigned boxes)

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:3箱 最大箱号:7

已分配
3号箱 数量 50
7号箱 数量 100
数量已填入,请确认或修改
2.4 已分配行 - 编辑中 (editing assigned) 行内编辑

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:3箱 最大箱号:7

已分配
3号箱 数量 50
7号箱
数量已填入,请确认或修改
2.5 已分配行 - 删除确认 (delete confirm) 行内确认

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:5

已分配
3号箱 数量 50
5号箱 数量 100
确认删除此条记录?
数量已填入,请确认或修改
2.6 正在提交 (submitting) 按钮 loading

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:5

正在提交…
2.7 箱号重复 (duplicate box no) 琥珀色警告

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:5

箱号 5 已存在,请重新输入
2.8 数量超限 (quantity too high) 红色警告

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:5

超出可装数量上限
2.9 已全部装箱完毕 (fully packed on scan) 红色覆盖

装箱编号

单码装箱 P2
ZP202605180002 已识别
PC-2605-B202 WO-2026-0901 100件
已有箱数:3箱 最大箱号:3

该总排号已全部装箱完毕
2.10 无效码 (invalid code) 红色覆盖 2s

装箱编号

单码装箱 P2
📷 请扫描执行卡二维码
-- -- --
已有箱数:-- 最大箱号:--

无效码,请重新扫描
2.11 网络异常 (network error) 黄色覆盖 2s

装箱编号

单码装箱 P2
📷 请扫描执行卡二维码
-- -- --
已有箱数:-- 最大箱号:--

网络异常,请检查网络连接
2.12 提交失败 (submit failure) 红色覆盖 2s

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:5

服务器响应异常,请稍后重试
2.13 提交重复 (duplicate on submit) 琥珀覆盖 2s

装箱编号

单码装箱 P2
ZP202605180001 已识别
PC-2605-A101 WO-2026-0882 200件
已有箱数:2箱 最大箱号:7

该总排号在箱号 5 已存在,请重新输入
2.14 未配置 API (no API configured) 红色覆盖 2s

装箱编号

单码装箱 P2
📷 请扫描执行卡二维码
-- -- --
已有箱数:-- 最大箱号:--

未配置 API 地址,请前往设置
2.15 同步转运数据中 (auto processing) 橙色横幅

装箱编号

单码装箱 P2
ZP202605180003 已识别
PC-2605-C303 WO-2026-0920 300件
已有箱数:0箱 最大箱号:0

正在同步转运数据...
2.16 自动装箱警告 (auto warnings) 琥珀横幅

装箱编号

单码装箱 P2
ZP202605180004 已识别
PC-2605-D404 WO-2026-0931 150件
已有箱数:1箱 最大箱号:3

数量已填入,请确认或修改
3. 提交按钮启用条件(_canSubmit)
确认按钮的 enabled 状态由 _canSubmit 决定。当以下所有条件同时满足时,按钮启用:
# 条件 代码表达式 不满足时效果
1 未处于提交中 !_isSubmitting 按钮显示 loading 转圈
2 阶段为 scanned _phase == _Phase.scanned 按钮灰色禁用
3 已有总排号 _zongpaiNo != null 按钮灰色禁用
4 箱号有效 boxNo != null && boxNo > 0 按钮灰色禁用
5 数量有效 qty != null && qty > 0 按钮灰色禁用
6 数量未超限 !_quantityTooHigh(qty ≤ remaining) 数量输入红色边框;状态栏红色 "超出可装数量上限"
7 箱号不重复 !_isDuplicateBoxNo 箱号输入琥珀边框;琥珀横幅;状态栏琥珀色 "箱号 X 已存在"
4. 状态栏覆盖机制(_showStatusOverride)
方法签名:void _showStatusOverride(String text, StatusDotColor dot, Duration duration)

大多数反馈状态通过此方法展示。调用后立即替换底部状态栏的 文字圆点颜色,并在 duration 到期后自动恢复为基础状态(由 _updateBaseStatus() 计算)。
场景 文字 颜色 持续时间
无效码 无效码,请重新扫描 红色 2s
网络异常 网络异常,请检查网络连接 黄色 2s
查询失败 查询失败(或具体消息) 红色 2s
提交重复 该总排号在箱号 X 已存在,请重新输入 琥珀 2s
提交失败 提交失败(或具体消息) 红色 2s
未配置 API 未配置 API 地址,请前往设置 红色 2s
装箱成功-继续 请继续完成剩余数量装箱 绿色 1.5s
装箱成功-全部 装箱成功 绿色 1.5s
全部装箱完毕 该总排号已全部装箱完毕 红色 常驻(_statusOverride)
删除成功 删除成功 绿色 1.5s
修改成功 修改成功 绿色 1.5s
保存编辑-数量无效 请输入有效数量 红色 2s
保存编辑-数量超限 超出可装数量上限 红色 2s
同步转运数据 正在同步转运数据... 橙色 常驻(直到处理结束)
5. 自动填充逻辑(_applyAutoFill)
扫码成功并获取 API 数据后自动触发。核心逻辑:填充箱号 = lastBoxNo+1 或 maxBoxNo+1填充数量 = 剩余可装数量;数量输入框自动全选并聚焦。
情况 箱号填充 数量填充
首次扫码(无历史箱号) maxBoxNo + 1(通常为 1) erpQuantity - packedQuantity(全部剩余)
上次装箱后有继续 lastBoxNo + 1 剩余可装数量
已全部装箱完毕 清空 清空
6. 已分配行操作详解
每个 CurrentZongpaiBoxData 渲染为一行 _buildAssignedBoxRow。操作按钮在 view/edit 模式delete 确认模式 间切换。
操作 入口 交互过程 成功后效果
修改 点击 ✏️ 行变橙色背景;数量文字→文本输入;显示 ✅保存 / ❌取消 按钮 调用 updateBoxRecord;状态栏绿 "修改成功" 1.5s;自动刷新输入区
删除 点击 🗑️ 行下方出现红色确认条 "确认删除此条记录?" [是] [否] 调用 deleteBoxRecord;状态栏绿 "删除成功" 1.5s;行移除此数据
取消编辑 点击 ❌ 或 "否" 恢复 view 模式,不做 API 调用
7. 模式切换(_cycleMode)
触发方式:点击 Pill按下 P2 物理按键(Android KeyCode R1 / LogicalKey gameButtonRight1 / LogicalKey select)。
切换时调用 _resetState() 清空所有状态,包括阶段、总排号、箱号输入等。
方向 Pill 背景色 标签
单码 → 多码 橙色 Colors.orange.shade700 多码凑箱
多码 → 单码 蓝色 Colors.blue.shade700 单码装箱
8. 装箱详情页(BoxingDetailPage)
源码:lib/pages/boxing_detail_page.dart
通过点击主页面 "详情 →" 进入。传入 paichanNoexistingBoxes,按箱号分组展示每个箱内的总排号明细(总排号、工单号、数量/总量)。
装箱详情(导航页)

装箱详情  PC-2605-A101

箱号 3 共 50 件
ZP202605180001 WO-2026-0882 50/--
箱号 7 共 100 件
ZP202605180001 WO-2026-0882 100/--
共 2 箱
9. 关键状态字段速查表
字段 类型 说明
_mode BoxingMode singleCode / multiCode
_phase _Phase (enum) waiting / scanned(单码无 submitted)
_zongpaiNo String? 当前总排号,null = 未扫码
_paichanNo String? 来自 API 的排产号
_workOrderNo String? 来自 API 的工单号
_erpQuantity int? 来自 API 的 ERP 总数量
_currentZongpaiBoxes List<CurrentZongpaiBoxData> 当前总排号已分配的箱号列表("已分配" 区数据源)
_existingBoxes List<BoxDetailData> 该排产号所有已有箱号(详情页数据源)
_maxBoxNo int 已有最大箱号,用于填充下一个箱号
_lastBoxNo int? 上次提交的箱号(用于 continue 时 boxNo+1)
_editingBox CurrentZongpaiBoxData? 当前正在编辑的已分配行(null = 非编辑状态)
_isSubmitting bool 正在提交 API 请求(禁用按钮、显示 loading 转圈)
_isDuplicateBoxNo bool 当前箱号是否和已有箱号重复
_editingAssignedItemId int? 正在编辑的已分配行 ID
_deletingAssignedItemId int? 待删除的已分配行 ID(显示确认条)
_statusOverrideText String? 覆盖状态栏文字(null = 使用基础状态文字)
_statusOverrideDot StatusDotColor? 覆盖状态栏圆点颜色
_isAutoProcessing bool 是否正在自动同步/装箱转运数据
10. API 数据依赖
所有核心信息来自 BoxInfoResult(方法:fetchBoxInfo),装箱操作依赖 saveBoxRecord / updateBoxRecord / deleteBoxRecord。
API 触发时机 返回关键字段
fetchBoxInfo(zongpaiNo) 扫码时 paichanNo, workOrderNo, quantity, currentZongpaiBoxes, existingBoxes, maxBoxNo
saveBoxRecord(zongpaiNo, boxNo, quantity) 首次添加装箱记录 success, isDuplicate, boxItemId
updateBoxRecord(boxItemId, boxNo, quantity) 修改已分配行 / 保存编辑 success, isDuplicate
deleteBoxRecord(boxItemId) 删除已分配行确认 success
生成时间:2026-05-18 · 源码:lib/pages/boxing_page.dart(2545行)
相关模块:lib/pages/boxing_detail_page.dart · lib/widgets/status_bar.dart · lib/services/boxing_context.dart