@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.esm.js
CHANGED
|
@@ -136,7 +136,7 @@ const SVG_MAP_VIEW_ID = 'fleet-maps-svg-map-view';
|
|
|
136
136
|
* 使用真正的矢量SVG渲染替代Canvas位图渲染
|
|
137
137
|
*/
|
|
138
138
|
class SvgMapView {
|
|
139
|
-
constructor(containerElement, width = 800, height = 600) {
|
|
139
|
+
constructor(sn, containerElement, width = 800, height = 600) {
|
|
140
140
|
// 图层管理
|
|
141
141
|
this.layers = [];
|
|
142
142
|
// 简单变换参数
|
|
@@ -149,6 +149,7 @@ class SvgMapView {
|
|
|
149
149
|
this.isDragging = false;
|
|
150
150
|
this.lastMouseX = 0;
|
|
151
151
|
this.lastMouseY = 0;
|
|
152
|
+
this.sn = sn;
|
|
152
153
|
this.container = containerElement;
|
|
153
154
|
// 创建SVG元素
|
|
154
155
|
this.svg = this.createSVGElement();
|
|
@@ -186,7 +187,7 @@ class SvgMapView {
|
|
|
186
187
|
this.svg.setAttribute('shape-rendering', 'geometricPrecision');
|
|
187
188
|
this.svg.setAttribute('text-rendering', 'geometricPrecision');
|
|
188
189
|
this.svg.setAttribute('image-rendering', 'optimizeQuality');
|
|
189
|
-
this.svg.setAttribute('id', SVG_MAP_VIEW_ID);
|
|
190
|
+
this.svg.setAttribute('id', `${this.sn}-${SVG_MAP_VIEW_ID}`);
|
|
190
191
|
}
|
|
191
192
|
/**
|
|
192
193
|
* 创建SVG组元素
|
|
@@ -641,6 +642,7 @@ class BaseLayer {
|
|
|
641
642
|
this.needsRedraw = true;
|
|
642
643
|
this.mapView = null;
|
|
643
644
|
this.type = '';
|
|
645
|
+
this.sn = '';
|
|
644
646
|
this.level = 0; // 中等层级
|
|
645
647
|
}
|
|
646
648
|
// ==================== 基础属性方法 ====================
|
|
@@ -780,11 +782,17 @@ const usePartitionDataStore = create((set, get) => ({
|
|
|
780
782
|
addSubBoundaryBorder: (key, element) => set((state) => ({
|
|
781
783
|
subBoundaryBorder: {
|
|
782
784
|
...state.subBoundaryBorder,
|
|
783
|
-
[key]: element,
|
|
785
|
+
[key]: { ...element },
|
|
784
786
|
},
|
|
785
787
|
})),
|
|
786
788
|
// 清空所有数据
|
|
787
|
-
|
|
789
|
+
deleteSubBoundaryBorder: (key) => set((state) => {
|
|
790
|
+
const newSubBoundaryBorder = { ...state.subBoundaryBorder };
|
|
791
|
+
delete newSubBoundaryBorder[key];
|
|
792
|
+
return {
|
|
793
|
+
subBoundaryBorder: newSubBoundaryBorder,
|
|
794
|
+
};
|
|
795
|
+
}),
|
|
788
796
|
// 障碍物
|
|
789
797
|
obstacles: {},
|
|
790
798
|
addObstacles: (key, element) => set((state) => ({
|
|
@@ -793,7 +801,13 @@ const usePartitionDataStore = create((set, get) => ({
|
|
|
793
801
|
[key]: element,
|
|
794
802
|
},
|
|
795
803
|
})),
|
|
796
|
-
|
|
804
|
+
deleteObstacles: (key) => set((state) => {
|
|
805
|
+
const newObstacles = { ...state.obstacles };
|
|
806
|
+
delete newObstacles[key];
|
|
807
|
+
return {
|
|
808
|
+
obstacles: newObstacles,
|
|
809
|
+
};
|
|
810
|
+
}),
|
|
797
811
|
// svg数据
|
|
798
812
|
svgElements: {},
|
|
799
813
|
addSvgElements: (key, element) => set((state) => ({
|
|
@@ -802,7 +816,13 @@ const usePartitionDataStore = create((set, get) => ({
|
|
|
802
816
|
[key]: element,
|
|
803
817
|
},
|
|
804
818
|
})),
|
|
805
|
-
|
|
819
|
+
deleteSvgElements: (key) => set((state) => {
|
|
820
|
+
const newSvgElements = { ...state.svgElements };
|
|
821
|
+
delete newSvgElements[key];
|
|
822
|
+
return {
|
|
823
|
+
svgElements: newSvgElements,
|
|
824
|
+
};
|
|
825
|
+
}),
|
|
806
826
|
}));
|
|
807
827
|
|
|
808
828
|
/**
|
|
@@ -3288,11 +3308,12 @@ var index = {
|
|
|
3288
3308
|
* 专门处理路径元素的渲染
|
|
3289
3309
|
*/
|
|
3290
3310
|
class ChannelLayer extends BaseLayer {
|
|
3291
|
-
constructor() {
|
|
3311
|
+
constructor(sn) {
|
|
3292
3312
|
super();
|
|
3293
3313
|
this.level = 1;
|
|
3294
3314
|
this.scale = 1;
|
|
3295
3315
|
this.type = LAYER_DEFAULT_TYPE.CHANNEL;
|
|
3316
|
+
this.sn = sn;
|
|
3296
3317
|
}
|
|
3297
3318
|
/**
|
|
3298
3319
|
* 获取元素
|
|
@@ -3325,7 +3346,8 @@ class ChannelLayer extends BaseLayer {
|
|
|
3325
3346
|
createExclusionClipPathDefinitions(svgGroup) {
|
|
3326
3347
|
// 获取所有分区边界数据
|
|
3327
3348
|
const subBoundaryBorder = usePartitionDataStore.getState().subBoundaryBorder;
|
|
3328
|
-
|
|
3349
|
+
const currentSubBoundaryBorder = subBoundaryBorder[this.sn];
|
|
3350
|
+
if (!currentSubBoundaryBorder || Object.keys(currentSubBoundaryBorder).length === 0) {
|
|
3329
3351
|
return {};
|
|
3330
3352
|
}
|
|
3331
3353
|
const clipPathIdsMap = {};
|
|
@@ -3338,7 +3360,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3338
3360
|
// 计算包含所有分区和通道的边界框
|
|
3339
3361
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3340
3362
|
// 1. 先计算所有分区的边界,如果能拿到边界的svg的大小,就使用这个如果拿不到,就根据分区去计算
|
|
3341
|
-
const svg = document.getElementById(SVG_MAP_VIEW_ID);
|
|
3363
|
+
const svg = document.getElementById(`${this.sn}-${SVG_MAP_VIEW_ID}`);
|
|
3342
3364
|
if (svg && svg instanceof SVGSVGElement && svg.viewBox) {
|
|
3343
3365
|
const viewBox = svg.viewBox.baseVal;
|
|
3344
3366
|
minX = viewBox.x;
|
|
@@ -3347,8 +3369,8 @@ class ChannelLayer extends BaseLayer {
|
|
|
3347
3369
|
maxY = viewBox.y + viewBox.height;
|
|
3348
3370
|
}
|
|
3349
3371
|
else {
|
|
3350
|
-
for (const partitionId in
|
|
3351
|
-
const boundaryData =
|
|
3372
|
+
for (const partitionId in currentSubBoundaryBorder) {
|
|
3373
|
+
const boundaryData = currentSubBoundaryBorder[partitionId];
|
|
3352
3374
|
if (boundaryData && boundaryData.coordinates && boundaryData.coordinates.length > 0) {
|
|
3353
3375
|
for (const coord of boundaryData.coordinates) {
|
|
3354
3376
|
minX = Math.min(minX, coord[0]);
|
|
@@ -3360,7 +3382,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3360
3382
|
}
|
|
3361
3383
|
}
|
|
3362
3384
|
// 整理出clipPath路径
|
|
3363
|
-
const clipPathId =
|
|
3385
|
+
const clipPathId = `${this.sn}-channel-exclude-all`;
|
|
3364
3386
|
// 创建 clipPath
|
|
3365
3387
|
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
|
|
3366
3388
|
clipPath.setAttribute('id', clipPathId);
|
|
@@ -3369,7 +3391,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3369
3391
|
// 外轮廓(顺时针)
|
|
3370
3392
|
let d = `M ${minX} ${minY} L ${maxX} ${minY} L ${maxX} ${maxY} L ${minX} ${maxY} Z`;
|
|
3371
3393
|
// 获取所有需要挖空的分区路径
|
|
3372
|
-
const partitionPaths = this.mergeOverlappingPartitions(
|
|
3394
|
+
const partitionPaths = this.mergeOverlappingPartitions(currentSubBoundaryBorder || {});
|
|
3373
3395
|
// 将所有分区路径追加到clipPath中(逆时针,形成挖空)
|
|
3374
3396
|
partitionPaths.forEach((partitionCoords) => {
|
|
3375
3397
|
if (partitionCoords.length >= 3) {
|
|
@@ -3495,12 +3517,12 @@ class ChannelLayer extends BaseLayer {
|
|
|
3495
3517
|
/**
|
|
3496
3518
|
* 智能合并重叠的分区,返回所有需要挖空的路径
|
|
3497
3519
|
*/
|
|
3498
|
-
mergeOverlappingPartitions(
|
|
3520
|
+
mergeOverlappingPartitions(currentSubBoundaryBorder) {
|
|
3499
3521
|
try {
|
|
3500
3522
|
// 将所有分区转换为polygon-clipping格式
|
|
3501
3523
|
const polygons = [];
|
|
3502
3524
|
const partitionIds = [];
|
|
3503
|
-
Object.entries(
|
|
3525
|
+
Object.entries(currentSubBoundaryBorder || {}).forEach(([partitionId, boundaryData]) => {
|
|
3504
3526
|
if (boundaryData?.coordinates && boundaryData.coordinates.length >= 3) {
|
|
3505
3527
|
// 确保坐标格式正确(去掉第三个z坐标)
|
|
3506
3528
|
const coords = boundaryData.coordinates.map((coord) => [coord[0], coord[1]]);
|
|
@@ -3585,7 +3607,7 @@ class ChannelLayer extends BaseLayer {
|
|
|
3585
3607
|
catch (error) {
|
|
3586
3608
|
console.error('polygon-clipping union failed:', error);
|
|
3587
3609
|
// 如果合并失败,返回原始分区
|
|
3588
|
-
return Object.values(
|
|
3610
|
+
return Object.values(currentSubBoundaryBorder)
|
|
3589
3611
|
.filter((boundaryData) => boundaryData?.coordinates && boundaryData.coordinates.length >= 3)
|
|
3590
3612
|
.map((boundaryData) => boundaryData.coordinates.map((coord) => [coord[0], coord[1]]));
|
|
3591
3613
|
}
|
|
@@ -3597,19 +3619,21 @@ class ChannelLayer extends BaseLayer {
|
|
|
3597
3619
|
* 专门处理路径元素的渲染
|
|
3598
3620
|
*/
|
|
3599
3621
|
class PathLayer extends BaseLayer {
|
|
3600
|
-
constructor() {
|
|
3622
|
+
constructor(sn) {
|
|
3601
3623
|
super();
|
|
3602
3624
|
this.level = 3;
|
|
3603
3625
|
this.scale = 1;
|
|
3604
3626
|
this.lineScale = 1;
|
|
3605
3627
|
this.boundaryPaths = {};
|
|
3606
3628
|
this.type = LAYER_DEFAULT_TYPE.PATH;
|
|
3629
|
+
this.sn = sn;
|
|
3607
3630
|
}
|
|
3608
3631
|
/**
|
|
3609
3632
|
* 为每个分区创建独立的 clipPath
|
|
3610
3633
|
*/
|
|
3611
3634
|
createPartitionClipPaths(svgGroup) {
|
|
3612
3635
|
const { subBoundaryBorder, obstacles, svgElements } = usePartitionDataStore.getState();
|
|
3636
|
+
const currentSubBoundaryBorder = subBoundaryBorder[this.sn];
|
|
3613
3637
|
// 确保 defs 元素存在
|
|
3614
3638
|
let defs = svgGroup.querySelector('defs');
|
|
3615
3639
|
if (!defs) {
|
|
@@ -3618,9 +3642,9 @@ class PathLayer extends BaseLayer {
|
|
|
3618
3642
|
}
|
|
3619
3643
|
const clipPathIds = {};
|
|
3620
3644
|
// 为每个分区创建独立的 clipPath
|
|
3621
|
-
Object.keys(
|
|
3622
|
-
const partitionData =
|
|
3623
|
-
const clipPathId = `clip-partition-${partitionId}`;
|
|
3645
|
+
Object.keys(currentSubBoundaryBorder || {}).forEach((partitionId) => {
|
|
3646
|
+
const partitionData = currentSubBoundaryBorder[partitionId];
|
|
3647
|
+
const clipPathId = `clip-partition-${this.sn}-${partitionId}`;
|
|
3624
3648
|
// 如果已存在,先移除
|
|
3625
3649
|
const existing = defs.querySelector(`#${clipPathId}`);
|
|
3626
3650
|
if (existing)
|
|
@@ -3637,7 +3661,8 @@ class PathLayer extends BaseLayer {
|
|
|
3637
3661
|
d += ' Z ';
|
|
3638
3662
|
}
|
|
3639
3663
|
// 2. 所有禁区(逆时针)- 禁区影响所有分区
|
|
3640
|
-
|
|
3664
|
+
const currentObstacles = obstacles[this.sn];
|
|
3665
|
+
Object.values(currentObstacles || {}).forEach((item) => {
|
|
3641
3666
|
const obstacleCoords = item.coordinates;
|
|
3642
3667
|
if (obstacleCoords.length >= 3) {
|
|
3643
3668
|
d += `M ${obstacleCoords[obstacleCoords.length - 1][0]} ${obstacleCoords[obstacleCoords.length - 1][1]}`;
|
|
@@ -3648,7 +3673,8 @@ class PathLayer extends BaseLayer {
|
|
|
3648
3673
|
}
|
|
3649
3674
|
});
|
|
3650
3675
|
// 3. 所有 svgElements(逆时针)- SVG元素影响所有分区
|
|
3651
|
-
|
|
3676
|
+
const currentSvgElements = svgElements[this.sn];
|
|
3677
|
+
Object.values(currentSvgElements || {}).forEach((svgPath) => {
|
|
3652
3678
|
const svgPathString = svgPath?.metadata?.svg;
|
|
3653
3679
|
if (svgPathString && typeof svgPathString === 'string' && svgPathString.trim()) {
|
|
3654
3680
|
// 处理转义字符
|
|
@@ -3743,7 +3769,7 @@ class PathLayer extends BaseLayer {
|
|
|
3743
3769
|
lineColor = style.lineColor || '#000000';
|
|
3744
3770
|
}
|
|
3745
3771
|
// 按分区+类型+样式分组存储
|
|
3746
|
-
const groupKey = `${id}-${lineColor}-${style.lineWidth || 1}`;
|
|
3772
|
+
const groupKey = `${id}-${lineColor}-${style.lineWidth || 1}-${this.sn}`;
|
|
3747
3773
|
if (!partitionTypeGroups.has(groupKey)) {
|
|
3748
3774
|
partitionTypeGroups.set(groupKey, {
|
|
3749
3775
|
pathData: [],
|
|
@@ -3895,9 +3921,7 @@ class BoundaryLayer extends BaseLayer {
|
|
|
3895
3921
|
createBoundaryFill(svgGroup, coordinates, style) {
|
|
3896
3922
|
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
3897
3923
|
// 构建点集合,使用整数坐标(只取x,y坐标)
|
|
3898
|
-
const points = coordinates
|
|
3899
|
-
.map((coord) => `${coord[0]},${coord[1]}`)
|
|
3900
|
-
.join(' ');
|
|
3924
|
+
const points = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(' ');
|
|
3901
3925
|
const fillColor = style.fillColor;
|
|
3902
3926
|
polygon.setAttribute('points', points);
|
|
3903
3927
|
polygon.setAttribute('fill', fillColor);
|
|
@@ -3941,9 +3965,7 @@ class ObstacleLayer extends BaseLayer {
|
|
|
3941
3965
|
return;
|
|
3942
3966
|
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
3943
3967
|
// 构建点集合,使用整数坐标
|
|
3944
|
-
const points = coordinates
|
|
3945
|
-
.map((coord) => `${coord[0]},${coord[1]}`)
|
|
3946
|
-
.join(' ');
|
|
3968
|
+
const points = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(' ');
|
|
3947
3969
|
polygon.setAttribute('points', points);
|
|
3948
3970
|
polygon.setAttribute('fill', style.fillColor || 'rgba(220, 53, 69, 0.2)');
|
|
3949
3971
|
polygon.setAttribute('stroke', style.lineColor || '#dc3545');
|
|
@@ -4223,9 +4245,7 @@ class VisionOffLayer extends BaseLayer {
|
|
|
4223
4245
|
return;
|
|
4224
4246
|
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
4225
4247
|
// 构建点集合,使用整数坐标
|
|
4226
|
-
const points = coordinates
|
|
4227
|
-
.map((coord) => `${coord[0]},${coord[1]}`)
|
|
4228
|
-
.join(' ');
|
|
4248
|
+
const points = coordinates.map((coord) => `${coord[0]},${coord[1]}`).join(' ');
|
|
4229
4249
|
const fillColor = style.fillColor || 'rgba(0, 255, 0, 0.4)';
|
|
4230
4250
|
polygon.setAttribute('points', points);
|
|
4231
4251
|
polygon.setAttribute('fill', fillColor);
|
|
@@ -5006,7 +5026,10 @@ currentMowingPartition, }) => {
|
|
|
5006
5026
|
// 关于location的分区,需要通过地图数据,结合射线法,判断当前的点在哪个分区里
|
|
5007
5027
|
let mowingStatus = isMowing || false;
|
|
5008
5028
|
let newCurrentMowingPartitionId = currentMowingPartition;
|
|
5009
|
-
console.info(
|
|
5029
|
+
// console.info(
|
|
5030
|
+
// 'handleMultipleRealTimeData==newCurrentMowingPartitionId=================',
|
|
5031
|
+
// newCurrentMowingPartitionId
|
|
5032
|
+
// );
|
|
5010
5033
|
realTimeData.forEach((item) => {
|
|
5011
5034
|
// 这里需要区分,是割草进度还是割草轨迹
|
|
5012
5035
|
if (item.type === REAL_TIME_DATA_TYPE.LOCATION) {
|
|
@@ -7920,7 +7943,7 @@ class BoundaryBorderLayer extends BaseLayer {
|
|
|
7920
7943
|
*/
|
|
7921
7944
|
setMowingBoundarys(mowingBoundarys) {
|
|
7922
7945
|
if (!mowingBoundarys) {
|
|
7923
|
-
this.mowingBoundarys = this.elements?.map(item => item?.originalData?.id);
|
|
7946
|
+
this.mowingBoundarys = this.elements?.map((item) => item?.originalData?.id);
|
|
7924
7947
|
}
|
|
7925
7948
|
else {
|
|
7926
7949
|
this.mowingBoundarys = mowingBoundarys;
|
|
@@ -8240,14 +8263,15 @@ class AntennaLayer extends BaseLayer {
|
|
|
8240
8263
|
* 使用具体的图层类来处理不同类型的渲染
|
|
8241
8264
|
*/
|
|
8242
8265
|
class DrawLayer extends BaseLayer {
|
|
8243
|
-
constructor() {
|
|
8266
|
+
constructor(sn) {
|
|
8244
8267
|
super();
|
|
8268
|
+
this.sn = sn;
|
|
8245
8269
|
// 初始化各种具体图层
|
|
8246
8270
|
this.antennaLayer = new AntennaLayer();
|
|
8247
8271
|
// 通道图层
|
|
8248
|
-
this.channelLayer = new ChannelLayer();
|
|
8272
|
+
this.channelLayer = new ChannelLayer(sn);
|
|
8249
8273
|
// 路径图层
|
|
8250
|
-
this.pathLayer = new PathLayer();
|
|
8274
|
+
this.pathLayer = new PathLayer(sn);
|
|
8251
8275
|
// 边界图层
|
|
8252
8276
|
this.boundaryLayer = new BoundaryLayer();
|
|
8253
8277
|
// 边界边框图层
|
|
@@ -8670,8 +8694,9 @@ class MapDataProcessor {
|
|
|
8670
8694
|
/**
|
|
8671
8695
|
* 处理地图数据,返回绘制图层
|
|
8672
8696
|
*/
|
|
8673
|
-
static processMapData(mapData, mapConfig) {
|
|
8697
|
+
static processMapData(sn, mapData, mapConfig) {
|
|
8674
8698
|
this.mapConfig = mapConfig;
|
|
8699
|
+
this.sn = sn;
|
|
8675
8700
|
// 收集所有地图元素
|
|
8676
8701
|
const allElements = [];
|
|
8677
8702
|
// 处理子地图中的元素
|
|
@@ -8751,6 +8776,9 @@ class MapDataProcessor {
|
|
|
8751
8776
|
*/
|
|
8752
8777
|
static processMapElements(elements) {
|
|
8753
8778
|
const result = [];
|
|
8779
|
+
let subBoundaryBorderObj = {};
|
|
8780
|
+
let obstaclesObj = {};
|
|
8781
|
+
let svgElementsObj = {};
|
|
8754
8782
|
for (const element of elements) {
|
|
8755
8783
|
try {
|
|
8756
8784
|
// 检查必要的基础字段
|
|
@@ -8769,10 +8797,18 @@ class MapDataProcessor {
|
|
|
8769
8797
|
result.push(boundaryBorderElement);
|
|
8770
8798
|
// 将边界边框存储到 store 中,以分区ID为key
|
|
8771
8799
|
if (element.id) {
|
|
8800
|
+
subBoundaryBorderObj = {
|
|
8801
|
+
...subBoundaryBorderObj,
|
|
8802
|
+
[`${element.id.toString()}`]: {
|
|
8803
|
+
...boundaryBorderElement,
|
|
8804
|
+
},
|
|
8805
|
+
};
|
|
8772
8806
|
const { addSubBoundaryBorder } = usePartitionDataStore.getState();
|
|
8773
|
-
addSubBoundaryBorder(
|
|
8774
|
-
|
|
8775
|
-
}
|
|
8807
|
+
addSubBoundaryBorder(`${this.sn}`, subBoundaryBorderObj);
|
|
8808
|
+
// const { addSubBoundaryBorder } = usePartitionDataStore.getState();
|
|
8809
|
+
// addSubBoundaryBorder(`${element.id.toString()}`, {
|
|
8810
|
+
// ...boundaryBorderElement,
|
|
8811
|
+
// });
|
|
8776
8812
|
}
|
|
8777
8813
|
}
|
|
8778
8814
|
}
|
|
@@ -8788,10 +8824,15 @@ class MapDataProcessor {
|
|
|
8788
8824
|
const obstacleElement = ObstacleDataBuilder.fromMapElement(mapElement, this.mapConfig.obstacle);
|
|
8789
8825
|
if (obstacleElement) {
|
|
8790
8826
|
result.push(obstacleElement);
|
|
8791
|
-
|
|
8792
|
-
addObstacles(`obstacle-${obstacleElement.originalData.id}`, {
|
|
8827
|
+
obstaclesObj[`obstacle-${obstacleElement.originalData.id}`] = {
|
|
8793
8828
|
...obstacleElement,
|
|
8794
|
-
}
|
|
8829
|
+
};
|
|
8830
|
+
const { addObstacles } = usePartitionDataStore.getState();
|
|
8831
|
+
addObstacles(`${this.sn}`, obstaclesObj);
|
|
8832
|
+
// const { addObstacles } = usePartitionDataStore.getState();
|
|
8833
|
+
// addObstacles(`obstacle-${obstacleElement.originalData.id}`, {
|
|
8834
|
+
// ...obstacleElement,
|
|
8835
|
+
// });
|
|
8795
8836
|
}
|
|
8796
8837
|
}
|
|
8797
8838
|
catch (error) {
|
|
@@ -8841,7 +8882,6 @@ class MapDataProcessor {
|
|
|
8841
8882
|
break;
|
|
8842
8883
|
}
|
|
8843
8884
|
case 'TIME_LIMIT_OBSTACLE': {
|
|
8844
|
-
console.info('TIME_LIMIT_OBSTACLE', element);
|
|
8845
8885
|
try {
|
|
8846
8886
|
// 如果有SVG数据,直接创建SVG绘制元素
|
|
8847
8887
|
if ('svg' in element &&
|
|
@@ -8856,10 +8896,15 @@ class MapDataProcessor {
|
|
|
8856
8896
|
const svgElement = SvgElementDataBuilder.fromMapElement(mapElement, this.mapConfig.doodle);
|
|
8857
8897
|
if (svgElement) {
|
|
8858
8898
|
result.push(svgElement);
|
|
8859
|
-
|
|
8860
|
-
addSvgElements(`time-limit-obstacle-${svgElement.originalData.id}`, {
|
|
8899
|
+
svgElementsObj[`time-limit-obstacle-${svgElement.originalData.id}`] = {
|
|
8861
8900
|
...svgElement,
|
|
8862
|
-
}
|
|
8901
|
+
};
|
|
8902
|
+
const { addSvgElements } = usePartitionDataStore.getState();
|
|
8903
|
+
addSvgElements(`${this.sn}`, svgElementsObj);
|
|
8904
|
+
// const { addSvgElements } = usePartitionDataStore.getState();
|
|
8905
|
+
// addSvgElements(`time-limit-obstacle-${svgElement.originalData.id}`, {
|
|
8906
|
+
// ...svgElement,
|
|
8907
|
+
// });
|
|
8863
8908
|
}
|
|
8864
8909
|
}
|
|
8865
8910
|
// 如果有points数据,按传统方式绘制
|
|
@@ -8871,10 +8916,15 @@ class MapDataProcessor {
|
|
|
8871
8916
|
const polygonElement = ObstacleDataBuilder.createTimeLimitObstacle(mapElement, this.mapConfig.obstacle);
|
|
8872
8917
|
if (polygonElement) {
|
|
8873
8918
|
result.push(polygonElement);
|
|
8874
|
-
|
|
8875
|
-
addObstacles(`time-limit-obstacle-${polygonElement.originalData.id}`, {
|
|
8919
|
+
svgElementsObj[`time-limit-obstacle-${polygonElement.originalData.id}`] = {
|
|
8876
8920
|
...polygonElement,
|
|
8877
|
-
}
|
|
8921
|
+
};
|
|
8922
|
+
const { addSvgElements } = usePartitionDataStore.getState();
|
|
8923
|
+
addSvgElements(`${this.sn}`, svgElementsObj);
|
|
8924
|
+
// const { addObstacles } = usePartitionDataStore.getState();
|
|
8925
|
+
// addObstacles(`time-limit-obstacle-${polygonElement.originalData.id}`, {
|
|
8926
|
+
// ...polygonElement,
|
|
8927
|
+
// });
|
|
8878
8928
|
}
|
|
8879
8929
|
}
|
|
8880
8930
|
}
|
|
@@ -9034,7 +9084,7 @@ class PathDataProcessor {
|
|
|
9034
9084
|
* 专门处理边界标签的创建、定位和管理
|
|
9035
9085
|
*/
|
|
9036
9086
|
class BoundaryLabelsManager {
|
|
9037
|
-
constructor(svgView, boundaryData, { unitType, language }) {
|
|
9087
|
+
constructor(svgView, boundaryData, { unitType, language, onlyRead }) {
|
|
9038
9088
|
this.container = null;
|
|
9039
9089
|
this.overlayDiv = null;
|
|
9040
9090
|
this.globalClickHandler = null;
|
|
@@ -9047,6 +9097,7 @@ class BoundaryLabelsManager {
|
|
|
9047
9097
|
this.initializeContainer();
|
|
9048
9098
|
this.unitType = unitType;
|
|
9049
9099
|
this.language = language;
|
|
9100
|
+
this.onlyRead = onlyRead;
|
|
9050
9101
|
}
|
|
9051
9102
|
/**
|
|
9052
9103
|
* 初始化容器
|
|
@@ -9121,7 +9172,12 @@ class BoundaryLabelsManager {
|
|
|
9121
9172
|
labelDiv.style.whiteSpace = 'nowrap';
|
|
9122
9173
|
labelDiv.style.maxWidth = '220px';
|
|
9123
9174
|
labelDiv.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
9124
|
-
|
|
9175
|
+
if (this.onlyRead) {
|
|
9176
|
+
labelDiv.style.pointerEvents = 'none';
|
|
9177
|
+
}
|
|
9178
|
+
else {
|
|
9179
|
+
labelDiv.style.pointerEvents = 'auto';
|
|
9180
|
+
}
|
|
9125
9181
|
labelDiv.style.boxShadow = '0 2px 8px rgba(0,0,0,0.4)';
|
|
9126
9182
|
labelDiv.style.cursor = 'pointer';
|
|
9127
9183
|
labelDiv.style.transition = 'background-color 0.2s ease';
|
|
@@ -9207,6 +9263,9 @@ class BoundaryLabelsManager {
|
|
|
9207
9263
|
* 展开标签
|
|
9208
9264
|
*/
|
|
9209
9265
|
expandLabel(boundaryId) {
|
|
9266
|
+
if (this.onlyRead) {
|
|
9267
|
+
return;
|
|
9268
|
+
}
|
|
9210
9269
|
const labelDiv = this.getLabelElement(boundaryId);
|
|
9211
9270
|
if (!labelDiv)
|
|
9212
9271
|
return;
|
|
@@ -9215,7 +9274,6 @@ class BoundaryLabelsManager {
|
|
|
9215
9274
|
return;
|
|
9216
9275
|
// 关闭其他展开的标签
|
|
9217
9276
|
this.collapseOtherLabels(boundaryId);
|
|
9218
|
-
// 展开当前标签
|
|
9219
9277
|
extendedContent.style.display = 'block';
|
|
9220
9278
|
this.currentExpandedBoundaryId = boundaryId;
|
|
9221
9279
|
}
|
|
@@ -9415,6 +9473,16 @@ class BoundaryLabelsManager {
|
|
|
9415
9473
|
const pixelY = relativeY * divHeight;
|
|
9416
9474
|
return { x: pixelX, y: pixelY };
|
|
9417
9475
|
}
|
|
9476
|
+
updateReadOnlyMode(onlyRead) {
|
|
9477
|
+
this.onlyRead = onlyRead;
|
|
9478
|
+
if (!this.container)
|
|
9479
|
+
return;
|
|
9480
|
+
const allLabels = this.container.querySelectorAll('.boundary-label');
|
|
9481
|
+
allLabels.forEach((label) => {
|
|
9482
|
+
const labelElement = label;
|
|
9483
|
+
labelElement.style.pointerEvents = onlyRead ? 'none' : 'auto';
|
|
9484
|
+
});
|
|
9485
|
+
}
|
|
9418
9486
|
/**
|
|
9419
9487
|
* 更新边界数据
|
|
9420
9488
|
*/
|
|
@@ -9723,7 +9791,7 @@ ChargingPileManager.Z_INDEX = {
|
|
|
9723
9791
|
* 专门处理天线元素的创建、定位和管理
|
|
9724
9792
|
*/
|
|
9725
9793
|
class AntennaManager {
|
|
9726
|
-
constructor(svgView) {
|
|
9794
|
+
constructor(svgView, onlyRead) {
|
|
9727
9795
|
this.antennaElements = [];
|
|
9728
9796
|
this.container = null;
|
|
9729
9797
|
this.overlayDiv = null;
|
|
@@ -9736,6 +9804,7 @@ class AntennaManager {
|
|
|
9736
9804
|
// 旋转角度
|
|
9737
9805
|
this.rotation = 0;
|
|
9738
9806
|
this.svgView = svgView;
|
|
9807
|
+
this.onlyRead = onlyRead;
|
|
9739
9808
|
this.initializeContainer();
|
|
9740
9809
|
this.setupGlobalClickHandler();
|
|
9741
9810
|
}
|
|
@@ -9808,7 +9877,12 @@ class AntennaManager {
|
|
|
9808
9877
|
antennaContainer.className = 'antenna-container-item';
|
|
9809
9878
|
antennaContainer.style.position = 'absolute';
|
|
9810
9879
|
antennaContainer.style.transform = `translate(-50%, -50%) rotate(${-this.rotation}deg)`;
|
|
9811
|
-
|
|
9880
|
+
if (this.onlyRead) {
|
|
9881
|
+
antennaContainer.style.pointerEvents = 'none';
|
|
9882
|
+
}
|
|
9883
|
+
else {
|
|
9884
|
+
antennaContainer.style.pointerEvents = 'auto';
|
|
9885
|
+
}
|
|
9812
9886
|
antennaContainer.style.zIndex = AntennaManager.Z_INDEX.DEFAULT.toString();
|
|
9813
9887
|
antennaContainer.setAttribute('data-antenna-id', antennaData.type.toString());
|
|
9814
9888
|
// 创建天线图标
|
|
@@ -9905,6 +9979,9 @@ class AntennaManager {
|
|
|
9905
9979
|
const tooltip = antennaContainer.querySelector('.antenna-tooltip');
|
|
9906
9980
|
if (!tooltip)
|
|
9907
9981
|
return;
|
|
9982
|
+
if (this.onlyRead) {
|
|
9983
|
+
return;
|
|
9984
|
+
}
|
|
9908
9985
|
// 展开当前tooltip
|
|
9909
9986
|
tooltip.style.display = 'block';
|
|
9910
9987
|
const extendedContent = tooltip.querySelector('.antenna-tooltip-extended');
|
|
@@ -9950,6 +10027,16 @@ class AntennaManager {
|
|
|
9950
10027
|
}
|
|
9951
10028
|
});
|
|
9952
10029
|
}
|
|
10030
|
+
updateReadOnlyMode(onlyRead) {
|
|
10031
|
+
this.onlyRead = onlyRead;
|
|
10032
|
+
if (!this.container)
|
|
10033
|
+
return;
|
|
10034
|
+
const allAntennas = this.container.querySelectorAll('.antenna-container-item');
|
|
10035
|
+
allAntennas.forEach((antenna) => {
|
|
10036
|
+
const antennaElement = antenna;
|
|
10037
|
+
antennaElement.style.pointerEvents = onlyRead ? 'none' : 'auto';
|
|
10038
|
+
});
|
|
10039
|
+
}
|
|
9953
10040
|
/**
|
|
9954
10041
|
* 添加主天线
|
|
9955
10042
|
*/
|
|
@@ -10591,24 +10678,29 @@ function isMobileDevice() {
|
|
|
10591
10678
|
// 记录割草状态,状态变更的时候,变量不触发重新渲染
|
|
10592
10679
|
const useCurrentMowingDataStore = create((set) => ({
|
|
10593
10680
|
// 当前进度数据返回的割草状态是否为在割草
|
|
10594
|
-
processStateIsMowing:
|
|
10595
|
-
updateProcessStateIsMowing: (isMowing) => set({ processStateIsMowing: isMowing }),
|
|
10596
|
-
resetProcessStateIsMowing: () => set({ processStateIsMowing: false }),
|
|
10681
|
+
processStateIsMowing: {},
|
|
10682
|
+
updateProcessStateIsMowing: (sn, isMowing) => set((state) => ({ processStateIsMowing: { ...state.processStateIsMowing, [sn]: isMowing } })),
|
|
10683
|
+
resetProcessStateIsMowing: (key) => set((state) => ({ processStateIsMowing: { ...state.processStateIsMowing, [key]: false } })),
|
|
10597
10684
|
// 当前割草的分区id
|
|
10598
|
-
currentMowingPartitionId:
|
|
10599
|
-
updateCurrentMowingPartitionId: (partitionId) => set(
|
|
10600
|
-
|
|
10685
|
+
currentMowingPartitionId: {},
|
|
10686
|
+
updateCurrentMowingPartitionId: (sn, partitionId) => set((state) => ({
|
|
10687
|
+
currentMowingPartitionId: { ...state.currentMowingPartitionId, [sn]: partitionId },
|
|
10688
|
+
})),
|
|
10689
|
+
resetCurrentMowingPartitionId: (key) => set((state) => ({
|
|
10690
|
+
currentMowingPartitionId: { ...state.currentMowingPartitionId, [key]: '' },
|
|
10691
|
+
})),
|
|
10601
10692
|
}));
|
|
10602
10693
|
|
|
10603
10694
|
// Google Maps 叠加层类 - 带编辑功能
|
|
10604
10695
|
class MowerMapOverlay {
|
|
10605
|
-
constructor(bounds, mapData, partitionBoundary, mowerPositionConfig, modelType, pathData, isEditMode = false, unitType = UnitsType.Imperial, language = 'en', mapConfig = {}, antennaConfig = {}, mowPartitionData = null, defaultTransform, onMapLoad, onPathLoad, dragCallbacks) {
|
|
10696
|
+
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) {
|
|
10606
10697
|
this.div = null;
|
|
10607
10698
|
this.svgMapView = null;
|
|
10608
10699
|
this.offscreenContainer = null;
|
|
10609
10700
|
this.overlayView = null;
|
|
10610
10701
|
this.defaultTransform = { x: 0, y: 0, rotation: 0 };
|
|
10611
10702
|
this.hasEdger = false;
|
|
10703
|
+
this.sn = '';
|
|
10612
10704
|
// boundary数据
|
|
10613
10705
|
this.boundaryData = [];
|
|
10614
10706
|
// 边界标签管理器
|
|
@@ -10646,6 +10738,7 @@ class MowerMapOverlay {
|
|
|
10646
10738
|
leading: true,
|
|
10647
10739
|
trailing: false,
|
|
10648
10740
|
});
|
|
10741
|
+
this.sn = sn;
|
|
10649
10742
|
this.bounds = bounds;
|
|
10650
10743
|
this.mapData = mapData;
|
|
10651
10744
|
this.partitionBoundary = partitionBoundary;
|
|
@@ -10653,6 +10746,7 @@ class MowerMapOverlay {
|
|
|
10653
10746
|
this.isEditMode = isEditMode;
|
|
10654
10747
|
this.unitType = unitType;
|
|
10655
10748
|
this.language = language;
|
|
10749
|
+
this.onlyRead = onlyRead;
|
|
10656
10750
|
this.mapConfig = mapConfig;
|
|
10657
10751
|
this.antennaConfig = antennaConfig;
|
|
10658
10752
|
this.onMapLoad = onMapLoad;
|
|
@@ -10683,6 +10777,7 @@ class MowerMapOverlay {
|
|
|
10683
10777
|
this.overlayView.hide = this.hide.bind(this);
|
|
10684
10778
|
// 添加编辑模式相关方法
|
|
10685
10779
|
this.overlayView.setEditMode = this.setEditMode.bind(this);
|
|
10780
|
+
this.overlayView.setReadOnlyMode = this.setReadOnlyMode.bind(this);
|
|
10686
10781
|
this.overlayView.getEditData = this.getEditData.bind(this);
|
|
10687
10782
|
this.overlayView.handleSave = this.handleSave.bind(this);
|
|
10688
10783
|
this.overlayView.setCustomIcons = this.setCustomIcons.bind(this);
|
|
@@ -10819,6 +10914,7 @@ class MowerMapOverlay {
|
|
|
10819
10914
|
this.boundaryLabelsManager = new BoundaryLabelsManager(this.svgMapView, this.boundaryData, {
|
|
10820
10915
|
unitType: this.unitType,
|
|
10821
10916
|
language: this.language,
|
|
10917
|
+
onlyRead: this.onlyRead,
|
|
10822
10918
|
});
|
|
10823
10919
|
// 设置叠加层div引用
|
|
10824
10920
|
this.boundaryLabelsManager.setOverlayDiv(this.div);
|
|
@@ -10849,7 +10945,7 @@ class MowerMapOverlay {
|
|
|
10849
10945
|
if (!this.div || !this.svgMapView)
|
|
10850
10946
|
return;
|
|
10851
10947
|
// 创建天线管理器
|
|
10852
|
-
this.antennaManager = new AntennaManager(this.svgMapView);
|
|
10948
|
+
this.antennaManager = new AntennaManager(this.svgMapView, this.onlyRead);
|
|
10853
10949
|
// 设置叠加层div引用
|
|
10854
10950
|
this.antennaManager.setOverlayDiv(this.div);
|
|
10855
10951
|
// 获取容器并添加到主div
|
|
@@ -10903,6 +10999,15 @@ class MowerMapOverlay {
|
|
|
10903
10999
|
this.chargingPileManager.updatePositions();
|
|
10904
11000
|
}
|
|
10905
11001
|
}
|
|
11002
|
+
// 设置编辑模式
|
|
11003
|
+
setReadOnlyMode(enabled) {
|
|
11004
|
+
if (this.boundaryLabelsManager) {
|
|
11005
|
+
this.boundaryLabelsManager.updateReadOnlyMode(enabled);
|
|
11006
|
+
}
|
|
11007
|
+
if (this.antennaManager) {
|
|
11008
|
+
this.antennaManager.updateReadOnlyMode(enabled);
|
|
11009
|
+
}
|
|
11010
|
+
}
|
|
10906
11011
|
// 创建编辑界面
|
|
10907
11012
|
createEditInterface() {
|
|
10908
11013
|
if (!this.div)
|
|
@@ -11457,7 +11562,7 @@ class MowerMapOverlay {
|
|
|
11457
11562
|
return;
|
|
11458
11563
|
try {
|
|
11459
11564
|
// 创建SvgMapView实例
|
|
11460
|
-
this.svgMapView = new SvgMapView(this.offscreenContainer, 800, 600);
|
|
11565
|
+
this.svgMapView = new SvgMapView(this.sn, this.offscreenContainer, 800, 600);
|
|
11461
11566
|
// 加载地图数据
|
|
11462
11567
|
this.loadMapData();
|
|
11463
11568
|
// 加载路径数据
|
|
@@ -11486,12 +11591,12 @@ class MowerMapOverlay {
|
|
|
11486
11591
|
return;
|
|
11487
11592
|
try {
|
|
11488
11593
|
// 使用现有的MapDataProcessor处理地图数据
|
|
11489
|
-
const elements = MapDataProcessor.processMapData(this.mapData, this.mapConfig);
|
|
11594
|
+
const elements = MapDataProcessor.processMapData(this.sn, this.mapData, this.mapConfig);
|
|
11490
11595
|
// 分离充电桩和天线元素,其他元素添加到SVG图层
|
|
11491
11596
|
const svgElements = elements.filter((element) => element.type !== 'charging_pile' && element.type !== 'antenna');
|
|
11492
11597
|
const chargingPileElements = elements.filter((element) => element.type === 'charging_pile');
|
|
11493
11598
|
// 处理SVG图层元素
|
|
11494
|
-
const drawLayer = new DrawLayer();
|
|
11599
|
+
const drawLayer = new DrawLayer(this.sn);
|
|
11495
11600
|
drawLayer.addElements(svgElements);
|
|
11496
11601
|
// 处理天线数据
|
|
11497
11602
|
let antennaElements = [];
|
|
@@ -11560,9 +11665,9 @@ class MowerMapOverlay {
|
|
|
11560
11665
|
return pathElement;
|
|
11561
11666
|
}
|
|
11562
11667
|
});
|
|
11563
|
-
const pathLayer = new PathLayer();
|
|
11668
|
+
const pathLayer = new PathLayer(this.sn);
|
|
11564
11669
|
pathLayer.addElements(newPathElements);
|
|
11565
|
-
// 添加图层到SvgMapView
|
|
11670
|
+
// // 添加图层到SvgMapView
|
|
11566
11671
|
this.svgMapView.removeLayerByType(LAYER_DEFAULT_TYPE.PATH);
|
|
11567
11672
|
this.svgMapView.addLayer(pathLayer);
|
|
11568
11673
|
this.svgMapView.renderLayer(LAYER_DEFAULT_TYPE.PATH);
|
|
@@ -11581,9 +11686,10 @@ class MowerMapOverlay {
|
|
|
11581
11686
|
updatePathDataByMowingPosition(position) {
|
|
11582
11687
|
// 找到当前position所在的分区id,将该点更新到pathData中
|
|
11583
11688
|
// 先查找当前的分区id是多少,然后确定当前的点是否在当前分区id中,如果不在,则重新获取分区id并重新设置
|
|
11584
|
-
const
|
|
11585
|
-
|
|
11586
|
-
const
|
|
11689
|
+
const partitionIdMap = useCurrentMowingDataStore.getState().currentMowingPartitionId;
|
|
11690
|
+
const processStateIsMowingMap = useCurrentMowingDataStore.getState().processStateIsMowing;
|
|
11691
|
+
const currentPartitionId = partitionIdMap[this.sn];
|
|
11692
|
+
const processStateIsMowing = processStateIsMowingMap[this.sn];
|
|
11587
11693
|
if (currentPartitionId && this.pathData?.[currentPartitionId]) {
|
|
11588
11694
|
const currentPathData = this.pathData[currentPartitionId];
|
|
11589
11695
|
this.pathData[currentPartitionId] = {
|
|
@@ -11888,7 +11994,9 @@ const getValidGpsBounds = (mapData, rotation = 0) => {
|
|
|
11888
11994
|
// 默认配置
|
|
11889
11995
|
const defaultMapConfig = DEFAULT_STYLES;
|
|
11890
11996
|
// 地图渲染器组件
|
|
11891
|
-
const MowerMapRenderer = forwardRef(({
|
|
11997
|
+
const MowerMapRenderer = forwardRef(({
|
|
11998
|
+
// 唯一的可以标识一个割草机的id
|
|
11999
|
+
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) => {
|
|
11892
12000
|
const [elementCount, setElementCount] = useState(0);
|
|
11893
12001
|
const [pathCount, setPathCount] = useState(0);
|
|
11894
12002
|
const [currentError, setCurrentError] = useState(null);
|
|
@@ -11896,10 +12004,9 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
11896
12004
|
// const mapRef = useMap();
|
|
11897
12005
|
const [isGoogleMapsReady, setIsGoogleMapsReady] = useState(false);
|
|
11898
12006
|
const [hasInitializedBounds, setHasInitializedBounds] = useState(false);
|
|
11899
|
-
const {
|
|
11900
|
-
const { resetCurrentMowingPartitionId } = useCurrentMowingDataStore();
|
|
12007
|
+
const { deleteSubBoundaryBorder, deleteObstacles, deleteSvgElements } = usePartitionDataStore();
|
|
11901
12008
|
const currentProcessMowingStatusRef = useRef(false);
|
|
11902
|
-
const { updateProcessStateIsMowing, processStateIsMowing, updateCurrentMowingPartitionId, currentMowingPartitionId, } = useCurrentMowingDataStore();
|
|
12009
|
+
const { updateProcessStateIsMowing, processStateIsMowing, updateCurrentMowingPartitionId, currentMowingPartitionId, resetCurrentMowingPartitionId, } = useCurrentMowingDataStore();
|
|
11903
12010
|
const [mowPartitionData, setMowPartitionData] = useState(null);
|
|
11904
12011
|
// Debug相关状态
|
|
11905
12012
|
const [debugInfo, setDebugInfo] = useState({});
|
|
@@ -11965,7 +12072,7 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
11965
12072
|
setCurrentError(error);
|
|
11966
12073
|
onError?.(error);
|
|
11967
12074
|
};
|
|
11968
|
-
const fitBounds = useCallback(() => {
|
|
12075
|
+
const fitBounds = useCallback((padding = 0) => {
|
|
11969
12076
|
if (!mapJson || !mapRef)
|
|
11970
12077
|
return null;
|
|
11971
12078
|
// 计算边界
|
|
@@ -11984,7 +12091,7 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
11984
12091
|
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
11985
12092
|
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
11986
12093
|
);
|
|
11987
|
-
mapRef.fitBounds(googleBounds);
|
|
12094
|
+
mapRef.fitBounds(googleBounds, padding);
|
|
11988
12095
|
}, [mapJson, mapRef, defaultTransform]);
|
|
11989
12096
|
// 初始化Google Maps叠加层
|
|
11990
12097
|
const initializeGoogleMapsOverlay = async () => {
|
|
@@ -12025,7 +12132,7 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12025
12132
|
overlayRef.current = null;
|
|
12026
12133
|
}
|
|
12027
12134
|
// 创建叠加层
|
|
12028
|
-
const overlay = new MowerMapOverlay(googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
12135
|
+
const overlay = new MowerMapOverlay(sn, googleBounds, mapJson, partitionBoundary, mowerPositionData, modelType, pathJson || {}, isEditMode, unitType, language, onlyRead, mergedMapConfig, mergedAntennaConfig, null, defaultTransform, (count) => {
|
|
12029
12136
|
setElementCount(count);
|
|
12030
12137
|
onMapLoad?.(count);
|
|
12031
12138
|
}, (count) => {
|
|
@@ -12038,7 +12145,7 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12038
12145
|
overlay.setEdger(edger);
|
|
12039
12146
|
// 只在首次初始化时自适应视图
|
|
12040
12147
|
if (!hasInitializedBounds) {
|
|
12041
|
-
mapInstance.fitBounds(googleBounds);
|
|
12148
|
+
// mapInstance.fitBounds(googleBounds);
|
|
12042
12149
|
setHasInitializedBounds(true);
|
|
12043
12150
|
}
|
|
12044
12151
|
setIsGoogleMapsReady(true);
|
|
@@ -12048,7 +12155,7 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12048
12155
|
}
|
|
12049
12156
|
};
|
|
12050
12157
|
const resetInCharginPie = useCallback(() => {
|
|
12051
|
-
const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
|
|
12158
|
+
const elements = MapDataProcessor.processMapData(sn, mapJson, mergedMapConfig);
|
|
12052
12159
|
const chargingPiles = elements.find((element) => element.type === 'charging_pile');
|
|
12053
12160
|
if (!overlayRef.current)
|
|
12054
12161
|
return;
|
|
@@ -12065,24 +12172,34 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12065
12172
|
initializeGoogleMapsOverlay();
|
|
12066
12173
|
// 清理函数
|
|
12067
12174
|
return () => {
|
|
12068
|
-
clearSubBoundaryBorder();
|
|
12069
|
-
clearObstacles();
|
|
12070
|
-
clearSvgElements();
|
|
12071
|
-
resetCurrentMowingPartitionId();
|
|
12072
|
-
updateProcessStateIsMowing(false);
|
|
12073
|
-
currentProcessMowingStatusRef.current = false;
|
|
12074
12175
|
if (overlayRef.current) {
|
|
12075
12176
|
overlayRef.current.setMap(null);
|
|
12076
12177
|
overlayRef.current = null;
|
|
12077
12178
|
}
|
|
12078
12179
|
};
|
|
12079
|
-
}, [mapJson, pathJson, mergedMapConfig, mergedAntennaConfig]);
|
|
12180
|
+
}, [sn, mapJson, pathJson, mergedMapConfig, mergedAntennaConfig]);
|
|
12181
|
+
useEffect(() => {
|
|
12182
|
+
return () => {
|
|
12183
|
+
deleteSubBoundaryBorder(sn);
|
|
12184
|
+
deleteObstacles(sn);
|
|
12185
|
+
deleteSvgElements(sn);
|
|
12186
|
+
resetCurrentMowingPartitionId(sn);
|
|
12187
|
+
updateProcessStateIsMowing(sn, false);
|
|
12188
|
+
currentProcessMowingStatusRef.current = false;
|
|
12189
|
+
};
|
|
12190
|
+
}, []);
|
|
12080
12191
|
// 监听编辑模式变化
|
|
12081
12192
|
useEffect(() => {
|
|
12082
12193
|
if (overlayRef.current) {
|
|
12083
12194
|
overlayRef.current.setEditMode(isEditMode);
|
|
12084
12195
|
}
|
|
12085
12196
|
}, [isEditMode]);
|
|
12197
|
+
// 监听只读模式变化
|
|
12198
|
+
useEffect(() => {
|
|
12199
|
+
if (overlayRef.current) {
|
|
12200
|
+
overlayRef.current.setReadOnlyMode(onlyRead);
|
|
12201
|
+
}
|
|
12202
|
+
}, [onlyRead]);
|
|
12086
12203
|
// 监听路径信息的更新,需要同步更新boundaary的进度信息
|
|
12087
12204
|
// useEffect(() => {
|
|
12088
12205
|
// if (!mapJson) return;
|
|
@@ -12092,7 +12209,7 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12092
12209
|
useEffect(() => {
|
|
12093
12210
|
if (!mapJson)
|
|
12094
12211
|
return;
|
|
12095
|
-
const elements = MapDataProcessor.processMapData(mapJson, mergedMapConfig);
|
|
12212
|
+
const elements = MapDataProcessor.processMapData(sn, mapJson, mergedMapConfig);
|
|
12096
12213
|
const chargingPiles = elements.find((element) => element.type === 'charging_pile');
|
|
12097
12214
|
if (!mowerPositionData || !overlayRef.current)
|
|
12098
12215
|
return;
|
|
@@ -12303,13 +12420,13 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12303
12420
|
if (realTimeData.length > 1) {
|
|
12304
12421
|
const { pathData, isMowing, currentMowingPartition } = handleMultipleRealTimeData({
|
|
12305
12422
|
realTimeData,
|
|
12306
|
-
isMowing: processStateIsMowing,
|
|
12423
|
+
isMowing: processStateIsMowing[sn],
|
|
12307
12424
|
pathData: pathJson,
|
|
12308
|
-
currentMowingPartition: currentMowingPartitionId,
|
|
12425
|
+
currentMowingPartition: currentMowingPartitionId[sn],
|
|
12309
12426
|
});
|
|
12310
|
-
updateProcessStateIsMowing(isMowing);
|
|
12311
|
-
if (currentMowingPartition !== currentMowingPartitionId) {
|
|
12312
|
-
updateCurrentMowingPartitionId(currentMowingPartition);
|
|
12427
|
+
updateProcessStateIsMowing(sn, isMowing);
|
|
12428
|
+
if (currentMowingPartition !== currentMowingPartitionId[sn]) {
|
|
12429
|
+
updateCurrentMowingPartitionId(sn, currentMowingPartition);
|
|
12313
12430
|
}
|
|
12314
12431
|
if (pathData) {
|
|
12315
12432
|
overlayRef.current.updatePathData(pathData, curMowPartitionData);
|
|
@@ -12319,13 +12436,13 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12319
12436
|
else {
|
|
12320
12437
|
const { isMowing, pathData, currentMowingPartition } = getProcessMowingDataFromRealTimeData({
|
|
12321
12438
|
realTimeData,
|
|
12322
|
-
isMowing: processStateIsMowing,
|
|
12439
|
+
isMowing: processStateIsMowing[sn],
|
|
12323
12440
|
pathData: pathJson,
|
|
12324
|
-
currentMowingPartition: currentMowingPartitionId,
|
|
12441
|
+
currentMowingPartition: currentMowingPartitionId[sn],
|
|
12325
12442
|
});
|
|
12326
|
-
updateProcessStateIsMowing(isMowing);
|
|
12327
|
-
if (currentMowingPartition !== currentMowingPartitionId) {
|
|
12328
|
-
updateCurrentMowingPartitionId(currentMowingPartition);
|
|
12443
|
+
updateProcessStateIsMowing(sn, isMowing);
|
|
12444
|
+
if (currentMowingPartition !== currentMowingPartitionId[sn]) {
|
|
12445
|
+
updateCurrentMowingPartitionId(sn, currentMowingPartition);
|
|
12329
12446
|
}
|
|
12330
12447
|
overlayRef.current.updatePathData(pathData, curMowPartitionData);
|
|
12331
12448
|
// 更新进度数据
|
|
@@ -12338,16 +12455,6 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12338
12455
|
if (!overlayRef.current || !defaultTransform)
|
|
12339
12456
|
return;
|
|
12340
12457
|
overlayRef.current?.setTransform(defaultTransform);
|
|
12341
|
-
const validBounds = getValidGpsBounds(mapJson, defaultTransform?.rotation);
|
|
12342
|
-
// 地图数据中的坐标格式是 [longitude, latitude]
|
|
12343
|
-
const swLat = validBounds.sw[1] + defaultTransform.y;
|
|
12344
|
-
const swLng = validBounds.sw[0] + defaultTransform.x;
|
|
12345
|
-
const neLat = validBounds.ne[1] + defaultTransform.y;
|
|
12346
|
-
const neLng = validBounds.ne[0] + defaultTransform.x;
|
|
12347
|
-
const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
|
|
12348
|
-
new window.google.maps.LatLng(neLat, neLng) // 东北角
|
|
12349
|
-
);
|
|
12350
|
-
mapRef.fitBounds(googleBounds);
|
|
12351
12458
|
}, [defaultTransform]);
|
|
12352
12459
|
useEffect(() => {
|
|
12353
12460
|
if (!overlayRef || !overlayRef.current)
|
|
@@ -12356,8 +12463,8 @@ const MowerMapRenderer = forwardRef(({ edger = false, unitType = UnitsType.Imper
|
|
|
12356
12463
|
}, [edger]);
|
|
12357
12464
|
// 提供ref方法
|
|
12358
12465
|
useImperativeHandle(ref, () => ({
|
|
12359
|
-
fitToView: () => {
|
|
12360
|
-
fitBounds();
|
|
12466
|
+
fitToView: (padding = 0) => {
|
|
12467
|
+
fitBounds(padding);
|
|
12361
12468
|
},
|
|
12362
12469
|
getOverlay: () => {
|
|
12363
12470
|
return overlayRef.current;
|