refactor: split boxing page into part files for scan, submit, ops and status
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>
This commit is contained in:
59
lib/pages/boxing/parts/boxing_box_mutation_part.dart
Normal file
59
lib/pages/boxing/parts/boxing_box_mutation_part.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
part of '../../boxing_page.dart';
|
||||
|
||||
extension _BoxingBoxMutationPart on _BoxingPageState {
|
||||
void _replaceExistingBoxItem(
|
||||
CurrentZongpaiBoxData editing,
|
||||
int boxNo,
|
||||
int quantity,
|
||||
) {
|
||||
final result = box_mutations.replaceExistingBoxItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
editing: editing,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
workOrderNo: _workOrderNo,
|
||||
totalQuantity: _erpQuantity,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
void _addExistingBoxItem(int boxNo, int quantity, int? boxItemId) {
|
||||
final result = box_mutations.addExistingBoxItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
boxItemId: boxItemId,
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
workOrderNo: _workOrderNo,
|
||||
totalQuantity: _erpQuantity,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
void _replaceExistingPackedItem(
|
||||
ManyToOnePackedItem item,
|
||||
int boxNo,
|
||||
int quantity,
|
||||
) {
|
||||
final result = box_mutations.replaceExistingPackedItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
item: item,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
void _removeExistingBoxItem(int boxItemId) {
|
||||
final result = box_mutations.removeExistingBoxItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
boxItemId: boxItemId,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
}
|
||||
112
lib/pages/boxing/parts/boxing_multi_ops_part.dart
Normal file
112
lib/pages/boxing/parts/boxing_multi_ops_part.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
// ignore_for_file: invalid_use_of_protected_member, unnecessary_this
|
||||
|
||||
part of '../../boxing_page.dart';
|
||||
|
||||
extension _BoxingMultiOpsPart on _BoxingPageState {
|
||||
void _finishManyToOneBox() {
|
||||
setState(() {
|
||||
_manyToOnePackedItems.clear();
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
_deletingPackedItemId = null;
|
||||
_zongpaiNo = null;
|
||||
_phase = BoxingPhase.waiting;
|
||||
_quantityController.clear();
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _startEditPackedItem(ManyToOnePackedItem item) {
|
||||
setState(() {
|
||||
_editingPackedItemId = item.boxItemId;
|
||||
_editingPackedQuantity = item.quantity.toString();
|
||||
_deletingPackedItemId = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelEditPackedItem() {
|
||||
setState(() {
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _savePackedItem(ManyToOnePackedItem item) async {
|
||||
final quantity = int.tryParse(_editingPackedQuantity);
|
||||
final boxNo = int.tryParse(_boxNoController.text.trim());
|
||||
if (quantity == null || quantity <= 0 || boxNo == null || boxNo <= 0) {
|
||||
this._showStatusOverride(
|
||||
'请输入有效箱号和数量',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.updateBoxRecord(
|
||||
boxItemId: item.boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
if (action.success) {
|
||||
setState(() {
|
||||
final index = _manyToOnePackedItems.indexWhere(
|
||||
(packed) => packed.boxItemId == item.boxItemId,
|
||||
);
|
||||
if (index >= 0) {
|
||||
_manyToOnePackedItems[index] = _manyToOnePackedItems[index].copyWith(
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
}
|
||||
this._replaceExistingPackedItem(item, boxNo, quantity);
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
});
|
||||
this._showStatusOverride(
|
||||
'修改成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
this._showActionError(action, fallback: '修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deletePackedItem(ManyToOnePackedItem item) async {
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.deleteBoxRecord(boxItemId: item.boxItemId);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
if (action.success) {
|
||||
_manyToOnePackedItems.removeWhere(
|
||||
(packed) => packed.boxItemId == item.boxItemId,
|
||||
);
|
||||
this._removeExistingBoxItem(item.boxItemId);
|
||||
if (_editingPackedItemId == item.boxItemId) {
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
}
|
||||
_deletingPackedItemId = null;
|
||||
}
|
||||
});
|
||||
|
||||
if (action.success) {
|
||||
this._showStatusOverride(
|
||||
'删除成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
this._showActionError(action, fallback: '删除失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
271
lib/pages/boxing/parts/boxing_scan_part.dart
Normal file
271
lib/pages/boxing/parts/boxing_scan_part.dart
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
133
lib/pages/boxing/parts/boxing_single_ops_part.dart
Normal file
133
lib/pages/boxing/parts/boxing_single_ops_part.dart
Normal file
@@ -0,0 +1,133 @@
|
||||
// ignore_for_file: invalid_use_of_protected_member, unnecessary_this
|
||||
|
||||
part of '../../boxing_page.dart';
|
||||
|
||||
extension _BoxingSingleOpsPart on _BoxingPageState {
|
||||
void _startEditAssigned(CurrentZongpaiBoxData item) {
|
||||
setState(() {
|
||||
_editingAssignedItemId = item.boxItemId;
|
||||
_editingAssignedQuantity = item.quantity.toString();
|
||||
_deletingAssignedItemId = null;
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelEditAssigned() {
|
||||
setState(() {
|
||||
_editingAssignedItemId = null;
|
||||
_editingAssignedQuantity = '';
|
||||
});
|
||||
}
|
||||
|
||||
int _maxAssignedQuantity(CurrentZongpaiBoxData item) {
|
||||
return boxing_calculations.maxAssignedQuantity(
|
||||
totalQuantity: _erpQuantity,
|
||||
packedQuantity: _packedQuantity,
|
||||
itemQuantity: item.quantity,
|
||||
);
|
||||
}
|
||||
|
||||
void _refreshSingleCodeNewInput({bool focusQuantity = true}) {
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
final remaining = _remainingQuantity;
|
||||
if (remaining > 0) {
|
||||
_quantityController.text = remaining.toString();
|
||||
_quantityController.selection = TextSelection(
|
||||
baseOffset: 0,
|
||||
extentOffset: _quantityController.text.length,
|
||||
);
|
||||
if (focusQuantity) {
|
||||
_focusQuantityInput();
|
||||
}
|
||||
} else {
|
||||
_quantityController.clear();
|
||||
}
|
||||
_isDuplicateBoxNo = false;
|
||||
}
|
||||
|
||||
Future<void> _saveAssignedItem(CurrentZongpaiBoxData item) async {
|
||||
final quantity = int.tryParse(_editingAssignedQuantity);
|
||||
if (quantity == null || quantity <= 0) {
|
||||
this._showStatusOverride(
|
||||
'请输入有效数量',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (quantity > this._maxAssignedQuantity(item)) {
|
||||
this._showStatusOverride(
|
||||
'超出可装数量上限',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.updateBoxRecord(
|
||||
boxItemId: item.boxItemId,
|
||||
boxNo: item.boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
if (action.success) {
|
||||
setState(() {
|
||||
_currentZongpaiBoxes = _currentZongpaiBoxes.map((box) {
|
||||
if (box.boxItemId != item.boxItemId) return box;
|
||||
return CurrentZongpaiBoxData(
|
||||
boxItemId: box.boxItemId,
|
||||
boxNo: box.boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
}).toList();
|
||||
this._replaceExistingBoxItem(item, item.boxNo, quantity);
|
||||
_editingAssignedItemId = null;
|
||||
_editingAssignedQuantity = '';
|
||||
this._refreshSingleCodeNewInput();
|
||||
});
|
||||
this._showStatusOverride(
|
||||
'修改成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
this._showActionError(action, fallback: '修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteAssignedItem(CurrentZongpaiBoxData item) async {
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.deleteBoxRecord(boxItemId: item.boxItemId);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
if (action.success) {
|
||||
_currentZongpaiBoxes = _currentZongpaiBoxes
|
||||
.where((box) => box.boxItemId != item.boxItemId)
|
||||
.toList();
|
||||
this._removeExistingBoxItem(item.boxItemId);
|
||||
if (_editingAssignedItemId == item.boxItemId) {
|
||||
_editingAssignedItemId = null;
|
||||
_editingAssignedQuantity = '';
|
||||
}
|
||||
_deletingAssignedItemId = null;
|
||||
this._refreshSingleCodeNewInput();
|
||||
}
|
||||
});
|
||||
|
||||
if (action.success) {
|
||||
this._showStatusOverride(
|
||||
'删除成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
this._showActionError(action, fallback: '删除失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
86
lib/pages/boxing/parts/boxing_status_part.dart
Normal file
86
lib/pages/boxing/parts/boxing_status_part.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
// ignore_for_file: invalid_use_of_protected_member, unnecessary_this
|
||||
|
||||
part of '../../boxing_page.dart';
|
||||
|
||||
extension _BoxingStatusPart on _BoxingPageState {
|
||||
StatusDotColor _dotForActionError(BoxingActionErrorKind? kind) {
|
||||
return switch (kind) {
|
||||
BoxingActionErrorKind.network => StatusDotColor.yellow,
|
||||
BoxingActionErrorKind.duplicate => StatusDotColor.amber,
|
||||
_ => StatusDotColor.red,
|
||||
};
|
||||
}
|
||||
|
||||
void _showActionError<T>(
|
||||
BoxingActionResult<T> action, {
|
||||
required String fallback,
|
||||
}) {
|
||||
_feedbackService.trigger(switch (action.errorKind) {
|
||||
BoxingActionErrorKind.network => FeedbackEvent.networkError,
|
||||
BoxingActionErrorKind.duplicate => FeedbackEvent.duplicateBoxNo,
|
||||
_ => FeedbackEvent.submitFailure,
|
||||
});
|
||||
this._showStatusOverride(
|
||||
action.errorMessage ?? fallback,
|
||||
this._dotForActionError(action.errorKind),
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
|
||||
void _showStatusOverride(String text, StatusDotColor dot, Duration duration) {
|
||||
setState(() {
|
||||
_statusOverrideText = text;
|
||||
_statusOverrideDot = dot;
|
||||
});
|
||||
Future.delayed(duration, () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _jumpToCompletedBox() {
|
||||
final boxNo = _completedJumpBoxNo;
|
||||
if (boxNo == null) return;
|
||||
setState(() {
|
||||
_boxNoController.text = boxNo.toString();
|
||||
_completedJumpBoxNo = null;
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _openDetail() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => BoxingDetailPage(
|
||||
paichanNo: _paichanNo ?? '',
|
||||
existingBoxes: _existingBoxes,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateBaseStatus() {
|
||||
final status = boxingStatusFor(
|
||||
isAutoProcessing: _isAutoProcessing,
|
||||
isSubmitting: _isSubmitting,
|
||||
isDuplicateBoxNo: _isDuplicateBoxNo,
|
||||
quantityTooHigh: _quantityTooHigh,
|
||||
isEditingAssigned: _editingAssignedItemId != null,
|
||||
isDeletingAssigned: _deletingAssignedItemId != null,
|
||||
hasPaichanSwitchNotice: _paichanSwitchNotice != null,
|
||||
phase: _phase,
|
||||
isMultiCode: _mode == BoxingMode.multiCode,
|
||||
hasVisibleManyToOneItems: _visibleManyToOnePackedItems.isNotEmpty,
|
||||
remainingQuantity: _remainingQuantity,
|
||||
boxNoText: _boxNoController.text,
|
||||
);
|
||||
_statusDot = status.dot;
|
||||
_statusText = status.text;
|
||||
}
|
||||
}
|
||||
161
lib/pages/boxing/parts/boxing_submit_part.dart
Normal file
161
lib/pages/boxing/parts/boxing_submit_part.dart
Normal file
@@ -0,0 +1,161 @@
|
||||
// ignore_for_file: invalid_use_of_protected_member, unnecessary_this
|
||||
|
||||
part of '../../boxing_page.dart';
|
||||
|
||||
extension _BoxingSubmitPart on _BoxingPageState {
|
||||
Future<bool> _submit() async {
|
||||
if (!_canSubmit) return false;
|
||||
|
||||
final boxNo = int.parse(_boxNoController.text);
|
||||
final quantity = int.parse(_quantityController.text);
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
|
||||
final editing = _editingBox;
|
||||
final action = editing == null
|
||||
? await _apiActions.saveBoxRecord(
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
)
|
||||
: await _apiActions.updateBoxRecord(
|
||||
boxItemId: editing.boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
|
||||
if (!mounted) return false;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
|
||||
if (action.success) {
|
||||
if (editing == null) {
|
||||
this._onSubmitSuccess(boxNo, quantity, action.data!.boxItemId);
|
||||
} else {
|
||||
this._onUpdateSuccess(editing, boxNo, quantity);
|
||||
}
|
||||
return true;
|
||||
} else if (action.errorKind == BoxingActionErrorKind.duplicate) {
|
||||
_feedbackService.trigger(FeedbackEvent.duplicateBoxNo);
|
||||
this._showStatusOverride(
|
||||
'该总排号在箱号 ${action.boxNo ?? ""} 已存在,请重新输入',
|
||||
StatusDotColor.amber,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
this._showActionError(action, fallback: '提交失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _onSubmitSuccess(int boxNo, int quantity, int? boxItemId) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
|
||||
setState(() {
|
||||
if (_mode == BoxingMode.singleCode && boxItemId != null) {
|
||||
_currentZongpaiBoxes = List.from(_currentZongpaiBoxes)
|
||||
..add(
|
||||
CurrentZongpaiBoxData(
|
||||
boxItemId: boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
),
|
||||
);
|
||||
}
|
||||
this._addExistingBoxItem(boxNo, quantity, boxItemId);
|
||||
_maxBoxNo = _maxBoxNo > boxNo ? _maxBoxNo : boxNo;
|
||||
});
|
||||
|
||||
switch (_mode) {
|
||||
case BoxingMode.singleCode:
|
||||
final remaining = _remainingQuantity;
|
||||
if (remaining <= 0) {
|
||||
this._showStatusOverride(
|
||||
'装箱成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
setState(() {
|
||||
_phase = BoxingPhase.scanned;
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
_quantityController.text = remaining.toString();
|
||||
_quantityController.selection = TextSelection(
|
||||
baseOffset: 0,
|
||||
extentOffset: _quantityController.text.length,
|
||||
);
|
||||
_isDuplicateBoxNo = false;
|
||||
});
|
||||
_focusQuantityInput();
|
||||
this._showStatusOverride(
|
||||
'请继续完成剩余数量装箱',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
}
|
||||
|
||||
case BoxingMode.multiCode:
|
||||
_lastScannedPaichanNo = _paichanNo;
|
||||
setState(() {
|
||||
if (boxItemId != null) {
|
||||
_manyToOnePackedItems.add(
|
||||
ManyToOnePackedItem(
|
||||
boxItemId: boxItemId,
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
paichanNo: _paichanNo,
|
||||
workOrderNo: _workOrderNo,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
totalQuantity: _erpQuantity,
|
||||
),
|
||||
);
|
||||
}
|
||||
_phase = BoxingPhase.waiting;
|
||||
_zongpaiNo = null;
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
_deletingPackedItemId = null;
|
||||
});
|
||||
this._showStatusOverride(
|
||||
'装箱成功,请继续扫码',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdateSuccess(
|
||||
CurrentZongpaiBoxData editing,
|
||||
int boxNo,
|
||||
int quantity,
|
||||
) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
|
||||
setState(() {
|
||||
_currentZongpaiBoxes = _currentZongpaiBoxes.map((item) {
|
||||
if (item.boxItemId != editing.boxItemId) return item;
|
||||
return CurrentZongpaiBoxData(
|
||||
boxItemId: item.boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
}).toList();
|
||||
this._replaceExistingBoxItem(editing, boxNo, quantity);
|
||||
_editingBox = null;
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
final remaining = _remainingQuantity;
|
||||
if (remaining > 0) {
|
||||
_quantityController.text = remaining.toString();
|
||||
} else {
|
||||
_quantityController.clear();
|
||||
}
|
||||
_isDuplicateBoxNo = false;
|
||||
});
|
||||
this._showStatusOverride(
|
||||
'修改成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: unnecessary_this
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -25,6 +27,13 @@ import 'package:pad_scanner/widgets/status_bar.dart';
|
||||
export 'package:pad_scanner/pages/boxing/boxing_models.dart'
|
||||
show BoxingMode, BoxingPageArguments;
|
||||
|
||||
part 'boxing/parts/boxing_box_mutation_part.dart';
|
||||
part 'boxing/parts/boxing_multi_ops_part.dart';
|
||||
part 'boxing/parts/boxing_scan_part.dart';
|
||||
part 'boxing/parts/boxing_single_ops_part.dart';
|
||||
part 'boxing/parts/boxing_status_part.dart';
|
||||
part 'boxing/parts/boxing_submit_part.dart';
|
||||
|
||||
// === 主页面 ===
|
||||
|
||||
class BoxingPage extends StatefulWidget {
|
||||
@@ -108,10 +117,12 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
_mode = arguments.initialMode;
|
||||
}
|
||||
_boxNoController.addListener(_onBoxNoTextChanged);
|
||||
_scanSubscription = _scannerService.scanResults.listen(_onScan);
|
||||
_scanSubscription = _scannerService.scanResults.listen(
|
||||
(result) => this._onScan(result),
|
||||
);
|
||||
if (arguments != null && arguments.autoScanCodes.isNotEmpty) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_processAutoScanCodes(arguments.autoScanCodes);
|
||||
this._processAutoScanCodes(arguments.autoScanCodes);
|
||||
});
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _requestFocus());
|
||||
@@ -152,11 +163,11 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
if (_isFinishBoxKey(event)) {
|
||||
final hasItems = _visibleManyToOnePackedItems.isNotEmpty;
|
||||
if (hasItems && _mode == BoxingMode.multiCode) {
|
||||
_finishManyToOneBox();
|
||||
this._finishManyToOneBox();
|
||||
}
|
||||
}
|
||||
if (_completedJumpBoxNo != null && _isJumpToBoxKey(event)) {
|
||||
_jumpToCompletedBox();
|
||||
this._jumpToCompletedBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,276 +246,6 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
_statusOverrideDot = null;
|
||||
}
|
||||
|
||||
// === 扫码处理 ===
|
||||
|
||||
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);
|
||||
_showStatusOverride(
|
||||
'无效码,请重新扫描',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final zongpai = parsed.value;
|
||||
|
||||
// 三种模式都允许后扫入的总排号覆盖当前扫码区;未确认数据不会入库。
|
||||
_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,
|
||||
});
|
||||
_showStatusOverride(
|
||||
action.errorMessage ?? '查询失败',
|
||||
_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;
|
||||
}
|
||||
|
||||
// 根据模式自动填充
|
||||
_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 _queryBoxInfo(code);
|
||||
if (!ok && mounted) {
|
||||
setState(() => _autoWarnings.add('总排号 $code 未找到排产号信息,已忽略'));
|
||||
}
|
||||
} else {
|
||||
for (final code in codes) {
|
||||
if (!mounted) return;
|
||||
final queried = await _queryBoxInfo(code);
|
||||
if (!queried) {
|
||||
if (mounted) {
|
||||
setState(() => _autoWarnings.add('总排号 $code 未找到排产号信息,已忽略'));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!_canSubmit) {
|
||||
if (mounted) {
|
||||
setState(() => _autoWarnings.add('总排号 $code 暂不能装箱,已忽略'));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
final saved = await _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);
|
||||
_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 ? _confirmPaichanSwitch() : _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;
|
||||
});
|
||||
}
|
||||
|
||||
// === 重复箱号检测 ===
|
||||
|
||||
bool _boxNoIsDuplicate() {
|
||||
@@ -557,7 +298,7 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
|
||||
void _submitFromKeyboard() {
|
||||
if (_canSubmit) {
|
||||
_submit();
|
||||
this._submit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,545 +326,11 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> _submit() async {
|
||||
if (!_canSubmit) return false;
|
||||
|
||||
final boxNo = int.parse(_boxNoController.text);
|
||||
final quantity = int.parse(_quantityController.text);
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
|
||||
final editing = _editingBox;
|
||||
final action = editing == null
|
||||
? await _apiActions.saveBoxRecord(
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
)
|
||||
: await _apiActions.updateBoxRecord(
|
||||
boxItemId: editing.boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
|
||||
if (!mounted) return false;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
|
||||
if (action.success) {
|
||||
if (editing == null) {
|
||||
_onSubmitSuccess(boxNo, quantity, action.data!.boxItemId);
|
||||
} else {
|
||||
_onUpdateSuccess(editing, boxNo, quantity);
|
||||
}
|
||||
return true;
|
||||
} else if (action.errorKind == BoxingActionErrorKind.duplicate) {
|
||||
_feedbackService.trigger(FeedbackEvent.duplicateBoxNo);
|
||||
_showStatusOverride(
|
||||
'该总排号在箱号 ${action.boxNo ?? ""} 已存在,请重新输入',
|
||||
StatusDotColor.amber,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
_showActionError(action, fallback: '提交失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _onSubmitSuccess(int boxNo, int quantity, int? boxItemId) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
|
||||
setState(() {
|
||||
if (_mode == BoxingMode.singleCode && boxItemId != null) {
|
||||
_currentZongpaiBoxes = List.from(_currentZongpaiBoxes)
|
||||
..add(
|
||||
CurrentZongpaiBoxData(
|
||||
boxItemId: boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
),
|
||||
);
|
||||
}
|
||||
_addExistingBoxItem(boxNo, quantity, boxItemId);
|
||||
_maxBoxNo = _maxBoxNo > boxNo ? _maxBoxNo : boxNo;
|
||||
});
|
||||
|
||||
switch (_mode) {
|
||||
case BoxingMode.singleCode:
|
||||
final remaining = _remainingQuantity;
|
||||
if (remaining <= 0) {
|
||||
_showStatusOverride(
|
||||
'装箱成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
setState(() {
|
||||
_phase = BoxingPhase.scanned;
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
_quantityController.text = remaining.toString();
|
||||
_quantityController.selection = TextSelection(
|
||||
baseOffset: 0,
|
||||
extentOffset: _quantityController.text.length,
|
||||
);
|
||||
_isDuplicateBoxNo = false;
|
||||
});
|
||||
_focusQuantityInput();
|
||||
_showStatusOverride(
|
||||
'请继续完成剩余数量装箱',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
}
|
||||
|
||||
case BoxingMode.multiCode:
|
||||
_lastScannedPaichanNo = _paichanNo;
|
||||
setState(() {
|
||||
if (boxItemId != null) {
|
||||
_manyToOnePackedItems.add(
|
||||
ManyToOnePackedItem(
|
||||
boxItemId: boxItemId,
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
paichanNo: _paichanNo,
|
||||
workOrderNo: _workOrderNo,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
totalQuantity: _erpQuantity,
|
||||
),
|
||||
);
|
||||
}
|
||||
_phase = BoxingPhase.waiting;
|
||||
_zongpaiNo = null;
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
_deletingPackedItemId = null;
|
||||
});
|
||||
_showStatusOverride(
|
||||
'装箱成功,请继续扫码',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdateSuccess(
|
||||
CurrentZongpaiBoxData editing,
|
||||
int boxNo,
|
||||
int quantity,
|
||||
) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
|
||||
setState(() {
|
||||
_currentZongpaiBoxes = _currentZongpaiBoxes.map((item) {
|
||||
if (item.boxItemId != editing.boxItemId) return item;
|
||||
return CurrentZongpaiBoxData(
|
||||
boxItemId: item.boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
}).toList();
|
||||
_replaceExistingBoxItem(editing, boxNo, quantity);
|
||||
_editingBox = null;
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
final remaining = _remainingQuantity;
|
||||
if (remaining > 0) {
|
||||
_quantityController.text = remaining.toString();
|
||||
} else {
|
||||
_quantityController.clear();
|
||||
}
|
||||
_isDuplicateBoxNo = false;
|
||||
});
|
||||
_showStatusOverride(
|
||||
'修改成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
}
|
||||
|
||||
void _replaceExistingBoxItem(
|
||||
CurrentZongpaiBoxData editing,
|
||||
int boxNo,
|
||||
int quantity,
|
||||
) {
|
||||
final result = box_mutations.replaceExistingBoxItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
editing: editing,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
workOrderNo: _workOrderNo,
|
||||
totalQuantity: _erpQuantity,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
void _addExistingBoxItem(int boxNo, int quantity, int? boxItemId) {
|
||||
final result = box_mutations.addExistingBoxItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
boxItemId: boxItemId,
|
||||
zongpaiNo: _zongpaiNo!,
|
||||
workOrderNo: _workOrderNo,
|
||||
totalQuantity: _erpQuantity,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
void _finishManyToOneBox() {
|
||||
setState(() {
|
||||
_manyToOnePackedItems.clear();
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
_deletingPackedItemId = null;
|
||||
_zongpaiNo = null;
|
||||
_phase = BoxingPhase.waiting;
|
||||
_quantityController.clear();
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _startEditAssigned(CurrentZongpaiBoxData item) {
|
||||
setState(() {
|
||||
_editingAssignedItemId = item.boxItemId;
|
||||
_editingAssignedQuantity = item.quantity.toString();
|
||||
_deletingAssignedItemId = null;
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelEditAssigned() {
|
||||
setState(() {
|
||||
_editingAssignedItemId = null;
|
||||
_editingAssignedQuantity = '';
|
||||
});
|
||||
}
|
||||
|
||||
int _maxAssignedQuantity(CurrentZongpaiBoxData item) {
|
||||
return boxing_calculations.maxAssignedQuantity(
|
||||
totalQuantity: _erpQuantity,
|
||||
packedQuantity: _packedQuantity,
|
||||
itemQuantity: item.quantity,
|
||||
);
|
||||
}
|
||||
|
||||
void _refreshSingleCodeNewInput({bool focusQuantity = true}) {
|
||||
_boxNoController.text = (_maxBoxNo + 1).toString();
|
||||
final remaining = _remainingQuantity;
|
||||
if (remaining > 0) {
|
||||
_quantityController.text = remaining.toString();
|
||||
_quantityController.selection = TextSelection(
|
||||
baseOffset: 0,
|
||||
extentOffset: _quantityController.text.length,
|
||||
);
|
||||
if (focusQuantity) {
|
||||
_focusQuantityInput();
|
||||
}
|
||||
} else {
|
||||
_quantityController.clear();
|
||||
}
|
||||
_isDuplicateBoxNo = false;
|
||||
}
|
||||
|
||||
Future<void> _saveAssignedItem(CurrentZongpaiBoxData item) async {
|
||||
final quantity = int.tryParse(_editingAssignedQuantity);
|
||||
if (quantity == null || quantity <= 0) {
|
||||
_showStatusOverride(
|
||||
'请输入有效数量',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (quantity > _maxAssignedQuantity(item)) {
|
||||
_showStatusOverride(
|
||||
'超出可装数量上限',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.updateBoxRecord(
|
||||
boxItemId: item.boxItemId,
|
||||
boxNo: item.boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
if (action.success) {
|
||||
setState(() {
|
||||
_currentZongpaiBoxes = _currentZongpaiBoxes.map((box) {
|
||||
if (box.boxItemId != item.boxItemId) return box;
|
||||
return CurrentZongpaiBoxData(
|
||||
boxItemId: box.boxItemId,
|
||||
boxNo: box.boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
}).toList();
|
||||
_replaceExistingBoxItem(item, item.boxNo, quantity);
|
||||
_editingAssignedItemId = null;
|
||||
_editingAssignedQuantity = '';
|
||||
_refreshSingleCodeNewInput();
|
||||
});
|
||||
_showStatusOverride(
|
||||
'修改成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
_showActionError(action, fallback: '修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteAssignedItem(CurrentZongpaiBoxData item) async {
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.deleteBoxRecord(boxItemId: item.boxItemId);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
if (action.success) {
|
||||
_currentZongpaiBoxes = _currentZongpaiBoxes
|
||||
.where((box) => box.boxItemId != item.boxItemId)
|
||||
.toList();
|
||||
_removeExistingBoxItem(item.boxItemId);
|
||||
if (_editingAssignedItemId == item.boxItemId) {
|
||||
_editingAssignedItemId = null;
|
||||
_editingAssignedQuantity = '';
|
||||
}
|
||||
_deletingAssignedItemId = null;
|
||||
_refreshSingleCodeNewInput();
|
||||
}
|
||||
});
|
||||
|
||||
if (action.success) {
|
||||
_showStatusOverride(
|
||||
'删除成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
_showActionError(action, fallback: '删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
void _startEditPackedItem(ManyToOnePackedItem item) {
|
||||
setState(() {
|
||||
_editingPackedItemId = item.boxItemId;
|
||||
_editingPackedQuantity = item.quantity.toString();
|
||||
_deletingPackedItemId = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _cancelEditPackedItem() {
|
||||
setState(() {
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _savePackedItem(ManyToOnePackedItem item) async {
|
||||
final quantity = int.tryParse(_editingPackedQuantity);
|
||||
final boxNo = int.tryParse(_boxNoController.text.trim());
|
||||
if (quantity == null || quantity <= 0 || boxNo == null || boxNo <= 0) {
|
||||
_showStatusOverride(
|
||||
'请输入有效箱号和数量',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.updateBoxRecord(
|
||||
boxItemId: item.boxItemId,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
if (action.success) {
|
||||
setState(() {
|
||||
final index = _manyToOnePackedItems.indexWhere(
|
||||
(packed) => packed.boxItemId == item.boxItemId,
|
||||
);
|
||||
if (index >= 0) {
|
||||
_manyToOnePackedItems[index] = _manyToOnePackedItems[index].copyWith(
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
}
|
||||
_replaceExistingPackedItem(item, boxNo, quantity);
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
});
|
||||
_showStatusOverride(
|
||||
'修改成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
_showActionError(action, fallback: '修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
void _replaceExistingPackedItem(
|
||||
ManyToOnePackedItem item,
|
||||
int boxNo,
|
||||
int quantity,
|
||||
) {
|
||||
final result = box_mutations.replaceExistingPackedItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
item: item,
|
||||
boxNo: boxNo,
|
||||
quantity: quantity,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
Future<void> _deletePackedItem(ManyToOnePackedItem item) async {
|
||||
setState(() => _isSubmitting = true);
|
||||
final action = await _apiActions.deleteBoxRecord(boxItemId: item.boxItemId);
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
if (action.success) {
|
||||
_manyToOnePackedItems.removeWhere(
|
||||
(packed) => packed.boxItemId == item.boxItemId,
|
||||
);
|
||||
_removeExistingBoxItem(item.boxItemId);
|
||||
if (_editingPackedItemId == item.boxItemId) {
|
||||
_editingPackedItemId = null;
|
||||
_editingPackedQuantity = '';
|
||||
}
|
||||
_deletingPackedItemId = null;
|
||||
}
|
||||
});
|
||||
|
||||
if (action.success) {
|
||||
_showStatusOverride(
|
||||
'删除成功',
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
} else {
|
||||
_showActionError(action, fallback: '删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
void _removeExistingBoxItem(int boxItemId) {
|
||||
final result = box_mutations.removeExistingBoxItem(
|
||||
existingBoxes: _existingBoxes,
|
||||
boxItemId: boxItemId,
|
||||
);
|
||||
_existingBoxes = result.boxes;
|
||||
_maxBoxNo = result.maxBoxNo;
|
||||
}
|
||||
|
||||
// === Status bar management ===
|
||||
|
||||
StatusDotColor _dotForActionError(BoxingActionErrorKind? kind) {
|
||||
return switch (kind) {
|
||||
BoxingActionErrorKind.network => StatusDotColor.yellow,
|
||||
BoxingActionErrorKind.duplicate => StatusDotColor.amber,
|
||||
_ => StatusDotColor.red,
|
||||
};
|
||||
}
|
||||
|
||||
void _showActionError<T>(
|
||||
BoxingActionResult<T> action, {
|
||||
required String fallback,
|
||||
}) {
|
||||
_feedbackService.trigger(switch (action.errorKind) {
|
||||
BoxingActionErrorKind.network => FeedbackEvent.networkError,
|
||||
BoxingActionErrorKind.duplicate => FeedbackEvent.duplicateBoxNo,
|
||||
_ => FeedbackEvent.submitFailure,
|
||||
});
|
||||
_showStatusOverride(
|
||||
action.errorMessage ?? fallback,
|
||||
_dotForActionError(action.errorKind),
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
|
||||
void _showStatusOverride(String text, StatusDotColor dot, Duration duration) {
|
||||
setState(() {
|
||||
_statusOverrideText = text;
|
||||
_statusOverrideDot = dot;
|
||||
});
|
||||
Future.delayed(duration, () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _jumpToCompletedBox() {
|
||||
final boxNo = _completedJumpBoxNo;
|
||||
if (boxNo == null) return;
|
||||
setState(() {
|
||||
_boxNoController.text = boxNo.toString();
|
||||
_completedJumpBoxNo = null;
|
||||
_statusOverrideText = null;
|
||||
_statusOverrideDot = null;
|
||||
});
|
||||
}
|
||||
|
||||
// === 导航到详情页 ===
|
||||
|
||||
void _openDetail() {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => BoxingDetailPage(
|
||||
paichanNo: _paichanNo ?? '',
|
||||
existingBoxes: _existingBoxes,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// === 状态文字 ===
|
||||
|
||||
void _updateBaseStatus() {
|
||||
final status = boxingStatusFor(
|
||||
isAutoProcessing: _isAutoProcessing,
|
||||
isSubmitting: _isSubmitting,
|
||||
isDuplicateBoxNo: _isDuplicateBoxNo,
|
||||
quantityTooHigh: _quantityTooHigh,
|
||||
isEditingAssigned: _editingAssignedItemId != null,
|
||||
isDeletingAssigned: _deletingAssignedItemId != null,
|
||||
hasPaichanSwitchNotice: _paichanSwitchNotice != null,
|
||||
phase: _phase,
|
||||
isMultiCode: _mode == BoxingMode.multiCode,
|
||||
hasVisibleManyToOneItems: _visibleManyToOnePackedItems.isNotEmpty,
|
||||
remainingQuantity: _remainingQuantity,
|
||||
boxNoText: _boxNoController.text,
|
||||
);
|
||||
_statusDot = status.dot;
|
||||
_statusText = status.text;
|
||||
}
|
||||
|
||||
// === Build ===
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_updateBaseStatus();
|
||||
this._updateBaseStatus();
|
||||
final effectiveDot = _statusOverrideDot ?? _statusDot;
|
||||
final effectiveText = _statusOverrideText ?? _statusText;
|
||||
|
||||
@@ -1160,7 +367,7 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
remainingQuantity: _remainingQuantity,
|
||||
zongpaiNo: _zongpaiNo,
|
||||
completedJumpBoxNo: _completedJumpBoxNo,
|
||||
onJumpToCompletedBox: _jumpToCompletedBox,
|
||||
onJumpToCompletedBox: () => this._jumpToCompletedBox(),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
@@ -1185,16 +392,18 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
editingPackedItemId: _editingPackedItemId,
|
||||
editingPackedQuantity: _editingPackedQuantity,
|
||||
deletingPackedItemId: _deletingPackedItemId,
|
||||
onOpenDetail: _openDetail,
|
||||
onOpenDetail: () => this._openDetail(),
|
||||
onSubmitFromKeyboard: _submitFromKeyboard,
|
||||
onSubmit: _submit,
|
||||
onSubmit: () => this._submit(),
|
||||
onQuantityChanged: () => setState(() {}),
|
||||
onFinishBox: _finishManyToOneBox,
|
||||
onFinishBox: () => this._finishManyToOneBox(),
|
||||
onEditingPackedQuantityChanged: (value) =>
|
||||
_editingPackedQuantity = value,
|
||||
onSavePackedItem: _savePackedItem,
|
||||
onCancelEditPackedItem: _cancelEditPackedItem,
|
||||
onStartEditPackedItem: _startEditPackedItem,
|
||||
onSavePackedItem: (item) => this._savePackedItem(item),
|
||||
onCancelEditPackedItem: () =>
|
||||
this._cancelEditPackedItem(),
|
||||
onStartEditPackedItem: (item) =>
|
||||
this._startEditPackedItem(item),
|
||||
onStartDeletePackedItem: (item) {
|
||||
setState(() {
|
||||
_deletingPackedItemId = item.boxItemId;
|
||||
@@ -1202,7 +411,8 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
_editingPackedQuantity = '';
|
||||
});
|
||||
},
|
||||
onDeletePackedItem: _deletePackedItem,
|
||||
onDeletePackedItem: (item) =>
|
||||
this._deletePackedItem(item),
|
||||
onCancelDeletePackedItem: () =>
|
||||
setState(() => _deletingPackedItemId = null),
|
||||
)
|
||||
@@ -1232,16 +442,18 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
editingAssignedItemId: _editingAssignedItemId,
|
||||
editingAssignedQuantity: _editingAssignedQuantity,
|
||||
deletingAssignedItemId: _deletingAssignedItemId,
|
||||
onOpenDetail: _openDetail,
|
||||
onOpenDetail: () => this._openDetail(),
|
||||
onCheckDuplicateBoxNo: _checkDuplicateBoxNo,
|
||||
onSubmitFromKeyboard: _submitFromKeyboard,
|
||||
onSubmit: _submit,
|
||||
onSubmit: () => this._submit(),
|
||||
onQuantityChanged: () => setState(() {}),
|
||||
onEditingAssignedQuantityChanged: (value) =>
|
||||
_editingAssignedQuantity = value,
|
||||
onSaveAssignedItem: _saveAssignedItem,
|
||||
onCancelEditAssigned: _cancelEditAssigned,
|
||||
onStartEditAssigned: _startEditAssigned,
|
||||
onSaveAssignedItem: (item) =>
|
||||
this._saveAssignedItem(item),
|
||||
onCancelEditAssigned: () => this._cancelEditAssigned(),
|
||||
onStartEditAssigned: (item) =>
|
||||
this._startEditAssigned(item),
|
||||
onStartDeleteAssigned: (item) {
|
||||
setState(() {
|
||||
_deletingAssignedItemId = item.boxItemId;
|
||||
@@ -1249,7 +461,8 @@ class _BoxingPageState extends State<BoxingPage> with WidgetsBindingObserver {
|
||||
_editingAssignedQuantity = '';
|
||||
});
|
||||
},
|
||||
onDeleteAssignedItem: _deleteAssignedItem,
|
||||
onDeleteAssignedItem: (item) =>
|
||||
this._deleteAssignedItem(item),
|
||||
onCancelDeleteAssigned: () =>
|
||||
setState(() => _deletingAssignedItemId = null),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user