From cf5415308a871b0b57b22b87bf270ffb27299b3a Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Tue, 3 Mar 2026 10:13:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=92=A4=E5=9B=9E=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BC=98=E5=8C=96=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/map/testmap_pages.dart | 242 +++++++++++------- .../presentation/widgets/startpoint_area.dart | 6 +- 2 files changed, 161 insertions(+), 87 deletions(-) diff --git a/lib/features/home/presentation/widgets/map/testmap_pages.dart b/lib/features/home/presentation/widgets/map/testmap_pages.dart index 92b4f8b2..93c07427 100644 --- a/lib/features/home/presentation/widgets/map/testmap_pages.dart +++ b/lib/features/home/presentation/widgets/map/testmap_pages.dart @@ -352,34 +352,6 @@ class _MapPageEnterpriseState extends State { ), 'planModel': _currentWorkMode == WorkMode.bow ? 0 : 2, // 作业模式值 }; - // 2. 解析并打印 Path - try { - final List pathData = jsonDecode(savePath['path'] as String); - print('\n🗺️ 路径数据 (共 ${pathData.length} 个点):'); - // 格式化打印前 3 个点,避免数据过多刷屏 - for (int i = 0; i < math.min(3, pathData.length); i++) { - final point = pathData[i] as Map; - print(' 点 $i: lat=${point['lat']}, lon=${point['lon']}'); - } - if (pathData.length > 3) { - print(' ... 还有 ${pathData.length - 3} 个点'); - } - } catch (e) { - print('❌ 解析 path 失败: $e'); - } - - // 3. 解析并打印 Outer - try { - final List outerData = jsonDecode(savePath['outer'] as String); - print('\n📏 外边界数据 (共 ${outerData.length} 个点):'); - // 格式化打印所有外边界点(通常数量少) - for (int i = 0; i < outerData.length; i++) { - final point = outerData[i] as Map; - print(' 点 $i: lat=${point['lat']}, lon=${point['lon']}'); - } - } catch (e) { - print('❌ 解析 outer 失败: $e'); - } // 3. 构造 workRecord (包裹一层) final Map workRecord = { @@ -425,6 +397,7 @@ class _MapPageEnterpriseState extends State { debugPrint('保存地块响应体: $responseBody'); if (response.statusCode == 200) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('地块「$plotName」保存成功!'), backgroundColor: Colors.green)); // 保存成功 setState(() { _markedPoints.clear(); // 清空打点 @@ -434,8 +407,6 @@ class _MapPageEnterpriseState extends State { _isPanelOpen = false; // 关闭操作面板 _isWorkAreaCompleted = false; // 重置作业区域完成状态 }); - - ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('地块「$plotName」保存成功!'), backgroundColor: Colors.green)); } else { throw Exception('服务器错误: ${response.statusCode}, $responseBody'); } @@ -453,34 +424,125 @@ class _MapPageEnterpriseState extends State { return polygonPoints; } - void _undoLastPoint() { - if (_markedPoints.isNotEmpty) { - setState(() { - _markedPoints.removeLast(); // 删除最后一个打点 - }); - debugPrint('撤回成功,剩余打点数量:${_markedPoints.length}'); - } else { - debugPrint('无打点可撤回'); - // 可选:提示用户 - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无打点可撤回'))); - } + //void _undoLastPoint() { + // if (_markedPoints.isNotEmpty) { + // setState(() { + // _markedPoints.removeLast(); // 删除最后一个打点 + // }); + // debugPrint('撤回成功,剩余打点数量:${_markedPoints.length}'); + // } else { + // debugPrint('无打点可撤回'); + // // 可选:提示用户 + // ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无打点可撤回'))); + // } + //} + + // ========== 统一撤回方法(适配完成后状态) ========== + void _undoAction() { + setState(() { + // 1. 作业区域模式 + if (_currentAreaMode == AreaMode.work) { + if (_markedPoints.isNotEmpty) { + _markedPoints.removeLast(); + // 作业点撤回后,若路径已生成,重新生成路径(保持同步) + if (_isWorkAreaCompleted && _markedPoints.length >= 3) { + _generatePath(showTips: false); + } + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已撤回作业区域最后一个点,剩余${_markedPoints.length}个点'))); + } else { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无作业区域点可撤回'))); + } + return; + } + + // 2. 空洞模式(完成后优先撤回已完成的空洞) + if (_currentAreaMode == AreaMode.obstacle) { + if (_currentObstaclePoints.isNotEmpty) { + _currentObstaclePoints.removeLast(); + // 如果当前空洞的点撤空了,退出编辑状态 + if (_currentObstaclePoints.isEmpty) { + _isObstacleEditing = false; + } + + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已撤回当前空洞最后1个点,剩余${_currentObstaclePoints.length}个点'))); + return; // 撤完点就结束,不执行后面的逻辑 + } + + // 第二步:当前空洞无点可撤 → 再撤回「上一个完整空洞」 + if (_obstacleHoles.isNotEmpty) { + _obstacleHoles.removeLast(); + // 撤完空洞后重新生成路径(排除该空洞) + if (_isWorkAreaCompleted) { + _generatePath(showTips: false); + } + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已撤回上一个完整空洞,剩余${_obstacleHoles.length}个空洞'))); + return; + } + + // 第三步:无任何可撤内容 + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无空洞可撤回'))); + } + }); } - // ========== 核心:清空所有打点和轨迹 ========== - void _deleteAllPoints() { - if (_markedPoints.isNotEmpty) { - setState(() { - _markedPoints.clear(); // 清空所有打点 - }); - debugPrint('删除成功,已清空所有打点和轨迹'); - // 可选:提示用户 - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有打点和轨迹'))); - } else { - debugPrint('无打点可删除'); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无打点可删除'))); - } + // 兼容原有调用(可保留,也可直接用上面的_unifiedUndo) + void _undoLastPoint() => _undoAction(); + void _undoObstaclePoint() => _undoAction(); + + // ========== 统一删除方法(适配完成后状态) ========== + void _deleteAllAction() { + setState(() { + // 1. 作业区域模式:清空作业点 + 重置路径 + if (_currentAreaMode == AreaMode.work) { + if (_markedPoints.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无作业区域点可删除'))); + return; + } + _markedPoints.clear(); + gcjPathPoints.clear(); + gcjOuterPoints.clear(); + _isWorkAreaCompleted = false; + _saveBoxOpen = false; + typedPathList.clear(); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有作业区域点和路径'))); + } + // 2. 空洞模式:清空所有空洞 + 恢复UI + 重新生成路径 + else if (_currentAreaMode == AreaMode.obstacle) { + if (_obstacleHoles.isEmpty && _currentObstaclePoints.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无空洞可删除'))); + return; + } + _obstacleHoles.clear(); + _currentObstaclePoints.clear(); + _isObstacleEditing = false; + // 清空后重新生成路径(无空洞) + if (_isWorkAreaCompleted) { + _generatePath(showTips: false); + } + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有空洞'))); + } + }); } + // 兼容原有调用 + void _deleteAllPoints() => _deleteAllAction(); + void _clearAllObstacles() => _deleteAllAction(); + + // ========== 核心:清空所有打点和轨迹 ========== + //void _deleteAllPoints() { + // if (_markedPoints.isNotEmpty) { + // setState(() { + // _markedPoints.clear(); // 清空所有打点 + // }); + // debugPrint('删除成功,已清空所有打点和轨迹'); + // // 可选:提示用户 + // ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有打点和轨迹'))); + // } else { + // debugPrint('无打点可删除'); + // ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无打点可删除'))); + // } + //} + /// =============================== /// 企业级定位初始化(已修复坐标系) /// =============================== @@ -634,32 +696,36 @@ class _MapPageEnterpriseState extends State { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已添加第${_obstacleHoles.length}个障碍物区域'), backgroundColor: Colors.green)); } - void _undoObstaclePoint() { - if (_currentObstaclePoints.isNotEmpty) { - setState(() => _currentObstaclePoints.removeLast()); - debugPrint('撤回障碍物打点,剩余:${_currentObstaclePoints.length}个'); - } else if (_obstacleHoles.isNotEmpty) { - // 无当前绘制点时,删除最后一组障碍物 - setState(() => _obstacleHoles.removeLast()); - debugPrint('删除最后一组障碍物,剩余组数:${_obstacleHoles.length}'); - } else { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无障碍物打点可撤回'))); - } - } + //void _undoObstaclePoint() { + // if (_currentObstaclePoints.isNotEmpty) { + // setState(() => _currentObstaclePoints.removeLast()); + // debugPrint('撤回障碍物打点,剩余:${_currentObstaclePoints.length}个'); + // } else if (_obstacleHoles.isNotEmpty) { + // // 无当前绘制点时,删除最后一组障碍物 + // setState(() => _obstacleHoles.removeLast()); + // debugPrint('删除最后一组障碍物,剩余组数:${_obstacleHoles.length}'); + // } else { + // ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无障碍物打点可撤回'))); + // } + //} - // ========== 障碍物模式:清空所有障碍物 ========== - void _clearAllObstacles() { - setState(() { - _obstacleHoles.clear(); - _currentObstaclePoints.clear(); - _isObstacleEditing = false; - }); - debugPrint('已清空所有障碍物区域'); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有障碍物区域'), backgroundColor: Colors.amber)); - } + //// ========== 障碍物模式:清空所有障碍物 ========== + //void _clearAllObstacles() { + // setState(() { + // _obstacleHoles.clear(); + // _currentObstaclePoints.clear(); + // _isObstacleEditing = false; + // }); + // debugPrint('已清空所有障碍物区域'); + // ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有障碍物区域'), backgroundColor: Colors.amber)); + //} // ========== 核心方法:打点逻辑 ========== void _addMarkedPoint() { + if (_currentAreaMode == AreaMode.work && _isWorkAreaCompleted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('作业区域已完成,无法继续添加打点!'), backgroundColor: Colors.red)); + return; + } setState(() { if (_currentAreaMode == AreaMode.work) { _markedPoints.add(_mapCenter); @@ -1827,6 +1893,7 @@ class _MapPageEnterpriseState extends State { initialRobotMode: RobotMode.point, initialAreaMode: AreaMode.work, initialWorkMode: _currentWorkMode!, + canUndo: _isWorkAreaCompleted && _currentAreaMode == AreaMode.work ? false : true, onComplete: () async { debugPrint( '操作完成回调:当前机器人模式=$_currentRobotMode,当前区域模式=$_currentAreaMode,当前作业模式:$_currentWorkMode,作业区域点:$_markedPoints,作业行距=$_workDistance,航线方向角=$_angle', @@ -1846,19 +1913,22 @@ class _MapPageEnterpriseState extends State { }, onUndoTap: () { // 区分模式:作业区域撤回/障碍物撤回 - if (_currentAreaMode == AreaMode.work) { - _undoLastPoint(); - } else { - _undoObstaclePoint(); - } + //if (_currentAreaMode == AreaMode.work) { + // _undoLastPoint(); + //} else { + // _undoObstaclePoint(); + //} + _undoAction(); }, onDeleteTap: () { + _deleteAllAction(); + // 区分模式:作业区域清空/障碍物清空 - if (_currentAreaMode == AreaMode.work) { - _deleteAllPoints(); - } else { - _clearAllObstacles(); - } + //if (_currentAreaMode == AreaMode.work) { + // _deleteAllPoints(); + //} else { + // _clearAllObstacles(); + //} }, onRobotModeChanged: (mode) { setState(() => _currentRobotMode = mode); diff --git a/lib/features/home/presentation/widgets/startpoint_area.dart b/lib/features/home/presentation/widgets/startpoint_area.dart index 0f590fa8..c323acfe 100644 --- a/lib/features/home/presentation/widgets/startpoint_area.dart +++ b/lib/features/home/presentation/widgets/startpoint_area.dart @@ -10,6 +10,7 @@ class BottomOperationPanel extends StatefulWidget { // 新增:外部控制初始选中的标签(默认地块) final String initialSelectedTab; final WorkMode initialWorkMode; + final bool? canUndo; // 是否可以撤回(控制撤回按钮状态) // 回调函数:向外部传递事件 final VoidCallback onComplete; // 完成按钮点击回调 final ValueChanged? onRobotModeChanged; // 机器人模式变更回调 @@ -41,6 +42,7 @@ class BottomOperationPanel extends StatefulWidget { this.onDistanceTap, this.onDeleteTap, this.onUndoTap, + this.canUndo, // 接收撤回按钮状态 }); @override @@ -52,6 +54,7 @@ class _BottomOperationPanelState extends State { late AreaMode _areaMode; // 新增:当前选中的标签(地块/航线)- 核心状态 late String _currentTab; + late bool canUndo; // 新增:航线模式下的作业行距(默认0.5米) double _operationDistance = 0.5; @@ -62,6 +65,7 @@ class _BottomOperationPanelState extends State { _robotMode = widget.initialRobotMode; _areaMode = widget.initialAreaMode; // 初始化选中的标签 + canUndo = widget.canUndo ?? true; _currentTab = widget.initialSelectedTab; } @@ -194,7 +198,7 @@ class _BottomOperationPanelState extends State { debugPrint('点击了删除按钮,清空所有打点'); widget.onDeleteTap?.call(); }), - _buildActionButton(Icons.undo_outlined, '撤回', true, () { + _buildActionButton(Icons.undo_outlined, '撤回', widget.canUndo ?? true, () { debugPrint('点击了撤回按钮,删除最后一个打点'); widget.onUndoTap?.call(); }),