import 'package:flutter/material.dart'; import 'package:pad_scanner/services/api_service.dart'; class AttachmentConfigDialogResult { final String determination; // 'has' or 'none' final List items; // empty if 'none' const AttachmentConfigDialogResult({ required this.determination, required this.items, }); } /// Show the attachment configuration dialog for an order. /// /// [categories] — the full active category list from GET /attachment/categories. /// [prefillItems] — existing attachment status items (for pre-fill when /// determination is already 'has' due to earlier attachment arrival). /// [erpQuantity] — default expected_qty per selected type. Future showAttachmentConfigDialog({ required BuildContext context, required String zongpaiNo, required List categories, required List prefillItems, required int erpQuantity, }) { return showDialog( context: context, barrierDismissible: false, builder: (ctx) => _AttachmentConfigDialog( zongpaiNo: zongpaiNo, categories: categories, prefillItems: prefillItems, erpQuantity: erpQuantity, ), ); } class _AttachmentConfigDialog extends StatefulWidget { final String zongpaiNo; final List categories; final List prefillItems; final int erpQuantity; const _AttachmentConfigDialog({ required this.zongpaiNo, required this.categories, required this.prefillItems, required this.erpQuantity, }); @override State<_AttachmentConfigDialog> createState() => _AttachmentConfigDialogState(); } class _AttachmentConfigDialogState extends State<_AttachmentConfigDialog> { late Map _selected; // category_id → checked late Map _controllers; // category_id → qty @override void initState() { super.initState(); _selected = {}; _controllers = {}; final prefillSet = {}; for (final item in widget.prefillItems) { _selected[item.categoryId] = true; prefillSet.add(item.categoryId); } for (final cat in widget.categories) { if (!_selected.containsKey(cat.id)) { _selected[cat.id] = prefillSet.contains(cat.id); } final ctrl = TextEditingController( text: prefillSet.contains(cat.id) ? widget.prefillItems .firstWhere((i) => i.categoryId == cat.id) .expectedQty .toString() : widget.erpQuantity.toString(), ); _controllers[cat.id] = ctrl; } } @override void dispose() { for (final ctrl in _controllers.values) { ctrl.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { final hasAnyChecked = _selected.values.any((v) => v); return AlertDialog( title: Text('${widget.zongpaiNo} — 附件配置'), content: SizedBox( width: 500, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text( '请勾选本订单携带的附件类型,并确认每种数量:', style: TextStyle(fontSize: 13), ), const SizedBox(height: 8), ...widget.categories.map( (cat) => CheckboxListTile( dense: true, title: Text(cat.name, style: const TextStyle(fontSize: 14)), subtitle: Row( children: [ const Text('应到: ', style: TextStyle(fontSize: 12)), SizedBox( width: 80, child: TextField( controller: _controllers[cat.id], keyboardType: TextInputType.number, enabled: _selected[cat.id] == true, style: const TextStyle(fontSize: 14), decoration: const InputDecoration( isDense: true, contentPadding: EdgeInsets.symmetric( vertical: 4, horizontal: 8, ), ), ), ), ], ), value: _selected[cat.id] ?? false, onChanged: (v) => setState(() => _selected[cat.id] = v ?? false), ), ), ], ), ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('取消'), ), TextButton( onPressed: () { Navigator.pop( context, AttachmentConfigDialogResult(determination: 'none', items: []), ); }, child: const Text('无附件'), ), ElevatedButton( onPressed: hasAnyChecked ? () { final items = []; for (final cat in widget.categories) { if (_selected[cat.id] == true) { final qty = int.tryParse(_controllers[cat.id]!.text.trim()) ?? widget.erpQuantity; items.add( AttachmentConfigItemData( categoryId: cat.id, expectedQty: qty, ), ); } } Navigator.pop( context, AttachmentConfigDialogResult( determination: 'has', items: items, ), ); } : null, child: const Text('确认'), ), ], ); } }