From b975b15ba7ae6dd11f1e0e4e96ea69c0ef729b18 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Wed, 13 May 2026 11:22:49 +0800 Subject: [PATCH] feat: improve one-to-many boxing workflow --- lib/pages/boxing_page.dart | 395 +++++++++++++++++++++++++++++----- lib/services/api_service.dart | 116 ++++++++++ 2 files changed, 462 insertions(+), 49 deletions(-) diff --git a/lib/pages/boxing_page.dart b/lib/pages/boxing_page.dart index f1324c8..057b761 100644 --- a/lib/pages/boxing_page.dart +++ b/lib/pages/boxing_page.dart @@ -51,6 +51,7 @@ class _BoxingPageState extends State { String? _paichanNo; String? _workOrderNo; int? _erpQuantity; + List _currentZongpaiBoxes = []; List _existingBoxes = []; int _maxBoxNo = 0; @@ -69,9 +70,9 @@ class _BoxingPageState extends State { // 提交中 bool _isSubmitting = false; - // 一码多箱:上次提交的值(用于继续添加预填) + // 一码多箱:上次提交的箱号(用于继续添加预填) int? _lastBoxNo; - int? _lastQuantity; + CurrentZongpaiBoxData? _editingBox; // 重复箱号 bool _isDuplicateBoxNo = false; @@ -132,6 +133,7 @@ class _BoxingPageState extends State { _paichanNo = null; _workOrderNo = null; _erpQuantity = null; + _currentZongpaiBoxes = []; _existingBoxes = []; _maxBoxNo = 0; _boxNoController.clear(); @@ -140,7 +142,7 @@ class _BoxingPageState extends State { _boxNoLocked = false; _isSubmitting = false; _lastBoxNo = null; - _lastQuantity = null; + _editingBox = null; _isDuplicateBoxNo = false; _statusOverrideText = null; _statusOverrideDot = null; @@ -169,8 +171,8 @@ class _BoxingPageState extends State { return; } - // 一码一箱:允许覆盖,直接查询新二维码(不提前清空状态,避免闪烁) - if (_mode != BoxingMode.one2one && _phase != _Phase.waiting) { + // 一码一箱和一码多箱:允许覆盖,直接查询新二维码(不提前清空状态,避免闪烁) + if (_mode == BoxingMode.many2one && _phase != _Phase.waiting) { return; } @@ -226,10 +228,12 @@ class _BoxingPageState extends State { _paichanNo = result.paichanNo; _workOrderNo = result.workOrderNo; _erpQuantity = result.quantity; + _currentZongpaiBoxes = result.currentZongpaiBoxes; _existingBoxes = result.existingBoxes; _maxBoxNo = result.maxBoxNo; _phase = _Phase.scanned; _isDuplicateBoxNo = false; + _editingBox = null; _statusOverrideText = null; // 多码一箱:记录已扫描列表 @@ -250,7 +254,7 @@ class _BoxingPageState extends State { case BoxingMode.one2many: if (_lastBoxNo != null) { _boxNoController.text = (_lastBoxNo! + 1).toString(); - _quantityController.text = _lastQuantity?.toString() ?? ''; + _quantityController.clear(); } else { _boxNoController.text = (_maxBoxNo + 1).toString(); _quantityController.clear(); @@ -273,7 +277,14 @@ class _BoxingPageState extends State { setState(() => _isDuplicateBoxNo = false); return; } - final exists = _existingBoxes.any((b) => b.boxNo == boxNo); + final exists = switch (_mode) { + BoxingMode.many2one => false, + BoxingMode.one2one => _existingBoxes.any((b) => b.boxNo == boxNo), + BoxingMode.one2many => _existingBoxes.any((b) { + if (b.boxNo != boxNo) return false; + return _editingBox == null || b.boxNo != _editingBox!.boxNo; + }), + }; setState(() => _isDuplicateBoxNo = exists); } @@ -314,20 +325,32 @@ class _BoxingPageState extends State { BoxingMode.one2many => 'one-to-many', BoxingMode.many2one => 'many-to-one', }; - final result = await _apiService.saveBoxRecord( - baseUrl: baseUrl, - zongpaiNo: _zongpaiNo!, - boxNo: boxNo, - quantity: quantity, - boxMode: modeStr, - ); + final editing = _editingBox; + final result = editing == null + ? await _apiService.saveBoxRecord( + baseUrl: baseUrl, + zongpaiNo: _zongpaiNo!, + boxNo: boxNo, + quantity: quantity, + boxMode: modeStr, + ) + : await _apiService.updateBoxRecord( + baseUrl: baseUrl, + boxItemId: editing.boxItemId, + boxNo: boxNo, + quantity: quantity, + ); if (!mounted) return; setState(() => _isSubmitting = false); if (result.success) { - _onSubmitSuccess(boxNo, quantity); + if (editing == null) { + _onSubmitSuccess(boxNo, quantity, result.boxItemId); + } else { + _onUpdateSuccess(editing, boxNo, quantity); + } } else if (result.isDuplicate) { _feedbackService.trigger(FeedbackEvent.duplicateBoxNo); if (_mode == BoxingMode.one2one) { @@ -356,62 +379,246 @@ class _BoxingPageState extends State { } } - void _onSubmitSuccess(int boxNo, int quantity) { + void _onSubmitSuccess(int boxNo, int quantity, int? boxItemId) { _feedbackService.trigger(FeedbackEvent.submitSuccess); setState(() { _lastBoxNo = boxNo; - _lastQuantity = quantity; - _existingBoxes = List.from(_existingBoxes) - ..add( - BoxDetailData( - boxNo: boxNo, - items: [ - BoxItemData( - zongpaiNo: _zongpaiNo!, - workOrderNo: _workOrderNo, - quantity: quantity, - ), - ], - ), - ); + if (_mode == BoxingMode.one2many && boxItemId != null) { + _currentZongpaiBoxes = List.from(_currentZongpaiBoxes) + ..add( + CurrentZongpaiBoxData( + boxItemId: boxItemId, + boxNo: boxNo, + quantity: quantity, + ), + ); + } + _addExistingBoxItem(boxNo, quantity, boxItemId); _maxBoxNo = _maxBoxNo > boxNo ? _maxBoxNo : boxNo; }); switch (_mode) { case BoxingMode.one2one: - _showStatusOverride('装箱成功', StatusDotColor.green, const Duration(milliseconds: 1500)); + _showStatusOverride( + '装箱成功', + StatusDotColor.green, + const Duration(milliseconds: 1500), + ); Future.delayed(const Duration(milliseconds: 1500), () { if (mounted) setState(() => _resetState()); }); case BoxingMode.one2many: setState(() { - _phase = _Phase.submitted; - _zongpaiNo = null; + _phase = _Phase.scanned; + _boxNoController.text = (boxNo + 1).toString(); + _quantityController.clear(); + _isDuplicateBoxNo = false; }); - _showStatusOverride('装箱成功,可继续添加或返回', StatusDotColor.green, const Duration(milliseconds: 1500)); + _showStatusOverride( + '装箱成功,可继续添加', + StatusDotColor.green, + const Duration(milliseconds: 1500), + ); case BoxingMode.many2one: setState(() { _phase = _Phase.submitted; _zongpaiNo = null; }); - _showStatusOverride('装箱成功,请扫描下一个总排号', StatusDotColor.green, const Duration(milliseconds: 1500)); + _showStatusOverride( + '装箱成功,请扫描下一个总排号', + StatusDotColor.green, + const Duration(milliseconds: 1500), + ); } } + void _onUpdateSuccess( + CurrentZongpaiBoxData editing, + int boxNo, + int quantity, + ) { + _feedbackService.trigger(FeedbackEvent.submitSuccess); + + setState(() { + _currentZongpaiBoxes = _currentZongpaiBoxes.map((item) { + if (item.boxItemId != editing.boxItemId) return item; + return CurrentZongpaiBoxData( + boxItemId: item.boxItemId, + boxNo: boxNo, + quantity: quantity, + ); + }).toList(); + _replaceExistingBoxItem(editing, boxNo, quantity); + _editingBox = null; + _lastBoxNo = _maxBoxNo; + _boxNoController.text = (_maxBoxNo + 1).toString(); + _quantityController.clear(); + _isDuplicateBoxNo = false; + }); + _showStatusOverride( + '修改成功', + StatusDotColor.green, + const Duration(milliseconds: 1500), + ); + } + + void _replaceExistingBoxItem( + CurrentZongpaiBoxData editing, + int boxNo, + int quantity, + ) { + final nextBoxes = []; + for (final box in _existingBoxes) { + final items = box.items.where((item) { + return item.boxItemId != editing.boxItemId; + }).toList(); + if (items.isNotEmpty) { + nextBoxes.add(BoxDetailData(boxNo: box.boxNo, items: items)); + } + } + + final targetIndex = nextBoxes.indexWhere((box) => box.boxNo == boxNo); + final updatedItem = BoxItemData( + boxItemId: editing.boxItemId, + zongpaiNo: _zongpaiNo!, + workOrderNo: _workOrderNo, + quantity: quantity, + ); + if (targetIndex >= 0) { + final target = nextBoxes[targetIndex]; + nextBoxes[targetIndex] = BoxDetailData( + boxNo: target.boxNo, + items: [...target.items, updatedItem], + ); + } else { + nextBoxes.add(BoxDetailData(boxNo: boxNo, items: [updatedItem])); + } + nextBoxes.sort((a, b) => a.boxNo.compareTo(b.boxNo)); + _existingBoxes = nextBoxes; + _maxBoxNo = nextBoxes.fold( + 0, + (max, box) => box.boxNo > max ? box.boxNo : max, + ); + } + + void _addExistingBoxItem(int boxNo, int quantity, int? boxItemId) { + final item = BoxItemData( + boxItemId: boxItemId, + zongpaiNo: _zongpaiNo!, + workOrderNo: _workOrderNo, + quantity: quantity, + ); + final index = _existingBoxes.indexWhere((box) => box.boxNo == boxNo); + if (index < 0) { + _existingBoxes = [ + ..._existingBoxes, + BoxDetailData(boxNo: boxNo, items: [item]), + ]..sort((a, b) => a.boxNo.compareTo(b.boxNo)); + return; + } + + final next = List.from(_existingBoxes); + final box = next[index]; + next[index] = BoxDetailData(boxNo: box.boxNo, items: [...box.items, item]); + _existingBoxes = next; + } + // === 操作按钮 === - void _onContinueAdding() { + void _onGoBack() { + setState(() => _resetState()); + } + + void _startEditAssigned(CurrentZongpaiBoxData item) { setState(() { - _phase = _Phase.waiting; + _editingBox = item; + _phase = _Phase.scanned; + _boxNoController.text = item.boxNo.toString(); + _quantityController.text = item.quantity.toString(); + _isDuplicateBoxNo = false; _statusOverrideText = null; + _statusOverrideDot = null; }); } - void _onGoBack() { - setState(() => _resetState()); + void _cancelEditAssigned() { + setState(() { + _editingBox = null; + _boxNoController.text = ((_lastBoxNo ?? _maxBoxNo) + 1).toString(); + _quantityController.clear(); + _isDuplicateBoxNo = false; + }); + } + + Future _deleteAssigned(CurrentZongpaiBoxData item) async { + final configService = AppConfigService(); + final baseUrl = await configService.getString('api_url') ?? ''; + if (baseUrl.isEmpty) { + _showStatusOverride( + '未配置 API 地址,请前往设置', + StatusDotColor.red, + const Duration(seconds: 2), + ); + return; + } + + setState(() => _isSubmitting = true); + final result = await _apiService.deleteBoxRecord( + baseUrl: baseUrl, + boxItemId: item.boxItemId, + ); + if (!mounted) return; + + setState(() { + _isSubmitting = false; + if (result.success) { + _currentZongpaiBoxes = _currentZongpaiBoxes + .where((box) => box.boxItemId != item.boxItemId) + .toList(); + _removeExistingBoxItem(item.boxItemId); + if (_editingBox?.boxItemId == item.boxItemId) { + _editingBox = null; + } + _lastBoxNo = _maxBoxNo; + _boxNoController.text = (_maxBoxNo + 1).toString(); + _quantityController.clear(); + _isDuplicateBoxNo = false; + } + }); + + if (result.success) { + _showStatusOverride( + '删除成功', + StatusDotColor.green, + const Duration(milliseconds: 1500), + ); + } else { + _showStatusOverride( + result.errorMessage ?? '删除失败', + StatusDotColor.red, + const Duration(seconds: 2), + ); + } + } + + void _removeExistingBoxItem(int boxItemId) { + final nextBoxes = []; + for (final box in _existingBoxes) { + final items = box.items + .where((item) => item.boxItemId != boxItemId) + .toList(); + if (items.isNotEmpty) { + nextBoxes.add(BoxDetailData(boxNo: box.boxNo, items: items)); + } + } + _existingBoxes = nextBoxes; + _maxBoxNo = nextBoxes.fold( + 0, + (max, box) => box.boxNo > max ? box.boxNo : max, + ); } // === Status bar management === @@ -489,7 +696,8 @@ class _BoxingPageState extends State { final colorScheme = Theme.of(context).colorScheme; final isWaiting = _phase == _Phase.waiting && _zongpaiNo == null; final showActionButtons = - _phase == _Phase.submitted && _mode != BoxingMode.one2one; + (_phase == _Phase.submitted && _mode == BoxingMode.many2one) || + (_phase == _Phase.scanned && _mode == BoxingMode.one2many); _updateBaseStatus(); final effectiveDot = _statusOverrideDot ?? _statusDot; @@ -545,6 +753,13 @@ class _BoxingPageState extends State { const Divider(height: 24), + if (_mode == BoxingMode.one2many && + !isWaiting && + _currentZongpaiBoxes.isNotEmpty) ...[ + _buildAssignedBoxes(), + const Divider(height: 24), + ], + // === 输入区 === _buildInputArea(isWaiting), ], @@ -567,15 +782,19 @@ class _BoxingPageState extends State { child: const Text('返回'), ), ), - if (_mode == BoxingMode.one2many) ...[ + if (_mode == BoxingMode.one2many && _editingBox == null) ...[ + const SizedBox(width: 12), + const Expanded(child: SizedBox.shrink()), + ], + if (_mode == BoxingMode.one2many && _editingBox != null) ...[ const SizedBox(width: 12), Expanded( child: ElevatedButton( - onPressed: _onContinueAdding, + onPressed: _cancelEditAssigned, style: ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(40), ), - child: const Text('继续添加'), + child: const Text('取消'), ), ), ], @@ -590,6 +809,63 @@ class _BoxingPageState extends State { ); } + Widget _buildAssignedBoxes() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '已分配', + style: TextStyle(fontSize: 13, color: Colors.black54), + ), + const SizedBox(height: 6), + ..._currentZongpaiBoxes.map((item) { + final editing = _editingBox?.boxItemId == item.boxItemId; + return Container( + margin: const EdgeInsets.only(bottom: 6), + decoration: BoxDecoration( + color: editing ? Colors.blue.shade50 : Colors.grey.shade50, + border: Border.all( + color: editing ? Colors.blue : Colors.grey.shade300, + ), + borderRadius: BorderRadius.circular(6), + ), + child: ListTile( + dense: true, + contentPadding: const EdgeInsets.only(left: 10, right: 4), + onTap: () => _startEditAssigned(item), + title: Text( + '${item.boxNo}号箱 / 数量 ${item.quantity}', + style: TextStyle( + fontSize: 14, + fontWeight: editing ? FontWeight.w700 : FontWeight.w500, + ), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + tooltip: '修改', + visualDensity: VisualDensity.compact, + icon: const Icon(Icons.edit, size: 18), + onPressed: () => _startEditAssigned(item), + ), + IconButton( + tooltip: '删除', + visualDensity: VisualDensity.compact, + icon: const Icon(Icons.delete_outline, size: 18), + onPressed: _isSubmitting + ? null + : () => _deleteAssigned(item), + ), + ], + ), + ), + ); + }), + ], + ); + } + // === 扫码区 Widget === Widget _buildScanArea(bool isWaiting) { @@ -669,6 +945,7 @@ class _BoxingPageState extends State { final grey = isWaiting; final paichanText = grey ? '--' : (_paichanNo ?? '--'); final workOrderText = grey ? '--' : (_workOrderNo ?? '--'); + final quantityText = grey ? '--' : (_erpQuantity?.toString() ?? '--'); return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -676,6 +953,7 @@ class _BoxingPageState extends State { Row( children: [ Expanded( + flex: 5, child: Text( paichanText, style: TextStyle( @@ -686,10 +964,25 @@ class _BoxingPageState extends State { overflow: TextOverflow.ellipsis, ), ), - const SizedBox(width: 12), + const SizedBox(width: 8), Expanded( + flex: 4, child: Text( workOrderText, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: grey ? Colors.grey : Colors.black87, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 8), + Expanded( + flex: 3, + child: Text( + quantityText, textAlign: TextAlign.right, style: TextStyle( fontSize: 18, @@ -781,7 +1074,7 @@ class _BoxingPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '箱号', + _editingBox == null ? '箱号' : '修改箱号', style: TextStyle( fontSize: 13, color: enabled ? Colors.black54 : Colors.grey, @@ -794,7 +1087,7 @@ class _BoxingPageState extends State { controller: _boxNoController, focusNode: _boxNoFocusNode, enabled: enabled && !_boxNoLocked, - keyboardType: TextInputType.number, + keyboardType: TextInputType.none, inputFormatters: [FilteringTextInputFormatter.digitsOnly], onChanged: (_) => _checkDuplicateBoxNo(), decoration: InputDecoration( @@ -825,7 +1118,7 @@ class _BoxingPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '数量', + _editingBox == null ? '数量' : '修改数量', style: TextStyle( fontSize: 13, color: enabled ? Colors.black54 : Colors.grey, @@ -838,8 +1131,9 @@ class _BoxingPageState extends State { controller: _quantityController, focusNode: _quantityFocusNode, enabled: enabled, - keyboardType: TextInputType.number, + keyboardType: TextInputType.none, inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChanged: (_) => setState(() {}), decoration: const InputDecoration( contentPadding: EdgeInsets.symmetric(horizontal: 10), border: OutlineInputBorder(), @@ -872,7 +1166,10 @@ class _BoxingPageState extends State { color: Colors.white, ), ) - : const Text('确认', style: TextStyle(fontSize: 15)), + : Text( + _editingBox == null ? '确认' : '保存', + style: const TextStyle(fontSize: 15), + ), ), ), ], diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 17d0ddb..ee3760b 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -32,11 +32,13 @@ class RegistrationResult { /// 箱号内单个总排号明细 class BoxItemData { + final int? boxItemId; final String zongpaiNo; final String? workOrderNo; final int quantity; BoxItemData({ + this.boxItemId, required this.zongpaiNo, this.workOrderNo, required this.quantity, @@ -44,6 +46,7 @@ class BoxItemData { factory BoxItemData.fromJson(Map json) { return BoxItemData( + boxItemId: json['box_item_id'] as int?, zongpaiNo: json['zongpai_no'] as String, workOrderNo: json['work_order_no']?.toString(), quantity: json['quantity'] as int, @@ -68,6 +71,27 @@ class BoxDetailData { } } +/// 当前总排号已分配的装箱明细 +class CurrentZongpaiBoxData { + final int boxItemId; + final int boxNo; + final int quantity; + + CurrentZongpaiBoxData({ + required this.boxItemId, + required this.boxNo, + required this.quantity, + }); + + factory CurrentZongpaiBoxData.fromJson(Map json) { + return CurrentZongpaiBoxData( + boxItemId: json['box_item_id'] as int, + boxNo: json['box_no'] as int, + quantity: json['quantity'] as int, + ); + } +} + /// 装箱信息查询结果 class BoxInfoResult { final bool success; @@ -76,6 +100,7 @@ class BoxInfoResult { final String? paichanNo; final String? workOrderNo; final int? quantity; + final List currentZongpaiBoxes; final List existingBoxes; final int maxBoxNo; final int suggestedBoxNo; @@ -87,6 +112,7 @@ class BoxInfoResult { this.paichanNo, this.workOrderNo, this.quantity, + this.currentZongpaiBoxes = const [], this.existingBoxes = const [], this.maxBoxNo = 0, this.suggestedBoxNo = 1, @@ -98,12 +124,20 @@ class BoxInfoResult { ?.map((b) => BoxDetailData.fromJson(b as Map)) .toList() ?? []; + final currentBoxes = + (json['current_zongpai_boxes'] as List?) + ?.map( + (b) => CurrentZongpaiBoxData.fromJson(b as Map), + ) + .toList() ?? + []; return BoxInfoResult( success: true, zongpaiNo: json['zongpai_no'] as String?, paichanNo: json['paichan_no'] as String?, workOrderNo: json['work_order_no']?.toString(), quantity: json['quantity'] as int?, + currentZongpaiBoxes: currentBoxes, existingBoxes: boxes, maxBoxNo: json['max_box_no'] as int? ?? 0, suggestedBoxNo: json['suggested_box_no'] as int? ?? 1, @@ -120,22 +154,28 @@ class BoxSaveResult { final bool success; final bool isDuplicate; final String? errorMessage; + final int? boxItemId; final String? paichanNo; final int? boxNo; + final int? quantity; BoxSaveResult({ required this.success, this.isDuplicate = false, this.errorMessage, + this.boxItemId, this.paichanNo, this.boxNo, + this.quantity, }); factory BoxSaveResult.ok(Map json) { return BoxSaveResult( success: true, + boxItemId: json['box_item_id'] as int?, paichanNo: json['paichan_no'] as String?, boxNo: json['box_no'] as int?, + quantity: json['quantity'] as int?, ); } @@ -153,6 +193,13 @@ class BoxSaveResult { } } +class BoxDeleteResult { + final bool success; + final String? errorMessage; + + BoxDeleteResult({required this.success, this.errorMessage}); +} + class ApiService { final http.Client _client; final Duration timeout; @@ -275,6 +322,75 @@ class ApiService { } } + /// 更新装箱明细 — PATCH /CargoTrace/box/{boxItemId} + Future updateBoxRecord({ + required String baseUrl, + required int boxItemId, + required int boxNo, + required int quantity, + }) async { + final uri = Uri.parse('$baseUrl/CargoTrace/box/$boxItemId'); + try { + final response = await _client + .patch( + uri, + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'box_no': boxNo, + 'quantity': quantity, + 'box_mode': 'one-to-many', + }), + ) + .timeout(timeout); + + switch (response.statusCode) { + case 200: + final body = jsonDecode(response.body) as Map; + return BoxSaveResult.ok(body); + case 400: + final body = jsonDecode(response.body) as Map; + final msg = body['message']?.toString() ?? '请求参数错误'; + return BoxSaveResult.error(msg); + case 409: + final body = jsonDecode(response.body) as Map; + return BoxSaveResult.duplicate(body); + case 404: + return BoxSaveResult.error('指定装箱明细不存在'); + default: + return BoxSaveResult.error('修改失败 (${response.statusCode})'); + } + } catch (e) { + return BoxSaveResult.error('网络异常,请检查网络连接'); + } + } + + /// 删除装箱明细 — DELETE /CargoTrace/box/{boxItemId} + Future deleteBoxRecord({ + required String baseUrl, + required int boxItemId, + }) async { + final uri = Uri.parse('$baseUrl/CargoTrace/box/$boxItemId'); + try { + final response = await _client + .delete(uri, headers: {'Content-Type': 'application/json'}) + .timeout(timeout); + + switch (response.statusCode) { + case 200: + return BoxDeleteResult(success: true); + case 404: + return BoxDeleteResult(success: false, errorMessage: '指定装箱明细不存在'); + default: + return BoxDeleteResult( + success: false, + errorMessage: '删除失败 (${response.statusCode})', + ); + } + } catch (e) { + return BoxDeleteResult(success: false, errorMessage: '网络异常,请检查网络连接'); + } + } + /// Test connectivity by making a HEAD request to the base URL. Future testConnection(String baseUrl) async { try {