@@ -11,8 +11,7 @@ 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/models/device_work_area_param_model.dart '
as work_area_model ;
import ' package:maibu_satabot_v2/features/devices/data/models/device_work_area_param_model.dart ' as work_area_model ;
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 ' ;
@@ -20,7 +19,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/components /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 ' ;
@@ -38,12 +37,7 @@ class PlotData {
final String imageUrl ; // 图片URL( 本地/assets/网络都可)
final String ? jsonData ; // 原始数据的JSON字符串( 可选, 便于调试或后续使用)
PlotData ( {
required this . id ,
required this . plotName ,
required this . imageUrl ,
this . jsonData ,
} ) ;
PlotData ( { required this . id , required this . plotName , required this . imageUrl , this . jsonData } ) ;
}
class PlotDataPath {
@@ -135,9 +129,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} ) ;
// 可选: 打印验证精度( 保留15位小数)
debugPrint (
' 地图中心更新(高精度):纬度= ${ _mapCenter . latitude . toStringAsFixed ( 15 ) } , 经度= ${ _mapCenter . longitude . toStringAsFixed ( 15 ) } ' ,
) ;
debugPrint ( ' 地图中心更新(高精度):纬度= ${ _mapCenter . latitude . toStringAsFixed ( 15 ) } , 经度= ${ _mapCenter . longitude . toStringAsFixed ( 15 ) } ' ) ;
}
}
} ) ;
@@ -236,12 +228,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
onConfirm: ( plotName ) {
_savePlotData ( plotName ) ;
ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar (
content: Text ( ' 地块「 $ plotName 」保存成功! ' ) ,
backgroundColor: Colors . green ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( ' 地块「 $ plotName 」保存成功! ' ) , backgroundColor: Colors . green ) ) ;
} ,
) ;
}
@@ -274,9 +261,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} else {
debugPrint ( ' 无打点可撤回 ' ) ;
// 可选:提示用户
ScaffoldMessenger . of (
context ,
) . showSnackBar ( const SnackBar ( content: Text ( ' 暂无打点可撤回 ' ) ) ) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 暂无打点可撤回 ' ) ) ) ;
}
}
@@ -288,14 +273,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} ) ;
debugPrint ( ' 删除成功,已清空所有打点和轨迹 ' ) ;
// 可选:提示用户
ScaffoldMessenger . of (
context ,
) . showSnackBar ( const SnackBar ( content: Text ( ' 已清空所有打点和轨迹 ' ) ) ) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 已清空所有打点和轨迹 ' ) ) ) ;
} else {
debugPrint ( ' 无打点可删除 ' ) ;
ScaffoldMessenger . of (
context ,
) . showSnackBar ( const SnackBar ( content: Text ( ' 暂无打点可删除 ' ) ) ) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 暂无打点可删除 ' ) ) ) ;
}
}
@@ -310,27 +291,19 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
if ( permission = = LocationPermission . denied ) {
permission = await Geolocator . requestPermission ( ) ;
}
if ( permission ! = LocationPermission . whileInUse & &
permission ! = LocationPermission . always ) {
if ( permission ! = LocationPermission . whileInUse & & permission ! = LocationPermission . always ) {
return ;
}
// 1️ ⃣ 首次强制获取定位
final position = await Geolocator . getCurrentPosition (
desiredAccuracy: LocationAccuracy . bestForNavigation ,
) ;
final position = await Geolocator . getCurrentPosition ( desiredAccuracy: LocationAccuracy . bestForNavigation ) ;
final gcj = wgs84ToGcj02 ( position . latitude , position . longitude ) ;
_updateLocation ( gcj , moveMap: true ) ;
// 2️ ⃣ 实时监听(不再强制移动地图)
_positionSub =
Geolocator . getPositionStream (
locationSettings: const LocationSettings (
accuracy: LocationAccuracy . bestForNavigation ,
distanceFilter: 2 ,
) ,
) . listen ( ( pos ) {
_positionSub = Geolocator . getPositionStream ( locationSettings: const LocationSettings ( accuracy: LocationAccuracy . bestForNavigation , distanceFilter: 2 ) )
. listen ( ( pos ) {
final gcj = wgs84ToGcj02 ( pos . latitude , pos . longitude ) ;
_updateLocation ( gcj , moveMap: false ) ;
} ) ;
@@ -397,25 +370,15 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 刷新成功提示
if ( mounted ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
const SnackBar (
content: Text ( ' 页面已刷新完成 ' ) ,
backgroundColor: Colors . green ,
duration: Duration ( seconds: 2 ) ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 页面已刷新完成 ' ) , backgroundColor: Colors . green , duration: Duration ( seconds: 2 ) ) ) ;
}
} catch ( e ) {
// 错误处理
debugPrint ( ' 刷新失败: $ e ' ) ;
if ( mounted ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar (
content: Text ( ' 刷新失败: ${ e . toString ( ) . substring ( 0 , 50 ) } ' ) ,
backgroundColor: Colors . red ,
duration: Duration ( seconds: 3 ) ,
) ,
) ;
ScaffoldMessenger . of (
context ,
) . showSnackBar ( SnackBar ( content: Text ( ' 刷新失败: ${ e . toString ( ) . substring ( 0 , 50 ) } ' ) , backgroundColor: Colors . red , duration: Duration ( seconds: 3 ) ) ) ;
}
} finally {
// 标记刷新结束
@@ -452,9 +415,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
setState ( ( ) {
_markedPoints . add ( _mapCenter ) ; // 在地图中心打点
} ) ;
debugPrint (
' $ _markedPoints ,新增打点:第 ${ _markedPoints . length } 个点,经纬度: ${ _mapCenter . latitude . toStringAsFixed ( 20 ) } , ${ _mapCenter . longitude . toStringAsFixed ( 20 ) } ' ,
) ;
debugPrint ( ' $ _markedPoints ,新增打点:第 ${ _markedPoints . length } 个点,经纬度: ${ _mapCenter . latitude . toStringAsFixed ( 20 ) } , ${ _mapCenter . longitude . toStringAsFixed ( 20 ) } ' ) ;
}
// ========== 新增:返回上一级页面的方法 ==========
@@ -474,19 +435,13 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 模拟添加完成点
void _addCompletePoint ( ) {
final newPoint = LatLng (
39.9042 + ( DateTime . now ( ) . millisecond / 10000 ) ,
116.4074 + ( DateTime . now ( ) . millisecond / 10000 ) ,
) ;
final newPoint = LatLng ( 39.9042 + ( DateTime . now ( ) . millisecond / 10000 ) , 116.4074 + ( DateTime . now ( ) . millisecond / 10000 ) ) ;
_traceManager . upsert ( newPoint , TPAction . ADD ) ;
}
// 模拟更新当前点
void _updateCurrentPoint ( ) {
final newPoint = LatLng (
39.9042 + ( DateTime . now ( ) . millisecond / 10000 ) ,
116.4074 + ( DateTime . now ( ) . millisecond / 10000 ) ,
) ;
final newPoint = LatLng ( 39.9042 + ( DateTime . now ( ) . millisecond / 10000 ) , 116.4074 + ( DateTime . now ( ) . millisecond / 10000 ) ) ;
_traceManager . upsert ( newPoint ) ;
}
@@ -510,23 +465,17 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 2. 立即解析并更新状态(在事件回调中,不在 build 中)
if ( _loadedPlot is List ) {
final List < dynamic > rawList = _loadedPlot as List < dynamic > ;
final pathRecords = rawList
. where ( ( item ) = > item is Map < String , dynamic > )
. cast < Map < String , dynamic > > ( )
. toList ( ) ;
final pathRecords = rawList . where ( ( item ) = > item is Map < String , dynamic > ) . cast < Map < String , dynamic > > ( ) . toList ( ) ;
if ( pathRecords . isNotEmpty ) {
final selectedPlotPath = PlotDataPath ( jsonData: pathRecords . first ) ;
try {
final dynamic nestedJsonRaw =
selectedPlotPath . jsonData [ ' jsonData ' ] ;
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
? " 弓字模式 "
: " 自定义模式 " ;
workMode = parsedJson [ ' planModel ' ] = = WorkMode . bow . value ? " 弓字模式 " : " 自定义模式 " ;
if ( parsedJson is Map < String , dynamic > ) {
final Map < String , dynamic > nestedJson = parsedJson ;
final dynamic rawPath = nestedJson [ ' path ' ] ;
@@ -606,9 +555,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
color: Colors . white ,
borderRadius: BorderRadius . circular ( 8 ) ,
border: Border . all (
color: _selectedPlot ? . id = = plot . id
? Colors . blue
: Colors . grey [ 100 ] ! ,
color: _selectedPlot ? . id = = plot . id ? Colors . blue : Colors . grey [ 100 ] ! ,
width: _selectedPlot ? . id = = plot . id ? 2 : 1 , // 选中项高亮边框
) ,
) ,
@@ -645,11 +592,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
IconButton (
onPressed: ( ) = > _showDeleteConfirmDialog ( plot , onDelete ) ,
icon: const Icon (
Icons . delete_outline ,
color: Colors . redAccent ,
size: 20 ,
) ,
icon: const Icon ( Icons . delete_outline , color: Colors . redAccent , size: 20 ) ,
padding: EdgeInsets . zero ,
constraints: const BoxConstraints ( ) ,
) ,
@@ -669,9 +612,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
onConfirm: ( ) {
onDelete ( plot ) ;
if ( context . mounted ) {
ScaffoldMessenger . of (
context ,
) . showSnackBar ( SnackBar ( content: Text ( ' 已删除地块「 ${ plot . plotName } 」 ' ) ) ) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( ' 已删除地块「 ${ plot . plotName } 」 ' ) ) ) ;
}
} ,
) ;
@@ -694,13 +635,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
decoration: const BoxDecoration (
color: Colors . white ,
borderRadius: BorderRadius . vertical ( top: Radius . circular ( 16 ) ) ,
boxShadow: [
BoxShadow (
color: Colors . black12 ,
blurRadius: 10 ,
offset: Offset ( 0 , - 2 ) ,
) ,
] ,
boxShadow: [ BoxShadow ( color: Colors . black12 , blurRadius: 10 , offset: Offset ( 0 , - 2 ) ) ] ,
) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
@@ -721,11 +656,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
width: 80 ,
height: 80 ,
color: Colors . grey [ 200 ] ,
child: const Icon (
Icons . image_outlined ,
color: Colors . grey ,
size: 32 ,
) ,
child: const Icon ( Icons . image_outlined , color: Colors . grey , size: 32 ) ,
) ;
} ,
) ,
@@ -737,22 +668,12 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
children: [
Text (
_selectedPlot ! . plotName ,
style: const TextStyle (
fontSize: 18 ,
fontWeight: FontWeight . bold ,
color: Colors . black87 ,
) ,
style: const TextStyle ( fontSize: 18 , fontWeight: FontWeight . bold , color: Colors . black87 ) ,
maxLines: 2 ,
overflow: TextOverflow . ellipsis ,
) ,
const SizedBox ( height: 4 ) ,
Text (
' 作业模式: $ workMode ' ,
style: const TextStyle (
fontSize: 14 ,
color: Colors . grey ,
) ,
) ,
Text ( ' 作业模式: $ workMode ' , style: const TextStyle ( fontSize: 14 , color: Colors . grey ) ) ,
] ,
) ,
) ,
@@ -765,16 +686,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
gcjOuterPoints = [ ] ;
} ) ;
} ,
icon: const Icon (
Icons . close ,
color: Colors . grey ,
size: 20 ,
) ,
icon: const Icon ( Icons . close , color: Colors . grey , size: 20 ) ,
padding: EdgeInsets . zero ,
constraints: const BoxConstraints (
minWidth: 24 ,
minHeight: 24 ,
) ,
constraints: const BoxConstraints ( minWidth: 24 , minHeight: 24 ) ,
) ,
] ,
) ,
@@ -784,10 +698,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 2. 错误提示(如果加载失败)
if ( state . errorMessage ! = null )
Container (
padding: const EdgeInsets . symmetric (
horizontal: 12 ,
vertical: 8 ,
) ,
padding: const EdgeInsets . symmetric ( horizontal: 12 , vertical: 8 ) ,
margin: const EdgeInsets . only ( bottom: 12 ) ,
decoration: BoxDecoration (
color: Colors . red [ 50 ] ,
@@ -796,19 +707,12 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
) ,
child: Row (
children: [
const Icon (
Icons . error_outline ,
color: Colors . redAccent ,
size: 16 ,
) ,
const Icon ( Icons . error_outline , color: Colors . redAccent , size: 16 ) ,
const SizedBox ( width: 8 ) ,
Expanded (
child: Text (
state . errorMessage ! ,
style: const TextStyle (
fontSize: 14 ,
color: Colors . redAccent ,
) ,
style: const TextStyle ( fontSize: 14 , color: Colors . redAccent ) ,
maxLines: 2 ,
overflow: TextOverflow . ellipsis ,
) ,
@@ -826,36 +730,19 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
? null
: ( ) {
// 作业逻辑: 使用加载的pathData
debugPrint (
' 开始作业: ${ _selectedPlot ! . plotName } ,路径数据: $ gcjPathPoints ' ,
) ;
debugPrint ( ' 开始作业: ${ _selectedPlot ! . plotName } ,路径数据: $ gcjPathPoints ' ) ;
} ,
style: ElevatedButton . styleFrom (
backgroundColor: const Color ( 0xFF00C853 ) ,
foregroundColor: Colors . white ,
shape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 8 ) ,
) ,
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: 14 ,
fontWeight: FontWeight . bold ,
) ,
) ,
? const SizedBox ( width: 20 , height: 20 , child: CircularProgressIndicator ( strokeWidth: 2 , color: Colors . white ) )
: const Text ( ' 开始作业 ' , style: TextStyle ( fontSize: 14 , fontWeight: FontWeight . bold ) ) ,
) ,
) ,
] ,
@@ -867,14 +754,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
// 🔥 改动4: 新增方法 - 将Bloc的workRecords转换为PlotData列表
List < PlotData > _convertWorkRecordsToPlotData (
List < Map < String , dynamic > > records ,
) {
List < PlotData > _convertWorkRecordsToPlotData ( List < Map < String , dynamic > > records ) {
return records . map ( ( record ) {
return PlotData (
id:
record [ ' id ' ] ? . toString ( ) ? ?
DateTime . now ( ) . microsecondsSinceEpoch . toString ( ) , // 唯一ID
id: record [ ' id ' ] ? . toString ( ) ? ? DateTime . now ( ) . microsecondsSinceEpoch . toString ( ) , // 唯一ID
plotName: record [ ' workName ' ] ? ? ' 未命名地块 ' , // 地块名称(从接口字段取)
imageUrl: record [ ' imgUrl ' ] ? ? ' ' , // 图片URL( 从接口字段取, 无则为空)
jsonData: record [ ' jsonData ' ] , // 原始数据的JSON字符串( 可选, 便于调试或后续使用)
@@ -896,8 +779,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
FlutterMap (
mapController: _mapController ,
options: MapOptions (
initialCenter:
_currentLatLng ? ? const LatLng ( 39.9042 , 116.4074 ) ,
initialCenter: _currentLatLng ? ? const LatLng ( 39.9042 , 116.4074 ) ,
initialZoom: 20 ,
maxZoom: 22 ,
// 禁止地图点击事件(避免和中心标冲突)
@@ -944,21 +826,13 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
PolylineLayer (
polylines: [
for ( int i = 0 ; i < _markedPoints . length - 1 ; i + + )
Polyline (
points: [ _markedPoints [ i ] , _markedPoints [ i + 1 ] ] ,
color: Colors . orange . withOpacity ( 0.5 ) ,
strokeWidth: 1.5 ,
) ,
Polyline ( points: [ _markedPoints [ i ] , _markedPoints [ i + 1 ] ] , color: Colors . orange . withOpacity ( 0.5 ) , strokeWidth: 1.5 ) ,
] ,
) ,
if ( _markedPoints . isNotEmpty )
PolylineLayer (
polylines: [
Polyline (
points: [ _mapCenter , _markedPoints . last ] ,
color: Colors . blue . withOpacity ( 0.5 ) ,
strokeWidth: 1.5 ,
) ,
Polyline ( points: [ _mapCenter , _markedPoints . last ] , color: Colors . blue . withOpacity ( 0.5 ) , strokeWidth: 1.5 ) ,
] ,
) ,
@@ -972,15 +846,12 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
height: 40 ,
child: CustomPaint (
size: const Size ( 40 , 40 ) ,
painter: HeadingMarkerPainter (
headingAngle: _headingAngle ,
) ,
painter: HeadingMarkerPainter ( headingAngle: _headingAngle ) ,
) ,
) ,
] ,
) ,
if ( _currentWorkMode = = WorkMode . bow & &
_markedPoints . isNotEmpty )
if ( _currentWorkMode = = WorkMode . bow & & _markedPoints . isNotEmpty )
PolygonLayer (
polygons: [
Polygon (
@@ -1014,18 +885,12 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
decoration: const BoxDecoration (
color: Color ( 0xFF00C853 ) , // 绿色主题色
shape: BoxShape . circle ,
boxShadow: [
BoxShadow ( color: Colors . black12 , blurRadius: 2 ) ,
] ,
boxShadow: [ BoxShadow ( color: Colors . black12 , blurRadius: 2 ) ] ,
) ,
child: Center (
child: Text (
' $ index ' ,
style: const TextStyle (
color: Colors . white ,
fontSize: 10 ,
fontWeight: FontWeight . bold ,
) ,
style: const TextStyle ( color: Colors . white , fontSize: 10 , fontWeight: FontWeight . bold ) ,
) ,
) ,
) ,
@@ -1044,12 +909,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
child: const Center (
child: Column (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
CircularProgressIndicator (
color: Colors . white ,
strokeWidth: 3 ,
) ,
] ,
children: [ CircularProgressIndicator ( color: Colors . white , strokeWidth: 3 ) ] ,
) ,
) ,
) ,
@@ -1069,13 +929,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
decoration: BoxDecoration (
color: Colors . white . withOpacity ( 0.8 ) ,
borderRadius: BorderRadius . circular ( 20 ) ,
boxShadow: [
BoxShadow (
color: Colors . black12 ,
blurRadius: 3 ,
offset: const Offset ( 0 , 2 ) ,
) ,
] ,
boxShadow: [ BoxShadow ( color: Colors . black12 , blurRadius: 3 , offset: const Offset ( 0 , 2 ) ) ] ,
) ,
// 核心:用 Stack 实现点击高亮层 + 图标层
child: Stack (
@@ -1091,11 +945,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
) ,
) ,
// 2. 图标层(绝对居中)
const Icon (
Icons . arrow_back_ios ,
color: Colors . black87 ,
size: 20 ,
) ,
const Icon ( Icons . arrow_back_ios , color: Colors . black87 , size: 20 ) ,
] ,
) ,
) ,
@@ -1131,13 +981,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
decoration: BoxDecoration (
color: const Color ( 0xFF00C853 ) ,
shape: BoxShape . circle ,
boxShadow: [
BoxShadow (
color: Colors . black12 ,
blurRadius: 4 ,
offset: const Offset ( 0 , 2 ) ,
) ,
] ,
boxShadow: [ BoxShadow ( color: Colors . black12 , blurRadius: 4 , offset: const Offset ( 0 , 2 ) ) ] ,
) ,
child: IconButton (
onPressed: ( ) {
@@ -1165,8 +1009,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} ,
onListBox: ( bool isOpen ) {
// 🔥 改动5: 加载作业记录( 原有逻辑保留)
final userId =
context . read < AppUserCubit > ( ) . state . user ? . userId ? ? " " ;
final userId = context . read < AppUserCubit > ( ) . state . user ? . userId ? ? " " ;
print ( ' 加载作业记录, 当前用户ID: $ userId ' ) ;
context . read < DevicesCubit > ( ) . loadWorkRecords ( userId ) ;
@@ -1200,30 +1043,15 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 1. 校验必要条件( 至少需要3个打点才能形成闭合区域)
if ( _markedPoints . isEmpty ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
const SnackBar (
content: Text ( ' 请先添加作业区域打点! ' ) ,
backgroundColor: Colors . red ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 请先添加作业区域打点! ' ) , backgroundColor: Colors . red ) ) ;
return ;
}
if ( _markedPoints . length < 3 ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
const SnackBar (
content: Text ( ' 作业区域至少需要3个打点才能生成路径! ' ) ,
backgroundColor: Colors . red ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 作业区域至少需要3个打点才能生成路径! ' ) , backgroundColor: Colors . red ) ) ;
return ;
}
if ( _currentWorkMode = = null ) {
ScaffoldMessenger . of ( context ) . showSnackBar (
const SnackBar (
content: Text ( ' 请先选择作业模式! ' ) ,
backgroundColor: Colors . red ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 请先选择作业模式! ' ) , backgroundColor: Colors . red ) ) ;
return ;
}
@@ -1231,14 +1059,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 2. 转换页面数据为 generatePath 所需的参数格式
// 2.1 参考点(取第一个打点,转换为 WGS84)
final firstPoint = _markedPoints . first ;
final wgs84FirstPoint = gcj02ToWgs84 (
firstPoint . latitude ,
firstPoint . longitude ,
) ;
final referencePoint = work_area_model . ReferencePoint (
lat: wgs84FirstPoint . latitude ,
lon: wgs84FirstPoint . longitude ,
) ;
final wgs84FirstPoint = gcj02ToWgs84 ( firstPoint . latitude , firstPoint . longitude ) ;
final referencePoint = work_area_model . ReferencePoint ( lat: wgs84FirstPoint . latitude , lon: wgs84FirstPoint . longitude ) ;
// 2.2 航向角(转换为整型,-1表示最优航向则取-1)
final heading = _angle = = - 1 ? - 1 : _angle . toInt ( ) ;
@@ -1248,23 +1070,15 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 将 LatLng 打点转换为 Position 列表(先转 WGS84)
position: _markedPoints . map ( ( latLng ) {
// 关键:每个点都转换为 WGS84 坐标系
final wgs84Point = gcj02ToWgs84 (
latLng . latitude ,
latLng . longitude ,
) ;
return work_area_model . Position (
lat: wgs84Point . latitude ,
lon: wgs84Point . longitude ,
) ;
final wgs84Point = gcj02ToWgs84 ( latLng . latitude , latLng . longitude ) ;
return work_area_model . Position ( lat: wgs84Point . latitude , lon: wgs84Point . longitude ) ;
} ) . toList ( ) ,
sideWidth: _workDistance ,
) ;
final holes = < work_area_model . HoleBoundary > [ ] ;
final workType = _currentWorkMode = = WorkMode . bow
? 0
: 2 ; // 注意: 这里弓字模式是0, 自定义是2( 确认和后端一致)
final workType = _currentWorkMode = = WorkMode . bow ? 0 : 2 ; // 注意: 这里弓字模式是0, 自定义是2( 确认和后端一致)
// 3. 调用 Cubit 的 generatePath 方法生成路径
await context . read < DevicesCubit > ( ) . generatePath (
@@ -1279,44 +1093,24 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
final cubitState = context . read < DevicesCubit > ( ) . state ;
if ( cubitState . errorMessage ! = null ) {
// 生成失败
ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar (
content: Text ( ' 路径生成失败: ${ cubitState . errorMessage } ' ) ,
backgroundColor: Colors . red ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( ' 路径生成失败: ${ cubitState . errorMessage } ' ) , backgroundColor: Colors . red ) ) ;
} else if ( cubitState . generatedPath ! = null ) {
// 生成成功
ScaffoldMessenger . of ( context ) . showSnackBar (
const SnackBar (
content: Text ( ' 路径生成成功! ' ) ,
backgroundColor: Colors . green ,
) ,
) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( ' 路径生成成功! ' ) , backgroundColor: Colors . green ) ) ;
debugPrint ( ' 生成的路径数据: ${ cubitState . generatedPath } ' ) ;
// 可选: 将生成的路径转换为地图可显示的坐标( WGS84 → GCJ02)
if ( cubitState . generatedPath is List ) {
setState ( ( ) {
// 关键:生成的路径是 WGS84, 需转回 GCJ02 才能在高德地图正确显示
gcjPathPoints = ( cubitState . generatedPath as List )
. map ( ( point ) {
gcjPathPoints = ( cubitState . generatedPath as List ) . map ( ( point ) {
// 先取 WGS84 坐标
double wgs84Lat =
safeToDouble ( point [ ' lat ' ] ) ? ? 0.0 ;
double wgs84Lon =
safeToDouble ( point [ ' lon ' ] ) ? ? 0.0 ;
double wgs84Lat = safeToDouble ( point [ ' lat ' ] ) ? ? 0.0 ;
double wgs84Lon = safeToDouble ( point [ ' lon ' ] ) ? ? 0.0 ;
// 转换为 GCJ02
final gcjPoint = wgs84ToGcj02 (
wgs84Lat ,
wgs84Lon ,
) ;
return LatLng (
gcjPoint . latitude ,
gcjPoint . longitude ,
) ;
} )
. toList ( ) ;
final gcjPoint = wgs84ToGcj02 ( wgs84Lat , wgs84Lon ) ;
return LatLng ( gcjPoint . latitude , gcjPoint . longitude ) ;
} ) . toList ( ) ;
// 外边界也转回 GCJ02 显示(可选,保持和地图坐标系一致)
gcjOuterPoints = _markedPoints ;
} ) ;
@@ -1327,14 +1121,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} catch ( e ) {
// 异常处理
debugPrint ( ' 路径生成异常: $ e ' ) ;
ScaffoldMessenger . of ( context ) . showSnackBar (
SnackBar (
content: Text (
' 路径生成异常: ${ e . toString ( ) . substring ( 0 , 50 ) } ' ,
) ,
backgroundColor: Colors . red ,
) ,
) ;
ScaffoldMessenger . of (
context ,
) . showSnackBar ( SnackBar ( content: Text ( ' 路径生成异常: ${ e . toString ( ) . substring ( 0 , 50 ) } ' ) , backgroundColor: Colors . red ) ) ;
}
// 5. 关闭操作面板
@@ -1356,9 +1145,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} ,
onDistanceTap: ( distance ) {
_workDistance = distance ;
debugPrint (
' 外部处理作业行距距离设置,当前距离: ${ distance . toStringAsFixed ( 1 ) } 米 ' ,
) ;
debugPrint ( ' 外部处理作业行距距离设置,当前距离: ${ distance . toStringAsFixed ( 1 ) } 米 ' ) ;
} ,
onSettingTap: ( ) {
setState ( ( ) {
@@ -1385,12 +1172,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
initialOptimalHeading: true ,
initialDirection: 0.0 ,
onConfirm: ( result ) {
debugPrint (
' 最优航向: ${ result [ ' optimalHeading ' ] } ,角度: ${ result [ ' direction ' ] } ' ,
) ;
_angle = result [ ' optimalHeading ' ] = = true
? - 1
: result [ ' direction ' ] ;
debugPrint ( ' 最优航向: ${ result [ ' optimalHeading ' ] } ,角度: ${ result [ ' direction ' ] } ' ) ;
_angle = result [ ' optimalHeading ' ] = = true ? - 1 : result [ ' direction ' ] ;
debugPrint ( ' 外部处理航线方向设置,当前角度: ${ _angle } ' ) ;
setState ( ( ) = > _directionBoxOpen = false ) ;
} ,
@@ -1404,9 +1187,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
if ( _isListBoxOpen )
BlocBuilder < DevicesCubit , DevicesState > (
builder: ( context , state ) {
final plotList = _convertWorkRecordsToPlotData (
state . workRecords ? ? [ ] ,
) ;
final plotList = _convertWorkRecordsToPlotData ( state . workRecords ? ? [ ] ) ;
return Positioned . fill (
child: Column (
@@ -1419,48 +1200,27 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_isListBoxOpen = false ;
} ) ;
} ,
child: Container (
color: Colors . black . withOpacity ( 0.3 ) ,
) ,
child: Container ( color: Colors . black . withOpacity ( 0.3 ) ) ,
) ,
) ,
Container (
width: double . infinity ,
decoration: const BoxDecoration (
color: Colors . white ,
borderRadius: BorderRadius . vertical (
top: Radius . circular ( 16 ) ,
) ,
boxShadow: [
BoxShadow (
color: Colors . black12 ,
blurRadius: 10 ,
offset: Offset ( 0 , - 2 ) ,
) ,
] ,
) ,
constraints: const BoxConstraints (
maxHeight: 600 ,
minHeight: 200 ,
borderRadius: BorderRadius . vertical ( top: Radius . circular ( 16 ) ) ,
boxShadow: [ BoxShadow ( color: Colors . black12 , blurRadius: 10 , offset: Offset ( 0 , - 2 ) ) ] ,
) ,
constraints: const BoxConstraints ( maxHeight: 600 , minHeight: 200 ) ,
child: Column (
children: [
Container (
padding: const EdgeInsets . symmetric (
horizontal: 16 ,
vertical: 12 ,
) ,
padding: const EdgeInsets . symmetric ( horizontal: 16 , vertical: 12 ) ,
child: Row (
mainAxisAlignment:
MainAxisAlignment . spaceBetween ,
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Text (
' 地块列表(共 ${ plotList . length } 条) ' ,
style: const TextStyle (
fontSize: 18 ,
fontWeight: FontWeight . bold ,
color: Colors . black87 ,
) ,
style: const TextStyle ( fontSize: 18 , fontWeight: FontWeight . bold , color: Colors . black87 ) ,
) ,
IconButton (
onPressed: ( ) {
@@ -1468,11 +1228,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_isListBoxOpen = false ;
} ) ;
} ,
icon: const Icon (
Icons . close ,
color: Colors . grey ,
size: 20 ,
) ,
icon: const Icon ( Icons . close , color: Colors . grey , size: 20 ) ,
) ,
] ,
) ,
@@ -1482,51 +1238,24 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
child: plotList . isEmpty
? const Center (
child: Column (
mainAxisAlignment:
MainAxisAlignment . center ,
mainAxisAlignment: MainAxisAlignment . center ,
children: [
Icon (
Icons . inbox_outlined ,
color: Colors . grey ,
size: 48 ,
) ,
Icon ( Icons . inbox_outlined , color: Colors . grey , size: 48 ) ,
SizedBox ( height: 16 ) ,
Text (
' 暂无地块数据 ' ,
style: TextStyle (
color: Colors . grey ,
fontSize: 16 ,
) ,
) ,
Text ( ' 暂无地块数据 ' , style: TextStyle ( color: Colors . grey , fontSize: 16 ) ) ,
] ,
) ,
)
: ListView . builder (
padding: const EdgeInsets . symmetric (
vertical: 8 ,
) ,
padding: const EdgeInsets . symmetric ( vertical: 8 ) ,
itemCount: plotList . length ,
itemBuilder: ( context , index ) {
final plot = plotList [ index ] ;
// 传入删除回调( 调用Bloc的删除方法)
return _buildPlotListItem ( plot , (
deletedPlot ,
) async {
await context
. read < DevicesCubit > ( )
. deleteWorkRecord (
deletedPlot . plotName ,
) ;
final userId =
context
. read < AppUserCubit > ( )
. state
. user
? . userId ? ?
" " ;
await context
. read < DevicesCubit > ( )
. loadWorkRecords ( userId ) ;
return _buildPlotListItem ( plot , ( deletedPlot ) async {
await context . read < DevicesCubit > ( ) . deleteWorkRecord ( deletedPlot . plotName ) ;
final userId = context . read < AppUserCubit > ( ) . state . user ? . userId ? ? " " ;
await context . read < DevicesCubit > ( ) . loadWorkRecords ( userId ) ;
setState ( ( ) { } ) ;
} ) ;
@@ -1602,13 +1331,7 @@ bool _outOfChina(double lat, double lon) {
}
double _transformLat ( double x , double y ) {
double ret =
- 100.0 +
2.0 * x +
3.0 * y +
0.2 * y * y +
0.1 * x * y +
0.2 * sqrt ( x . abs ( ) ) ;
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 ;
@@ -1616,11 +1339,9 @@ double _transformLat(double x, double y) {
}
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 ( ) ) ;
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 ;
ret + = ( 150.0 * sin ( x / 12.0 * _pi ) + 300.0 * sin ( x / 30.0 * _pi ) ) * 2.0 / 3.0 ;
return ret ;
}