feat: extend boxing page for accessory boxing support

Add pending accessories section to both single-code and multi-code boxing
modes. Accessories display with orange styling and a "加入本箱" button that
calls saveBoxRecord with item_type=accessory. Extend API service with
PendingAccessory model, itemType fields on data classes, and optional
accessory_id parameter on saveBoxRecord. Show accessories with visual
indicator (wrench icon + orange background) in boxing detail page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-05-24 18:01:01 +08:00
parent 1416cff52c
commit 9dc714dbeb
8 changed files with 364 additions and 35 deletions

View File

@@ -118,6 +118,30 @@ class PaichaOverviewResult {
// === 装箱模块数据类 ===
/// 待装箱附件
class PendingAccessory {
final int accessoryId;
final String accessoryType;
final int quantity;
final String? locationCode;
PendingAccessory({
required this.accessoryId,
required this.accessoryType,
required this.quantity,
this.locationCode,
});
factory PendingAccessory.fromJson(Map<String, dynamic> json) {
return PendingAccessory(
accessoryId: json['accessory_id'] as int,
accessoryType: json['accessory_type'] as String,
quantity: json['quantity'] as int,
locationCode: json['location_code']?.toString(),
);
}
}
/// 箱号内单个总排号明细
class BoxItemData {
final int? boxItemId;
@@ -125,6 +149,7 @@ class BoxItemData {
final String? workOrderNo;
final int quantity;
final int? totalQuantity;
final String itemType;
BoxItemData({
this.boxItemId,
@@ -132,6 +157,7 @@ class BoxItemData {
this.workOrderNo,
required this.quantity,
this.totalQuantity,
this.itemType = 'product',
});
factory BoxItemData.fromJson(Map<String, dynamic> json) {
@@ -141,6 +167,7 @@ class BoxItemData {
workOrderNo: json['work_order_no']?.toString(),
quantity: json['quantity'] as int,
totalQuantity: json['total_quantity'] as int?,
itemType: json['item_type'] as String? ?? 'product',
);
}
}
@@ -167,11 +194,13 @@ class CurrentZongpaiBoxData {
final int boxItemId;
final int boxNo;
final int quantity;
final String itemType;
CurrentZongpaiBoxData({
required this.boxItemId,
required this.boxNo,
required this.quantity,
this.itemType = 'product',
});
factory CurrentZongpaiBoxData.fromJson(Map<String, dynamic> json) {
@@ -179,6 +208,7 @@ class CurrentZongpaiBoxData {
boxItemId: json['box_item_id'] as int,
boxNo: json['box_no'] as int,
quantity: json['quantity'] as int,
itemType: json['item_type'] as String? ?? 'product',
);
}
}
@@ -195,7 +225,7 @@ class BoxInfoResult {
final List<BoxDetailData> existingBoxes;
final int maxBoxNo;
final int suggestedBoxNo;
final int pendingAccessories;
final List<PendingAccessory> pendingAccessories;
BoxInfoResult({
required this.success,
@@ -208,7 +238,7 @@ class BoxInfoResult {
this.existingBoxes = const [],
this.maxBoxNo = 0,
this.suggestedBoxNo = 1,
this.pendingAccessories = 0,
this.pendingAccessories = const [],
});
factory BoxInfoResult.ok(Map<String, dynamic> json) {
@@ -224,6 +254,11 @@ class BoxInfoResult {
)
.toList() ??
[];
final pendingAccessories =
(json['pending_accessories'] as List<dynamic>?)
?.map((a) => PendingAccessory.fromJson(a as Map<String, dynamic>))
.toList() ??
[];
return BoxInfoResult(
success: true,
zongpaiNo: json['zongpai_no'] as String?,
@@ -234,7 +269,7 @@ class BoxInfoResult {
existingBoxes: boxes,
maxBoxNo: json['max_box_no'] as int? ?? 0,
suggestedBoxNo: json['suggested_box_no'] as int? ?? 1,
pendingAccessories: json['pending_accessories'] as int? ?? 0,
pendingAccessories: pendingAccessories,
);
}
@@ -583,18 +618,25 @@ class ApiService {
required String zongpaiNo,
required int boxNo,
required int quantity,
String itemType = 'product',
int? accessoryId,
}) async {
final uri = Uri.parse('$baseUrl/CargoTrace/box');
try {
final bodyMap = <String, dynamic>{
'zongpai_no': zongpaiNo,
'box_no': boxNo,
'quantity': quantity,
'item_type': itemType,
};
if (accessoryId != null) {
bodyMap['accessory_id'] = accessoryId;
}
final response = await _client
.post(
uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'zongpai_no': zongpaiNo,
'box_no': boxNo,
'quantity': quantity,
}),
body: jsonEncode(bodyMap),
)
.timeout(timeout);