- boxing_models.dart: add BoxingMode, BoxingPhase, BoxingPageArguments - boxing_box_mutations.dart: box item add/replace/remove operations - boxing_calculations.dart: pure calculation helpers (quantities, validation) - boxing_status_presenter.dart: status bar text/dot determination - dialogs/cross_paichan_dialog.dart: cross-paichan confirmation dialog - widgets/boxing_notice_banners.dart: notice banner widgets - Add unit tests for mutations, calculations, and status presenter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
179 lines
5.1 KiB
Dart
179 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:pad_scanner/pages/boxing/boxing_models.dart';
|
||
|
||
class BoxingNoticeBanners extends StatelessWidget {
|
||
final bool isAutoProcessing;
|
||
final List<String> autoWarnings;
|
||
final String? paichanSwitchNotice;
|
||
final bool isDuplicateBoxNo;
|
||
final bool quantityTooHigh;
|
||
final BoxingPhase phase;
|
||
final String boxNoText;
|
||
final int remainingQuantity;
|
||
final String? zongpaiNo;
|
||
final int? completedJumpBoxNo;
|
||
final VoidCallback onJumpToCompletedBox;
|
||
|
||
const BoxingNoticeBanners({
|
||
super.key,
|
||
required this.isAutoProcessing,
|
||
required this.autoWarnings,
|
||
required this.paichanSwitchNotice,
|
||
required this.isDuplicateBoxNo,
|
||
required this.quantityTooHigh,
|
||
required this.phase,
|
||
required this.boxNoText,
|
||
required this.remainingQuantity,
|
||
required this.zongpaiNo,
|
||
required this.completedJumpBoxNo,
|
||
required this.onJumpToCompletedBox,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final banners = <Widget>[];
|
||
if (isAutoProcessing) {
|
||
banners.add(
|
||
_NoticeBanner(
|
||
icon: SizedBox(
|
||
width: 12,
|
||
height: 12,
|
||
child: CircularProgressIndicator(
|
||
strokeWidth: 1.5,
|
||
color: Colors.orange.shade700,
|
||
),
|
||
),
|
||
text: '正在同步转运数据...',
|
||
background: Colors.orange.shade50,
|
||
foreground: Colors.orange.shade900,
|
||
border: Colors.orange.shade100,
|
||
),
|
||
);
|
||
}
|
||
if (autoWarnings.isNotEmpty) {
|
||
banners.add(
|
||
_NoticeBanner(
|
||
icon: Icon(
|
||
Icons.warning_amber,
|
||
size: 14,
|
||
color: Colors.amber.shade900,
|
||
),
|
||
text: autoWarnings.join(';'),
|
||
background: Colors.amber.shade100,
|
||
foreground: Colors.amber.shade900,
|
||
border: Colors.amber.shade200,
|
||
),
|
||
);
|
||
}
|
||
if (paichanSwitchNotice != null) {
|
||
banners.add(
|
||
_NoticeBanner(
|
||
icon: Icon(Icons.info_outline, size: 14, color: Colors.blue.shade900),
|
||
text: paichanSwitchNotice!,
|
||
background: Colors.blue.shade50,
|
||
foreground: Colors.blue.shade900,
|
||
border: Colors.blue.shade100,
|
||
),
|
||
);
|
||
}
|
||
if (isDuplicateBoxNo && phase == BoxingPhase.scanned) {
|
||
banners.add(
|
||
_NoticeBanner(
|
||
icon: Icon(
|
||
Icons.warning_amber,
|
||
size: 14,
|
||
color: Colors.amber.shade900,
|
||
),
|
||
text: '箱号 $boxNoText 已存在,请重新输入',
|
||
background: Colors.amber.shade100,
|
||
foreground: Colors.amber.shade900,
|
||
border: Colors.amber.shade200,
|
||
),
|
||
);
|
||
}
|
||
if (quantityTooHigh && phase == BoxingPhase.scanned) {
|
||
banners.add(
|
||
_NoticeBanner(
|
||
icon: Icon(Icons.close, size: 14, color: Colors.red.shade800),
|
||
text: '超出可装数量上限(最多可装 $remainingQuantity 件)',
|
||
background: Colors.red.shade50,
|
||
foreground: Colors.red.shade800,
|
||
border: Colors.red.shade100,
|
||
),
|
||
);
|
||
}
|
||
if (phase == BoxingPhase.scanned &&
|
||
zongpaiNo != null &&
|
||
remainingQuantity <= 0) {
|
||
final jumpBoxNo = completedJumpBoxNo;
|
||
if (jumpBoxNo != null) {
|
||
banners.add(
|
||
GestureDetector(
|
||
onTap: onJumpToCompletedBox,
|
||
behavior: HitTestBehavior.opaque,
|
||
child: _NoticeBanner(
|
||
icon: Icon(Icons.warning, size: 14, color: Colors.amber.shade800),
|
||
text: '$zongpaiNo 已完成装箱。(P4跳转所在箱号)',
|
||
background: Colors.amber.shade50,
|
||
foreground: Colors.amber.shade800,
|
||
border: Colors.amber.shade200,
|
||
),
|
||
),
|
||
);
|
||
} else {
|
||
banners.add(
|
||
_NoticeBanner(
|
||
icon: Icon(Icons.warning, size: 14, color: Colors.red.shade800),
|
||
text: '$zongpaiNo 已全部装箱完毕,无需操作',
|
||
background: Colors.red.shade50,
|
||
foreground: Colors.red.shade800,
|
||
border: Colors.red.shade100,
|
||
),
|
||
);
|
||
}
|
||
}
|
||
return Column(mainAxisSize: MainAxisSize.min, children: banners);
|
||
}
|
||
}
|
||
|
||
class _NoticeBanner extends StatelessWidget {
|
||
final Widget icon;
|
||
final String text;
|
||
final Color background;
|
||
final Color foreground;
|
||
final Color border;
|
||
|
||
const _NoticeBanner({
|
||
required this.icon,
|
||
required this.text,
|
||
required this.background,
|
||
required this.foreground,
|
||
required this.border,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||
decoration: BoxDecoration(
|
||
color: background,
|
||
border: Border(bottom: BorderSide(color: border)),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
icon,
|
||
const SizedBox(width: 6),
|
||
Expanded(
|
||
child: Text(
|
||
text,
|
||
style: TextStyle(fontSize: 12, color: foreground),
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|