refactor: split registration page into focused parts
This commit is contained in:
317
lib/pages/registration/parts/registration_submit_part.dart
Normal file
317
lib/pages/registration/parts/registration_submit_part.dart
Normal file
@@ -0,0 +1,317 @@
|
||||
// ignore_for_file: invalid_use_of_protected_member
|
||||
|
||||
part of '../../registration_page.dart';
|
||||
|
||||
extension _RegistrationSubmitPart on _RegistrationPageState {
|
||||
Future<void> _submit() async {
|
||||
if (!_canSubmit) return;
|
||||
|
||||
final baseUrl = await _baseUrl();
|
||||
if (baseUrl == null) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showStatusOverride(
|
||||
'未配置 API 地址,请前往设置',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
|
||||
// For transit target: block if any scanned item already has a location
|
||||
if (_isTransitTarget) {
|
||||
if (_hasAnyLocatedInScanned()) {
|
||||
final onShelfItems = _findOnShelfItemsInScanned();
|
||||
final transferredItems = _findTransferredItemsInScanned();
|
||||
setState(() => _isSubmitting = false);
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showLocationConflictDialog(
|
||||
onShelfItems: onShelfItems,
|
||||
transferredItems: transferredItems,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_mode == RegistrationMode.multiCode && _zongpaiNos.length > 1) {
|
||||
if (_isTransitTarget && !await _ensureBatchSamePaicha(baseUrl)) {
|
||||
if (!mounted) return;
|
||||
setState(() => _isSubmitting = false);
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showStatusOverride(
|
||||
batchPaichaMismatchMessage,
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Pre-check: block entire batch if any item already has a location binding
|
||||
if (!_isTransitTarget) {
|
||||
final onShelfItems = _findOnShelfItemsInScanned();
|
||||
final transferredItems = _findTransferredItemsInScanned();
|
||||
if (onShelfItems.isNotEmpty || transferredItems.isNotEmpty) {
|
||||
setState(() => _isSubmitting = false);
|
||||
_feedbackService.trigger(FeedbackEvent.duplicateError);
|
||||
_showLocationConflictDialog(
|
||||
onShelfItems: onShelfItems,
|
||||
transferredItems: transferredItems,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await _submitBatch(baseUrl);
|
||||
} else {
|
||||
await _submitOne(baseUrl, _zongpaiNos.first);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _ensureBatchSamePaicha(String baseUrl) async {
|
||||
final paichaByCode = <String, String>{};
|
||||
final cachedOverview = _overview;
|
||||
if (cachedOverview?.success == true && cachedOverview!.paichaNo != null) {
|
||||
for (final item in cachedOverview.items) {
|
||||
paichaByCode[item.zongpaiNo] = cachedOverview.paichaNo!;
|
||||
}
|
||||
}
|
||||
|
||||
for (final zongpaiNo in _zongpaiNos) {
|
||||
if (paichaByCode.containsKey(zongpaiNo)) continue;
|
||||
final result = await _apiService.fetchPaichaOverview(
|
||||
baseUrl: baseUrl,
|
||||
zongpaiNo: zongpaiNo,
|
||||
);
|
||||
if (!result.success || result.paichaNo == null) {
|
||||
return false;
|
||||
}
|
||||
for (final item in result.items) {
|
||||
paichaByCode[item.zongpaiNo] = result.paichaNo!;
|
||||
}
|
||||
}
|
||||
|
||||
return allSamePaicha(_zongpaiNos.map((code) => paichaByCode[code]));
|
||||
}
|
||||
|
||||
Future<void> _submitOne(String baseUrl, String zongpaiNo) async {
|
||||
final result = await _apiService.registerLocation(
|
||||
baseUrl: baseUrl,
|
||||
zongpaiNo: zongpaiNo,
|
||||
locationCode: _locationCode!,
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() => _isSubmitting = false);
|
||||
|
||||
if (result.success) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
if (_isTransitTarget) {
|
||||
setState(() => _isSubmitting = false);
|
||||
await _navigateToBoxing(
|
||||
mode: BoxingMode.singleCode,
|
||||
codes: [zongpaiNo],
|
||||
codesToClear: [zongpaiNo],
|
||||
clearLocation: _mode == RegistrationMode.singleCode,
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_zongpaiNos.remove(zongpaiNo);
|
||||
if (_mode == RegistrationMode.singleCode) {
|
||||
_locationCode = null;
|
||||
_locationType = null;
|
||||
}
|
||||
});
|
||||
final msg = result.isOffShelfSuccess
|
||||
? '下架成功'
|
||||
: (_mode == RegistrationMode.multiCode ? '多码上架模式,请扫描下一张执行卡' : '上架成功');
|
||||
_showStatusOverride(
|
||||
msg,
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
await _refreshCurrentOverview();
|
||||
} else if (result.isDuplicate) {
|
||||
_feedbackService.trigger(FeedbackEvent.duplicateError);
|
||||
_showDuplicateDialog(zongpaiNo, result.duplicateInfo);
|
||||
} else if (result.isAlreadyOffShelf) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showStatusOverride(
|
||||
result.errorMessage ?? '该总排号已下架至转运区域,不可重新上架',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
} else {
|
||||
final isNetwork = result.errorMessage == '网络异常,请检查网络连接';
|
||||
_feedbackService.trigger(
|
||||
isNetwork ? FeedbackEvent.networkError : FeedbackEvent.submitFailure,
|
||||
);
|
||||
_showStatusOverride(
|
||||
result.errorMessage ?? '提交失败',
|
||||
isNetwork ? StatusDotColor.yellow : StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _submitBatch(String baseUrl) async {
|
||||
int successCount = 0;
|
||||
int offShelfCount = 0;
|
||||
final failed = <String>[];
|
||||
final toRemove = <String>[];
|
||||
final transitCodes = <String>[];
|
||||
|
||||
for (final zongpaiNo in List.of(_zongpaiNos)) {
|
||||
final result = await _apiService.registerLocation(
|
||||
baseUrl: baseUrl,
|
||||
zongpaiNo: zongpaiNo,
|
||||
locationCode: _locationCode!,
|
||||
);
|
||||
if (result.success) {
|
||||
successCount++;
|
||||
if (result.isOffShelfSuccess) {
|
||||
offShelfCount++;
|
||||
}
|
||||
toRemove.add(zongpaiNo);
|
||||
if (_isTransitTarget) {
|
||||
transitCodes.add(zongpaiNo);
|
||||
}
|
||||
} else {
|
||||
failed.add(zongpaiNo);
|
||||
if (result.isDuplicate) {
|
||||
if (_isTransitTarget && transitCodes.isNotEmpty) {
|
||||
setState(() {
|
||||
_zongpaiNos.removeWhere((item) => toRemove.contains(item));
|
||||
_isSubmitting = false;
|
||||
});
|
||||
await _navigateToBoxing(
|
||||
mode: BoxingMode.multiCode,
|
||||
codes: transitCodes,
|
||||
codesToClear: toRemove,
|
||||
clearLocation: false,
|
||||
returnMessage: '成功 $successCount 条,失败 ${failed.length} 条',
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_zongpaiNos.removeWhere((item) => toRemove.contains(item));
|
||||
_isSubmitting = false;
|
||||
});
|
||||
_feedbackService.trigger(FeedbackEvent.duplicateError);
|
||||
_showDuplicateDialog(zongpaiNo, result.duplicateInfo);
|
||||
return;
|
||||
}
|
||||
if (result.isAlreadyOffShelf) {
|
||||
if (_isTransitTarget && transitCodes.isNotEmpty) {
|
||||
setState(() {
|
||||
_zongpaiNos.removeWhere((item) => toRemove.contains(item));
|
||||
_isSubmitting = false;
|
||||
});
|
||||
await _navigateToBoxing(
|
||||
mode: BoxingMode.multiCode,
|
||||
codes: transitCodes,
|
||||
codesToClear: toRemove,
|
||||
clearLocation: false,
|
||||
returnMessage: '成功 $successCount 条,失败 ${failed.length} 条',
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_zongpaiNos.removeWhere((item) => toRemove.contains(item));
|
||||
_isSubmitting = false;
|
||||
});
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showStatusOverride(
|
||||
result.errorMessage ?? '该总排号已下架至转运区域,不可重新上架',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_zongpaiNos.removeWhere((item) => toRemove.contains(item));
|
||||
_isSubmitting = false;
|
||||
});
|
||||
|
||||
if (_isTransitTarget && transitCodes.isNotEmpty) {
|
||||
await _navigateToBoxing(
|
||||
mode: BoxingMode.multiCode,
|
||||
codes: transitCodes,
|
||||
codesToClear: toRemove,
|
||||
clearLocation: failed.isEmpty,
|
||||
returnMessage: failed.isEmpty
|
||||
? null
|
||||
: '成功 $successCount 条,失败 ${failed.length} 条',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (failed.isEmpty) {
|
||||
_feedbackService.trigger(FeedbackEvent.submitSuccess);
|
||||
final msg = offShelfCount == successCount
|
||||
? '批量下架成功($successCount 条)'
|
||||
: '批量上架成功($successCount 条)';
|
||||
_showStatusOverride(
|
||||
msg,
|
||||
StatusDotColor.green,
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
await _refreshCurrentOverview();
|
||||
} else {
|
||||
_feedbackService.trigger(FeedbackEvent.submitFailure);
|
||||
_showStatusOverride(
|
||||
'成功 $successCount 条,失败 ${failed.length} 条',
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _navigateToBoxing({
|
||||
required BoxingMode mode,
|
||||
required List<String> codes,
|
||||
required List<String> codesToClear,
|
||||
required bool clearLocation,
|
||||
String? returnMessage,
|
||||
}) async {
|
||||
await _refreshCurrentOverview();
|
||||
if (!mounted) return;
|
||||
final navigator = Navigator.of(context);
|
||||
await _stopScanListening();
|
||||
await navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => BoxingPage(
|
||||
arguments: BoxingPageArguments(
|
||||
initialMode: mode,
|
||||
autoScanCodes: codes,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!mounted) return;
|
||||
await Future<void>.delayed(const Duration(milliseconds: 120));
|
||||
if (!mounted) return;
|
||||
_restartScanListening();
|
||||
setState(() {
|
||||
_zongpaiNos.removeWhere((item) => codesToClear.contains(item));
|
||||
if (clearLocation) {
|
||||
_locationCode = null;
|
||||
_locationType = null;
|
||||
}
|
||||
});
|
||||
await _refreshCurrentOverview();
|
||||
if (!mounted) return;
|
||||
if (returnMessage != null) {
|
||||
_showStatusOverride(
|
||||
returnMessage,
|
||||
StatusDotColor.red,
|
||||
const Duration(seconds: 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user