路径规划点击 某项作业
This commit is contained in:
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user