feat(android): add attachment pending banner on boxing page

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-08-12 15:06:15 +08:00
parent 15c29bceb8
commit cf974cee92
4 changed files with 39 additions and 0 deletions

View File

@@ -71,6 +71,13 @@ extension _BoxingScanPart on _BoxingPageState {
: FeedbackEvent.scanValid, : FeedbackEvent.scanValid,
); );
final attSummary = result.attachmentSummary;
final pending =
attSummary != null &&
attSummary['determination'] == 'has' &&
attSummary['all_complete'] != true;
final pendingCount = attSummary?['pending_count'] as int? ?? 0;
setState(() { setState(() {
_zongpaiNo = zongpai; _zongpaiNo = zongpai;
_paichanNo = result.paichanNo; _paichanNo = result.paichanNo;
@@ -87,6 +94,8 @@ extension _BoxingScanPart on _BoxingPageState {
_deletingAssignedItemId = null; _deletingAssignedItemId = null;
_completedJumpBoxNo = null; _completedJumpBoxNo = null;
_statusOverrideText = null; _statusOverrideText = null;
_attachmentPending = pending;
_attachmentPendingCount = pendingCount;
if (_mode == BoxingMode.singleCode) { if (_mode == BoxingMode.singleCode) {
_paichanSwitchNotice = null; _paichanSwitchNotice = null;
} }

View File

@@ -12,6 +12,8 @@ class BoxingNoticeBanners extends StatelessWidget {
final int remainingQuantity; final int remainingQuantity;
final String? zongpaiNo; final String? zongpaiNo;
final int? completedJumpBoxNo; final int? completedJumpBoxNo;
final bool attachmentPending;
final int attachmentPendingCount;
final VoidCallback onJumpToCompletedBox; final VoidCallback onJumpToCompletedBox;
const BoxingNoticeBanners({ const BoxingNoticeBanners({
@@ -26,6 +28,8 @@ class BoxingNoticeBanners extends StatelessWidget {
required this.remainingQuantity, required this.remainingQuantity,
required this.zongpaiNo, required this.zongpaiNo,
required this.completedJumpBoxNo, required this.completedJumpBoxNo,
this.attachmentPending = false,
this.attachmentPendingCount = 0,
required this.onJumpToCompletedBox, required this.onJumpToCompletedBox,
}); });
@@ -76,6 +80,21 @@ class BoxingNoticeBanners extends StatelessWidget {
), ),
); );
} }
if (attachmentPending) {
banners.add(
_NoticeBanner(
icon: Icon(
Icons.warning_amber,
size: 14,
color: Colors.amber.shade900,
),
text: '本单有附件,尚未到齐(剩 $attachmentPendingCount 种)',
background: Colors.amber.shade50,
foreground: Colors.amber.shade900,
border: Colors.amber.shade200,
),
);
}
if (isDuplicateBoxNo && phase == BoxingPhase.scanned) { if (isDuplicateBoxNo && phase == BoxingPhase.scanned) {
banners.add( banners.add(
_NoticeBanner( _NoticeBanner(

View File

@@ -118,6 +118,10 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
// ignore: unused_field -- already boxed qty for this category (later task) // ignore: unused_field -- already boxed qty for this category (later task)
int? _attachmentBoxedQty; int? _attachmentBoxedQty;
/// Whether to show the attachment-pending banner.
bool _attachmentPending = false;
int _attachmentPendingCount = 0;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -265,6 +269,8 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
_attachmentCategoryName = null; _attachmentCategoryName = null;
_attachmentExpectedQty = null; _attachmentExpectedQty = null;
_attachmentBoxedQty = null; _attachmentBoxedQty = null;
_attachmentPending = false;
_attachmentPendingCount = 0;
} }
// === 重复箱号检测 === // === 重复箱号检测 ===
@@ -388,6 +394,8 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
remainingQuantity: _remainingQuantity, remainingQuantity: _remainingQuantity,
zongpaiNo: _zongpaiNo, zongpaiNo: _zongpaiNo,
completedJumpBoxNo: _completedJumpBoxNo, completedJumpBoxNo: _completedJumpBoxNo,
attachmentPending: _attachmentPending,
attachmentPendingCount: _attachmentPendingCount,
onJumpToCompletedBox: () => this._jumpToCompletedBox(), onJumpToCompletedBox: () => this._jumpToCompletedBox(),
), ),

View File

@@ -195,6 +195,7 @@ class BoxInfoResult {
final List<BoxDetailData> existingBoxes; final List<BoxDetailData> existingBoxes;
final int maxBoxNo; final int maxBoxNo;
final int suggestedBoxNo; final int suggestedBoxNo;
final Map<String, dynamic>? attachmentSummary;
BoxInfoResult({ BoxInfoResult({
required this.success, required this.success,
@@ -207,6 +208,7 @@ class BoxInfoResult {
this.existingBoxes = const [], this.existingBoxes = const [],
this.maxBoxNo = 0, this.maxBoxNo = 0,
this.suggestedBoxNo = 1, this.suggestedBoxNo = 1,
this.attachmentSummary,
}); });
factory BoxInfoResult.ok(Map<String, dynamic> json) { factory BoxInfoResult.ok(Map<String, dynamic> json) {
@@ -232,6 +234,7 @@ class BoxInfoResult {
existingBoxes: boxes, existingBoxes: boxes,
maxBoxNo: json['max_box_no'] as int? ?? 0, maxBoxNo: json['max_box_no'] as int? ?? 0,
suggestedBoxNo: json['suggested_box_no'] as int? ?? 1, suggestedBoxNo: json['suggested_box_no'] as int? ?? 1,
attachmentSummary: json['attachment_summary'] as Map<String, dynamic>?,
); );
} }