Recognize B-prefix location codes as temp storage shelves. Add purple color scheme, conflict detection for temp_stored status, and updated submit/dialog logic for temp storage target handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
358 lines
11 KiB
Dart
358 lines
11 KiB
Dart
// ignore_for_file: invalid_use_of_protected_member
|
||
|
||
part of '../../registration_page.dart';
|
||
|
||
extension _RegistrationDialogsPart on _RegistrationPageState {
|
||
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 _showDuplicateDialog(String zongpaiNo, Map<String, dynamic>? info) {
|
||
showDialog(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (ctx) => AlertDialog(
|
||
backgroundColor: Colors.red.shade50,
|
||
title: const Row(
|
||
children: [
|
||
Icon(Icons.warning, color: Colors.red),
|
||
SizedBox(width: 8),
|
||
Text('重复上架', style: TextStyle(color: Colors.red)),
|
||
],
|
||
),
|
||
content: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text('总排号:$zongpaiNo'),
|
||
const SizedBox(height: 4),
|
||
Text('已登记货位:${info?["location_code"] ?? "未知"}'),
|
||
const SizedBox(height: 4),
|
||
Text('登记时间:${info?["registered_at"] ?? "未知"}'),
|
||
const SizedBox(height: 12),
|
||
const Text(
|
||
'请核查实物,确认是否操作错误。',
|
||
style: TextStyle(fontWeight: FontWeight.bold),
|
||
),
|
||
],
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () {
|
||
_feedbackService.stopAlert();
|
||
Navigator.pop(ctx);
|
||
},
|
||
child: const Text('关闭'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void _showLocationConflictDialog({
|
||
required List<PaichaOverviewItem> onShelfItems,
|
||
required List<PaichaOverviewItem> transferredItems,
|
||
List<PaichaOverviewItem> tempStoredItems = const [],
|
||
}) {
|
||
final contentParts = <Widget>[];
|
||
|
||
if (onShelfItems.isNotEmpty) {
|
||
contentParts.add(
|
||
Text(
|
||
'重复上架',
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: Colors.red.shade700,
|
||
),
|
||
),
|
||
);
|
||
for (final item in onShelfItems) {
|
||
contentParts.addAll([
|
||
Text('总排号:${item.zongpaiNo}'),
|
||
Text('已登记货位:${item.locationCode ?? "未知"}'),
|
||
const SizedBox(height: 6),
|
||
]);
|
||
}
|
||
}
|
||
|
||
if (transferredItems.isNotEmpty) {
|
||
if (onShelfItems.isNotEmpty) {
|
||
contentParts.add(const Divider());
|
||
contentParts.add(const SizedBox(height: 4));
|
||
}
|
||
contentParts.add(
|
||
Text(
|
||
'货物已转运',
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: Colors.orange.shade700,
|
||
),
|
||
),
|
||
);
|
||
for (final item in transferredItems) {
|
||
contentParts.addAll([
|
||
Text('总排号:${item.zongpaiNo}'),
|
||
Text('转运货位:${item.locationCode ?? "未知"}'),
|
||
const SizedBox(height: 6),
|
||
]);
|
||
}
|
||
}
|
||
|
||
if (tempStoredItems.isNotEmpty) {
|
||
if (onShelfItems.isNotEmpty || transferredItems.isNotEmpty) {
|
||
contentParts.add(const Divider());
|
||
contentParts.add(const SizedBox(height: 4));
|
||
}
|
||
contentParts.add(
|
||
Text(
|
||
'暂存中',
|
||
style: TextStyle(
|
||
fontWeight: FontWeight.bold,
|
||
color: const Color(0xFF7B1FA2),
|
||
),
|
||
),
|
||
);
|
||
for (final item in tempStoredItems) {
|
||
contentParts.addAll([
|
||
Text('总排号:${item.zongpaiNo}'),
|
||
Text('暂存货位:${item.locationCode ?? "未知"}'),
|
||
const SizedBox(height: 6),
|
||
]);
|
||
}
|
||
}
|
||
|
||
contentParts.add(const SizedBox(height: 4));
|
||
contentParts.add(
|
||
const Text(
|
||
'请核查实物,确认是否操作错误。',
|
||
style: TextStyle(fontWeight: FontWeight.bold),
|
||
),
|
||
);
|
||
|
||
showDialog(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (ctx) => AlertDialog(
|
||
backgroundColor: Colors.red.shade50,
|
||
title: const Row(
|
||
children: [
|
||
Icon(Icons.warning, color: Colors.red),
|
||
SizedBox(width: 8),
|
||
Text('操作冲突', style: TextStyle(color: Colors.red)),
|
||
],
|
||
),
|
||
content: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: contentParts,
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () {
|
||
_feedbackService.stopAlert();
|
||
Navigator.pop(ctx);
|
||
},
|
||
child: const Text('关闭'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<void> _showCrossPaichaDialog(String newZongpaiNo) async {
|
||
final currentPaicha = _overview?.paichaNo ?? '--';
|
||
final shouldSwitch = await _confirmCrossPaichaSwitch(
|
||
newZongpaiNo: newZongpaiNo,
|
||
currentPaicha: currentPaicha,
|
||
);
|
||
if (!mounted) return;
|
||
if (shouldSwitch) {
|
||
_switchToNewPaicha(newZongpaiNo);
|
||
return;
|
||
}
|
||
setState(() {
|
||
_crossPaichaPending = false;
|
||
});
|
||
}
|
||
|
||
Future<bool> _confirmCrossPaichaSwitch({
|
||
required String newZongpaiNo,
|
||
required String currentPaicha,
|
||
}) async {
|
||
final dialogFocus = FocusNode();
|
||
var dismissed = false;
|
||
var selectedIndex = 0; // 0 = "否" (default), 1 = "是"
|
||
|
||
void dismissAsNo() {
|
||
if (dismissed) return;
|
||
dismissed = true;
|
||
Navigator.of(context).pop(false);
|
||
}
|
||
|
||
void confirmSwitch() {
|
||
if (dismissed) return;
|
||
dismissed = true;
|
||
Navigator.of(context).pop(true);
|
||
}
|
||
|
||
final dialogFuture = showDialog<bool>(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (ctx) => StatefulBuilder(
|
||
builder: (ctx, setDialogState) {
|
||
return KeyboardListener(
|
||
focusNode: dialogFocus,
|
||
onKeyEvent: (event) {
|
||
if (event is! KeyDownEvent) return;
|
||
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||
setDialogState(() => selectedIndex = 0);
|
||
} else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
|
||
setDialogState(() => selectedIndex = 1);
|
||
} else if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||
selectedIndex == 0 ? dismissAsNo() : confirmSwitch();
|
||
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||
dismissAsNo();
|
||
}
|
||
},
|
||
child: PopScope(
|
||
canPop: false,
|
||
onPopInvokedWithResult: (didPop, _) {
|
||
if (!didPop) dismissAsNo();
|
||
},
|
||
child: 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: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
Text(
|
||
'总排号 $newZongpaiNo 不属于当前排产号($currentPaicha),'
|
||
'是否切换到新的排产号?\n\n'
|
||
'选择「是」将放弃当前已扫描的所有数据。',
|
||
style: const TextStyle(fontSize: 14),
|
||
),
|
||
const SizedBox(height: 16),
|
||
_dialogOptionBtn(
|
||
label: '否',
|
||
selected: selectedIndex == 0,
|
||
onTap: dismissAsNo,
|
||
),
|
||
const SizedBox(height: 8),
|
||
_dialogOptionBtn(
|
||
label: '是,切换排产号',
|
||
selected: selectedIndex == 1,
|
||
onTap: confirmSwitch,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
dialogFocus.requestFocus();
|
||
});
|
||
final shouldSwitch = await dialogFuture;
|
||
return shouldSwitch ?? false;
|
||
}
|
||
|
||
Widget _dialogOptionBtn({
|
||
required String label,
|
||
required bool selected,
|
||
required VoidCallback onTap,
|
||
}) {
|
||
return SizedBox(
|
||
width: double.infinity,
|
||
height: 40,
|
||
child: ElevatedButton(
|
||
onPressed: onTap,
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: selected
|
||
? Colors.blue.shade700
|
||
: Colors.grey.shade200,
|
||
foregroundColor: selected ? Colors.white : Colors.black87,
|
||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||
),
|
||
child: Text(
|
||
label,
|
||
style: TextStyle(
|
||
fontWeight: selected ? FontWeight.w800 : FontWeight.w600,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
void _switchToNewPaicha(String newZongpaiNo) {
|
||
_feedbackService.trigger(FeedbackEvent.paichanSwitch);
|
||
setState(() {
|
||
_crossPaichaPending = false;
|
||
_zongpaiNos.clear();
|
||
_zongpaiNos.add(newZongpaiNo);
|
||
_overview = null;
|
||
_overviewNotFound = false;
|
||
_overviewError = null;
|
||
_clearStatusOverride();
|
||
});
|
||
_loadOverview(newZongpaiNo);
|
||
}
|
||
}
|