Extract large methods from _BoxingPageState into 6 part files: - boxing_scan_part.dart: scan handling and auto-scan processing - boxing_submit_part.dart: submit/update/delete API calls - boxing_box_mutation_part.dart: box item mutations - boxing_single_ops_part.dart: single-code boxing operations - boxing_multi_ops_part.dart: multi-code boxing operations - boxing_status_part.dart: status bar management Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
272 lines
8.4 KiB
Dart
272 lines
8.4 KiB
Dart
// ignore_for_file: invalid_use_of_protected_member, unnecessary_this
|
|
|
|
part of '../../boxing_page.dart';
|
|
|
|
extension _BoxingScanPart on _BoxingPageState {
|
|
void _onScan(ScanResult result) {
|
|
if (_isAutoProcessing) {
|
|
return;
|
|
}
|
|
|
|
if (_crossPaichaPending) {
|
|
_feedbackService.trigger(FeedbackEvent.alreadyCompleted);
|
|
return;
|
|
}
|
|
|
|
final parsed = CodeParser.parse(result.barcode);
|
|
|
|
if (parsed.type != CodeType.zongpaiNo) {
|
|
_feedbackService.trigger(FeedbackEvent.scanInvalid);
|
|
this._showStatusOverride(
|
|
'无效码,请重新扫描',
|
|
StatusDotColor.red,
|
|
const Duration(seconds: 2),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final zongpai = parsed.value;
|
|
|
|
// 三种模式都允许后扫入的总排号覆盖当前扫码区;未确认数据不会入库。
|
|
this._queryBoxInfo(zongpai);
|
|
}
|
|
|
|
Future<bool> _queryBoxInfo(String zongpai) async {
|
|
final action = await _apiActions.fetchBoxInfo(zongpai);
|
|
|
|
if (!mounted) return false;
|
|
|
|
if (!action.success) {
|
|
_feedbackService.trigger(switch (action.errorKind) {
|
|
BoxingActionErrorKind.network => FeedbackEvent.networkError,
|
|
BoxingActionErrorKind.missingApiUrl => FeedbackEvent.submitFailure,
|
|
_ => FeedbackEvent.scanInvalid,
|
|
});
|
|
this._showStatusOverride(
|
|
action.errorMessage ?? '查询失败',
|
|
this._dotForActionError(action.errorKind),
|
|
const Duration(seconds: 2),
|
|
);
|
|
return false;
|
|
}
|
|
|
|
final result = action.data!;
|
|
// 判断该总牌号是否已全部装箱完毕
|
|
final packedQuantity = result.currentZongpaiBoxes.fold<int>(
|
|
0,
|
|
(sum, item) => sum + item.quantity,
|
|
);
|
|
final totalQuantity = result.quantity ?? 0;
|
|
final alreadyCompleted =
|
|
totalQuantity > 0 && packedQuantity >= totalQuantity;
|
|
|
|
_feedbackService.trigger(
|
|
alreadyCompleted
|
|
? FeedbackEvent.alreadyCompleted
|
|
: FeedbackEvent.scanValid,
|
|
);
|
|
|
|
setState(() {
|
|
_zongpaiNo = zongpai;
|
|
_paichanNo = result.paichanNo;
|
|
_workOrderNo = result.workOrderNo;
|
|
_erpQuantity = result.quantity;
|
|
_currentZongpaiBoxes = result.currentZongpaiBoxes;
|
|
_existingBoxes = result.existingBoxes;
|
|
_maxBoxNo = result.maxBoxNo;
|
|
_phase = BoxingPhase.scanned;
|
|
_isDuplicateBoxNo = false;
|
|
_editingBox = null;
|
|
_editingAssignedItemId = null;
|
|
_editingAssignedQuantity = '';
|
|
_deletingAssignedItemId = null;
|
|
_completedJumpBoxNo = null;
|
|
_statusOverrideText = null;
|
|
if (_mode == BoxingMode.singleCode) {
|
|
_paichanSwitchNotice = null;
|
|
}
|
|
|
|
// 根据模式自动填充
|
|
this._applyAutoFill();
|
|
});
|
|
return true;
|
|
}
|
|
|
|
Future<void> _processAutoScanCodes(List<String> codes) async {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_isAutoProcessing = true;
|
|
_autoWarnings.clear();
|
|
_statusOverrideText = '正在同步转运数据...';
|
|
_statusOverrideDot = StatusDotColor.orange;
|
|
});
|
|
|
|
if (_mode == BoxingMode.singleCode) {
|
|
final code = codes.first;
|
|
final ok = await this._queryBoxInfo(code);
|
|
if (!ok && mounted) {
|
|
setState(() => _autoWarnings.add('总排号 $code 未找到排产号信息,已忽略'));
|
|
}
|
|
} else {
|
|
for (final code in codes) {
|
|
if (!mounted) return;
|
|
final queried = await this._queryBoxInfo(code);
|
|
if (!queried) {
|
|
if (mounted) {
|
|
setState(() => _autoWarnings.add('总排号 $code 未找到排产号信息,已忽略'));
|
|
}
|
|
continue;
|
|
}
|
|
if (!_canSubmit) {
|
|
if (mounted) {
|
|
setState(() => _autoWarnings.add('总排号 $code 暂不能装箱,已忽略'));
|
|
}
|
|
continue;
|
|
}
|
|
final saved = await this._submit();
|
|
if (!saved && mounted) {
|
|
setState(() => _autoWarnings.add('总排号 $code 自动装箱失败,请手动处理'));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_isAutoProcessing = false;
|
|
_statusOverrideText = null;
|
|
_statusOverrideDot = null;
|
|
});
|
|
_focusQuantityInput();
|
|
_requestFocus();
|
|
}
|
|
|
|
void _applyAutoFill() {
|
|
switch (_mode) {
|
|
case BoxingMode.singleCode:
|
|
final remaining = _remainingQuantity;
|
|
if (remaining <= 0) {
|
|
_boxNoController.clear();
|
|
_quantityController.clear();
|
|
_statusOverrideText = '该总排号已全部装箱完毕';
|
|
_statusOverrideDot = StatusDotColor.red;
|
|
return;
|
|
}
|
|
_boxNoController.text = (_maxBoxNo + 1).toString();
|
|
_quantityController.text = remaining.toString();
|
|
_quantityController.selection = TextSelection(
|
|
baseOffset: 0,
|
|
extentOffset: _quantityController.text.length,
|
|
);
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (mounted && _mode == BoxingMode.singleCode) {
|
|
_quantityFocusNode.requestFocus();
|
|
}
|
|
});
|
|
case BoxingMode.multiCode:
|
|
final decision = decideMultiCodePaichanContext(
|
|
lastPaichanNo: _lastScannedPaichanNo,
|
|
currentPaichanNo: _paichanNo,
|
|
currentBoxNoText: _boxNoController.text,
|
|
maxBoxNo: _maxBoxNo,
|
|
);
|
|
if (decision.isSwitched) {
|
|
_crossPaichaPending = true;
|
|
_feedbackService.trigger(FeedbackEvent.alreadyCompleted);
|
|
this._showCrossPaichaDialog();
|
|
return;
|
|
} else {
|
|
_paichanSwitchNotice = null;
|
|
}
|
|
final boxNoToApply = decision.boxNoToApply;
|
|
if (boxNoToApply != null) {
|
|
_boxNoController.text = boxNoToApply.toString();
|
|
}
|
|
_lastScannedPaichanNo = _paichanNo;
|
|
final remaining = _remainingQuantity;
|
|
if (remaining <= 0) {
|
|
_quantityController.clear();
|
|
if (_manyToOnePackedItems.isEmpty &&
|
|
_currentZongpaiBoxes.isNotEmpty) {
|
|
_completedJumpBoxNo = _currentZongpaiBoxes.first.boxNo;
|
|
}
|
|
_statusOverrideText = '该总排号已全部装箱完毕';
|
|
_statusOverrideDot = StatusDotColor.red;
|
|
return;
|
|
}
|
|
_quantityController.text = remaining.toString();
|
|
_quantityController.selection = TextSelection(
|
|
baseOffset: 0,
|
|
extentOffset: _quantityController.text.length,
|
|
);
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (mounted && _mode == BoxingMode.multiCode) {
|
|
_quantityFocusNode.requestFocus();
|
|
}
|
|
});
|
|
}
|
|
_isDuplicateBoxNo = _boxNoIsDuplicate();
|
|
}
|
|
|
|
Future<void> _showCrossPaichaDialog() async {
|
|
final currentPaicha = _lastScannedPaichanNo ?? '--';
|
|
final newPaicha = _paichanNo ?? '--';
|
|
final confirmed = await showCrossPaichanDialog(
|
|
context: context,
|
|
currentPaicha: currentPaicha,
|
|
newPaicha: newPaicha,
|
|
);
|
|
if (!mounted) return;
|
|
confirmed ? this._confirmPaichanSwitch() : this._cancelPaichanSwitch();
|
|
}
|
|
|
|
void _confirmPaichanSwitch() {
|
|
setState(() {
|
|
_crossPaichaPending = false;
|
|
_manyToOnePackedItems.clear();
|
|
_editingPackedItemId = null;
|
|
_editingPackedQuantity = '';
|
|
_deletingPackedItemId = null;
|
|
_paichanSwitchNotice = '排产号已切换:${_paichanNo ?? "--"}';
|
|
_boxNoController.text = (_maxBoxNo + 1).toString();
|
|
_lastScannedPaichanNo = _paichanNo;
|
|
});
|
|
_feedbackService.trigger(FeedbackEvent.paichanSwitch);
|
|
|
|
// Continue with remaining quantity flow
|
|
final remaining = _remainingQuantity;
|
|
if (remaining <= 0) {
|
|
_quantityController.clear();
|
|
if (_manyToOnePackedItems.isEmpty && _currentZongpaiBoxes.isNotEmpty) {
|
|
_completedJumpBoxNo = _currentZongpaiBoxes.first.boxNo;
|
|
}
|
|
setState(() {
|
|
_statusOverrideText = '该总排号已全部装箱完毕';
|
|
_statusOverrideDot = StatusDotColor.red;
|
|
});
|
|
return;
|
|
}
|
|
setState(() {
|
|
_quantityController.text = remaining.toString();
|
|
_quantityController.selection = TextSelection(
|
|
baseOffset: 0,
|
|
extentOffset: _quantityController.text.length,
|
|
);
|
|
_isDuplicateBoxNo = _boxNoIsDuplicate();
|
|
});
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (mounted && _mode == BoxingMode.multiCode) {
|
|
_quantityFocusNode.requestFocus();
|
|
}
|
|
});
|
|
}
|
|
|
|
void _cancelPaichanSwitch() {
|
|
setState(() {
|
|
_crossPaichaPending = false;
|
|
_zongpaiNo = null;
|
|
_paichanNo = _lastScannedPaichanNo;
|
|
_phase = BoxingPhase.waiting;
|
|
});
|
|
}
|
|
}
|