From cf974cee92bef77620434eda7f325bc6667bdceb Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Wed, 12 Aug 2026 15:06:15 +0800 Subject: [PATCH] feat(android): add attachment pending banner on boxing page Co-Authored-By: Claude --- lib/pages/boxing/parts/boxing_scan_part.dart | 9 +++++++++ .../boxing/widgets/boxing_notice_banners.dart | 19 +++++++++++++++++++ lib/pages/boxing_page.dart | 8 ++++++++ lib/services/api_service.dart | 3 +++ 4 files changed, 39 insertions(+) diff --git a/lib/pages/boxing/parts/boxing_scan_part.dart b/lib/pages/boxing/parts/boxing_scan_part.dart index f4221e7..35be7a3 100644 --- a/lib/pages/boxing/parts/boxing_scan_part.dart +++ b/lib/pages/boxing/parts/boxing_scan_part.dart @@ -71,6 +71,13 @@ extension _BoxingScanPart on _BoxingPageState { : 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(() { _zongpaiNo = zongpai; _paichanNo = result.paichanNo; @@ -87,6 +94,8 @@ extension _BoxingScanPart on _BoxingPageState { _deletingAssignedItemId = null; _completedJumpBoxNo = null; _statusOverrideText = null; + _attachmentPending = pending; + _attachmentPendingCount = pendingCount; if (_mode == BoxingMode.singleCode) { _paichanSwitchNotice = null; } diff --git a/lib/pages/boxing/widgets/boxing_notice_banners.dart b/lib/pages/boxing/widgets/boxing_notice_banners.dart index 74f97ec..18e53d1 100644 --- a/lib/pages/boxing/widgets/boxing_notice_banners.dart +++ b/lib/pages/boxing/widgets/boxing_notice_banners.dart @@ -12,6 +12,8 @@ class BoxingNoticeBanners extends StatelessWidget { final int remainingQuantity; final String? zongpaiNo; final int? completedJumpBoxNo; + final bool attachmentPending; + final int attachmentPendingCount; final VoidCallback onJumpToCompletedBox; const BoxingNoticeBanners({ @@ -26,6 +28,8 @@ class BoxingNoticeBanners extends StatelessWidget { required this.remainingQuantity, required this.zongpaiNo, required this.completedJumpBoxNo, + this.attachmentPending = false, + this.attachmentPendingCount = 0, 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) { banners.add( _NoticeBanner( diff --git a/lib/pages/boxing_page.dart b/lib/pages/boxing_page.dart index d521bc5..3d38f27 100644 --- a/lib/pages/boxing_page.dart +++ b/lib/pages/boxing_page.dart @@ -118,6 +118,10 @@ class _BoxingPageState extends State with WidgetsBindingObserver { // ignore: unused_field -- already boxed qty for this category (later task) int? _attachmentBoxedQty; + /// Whether to show the attachment-pending banner. + bool _attachmentPending = false; + int _attachmentPendingCount = 0; + @override void initState() { super.initState(); @@ -265,6 +269,8 @@ class _BoxingPageState extends State with WidgetsBindingObserver { _attachmentCategoryName = null; _attachmentExpectedQty = null; _attachmentBoxedQty = null; + _attachmentPending = false; + _attachmentPendingCount = 0; } // === 重复箱号检测 === @@ -388,6 +394,8 @@ class _BoxingPageState extends State with WidgetsBindingObserver { remainingQuantity: _remainingQuantity, zongpaiNo: _zongpaiNo, completedJumpBoxNo: _completedJumpBoxNo, + attachmentPending: _attachmentPending, + attachmentPendingCount: _attachmentPendingCount, onJumpToCompletedBox: () => this._jumpToCompletedBox(), ), diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 5b3a1ef..7d13bb2 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -195,6 +195,7 @@ class BoxInfoResult { final List existingBoxes; final int maxBoxNo; final int suggestedBoxNo; + final Map? attachmentSummary; BoxInfoResult({ required this.success, @@ -207,6 +208,7 @@ class BoxInfoResult { this.existingBoxes = const [], this.maxBoxNo = 0, this.suggestedBoxNo = 1, + this.attachmentSummary, }); factory BoxInfoResult.ok(Map json) { @@ -232,6 +234,7 @@ class BoxInfoResult { existingBoxes: boxes, maxBoxNo: json['max_box_no'] as int? ?? 0, suggestedBoxNo: json['suggested_box_no'] as int? ?? 1, + attachmentSummary: json['attachment_summary'] as Map?, ); }