diff --git a/lib/features/home/presentation/widgets/BottomDirectionLine.dart b/lib/features/home/presentation/widgets/BottomDirectionLine.dart new file mode 100644 index 00000000..188a5d29 --- /dev/null +++ b/lib/features/home/presentation/widgets/BottomDirectionLine.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; + +class RouteDirectionPanel extends StatefulWidget { // 重命名为Panel更符合组件语义 + // 接收外部传入的初始值 + final bool initialOptimalHeading; + final double initialDirection; + // 新增:回调函数 - 关闭面板/返回设置结果 + final ValueChanged>? onConfirm; + final VoidCallback? onCancel; + + const RouteDirectionPanel({ + super.key, + this.initialOptimalHeading = true, // 默认开启最优航向 + this.initialDirection = 0.0, // 默认方向0度 + this.onConfirm, + this.onCancel, + }); + + @override + State createState() => _RouteDirectionPanelState(); +} + +class _RouteDirectionPanelState extends State { + late bool _optimalHeading; + late double _direction; // 0-360度范围 + + @override + void initState() { + super.initState(); + _optimalHeading = widget.initialOptimalHeading; + _direction = widget.initialDirection.clamp(0, 360); // 限制在0-360度 + } + + @override + Widget build(BuildContext context) { + // 纯面板结构:无Scaffold,仅Container包裹核心UI + return Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)], + ), + padding: const EdgeInsets.all(16), + constraints: const BoxConstraints(minHeight: 300), // 最小高度保证UI完整 + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 顶部标题栏 + 返回按钮 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // 取消/返回按钮 + GestureDetector( + onTap: () { + widget.onCancel?.call(); // 触发取消回调 + }, + child: const Icon( + Icons.arrow_back_ios, + color: Colors.black87, + size: 20, + ), + ), + const Text( + '航线方向', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(width: 24), // 占位保持标题居中 + ], + ), + const SizedBox(height: 20), + const Divider(height: 1, color: Colors.grey), + const SizedBox(height: 20), + + // 最优航向开关 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '最优航向', + style: TextStyle(fontSize: 16, color: Colors.black87), + ), + Switch( + value: _optimalHeading, + activeColor: const Color(0xFF00C853), + onChanged: (bool value) { + setState(() { + _optimalHeading = value; + }); + }, + ), + ], + ), + const SizedBox(height: 30), + + // 航线方向滑块 + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '航线方向', + style: TextStyle(fontSize: 16, color: Colors.black87), + ), + Text( + '${_direction.toStringAsFixed(0)}°', + style: TextStyle( + fontSize: 16, + color: _optimalHeading ? Colors.grey : Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + const SizedBox(height: 15), + Slider( + value: _direction, + min: 0, + max: 360, + divisions: 36, + label: '${_direction.toStringAsFixed(0)}°', + activeColor: const Color(0xFF00C853), + inactiveColor: Colors.grey[300], + onChanged: _optimalHeading + ? null + : (double value) { + setState(() { + _direction = value; + }); + }, + ), + ], + ), + const SizedBox(height: 40), + + // 确认按钮 + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + // 触发确认回调,传递设置结果 + widget.onConfirm?.call({ + 'optimalHeading': _optimalHeading, + 'direction': _direction, + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF00C853), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text('确认'), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/home/presentation/widgets/map/testmap_pages.dart b/lib/features/home/presentation/widgets/map/testmap_pages.dart index 1235e237..c10d9f43 100644 --- a/lib/features/home/presentation/widgets/map/testmap_pages.dart +++ b/lib/features/home/presentation/widgets/map/testmap_pages.dart @@ -4,12 +4,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.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/startpoint_area.dart'; import '../path_list_pages.dart'; - class MapPageEnterprise extends StatefulWidget { const MapPageEnterprise({Key? key}) : super(key: key); @@ -24,8 +24,32 @@ class _MapPageEnterpriseState extends State { StreamSubscription? _positionSub; bool _hasMovedOnce = false; bool _isPanelOpen = false; // 控制面板显示/隐藏 - RobotMode _currentRobotMode = RobotMode.point; - AreaMode _currentAreaMode = AreaMode.work; + bool _directionBoxOpen = false; //航线面板显示/隐藏 + + RobotMode _currentRobotMode = RobotMode.point; //打点模式 + AreaMode _currentAreaMode = AreaMode.work; //作业区域还是障碍物区域 + WorkMode? _currentWorkMode = WorkMode.bow; // 当前作业模式 + + // ========== 新增:打点相关状态 ========== + List _markedPoints = []; // 存储所有打点的坐标 + int _pointIndex = 1; // 打点序号(用于标记显示) + + void _addMapMarker() { + // 1. 获取当前地图中心的坐标 + LatLng centerLatLng = _mapController.center; + + // 2. 记录打点坐标(带序号) + setState(() { + _markedPoints.add(centerLatLng); + }); + + // 3. 打印打点信息(调试用) + debugPrint( + '第$_pointIndex个点:纬度=${centerLatLng.latitude.toStringAsFixed(6)},经度=${centerLatLng.longitude.toStringAsFixed(6)}', + ); + // 4. 序号自增 + _pointIndex++; + } @override void initState() { @@ -58,15 +82,16 @@ class _MapPageEnterpriseState extends State { _updateLocation(gcj, moveMap: true); // 2️⃣ 实时监听(不再强制移动地图) - _positionSub = Geolocator.getPositionStream( - locationSettings: const LocationSettings( - accuracy: LocationAccuracy.bestForNavigation, - distanceFilter: 2, - ), - ).listen((pos) { - final gcj = wgs84ToGcj02(pos.latitude, pos.longitude); - _updateLocation(gcj, moveMap: false); - }); + _positionSub = + Geolocator.getPositionStream( + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.bestForNavigation, + distanceFilter: 2, + ), + ).listen((pos) { + final gcj = wgs84ToGcj02(pos.latitude, pos.longitude); + _updateLocation(gcj, moveMap: false); + }); } /// =============================== @@ -101,102 +126,137 @@ class _MapPageEnterpriseState extends State { final menuHeight = 16 * 6; // 假设 VerticalFloatMenu 有 6 个选项,每个高度为 56 final maxTop = screenHeight - menuHeight; return Scaffold( - body: - SafeArea( + body: SafeArea( child: Stack( - children: [ - FlutterMap( - mapController: _mapController, - options: MapOptions( - initialCenter: - _currentLatLng ?? const LatLng(39.9042, 116.4074), - initialZoom: 15, - maxZoom: 18, - ), - children: [ - /// 高德瓦片(GCJ-02) - TileLayer( - urlTemplate: - 'https://webrd02.is.autonavi.com/appmaptile' - '?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1' - '&key=bbb1f0f20eed6bf679eddf2625630aba', + children: [ + FlutterMap( + mapController: _mapController, + options: MapOptions( + initialCenter: + _currentLatLng ?? const LatLng(39.9042, 116.4074), + initialZoom: 15, + maxZoom: 18, ), - - /// 当前定位 Marker - if (_currentLatLng != null) - MarkerLayer( - markers: [ - Marker( - point: _currentLatLng!, - width: 40, - height: 40, - child: const Icon( - Icons.my_location, - color: Colors.blue, - size: 30, - ), - ), - ], + children: [ + /// 高德瓦片(GCJ-02) + TileLayer( + urlTemplate: + 'https://webrd02.is.autonavi.com/appmaptile' + '?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1' + '&key=bbb1f0f20eed6bf679eddf2625630aba', ), - ], - ), - Positioned( - height: 336, - right: 16, - top: maxTop > 10 ? 11 : maxTop, // 确保不超出屏幕范围 - child: FloatingActionButton( - onPressed: _moveToCurrentLocation, - child: new VerticalFloatMenu( - onEditTap: (bool isOpen) { - // 关键:接收子组件的回调,修改外部的_isPanelOpen - setState(() { - _isPanelOpen = isOpen; - }); - },), + /// 当前定位 Marker + if (_currentLatLng != null) + MarkerLayer( + markers: [ + Marker( + point: _currentLatLng!, + width: 40, + height: 40, + child: const Icon( + Icons.my_location, + color: Colors.blue, + size: 30, + ), + ), + ], + ), + ], ), - ), - if (_isPanelOpen) + Positioned( - left: 0, - right: 0, - bottom: 0, - child: BottomOperationPanel( - // 传入初始值 - initialRobotMode:RobotMode.point , - initialAreaMode: AreaMode.work, - // 接收面板的事件回调 - onComplete: () { - // 完成按钮点击:关闭面板 - setState(() => _isPanelOpen = false); - }, - onRobotModeChanged: (mode) { - // 接收机器人模式变更 - setState(() => _currentRobotMode = mode); - debugPrint('外部收到机器人模式:$mode'); - }, - onAreaModeChanged: (mode) { - // 接收作业区域模式变更 - setState(() => _currentAreaMode = mode); - debugPrint('外部收到区域模式:$mode'); - }, - onAddTap: () { - // 加号按钮自定义逻辑 - debugPrint('外部处理加号按钮点击'); - }, - onLandTap: () { - // 地块标签自定义逻辑 - debugPrint('外部处理地块标签点击'); - }, - onRouteTap: () { - // 航线标签自定义逻辑 - debugPrint('外部处理航线标签点击'); - }, + height: 336, + right: 16, + top: maxTop > 10 ? 11 : maxTop, // 确保不超出屏幕范围 + child: FloatingActionButton( + onPressed: _moveToCurrentLocation, + child: new VerticalFloatMenu( + onEditTap: (bool isOpen) { + // 关键:接收子组件的回调,修改外部的_isPanelOpen + setState(() { + _isPanelOpen = isOpen; + }); + }, + onWorkModeSelected: (mode) { + // 接收作业模式选择结果 + _currentWorkMode = mode; + debugPrint('外部收到作业模式:$mode'); + }, + ), ), ), - ], + if (_isPanelOpen) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: BottomOperationPanel( + // 传入初始值 + initialRobotMode: RobotMode.point, + initialAreaMode: AreaMode.work, + // 接收面板的事件回调 + onComplete: () { + // 完成按钮点击:关闭面板 + setState(() => _isPanelOpen = false); + }, + onRobotModeChanged: (mode) { + // 接收机器人模式变更 + setState(() => _currentRobotMode = mode); + debugPrint('外部收到机器人模式:$mode'); + }, + onAreaModeChanged: (mode) { + // 接收作业区域模式变更 + setState(() => _currentAreaMode = mode); + debugPrint('外部收到区域模式:$mode'); + }, + onAddTap: () { + // 加号按钮自定义逻辑 + _addMapMarker(); + debugPrint('外部处理加号按钮点击'); + }, + onSettingTap: () { + // 设置按钮自定义逻辑 + debugPrint('外部处理设置按钮点击'); + setState(() { + _directionBoxOpen = true; + }); + }, + onLandTap: () { + // 地块标签自定义逻辑 + debugPrint('外部处理地块标签点击'); + }, + onRouteTap: () { + // 航线标签自定义逻辑 + debugPrint('外部处理航线标签点击'); + }, + ), + ), + if (_directionBoxOpen) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: RouteDirectionPanel( + initialOptimalHeading: true, + initialDirection: 0.0, + onConfirm: (result) { + // 处理确认结果 + debugPrint( + '最优航向:${result['optimalHeading']},角度:${result['direction']}', + ); + setState(() => _directionBoxOpen = false); + }, + onCancel: () { + // 关闭面板 + setState(() => _directionBoxOpen = false); + }, + ), + ), + ], + ), ), - )); + ); } } @@ -218,8 +278,7 @@ LatLng wgs84ToGcj02(double lat, double lon) { double magic = sin(radLat); magic = 1 - _ee * magic * magic; double sqrtMagic = sqrt(magic); - dLat = (dLat * 180.0) / - ((_a * (1 - _ee)) / (magic * sqrtMagic) * _pi); + dLat = (dLat * 180.0) / ((_a * (1 - _ee)) / (magic * sqrtMagic) * _pi); dLon = (dLon * 180.0) / (_a / sqrtMagic * cos(radLat) * _pi); double mgLat = lat + dLat; double mgLon = lon + dLon; @@ -227,52 +286,29 @@ LatLng wgs84ToGcj02(double lat, double lon) { } bool _outOfChina(double lat, double lon) { - return lon < 72.004 || - lon > 137.8347 || - lat < 0.8293 || - lat > 55.8271; + return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271; } double _transformLat(double x, double y) { - double ret = -100.0 + + double ret = + -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(x.abs()); - ret += (20.0 * sin(6.0 * x * _pi) + - 20.0 * sin(2.0 * x * _pi)) * - 2.0 / - 3.0; - ret += (20.0 * sin(y * _pi) + - 40.0 * sin(y / 3.0 * _pi)) * - 2.0 / - 3.0; - ret += (160.0 * sin(y / 12.0 * _pi) + - 320 * sin(y * _pi / 30.0)) * - 2.0 / - 3.0; + ret += (20.0 * sin(6.0 * x * _pi) + 20.0 * sin(2.0 * x * _pi)) * 2.0 / 3.0; + ret += (20.0 * sin(y * _pi) + 40.0 * sin(y / 3.0 * _pi)) * 2.0 / 3.0; + ret += (160.0 * sin(y / 12.0 * _pi) + 320 * sin(y * _pi / 30.0)) * 2.0 / 3.0; return ret; } double _transformLon(double x, double y) { - double ret = 300.0 + - x + - 2.0 * y + - 0.1 * x * x + - 0.1 * x * y + - 0.1 * sqrt(x.abs()); - ret += (20.0 * sin(6.0 * x * _pi) + - 20.0 * sin(2.0 * x * _pi)) * - 2.0 / - 3.0; - ret += (20.0 * sin(x * _pi) + - 40.0 * sin(x / 3.0 * _pi)) * - 2.0 / - 3.0; - ret += (150.0 * sin(x / 12.0 * _pi) + - 300.0 * sin(x / 30.0 * _pi)) * - 2.0 / - 3.0; + double ret = + 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(x.abs()); + ret += (20.0 * sin(6.0 * x * _pi) + 20.0 * sin(2.0 * x * _pi)) * 2.0 / 3.0; + ret += (20.0 * sin(x * _pi) + 40.0 * sin(x / 3.0 * _pi)) * 2.0 / 3.0; + ret += + (150.0 * sin(x / 12.0 * _pi) + 300.0 * sin(x / 30.0 * _pi)) * 2.0 / 3.0; return ret; } diff --git a/lib/features/home/presentation/widgets/path_list_pages.dart b/lib/features/home/presentation/widgets/path_list_pages.dart index 0293edc2..21ce7015 100644 --- a/lib/features/home/presentation/widgets/path_list_pages.dart +++ b/lib/features/home/presentation/widgets/path_list_pages.dart @@ -4,7 +4,13 @@ import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/enum. class VerticalFloatMenu extends StatefulWidget { final Function(int index, String name)? onItemTap; final ValueChanged? onEditTap; - const VerticalFloatMenu({super.key, this.onItemTap, this.onEditTap}); + final ValueChanged? onWorkModeSelected; // 新增:作业模式选择回调 + const VerticalFloatMenu({ + super.key, + this.onItemTap, + this.onEditTap, + this.onWorkModeSelected, + }); @override State createState() => _VerticalFloatMenuState(); @@ -63,6 +69,8 @@ class _VerticalFloatMenuState extends State { if (result != null) { _selectedWorkMode = result; widget.onEditTap?.call(true); + // ========== 核心修改:传递result到外部 ========== + widget.onWorkModeSelected?.call(result); //_showBottomPanel(); } } @@ -210,10 +218,6 @@ class _VerticalFloatMenuState extends State { ); } - - - - void _handleSubmit() { debugPrint("作业模式: $_selectedWorkMode"); debugPrint("机器人模式: $_robotMode"); diff --git a/lib/features/home/presentation/widgets/startpoint_area.dart b/lib/features/home/presentation/widgets/startpoint_area.dart index 658fa17c..5605fd37 100644 --- a/lib/features/home/presentation/widgets/startpoint_area.dart +++ b/lib/features/home/presentation/widgets/startpoint_area.dart @@ -7,6 +7,8 @@ class BottomOperationPanel extends StatefulWidget { // 外部传入的初始值 final RobotMode initialRobotMode; final AreaMode initialAreaMode; + // 新增:外部控制初始选中的标签(默认地块) + final String initialSelectedTab; // 回调函数:向外部传递事件 final VoidCallback onComplete; // 完成按钮点击回调 final ValueChanged? onRobotModeChanged; // 机器人模式变更回调 @@ -14,17 +16,20 @@ class BottomOperationPanel extends StatefulWidget { final VoidCallback? onAddTap; // 加号按钮点击回调 final VoidCallback? onLandTap; // 地块标签点击回调 final VoidCallback? onRouteTap; // 航线标签点击回调 + final VoidCallback? onSettingTap; // 设置标签点击回调 const BottomOperationPanel({ super.key, this.initialRobotMode = RobotMode.point, this.initialAreaMode = AreaMode.work, + this.initialSelectedTab = '地块', // 默认选中地块 required this.onComplete, // 完成按钮必须传 this.onRobotModeChanged, this.onAreaModeChanged, this.onAddTap, this.onLandTap, this.onRouteTap, + this.onSettingTap, }); @override @@ -34,6 +39,10 @@ class BottomOperationPanel extends StatefulWidget { class _BottomOperationPanelState extends State { late RobotMode _robotMode; late AreaMode _areaMode; + // 新增:当前选中的标签(地块/航线)- 核心状态 + late String _currentTab; + // 新增:航线模式下的作业行距(默认0.5米) + double _operationDistance = 0.5; @override void initState() { @@ -41,6 +50,8 @@ class _BottomOperationPanelState extends State { // 初始化外部传入的默认值 _robotMode = widget.initialRobotMode; _areaMode = widget.initialAreaMode; + // 初始化选中的标签 + _currentTab = widget.initialSelectedTab; } @override @@ -52,87 +63,13 @@ class _BottomOperationPanelState extends State { boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)], ), padding: const EdgeInsets.all(16), - constraints: const BoxConstraints(minHeight: 260), + //constraints: const BoxConstraints(minHeight: 260), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ - // 面板核心内容 - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - DropdownButton( - value: _robotMode, - items: const [ - DropdownMenuItem( - value: RobotMode.point, - child: Text("地图打点"), - ), - DropdownMenuItem( - value: RobotMode.robot, - child: Text("机器人模式"), - ), - ], - onChanged: (v) { - if (v != null) { - setState(() => _robotMode = v); - // 触发回调,把选中的模式传给外部 - widget.onRobotModeChanged?.call(v); - } - }, - ), - GestureDetector( - onTap: () { - debugPrint('点击了添加按钮'); - // 触发加号按钮回调 - widget.onAddTap?.call(); - }, - child: Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - color: Color(0xFF00C853), - shape: BoxShape.circle, - ), - child: const Icon(Icons.add, color: Colors.white, size: 32), - ), - ), - DropdownButton( - value: _areaMode, - items: const [ - DropdownMenuItem(value: AreaMode.work, child: Text("作业区域")), - DropdownMenuItem( - value: AreaMode.obstacle, - child: Text("障碍区域"), - ), - ], - onChanged: (v) { - if (v != null) { - setState(() => _areaMode = v); - // 触发回调,把选中的区域模式传给外部 - widget.onAreaModeChanged?.call(v); - } - }, - ), - ], - ), - const SizedBox(height: 32), - - // 操作按钮行 - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _buildActionButton(Icons.delete_outline, '删除', true, () {}), - _buildActionButton(Icons.undo_outlined, '撤回', true, () {}), - _buildActionButton(Icons.check_outlined, '完成', true, () { - // 触发完成按钮回调,通知外部关闭面板 - widget.onComplete.call(); - }), - ], - ), - const SizedBox(height: 32), - - // 底部标签页切换 + _buildTabContent(), + // 底部标签页切换(修复背景色切换+状态更新) Container( decoration: BoxDecoration( color: Colors.grey[200], @@ -143,13 +80,19 @@ class _BottomOperationPanelState extends State { Expanded( child: GestureDetector( onTap: () { + setState(() { + _currentTab = '地块'; // 更新选中状态 + }); debugPrint('切换到地块标签'); widget.onLandTap?.call(); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 10), + // 选中地块时背景为白色,否则透明 decoration: BoxDecoration( - color: Colors.white, + color: _currentTab == '地块' + ? Colors.white + : Colors.transparent, borderRadius: BorderRadius.circular(8), ), child: const Center( @@ -167,13 +110,29 @@ class _BottomOperationPanelState extends State { Expanded( child: GestureDetector( onTap: () { + setState(() { + _currentTab = '航线'; // 更新选中状态 + }); debugPrint('切换到航线标签'); widget.onRouteTap?.call(); }, - child: const Center( - child: Text( - '航线', - style: TextStyle(color: Colors.grey, fontSize: 16), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + // 选中航线时背景为白色,否则透明 + decoration: BoxDecoration( + color: _currentTab == '航线' + ? Colors.white + : Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + child: const Center( + child: Text( + '航线', + style: TextStyle( + color: Colors.black87, + fontSize: 16, + ), + ), ), ), ), @@ -188,6 +147,182 @@ class _BottomOperationPanelState extends State { ); } + // 地块模式内容构建方法 + Widget _buildLandContent() { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 面板核心内容 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + DropdownButton( + value: _robotMode, + items: const [ + DropdownMenuItem(value: RobotMode.point, child: Text("地图打点")), + DropdownMenuItem(value: RobotMode.robot, child: Text("机器人模式")), + ], + onChanged: (v) { + if (v != null) { + setState(() => _robotMode = v); + widget.onRobotModeChanged?.call(v); + } + }, + ), + GestureDetector( + onTap: () { + debugPrint('点击了添加按钮'); + widget.onAddTap?.call(); + }, + child: Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + color: Color(0xFF00C853), + shape: BoxShape.circle, + ), + child: const Icon(Icons.add, color: Colors.white, size: 32), + ), + ), + DropdownButton( + value: _areaMode, + items: const [ + DropdownMenuItem(value: AreaMode.work, child: Text("作业区域")), + DropdownMenuItem(value: AreaMode.obstacle, child: Text("障碍区域")), + ], + onChanged: (v) { + if (v != null) { + setState(() => _areaMode = v); + widget.onAreaModeChanged?.call(v); + } + }, + ), + ], + ), + const SizedBox(height: 32), + + // 操作按钮行 + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildActionButton(Icons.delete_outline, '删除', true, () {}), + _buildActionButton(Icons.undo_outlined, '撤回', true, () {}), + _buildActionButton(Icons.check_outlined, '完成', true, () { + widget.onComplete.call(); + }), + ], + ), + const SizedBox(height: 32), + ], + ); + } + + // 标签内容切换方法(地块/航线) + Widget _buildTabContent() { + if (_currentTab == '地块') { + // 地块模式:显示机器人/区域选择+操作按钮 + return _buildLandContent(); + } else { + // 航线模式:显示作业行距+航线方向设置 + return Column( + children: [ + // 作业行距配置行 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '作业行距(米)', + style: TextStyle(fontSize: 16, color: Colors.black87), + ), + Row( + children: [ + // 减号按钮 + GestureDetector( + onTap: () { + setState(() { + if (_operationDistance > 0.1) { + _operationDistance -= 0.1; + } + }); + }, + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: Colors.grey[200], + shape: BoxShape.circle, + ), + child: const Icon( + Icons.remove, + size: 16, + color: Colors.black87, + ), + ), + ), + const SizedBox(width: 10), + // 行距数值 + Text( + '${_operationDistance.toStringAsFixed(1)}', + style: const TextStyle(fontSize: 16, color: Colors.black87), + ), + const SizedBox(width: 10), + // 加号按钮 + GestureDetector( + onTap: () { + setState(() { + _operationDistance += 0.1; + }); + }, + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: Colors.grey[200], + shape: BoxShape.circle, + ), + child: const Icon( + Icons.add, + size: 16, + color: Colors.black87, + ), + ), + ), + ], + ), + ], + ), + const SizedBox(height: 16), + // 航线方向配置行 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + '航线方向', + style: TextStyle(fontSize: 16, color: Colors.black87), + ), + // 设置按钮 + ElevatedButton( + onPressed: () { + widget.onSettingTap?.call(); + + debugPrint('点击了航线方向设置按钮'); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF00C853), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + child: const Text('设置'), + ), + ], + ), + ], + ); + } + } + // 构建操作按钮 Widget _buildActionButton( IconData icon,