diff --git a/lib/features/home/presentation/widgets/common/input_confirm_dialog.dart b/lib/features/home/presentation/widgets/common/input_confirm_dialog.dart new file mode 100644 index 00000000..ef7fd301 --- /dev/null +++ b/lib/features/home/presentation/widgets/common/input_confirm_dialog.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; + +/// 通用带输入框的确认弹窗组件 +/// [title]:弹窗标题 +/// [hintText]:输入框提示文字 +/// [labelText]:输入框标签文字 +/// [confirmText]:确认按钮文字(默认"保存") +/// [cancelText]:取消按钮文字(默认"取消") +/// [onConfirm]:确认按钮回调,参数为输入的文本,返回bool表示是否关闭弹窗 +/// [inputValidator]:输入内容校验器,返回null表示校验通过,返回字符串表示错误提示 +Future showInputConfirmDialog({ + required BuildContext context, + required String title, + required String hintText, + String labelText = '', + String confirmText = '保存', + String cancelText = '取消', + required Function(String inputText) onConfirm, + String? Function(String inputText)? inputValidator, +}) async { + // 输入框控制器 + final TextEditingController _controller = TextEditingController(); + + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: TextField( + controller: _controller, + decoration: InputDecoration( + hintText: hintText, + border: const OutlineInputBorder(), + labelText: labelText, + ), + autofocus: true, // 自动聚焦输入框 + ), + actions: [ + // 取消按钮 + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(cancelText), + ), + // 确认按钮 + TextButton( + onPressed: () { + // 获取输入文本并去空格 + final String inputText = _controller.text.trim(); + + // 1. 执行自定义校验 + if (inputValidator != null) { + final String? errorMsg = inputValidator(inputText); + if (errorMsg != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(errorMsg), + backgroundColor: Colors.red, + ), + ); + return; + } + } + + // 2. 执行确认回调 + onConfirm(inputText); + + // 3. 关闭弹窗 + Navigator.of(context).pop(); + }, + child: Text(confirmText), + ), + ], + ); + }, + ); +} diff --git a/lib/features/home/presentation/widgets/map/testmap_pages.dart b/lib/features/home/presentation/widgets/map/testmap_pages.dart index c938b197..9c0b715a 100644 --- a/lib/features/home/presentation/widgets/map/testmap_pages.dart +++ b/lib/features/home/presentation/widgets/map/testmap_pages.dart @@ -20,6 +20,7 @@ import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubi import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.dart'; import 'package:maibu_satabot_v2/features/home/presentation/widgets/BottomDirectionLine.dart'; import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/enum.dart'; +import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/input_confirm_dialog.dart'; import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/tracepoint.dart'; import 'package:maibu_satabot_v2/features/home/presentation/widgets/map/CenterLocation.dart'; import 'package:maibu_satabot_v2/features/home/presentation/widgets/map/HeadingPointer.dart'; @@ -64,9 +65,9 @@ class _MapPageEnterpriseState extends State { bool _isRefreshing = false; // 新增:页面刷新状态标志 final MapController _mapController = MapController(); - final TextEditingController _plotNameController = TextEditingController(); // 初始化轨迹管理器(泛型指定为LatLng) late final TracePoint _traceManager; + late String workMode = "弓字模式"; // 作业模式:默认值为"弓字模式" LatLng? _currentLatLng; StreamSubscription? _positionSub; @@ -215,65 +216,31 @@ class _MapPageEnterpriseState extends State { } void _showSavePlotDialog() { - // 清空上次的输入内容 - _plotNameController.clear(); - - // 显示弹窗 - showDialog( + showInputConfirmDialog( context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('保存地块'), - content: TextField( - controller: _plotNameController, - decoration: const InputDecoration( - hintText: '请输入地块名称(如:北地块、一号田)', - border: OutlineInputBorder(), - labelText: '地块名称', - ), + title: '保存地块', // 自定义标题 + hintText: '请输入地块名称(如:北地块、一号田)', // 自定义输入提示 + labelText: '地块名称', // 自定义输入框标签 + confirmText: '保存', // 确认按钮文字 + cancelText: '取消', // 取消按钮文字 + // 输入校验器(可选) + inputValidator: (inputText) { + if (inputText.isEmpty) { + return '地块名称不能为空!'; + } + if (inputText.length > 20) { + return '地块名称不能超过20个字符!'; + } + return null; // 校验通过 + }, + onConfirm: (plotName) { + _savePlotData(plotName); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('地块「$plotName」保存成功!'), + backgroundColor: Colors.green, ), - actions: [ - // 取消按钮 - TextButton( - onPressed: () { - Navigator.of(context).pop(); // 关闭弹窗 - }, - child: const Text('取消'), - ), - // 确认保存按钮 - TextButton( - onPressed: () { - // 获取输入的地块名称并去除首尾空格 - String plotName = _plotNameController.text.trim(); - - // 验证输入是否为空 - if (plotName.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('地块名称不能为空!'), - backgroundColor: Colors.red, - ), - ); - return; - } - - // 执行保存逻辑 - _savePlotData(plotName); - - // 关闭弹窗 - Navigator.of(context).pop(); - - // 保存成功提示 - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('地块「$plotName」保存成功!'), - backgroundColor: Colors.green, - ), - ); - }, - child: const Text('保存'), - ), - ], ); }, ); @@ -501,9 +468,7 @@ class _MapPageEnterpriseState extends State { _positionSub?.cancel(); _traceManager.reset(); _mapController.dispose(); - // 修复:_sub 是 non-null 类型,直接 cancel 无需 ? _sub.cancel(); - _plotNameController.dispose(); // 新增:释放文本控制器资源 super.dispose(); } @@ -555,9 +520,13 @@ class _MapPageEnterpriseState extends State { try { final dynamic nestedJsonRaw = selectedPlotPath.jsonData['jsonData']; + if (nestedJsonRaw != null && nestedJsonRaw is String) { final String nestedJsonStr = nestedJsonRaw; final dynamic parsedJson = jsonDecode(nestedJsonStr); + workMode = parsedJson['planModel'] == WorkMode.bow.value + ? "弓字模式" + : "自定义模式"; if (parsedJson is Map) { final Map nestedJson = parsedJson; final dynamic rawPath = nestedJson['path']; @@ -716,19 +685,6 @@ class _MapPageEnterpriseState extends State { return const SizedBox.shrink(); } - String workMode = "未知模式"; - if (_selectedPlotPath != null) { - try { - final String nestedJsonStr = - _selectedPlotPath!.jsonData['jsonData'] as String; - final Map nestedJson = jsonDecode(nestedJsonStr); - workMode = nestedJson['planModel'] == WorkMode.bow.value - ? "弓字模式" - : "自定义模式"; - } catch (e) { - workMode = "未知模式"; - } - } return Positioned( left: 0, right: 0,