路径规划点击 某项作业

This commit is contained in:
mmc
2026-02-28 09:55:14 +08:00
parent 4fd5877a3b
commit cf0c18f1ad
5 changed files with 276 additions and 158 deletions

View File

@@ -11,7 +11,9 @@ import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
import 'package:maibu_satabot_v2/core/di/injection.dart';
import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart';
import 'package:maibu_satabot_v2/core/network/protocol_decoder.dart';
import 'package:maibu_satabot_v2/features/devices/data/repositories/generate_path_repository_Impl.dart';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/get_work_record_usecase.dart';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/select_work_record_usecase.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/BottomDirectionLine.dart';
@@ -82,9 +84,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
//打点
double _workDistance = 0.0; // 作业行距(单位:米)
double _angle = 0.0; // 航线方向角(单位:度)
// =================================
// 🔥 改动1:删除硬编码的 _plotList,改为从Bloc读取
// final List<PlotData> _plotList = [...];
@override
void initState() {
@@ -111,9 +110,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_mapController.mapEventStream.listen((event) {
if (event is MapEventMove || event is MapEventMoveEnd) {
if (mounted && _mapController.camera != null) {
// 核心:直接获取原始经纬度,不做任何截断,保留最大精度
final originalCenter = _mapController.camera!.center;
// 显式创建新的 LatLng 对象,确保精度不丢失(避免引用传递导致的隐式截断)
final highPrecisionCenter = LatLng(
originalCenter.latitude, // 原始纬度(保留全部小数位)
originalCenter.longitude, // 原始经度(保留全部小数位)
@@ -238,7 +235,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
debugPrint('打点数量:${_markedPoints.length}');
debugPrint('打点坐标:$_markedPoints');
debugPrint('作业模式:$_currentWorkMode');
}
List<LatLng> _getPolygonPoints() {
@@ -390,7 +386,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
return GestureDetector(
// 核心:点击列表项触发选中逻辑
onTap: () async {
await context.read<DevicesCubit>().deleteWorkRecord(plot.plotName);
await context.read<DevicesCubit>().loadSelectedPath(plot.plotName);
final cubitState = context.read<DevicesCubit>().state;
final _loadedPlot = cubitState.pathData;
print('加载地块「${plot.plotName}」的路径数据: $_loadedPlot');
setState(() {
// 1. 选中当前地块
@@ -416,7 +415,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
),
child: Row(
children: [
// 1. 地块图片(原有代码)
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
@@ -437,7 +435,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
const SizedBox(width: 10),
// 2. 地块名称(原有代码)
Expanded(
child: Text(
plot.plotName,
@@ -447,7 +444,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
),
),
// 3. 删除按钮(修改:调用外部传入的删除方法)
IconButton(
onPressed: () => _showDeleteConfirmDialog(plot, onDelete),
icon: const Icon(
@@ -484,134 +480,226 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 注意:方法名改为 _buildWorkPanel(因为实际是作业面板,不是列表,避免混淆)
Widget _buildWorkPanel() {
// 核心修复1:空安全判断 - 无选中地块时返回空容器
if (_selectedPlot == null) {
return const SizedBox.shrink();
}
return BlocBuilder<DevicesCubit, DevicesState>(
builder: (context, state) {
print(
'当前DevicesState的pathData: ${state}',
); // 调试输出,查看pathData内容
// 无选中地块或无路径数据时返回空
if (_selectedPlot == null || state.pathData == null) {
return const SizedBox.shrink();
}
return Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: Offset(0, -2),
// 解析路径数据(根据你的实际数据结构调整)
List<Map<String, dynamic>> pathRecords = [];
if (state.pathData is List) {
pathRecords = state.pathData as List<Map<String, dynamic>>;
}
// 提取关键信息用于展示
String workArea = '0 亩';
int pointCount = 0;
String workMode = '未知';
if (pathRecords.isNotEmpty) {
final record = pathRecords.first;
workArea = record['workingArea']?.toString() ?? '0 亩';
pointCount = record['plotPoints'] != null
? (record['plotPoints'] as List).length
: 0;
workMode = record['workMode'] ?? '未知';
}
return Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: Offset(0, -2),
),
],
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min, // 关键:只占用内容高度,不挤压其他组件
children: [
// 1. 选中地块信息行
Row(
crossAxisAlignment: CrossAxisAlignment.center, // 修复:垂直居中对齐
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 地块图片
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
_selectedPlot!.imageUrl,
width: 80,
height: 80,
fit: BoxFit.cover,
// 核心修复2:图片加载错误占位优化
errorBuilder: (context, error, stackTrace) {
return Container(
// 1. 地块基础信息
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
_selectedPlot!.imageUrl,
width: 80,
height: 80,
color: Colors.grey[200],
child: const Icon(
Icons.image_outlined,
color: Colors.grey,
size: 32,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 80,
height: 80,
color: Colors.grey[200],
child: const Icon(
Icons.image_outlined,
color: Colors.grey,
size: 32,
),
);
},
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_selectedPlot!.plotName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
// 新增:展示路径数据关键信息
Text(
'作业面积:$workArea',
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
Text(
'打点数量:$pointCount 个',
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
Text(
'作业模式:$workMode',
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
],
),
),
IconButton(
onPressed: () {
setState(() {
_isWorkPanelOpen = false;
_selectedPlot = null;
});
},
icon: const Icon(
Icons.close,
color: Colors.grey,
size: 20,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(
minWidth: 24,
minHeight: 24,
),
),
],
),
const SizedBox(height: 16),
// 2. 错误提示(如果加载失败)
if (state.errorMessage != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.red[50],
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.red[200]!),
),
child: Row(
children: [
const Icon(
Icons.error_outline,
color: Colors.redAccent,
size: 16,
),
);
},
),
),
const SizedBox(width: 16),
// 地块名称(修复:占满剩余空间)
Expanded(
child: Text(
_selectedPlot!.plotName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
const SizedBox(width: 8),
Expanded(
child: Text(
state.errorMessage!,
style: const TextStyle(
fontSize: 14,
color: Colors.redAccent,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
// 关闭按钮(修复:减少点击区域,避免误触)
Padding(
padding: const EdgeInsets.only(left: 8),
child: IconButton(
onPressed: () {
setState(() {
_isWorkPanelOpen = false;
_selectedPlot = null; // 清空选中状态
});
},
icon: const Icon(Icons.close, color: Colors.grey, size: 20),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(
minWidth: 24,
minHeight: 24,
// 3. 开始作业按钮(加载中禁用)
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: state.isLoading || pathRecords.isEmpty
? null
: () {
// 作业逻辑:使用加载的pathData
debugPrint(
'开始作业:${_selectedPlot!.plotName},路径数据:$pathRecords',
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00C853),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 2,
disabledBackgroundColor: Colors.grey[300],
disabledForegroundColor: Colors.grey[600],
),
child: state.isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
'开始作业',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: () {
// 核心修复3:再次空安全校验(双重保险)
if (_selectedPlot != null) {
//_startWork(_selectedPlot!);
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00C853), // 绿色主题
foregroundColor: Colors.white, // 文字颜色(Flutter 3.0+ 推荐)
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 2,
shadowColor: Colors.black12,
padding: const EdgeInsets.symmetric(
vertical: 0,
horizontal: 16,
),
minimumSize: const Size(double.infinity, 48),
),
child: const Text(
'开始作业',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
height: 1.0,
),
),
),
),
],
),
),
),
);
},
);
}