@fleet-frontend/mower-maps 0.1.0-beta.1 → 0.1.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +218 -111
- package/dist/index.js +218 -111
- package/dist/processor/MapDataProcessor.d.ts +2 -1
- package/dist/processor/MapDataProcessor.d.ts.map +1 -1
- package/dist/render/AntennaManager.d.ts +3 -1
- package/dist/render/AntennaManager.d.ts.map +1 -1
- package/dist/render/BoundaryLabelsManager.d.ts +4 -1
- package/dist/render/BoundaryLabelsManager.d.ts.map +1 -1
- package/dist/render/MowerMapOverlay.d.ts +4 -1
- package/dist/render/MowerMapOverlay.d.ts.map +1 -1
- package/dist/render/MowerMapRenderer.d.ts.map +1 -1
- package/dist/render/SvgMapView.d.ts +2 -1
- package/dist/render/SvgMapView.d.ts.map +1 -1
- package/dist/render/layers/BaseLayer.d.ts +1 -0
- package/dist/render/layers/BaseLayer.d.ts.map +1 -1
- package/dist/render/layers/BoundaryBorderLayer.d.ts.map +1 -1
- package/dist/render/layers/BoundaryLayer.d.ts.map +1 -1
- package/dist/render/layers/ChannelLayer.d.ts +2 -1
- package/dist/render/layers/ChannelLayer.d.ts.map +1 -1
- package/dist/render/layers/DrawLayer.d.ts +2 -1
- package/dist/render/layers/DrawLayer.d.ts.map +1 -1
- package/dist/render/layers/ObstacleLayer.d.ts.map +1 -1
- package/dist/render/layers/PathLayer.d.ts +2 -1
- package/dist/render/layers/PathLayer.d.ts.map +1 -1
- package/dist/render/layers/VisionOffLayer.d.ts.map +1 -1
- package/dist/store/useCurrentMowingDataStore.d.ts.map +1 -1
- package/dist/store/usePartitionDataStore.d.ts.map +1 -1
- package/dist/types/renderer.d.ts +2 -0
- package/dist/types/renderer.d.ts.map +1 -1
- package/dist/types/store.d.ts +15 -15
- package/dist/types/store.d.ts.map +1 -1
- package/dist/utils/handleRealTime.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -138,7 +138,7 @@ const SVG_MAP_VIEW_ID = 'fleet-maps-svg-map-view';
|
|
|
138
138
|
* 使用真正的矢量SVG渲染替代Canvas位图渲染
|
|
139
139
|
*/
|
|
140
140
|
class SvgMapView {
|
|
141
|
-
constructor(containerElement, width = 800, height = 600) {
|
|
141
|
+
constructor(sn, containerElement, width = 800, height = 600) {
|
|
142
142
|
// 图层管理
|
|
143
143
|
this.layers = [];
|
|
144
144
|
// 简单变换参数
|
|
@@ -151,6 +151,7 @@ class SvgMapView {
|
|
|
151
151
|
this.isDragging = false;
|
|
152
152
|
this.lastMouseX = 0;
|
|
153
153
|
this.lastMouseY = 0;
|
|
154
|
+
this.sn = sn;
|
|
154
155
|
this.container = containerElement;
|
|
155
156
|
// 创建SVG元素
|
|
156
157
|
this.svg = this.createSVGElement();
|
|
@@ -188,7 +189,7 @@ class SvgMapView {
|
|
|
188
189
|
this.svg.setAttribute('shape-rendering', 'geometricPrecision');
|
|
189
190
|
this.svg.setAttribute('text-rendering', 'geometricPrecision');
|
|
190
191
|
this.svg.setAttribute('image-rendering', 'optimizeQuality');
|
|
191
|
-
this.svg.setAttribute('id', SVG_MAP_VIEW_ID);
|
|
192
|
+
this.svg.setAttribute('id', `${this.sn}-${SVG_MAP_VIEW_ID}`);
|
|
192
193
|
}
|
|
193
194
|
/**
|
|
194
195
|
* 创建SVG组元素
|
|
@@ -643,6 +644,7 @@ class BaseLayer {
|
|
|
643
644
|
this.needsRedraw = true;
|
|
644
645
|
this.mapView = null;
|
|
645
646
|
this.type = '';
|
|
647
|
+
this.sn = '';
|
|
646
648
|
this.level = 0; // 中等层级
|
|
647
649
|
}
|
|
648
650
|
// ==================== 基础属性方法 ====================
|
|
@@ -782,11 +784,17 @@ const usePartitionDataStore = create((set, get) => ({
|
|
|
782
784
|
addSubBoundaryBorder: (key, element) => set((state) => ({
|
|
783
785
|
subBoundaryBorder: {
|
|
784
786
|
...state.subBoundaryBorder,
|
|
785
|
-
[key]: element,
|
|
787
|
+
[key]: { ...element },
|
|
786
788
|
},
|
|
787
789
|
})),
|
|
788
790
|
// 清空所有数据
|
|
789
|
-
|
|
791
|
+
deleteSubBoundaryBorder: (key) => set((state) => {
|
|
792
|
+
const newSubBoundaryBorder = { ...state.subBoundaryBorder };
|
|
793
|
+
delete newSubBoundaryBorder[key];
|
|
794
|
+
return {
|
|
795
|
+
subBoundaryBorder: newSubBoundaryBorder,
|
|
796
|
+
};
|
|
797
|
+
}),
|
|
790
798
|
// 障碍物
|
|
791
799
|
obstacles: {},
|
|
792
800
|
addObstacles: (key, element) => set((state) => ({
|
|
@@ -795,7 +803,13 @@ const usePartitionDataStore = create((set, get) => ({
|
|
|
795
803
|
[key]: element,
|
|
796
804
|
},
|
|
797
805
|
})),
|
|
798
|
-
|
|
806
|
+
deleteObstacles: (key) => set((state) => {
|
|
807
|
+
const newObstacles = { ...state.obstacles };
|
|
808
|
+
delete newObstacles[key];
|
|
809
|
+
return {
|
|
810
|
+
obstacles: newObstacles,
|
|
811
|
+
};
|
|
812
|
+
}),
|
|
799
813
|
// svg数据
|
|
800
814
|
svgElements: {},
|
|
801
815
|
addSvgElements: (key, element) => set((state) => ({
|
|
@@ -804,7 +818,13 @@ const usePartitionDataStore = create((set, get) => ({
|
|
|
804
818
|
[key]: element,
|
|
805
819
|
},
|
|
806
820
|
})),
|
|
807
|
-
|
|
821
|
+
deleteSvgElements: (key) => set((state) => {
|
|
822
|
+
const newSvgElements = { ...state.svgElements };
|
|
823
|
+
delete newSvgElements[key];
|
|
824
|
+
return {
|
|
825
|
+
svgElements: newSvgElements,
|
|
826
|
+
};
|
|
827
|
+
}),
|
|
808
828
|
}));
|
|
809
829
|
|
|
810
830
|
/**
|
|
@@ -3290,11 +3310,12 @@ var index = {
|
|
|
3290
3310
|
* 专门处理路径元素的渲染
|
|
3291
3311
|
*/
|
|
3292
3312
|
class ChannelLayer extends BaseLayer {
|
|
3293
|
-
constructor() {
|
|
3313
|
+
constructor(sn) {
|
|
3294
3314
|
super();
|
|
3295
3315
|
this.level = 1;
|
|
3296
3316
|
this.scale = 1;
|
|
3297
3317
|
this.type = LAYER_DEFAULT_TYPE.CHANNEL;
|
|
3318
|
+
this.sn = sn;
|
|
3298
3319
|
}
|
|
3299
3320
|
/**
|
|
3300
3321
|
* 获取元素
|
|
@@ -3327,7 +3348,8 @@ class ChannelLayer extends BaseLayer {
|
|
|
3327
3348
|
createExclusionClipPathDefinitions(svgGroup) {
|
|
3328
3349
|
// 获取所有分区边界数据
|
|
3329
3350
|
const subBoundaryBorder = usePartitionDataStore.getState().subBoundaryBorder;
|
|
3330
|
-
|
|
3351
|
+
const currentSubBoundaryBorder = subBoundaryBorder[this.sn];
|
|
3352
|
+
if (!currentSubBoundaryBorder || Object.keys(currentSubBoundaryBorder).length === 0) {
|
|
3331
3353
|
return {};
|
|
3332
3354
|
}
|
|
3333
3355
|
const clipPathIdsMap = {};
|
|
@@ -3340,7 +3362,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3340
3362
|
// 计算包含所有分区和通道的边界框
|
|
3341
3363
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3342
3364
|
// 1. 先计算所有分区的边界,如果能拿到边界的svg的大小,就使用这个如果拿不到,就根据分区去计算
|
|
3343
|
-
const svg = document.getElementById(SVG_MAP_VIEW_ID);
|
|
3365
|
+
const svg = document.getElementById(`${this.sn}-${SVG_MAP_VIEW_ID}`);
|
|
3344
3366
|
if (svg && svg instanceof SVGSVGElement && svg.viewBox) {
|
|
3345
3367
|
const viewBox = svg.viewBox.baseVal;
|
|
3346
3368
|
minX = viewBox.x;
|
|
@@ -3349,8 +3371,8 @@ class ChannelLayer extends BaseLayer {
|
|
|
3349
3371
|
maxY = viewBox.y + viewBox.height;
|
|
3350
3372
|
}
|
|
3351
3373
|
else {
|
|
3352
|
-
for (const partitionId in
|
|
3353
|
-
const boundaryData =
|
|
3374
|
+
for (const partitionId in currentSubBoundaryBorder) {
|
|
3375
|
+
const boundaryData = currentSubBoundaryBorder[partitionId];
|
|
3354
3376
|
if (boundaryData && boundaryData.coordinates && boundaryData.coordinates.length > 0) {
|
|
3355
3377
|
for (const coord of boundaryData.coordinates) {
|
|
3356
3378
|
minX = Math.min(minX, coord[0]);
|
|
@@ -3362,7 +3384,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3362
3384
|
}
|
|
3363
3385
|
}
|
|
3364
3386
|
// 整理出clipPath路径
|
|
3365
|
-
const clipPathId =
|
|
3387
|
+
const clipPathId = `${this.sn}-channel-exclude-all`;
|
|
3366
3388
|
// 创建 clipPath
|
|
3367
3389
|
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
3368
3390
|
clipPath.setAttribute('id', clipPathId);
|
|
@@ -3371,7 +3393,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3371
3393
|
// 外轮廓(顺时针)
|
|
3372
3394
|
let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
|
|
3373
3395
|
// 获取所有需要挖空的分区路径
|
|
3374
|
-
const partitionPaths = this.mergeOverlappingPartitions(
|
|
3396
|
+
const partitionPaths = this.mergeOverlappingPartitions(currentSubBoundaryBorder || {});
|
|
3375
3397
|
// 将所有分区路径追加到clipPath中(逆时针,形成挖空)
|
|
3376
3398
|
partitionPaths.forEach((partitionCoords) => {
|
|
3377
3399
|
if (partitionCoords.length >= 3) {
|
|
@@ -3497,12 +3519,12 @@ class ChannelLayer extends BaseLayer {
|
|
|
3497
3519
|
/**
|
|
3498
3520
|
* 智能合并重叠的分区,返回所有需要挖空的路径
|
|
3499
3521
|
*/
|
|
3500
|
-
mergeOverlappingPartitions(
|
|
3522
|
+
mergeOverlappingPartitions(currentSubBoundaryBorder) {
|
|
3501
3523
|
try {
|
|
3502
3524
|
// 将所有分区转换为polygon-clipping格式
|
|
3503
3525
|
const polygons = [];
|
|
3504
3526
|
const partitionIds = [];
|
|
3505
|
-
Object.entries(
|
|
3527
|
+
Object.entries(currentSubBoundaryBorder || {}).forEach(([partitionId, boundaryData]) => {
|
|
3506
3528
|
if (boundaryData?.coordinates && boundaryData.coordinates.length >= 3) {
|
|
3507
3529
|
// 确保坐标格式正确(去掉第三个z坐标)
|
|
3508
3530
|
const coords = boundaryData.coordinates.map((coord) => [coord[0], coord[1]]);
|
|
@@ -3587,7 +3609,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3587
3609
|
catch (error) {
|
|
3588
3610
|
console.error('polygon-clipping union failed:', error);
|
|
3589
3611
|
// 如果合并失败,返回原始分区
|
|
3590
|
-
return Object.values(
|
|
3612
|
+
return Object.values(currentSubBoundaryBorder)
|
|
3591
3613
|
.filter((boundaryData) => boundaryData?.coordinates && boundaryData.coordinates.length >= 3)
|
|
3592
3614
|
.map((boundaryData) => boundaryData.coordinates.map((coord) => [coord[0], coord[1]]));
|
|
3593
3615
|
}
|
|
@@ -3599,19 +3621,21 @@ class ChannelLayer extends BaseLayer {
|
|
|
3599
3621
|
* 专门处理路径元素的渲染
|
|
3600
3622
|
*/
|
|
3601
3623
|
class PathLayer extends BaseLayer {
|
|
3602
|
-
constructor() {
|
|
3624
|
+
constructor(sn) {
|
|
3603
3625
|
super();
|
|
3604
3626
|
this.level = 3;
|
|
3605
3627
|
this.scale = 1;
|
|
3606
3628
|
this.lineScale = 1;
|
|
3607
3629
|
this.boundaryPaths = {};
|
|
3608
3630
|
this.type = LAYER_DEFAULT_TYPE.PATH;
|
|
3631
|
+
this.sn = sn;
|
|
3609
3632
|
}
|
|
3610
3633
|
/**
|
|
3611
3634
|
* 为每个分区创建独立的 clipPath
|
|
3612
3635
|
*/
|
|
3613
3636
|
createPartitionClipPaths(svgGroup) {
|
|
3614
3637
|
const { subBoundaryBorder, obstacles, svgElements } = usePartitionDataStore.getState();
|
|
3638
|
+
const currentSubBoundaryBorder = subBoundaryBorder[this.sn];
|
|
3615
3639
|
// 确保 defs 元素存在
|
|
3616
3640
|
let defs = svgGroup.querySelector('defs');
|
|
3617
3641
|
if (!defs) {
|
|
@@ -3620,9 +3644,9 @@ class PathLayer extends BaseLayer {
|
|
|
3620
3644
|
}
|
|
3621
3645
|
const clipPathIds = {};
|
|
3622
3646
|
// 为每个分区创建独立的 clipPath
|
|
3623
|
-
Object.keys(
|
|
3624
|
-
const partitionData =
|
|
3625
|
-
const clipPathId = `clip-partition-${partitionId}`;
|
|
3647
|
+
Object.keys(currentSubBoundaryBorder || {}).forEach((partitionId) => {
|
|
3648
|
+
const partitionData = currentSubBoundaryBorder[partitionId];
|
|
3649
|
+
const clipPathId = `clip-partition-${this.sn}-${partitionId}`;
|
|
3626
3650
|
// 如果已存在,先移除
|
|
3627
3651
|
const existing = defs.querySelector(`#${clipPathId}`);
|
|
3628
3652
|
if (existing)
|
|
@@ -3639,7 +3663,8 @@ class PathLayer extends BaseLayer {
|
|
|
3639
3663
|
d += ' Z ';
|
|
3640
3664
|
}
|
|
3641
3665
|
// 2. 所有禁区(逆时针)- 禁区影响所有分区
|
|
3642
|
-
|
|
3666
|
+
const currentObstacles = obstacles[this.sn];
|
|
3667
|
+
Object.values(currentObstacles || {}).forEach((item) => {
|
|
3643
3668
|
const obstacleCoords = item.coordinates;
|
|
3644
3669
|
if (obstacleCoords.length >= 3) {
|
|
3645
3670
|
d += `M ${obstacleCoords[obstacleCoords.length - 1][0]} ${obstacleCoords[obstacleCoords.length - 1][1]}`;
|
|
@@ -3650,7 +3675,8 @@ class PathLayer extends BaseLayer {
|
|
|
3650
3675
|
}
|
|
3651
3676
|
});
|
|
3652
3677
|
// 3. 所有 svgElements(逆时针)- SVG元素影响所有分区
|
|
3653
|
-
|
|
3678
|
+
const currentSvgElements = svgElements[this.sn];
|
|
3679
|
+
Object.values(currentSvgElements || {}).forEach((svgPath) => {
|
|
3654
3680
|
const svgPathString = svgPath?.metadata?.svg;
|
|
3655
3681
|
if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
|
|
3656
3682
|
// 处理转义字符
|
|
@@ -3745,7 +3771,7 @@ class PathLayer extends BaseLayer {
|
|
|
3745
3771
|
lineColor = style.lineColor || '#000000';
|
|
3746
3772
|
}
|
|
3747
3773
|
// 按分区+类型+样式分组存储
|
|
3748
|
-
const groupKey = `${id}-${lineColor}-${style.lineWidth || 1}`;
|
|
3774
|
+
const groupKey = `${id}-${lineColor}-${style.lineWidth || 1}-${this.sn}`;
|
|
3749
3775
|
if (!partitionTypeGroups.has(groupKey)) {
|
|
3750
3776
|
partitionTypeGroups.set(groupKey, {
|
|
3751
3777
|
pathData: [],
|
|
@@ -3897,9 +3923,7 @@ class BoundaryLayer extends BaseLayer {
|
|
|
3897
3923
|
createBoundaryFill(svgGroup, coordinates, style) {
|
|
3898
3924
|
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
3899
3925
|
// 构建点集合,使用整数坐标(只取x,y坐标)
|
|
3900
|
-
const points = coordinates
|
|
3901
|
-
.map((coord) => `${coord[0]},${coord[1]}`)
|
|
3902
|
-
.join(' ');
|
|
3926
|
+
const points = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(' ');
|
|
3903
3927
|
const fillColor = style.fillColor;
|
|
3904
3928
|
polygon.setAttribute('points', points);
|
|
3905
3929
|
polygon.setAttribute('fill', fillColor);
|
|
@@ -3943,9 +3967,7 @@ class ObstacleLayer extends BaseLayer {
|
|
|
3943
3967
|
return;
|
|
3944
3968
|
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
3945
3969
|
// 构建点集合,使用整数坐标
|
|
3946
|
-
const points = coordinates
|
|
3947
|
-
.map((coord) => `${coord[0]},${coord[1]}`)
|
|
3948
|
-
.join(' ');
|
|
3970
|
+
const points = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(' ');
|
|
3949
3971
|
polygon.setAttribute('points', points);
|
|
3950
3972
|
polygon.setAttribute('fill', style.fillColor || 'rgba(220, 53, 69, 0.2)');
|
|
3951
3973
|
polygon.setAttribute('stroke', style.lineColor || '#dc3545');
|
|
@@ -4225,9 +4247,7 @@ class VisionOffLayer extends BaseLayer {
|
|
|
4225
4247
|
return;
|
|
4226
4248
|
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
4227
4249
|
// 构建点集合,使用整数坐标
|
|
4228
|
-
const points = coordinates
|
|
4229
|
-
.map((coord) => `${coord[0]},${coord[1]}`)
|
|
4230
|
-
.join(' ');
|
|
4250
|
+
const points = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(' ');
|
|
4231
4251
|
const fillColor = style.fillColor || 'rgba(0, 255, 0, 0.4)';
|
|
4232
4252
|
polygon.setAttribute('points', points);
|
|
4233
4253
|
polygon.setAttribute('fill', fillColor);
|
|
@@ -5008,7 +5028,10 @@ currentMowingPartition, }) => {
|
|
|
5008
5028
|
// 关于location的分区,需要通过地图数据,结合射线法,判断当前的点在哪个分区里
|
|
5009
5029
|
let mowingStatus = isMowing || false;
|
|
5010
5030
|
let newCurrentMowingPartitionId = currentMowingPartition;
|
|
5011
|
-
console.info(
|
|
5031
|
+
// console.info(
|
|
5032
|
+
// 'handleMultipleRealTimeData==newCurrentMowingPartitionId=================',
|
|
5033
|
+
// newCurrentMowingPartitionId
|
|
5034
|
+
// );
|
|
5012
5035
|
realTimeData.forEach((item) => {
|
|
5013
5036
|
// 这里需要区分,是割草进度还是割草轨迹
|
|
5014
5037
|
if (item.type === REAL_TIME_DATA_TYPE.LOCATION) {
|
|
@@ -7922,7 +7945,7 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
7922
7945
|
*/
|
|
7923
7946
|
setMowingBoundarys(mowingBoundarys) {
|
|
7924
7947
|
if (!mowingBoundarys) {
|
|
7925
|
-
this.mowingBoundarys = this.elements?.map(item => item?.originalData?.id);
|
|
7948
|
+
this.mowingBoundarys = this.elements?.map((item) => item?.originalData?.id);
|
|
7926
7949
|
}
|
|
7927
7950
|
else {
|
|
7928
7951
|
this.mowingBoundarys = mowingBoundarys;
|
|
@@ -8242,14 +8265,15 @@ class AntennaLayer extends BaseLayer {
|
|
|
8242
8265
|
* 使用具体的图层类来处理不同类型的渲染
|
|
8243
8266
|
*/
|
|
8244
8267
|
class DrawLayer extends BaseLayer {
|
|
8245
|
-
constructor() {
|
|
8268
|
+
constructor(sn) {
|
|
8246
8269
|
super();
|
|
8270
|
+
this.sn = sn;
|
|
8247
8271
|
// 初始化各种具体图层
|
|
8248
8272
|
this.antennaLayer = new AntennaLayer();
|
|
8249
8273
|
// 通道图层
|
|
8250
|
-
this.channelLayer = new ChannelLayer();
|
|
8274
|
+
this.channelLayer = new ChannelLayer(sn);
|
|
8251
8275
|
// 路径图层
|
|
8252
|
-
this.pathLayer = new PathLayer();
|
|
8276
|
+
this.pathLayer = new PathLayer(sn);
|
|
8253
8277
|
// 边界图层
|
|
8254
8278
|
this.boundaryLayer = new BoundaryLayer();
|
|
8255
8279
|
// 边界边框图层
|
|
@@ -8672,8 +8696,9 @@ class MapDataProcessor {
|
|
|
8672
8696
|
/**
|
|
8673
8697
|
* 处理地图数据,返回绘制图层
|
|
8674
8698
|
*/
|
|
8675
|
-
static processMapData(mapData, mapConfig) {
|
|
8699
|
+
static processMapData(sn, mapData, mapConfig) {
|
|
8676
8700
|
this.mapConfig = mapConfig;
|
|
8701
|
+
this.sn = sn;
|
|
8677
8702
|
// 收集所有地图元素
|
|
8678
8703
|
const allElements = [];
|
|
8679
8704
|
// 处理子地图中的元素
|
|
@@ -8753,6 +8778,9 @@ class MapDataProcessor {
|
|
|
8753
8778
|
*/
|
|
8754
8779
|
static processMapElements(elements) {
|
|
8755
8780
|
const result = [];
|
|
8781
|
+
let subBoundaryBorderObj = {};
|
|
8782
|
+
let obstaclesObj = {};
|
|
8783
|
+
let svgElementsObj = {};
|
|
8756
8784
|
for (const element of elements) {
|
|
8757
8785
|
try {
|
|
8758
8786
|
// 检查必要的基础字段
|
|
@@ -8771,10 +8799,18 @@ class MapDataProcessor {
|
|
|
8771
8799
|
result.push(boundaryBorderElement);
|
|
8772
8800
|
// 将边界边框存储到 store 中,以分区ID为key
|
|
8773
8801
|
if (element.id) {
|
|
8802
|
+
subBoundaryBorderObj = {
|
|
8803
|
+
...subBoundaryBorderObj,
|
|
8804
|
+
[`${element.id.toString()}`]: {
|
|
8805
|
+
...boundaryBorderElement,
|
|
8806
|
+
},
|
|
8807
|
+
};
|
|
8774
8808
|
const { addSubBoundaryBorder } = usePartitionDataStore.getState();
|
|
8775
|
-
addSubBoundaryBorder(
|
|
8776
|
-
|
|
8777
|
-
}
|
|
8809
|
+
addSubBoundaryBorder(`${this.sn}`, subBoundaryBorderObj);
|
|
8810
|
+
// const { addSubBoundaryBorder } = usePartitionDataStore.getState();
|
|
8811
|
+
// addSubBoundaryBorder(`${element.id.toString()}`, {
|
|
8812
|
+
// ...boundaryBorderElement,
|
|
8813
|
+
// });
|
|
8778
8814
|
}
|
|
8779
8815
|
}
|
|
8780
8816
|
}
|
|
@@ -8790,10 +8826,15 @@ class MapDataProcessor {
|
|
|
8790
8826
|
const obstacleElement = ObstacleDataBuilder.fromMapElement(mapElement, this.mapConfig.obstacle);
|
|
8791
8827
|
if (obstacleElement) {
|
|
8792
8828
|
result.push(obstacleElement);
|
|
8793
|
-
|
|
8794
|
-
addObstacles(`obstacle-${obstacleElement.originalData.id}`, {
|
|
8829
|
+
obstaclesObj[`obstacle-${obstacleElement.originalData.id}`] = {
|
|
8795
8830
|
...obstacleElement,
|
|
8796
|
-
}
|
|
8831
|
+
};
|
|
8832
|
+
const { addObstacles } = usePartitionDataStore.getState();
|
|
8833
|
+
addObstacles(`${this.sn}`, obstaclesObj);
|
|
8834
|
+
// const { addObstacles } = usePartitionDataStore.getState();
|
|
8835
|
+
// addObstacles(`obstacle-${obstacleElement.originalData.id}`, {
|
|
8836
|
+
// ...obstacleElement,
|
|
8837
|
+
// });
|
|
8797
8838
|
}
|
|
8798
8839
|
}
|
|
8799
8840
|
catch (error) {
|
|
@@ -8843,7 +8884,6 @@ class MapDataProcessor {
|
|
|
8843
8884
|
break;
|
|
8844
8885
|
}
|
|
8845
8886
|
case 'TIME_LIMIT_OBSTACLE': {
|
|
8846
|
-
console.info('TIME_LIMIT_OBSTACLE', element);
|
|
8847
8887
|
try {
|
|
8848
8888
|
// 如果有SVG数据,直接创建SVG绘制元素
|
|
8849
8889
|
if ('svg' in element &&
|
|
@@ -8858,10 +8898,15 @@ class MapDataProcessor {
|
|
|
8858
8898
|
const svgElement = SvgElementDataBuilder.fromMapElement(mapElement, this.mapConfig.doodle);
|
|
8859
8899
|
if (svgElement) {
|
|
8860
8900
|
result.push(svgElement);
|
|
8861
|
-
|
|
8862
|
-
addSvgElements(`time-limit-obstacle-${svgElement.originalData.id}`, {
|
|
8901
|
+
svgElementsObj[`time-limit-obstacle-${svgElement.originalData.id}`] = {
|
|
8863
8902
|
...svgElement,
|
|
8864
|
-
}
|
|
8903
|
+
};
|
|
8904
|
+
const { addSvgElements } = usePartitionDataStore.getState();
|
|
8905
|
+
addSvgElements(`${this.sn}`, svgElementsObj);
|
|
8906
|
+
// const { addSvgElements } = usePartitionDataStore.getState();
|
|
8907
|
+
// addSvgElements(`time-limit-obstacle-${svgElement.originalData.id}`, {
|
|
8908
|
+
// ...svgElement,
|
|
8909
|
+
// });
|
|
8865
8910
|
}
|
|
8866
8911
|
}
|
|
8867
8912
|
// 如果有points数据,按传统方式绘制
|
|
@@ -8873,10 +8918,15 @@ class MapDataProcessor {
|
|
|
8873
8918
|
const polygonElement = ObstacleDataBuilder.createTimeLimitObstacle(mapElement, this.mapConfig.obstacle);
|
|
8874
8919
|
if (polygonElement) {
|
|
8875
8920
|
result.push(polygonElement);
|
|
8876
|
-
|
|
8877
|
-
addObstacles(`time-limit-obstacle-${polygonElement.originalData.id}`, {
|
|
8921
|
+
svgElementsObj[`time-limit-obstacle-${polygonElement.originalData.id}`] = {
|
|
8878
8922
|
...polygonElement,
|
|
8879
|
-
}
|
|
8923
|
+
};
|
|
8924
|
+
const { addSvgElements } = usePartitionDataStore.getState();
|
|
8925
|
+
addSvgElements(`${this.sn}`, svgElementsObj);
|
|
8926
|
+
// const { addObstacles } = usePartitionDataStore.getState();
|
|
8927
|
+
// addObstacles(`time-limit-obstacle-${polygonElement.originalData.id}`, {
|
|
8928
|
+
// ...polygonElement,
|
|
8929
|
+
// });
|
|
8880
8930
|
}
|
|
8881
8931
|
}
|
|
8882
8932
|
}
|
|
@@ -9036,7 +9086,7 @@ class PathDataProcessor {
|
|
|
9036
9086
|
* 专门处理边界标签的创建、定位和管理
|
|
9037
9087
|
*/
|
|
9038
9088
|
class BoundaryLabelsManager {
|
|
9039
|
-
constructor(svgView, boundaryData, { unitType, language }) {
|
|
9089
|
+
constructor(svgView, boundaryData, { unitType, language, onlyRead }) {
|
|
9040
9090
|
this.container = null;
|
|
9041
9091
|
this.overlayDiv = null;
|
|
9042
9092
|
this.globalClickHandler = null;
|
|
@@ -9049,6 +9099,7 @@ class BoundaryLabelsManager {
|
|
|
9049
9099
|
this.initializeContainer();
|
|
9050
9100
|
this.unitType = unitType;
|
|
9051
9101
|
this.language = language;
|
|
9102
|
+
this.onlyRead = onlyRead;
|
|
9052
9103
|
}
|
|
9053
9104
|
/**
|
|
9054
9105
|
* 初始化容器
|
|
@@ -9123,7 +9174,12 @@ class BoundaryLabelsManager {
|
|
|
9123
9174
|
labelDiv.style.whiteSpace = 'nowrap';
|
|
9124
9175
|
labelDiv.style.maxWidth = '220px';
|
|
9125
9176
|
labelDiv.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
9126
|
-
|
|
9177
|
+
if (this.onlyRead) {
|
|
9178
|
+
labelDiv.style.pointerEvents = 'none';
|
|
9179
|
+
}
|
|
9180
|
+
else {
|
|
9181
|
+
labelDiv.style.pointerEvents = 'auto';
|
|
9182
|
+
}
|
|
9127
9183
|
labelDiv.style.boxShadow = '0 2px 8px rgba(0,0,0,0.4)';
|
|
9128
9184
|
labelDiv.style.cursor = 'pointer';
|
|
9129
9185
|
labelDiv.style.transition = 'background-color 0.2s ease';
|
|
@@ -9209,6 +9265,9 @@ class BoundaryLabelsManager {
|
|
|
9209
9265
|
* 展开标签
|
|
9210
9266
|
*/
|
|
9211
9267
|
expandLabel(boundaryId) {
|
|
9268
|
+
if (this.onlyRead) {
|
|
9269
|
+
return;
|
|
9270
|
+
}
|
|
9212
9271
|
const labelDiv = this.getLabelElement(boundaryId);
|
|
9213
9272
|
if (!labelDiv)
|
|
9214
9273
|
return;
|
|
@@ -9217,7 +9276,6 @@ class BoundaryLabelsManager {
|
|
|
9217
9276
|
return;
|
|
9218
9277
|
// 关闭其他展开的标签
|
|
9219
9278
|
this.collapseOtherLabels(boundaryId);
|
|
9220
|
-
// 展开当前标签
|
|
9221
9279
|
extendedContent.style.display = 'block';
|
|
9222
9280
|
this.currentExpandedBoundaryId = boundaryId;
|
|
9223
9281
|
}
|
|
@@ -9417,6 +9475,16 @@ class BoundaryLabelsManager {
|
|
|
9417
9475
|
const pixelY = relativeY * divHeight;
|
|
9418
9476
|
return { x: pixelX, y: pixelY };
|
|
9419
9477
|
}
|
|
9478
|
+
updateReadOnlyMode(onlyRead) {
|
|
9479
|
+
this.onlyRead = onlyRead;
|
|
9480
|
+
if (!this.container)
|
|
9481
|
+
return;
|
|
9482
|
+
const allLabels = this.container.querySelectorAll('.boundary-label');
|
|
9483
|
+
allLabels.forEach((label) => {
|
|
9484
|
+
const labelElement = label;
|
|
9485
|
+
labelElement.style.pointerEvents = onlyRead ? 'none' : 'auto';
|
|
9486
|
+
});
|
|
9487
|
+
}
|
|
9420
9488
|
/**
|
|
9421
9489
|
* 更新边界数据
|
|
9422
9490
|
*/
|
|
@@ -9725,7 +9793,7 @@ ChargingPileManager.Z_INDEX = {
|
|
|
9725
9793
|
* 专门处理天线元素的创建、定位和管理
|
|
9726
9794
|
*/
|
|
9727
9795
|
class AntennaManager {
|
|
9728
|
-
constructor(svgView) {
|
|
9796
|
+
constructor(svgView, onlyRead) {
|
|
9729
9797
|
this.antennaElements = [];
|
|
9730
9798
|
this.container = null;
|
|
9731
9799
|
this.overlayDiv = null;
|
|
@@ -9738,6 +9806,7 @@ class AntennaManager {
|
|
|
9738
9806
|
// 旋转角度
|
|
9739
9807
|
this.rotation = 0;
|
|
9740
9808
|
this.svgView = svgView;
|
|
9809
|
+
this.onlyRead = onlyRead;
|
|
9741
9810
|
this.initializeContainer();
|
|
9742
9811
|
this.setupGlobalClickHandler();
|
|
9743
9812
|
}
|
|
@@ -9810,7 +9879,12 @@ class AntennaManager {
|
|
|
9810
9879
|
antennaContainer.className = 'antenna-container-item';
|
|
9811
9880
|
antennaContainer.style.position = 'absolute';
|
|
9812
9881
|
antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
9813
|
-
|
|
9882
|
+
if (this.onlyRead) {
|
|
9883
|
+
antennaContainer.style.pointerEvents = 'none';
|
|
9884
|
+
}
|
|
9885
|
+
else {
|
|
9886
|
+
antennaContainer.style.pointerEvents = 'auto';
|
|
9887
|
+
}
|
|
9814
9888
|
antennaContainer.style.zIndex = AntennaManager.Z_INDEX.DEFAULT.toString();
|
|
9815
9889
|
antennaContainer.setAttribute('data-antenna-id', antennaData.type.toString());
|
|
9816
9890
|
// 创建天线图标
|
|
@@ -9907,6 +9981,9 @@ class AntennaManager {
|
|
|
9907
9981
|
const tooltip = antennaContainer.querySelector('.antenna-tooltip');
|
|
9908
9982
|
if (!tooltip)
|
|
9909
9983
|
return;
|
|
9984
|
+
if (this.onlyRead) {
|
|
9985
|
+
return;
|
|
9986
|
+
}
|
|
9910
9987
|
// 展开当前tooltip
|
|
9911
9988
|
tooltip.style.display = 'block';
|
|
9912
9989
|
const extendedContent = tooltip.querySelector('.antenna-tooltip-extended');
|
|
@@ -9952,6 +10029,16 @@ class AntennaManager {
|
|
|
9952
10029
|
}
|
|
9953
10030
|
});
|
|
9954
10031
|
}
|
|
10032
|
+
updateReadOnlyMode(onlyRead) {
|
|
10033
|
+
this.onlyRead = onlyRead;
|
|
10034
|
+
if (!this.container)
|
|
10035
|
+
return;
|
|
10036
|
+
const allAntennas = this.container.querySelectorAll('.antenna-container-item');
|
|
10037
|
+
allAntennas.forEach((antenna) => {
|
|
10038
|
+
const antennaElement = antenna;
|
|
10039
|
+
antennaElement.style.pointerEvents = onlyRead ? 'none' : 'auto';
|
|
10040
|
+
});
|
|
10041
|
+
}
|
|
9955
10042
|
/**
|
|
9956
10043
|
* 添加主天线
|
|
9957
10044
|
*/
|
|
@@ -10593,24 +10680,29 @@ function isMobileDevice() {
|
|
|
10593
10680
|
// 记录割草状态,状态变更的时候,变量不触发重新渲染
|
|
10594
10681
|
const useCurrentMowingDataStore = create((set) => ({
|
|
10595
10682
|
// 当前进度数据返回的割草状态是否为在割草
|
|
10596
|
-
processStateIsMowing:
|
|
10597
|
-
updateProcessStateIsMowing: (isMowing) => set({ processStateIsMowing: isMowing }),
|
|
10598
|
-
resetProcessStateIsMowing: () => set({ processStateIsMowing: false }),
|
|
10683
|
+
processStateIsMowing: {},
|
|
10684
|
+
updateProcessStateIsMowing: (sn, isMowing) => set((state) => ({ processStateIsMowing: { ...state.processStateIsMowing, [sn]: isMowing } })),
|
|
10685
|
+
resetProcessStateIsMowing: (key) => set((state) => ({ processStateIsMowing: { ...state.processStateIsMowing, [key]: false } })),
|
|
10599
10686
|
// 当前割草的分区id
|
|
10600
|
-
currentMowingPartitionId:
|
|
10601
|
-
updateCurrentMowingPartitionId: (partitionId) => set(
|
|
10602
|
-
|
|
10687
|
+
currentMowingPartitionId: {},
|
|
10688
|
+
updateCurrentMowingPartitionId: (sn, partitionId) => set((state) => ({
|
|
10689
|
+
currentMowingPartitionId: { ...state.currentMowingPartitionId, [sn]: partitionId },
|
|
10690
|
+
})),
|
|
10691
|
+
resetCurrentMowingPartitionId: (key) => set((state) => ({
|
|
10692
|
+
currentMowingPartitionId: { ...state.currentMowingPartitionId, [key]: '' },
|
|
10693
|
+
})),
|
|
10603
10694
|
}));
|
|
10604
10695
|
|
|
10605
10696
|
// Google Maps 叠加层类 - 带编辑功能
|
|
10606
10697
|
class MowerMapOverlay {
|
|
10607
|
-
constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, unitType = UnitsType.Imperial, language = 'en', mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
|
|
10698
|
+
constructor(sn, bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, unitType = UnitsType.Imperial, language = 'en', onlyRead = false, mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
|
|
10608
10699
|
this.div = null;
|
|
10609
10700
|
this.svgMapView = null;
|
|
10610
10701
|
this.offscreenContainer = null;
|
|
10611
10702
|
this.overlayView = null;
|
|
10612
10703
|
this.defaultTransform = { x: 0, y: 0, rotation: 0 };
|
|
10613
10704
|
this.hasEdger = false;
|
|
10705
|
+
this.sn = '';
|
|
10614
10706
|
// boundary数据
|
|
10615
10707
|
this.boundaryData = [];
|
|
10616
10708
|
// 边界标签管理器
|
|
@@ -10648,6 +10740,7 @@ class MowerMapOverlay {
|
|
|
10648
10740
|
leading: true,
|
|
10649
10741
|
trailing: false,
|
|
10650
10742
|
});
|
|
10743
|
+
this.sn = sn;
|
|
10651
10744
|
this.bounds = bounds;
|
|
10652
10745
|
this.mapData = mapData;
|
|
10653
10746
|
this.partitionBoundary = partitionBoundary;
|
|
@@ -10655,6 +10748,7 @@ class MowerMapOverlay {
|
|
|
10655
10748
|
this.isEditMode = isEditMode;
|
|
10656
10749
|
this.unitType = unitType;
|
|
10657
10750
|
this.language = language;
|
|
10751
|
+
this.onlyRead = onlyRead;
|
|
10658
10752
|
this.mapConfig = mapConfig;
|
|
10659
10753
|
this.antennaConfig = antennaConfig;
|
|
10660
10754
|
this.onMapLoad = onMapLoad;
|
|
@@ -10685,6 +10779,7 @@ class MowerMapOverlay {
|
|
|
10685
10779
|
this.overlayView.hide = this.hide.bind(this);
|
|
10686
10780
|
// 添加编辑模式相关方法
|
|
10687
10781
|
this.overlayView.setEditMode = this.setEditMode.bind(this);
|
|
10782
|
+
this.overlayView.setReadOnlyMode = this.setReadOnlyMode.bind(this);
|
|
10688
10783
|
this.overlayView.getEditData = this.getEditData.bind(this);
|
|
10689
10784
|
this.overlayView.handleSave = this.handleSave.bind(this);
|
|
10690
10785
|
this.overlayView.setCustomIcons = this.setCustomIcons.bind(this);
|
|
@@ -10821,6 +10916,7 @@ class MowerMapOverlay {
|
|
|
10821
10916
|
this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData, {
|
|
10822
10917
|
unitType: this.unitType,
|
|
10823
10918
|
language: this.language,
|
|
10919
|
+
onlyRead: this.onlyRead,
|
|
10824
10920
|
});
|
|
10825
10921
|
// 设置叠加层div引用
|
|
10826
10922
|
this.boundaryLabelsManager.setOverlayDiv(this.div);
|
|
@@ -10851,7 +10947,7 @@ class MowerMapOverlay {
|
|
|
10851
10947
|
if (!this.div || !this.svgMapView)
|
|
10852
10948
|
return;
|
|
10853
10949
|
// 创建天线管理器
|
|
10854
|
-
this.antennaManager = new AntennaManager(this.svgMapView);
|
|
10950
|
+
this.antennaManager = new AntennaManager(this.svgMapView, this.onlyRead);
|
|
10855
10951
|
// 设置叠加层div引用
|
|
10856
10952
|
this.antennaManager.setOverlayDiv(this.div);
|
|
10857
10953
|
// 获取容器并添加到主div
|
|
@@ -10905,6 +11001,15 @@ class MowerMapOverlay {
|
|
|
10905
11001
|
this.chargingPileManager.updatePositions();
|
|
10906
11002
|
}
|
|
10907
11003
|
}
|
|
11004
|
+
// 设置编辑模式
|
|
11005
|
+
setReadOnlyMode(enabled) {
|
|
11006
|
+
if (this.boundaryLabelsManager) {
|
|
11007
|
+
this.boundaryLabelsManager.updateReadOnlyMode(enabled);
|
|
11008
|
+
}
|
|
11009
|
+
if (this.antennaManager) {
|
|
11010
|
+
this.antennaManager.updateReadOnlyMode(enabled);
|
|
11011
|
+
}
|
|
11012
|
+
}
|
|
10908
11013
|
// 创建编辑界面
|
|
10909
11014
|
createEditInterface() {
|
|
10910
11015
|
if (!this.div)
|
|
@@ -11459,7 +11564,7 @@ class MowerMapOverlay {
|
|
|
11459
11564
|
return;
|
|
11460
11565
|
try {
|
|
11461
11566
|
// 创建SvgMapView实例
|
|
11462
|
-
this.svgMapView = new SvgMapView(this.offscreenContainer, 800, 600);
|
|
11567
|
+
this.svgMapView = new SvgMapView(this.sn, this.offscreenContainer, 800, 600);
|
|
11463
11568
|
// 加载地图数据
|
|
11464
11569
|
this.loadMapData();
|
|
11465
11570
|
// 加载路径数据
|
|
@@ -11488,12 +11593,12 @@ class MowerMapOverlay {
|
|
|
11488
11593
|
return;
|
|
11489
11594
|
try {
|
|
11490
11595
|
// 使用现有的MapDataProcessor处理地图数据
|
|
11491
|
-
const elements = MapDataProcessor.processMapData(this.mapData, this.mapConfig);
|
|
11596
|
+
const elements = MapDataProcessor.processMapData(this.sn, this.mapData, this.mapConfig);
|
|
11492
11597
|
// 分离充电桩和天线元素,其他元素添加到SVG图层
|
|
11493
11598
|
const svgElements = elements.filter((element) => element.type !== 'charging_pile' && element.type !== 'antenna');
|
|
11494
11599
|
const chargingPileElements = elements.filter((element) => element.type === 'charging_pile');
|
|
11495
11600
|
// 处理SVG图层元素
|
|
11496
|
-
const drawLayer = new DrawLayer();
|
|
11601
|
+
const drawLayer = new DrawLayer(this.sn);
|
|
11497
11602
|
drawLayer.addElements(svgElements);
|
|
11498
11603
|
// 处理天线数据
|
|
11499
11604
|
let antennaElements = [];
|
|
@@ -11562,9 +11667,9 @@ class MowerMapOverlay {
|
|
|
11562
11667
|
return pathElement;
|
|
11563
11668
|
}
|
|
11564
11669
|
});
|
|
11565
|
-
const pathLayer = new PathLayer();
|
|
11670
|
+
const pathLayer = new PathLayer(this.sn);
|
|
11566
11671
|
pathLayer.addElements(newPathElements);
|
|
11567
|
-
// 添加图层到SvgMapView
|
|
11672
|
+
// // 添加图层到SvgMapView
|
|
11568
11673
|
this.svgMapView.removeLayerByType(LAYER_DEFAULT_TYPE.PATH);
|
|
11569
11674
|
this.svgMapView.addLayer(pathLayer);
|
|
11570
11675
|
this.svgMapView.renderLayer(LAYER_DEFAULT_TYPE.PATH);
|
|
@@ -11583,9 +11688,10 @@ class MowerMapOverlay {
|
|
|
11583
11688
|
updatePathDataByMowingPosition(position) {
|
|
11584
11689
|
// 找到当前position所在的分区id,将该点更新到pathData中
|
|
11585
11690
|
// 先查找当前的分区id是多少,然后确定当前的点是否在当前分区id中,如果不在,则重新获取分区id并重新设置
|
|
11586
|
-
const
|
|
11587
|
-
|
|
11588
|
-
const
|
|
11691
|
+
const partitionIdMap = useCurrentMowingDataStore.getState().currentMowingPartitionId;
|
|
11692
|
+
const processStateIsMowingMap = useCurrentMowingDataStore.getState().processStateIsMowing;
|
|
11693
|
+
const currentPartitionId = partitionIdMap[this.sn];
|
|
11694
|
+
const processStateIsMowing = processStateIsMowingMap[this.sn];
|
|
11589
11695
|
if (currentPartitionId && this.pathData?.[currentPartitionId]) {
|
|
11590
11696
|
const currentPathData = this.pathData[currentPartitionId];
|
|
11591
11697
|
this.pathData[currentPartitionId] = {
|
|
@@ -11890,7 +11996,9 @@ const getValidGpsBounds = (mapData, rotation = 0) => {
|
|
|
11890
11996
|
// 默认配置
|
|
11891
11997
|
const defaultMapConfig = DEFAULT_STYLES;
|
|
11892
11998
|
// 地图渲染器组件
|
|
11893
|
-
const MowerMapRenderer = React.forwardRef(({
|
|
11999
|
+
const MowerMapRenderer = React.forwardRef(({
|
|
12000
|
+
// 唯一的可以标识一个割草机的id
|
|
12001
|
+
sn, edger = false, unitType = UnitsType.Imperial, language = 'en', onlyRead = false, mapConfig, modelType, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onMapLoad, onPathLoad, onError, className, style, googleMapInstance, isEditMode = false, dragCallbacks, defaultTransform, debug = false, }, ref) => {
|
|
11894
12002
|
const [elementCount, setElementCount] = React.useState(0);
|
|
11895
12003
|
const [pathCount, setPathCount] = React.useState(0);
|
|
11896
12004
|
const [currentError, setCurrentError] = React.useState(null);
|
|
@@ -11898,10 +12006,9 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
11898
12006
|
// const mapRef = useMap();
|
|
11899
12007
|
const [isGoogleMapsReady, setIsGoogleMapsReady] = React.useState(false);
|
|
11900
12008
|
const [hasInitializedBounds, setHasInitializedBounds] = React.useState(false);
|
|
11901
|
-
const {
|
|
11902
|
-
const { resetCurrentMowingPartitionId } = useCurrentMowingDataStore();
|
|
12009
|
+
const { deleteSubBoundaryBorder, deleteObstacles, deleteSvgElements } = usePartitionDataStore();
|
|
11903
12010
|
const currentProcessMowingStatusRef = React.useRef(false);
|
|
11904
|
-
const { updateProcessStateIsMowing, processStateIsMowing, updateCurrentMowingPartitionId, currentMowingPartitionId, } = useCurrentMowingDataStore();
|
|
12011
|
+
const { updateProcessStateIsMowing, processStateIsMowing, updateCurrentMowingPartitionId, currentMowingPartitionId, resetCurrentMowingPartitionId, } = useCurrentMowingDataStore();
|
|
11905
12012
|
const [mowPartitionData, setMowPartitionData] = React.useState(null);
|
|
11906
12013
|
// Debug相关状态
|
|
11907
12014
|
const [debugInfo, setDebugInfo] = React.useState({});
|
|
@@ -11967,7 +12074,7 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
11967
12074
|
setCurrentError(error);
|
|
11968
12075
|
onError?.(error);
|
|
11969
12076
|
};
|
|
11970
|
-
const fitBounds = React.useCallback(() => {
|
|
12077
|
+
const fitBounds = React.useCallback((padding = 0) => {
|
|
11971
12078
|
if (!mapJson || !mapRef)
|
|
11972
12079
|
return null;
|
|
11973
12080
|
// 计算边界
|
|
@@ -11986,7 +12093,7 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
11986
12093
|
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
11987
12094
|
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
11988
12095
|
);
|
|
11989
|
-
mapRef.fitBounds(googleBounds);
|
|
12096
|
+
mapRef.fitBounds(googleBounds, padding);
|
|
11990
12097
|
}, [mapJson, mapRef, defaultTransform]);
|
|
11991
12098
|
// 初始化Google Maps叠加层
|
|
11992
12099
|
const initializeGoogleMapsOverlay = async () => {
|
|
@@ -12027,7 +12134,7 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12027
12134
|
overlayRef.current = null;
|
|
12028
12135
|
}
|
|
12029
12136
|
// 创建叠加层
|
|
12030
|
-
const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
12137
|
+
const overlay = new MowerMapOverlay(sn, googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, onlyRead, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
12031
12138
|
setElementCount(count);
|
|
12032
12139
|
onMapLoad?.(count);
|
|
12033
12140
|
}, (count) => {
|
|
@@ -12040,7 +12147,7 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12040
12147
|
overlay.setEdger(edger);
|
|
12041
12148
|
// 只在首次初始化时自适应视图
|
|
12042
12149
|
if (!hasInitializedBounds) {
|
|
12043
|
-
mapInstance.fitBounds(googleBounds);
|
|
12150
|
+
// mapInstance.fitBounds(googleBounds);
|
|
12044
12151
|
setHasInitializedBounds(true);
|
|
12045
12152
|
}
|
|
12046
12153
|
setIsGoogleMapsReady(true);
|
|
@@ -12050,7 +12157,7 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12050
12157
|
}
|
|
12051
12158
|
};
|
|
12052
12159
|
const resetInCharginPie = React.useCallback(() => {
|
|
12053
|
-
const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
|
|
12160
|
+
const elements = MapDataProcessor.processMapData(sn, mapJson, mergedMapConfig);
|
|
12054
12161
|
const chargingPiles = elements.find((element) => element.type === 'charging_pile');
|
|
12055
12162
|
if (!overlayRef.current)
|
|
12056
12163
|
return;
|
|
@@ -12067,24 +12174,34 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12067
12174
|
initializeGoogleMapsOverlay();
|
|
12068
12175
|
// 清理函数
|
|
12069
12176
|
return () => {
|
|
12070
|
-
clearSubBoundaryBorder();
|
|
12071
|
-
clearObstacles();
|
|
12072
|
-
clearSvgElements();
|
|
12073
|
-
resetCurrentMowingPartitionId();
|
|
12074
|
-
updateProcessStateIsMowing(false);
|
|
12075
|
-
currentProcessMowingStatusRef.current = false;
|
|
12076
12177
|
if (overlayRef.current) {
|
|
12077
12178
|
overlayRef.current.setMap(null);
|
|
12078
12179
|
overlayRef.current = null;
|
|
12079
12180
|
}
|
|
12080
12181
|
};
|
|
12081
|
-
}, [mapJson, pathJson, mergedMapConfig, mergedAntennaConfig]);
|
|
12182
|
+
}, [sn, mapJson, pathJson, mergedMapConfig, mergedAntennaConfig]);
|
|
12183
|
+
React.useEffect(() => {
|
|
12184
|
+
return () => {
|
|
12185
|
+
deleteSubBoundaryBorder(sn);
|
|
12186
|
+
deleteObstacles(sn);
|
|
12187
|
+
deleteSvgElements(sn);
|
|
12188
|
+
resetCurrentMowingPartitionId(sn);
|
|
12189
|
+
updateProcessStateIsMowing(sn, false);
|
|
12190
|
+
currentProcessMowingStatusRef.current = false;
|
|
12191
|
+
};
|
|
12192
|
+
}, []);
|
|
12082
12193
|
// 监听编辑模式变化
|
|
12083
12194
|
React.useEffect(() => {
|
|
12084
12195
|
if (overlayRef.current) {
|
|
12085
12196
|
overlayRef.current.setEditMode(isEditMode);
|
|
12086
12197
|
}
|
|
12087
12198
|
}, [isEditMode]);
|
|
12199
|
+
// 监听只读模式变化
|
|
12200
|
+
React.useEffect(() => {
|
|
12201
|
+
if (overlayRef.current) {
|
|
12202
|
+
overlayRef.current.setReadOnlyMode(onlyRead);
|
|
12203
|
+
}
|
|
12204
|
+
}, [onlyRead]);
|
|
12088
12205
|
// 监听路径信息的更新,需要同步更新boundaary的进度信息
|
|
12089
12206
|
// useEffect(() => {
|
|
12090
12207
|
// if (!mapJson) return;
|
|
@@ -12094,7 +12211,7 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12094
12211
|
React.useEffect(() => {
|
|
12095
12212
|
if (!mapJson)
|
|
12096
12213
|
return;
|
|
12097
|
-
const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
|
|
12214
|
+
const elements = MapDataProcessor.processMapData(sn, mapJson, mergedMapConfig);
|
|
12098
12215
|
const chargingPiles = elements.find((element) => element.type === 'charging_pile');
|
|
12099
12216
|
if (!mowerPositionData || !overlayRef.current)
|
|
12100
12217
|
return;
|
|
@@ -12305,13 +12422,13 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12305
12422
|
if (realTimeData.length > 1) {
|
|
12306
12423
|
const { pathData, isMowing, currentMowingPartition } = handleMultipleRealTimeData({
|
|
12307
12424
|
realTimeData,
|
|
12308
|
-
isMowing: processStateIsMowing,
|
|
12425
|
+
isMowing: processStateIsMowing[sn],
|
|
12309
12426
|
pathData: pathJson,
|
|
12310
|
-
currentMowingPartition: currentMowingPartitionId,
|
|
12427
|
+
currentMowingPartition: currentMowingPartitionId[sn],
|
|
12311
12428
|
});
|
|
12312
|
-
updateProcessStateIsMowing(isMowing);
|
|
12313
|
-
if (currentMowingPartition !== currentMowingPartitionId) {
|
|
12314
|
-
updateCurrentMowingPartitionId(currentMowingPartition);
|
|
12429
|
+
updateProcessStateIsMowing(sn, isMowing);
|
|
12430
|
+
if (currentMowingPartition !== currentMowingPartitionId[sn]) {
|
|
12431
|
+
updateCurrentMowingPartitionId(sn, currentMowingPartition);
|
|
12315
12432
|
}
|
|
12316
12433
|
if (pathData) {
|
|
12317
12434
|
overlayRef.current.updatePathData(pathData, curMowPartitionData);
|
|
@@ -12321,13 +12438,13 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12321
12438
|
else {
|
|
12322
12439
|
const { isMowing, pathData, currentMowingPartition } = getProcessMowingDataFromRealTimeData({
|
|
12323
12440
|
realTimeData,
|
|
12324
|
-
isMowing: processStateIsMowing,
|
|
12441
|
+
isMowing: processStateIsMowing[sn],
|
|
12325
12442
|
pathData: pathJson,
|
|
12326
|
-
currentMowingPartition: currentMowingPartitionId,
|
|
12443
|
+
currentMowingPartition: currentMowingPartitionId[sn],
|
|
12327
12444
|
});
|
|
12328
|
-
updateProcessStateIsMowing(isMowing);
|
|
12329
|
-
if (currentMowingPartition !== currentMowingPartitionId) {
|
|
12330
|
-
updateCurrentMowingPartitionId(currentMowingPartition);
|
|
12445
|
+
updateProcessStateIsMowing(sn, isMowing);
|
|
12446
|
+
if (currentMowingPartition !== currentMowingPartitionId[sn]) {
|
|
12447
|
+
updateCurrentMowingPartitionId(sn, currentMowingPartition);
|
|
12331
12448
|
}
|
|
12332
12449
|
overlayRef.current.updatePathData(pathData, curMowPartitionData);
|
|
12333
12450
|
// 更新进度数据
|
|
@@ -12340,16 +12457,6 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12340
12457
|
if (!overlayRef.current || !defaultTransform)
|
|
12341
12458
|
return;
|
|
12342
12459
|
overlayRef.current?.setTransform(defaultTransform);
|
|
12343
|
-
const validBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
|
|
12344
|
-
// 地图数据中的坐标格式是 [longitude, latitude]
|
|
12345
|
-
const swLat = validBounds.sw[1] + defaultTransform.y;
|
|
12346
|
-
const swLng = validBounds.sw[0] + defaultTransform.x;
|
|
12347
|
-
const neLat = validBounds.ne[1] + defaultTransform.y;
|
|
12348
|
-
const neLng = validBounds.ne[0] + defaultTransform.x;
|
|
12349
|
-
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
12350
|
-
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
12351
|
-
);
|
|
12352
|
-
mapRef.fitBounds(googleBounds);
|
|
12353
12460
|
}, [defaultTransform]);
|
|
12354
12461
|
React.useEffect(() => {
|
|
12355
12462
|
if (!overlayRef || !overlayRef.current)
|
|
@@ -12358,8 +12465,8 @@ const MowerMapRenderer = React.forwardRef(({ edger = false, unitType = UnitsType
|
|
|
12358
12465
|
}, [edger]);
|
|
12359
12466
|
// 提供ref方法
|
|
12360
12467
|
React.useImperativeHandle(ref, () => ({
|
|
12361
|
-
fitToView: () => {
|
|
12362
|
-
fitBounds();
|
|
12468
|
+
fitToView: (padding = 0) => {
|
|
12469
|
+
fitBounds(padding);
|
|
12363
12470
|
},
|
|
12364
12471
|
getOverlay: () => {
|
|
12365
12472
|
return overlayRef.current;
|