From a6018582253459bdacad1573badae5d9811daf7f Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Thu, 21 May 2026 17:30:23 +0800 Subject: [PATCH] refactor: unify registration mode concept to single-code/multi-code shelving Replace the locked/unlocked terminology with RegistrationMode enum (singleCode/multiCode) to align with boxing module's naming convention. Multi-code mode now allows switching shelf locations with a confirmation dialog when pending data exists. Use shared BoxingModePill widget for consistent UI. Co-Authored-By: Claude Opus 4.6 --- lib/pages/registration_page.dart | 185 ++++++++++++++----------- lib/services/registration_linking.dart | 6 +- 2 files changed, 106 insertions(+), 85 deletions(-) diff --git a/lib/pages/registration_page.dart b/lib/pages/registration_page.dart index 2de2282..fd85c9c 100644 --- a/lib/pages/registration_page.dart +++ b/lib/pages/registration_page.dart @@ -11,6 +11,12 @@ import 'package:pad_scanner/services/registration_linking.dart'; import 'package:vibration/vibration.dart'; import 'package:pad_scanner/pages/boxing_page.dart'; import 'package:pad_scanner/widgets/status_bar.dart'; +import 'package:pad_scanner/pages/boxing/widgets/boxing_shared_widgets.dart'; + +enum RegistrationMode { + singleCode, // 单码上架 + multiCode, // 多码上架 +} class RegistrationPage extends StatefulWidget { const RegistrationPage({super.key}); @@ -29,7 +35,7 @@ class _RegistrationPageState extends State { final _zongpaiNos = []; String? _locationCode; CodeType? _locationType; - bool _isLocked = false; + RegistrationMode _mode = RegistrationMode.singleCode; bool _isSubmitting = false; StatusDotColor _statusDot = StatusDotColor.blue; @@ -88,7 +94,7 @@ class _RegistrationPageState extends State { void _onKeyEvent(KeyEvent event) { if (event is! KeyDownEvent) return; if (_isLockToggleKey(event)) { - _toggleLock(!_isLocked); + _cycleMode(); return; } if (event.logicalKey == LogicalKeyboardKey.enter) { @@ -109,14 +115,7 @@ class _RegistrationPageState extends State { _handleZongpaiScan(parsed.value); case CodeType.locationNormal: case CodeType.locationTransit: - if (!_isLocked) { - _feedbackService.trigger(FeedbackEvent.scanValid); - setState(() { - _locationCode = parsed.value; - _locationType = parsed.type; - _clearStatusOverride(); - }); - } else {} + _handleLocationScan(parsed.value, parsed.type); case CodeType.invalid: _feedbackService.trigger(FeedbackEvent.scanInvalid); _showStatusOverride( @@ -127,6 +126,71 @@ class _RegistrationPageState extends State { } } + void _handleLocationScan(String locationCode, CodeType locationType) { + // In singleCode mode or no existing data, switch directly + if (_mode == RegistrationMode.singleCode || _zongpaiNos.isEmpty) { + _feedbackService.trigger(FeedbackEvent.scanValid); + setState(() { + _locationCode = locationCode; + _locationType = locationType; + _clearStatusOverride(); + }); + return; + } + + // In multiCode mode with existing data, confirm before switching + _showLocationSwitchDialog(locationCode, locationType); + } + + void _showLocationSwitchDialog(String newLocationCode, CodeType newLocationType) { + final count = _zongpaiNos.length; + showDialog( + context: context, + barrierDismissible: false, + builder: (ctx) => AlertDialog( + backgroundColor: Colors.orange.shade50, + title: Row( + children: [ + Icon(Icons.warning_amber, color: Colors.orange.shade800), + const SizedBox(width: 8), + Text( + '切换货架号', + style: TextStyle(color: Colors.orange.shade900), + ), + ], + ), + content: Text( + '当前已有 $count 条待上架数据,切换货架号不会清除已扫描的总排号。', + style: const TextStyle(fontSize: 14), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(ctx); + }, + child: const Text('取消'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + _feedbackService.trigger(FeedbackEvent.scanValid); + setState(() { + _locationCode = newLocationCode; + _locationType = newLocationType; + _clearStatusOverride(); + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.orange.shade700, + foregroundColor: Colors.white, + ), + child: const Text('确认切换'), + ), + ], + ), + ); + } + void _handleZongpaiScan(String zongpaiNo) { // If cross-paicha dialog is showing, just vibrate and discard if (_crossPaichaPending) { @@ -134,8 +198,8 @@ class _RegistrationPageState extends State { return; } - // In locked mode with existing data, check for cross-paicha scan - if (_isLocked && + // In multiCode mode with existing data, check for cross-paicha scan + if (_mode == RegistrationMode.multiCode && _zongpaiNos.isNotEmpty && _overview?.success == true && !_overview!.items.any((item) => item.zongpaiNo == zongpaiNo)) { @@ -148,7 +212,7 @@ class _RegistrationPageState extends State { final shouldRefresh = _overviewZongpaiNo != zongpaiNo; _feedbackService.trigger(FeedbackEvent.scanValid); setState(() { - if (_isLocked) { + if (_mode == RegistrationMode.multiCode) { if (!_zongpaiNos.contains(zongpaiNo)) { _zongpaiNos.add(zongpaiNo); } @@ -197,9 +261,9 @@ class _RegistrationPageState extends State { _statusText = '正在提交…'; return; } - if (_isLocked && _locationCode != null && _zongpaiNos.isEmpty) { + if (_mode == RegistrationMode.multiCode && _locationCode != null && _zongpaiNos.isEmpty) { _statusDot = StatusDotColor.blue; - _statusText = '货位已锁定,请扫描下一张执行卡'; + _statusText = '多码上架模式,请扫描下一张执行卡'; return; } final hasZongpai = _zongpaiNos.isNotEmpty; @@ -343,7 +407,7 @@ class _RegistrationPageState extends State { } } - if (_isLocked && _zongpaiNos.length > 1) { + if (_mode == RegistrationMode.multiCode && _zongpaiNos.length > 1) { if (_isTransitTarget && !await _ensureBatchSamePaicha(baseUrl)) { if (!mounted) return; setState(() => _isSubmitting = false); @@ -420,20 +484,20 @@ class _RegistrationPageState extends State { mode: BoxingMode.singleCode, codes: [zongpaiNo], codesToClear: [zongpaiNo], - clearLocation: !_isLocked, + clearLocation: _mode == RegistrationMode.singleCode, ); return; } setState(() { _zongpaiNos.remove(zongpaiNo); - if (!_isLocked) { + if (_mode == RegistrationMode.singleCode) { _locationCode = null; _locationType = null; } }); final msg = result.isOffShelfSuccess ? '下架成功' - : (_isLocked ? '货位已锁定,请扫描下一张执行卡' : '上架成功'); + : (_mode == RegistrationMode.multiCode ? '多码上架模式,请扫描下一张执行卡' : '上架成功'); _showStatusOverride( msg, StatusDotColor.green, @@ -886,20 +950,18 @@ class _RegistrationPageState extends State { _loadOverview(newZongpaiNo); } - void _toggleLock(bool value) { - if (value && _locationCode == null) { - _feedbackService.trigger(FeedbackEvent.submitFailure); - _showStatusOverride( - '请先扫描货位号', - StatusDotColor.red, - const Duration(seconds: 2), - ); - return; - } + void _cycleMode() { + _feedbackService.trigger(FeedbackEvent.modeSwitch); setState(() { - _isLocked = value; - if (!value && _zongpaiNos.length > 1) { - _zongpaiNos.removeRange(0, _zongpaiNos.length - 1); + switch (_mode) { + case RegistrationMode.singleCode: + _mode = RegistrationMode.multiCode; + case RegistrationMode.multiCode: + _mode = RegistrationMode.singleCode; + // 切换到单码时,只保留最后一个总排号 + if (_zongpaiNos.length > 1) { + _zongpaiNos.removeRange(0, _zongpaiNos.length - 1); + } } }); } @@ -930,7 +992,7 @@ class _RegistrationPageState extends State { children: [ const Text('上架登记'), const SizedBox(width: 12), - Expanded(child: _buildLockHeader()), + Expanded(child: _buildModePill()), ], ), titleSpacing: 12, @@ -948,53 +1010,12 @@ class _RegistrationPageState extends State { ); } - Widget _buildLockHeader() { - final backgroundColor = - _isLocked ? Colors.orange.shade700 : Colors.blue.shade700; - final label = _isLocked ? '货位锁定' : '未锁定'; - - return Material( - borderRadius: BorderRadius.circular(8), - color: backgroundColor, - child: InkWell( - borderRadius: BorderRadius.circular(8), - onTap: () => _toggleLock(!_isLocked), - child: Container( - width: double.infinity, - height: 36, - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.swap_horiz, color: Colors.white, size: 18), - const SizedBox(width: 6), - Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - label, - maxLines: 1, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.w800, - ), - ), - ), - ), - const SizedBox(width: 8), - Text( - 'P2', - style: TextStyle( - color: Colors.white.withValues(alpha: 0.88), - fontSize: 12, - fontWeight: FontWeight.w700, - ), - ), - ], - ), - ), - ), + Widget _buildModePill() { + return BoxingModePill( + isSingle: _mode == RegistrationMode.singleCode, + label: _mode == RegistrationMode.singleCode ? '单码上架' : '多码上架', + enabled: !_isSubmitting, + onTap: _cycleMode, ); } @@ -1336,7 +1357,7 @@ class _RegistrationPageState extends State { style: rowStyle, ), ), - if (isScanned && _isLocked) ...[ + if (isScanned && _mode == RegistrationMode.multiCode) ...[ const SizedBox(width: 6), SizedBox( width: 28, @@ -1447,7 +1468,7 @@ class _RegistrationPageState extends State { : Text( registrationSubmitLabel( isTransitTarget: _isTransitTarget, - isLocked: _isLocked, + isMultiCode: _mode == RegistrationMode.multiCode, zongpaiCount: _zongpaiNos.length, ), style: const TextStyle( diff --git a/lib/services/registration_linking.dart b/lib/services/registration_linking.dart index 45d688b..7d2455d 100644 --- a/lib/services/registration_linking.dart +++ b/lib/services/registration_linking.dart @@ -10,13 +10,13 @@ bool isTransitTarget(CodeType? locationType, String? locationCode) { String registrationSubmitLabel({ required bool isTransitTarget, - required bool isLocked, + required bool isMultiCode, required int zongpaiCount, }) { if (!isTransitTarget) { - return isLocked && zongpaiCount > 1 ? '批量上架($zongpaiCount 条)' : '确 认 上 架'; + return isMultiCode && zongpaiCount > 1 ? '批量上架($zongpaiCount 条)' : '确 认 上 架'; } - return isLocked && zongpaiCount > 1 ? '批量转运并凑箱($zongpaiCount 条)' : '转运并装箱'; + return isMultiCode && zongpaiCount > 1 ? '批量转运并凑箱($zongpaiCount 条)' : '转运并装箱'; } Color registrationSubmitColor({