@fleet-frontend/mower-maps 0.2.5-beta.9 → 0.2.6-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.
Files changed (72) hide show
  1. package/dist/config/constants.d.ts +3 -0
  2. package/dist/config/constants.d.ts.map +1 -1
  3. package/dist/config/styles.d.ts.map +1 -1
  4. package/dist/context/common.d.ts +17 -0
  5. package/dist/context/common.d.ts.map +1 -1
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.esm.js +2660 -542
  9. package/dist/index.js +2671 -540
  10. package/dist/render/BoundarySvgRender.d.ts +3 -1
  11. package/dist/render/BoundarySvgRender.d.ts.map +1 -1
  12. package/dist/render/MowerMapRenderer.d.ts.map +1 -1
  13. package/dist/render/MowerPartitionPickerMap.d.ts +17 -0
  14. package/dist/render/MowerPartitionPickerMap.d.ts.map +1 -0
  15. package/dist/render/antennas/useAntennaInfo.d.ts.map +1 -1
  16. package/dist/render/boundaryLabels/useBoundaryLabels.d.ts.map +1 -1
  17. package/dist/render/charginPile/index.d.ts +11 -1
  18. package/dist/render/charginPile/index.d.ts.map +1 -1
  19. package/dist/render/charginPile/useChargingPile.d.ts +11 -1
  20. package/dist/render/charginPile/useChargingPile.d.ts.map +1 -1
  21. package/dist/render/partitionPicker/constants.d.ts +169 -0
  22. package/dist/render/partitionPicker/constants.d.ts.map +1 -0
  23. package/dist/render/partitionPicker/hooks/useBoundaryHover.d.ts +18 -0
  24. package/dist/render/partitionPicker/hooks/useBoundaryHover.d.ts.map +1 -0
  25. package/dist/render/partitionPicker/hooks/useBoundarySelection.d.ts +20 -0
  26. package/dist/render/partitionPicker/hooks/useBoundarySelection.d.ts.map +1 -0
  27. package/dist/render/partitionPicker/hooks/useContainerSize.d.ts +16 -0
  28. package/dist/render/partitionPicker/hooks/useContainerSize.d.ts.map +1 -0
  29. package/dist/render/partitionPicker/hooks/useGoogleMapsOverlay.d.ts +31 -0
  30. package/dist/render/partitionPicker/hooks/useGoogleMapsOverlay.d.ts.map +1 -0
  31. package/dist/render/partitionPicker/hooks/useIsolatedBoundaryIds.d.ts +6 -0
  32. package/dist/render/partitionPicker/hooks/useIsolatedBoundaryIds.d.ts.map +1 -0
  33. package/dist/render/partitionPicker/hooks/useMapViewport.d.ts +62 -0
  34. package/dist/render/partitionPicker/hooks/useMapViewport.d.ts.map +1 -0
  35. package/dist/render/partitionPicker/hooks/usePartitionLabelItems.d.ts +22 -0
  36. package/dist/render/partitionPicker/hooks/usePartitionLabelItems.d.ts.map +1 -0
  37. package/dist/render/partitionPicker/index.d.ts +9 -0
  38. package/dist/render/partitionPicker/index.d.ts.map +1 -0
  39. package/dist/render/partitionPicker/layers/MapGridBackground.d.ts +22 -0
  40. package/dist/render/partitionPicker/layers/MapGridBackground.d.ts.map +1 -0
  41. package/dist/render/partitionPicker/layers/PartitionNameLabels.d.ts +21 -0
  42. package/dist/render/partitionPicker/layers/PartitionNameLabels.d.ts.map +1 -0
  43. package/dist/render/partitionPicker/layers/PartitionPickerGridSvg.d.ts +10 -0
  44. package/dist/render/partitionPicker/layers/PartitionPickerGridSvg.d.ts.map +1 -0
  45. package/dist/render/partitionPicker/layers/PartitionPickerSvg.d.ts +24 -0
  46. package/dist/render/partitionPicker/layers/PartitionPickerSvg.d.ts.map +1 -0
  47. package/dist/render/partitionPicker/layers/SelectableBoundaryElement.d.ts +34 -0
  48. package/dist/render/partitionPicker/layers/SelectableBoundaryElement.d.ts.map +1 -0
  49. package/dist/render/partitionPicker/types.d.ts +163 -0
  50. package/dist/render/partitionPicker/types.d.ts.map +1 -0
  51. package/dist/render/partitionPicker/viewportUtils.d.ts +100 -0
  52. package/dist/render/partitionPicker/viewportUtils.d.ts.map +1 -0
  53. package/dist/render/svgElement/ChannelClipPath/constants.d.ts +9 -0
  54. package/dist/render/svgElement/ChannelClipPath/constants.d.ts.map +1 -0
  55. package/dist/render/svgElement/ChannelElement/index.d.ts.map +1 -1
  56. package/dist/render/svgElement/ObstacleElement/index.d.ts.map +1 -1
  57. package/dist/render/svgElement/PathElement/index.d.ts.map +1 -1
  58. package/dist/render/svgElement/PolygonELement/components/Magnifier/index.d.ts.map +1 -1
  59. package/dist/render/svgElement/PolygonELement/components/Tooltip.d.ts.map +1 -1
  60. package/dist/render/svgElement/PolygonELement/index.d.ts.map +1 -1
  61. package/dist/render/svgElement/TransformWrapper/DoodleTransformWrapper/DoodleTransformWrapper.d.ts.map +1 -1
  62. package/dist/render/svgElement/TransformWrapper/VisionOffTransformWrapper/VisionOffTransformWrapper.d.ts.map +1 -1
  63. package/dist/types/renderer.d.ts +21 -0
  64. package/dist/types/renderer.d.ts.map +1 -1
  65. package/dist/utils/dateTimeUtils.d.ts +6 -2
  66. package/dist/utils/dateTimeUtils.d.ts.map +1 -1
  67. package/dist/utils/googleStaticMapSign.d.ts +17 -0
  68. package/dist/utils/googleStaticMapSign.d.ts.map +1 -0
  69. package/dist/utils/mapBounds.d.ts.map +1 -1
  70. package/dist/utils/pathData.d.ts +2 -0
  71. package/dist/utils/pathData.d.ts.map +1 -0
  72. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2109,7 +2109,7 @@ var ellipsoids = {
2109
2109
  }
2110
2110
  };
2111
2111
 
2112
- const WGS84$1 = ellipsoids.WGS84; // default ellipsoid
2112
+ const WGS84$2 = ellipsoids.WGS84; // default ellipsoid
2113
2113
 
2114
2114
  function eccentricity(a, b, rf, R_A) {
2115
2115
  var a2 = a * a; // used in geocentric
@@ -2134,7 +2134,7 @@ function sphere(a, b, rf, ellps, sphere) {
2134
2134
  if (!a) { // do we have an ellipsoid?
2135
2135
  var ellipse = match(ellipsoids, ellps);
2136
2136
  if (!ellipse) {
2137
- ellipse = WGS84$1;
2137
+ ellipse = WGS84$2;
2138
2138
  }
2139
2139
  a = ellipse.a;
2140
2140
  b = ellipse.b;
@@ -10633,9 +10633,16 @@ var SvgMapOpacity;
10633
10633
  * 地图渲染相关常量配置
10634
10634
  */
10635
10635
  // 默认坐标
10636
+ // export const DEFAULT_COORDINATES = {
10637
+ // sw: [116.071044, 40.102099],
10638
+ // ne: [116.0734, 40.103901],
10639
+ // };
10640
+ /** 无效 GPS 时的兜底中心点 [lng, lat] */
10641
+ const DEFAULT_GPS_CENTER = [0, 0];
10642
+ /** 无 map_width/map_height 时的默认 GPS 边界 [lng, lat](中心约 (0,0)) */
10636
10643
  const DEFAULT_COORDINATES = {
10637
- sw: [116.071044, 40.102099],
10638
- ne: [116.0734, 40.103901],
10644
+ sw: [-225e-6, -225e-6],
10645
+ ne: [0.000225, 0.000225],
10639
10646
  };
10640
10647
  /**
10641
10648
  * 缩放因子 - 将米转换为像素
@@ -13935,6 +13942,13 @@ exports.MapType = void 0;
13935
13942
  */
13936
13943
  MapType["GEO_FENCE"] = "geo-fence";
13937
13944
  })(exports.MapType || (exports.MapType = {}));
13945
+ var HoverType;
13946
+ (function (HoverType) {
13947
+ HoverType["Translate"] = "translate";
13948
+ HoverType["Scale"] = "scale";
13949
+ HoverType["Rotate"] = "rotate";
13950
+ HoverType["Delete"] = "delete";
13951
+ })(HoverType || (HoverType = {}));
13938
13952
 
13939
13953
  var RealTimeDataType;
13940
13954
  (function (RealTimeDataType) {
@@ -18086,6 +18100,8 @@ const OBSTACLE_STYLES = {
18086
18100
  hoverFillColor: 'rgba(255, 90, 0, 0.4)',
18087
18101
  lineWidth: DEFAULT_LINE_WIDTHS.OBSTACLE,
18088
18102
  opacity: DEFAULT_OPACITIES.FULL,
18103
+ disabledLineColor: 'rgba(255, 90, 0, 0.4)',
18104
+ disabledFillColor: 'rgba(255, 90, 0, 0.1)',
18089
18105
  };
18090
18106
  const CHARGING_PILE_STYLES = {
18091
18107
  lineColor: 'blue',
@@ -18567,6 +18583,38 @@ function calculateBoundaryBoundsCenter(mapData) {
18567
18583
  boundaryBounds.minY + (boundaryBounds.maxY - boundaryBounds.minY) / 2,
18568
18584
  ];
18569
18585
  }
18586
+ /**
18587
+ * 由中心点与物理尺寸(米)计算 GPS 边界 [lng, lat]
18588
+ */
18589
+ function computeGpsBoundsFromCenterAndSize(centerLng, centerLat, mapWidthMeters, mapHeightMeters) {
18590
+ const METERS_PER_DEGREE_LAT = EQUATORIAL_CIRCUMFERENCE / 360;
18591
+ const METERS_PER_DEGREE_LNG = METERS_PER_DEGREE_LAT * Math.cos((centerLat * Math.PI) / 180);
18592
+ const swLat = centerLat - mapHeightMeters / 2 / METERS_PER_DEGREE_LAT;
18593
+ const swLng = centerLng - mapWidthMeters / 2 / METERS_PER_DEGREE_LNG;
18594
+ const neLat = centerLat + mapHeightMeters / 2 / METERS_PER_DEGREE_LAT;
18595
+ const neLng = centerLng + mapWidthMeters / 2 / METERS_PER_DEGREE_LNG;
18596
+ return {
18597
+ sw: [swLng, swLat],
18598
+ ne: [neLng, neLat],
18599
+ };
18600
+ }
18601
+ /**
18602
+ * GPS 无效时:中心 (0,0) + JSON 的 map_width/map_height;无尺寸则用 DEFAULT_COORDINATES
18603
+ */
18604
+ function getFallbackGpsBounds(mapData) {
18605
+ const [centerLng, centerLat] = DEFAULT_GPS_CENTER;
18606
+ const hasMapSize = typeof mapData.map_width === 'number' &&
18607
+ typeof mapData.map_height === 'number' &&
18608
+ mapData.map_width > 0 &&
18609
+ mapData.map_height > 0;
18610
+ if (hasMapSize) {
18611
+ return computeGpsBoundsFromCenterAndSize(centerLng, centerLat, mapData.map_width, mapData.map_height);
18612
+ }
18613
+ return {
18614
+ sw: [...DEFAULT_COORDINATES.sw],
18615
+ ne: [...DEFAULT_COORDINATES.ne],
18616
+ };
18617
+ }
18570
18618
  /**
18571
18619
  * 检查GPS坐标是否有效
18572
18620
  */
@@ -18602,10 +18650,10 @@ function estimateGpsFromMapBounds(mapData) {
18602
18650
  const mapWidthMeters = maxXMeters - minXMeters;
18603
18651
  const mapHeightMeters = maxYMeters - minYMeters;
18604
18652
  // 凤凰岭的GPS坐标作为地图中心点
18605
- const centerLat = 40.103;
18606
- const centerLng = 116.072222;
18607
- // const centerLat = 40.03806686401367;
18608
- // const centerLng = 116.35540771484375;
18653
+ // const centerLat = 40.103;
18654
+ // const centerLng = 116.072222;
18655
+ const centerLat = 0;
18656
+ const centerLng = 0;
18609
18657
  // 精确的坐标转换常数
18610
18658
  // 1度纬度 = 约111,320米(在地球上任何地方都基本相同)
18611
18659
  // 1度经度 = 约111,320 * cos(纬度) 米(随纬度变化)
@@ -18671,39 +18719,44 @@ function calculateMapGpsCenter(mapData) {
18671
18719
  }
18672
18720
  }
18673
18721
  // 如果有西南角和东北角坐标,检查其有效性并计算中心点
18674
- if (mapData.sw_gps &&
18675
- mapData.ne_gps &&
18676
- mapData.sw_gps.length >= 2 &&
18677
- mapData.ne_gps.length >= 2) {
18678
- const swLat = mapData.sw_gps[1];
18679
- const swLng = mapData.sw_gps[0];
18680
- const neLat = mapData.ne_gps[1];
18681
- const neLng = mapData.ne_gps[0];
18682
- // 检查边界坐标的有效性
18683
- if (isValidGpsCoordinate(swLat, swLng) && isValidGpsCoordinate(neLat, neLng)) {
18684
- return {
18685
- lat: (swLat + neLat) / 2,
18686
- lng: (swLng + neLng) / 2,
18687
- };
18688
- }
18689
- else {
18690
- console.warn('sw_gps或ne_gps坐标无效:', {
18691
- sw: { lat: swLat, lng: swLng },
18692
- ne: { lat: neLat, lng: neLng },
18693
- });
18694
- }
18695
- }
18722
+ // if (
18723
+ // mapData.sw_gps &&
18724
+ // mapData.ne_gps &&
18725
+ // mapData.sw_gps.length >= 2 &&
18726
+ // mapData.ne_gps.length >= 2
18727
+ // ) {
18728
+ // const swLat = mapData.sw_gps[1];
18729
+ // const swLng = mapData.sw_gps[0];
18730
+ // const neLat = mapData.ne_gps[1];
18731
+ // const neLng = mapData.ne_gps[0];
18732
+ // // 检查边界坐标的有效性
18733
+ // if (isValidGpsCoordinate(swLat, swLng) && isValidGpsCoordinate(neLat, neLng)) {
18734
+ // return {
18735
+ // lat: (swLat + neLat) / 2,
18736
+ // lng: (swLng + neLng) / 2,
18737
+ // };
18738
+ // } else {
18739
+ // console.warn('sw_gps或ne_gps坐标无效:', {
18740
+ // sw: { lat: swLat, lng: swLng },
18741
+ // ne: { lat: neLat, lng: neLng },
18742
+ // });
18743
+ // }
18744
+ // }
18696
18745
  // 尝试从地图几何边界估算GPS坐标
18697
- const estimatedBounds = estimateGpsFromMapBounds(mapData);
18698
- if (estimatedBounds) {
18699
- // 从估算的边界计算中心点
18700
- const centerLat = (estimatedBounds.sw[1] + estimatedBounds.ne[1]) / 2;
18701
- const centerLng = (estimatedBounds.sw[0] + estimatedBounds.ne[0]) / 2;
18702
- return { lat: centerLat, lng: centerLng };
18703
- }
18746
+ // const estimatedBounds = estimateGpsFromMapBounds(mapData);
18747
+ // if (estimatedBounds) {
18748
+ // // 从估算的边界计算中心点
18749
+ // const centerLat = (estimatedBounds.sw[1] + estimatedBounds.ne[1]) / 2;
18750
+ // const centerLng = (estimatedBounds.sw[0] + estimatedBounds.ne[0]) / 2;
18751
+ // return { lat: centerLat, lng: centerLng };
18752
+ // }
18753
+ // return {
18754
+ // lat: 39.9042, // 北京纬度
18755
+ // lng: 116.4074, // 北京经度
18756
+ // };
18704
18757
  return {
18705
- lat: 39.9042, // 北京纬度
18706
- lng: 116.4074, // 北京经度
18758
+ lat: DEFAULT_GPS_CENTER[1],
18759
+ lng: DEFAULT_GPS_CENTER[0],
18707
18760
  };
18708
18761
  }
18709
18762
  // 旋转坐标点
@@ -18733,50 +18786,22 @@ const getValidGpsBounds = (mapData, rotation = 0) => {
18733
18786
  mapData.map_width > 0 &&
18734
18787
  mapData.map_height > 0) {
18735
18788
  const [centerLng, centerLat] = mapData.center_gps;
18736
- const mapWidthMeters = mapData.map_width;
18737
- const mapHeightMeters = mapData.map_height;
18738
- // 精确的坐标转换常数(基于 WGS84 标准 - GPS 国际标准)
18739
- // WGS84 赤道半径 = 6,378,137 米
18740
- // 地球赤道周长 = 2 × π × 6,378,137 ≈ 40,075,017 米
18741
- // 1度纬度 = 40075017 / 360 ≈ 111,319.49 米(在地球上任何地方都基本相同)
18742
- // 1度经度 = (40075017 / 360) * cos(纬度) 米(随纬度变化)
18743
- const METERS_PER_DEGREE_LAT = EQUATORIAL_CIRCUMFERENCE / 360;
18744
- const METERS_PER_DEGREE_LNG = METERS_PER_DEGREE_LAT * Math.cos((centerLat * Math.PI) / 180);
18745
- // 计算SW(西南角)GPS坐标:从中心点减去半个地图的宽度和高度
18746
- const swLat = centerLat - mapHeightMeters / 2 / METERS_PER_DEGREE_LAT;
18747
- const swLng = centerLng - mapWidthMeters / 2 / METERS_PER_DEGREE_LNG;
18748
- // 计算NE(东北角)GPS坐标:从中心点加上半个地图的宽度和高度
18749
- const neLat = centerLat + mapHeightMeters / 2 / METERS_PER_DEGREE_LAT;
18750
- const neLng = centerLng + mapWidthMeters / 2 / METERS_PER_DEGREE_LNG;
18751
- bounds = {
18752
- sw: [swLng, swLat],
18753
- ne: [neLng, neLat],
18754
- };
18755
- }
18756
- else if (isValidGpsCoordinate(mapData.sw_gps?.[1], mapData.sw_gps?.[0]) &&
18757
- isValidGpsCoordinate(mapData.ne_gps?.[1], mapData.ne_gps?.[0])) {
18758
- // 降级方案:使用地图数据中的GPS坐标
18759
- console.warn('center_gps 或 map_width/map_height 不可用,使用 sw_gps 和 ne_gps');
18760
- bounds = {
18761
- sw: mapData.sw_gps,
18762
- ne: mapData.ne_gps,
18763
- };
18764
- }
18789
+ bounds = computeGpsBoundsFromCenterAndSize(centerLng, centerLat, mapData.map_width, mapData.map_height);
18790
+ }
18791
+ // else if (
18792
+ // isValidGpsCoordinate(mapData.sw_gps?.[1], mapData.sw_gps?.[0]) &&
18793
+ // isValidGpsCoordinate(mapData.ne_gps?.[1], mapData.ne_gps?.[0])
18794
+ // ) {
18795
+ // // 降级方案:使用地图数据中的GPS坐标
18796
+ // console.warn('center_gps map_width/map_height 不可用,使用 sw_gps 和 ne_gps');
18797
+ // bounds = {
18798
+ // sw: mapData.sw_gps,
18799
+ // ne: mapData.ne_gps,
18800
+ // };
18801
+ // }
18765
18802
  else {
18766
- // 如果GPS坐标无效,尝试从地图几何数据估算
18767
- const { sw, ne } = estimateGpsFromMapBounds(mapData);
18768
- if (sw && ne) {
18769
- console.warn('GPS坐标无效,使用地图几何数据估算边界:', sw, ne);
18770
- bounds = {
18771
- sw: [sw[0], sw[1]],
18772
- ne: [ne[0], ne[1]],
18773
- };
18774
- }
18775
- else {
18776
- // 最后的fallback:使用默认坐标
18777
- console.warn('无法获取有效的GPS边界,使用默认坐标');
18778
- bounds = DEFAULT_COORDINATES;
18779
- }
18803
+ console.warn('无法获取有效的GPS边界,使用 (0,0) 中心 + map_width/map_height 或 DEFAULT_COORDINATES');
18804
+ bounds = getFallbackGpsBounds(mapData);
18780
18805
  }
18781
18806
  // 如果有旋转角度,计算旋转后的边界
18782
18807
  if (rotation !== 0) {
@@ -19209,7 +19234,7 @@ function getMowerImage(positonConfig, mowerIconConfig, modelType, hasEdger) {
19209
19234
  const mowerImage = mowerIconConfig?.mapImgUrl || getMowerImageByModal(model, hasEdger);
19210
19235
  const disabledImage = mowerIconConfig?.mapDisabledUrl || getDisabledMowerImageByModal(model);
19211
19236
  const noPositionImage = mowerIconConfig?.mapNoPositionUrl || getNoPositionMowerImageByModal(model);
19212
- const positonOutOfRange = isOutOfRange(positonConfig);
19237
+ // const positonOutOfRange = isOutOfRange(positonConfig);
19213
19238
  const positionValid = isInvalidPosition(positonConfig);
19214
19239
  const isOffLine = state === RobotStatus.DISCONNECTED;
19215
19240
  const isInPark = state === RobotStatus.CHARGING || state === RobotStatus.PARKED;
@@ -19219,7 +19244,7 @@ function getMowerImage(positonConfig, mowerIconConfig, modelType, hasEdger) {
19219
19244
  if (isInPark) {
19220
19245
  return mowerImage;
19221
19246
  }
19222
- if (positionValid || positonOutOfRange) {
19247
+ if (positionValid) {
19223
19248
  return noPositionImage;
19224
19249
  }
19225
19250
  else {
@@ -19243,12 +19268,6 @@ function isInvalidPosition(positonConfig) {
19243
19268
  isNoPosture(positonConfig.postureY) &&
19244
19269
  isNoPosture(positonConfig.postureTheta));
19245
19270
  }
19246
- function isOutOfRange(positonConfig) {
19247
- return (positonConfig.postureX != null &&
19248
- Math.abs(positonConfig.postureX) > 1000 &&
19249
- positonConfig.postureY != null &&
19250
- Math.abs(positonConfig.postureY) > 1000);
19251
- }
19252
19271
  function getMindistanceByModel(modelType) {
19253
19272
  if (isIModel(modelType)) {
19254
19273
  return I_MIN_DISTANCE;
@@ -19545,8 +19564,16 @@ function formatTime(date) {
19545
19564
  * @param date 目标日期
19546
19565
  * @returns 星期几的英文缩写
19547
19566
  */
19548
- function getWeekdayAbbr(date) {
19549
- const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
19567
+ function getWeekdayAbbr(date, locale) {
19568
+ const weekdays = [
19569
+ locale?.['map.renderer.weekday.sun'] || 'Sun',
19570
+ locale?.['map.renderer.weekday.mon'] || 'Mon',
19571
+ locale?.['map.renderer.weekday.tue'] || 'Tue',
19572
+ locale?.['map.renderer.weekday.wed'] || 'Wed',
19573
+ locale?.['map.renderer.weekday.thu'] || 'Thu',
19574
+ locale?.['map.renderer.weekday.fri'] || 'Fri',
19575
+ locale?.['map.renderer.weekday.sat'] || 'Sat'
19576
+ ];
19550
19577
  return weekdays[date.getDay()];
19551
19578
  }
19552
19579
  /**
@@ -19560,7 +19587,7 @@ function getWeekdayAbbr(date) {
19560
19587
  * @param timestamp 时间戳(秒)
19561
19588
  * @returns 格式化后的日期文本
19562
19589
  */
19563
- function formatBoundaryDateText(timestamp) {
19590
+ function formatBoundaryDateText(timestamp, locale) {
19564
19591
  if (!timestamp || timestamp <= 0) {
19565
19592
  return '-';
19566
19593
  }
@@ -19568,15 +19595,15 @@ function formatBoundaryDateText(timestamp) {
19568
19595
  const timeStr = formatTime(date);
19569
19596
  // 判断是否为今天
19570
19597
  if (isToday(date)) {
19571
- return `Today ${timeStr}`;
19598
+ return `${locale?.['map.renderer.today'] || 'Today'} ${timeStr}`;
19572
19599
  }
19573
19600
  // 判断是否为昨天
19574
19601
  if (isYesterday(date)) {
19575
- return `Yesterday ${timeStr}`;
19602
+ return `${locale?.['map.renderer.yesterday'] || 'Yesterday'} ${timeStr}`;
19576
19603
  }
19577
19604
  // 判断是否为本周内
19578
19605
  if (isThisWeek(date)) {
19579
- const weekdayAbbr = getWeekdayAbbr(date);
19606
+ const weekdayAbbr = getWeekdayAbbr(date, locale);
19580
19607
  return `${weekdayAbbr} ${timeStr}`;
19581
19608
  }
19582
19609
  // 其他情况显示完整日期
@@ -20068,12 +20095,12 @@ function styleInject(css, ref) {
20068
20095
  }
20069
20096
  }
20070
20097
 
20071
- var css_248z$7 = ".index-module_edit__-5VvX {\n position: absolute;\n top: 0px;\n left: 0px;\n width: 100%;\n height: 100%;\n pointer-events: auto;\n z-index: 1000;\n cursor: move;\n touch-action: none;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.index-module_edit__-5VvX .index-module_border__JdNLM {\n position: absolute;\n inset: -10px;\n border: 2px dashed rgb(241, 102, 41);\n border-radius: 2px;\n pointer-events: none;\n}\n.index-module_edit__-5VvX .index-module_drag__uvdPG {\n position: absolute;\n inset: -10px;\n border-radius: 2px;\n}\n.index-module_edit__-5VvX .index-module_rotate__H-KIZ {\n position: absolute;\n top: -20px;\n right: -20px;\n width: 20px;\n height: 20px;\n cursor: grab;\n z-index: 1001;\n pointer-events: auto;\n}\n.index-module_edit__-5VvX .index-module_move__mZF8s {\n position: absolute;\n bottom: -20px;\n left: -20px;\n width: 20px;\n height: 20px;\n cursor: move;\n z-index: 1001;\n pointer-events: auto;\n}";
20072
- var styles$7 = {"edit":"index-module_edit__-5VvX","border":"index-module_border__JdNLM","drag":"index-module_drag__uvdPG","rotate":"index-module_rotate__H-KIZ","move":"index-module_move__mZF8s"};
20073
- styleInject(css_248z$7);
20098
+ var css_248z$9 = ".index-module_edit__-5VvX {\n position: absolute;\n top: 0px;\n left: 0px;\n width: 100%;\n height: 100%;\n pointer-events: auto;\n z-index: 1000;\n cursor: move;\n touch-action: none;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.index-module_edit__-5VvX .index-module_border__JdNLM {\n position: absolute;\n inset: -10px;\n border: 2px dashed rgb(241, 102, 41);\n border-radius: 2px;\n pointer-events: none;\n}\n.index-module_edit__-5VvX .index-module_drag__uvdPG {\n position: absolute;\n inset: -10px;\n border-radius: 2px;\n}\n.index-module_edit__-5VvX .index-module_rotate__H-KIZ {\n position: absolute;\n top: -20px;\n right: -20px;\n width: 20px;\n height: 20px;\n cursor: grab;\n z-index: 1001;\n pointer-events: auto;\n}\n.index-module_edit__-5VvX .index-module_move__mZF8s {\n position: absolute;\n bottom: -20px;\n left: -20px;\n width: 20px;\n height: 20px;\n cursor: move;\n z-index: 1001;\n pointer-events: auto;\n}";
20099
+ var styles$9 = {"edit":"index-module_edit__-5VvX","border":"index-module_border__JdNLM","drag":"index-module_drag__uvdPG","rotate":"index-module_rotate__H-KIZ","move":"index-module_move__mZF8s"};
20100
+ styleInject(css_248z$9);
20074
20101
 
20075
20102
  const RotateHandle = ({ onRotateStart, isRotating }) => {
20076
- return (jsxRuntime.jsx("div", { className: styles$7.rotate, onMouseDown: (e) => onRotateStart(e), onTouchStart: (e) => onRotateStart(e), style: { cursor: isRotating ? 'grabbing' : 'grab' }, dangerouslySetInnerHTML: { __html: DEFAULT_ROTATE_ICON } }));
20103
+ return (jsxRuntime.jsx("div", { className: styles$9.rotate, onMouseDown: (e) => onRotateStart(e), onTouchStart: (e) => onRotateStart(e), style: { cursor: isRotating ? 'grabbing' : 'grab' }, dangerouslySetInnerHTML: { __html: DEFAULT_ROTATE_ICON } }));
20077
20104
  };
20078
20105
 
20079
20106
  /**
@@ -20150,7 +20177,7 @@ const DragHandle = ({ onDragStart, isDragging }) => {
20150
20177
  if (!isMobileDevice()) {
20151
20178
  return null;
20152
20179
  }
20153
- return (jsxRuntime.jsx("div", { className: styles$7.move, onMouseDown: (e) => {
20180
+ return (jsxRuntime.jsx("div", { className: styles$9.move, onMouseDown: (e) => {
20154
20181
  onDragStart(e);
20155
20182
  }, onTouchStart: (e) => onDragStart(e), onMouseUp: (e) => {
20156
20183
  // onDragEnd(e);
@@ -20182,7 +20209,7 @@ const MapDrag = ({ map: _map, dragCallbacks, onBoundaryLabelsCollapse, onTransfo
20182
20209
  if (!isDragMap) {
20183
20210
  return null;
20184
20211
  }
20185
- return (jsxRuntime.jsxs("div", { ref: containerRef, className: styles$7.edit, style: { cursor: isDragging ? 'grabbing' : 'move' }, children: [jsxRuntime.jsx("div", { className: styles$7.border }), canRotateMap && jsxRuntime.jsx(RotateHandle, { onRotateStart: handleRotateStart, isRotating: isRotating }), jsxRuntime.jsx("div", { className: styles$7.drag, onMouseDown: handleDragStartEvent, onTouchStart: handleDragStartEvent }), jsxRuntime.jsx(DragHandle, { onDragStart: handleDragStartEvent, isDragging: isDragging })] }));
20212
+ return (jsxRuntime.jsxs("div", { ref: containerRef, className: styles$9.edit, style: { cursor: isDragging ? 'grabbing' : 'move' }, children: [jsxRuntime.jsx("div", { className: styles$9.border }), canRotateMap && jsxRuntime.jsx(RotateHandle, { onRotateStart: handleRotateStart, isRotating: isRotating }), jsxRuntime.jsx("div", { className: styles$9.drag, onMouseDown: handleDragStartEvent, onTouchStart: handleDragStartEvent }), jsxRuntime.jsx(DragHandle, { onDragStart: handleDragStartEvent, isDragging: isDragging })] }));
20186
20213
  };
20187
20214
 
20188
20215
  const SvgEditContext = React.createContext({
@@ -22698,13 +22725,17 @@ const CommonContext = React.createContext({
22698
22725
  overlayScale: 1,
22699
22726
  showStraddleBoundaryBorder: true,
22700
22727
  googleMapStaticApiKey: '',
22728
+ googleMapStaticUrlSigningSecret: '',
22729
+ locale: {},
22730
+ signGoogleStaticMapUrl: undefined,
22701
22731
  onH5FirstSelectObstaclePoint: undefined,
22732
+ channelClipPathId: '',
22702
22733
  });
22703
22734
  const CommonContextProvider = CommonContext.Provider;
22704
22735
  const useCommonContext = () => React.useContext(CommonContext);
22705
22736
 
22706
22737
  const ChannelClipPath = React.memo(() => {
22707
- const { svgViewBox, sn } = useCommonContext();
22738
+ const { svgViewBox, channelClipPathId } = useCommonContext();
22708
22739
  const { svgElementDatas } = useSvgEditContext();
22709
22740
  const boundaryData = svgElementDatas?.[exports.DataType.BOUNDARY] || [];
22710
22741
  const [minX, minY, maxX, maxY] = React.useMemo(() => {
@@ -22883,7 +22914,7 @@ const ChannelClipPath = React.memo(() => {
22883
22914
  });
22884
22915
  return baseD;
22885
22916
  }, [boundaryData, minX, minY, maxX, maxY]);
22886
- return (jsxRuntime.jsx("defs", { children: jsxRuntime.jsx("clipPath", { id: `channel-clip-path-${sn}`, fillRule: "evenodd", children: jsxRuntime.jsx("path", { d: d }) }) }));
22917
+ return (jsxRuntime.jsx("defs", { children: jsxRuntime.jsx("clipPath", { id: channelClipPathId, fillRule: "evenodd", children: jsxRuntime.jsx("path", { d: d }) }) }));
22887
22918
  });
22888
22919
 
22889
22920
  /**
@@ -22945,9 +22976,9 @@ const VertexElement = React.memo(({ r, stroke, fill, ...props }) => {
22945
22976
  });
22946
22977
 
22947
22978
  var _path$1;
22948
- function _extends$8() { return _extends$8 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$8.apply(null, arguments); }
22979
+ function _extends$5() { return _extends$5 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$5.apply(null, arguments); }
22949
22980
  var SvgDelete = function SvgDelete(props) {
22950
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$8({
22981
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$5({
22951
22982
  xmlns: "http://www.w3.org/2000/svg",
22952
22983
  width: 16,
22953
22984
  height: 16,
@@ -23179,11 +23210,487 @@ const MapEditContext = React.createContext({
23179
23210
  const MapEditContextProvider = MapEditContext.Provider;
23180
23211
  const useMapEditContext = () => React.useContext(MapEditContext);
23181
23212
 
23213
+ /**
23214
+ * Google Maps Static API 数字签名
23215
+ * @see https://developers.google.com/maps/documentation/maps-static/digital-signature
23216
+ *
23217
+ * 使用纯 JS HMAC-SHA1,兼容 H5 WebView / 非 HTTPS 等无 crypto.subtle 的环境。
23218
+ */
23219
+ /// <reference lib="dom" />
23220
+ function removeWebSafe(encoded) {
23221
+ return encoded.replace(/-/g, '+').replace(/_/g, '/');
23222
+ }
23223
+ function makeWebSafe(encoded) {
23224
+ return encoded.replace(/\+/g, '-').replace(/\//g, '_');
23225
+ }
23226
+ function decodeUrlSigningSecret(secret) {
23227
+ let base64 = removeWebSafe(secret);
23228
+ const remainder = base64.length % 4;
23229
+ if (remainder) {
23230
+ base64 += '='.repeat(4 - remainder);
23231
+ }
23232
+ const binary = globalThis.atob(base64);
23233
+ const bytes = new Uint8Array(binary.length);
23234
+ for (let i = 0; i < binary.length; i++) {
23235
+ bytes[i] = binary.charCodeAt(i);
23236
+ }
23237
+ return bytes;
23238
+ }
23239
+ function bytesToWebSafeBase64(bytes) {
23240
+ let binary = '';
23241
+ for (let i = 0; i < bytes.length; i++) {
23242
+ binary += String.fromCharCode(bytes[i]);
23243
+ }
23244
+ return makeWebSafe(globalThis.btoa(binary));
23245
+ }
23246
+ function utf8Encode(text) {
23247
+ if (typeof globalThis.TextEncoder !== 'undefined') {
23248
+ return new globalThis.TextEncoder().encode(text);
23249
+ }
23250
+ // Static Map URL 仅含 ASCII;兼容无 TextEncoder 的旧 WebView / Jest jsdom
23251
+ const bytes = new Uint8Array(text.length);
23252
+ for (let i = 0; i < text.length; i++) {
23253
+ const code = text.charCodeAt(i);
23254
+ if (code > 127) {
23255
+ throw new Error('signGoogleStaticMapUrl: non-ASCII URL is not supported in this environment');
23256
+ }
23257
+ bytes[i] = code;
23258
+ }
23259
+ return bytes;
23260
+ }
23261
+ function concatBytes(...parts) {
23262
+ const total = parts.reduce((sum, part) => sum + part.length, 0);
23263
+ const out = new Uint8Array(total);
23264
+ let offset = 0;
23265
+ for (const part of parts) {
23266
+ out.set(part, offset);
23267
+ offset += part.length;
23268
+ }
23269
+ return out;
23270
+ }
23271
+ function leftRotate(value, amount) {
23272
+ return ((value << amount) | (value >>> (32 - amount))) >>> 0;
23273
+ }
23274
+ function sha1(message) {
23275
+ const originalLength = message.length;
23276
+ const bitLength = originalLength * 8;
23277
+ const paddedLength = Math.ceil((originalLength + 9) / 64) * 64;
23278
+ const padded = new Uint8Array(paddedLength);
23279
+ padded.set(message);
23280
+ padded[originalLength] = 0x80;
23281
+ const view = new DataView(padded.buffer);
23282
+ view.setUint32(paddedLength - 4, bitLength >>> 0, false);
23283
+ view.setUint32(paddedLength - 8, Math.floor(bitLength / 0x100000000), false);
23284
+ let h0 = 0x67452301;
23285
+ let h1 = 0xefcdab89;
23286
+ let h2 = 0x98badcfe;
23287
+ let h3 = 0x10325476;
23288
+ let h4 = 0xc3d2e1f0;
23289
+ const w = new Uint32Array(80);
23290
+ for (let offset = 0; offset < paddedLength; offset += 64) {
23291
+ for (let i = 0; i < 16; i++) {
23292
+ w[i] = view.getUint32(offset + i * 4, false);
23293
+ }
23294
+ for (let i = 16; i < 80; i++) {
23295
+ w[i] = leftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
23296
+ }
23297
+ let a = h0;
23298
+ let b = h1;
23299
+ let c = h2;
23300
+ let d = h3;
23301
+ let e = h4;
23302
+ for (let i = 0; i < 80; i++) {
23303
+ let f;
23304
+ let k;
23305
+ if (i < 20) {
23306
+ f = (b & c) | (~b & d);
23307
+ k = 0x5a827999;
23308
+ }
23309
+ else if (i < 40) {
23310
+ f = b ^ c ^ d;
23311
+ k = 0x6ed9eba1;
23312
+ }
23313
+ else if (i < 60) {
23314
+ f = (b & c) | (b & d) | (c & d);
23315
+ k = 0x8f1bbcdc;
23316
+ }
23317
+ else {
23318
+ f = b ^ c ^ d;
23319
+ k = 0xca62c1d6;
23320
+ }
23321
+ const temp = (leftRotate(a, 5) + f + e + k + w[i]) >>> 0;
23322
+ e = d;
23323
+ d = c;
23324
+ c = leftRotate(b, 30);
23325
+ b = a;
23326
+ a = temp;
23327
+ }
23328
+ h0 = (h0 + a) >>> 0;
23329
+ h1 = (h1 + b) >>> 0;
23330
+ h2 = (h2 + c) >>> 0;
23331
+ h3 = (h3 + d) >>> 0;
23332
+ h4 = (h4 + e) >>> 0;
23333
+ }
23334
+ const digest = new Uint8Array(20);
23335
+ const digestView = new DataView(digest.buffer);
23336
+ digestView.setUint32(0, h0, false);
23337
+ digestView.setUint32(4, h1, false);
23338
+ digestView.setUint32(8, h2, false);
23339
+ digestView.setUint32(12, h3, false);
23340
+ digestView.setUint32(16, h4, false);
23341
+ return digest;
23342
+ }
23343
+ function hmacSha1(key, message) {
23344
+ const blockSize = 64;
23345
+ let normalizedKey = key;
23346
+ if (normalizedKey.length > blockSize) {
23347
+ normalizedKey = sha1(normalizedKey);
23348
+ }
23349
+ const paddedKey = new Uint8Array(blockSize);
23350
+ paddedKey.set(normalizedKey);
23351
+ const innerPad = new Uint8Array(blockSize);
23352
+ const outerPad = new Uint8Array(blockSize);
23353
+ for (let i = 0; i < blockSize; i++) {
23354
+ innerPad[i] = paddedKey[i] ^ 0x36;
23355
+ outerPad[i] = paddedKey[i] ^ 0x5c;
23356
+ }
23357
+ return sha1(concatBytes(outerPad, sha1(concatBytes(innerPad, message))));
23358
+ }
23359
+ function signUrlToSign(urlToSign, urlSigningSecret) {
23360
+ const keyBytes = decodeUrlSigningSecret(urlSigningSecret);
23361
+ const signatureBytes = hmacSha1(keyBytes, utf8Encode(urlToSign));
23362
+ return bytesToWebSafeBase64(signatureBytes);
23363
+ }
23364
+ /**
23365
+ * 对 Maps Static API 请求 URL 追加 `signature` 参数。
23366
+ * 签名内容为 pathname + search(不含 scheme/host),算法 HMAC-SHA1 + URL-safe Base64。
23367
+ *
23368
+ * @param inputUrl 完整未签名 URL(须已包含 key 等 query 参数)
23369
+ * @param urlSigningSecret Cloud Console「URL 签名密钥」(URL-safe Base64)
23370
+ */
23371
+ function signGoogleStaticMapUrlSync(inputUrl, urlSigningSecret) {
23372
+ if (!inputUrl || !urlSigningSecret) {
23373
+ throw new Error('signGoogleStaticMapUrl: inputUrl and urlSigningSecret are required');
23374
+ }
23375
+ const url = new globalThis.URL(inputUrl);
23376
+ url.searchParams.delete('signature');
23377
+ const urlToSign = `${url.pathname}${url.search}`;
23378
+ const signature = signUrlToSign(urlToSign, urlSigningSecret);
23379
+ return `${url.origin}${urlToSign}&signature=${signature}`;
23380
+ }
23381
+ /** 异步包装,便于与宿主 signGoogleStaticMapUrl 回调统一 Promise 接口 */
23382
+ async function signGoogleStaticMapUrl(inputUrl, urlSigningSecret) {
23383
+ return signGoogleStaticMapUrlSync(inputUrl, urlSigningSecret);
23384
+ }
23385
+
23182
23386
  var chargingPileImage = "data:image/png;base64,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";
23183
23387
 
23184
- const useChargingPile = ({ viewBox, rotation = 0 }) => {
23388
+ const DEFAULT_SCREEN_PADDING = 50;
23389
+ function normalizeScreenPadding(padding, defaultValue = DEFAULT_SCREEN_PADDING) {
23390
+ if (padding == null) {
23391
+ return {
23392
+ top: defaultValue,
23393
+ right: defaultValue,
23394
+ bottom: defaultValue,
23395
+ left: defaultValue,
23396
+ };
23397
+ }
23398
+ if (typeof padding === 'number') {
23399
+ return { top: padding, right: padding, bottom: padding, left: padding };
23400
+ }
23401
+ if (padding.length === 2) {
23402
+ const [vertical, horizontal] = padding;
23403
+ return { top: vertical, right: horizontal, bottom: vertical, left: horizontal };
23404
+ }
23405
+ if (padding.length === 3) {
23406
+ const [top, horizontal, bottom] = padding;
23407
+ return { top, right: horizontal, bottom, left: horizontal };
23408
+ }
23409
+ const [top, right, bottom, left] = padding;
23410
+ return { top, right, bottom, left };
23411
+ }
23412
+ function isZeroScreenPadding(padding) {
23413
+ return padding.top === 0 && padding.right === 0 && padding.bottom === 0 && padding.left === 0;
23414
+ }
23415
+
23416
+ /** 拖拽超过该像素阈值时视为平移,而非点击 */
23417
+ const PAN_CLICK_THRESHOLD_PX = 5;
23418
+ /**
23419
+ * 根据地图包围盒 + padding 计算「内容 viewBox」
23420
+ * 用于 ChannelClipPath 裁剪,padding 为 SVG 坐标系单位
23421
+ */
23422
+ function boundsToContentViewBox(bounds, padding) {
23423
+ if (!Number.isFinite(bounds.minX) || !Number.isFinite(bounds.maxX)) {
23424
+ return { x: 0, y: 0, width: 100, height: 100 };
23425
+ }
23426
+ const width = Math.max(bounds.maxX - bounds.minX + padding * 2, 1);
23427
+ const height = Math.max(bounds.maxY - bounds.minY + padding * 2, 1);
23428
+ return {
23429
+ x: bounds.minX - padding,
23430
+ y: bounds.minY - padding,
23431
+ width,
23432
+ height,
23433
+ };
23434
+ }
23435
+ /**
23436
+ * 在固定容器宽高比下,计算 fit 的 viewBox(等价于 SVG preserveAspectRatio="meet")
23437
+ *
23438
+ * 若内容与容器比例不一致,会在 SVG 坐标系中扩展 viewBox 的宽或高,形成 letterbox 区域
23439
+ */
23440
+ function computeFitViewBoxToContainer(content, containerWidth, containerHeight) {
23441
+ const containerAspect = containerWidth / containerHeight;
23442
+ const contentAspect = content.width / content.height;
23443
+ // 内容更「宽」:扩展 viewBox 高度,上下留白
23444
+ if (contentAspect > containerAspect) {
23445
+ const height = content.width / containerAspect;
23446
+ return {
23447
+ x: content.x,
23448
+ y: content.y - (height - content.height) / 2,
23449
+ width: content.width,
23450
+ height,
23451
+ };
23452
+ }
23453
+ // 内容更「高」:扩展 viewBox 宽度,左右留白
23454
+ const width = content.height * containerAspect;
23455
+ return {
23456
+ x: content.x - (width - content.width) / 2,
23457
+ y: content.y,
23458
+ width,
23459
+ height: content.height,
23460
+ };
23461
+ }
23462
+ /** 地图内容在屏幕坐标系下的包围盒(meet 渲染,未考虑旋转) */
23463
+ function getContentScreenBounds(content, viewBox, containerWidth, containerHeight) {
23464
+ const { x: offsetX, y: offsetY, scale } = getViewBoxMeetOffset(viewBox, containerWidth, containerHeight);
23465
+ return {
23466
+ left: offsetX + (content.x - viewBox.x) * scale,
23467
+ top: offsetY + (content.y - viewBox.y) * scale,
23468
+ right: offsetX + (content.x + content.width - viewBox.x) * scale,
23469
+ bottom: offsetY + (content.y + content.height - viewBox.y) * scale,
23470
+ };
23471
+ }
23472
+ function contentFitsInScreenPadding(content, viewBox, pad, containerWidth, containerHeight) {
23473
+ const bounds = getContentScreenBounds(content, viewBox, containerWidth, containerHeight);
23474
+ return (bounds.left >= pad.left &&
23475
+ bounds.top >= pad.top &&
23476
+ bounds.right <= containerWidth - pad.right &&
23477
+ bounds.bottom <= containerHeight - pad.bottom);
23478
+ }
23479
+ function expandViewBoxFromCenter(viewBox, factor) {
23480
+ const centerX = viewBox.x + viewBox.width / 2;
23481
+ const centerY = viewBox.y + viewBox.height / 2;
23482
+ const width = viewBox.width * factor;
23483
+ const height = viewBox.height * factor;
23484
+ return {
23485
+ x: centerX - width / 2,
23486
+ y: centerY - height / 2,
23487
+ width,
23488
+ height,
23489
+ };
23490
+ }
23491
+ /** 将 viewBox 中心对齐到 padding 内矩形中心(增大 top/left → 内容向对应方向避让) */
23492
+ function applyViewBoxPaddingOffset(viewBox, pad, containerWidth, containerHeight) {
23493
+ const offsetX = ((pad.right - pad.left) / 2) * (viewBox.width / containerWidth);
23494
+ const offsetY = ((pad.bottom - pad.top) / 2) * (viewBox.height / containerHeight);
23495
+ return {
23496
+ ...viewBox,
23497
+ x: viewBox.x + offsetX,
23498
+ y: viewBox.y + offsetY,
23499
+ };
23500
+ }
23501
+ function buildPaddedFitViewBox(baseFit, pad, containerWidth, containerHeight, zoomOutFactor) {
23502
+ const expanded = expandViewBoxFromCenter(baseFit, zoomOutFactor);
23503
+ return applyViewBoxPaddingOffset(expanded, pad, containerWidth, containerHeight);
23504
+ }
23505
+ function findMinimumZoomOutForPadding(content, baseFit, pad, containerWidth, containerHeight) {
23506
+ const fits = (zoomOutFactor) => contentFitsInScreenPadding(content, buildPaddedFitViewBox(baseFit, pad, containerWidth, containerHeight, zoomOutFactor), pad, containerWidth, containerHeight);
23507
+ if (fits(1)) {
23508
+ return 1;
23509
+ }
23510
+ const innerWidth = Math.max(containerWidth - pad.left - pad.right, 1);
23511
+ const innerHeight = Math.max(containerHeight - pad.top - pad.bottom, 1);
23512
+ const legacyScale = Math.min(innerWidth / containerWidth, innerHeight / containerHeight);
23513
+ let hi = legacyScale > 0 && legacyScale < 1 ? 1 / legacyScale : 2;
23514
+ while (!fits(hi) && hi < 1024) {
23515
+ hi *= 2;
23516
+ }
23517
+ let lo = 1;
23518
+ for (let i = 0; i < 32; i++) {
23519
+ const mid = (lo + hi) / 2;
23520
+ if (fits(mid)) {
23521
+ hi = mid;
23522
+ }
23523
+ else {
23524
+ lo = mid;
23525
+ }
23526
+ }
23527
+ return hi;
23528
+ }
23529
+ /**
23530
+ * 计算默认 fit viewBox
23531
+ *
23532
+ * @param screenPadding 容器四边留出的屏幕边距(px),语法同 CSS padding
23533
+ */
23534
+ function computeFitViewBox(content, containerWidth, containerHeight, screenPadding = 0) {
23535
+ const baseFit = computeFitViewBoxToContainer(content, containerWidth, containerHeight);
23536
+ const pad = normalizeScreenPadding(screenPadding, 0);
23537
+ if (isZeroScreenPadding(pad)) {
23538
+ return baseFit;
23539
+ }
23540
+ const zoomOutFactor = findMinimumZoomOutForPadding(content, baseFit, pad, containerWidth, containerHeight);
23541
+ return buildPaddedFitViewBox(baseFit, pad, containerWidth, containerHeight, zoomOutFactor);
23542
+ }
23543
+ function viewBoxToString(viewBox) {
23544
+ return `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`;
23545
+ }
23546
+ /** SVG preserveAspectRatio="xMidYMid meet" 下的实际缩放比(地图单位 → 屏幕 px) */
23547
+ function getViewBoxMeetScale(viewBox, containerWidth, containerHeight) {
23548
+ return Math.min(containerWidth / viewBox.width, containerHeight / viewBox.height);
23549
+ }
23550
+ /** meet 模式下 SVG 内容相对容器左上角的 letterbox 偏移及缩放比 */
23551
+ function getViewBoxMeetOffset(viewBox, containerWidth, containerHeight) {
23552
+ const scale = getViewBoxMeetScale(viewBox, containerWidth, containerHeight);
23553
+ return {
23554
+ x: (containerWidth - viewBox.width * scale) / 2,
23555
+ y: (containerHeight - viewBox.height * scale) / 2,
23556
+ scale,
23557
+ };
23558
+ }
23559
+ /** 将屏幕坐标绕指定中心旋转(度,顺时针) */
23560
+ function rotatePointAround(x, y, centerX, centerY, rotationDeg) {
23561
+ if (rotationDeg === 0)
23562
+ return { x, y };
23563
+ const rad = (rotationDeg * Math.PI) / 180;
23564
+ const cos = Math.cos(rad);
23565
+ const sin = Math.sin(rad);
23566
+ const dx = x - centerX;
23567
+ const dy = y - centerY;
23568
+ return {
23569
+ x: centerX + dx * cos - dy * sin,
23570
+ y: centerY + dx * sin + dy * cos,
23571
+ };
23572
+ }
23573
+ /** 在地图坐标系内绕 viewBox 中心旋转(与 SVG transform="rotate(deg, cx, cy)" 一致) */
23574
+ function rotateMapPointAroundViewBoxCenter(mapX, mapY, viewBox, rotationDeg) {
23575
+ if (rotationDeg === 0)
23576
+ return { x: mapX, y: mapY };
23577
+ const cx = viewBox.x + viewBox.width / 2;
23578
+ const cy = viewBox.y + viewBox.height / 2;
23579
+ return rotatePointAround(mapX, mapY, cx, cy, rotationDeg);
23580
+ }
23581
+ function getViewBoxCenter(viewBox) {
23582
+ return {
23583
+ x: viewBox.x + viewBox.width / 2,
23584
+ y: viewBox.y + viewBox.height / 2,
23585
+ };
23586
+ }
23587
+ /**
23588
+ * 将地图坐标转换为 HTML overlay 的定位百分比
23589
+ * 需与 SVG viewBox + preserveAspectRatio="meet" 的渲染规则一致
23590
+ *
23591
+ * @returns leftPct / topPct — 相对容器宽高的百分比,配合 translate(-50%, -50%) 居中
23592
+ */
23593
+ function mapPointToContainerPercent(mapX, mapY, viewBox, containerWidth, containerHeight, mapRotationDeg = 0) {
23594
+ const { x: offsetX, y: offsetY, scale } = getViewBoxMeetOffset(viewBox, containerWidth, containerHeight);
23595
+ const { x: projectedX, y: projectedY } = rotateMapPointAroundViewBoxCenter(mapX, mapY, viewBox, mapRotationDeg);
23596
+ const screenX = offsetX + (projectedX - viewBox.x) * scale;
23597
+ const screenY = offsetY + (projectedY - viewBox.y) * scale;
23598
+ return {
23599
+ leftPct: (screenX / containerWidth) * 100,
23600
+ topPct: (screenY / containerHeight) * 100,
23601
+ };
23602
+ }
23603
+ /** 计算多边形包围盒在当前视口下的屏幕宽度(px) */
23604
+ function computePolygonScreenWidthPx(points, viewBox, containerWidth, containerHeight) {
23605
+ const valid = points.filter((point) => point.length >= 2);
23606
+ if (valid.length === 0)
23607
+ return 0;
23608
+ let minX = Infinity;
23609
+ let maxX = -Infinity;
23610
+ for (const [x] of valid) {
23611
+ minX = Math.min(minX, x);
23612
+ maxX = Math.max(maxX, x);
23613
+ }
23614
+ const mapWidth = maxX - minX;
23615
+ if (!Number.isFinite(mapWidth) || mapWidth <= 0)
23616
+ return 0;
23617
+ const scale = getViewBoxMeetScale(viewBox, containerWidth, containerHeight);
23618
+ return mapWidth * scale;
23619
+ }
23620
+ function scaleStrokeWidthForViewBox(viewBox, options) {
23621
+ const { containerWidth, containerHeight, targetScreenStrokePx = 2 } = options;
23622
+ const meetScale = getViewBoxMeetScale(viewBox, containerWidth, containerHeight);
23623
+ if (meetScale <= 0 || !Number.isFinite(targetScreenStrokePx)) {
23624
+ return targetScreenStrokePx;
23625
+ }
23626
+ return targetScreenStrokePx / meetScale;
23627
+ }
23628
+ /** 屏幕拖拽像素 → viewBox 坐标增量(与 mapPointToContainerPercent 互逆,供 applyPan 使用) */
23629
+ function containerDeltaToViewBoxDelta(deltaXPx, deltaYPx, viewBox, containerWidth, containerHeight, mapRotationDeg = 0) {
23630
+ const scale = getViewBoxMeetScale(viewBox, containerWidth, containerHeight);
23631
+ if (mapRotationDeg === 0) {
23632
+ return {
23633
+ dx: deltaXPx / scale,
23634
+ dy: deltaYPx / scale,
23635
+ };
23636
+ }
23637
+ // 地图在屏幕上旋转了 mapRotationDeg,将屏幕位移矢量逆旋转后再换算为 viewBox 增量
23638
+ const rad = (-mapRotationDeg * Math.PI) / 180;
23639
+ const cos = Math.cos(rad);
23640
+ const sin = Math.sin(rad);
23641
+ const mapDeltaXPx = deltaXPx * cos - deltaYPx * sin;
23642
+ const mapDeltaYPx = deltaXPx * sin + deltaYPx * cos;
23643
+ return {
23644
+ dx: mapDeltaXPx / scale,
23645
+ dy: mapDeltaYPx / scale,
23646
+ };
23647
+ }
23648
+ /**
23649
+ * 限制 viewBox 宽度在 zoom 范围内
23650
+ *
23651
+ * zoom = fitViewBox.width / viewBox.width
23652
+ * - zoom 越大 → viewBox 越小 → 放得越大
23653
+ * - minZoom=0.5 → 最多缩小到 fit 的 2 倍视野(viewBox.width 最大为 fit/0.5)
23654
+ * - maxZoom=4 → 最多放大 4 倍(viewBox.width 最小为 fit/4)
23655
+ */
23656
+ function clampViewBoxWidth(width, fitViewBox, minZoom, maxZoom) {
23657
+ const minWidth = fitViewBox.width / maxZoom;
23658
+ const maxWidth = fitViewBox.width / minZoom;
23659
+ return Math.min(maxWidth, Math.max(minWidth, width));
23660
+ }
23661
+ /**
23662
+ * 以屏幕 focal 点为中心缩放 viewBox
23663
+ * 将容器像素坐标转换为 SVG 坐标,保持焦点下的地图点位置不变
23664
+ *
23665
+ * @param viewBox 缩放前的 viewBox
23666
+ * @param newWidth 缩放后的 viewBox 宽度(高度按容器宽高比推导)
23667
+ * @param containerWidth 容器宽度(px)
23668
+ * @param containerHeight 容器高度(px)
23669
+ * @param focalX 焦点相对容器左上角的 X(px)
23670
+ * @param focalY 焦点相对容器左上角的 Y(px)
23671
+ */
23672
+ function resizeViewBoxAroundPoint(viewBox, newWidth, containerWidth, containerHeight, focalX, focalY) {
23673
+ const containerAspect = containerWidth / containerHeight;
23674
+ const newHeight = newWidth / containerAspect;
23675
+ const relX = focalX / containerWidth;
23676
+ const relY = focalY / containerHeight;
23677
+ const svgFocalX = viewBox.x + relX * viewBox.width;
23678
+ const svgFocalY = viewBox.y + relY * viewBox.height;
23679
+ return {
23680
+ x: svgFocalX - relX * newWidth,
23681
+ y: svgFocalY - relY * newHeight,
23682
+ width: newWidth,
23683
+ height: newHeight,
23684
+ };
23685
+ }
23686
+
23687
+ const useChargingPile = ({ viewBox, rotation = 0, viewRotationMode = 'overlay-parent', containerWidth, containerHeight, }) => {
23185
23688
  const { svgElementDatas } = useSvgEditContext();
23186
23689
  const { sn } = useCommonContext();
23690
+ const useMeetPositioning = containerWidth != null &&
23691
+ containerHeight != null &&
23692
+ containerWidth > 0 &&
23693
+ containerHeight > 0;
23187
23694
  const items = React.useMemo(() => {
23188
23695
  if (!svgElementDatas || !viewBox)
23189
23696
  return [];
@@ -23199,13 +23706,26 @@ const useChargingPile = ({ viewBox, rotation = 0 }) => {
23199
23706
  const direction = element?.direction || 0;
23200
23707
  const angle = (direction * 180) / Math.PI;
23201
23708
  const rotationDegree = 270 - angle; // 正东是正方向,需要处理下
23202
- const actualRotation = rotationDegree - rotation;
23203
- const relX = (center[0] - viewBox.x) / viewBox.width;
23204
- const relY = (center[1] - viewBox.y) / viewBox.height;
23205
- if (isFinite(relX) && isFinite(relY)) {
23709
+ const actualRotation = viewRotationMode === 'svg-transform'
23710
+ ? rotationDegree
23711
+ : rotationDegree - rotation;
23712
+ let leftPct;
23713
+ let topPct;
23714
+ if (useMeetPositioning) {
23715
+ const position = mapPointToContainerPercent(center[0], center[1], viewBox, containerWidth, containerHeight, viewRotationMode === 'svg-transform' ? rotation : 0);
23716
+ leftPct = position.leftPct;
23717
+ topPct = position.topPct;
23718
+ }
23719
+ else {
23720
+ const relX = (center[0] - viewBox.x) / viewBox.width;
23721
+ const relY = (center[1] - viewBox.y) / viewBox.height;
23722
+ leftPct = relX * 100;
23723
+ topPct = relY * 100;
23724
+ }
23725
+ if (isFinite(leftPct) && isFinite(topPct)) {
23206
23726
  results.push({
23207
- leftPct: relX * 100,
23208
- topPct: relY * 100,
23727
+ leftPct,
23728
+ topPct,
23209
23729
  size,
23210
23730
  rotateDeg: actualRotation,
23211
23731
  imageSrc: chargingPileImage,
@@ -23213,12 +23733,30 @@ const useChargingPile = ({ viewBox, rotation = 0 }) => {
23213
23733
  }
23214
23734
  }
23215
23735
  return results;
23216
- }, [svgElementDatas, viewBox?.x, viewBox?.y, viewBox?.width, viewBox?.height, rotation, sn]);
23736
+ }, [
23737
+ svgElementDatas,
23738
+ viewBox?.x,
23739
+ viewBox?.y,
23740
+ viewBox?.width,
23741
+ viewBox?.height,
23742
+ rotation,
23743
+ viewRotationMode,
23744
+ sn,
23745
+ useMeetPositioning,
23746
+ containerWidth,
23747
+ containerHeight,
23748
+ ]);
23217
23749
  return items;
23218
23750
  };
23219
23751
 
23220
- const CharginPile = React.memo(({ viewBox, rotation = 0, sizeScale = 1, sizeInSvgUnits = false, isHighlight = true, }) => {
23221
- const items = useChargingPile({ viewBox: viewBox || null, rotation });
23752
+ const CharginPile = React.memo(({ viewBox, rotation = 0, viewRotationMode = 'overlay-parent', containerWidth, containerHeight, sizeScale = 1, sizeInSvgUnits = false, minDisplaySizePx, isHighlight = true, }) => {
23753
+ const items = useChargingPile({
23754
+ viewBox: viewBox || null,
23755
+ rotation,
23756
+ viewRotationMode,
23757
+ containerWidth,
23758
+ containerHeight,
23759
+ });
23222
23760
  const containerStyle = React.useMemo(() => ({
23223
23761
  position: 'absolute',
23224
23762
  top: 0,
@@ -23229,13 +23767,18 @@ const CharginPile = React.memo(({ viewBox, rotation = 0, sizeScale = 1, sizeInSv
23229
23767
  zIndex: 750,
23230
23768
  opacity: isHighlight ? SvgMapOpacity.HIGHLIGHT : SvgMapOpacity.UN_HIGHLIGHT,
23231
23769
  }), [isHighlight]);
23770
+ const useMinPx = Boolean(sizeInSvgUnits && viewBox && minDisplaySizePx != null && minDisplaySizePx > 0);
23232
23771
  return (jsxRuntime.jsx("div", { style: containerStyle, children: items?.map((item, idx) => (jsxRuntime.jsx("div", { className: "charging-pile", style: {
23233
23772
  position: 'absolute',
23234
23773
  width: sizeInSvgUnits && viewBox
23235
- ? `${(item.size / Math.max(viewBox.width, 1)) * 100 * sizeScale}%`
23774
+ ? useMinPx
23775
+ ? `max(${minDisplaySizePx}px, ${(item.size / Math.max(viewBox.width, 1)) * 100 * sizeScale}%)`
23776
+ : `${(item.size / Math.max(viewBox.width, 1)) * 100 * sizeScale}%`
23236
23777
  : item.size * sizeScale,
23237
23778
  height: sizeInSvgUnits && viewBox
23238
- ? `${(item.size / Math.max(viewBox.height, 1)) * 100 * sizeScale}%`
23779
+ ? useMinPx
23780
+ ? `max(${minDisplaySizePx}px, ${(item.size / Math.max(viewBox.height, 1)) * 100 * sizeScale}%)`
23781
+ : `${(item.size / Math.max(viewBox.height, 1)) * 100 * sizeScale}%`
23239
23782
  : item.size * sizeScale,
23240
23783
  left: `${item.leftPct}%`,
23241
23784
  top: `${item.topPct}%`,
@@ -23248,8 +23791,10 @@ const CharginPile = React.memo(({ viewBox, rotation = 0, sizeScale = 1, sizeInSv
23248
23791
  const MAX_STATIC_SIZE = 640;
23249
23792
  const MAX_STATIC_MAP_RETRY = 3;
23250
23793
  const STATIC_MAP_RETRY_COOLDOWN_MS = 5000;
23794
+ /** 放大镜内充电桩屏幕像素下限,避免过小导致通道与桩视觉错位 */
23795
+ const MAGNIFIER_CHARGING_PILE_MIN_DISPLAY_PX = 20;
23251
23796
  const Magnifier = ({ visible, dragState, polygonPoints, fillColor = 'rgba(255, 122, 51, 0.1)', strokeColor = 'rgba(255, 122, 51, 1)', strokeWidth = 2, zoom = 3, size = 200, }) => {
23252
- const { svgViewBox, mapRef, googleMapStaticApiKey, drag } = useCommonContext();
23797
+ const { svgViewBox, mapRef, googleMapStaticApiKey, googleMapStaticUrlSigningSecret, signGoogleStaticMapUrl: signGoogleStaticMapUrl$1, drag, } = useCommonContext();
23253
23798
  const { svgElementDatas, svgRef } = useSvgEditContext();
23254
23799
  const { editMapInfo } = useMapEditContext();
23255
23800
  const [magnifierPosition, setMagnifierPosition] = React.useState({ x: 0, y: 0 });
@@ -23337,76 +23882,107 @@ const Magnifier = ({ visible, dragState, polygonPoints, fillColor = 'rgba(255, 1
23337
23882
  const svgPoint = svgRef.createSVGPoint();
23338
23883
  // 只要有 key 就尝试 static api;请求失败时不截图,直接回退灰色背景
23339
23884
  if (googleMapStaticApiKey) {
23340
- // console.log('[Magnifier] Background source: static api viewport snapshot');
23341
- const mapBounds = mapRef.getBounds?.();
23342
- if (!mapBounds) {
23343
- // console.log('[Magnifier] Skip static map effect: mapBounds missing');
23344
- setMapImageFrame(null);
23345
- return;
23346
- }
23347
- const screenToSvg = (screenX, screenY) => {
23348
- svgPoint.x = screenX;
23349
- svgPoint.y = screenY;
23350
- const p = svgPoint.matrixTransform(inverseCtm);
23351
- return { x: p.x, y: p.y };
23885
+ let cancelled = false;
23886
+ (async () => {
23887
+ // console.log('[Magnifier] Background source: static api viewport snapshot');
23888
+ const mapBounds = mapRef.getBounds?.();
23889
+ if (!mapBounds) {
23890
+ // console.log('[Magnifier] Skip static map effect: mapBounds missing');
23891
+ if (!cancelled) {
23892
+ setMapImageFrame(null);
23893
+ }
23894
+ return;
23895
+ }
23896
+ const screenToSvg = (screenX, screenY) => {
23897
+ svgPoint.x = screenX;
23898
+ svgPoint.y = screenY;
23899
+ const p = svgPoint.matrixTransform(inverseCtm);
23900
+ return { x: p.x, y: p.y };
23901
+ };
23902
+ // 使用当前地图视口矩形四角,将屏幕坐标映射到 SVG 坐标,确保底图与主视口对齐
23903
+ const topLeftSvg = screenToSvg(mapRect.left, mapRect.top);
23904
+ const topRightSvg = screenToSvg(mapRect.right, mapRect.top);
23905
+ const bottomLeftSvg = screenToSvg(mapRect.left, mapRect.bottom);
23906
+ if (!topLeftSvg || !topRightSvg || !bottomLeftSvg) {
23907
+ if (!cancelled) {
23908
+ setMapImageFrame(null);
23909
+ }
23910
+ return;
23911
+ }
23912
+ // 把当前主地图可视区域尺寸,按比例压到 Static API 允许的最大尺寸以内(受 Static API 640 上限约束)),同时不改变宽高比。
23913
+ const mapWidth = Math.max(1, Math.round(mapRect.width));
23914
+ const mapHeight = Math.max(1, Math.round(mapRect.height));
23915
+ const shrinkRatio = Math.min(1, MAX_STATIC_SIZE / Math.max(mapWidth, mapHeight));
23916
+ const staticWidth = Math.max(1, Math.round(mapWidth * shrinkRatio));
23917
+ const staticHeight = Math.max(1, Math.round(mapHeight * shrinkRatio));
23918
+ const center = mapBounds.getCenter();
23919
+ const currentZoom = mapRef.getZoom() || 20;
23920
+ // 尺寸缩小后同步补偿 zoom,保持与当前主图一致的地理覆盖范围
23921
+ const zoomCompensation = Math.log2(shrinkRatio);
23922
+ const adjustedZoom = currentZoom + zoomCompensation;
23923
+ const staticZoom = Math.max(0, Math.min(Math.floor(adjustedZoom), 22));
23924
+ const unsignedStaticMapUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${center.lat()},${center.lng()}&zoom=${staticZoom}&size=${staticWidth}x${staticHeight}&maptype=satellite&key=${googleMapStaticApiKey}`;
23925
+ // 可选:对 Static Map URL 做 Google 数字签名(宿主回调优先,其次客户端 signing secret)
23926
+ let staticMapUrl = unsignedStaticMapUrl;
23927
+ try {
23928
+ if (signGoogleStaticMapUrl$1) {
23929
+ staticMapUrl = await Promise.resolve(signGoogleStaticMapUrl$1(unsignedStaticMapUrl));
23930
+ }
23931
+ else if (googleMapStaticUrlSigningSecret) {
23932
+ staticMapUrl = await signGoogleStaticMapUrl(unsignedStaticMapUrl, googleMapStaticUrlSigningSecret);
23933
+ console.log('[Magnifier] staticMapUrl', staticMapUrl);
23934
+ }
23935
+ }
23936
+ catch (error) {
23937
+ console.warn('[Magnifier] Static map URL signing failed', error);
23938
+ if (!cancelled) {
23939
+ setMapImageFrame(null);
23940
+ }
23941
+ return;
23942
+ }
23943
+ // 通过三角点推导静态图在 SVG 中的宽高和旋转角
23944
+ // Math.hypot(x, y) 等价于 sqrt(x*x + y*y),计算两点之间的距离
23945
+ const width = Math.hypot(topRightSvg.x - topLeftSvg.x, topRightSvg.y - topLeftSvg.y);
23946
+ const height = Math.hypot(bottomLeftSvg.x - topLeftSvg.x, bottomLeftSvg.y - topLeftSvg.y);
23947
+ // Math.atan2(y, x) 计算反正切值,返回 [-π, π] 弧度,*180/π 转换为角度
23948
+ const rotationDeg = (Math.atan2(topRightSvg.y - topLeftSvg.y, topRightSvg.x - topLeftSvg.x) * 180) / Math.PI;
23949
+ // Static API zoom 只能取整;额外补偿小数部分,避免底图视觉偏小
23950
+ // 在有旋转的坐标系里,做“以中心为锚点”的等比放大补偿。
23951
+ const fractionalZoomScale = Math.pow(2, adjustedZoom - staticZoom); //算出“还差多少缩放比例”。例子:adjustedZoom=19.6,staticZoom=19,差 0.6,比例是 2^0.6 ≈ 1.515。
23952
+ //把原始 width/height 乘这个比例,得到补偿后的尺寸。
23953
+ const compensatedWidth = width * fractionalZoomScale;
23954
+ const compensatedHeight = height * fractionalZoomScale;
23955
+ //尺寸变大后,多出来的宽/高的一半(要从两边均匀扩张)。
23956
+ const offsetAlongX = (compensatedWidth - width) / 2;
23957
+ const offsetAlongY = (compensatedHeight - height) / 2;
23958
+ //“右方向”单位向量(从左上指向右上),因为图可能旋转了,不能只按水平 x 轴算。
23959
+ const unitX = width > 0 ? (topRightSvg.x - topLeftSvg.x) / width : 1;
23960
+ const unitY = width > 0 ? (topRightSvg.y - topLeftSvg.y) / width : 0;
23961
+ //“下方向”单位向量(从左上指向左下),同理用于旋转场景。
23962
+ const unitDownX = height > 0 ? (bottomLeftSvg.x - topLeftSvg.x) / height : 0;
23963
+ const unitDownY = height > 0 ? (bottomLeftSvg.y - topLeftSvg.y) / height : 0;
23964
+ /**
23965
+ * 计算补偿后的新左上角:
23966
+ * 从原左上角沿“右方向”回退半个扩展量,再沿“下方向”回退半个扩展量。
23967
+ * 这样放大后仍以原区域中心为中心,不会放大后位置偏移。
23968
+ */
23969
+ const compensatedTopLeftX = topLeftSvg.x - unitX * offsetAlongX - unitDownX * offsetAlongY;
23970
+ const compensatedTopLeftY = topLeftSvg.y - unitY * offsetAlongX - unitDownY * offsetAlongY;
23971
+ if (cancelled) {
23972
+ return;
23973
+ }
23974
+ setMapImageFrame({
23975
+ url: staticMapUrl,
23976
+ x: compensatedTopLeftX,
23977
+ y: compensatedTopLeftY,
23978
+ width: compensatedWidth,
23979
+ height: compensatedHeight,
23980
+ transform: `rotate(${rotationDeg} ${compensatedTopLeftX} ${compensatedTopLeftY})`,
23981
+ });
23982
+ })();
23983
+ return () => {
23984
+ cancelled = true;
23352
23985
  };
23353
- // 使用当前地图视口矩形四角,将屏幕坐标映射到 SVG 坐标,确保底图与主视口对齐
23354
- const topLeftSvg = screenToSvg(mapRect.left, mapRect.top);
23355
- const topRightSvg = screenToSvg(mapRect.right, mapRect.top);
23356
- const bottomLeftSvg = screenToSvg(mapRect.left, mapRect.bottom);
23357
- if (!topLeftSvg || !topRightSvg || !bottomLeftSvg) {
23358
- setMapImageFrame(null);
23359
- return;
23360
- }
23361
- // 把当前主地图可视区域尺寸,按比例压到 Static API 允许的最大尺寸以内(受 Static API 640 上限约束)),同时不改变宽高比。
23362
- const mapWidth = Math.max(1, Math.round(mapRect.width));
23363
- const mapHeight = Math.max(1, Math.round(mapRect.height));
23364
- const shrinkRatio = Math.min(1, MAX_STATIC_SIZE / Math.max(mapWidth, mapHeight));
23365
- const staticWidth = Math.max(1, Math.round(mapWidth * shrinkRatio));
23366
- const staticHeight = Math.max(1, Math.round(mapHeight * shrinkRatio));
23367
- const center = mapBounds.getCenter();
23368
- const currentZoom = mapRef.getZoom() || 20;
23369
- // 尺寸缩小后同步补偿 zoom,保持与当前主图一致的地理覆盖范围
23370
- const zoomCompensation = Math.log2(shrinkRatio);
23371
- const adjustedZoom = currentZoom + zoomCompensation;
23372
- const staticZoom = Math.max(0, Math.min(Math.floor(adjustedZoom), 22));
23373
- const staticMapUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${center.lat()},${center.lng()}&zoom=${staticZoom}&size=${staticWidth}x${staticHeight}&maptype=satellite&key=${googleMapStaticApiKey}`;
23374
- // 通过三角点推导静态图在 SVG 中的宽高和旋转角
23375
- // Math.hypot(x, y) 等价于 sqrt(x*x + y*y),计算两点之间的距离
23376
- const width = Math.hypot(topRightSvg.x - topLeftSvg.x, topRightSvg.y - topLeftSvg.y);
23377
- const height = Math.hypot(bottomLeftSvg.x - topLeftSvg.x, bottomLeftSvg.y - topLeftSvg.y);
23378
- // Math.atan2(y, x) 计算反正切值,返回 [-π, π] 弧度,*180/π 转换为角度
23379
- const rotationDeg = (Math.atan2(topRightSvg.y - topLeftSvg.y, topRightSvg.x - topLeftSvg.x) * 180) / Math.PI;
23380
- // Static API zoom 只能取整;额外补偿小数部分,避免底图视觉偏小
23381
- // 在有旋转的坐标系里,做“以中心为锚点”的等比放大补偿。
23382
- const fractionalZoomScale = Math.pow(2, adjustedZoom - staticZoom); //算出“还差多少缩放比例”。例子:adjustedZoom=19.6,staticZoom=19,差 0.6,比例是 2^0.6 ≈ 1.515。
23383
- //把原始 width/height 乘这个比例,得到补偿后的尺寸。
23384
- const compensatedWidth = width * fractionalZoomScale;
23385
- const compensatedHeight = height * fractionalZoomScale;
23386
- //尺寸变大后,多出来的宽/高的一半(要从两边均匀扩张)。
23387
- const offsetAlongX = (compensatedWidth - width) / 2;
23388
- const offsetAlongY = (compensatedHeight - height) / 2;
23389
- //“右方向”单位向量(从左上指向右上),因为图可能旋转了,不能只按水平 x 轴算。
23390
- const unitX = width > 0 ? (topRightSvg.x - topLeftSvg.x) / width : 1;
23391
- const unitY = width > 0 ? (topRightSvg.y - topLeftSvg.y) / width : 0;
23392
- //“下方向”单位向量(从左上指向左下),同理用于旋转场景。
23393
- const unitDownX = height > 0 ? (bottomLeftSvg.x - topLeftSvg.x) / height : 0;
23394
- const unitDownY = height > 0 ? (bottomLeftSvg.y - topLeftSvg.y) / height : 1;
23395
- /**
23396
- * 计算补偿后的新左上角:
23397
- * 从原左上角沿“右方向”回退半个扩展量,再沿“下方向”回退半个扩展量。
23398
- * 这样放大后仍以原区域中心为中心,不会放大后位置偏移。
23399
- */
23400
- const compensatedTopLeftX = topLeftSvg.x - unitX * offsetAlongX - unitDownX * offsetAlongY;
23401
- const compensatedTopLeftY = topLeftSvg.y - unitY * offsetAlongX - unitDownY * offsetAlongY;
23402
- setMapImageFrame({
23403
- url: staticMapUrl,
23404
- x: compensatedTopLeftX,
23405
- y: compensatedTopLeftY,
23406
- width: compensatedWidth,
23407
- height: compensatedHeight,
23408
- transform: `rotate(${rotationDeg} ${compensatedTopLeftX} ${compensatedTopLeftY})`,
23409
- });
23410
23986
  }
23411
23987
  else {
23412
23988
  setMapImageFrame(null);
@@ -23426,6 +24002,8 @@ const Magnifier = ({ visible, dragState, polygonPoints, fillColor = 'rgba(255, 1
23426
24002
  size,
23427
24003
  zoom,
23428
24004
  googleMapStaticApiKey,
24005
+ googleMapStaticUrlSigningSecret,
24006
+ signGoogleStaticMapUrl$1,
23429
24007
  staticMapRetryCount,
23430
24008
  staticMapBlockedUntil,
23431
24009
  ]);
@@ -23601,16 +24179,16 @@ const Magnifier = ({ visible, dragState, polygonPoints, fillColor = 'rgba(255, 1
23601
24179
  ? 3.6 / styleZoomFactor
23602
24180
  : 3 / styleZoomFactor;
23603
24181
  return (jsxRuntime.jsx("circle", { cx: x, cy: y, r: pointRadius, fill: pointFill, stroke: pointStroke, strokeWidth: pointStrokeWidth }, idx));
23604
- })] })), dragState.dragType === 'new' && dragState.edgeInfo && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx("line", { x1: dragState.edgeInfo.startPoint.x, y1: dragState.edgeInfo.startPoint.y, x2: dragState.edgeInfo.endPoint.x, y2: dragState.edgeInfo.endPoint.y, stroke: "#999", strokeWidth: (strokeWidth * 3.2) / styleZoomFactor, strokeDasharray: `${6 / styleZoomFactor},${6 / styleZoomFactor}` }) }))] }), jsxRuntime.jsx(CharginPile, { viewBox: magnifierViewBox || null, rotation: actureRotate, sizeInSvgUnits: true,
24182
+ })] })), dragState.dragType === 'new' && dragState.edgeInfo && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx("line", { x1: dragState.edgeInfo.startPoint.x, y1: dragState.edgeInfo.startPoint.y, x2: dragState.edgeInfo.endPoint.x, y2: dragState.edgeInfo.endPoint.y, stroke: "#999", strokeWidth: (strokeWidth * 3.2) / styleZoomFactor, strokeDasharray: `${6 / styleZoomFactor},${6 / styleZoomFactor}` }) }))] }), jsxRuntime.jsx(CharginPile, { viewBox: magnifierViewBox || null, rotation: actureRotate, sizeInSvgUnits: true, minDisplaySizePx: MAGNIFIER_CHARGING_PILE_MIN_DISPLAY_PX,
23605
24183
  // 充电桩最多保持原始尺寸,不允许放大超过 1 倍
23606
24184
  sizeScale: Math.min(1, 1 / styleZoomFactor) })] }));
23607
24185
  // 使用 Portal 渲染到 body,避免层级问题
23608
24186
  return ReactDOM.createPortal(magnifierContent, document.body);
23609
24187
  };
23610
24188
 
23611
- var css_248z$6 = ".index-module_pointerCursor__Ee6pr {\n cursor: pointer;\n}\n\n.index-module_polygonPath__PynOn {\n pointer-events: stroke;\n cursor: pointer;\n}\n\n.index-module_dragging__xSFdO {\n cursor: grabbing;\n}\n\n.index-module_notCreate__bFnkV {\n cursor: no-drop;\n}";
23612
- var styles$6 = {"pointerCursor":"index-module_pointerCursor__Ee6pr","polygonPath":"index-module_polygonPath__PynOn","dragging":"index-module_dragging__xSFdO","notCreate":"index-module_notCreate__bFnkV"};
23613
- styleInject(css_248z$6);
24189
+ var css_248z$8 = ".index-module_pointerCursor__Ee6pr {\n cursor: pointer;\n}\n\n.index-module_polygonPath__PynOn {\n pointer-events: stroke;\n cursor: pointer;\n}\n\n.index-module_dragging__xSFdO {\n cursor: grabbing;\n}\n\n.index-module_notCreate__bFnkV {\n cursor: no-drop;\n}";
24190
+ var styles$8 = {"pointerCursor":"index-module_pointerCursor__Ee6pr","polygonPath":"index-module_polygonPath__PynOn","dragging":"index-module_dragging__xSFdO","notCreate":"index-module_notCreate__bFnkV"};
24191
+ styleInject(css_248z$8);
23614
24192
 
23615
24193
  /**
23616
24194
  * 几何计算工具函数
@@ -24033,34 +24611,126 @@ const useCheckElement = () => {
24033
24611
  };
24034
24612
  };
24035
24613
 
24614
+ var css_248z$7 = ".Tooltip-module_tooltip__Wexlk {\n position: fixed;\n background-color: #fff;\n border-radius: 4px;\n height: 20px;\n line-height: 20px;\n font-size: 12px;\n color: #211F1F;\n padding: 0 4px;\n white-space: nowrap;\n z-index: 9999;\n cursor: pointer;\n user-select: none;\n}\n.Tooltip-module_tooltip__Wexlk.Tooltip-module_tooltipHover__Tfqme {\n height: 30px;\n padding: 5px 12px;\n}\n.Tooltip-module_tooltip__Wexlk.Tooltip-module_tooltipHover__Tfqme:hover {\n background-color: #FFEFE6;\n color: #FF5A00;\n}";
24615
+ var styles$7 = {"tooltip":"Tooltip-module_tooltip__Wexlk","tooltipHover":"Tooltip-module_tooltipHover__Tfqme"};
24616
+ styleInject(css_248z$7);
24617
+
24618
+ var classnames = {exports: {}};
24619
+
24620
+ /*!
24621
+ Copyright (c) 2018 Jed Watson.
24622
+ Licensed under the MIT License (MIT), see
24623
+ http://jedwatson.github.io/classnames
24624
+ */
24625
+
24626
+ (function (module) {
24627
+ /* global define */
24628
+
24629
+ (function () {
24630
+
24631
+ var hasOwn = {}.hasOwnProperty;
24632
+
24633
+ function classNames () {
24634
+ var classes = '';
24635
+
24636
+ for (var i = 0; i < arguments.length; i++) {
24637
+ var arg = arguments[i];
24638
+ if (arg) {
24639
+ classes = appendClass(classes, parseValue(arg));
24640
+ }
24641
+ }
24642
+
24643
+ return classes;
24644
+ }
24645
+
24646
+ function parseValue (arg) {
24647
+ if (typeof arg === 'string' || typeof arg === 'number') {
24648
+ return arg;
24649
+ }
24650
+
24651
+ if (typeof arg !== 'object') {
24652
+ return '';
24653
+ }
24654
+
24655
+ if (Array.isArray(arg)) {
24656
+ return classNames.apply(null, arg);
24657
+ }
24658
+
24659
+ if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
24660
+ return arg.toString();
24661
+ }
24662
+
24663
+ var classes = '';
24664
+
24665
+ for (var key in arg) {
24666
+ if (hasOwn.call(arg, key) && arg[key]) {
24667
+ classes = appendClass(classes, key);
24668
+ }
24669
+ }
24670
+
24671
+ return classes;
24672
+ }
24673
+
24674
+ function appendClass (value, newClass) {
24675
+ if (!newClass) {
24676
+ return value;
24677
+ }
24678
+
24679
+ if (value) {
24680
+ return value + ' ' + newClass;
24681
+ }
24682
+
24683
+ return value + newClass;
24684
+ }
24685
+
24686
+ if (module.exports) {
24687
+ classNames.default = classNames;
24688
+ module.exports = classNames;
24689
+ } else {
24690
+ window.classNames = classNames;
24691
+ }
24692
+ }());
24693
+ } (classnames));
24694
+
24695
+ var classnamesExports = classnames.exports;
24696
+ var classNames = /*@__PURE__*/getDefaultExportFromCjs(classnamesExports);
24697
+
24036
24698
  const Tooltip = ({ open, x, y, text, style, onClick }) => {
24037
24699
  if (!open)
24038
24700
  return null;
24039
24701
  const tooltipStyle = {
24040
- position: 'fixed',
24041
24702
  left: x,
24042
24703
  top: y,
24043
- backgroundColor: '#fff',
24044
- borderRadius: '4px',
24045
- height: '20px',
24046
- lineHeight: '16px',
24047
- fontSize: '12px',
24048
- color: '#211F1F',
24049
- padding: '2px 4px',
24050
- whiteSpace: 'nowrap',
24051
- zIndex: 9999,
24052
- cursor: 'pointer',
24053
- userSelect: 'none',
24054
24704
  ...style,
24055
24705
  };
24056
24706
  // 使用 createPortal 挂载到 body
24057
- return ReactDOM.createPortal(jsxRuntime.jsx("div", { style: tooltipStyle, onClick: (e) => {
24707
+ return ReactDOM.createPortal(jsxRuntime.jsx("div", { className: classNames(styles$7.tooltip, {
24708
+ [styles$7.tooltipHover]: !!onClick,
24709
+ }), style: tooltipStyle, onClick: (e) => {
24058
24710
  e.preventDefault();
24059
24711
  e.stopPropagation();
24060
24712
  onClick?.();
24061
24713
  }, children: text }), document.body);
24062
24714
  };
24063
24715
 
24716
+ function getPathData(points) {
24717
+ // 1. 安全守卫:如果没有点,返回空字符串
24718
+ if (!points || points.length === 0)
24719
+ return '';
24720
+ // 2. 如果只有 1 个点,只生成起点 M 即可(或者在最外层判断中直接 return 过滤掉)
24721
+ if (points.length === 1) {
24722
+ return `M ${points[0][0]} ${points[0][1]}`;
24723
+ }
24724
+ // 3. 2个点及以上时,再进行 L 拼接
24725
+ const pathData = points.map((p, index) => {
24726
+ if (index === 0) {
24727
+ return `M ${p[0]} ${p[1]}`;
24728
+ }
24729
+ return ` L ${p[0]} ${p[1]}`;
24730
+ }).join('');
24731
+ return pathData;
24732
+ }
24733
+
24064
24734
  /**
24065
24735
  * 将坐标点数组转换为polygon points字符串
24066
24736
  * @param coordinates 坐标点数组 [[x1, y1], [x2, y2], ...]
@@ -24144,15 +24814,12 @@ const groupCoordinatesByType = (coordinates, autoClose = true) => {
24144
24814
  const createPathData = (points) => {
24145
24815
  if (points.length < 2)
24146
24816
  return '';
24147
- let pathData = `M ${points[0][0]} ${points[0][1]}`;
24148
- for (let i = 1; i < points.length; i++) {
24149
- pathData += ` L ${points[i][0]} ${points[i][1]}`;
24150
- }
24817
+ const pathData = getPathData(points);
24151
24818
  return pathData;
24152
24819
  };
24153
24820
  const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor = 'rgba(0, 0, 0, 0.1)', hoverFillColor = '', fillOpacity = 1, strokeColor = '#000', strokeWidth = 2, strokeOpacity = 1, createMode = false, showPoints = false, onPointClick, completed = false, mousePos = null, editMode = false, onCoordinatesChange, onPathClick, onPolygonClick, onVertexDelete, draggable = true, // 新增参数,如果未指定则根据createMode和editMode自动判断
24154
24821
  }) => {
24155
- const { overlayScale, unitType, showStraddleBoundaryBorder, platform, onH5FirstSelectObstaclePoint } = useCommonContext();
24822
+ const { overlayScale, unitType, showStraddleBoundaryBorder, platform, locale, onH5FirstSelectObstaclePoint, } = useCommonContext();
24156
24823
  const { onHandleEvent } = useMapEditContext();
24157
24824
  const { svgRef } = useSvgEditContext();
24158
24825
  const [dragState, setDragState] = React.useState({
@@ -24200,11 +24867,12 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24200
24867
  const [tooltipIndex, setTooltipIndex] = React.useState(null);
24201
24868
  const [tooltipPos, setTooltipPos] = React.useState({ x: 0, y: 0 });
24202
24869
  const [completeTooltipOpen, setCompleteTooltipOpen] = React.useState(false);
24870
+ const [moveTooltipOpen, setMoveTooltipOpen] = React.useState(false);
24203
24871
  const updateTooltipPos = (target) => {
24204
24872
  const rect = target.getBoundingClientRect();
24205
24873
  setTooltipPos({
24206
24874
  x: rect.left + rect.width + 4,
24207
- y: rect.top - 10
24875
+ y: rect.top - 10,
24208
24876
  });
24209
24877
  };
24210
24878
  // 处理顶点点击(仅透出点击;不触发删除)
@@ -24339,6 +25007,13 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24339
25007
  edgeInfo: edgeInfo,
24340
25008
  dragType: 'new',
24341
25009
  });
25010
+ // 边上新点:insertIndex 可能与当前 selectedVertexIndex 相同(例如原选中 2,在 1-2 间插入后仍写 2),
25011
+ // React 会跳过 setState,依赖 selectedVertexIndex 的 useLayoutEffect 不会跑,故用微任务直接震一次
25012
+ if (platform === exports.PlatformType.H5 && editMapInfo?.elementType === exports.DataType.OBSTACLE) {
25013
+ void Promise.resolve().then(() => {
25014
+ onH5FirstSelectObstaclePoint?.();
25015
+ });
25016
+ }
24342
25017
  // 将新创建的点设为选中态,便于后续直接拖拽
24343
25018
  setSelectedVertexIndex(insertIndex);
24344
25019
  }, [
@@ -24349,6 +25024,9 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24349
25024
  coordinates,
24350
25025
  getSVGCoordinates,
24351
25026
  calculatePerpendicularFoot$1,
25027
+ platform,
25028
+ editMapInfo?.elementType,
25029
+ onH5FirstSelectObstaclePoint,
24352
25030
  ]);
24353
25031
  // H5平台长按边线创建新顶点
24354
25032
  const handleEdgeLongPressStart = React.useCallback((e, edgeStartIndex) => {
@@ -24393,6 +25071,8 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24393
25071
  const vertexLongPressTimerRef = React.useRef(null);
24394
25072
  const isVertexLongPressedRef = React.useRef(false);
24395
25073
  const longPressVertexIndexRef = React.useRef(-1);
25074
+ /** 长按选中顶点且 index 发生变化时置 true,在 useLayoutEffect 里再触发震动,避免早于视觉选中态 */
25075
+ const pendingH5ObstacleSelectHapticRef = React.useRef(false);
24396
25076
  // 长按拖拽判定相关
24397
25077
  const touchStartPosRef = React.useRef(null);
24398
25078
  const hasStartedDragViaLongPressRef = React.useRef(false);
@@ -24413,13 +25093,21 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24413
25093
  vertexLongPressTimerRef.current = window.setTimeout(() => {
24414
25094
  isVertexLongPressedRef.current = true;
24415
25095
  setSelectedVertexIndex((prev) => {
24416
- if (prev !== index) {
24417
- onH5FirstSelectObstaclePoint?.();
24418
- }
25096
+ pendingH5ObstacleSelectHapticRef.current = prev !== index;
24419
25097
  return index;
24420
25098
  });
24421
25099
  }, 1000);
24422
- }, [editMode, createMode, completed, dragState.isDragging, onH5FirstSelectObstaclePoint]);
25100
+ }, [editMode, createMode, completed, dragState.isDragging]);
25101
+ React.useLayoutEffect(() => {
25102
+ if (!pendingH5ObstacleSelectHapticRef.current)
25103
+ return;
25104
+ pendingH5ObstacleSelectHapticRef.current = false;
25105
+ if (platform !== exports.PlatformType.H5)
25106
+ return;
25107
+ if (editMapInfo?.elementType !== exports.DataType.OBSTACLE)
25108
+ return;
25109
+ onH5FirstSelectObstaclePoint?.();
25110
+ }, [selectedVertexIndex, platform, editMapInfo?.elementType, onH5FirstSelectObstaclePoint]);
24423
25111
  const handleVertexLongPressEnd = React.useCallback(() => {
24424
25112
  if (vertexLongPressTimerRef.current !== null) {
24425
25113
  window.clearTimeout(vertexLongPressTimerRef.current);
@@ -24448,8 +25136,8 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24448
25136
  }, [dragState.currentPosition, checkCanNotCreateAtPosition, coordinates]);
24449
25137
  const generateVertexClassName = React.useMemo(() => {
24450
25138
  if (!dragState.isDragging)
24451
- return styles$6.pointerCursor;
24452
- return showNotCreateCursor ? styles$6.notCreate : styles$6.dragging;
25139
+ return styles$8.pointerCursor;
25140
+ return showNotCreateCursor ? styles$8.notCreate : styles$8.dragging;
24453
25141
  }, [dragState.isDragging, showNotCreateCursor]);
24454
25142
  // 使用 useRef 来存储 requestAnimationFrame ID
24455
25143
  const rafIdRef = React.useRef(null);
@@ -24740,7 +25428,7 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24740
25428
  WebkitTouchCallout: 'none',
24741
25429
  touchAction: 'none',
24742
25430
  } })] }));
24743
- })()] })), renderCoordinates.length >= 2 && (jsxRuntime.jsx("polygon", { className: canSelect ? styles$6.pointerCursor : '', points: polygonPoints, fill: isHover ? (hoverFillColor || fillColor) : fillColor, fillOpacity: fillOpacity, stroke: "none" // 边框透明
25431
+ })()] })), renderCoordinates.length >= 2 && (jsxRuntime.jsx("polygon", { className: canSelect ? styles$8.pointerCursor : '', points: polygonPoints, fill: isHover ? hoverFillColor || fillColor : fillColor, fillOpacity: fillOpacity, stroke: "none" // 边框透明
24744
25432
  , onClick: onPolygonClick })), jsxRuntime.jsx("g", { children: renderCoordinates.length >= 2 &&
24745
25433
  pathSegments.map((segment, index) => {
24746
25434
  if (segment.points.length < 2)
@@ -24748,30 +25436,45 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24748
25436
  const pathData = createPathData(segment.points);
24749
25437
  const isDash = segment.type === 1;
24750
25438
  if (isDash && showStraddleBoundaryBorder) {
24751
- return (jsxRuntime.jsx(DashPath, { className: canSelect ? styles$6.polygonPath : '', points: segment.points, stroke: strokeColor, strokeWidth: isHover ? strokeWidth + 1 : strokeWidth, strokeOpacity: strokeOpacity }, index));
25439
+ return (jsxRuntime.jsx(DashPath, { className: canSelect ? styles$8.polygonPath : '', points: segment.points, stroke: strokeColor, strokeWidth: isHover ? strokeWidth + 1 : strokeWidth, strokeOpacity: strokeOpacity }, index));
24752
25440
  }
24753
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(editMode || createMode) && (jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: '#fff', strokeWidth: strokeWidth * 2, strokeOpacity: strokeOpacity, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke", style: {
25441
+ return (jsxRuntime.jsxs(React.Fragment, { children: [(editMode || createMode) && (jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: '#fff', strokeWidth: strokeWidth * 2, strokeOpacity: strokeOpacity, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke", style: {
24754
25442
  userSelect: 'none',
24755
25443
  WebkitUserSelect: 'none',
24756
25444
  WebkitTouchCallout: 'none',
24757
25445
  touchAction: 'none',
24758
- } }, 'warp' + index)), jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: strokeColor, strokeWidth: isHover ? strokeWidth + 1 : strokeWidth, strokeOpacity: strokeOpacity, strokeLinecap: "round", strokeLinejoin: "round", className: canSelect ? styles$6.polygonPath : '', vectorEffect: "non-scaling-stroke", onClick: onPathClick, style: {
25446
+ } })), jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: strokeColor, strokeWidth: isHover ? strokeWidth + 1 : strokeWidth, strokeOpacity: strokeOpacity, strokeLinecap: "round", strokeLinejoin: "round", className: canSelect ? styles$8.polygonPath : '', vectorEffect: "non-scaling-stroke", onClick: onPathClick, style: {
24759
25447
  userSelect: 'none',
24760
25448
  WebkitUserSelect: 'none',
24761
25449
  WebkitTouchCallout: 'none',
24762
25450
  touchAction: 'none',
24763
- } }, index)] }));
25451
+ } }, index)] }, 'warp' + index));
24764
25452
  }) }), (editMode || (createMode && completed)) &&
24765
25453
  renderCoordinates.length >= 2 &&
24766
25454
  renderCoordinates.map((coord, index) => {
24767
25455
  const nextCoord = renderCoordinates[(index + 1) % renderCoordinates.length];
24768
25456
  const isDashPath = coord[2] === 1;
24769
- return (jsxRuntime.jsx("line", { x1: coord[0], y1: coord[1], x2: nextCoord[0], y2: nextCoord[1], stroke: isDashPath ? 'transparent' : strokeColor, strokeWidth: platform === exports.PlatformType.H5 ? strokeWidth * 3 : strokeWidth, className: editMode ? styles$6.pointerCursor : '', style: {
24770
- userSelect: 'none',
24771
- WebkitUserSelect: 'none',
24772
- WebkitTouchCallout: 'none',
24773
- touchAction: 'none',
24774
- }, vectorEffect: "non-scaling-stroke", ...(platform === exports.PlatformType.H5
25457
+ const edgeLineCommon = {
25458
+ x1: coord[0],
25459
+ y1: coord[1],
25460
+ x2: nextCoord[0],
25461
+ y2: nextCoord[1],
25462
+ };
25463
+ const edgeLineStyle = {
25464
+ userSelect: 'none',
25465
+ WebkitUserSelect: 'none',
25466
+ WebkitTouchCallout: 'none',
25467
+ touchAction: 'none',
25468
+ };
25469
+ // H5 实线:透明粗线仅作长按热区,描边只看上方 path;H5 虚线/Web:沿用透明或主题色边线 + 对应事件(合并为一个 line,避免两段重复)
25470
+ const isH5SolidHitOnly = platform === exports.PlatformType.H5 && ((editMode && !isDashPath) || createMode);
25471
+ const edgeStroke = isH5SolidHitOnly || isDashPath ? 'transparent' : strokeColor;
25472
+ const edgeStrokeWidth = isH5SolidHitOnly
25473
+ ? strokeWidth * 12
25474
+ : platform === exports.PlatformType.H5
25475
+ ? strokeWidth * 2
25476
+ : strokeWidth;
25477
+ return (jsxRuntime.jsx("line", { ...edgeLineCommon, stroke: edgeStroke, strokeWidth: edgeStrokeWidth, ...(isH5SolidHitOnly ? { strokeLinecap: 'round' } : {}), className: editMode ? styles$8.pointerCursor : '', style: edgeLineStyle, vectorEffect: "non-scaling-stroke", ...(platform === exports.PlatformType.H5
24775
25478
  ? {
24776
25479
  onTouchStart: createReactEventHandler((e) => handleEdgeLongPressStart(e, index)),
24777
25480
  onTouchEnd: createReactEventHandler(() => handleEdgeLongPressEnd()),
@@ -24841,7 +25544,7 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24841
25544
  isDoubleClickingRef.current = false;
24842
25545
  }, 300);
24843
25546
  // 打开删除确认 Tooltip(仅在可编辑时)
24844
- if (editMode || (createMode && completed)) {
25547
+ if ((editMode || (createMode && completed)) && renderCoordinates.length > 3) {
24845
25548
  updateTooltipPos(e.target);
24846
25549
  setTooltipIndex(idx);
24847
25550
  }
@@ -24918,13 +25621,25 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24918
25621
  }
24919
25622
  }
24920
25623
  },
24921
- onMouseEnter: canComplete ? (e) => {
24922
- setCompleteTooltipOpen(true);
24923
- updateTooltipPos(e.target);
24924
- } : undefined,
24925
- onMouseLeave: canComplete ? () => {
24926
- setCompleteTooltipOpen(false);
24927
- } : undefined,
25624
+ onMouseEnter: (e) => {
25625
+ if (canComplete) {
25626
+ setCompleteTooltipOpen(true);
25627
+ }
25628
+ if (editMode || (createMode && completed)) {
25629
+ setMoveTooltipOpen(true);
25630
+ }
25631
+ if (tooltipIndex === null) {
25632
+ updateTooltipPos(e.target);
25633
+ }
25634
+ },
25635
+ onMouseLeave: () => {
25636
+ if (canComplete) {
25637
+ setCompleteTooltipOpen(false);
25638
+ }
25639
+ if (editMode || (createMode && completed)) {
25640
+ setMoveTooltipOpen(false);
25641
+ }
25642
+ },
24928
25643
  }) }), platform === exports.PlatformType.H5 && tooltipIndex === idx && coordinates.length > 3 && (jsxRuntime.jsxs("g", { transform: `translate(${coord[0] + 4 * overlayScale * 2}, ${coord[1] - 4 * overlayScale * 2}) scale(${overlayScale * 2})`, onClick: (e) => {
24929
25644
  e.preventDefault();
24930
25645
  e.stopPropagation();
@@ -24948,12 +25663,16 @@ const PolygonElement = ({ canSelect = false, isHover = false, points, fillColor
24948
25663
  // >
24949
25664
  // </rect>
24950
25665
  )] }, `vertex-${idx}`));
24951
- }), platform === exports.PlatformType.WEB && tooltipIndex !== null && (jsxRuntime.jsx(Tooltip, { open: true, x: tooltipPos.x, y: tooltipPos.y, text: "Remove", onClick: () => {
24952
- if (onVertexDelete && (editMode || (createMode && completed)) && coordinates.length > 3) {
25666
+ }), platform === exports.PlatformType.WEB && tooltipIndex !== null && (jsxRuntime.jsx(Tooltip, { open: true, x: tooltipPos.x, y: tooltipPos.y, text: locale?.['map.renderer.remove'] || 'Remove', onClick: () => {
25667
+ if (onVertexDelete &&
25668
+ (editMode || (createMode && completed)) &&
25669
+ coordinates.length > 3) {
24953
25670
  onVertexDelete(renderCoordinates?.length - 1 - tooltipIndex);
24954
25671
  }
24955
25672
  setTooltipIndex(null);
24956
- } })), platform === exports.PlatformType.WEB && completeTooltipOpen && (jsxRuntime.jsx(Tooltip, { open: true, x: tooltipPos.x, y: tooltipPos.y, text: "Click to finish" }))] }));
25673
+ } })), platform === exports.PlatformType.WEB && completeTooltipOpen && (jsxRuntime.jsx(Tooltip, { open: true, x: tooltipPos.x, y: tooltipPos.y, text: locale?.['map.renderer.tips.clickFinish'] || 'Click to finish' })), platform === exports.PlatformType.WEB && moveTooltipOpen && tooltipIndex === null && (jsxRuntime.jsx(Tooltip, { open: true, x: tooltipPos.x, y: tooltipPos.y, text: renderCoordinates.length > 3 ?
25674
+ (locale?.['map.renderer.tips.dragAndRemove'] || 'Drag to change, double click to remove') :
25675
+ (locale?.['map.renderer.tips.drag'] || 'Drag to change') }))] }));
24957
25676
  };
24958
25677
 
24959
25678
  const MowPartitionContext = React.createContext({
@@ -25033,17 +25752,15 @@ const BoundaryElement = ({ data, isHover }) => {
25033
25752
  };
25034
25753
 
25035
25754
  const ChannelElement = ({ data }) => {
25036
- const { sn } = useCommonContext();
25755
+ const { channelClipPathId } = useCommonContext();
25756
+ const clipPathUrl = `url(#${channelClipPathId})`;
25037
25757
  const points = data.points;
25038
25758
  const style = data.style;
25039
- let pathData = `M ${points[0][0]} ${points[0][1]}`;
25040
- for (let i = 1; i < points.length; i++) {
25041
- pathData += ` L ${points[i][0]} ${points[i][1]}`;
25042
- }
25759
+ const pathData = getPathData(points);
25043
25760
  const topLineWidth = dp2px(style.lineWidth).toString();
25044
25761
  const bottomLineWidth = dp2px(style.bottomLineWidth).toString();
25045
25762
  const dashLength = dp2px(style.lineWidth);
25046
- return (jsxRuntime.jsxs("g", { id: data.id.toString(), children: [jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: "#8498A9", strokeWidth: bottomLineWidth, strokeLinejoin: "round", opacity: style.opacity || 1, style: { clipPath: `url(#channel-clip-path-${sn})` } }), jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: "#FFFFFF", strokeWidth: topLineWidth, strokeLinejoin: "round", strokeDasharray: `${dashLength * 2} ${dashLength}`, opacity: style.opacity || 1, style: { clipPath: `url(#channel-clip-path-${sn})` } })] }));
25763
+ return (jsxRuntime.jsxs("g", { id: data.id.toString(), children: [jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: "#8498A9", strokeWidth: bottomLineWidth, strokeLinejoin: "round", opacity: style.opacity || 1, style: { clipPath: clipPathUrl } }), jsxRuntime.jsx("path", { d: pathData, fill: "none", stroke: "#FFFFFF", strokeWidth: topLineWidth, strokeLinejoin: "round", strokeDasharray: `${dashLength * 2} ${dashLength}`, opacity: style.opacity || 1, style: { clipPath: clipPathUrl } })] }));
25047
25764
  };
25048
25765
 
25049
25766
  /**
@@ -25169,10 +25886,11 @@ const ObstacleElement = ({ data, isHover }) => {
25169
25886
  const strokeWidth = React.useMemo(() => {
25170
25887
  // 如果是h5选中的情况下,禁区只有在start模式下才需要加粗
25171
25888
  // 其他模式展示的是其他的样式
25172
- if (platform === exports.PlatformType.H5 &&
25889
+ if ((platform === exports.PlatformType.H5 &&
25173
25890
  editMapInfo?.selectElement?.id === data.id &&
25174
- editMapInfo.mobileMode === exports.MobileEditMode.START) {
25175
- return dp2px((style.lineWidth || 1) * 2);
25891
+ editMapInfo.mobileMode === exports.MobileEditMode.START) ||
25892
+ editMapInfo.mobileMode === exports.MobileEditMode.EDIT) {
25893
+ return dp2px((style.lineWidth || 1) * 2.2);
25176
25894
  // return dp2px(((style.lineWidth as number) || 3) * 3);
25177
25895
  }
25178
25896
  return dp2px(style.lineWidth || 3);
@@ -25207,7 +25925,10 @@ const ObstacleElement = ({ data, isHover }) => {
25207
25925
  }
25208
25926
  return editMapInfo?.selectElement?.id === data.id;
25209
25927
  }, [editMapInfo, data, platform]);
25210
- return (jsxRuntime.jsx(PolygonElement, { canSelect: Boolean(!editMapInfo?.elementType) && editMap, points: currentPoints, fillColor: style.fillColor, hoverFillColor: style.hoverFillColor, fillOpacity: style.fillOpacity, strokeColor: style.lineColor, strokeWidth: strokeWidth, editMode: editMode, isHover: isHover, showPoints: editMapInfo?.selectElement?.id === data.id, onPathClick: onPathClick, onPolygonClick: () => {
25928
+ const disabled = React.useMemo(() => {
25929
+ return data?.status === 0 && !editMode;
25930
+ }, [data?.status, editMode, editMapInfo?.createMode]);
25931
+ return (jsxRuntime.jsx(PolygonElement, { canSelect: Boolean(!editMapInfo?.elementType) && editMap, points: currentPoints, fillColor: disabled ? style.disabledFillColor : style.fillColor, hoverFillColor: style.hoverFillColor, fillOpacity: style.fillOpacity, strokeColor: disabled ? style.disabledLineColor : style.lineColor, strokeWidth: strokeWidth, editMode: editMode, isHover: isHover, showPoints: editMapInfo?.selectElement?.id === data.id, onPathClick: onPathClick, onPolygonClick: () => {
25211
25932
  onPathClick();
25212
25933
  }, onVertexDelete: (vertexIndex) => handleCreateVertexDelete(vertexIndex), onCoordinatesChange: (coordinates) => {
25213
25934
  if (platform === exports.PlatformType.H5 && editMapInfo.mobileMode === exports.MobileEditMode.CREATE) {
@@ -25632,15 +26353,15 @@ const useVisionOffTransform = (data, scaleConstraints
25632
26353
  };
25633
26354
  };
25634
26355
 
25635
- var _g$6, _defs$6;
25636
- function _extends$7() { return _extends$7 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$7.apply(null, arguments); }
26356
+ var _g$3, _defs$3;
26357
+ function _extends$4() { return _extends$4 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$4.apply(null, arguments); }
25637
26358
  var SvgTransformDelete = function SvgTransformDelete(props) {
25638
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$7({
26359
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$4({
25639
26360
  xmlns: "http://www.w3.org/2000/svg",
25640
26361
  width: 20,
25641
26362
  height: 20,
25642
26363
  fill: "none"
25643
- }, props), _g$6 || (_g$6 = /*#__PURE__*/React__namespace.createElement("g", {
26364
+ }, props), _g$3 || (_g$3 = /*#__PURE__*/React__namespace.createElement("g", {
25644
26365
  clipPath: "url(#transform-delete_svg__a)"
25645
26366
  }, /*#__PURE__*/React__namespace.createElement("circle", {
25646
26367
  cx: 10,
@@ -25663,7 +26384,7 @@ var SvgTransformDelete = function SvgTransformDelete(props) {
25663
26384
  fill: "#fff",
25664
26385
  rx: 0.8,
25665
26386
  transform: "rotate(45 6.323 5.191)"
25666
- }))), _defs$6 || (_defs$6 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
26387
+ }))), _defs$3 || (_defs$3 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
25667
26388
  id: "transform-delete_svg__b",
25668
26389
  x1: 17.727,
25669
26390
  x2: -1.215,
@@ -25683,15 +26404,15 @@ var SvgTransformDelete = function SvgTransformDelete(props) {
25683
26404
  })))));
25684
26405
  };
25685
26406
 
25686
- var _g$5, _defs$5;
25687
- function _extends$6() { return _extends$6 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$6.apply(null, arguments); }
26407
+ var _g$2, _defs$2;
26408
+ function _extends$3() { return _extends$3 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$3.apply(null, arguments); }
25688
26409
  var SvgTransformRotate = function SvgTransformRotate(props) {
25689
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$6({
26410
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$3({
25690
26411
  xmlns: "http://www.w3.org/2000/svg",
25691
26412
  width: 21,
25692
26413
  height: 20,
25693
26414
  fill: "none"
25694
- }, props), _g$5 || (_g$5 = /*#__PURE__*/React__namespace.createElement("g", {
26415
+ }, props), _g$2 || (_g$2 = /*#__PURE__*/React__namespace.createElement("g", {
25695
26416
  clipPath: "url(#transform-rotate_svg__a)"
25696
26417
  }, /*#__PURE__*/React__namespace.createElement("path", {
25697
26418
  fill: "url(#transform-rotate_svg__b)",
@@ -25699,7 +26420,7 @@ var SvgTransformRotate = function SvgTransformRotate(props) {
25699
26420
  }), /*#__PURE__*/React__namespace.createElement("path", {
25700
26421
  fill: "#fff",
25701
26422
  d: "M11.602 14.719a5.447 5.447 0 0 1-6.608-5.116l-.004-.205a.818.818 0 0 1 1.633-.084l.004.084.01.285a3.81 3.81 0 0 0 2.127 3.137l.26.115a3.8 3.8 0 0 0 2.227.184l.276-.071q.29-.088.561-.219l-1.147-.441a.817.817 0 0 1 .587-1.525l2.54.977a.817.817 0 0 1 .316 1.308l.001.001a5.45 5.45 0 0 1-2.783 1.57M8.623 4.548a5.447 5.447 0 0 1 6.608 5.116l.004.204a.818.818 0 0 1-1.633.084l-.004-.084-.01-.284a3.81 3.81 0 0 0-2.127-3.138l-.26-.115a3.8 3.8 0 0 0-2.227-.184l-.276.072q-.29.087-.561.218l1.147.442a.817.817 0 0 1-.586 1.524l-2.542-.976a.817.817 0 0 1-.315-1.309 5.45 5.45 0 0 1 2.782-1.57"
25702
- }))), _defs$5 || (_defs$5 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
26423
+ }))), _defs$2 || (_defs$2 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
25703
26424
  id: "transform-rotate_svg__b",
25704
26425
  x1: 17.84,
25705
26426
  x2: -1.103,
@@ -25719,15 +26440,15 @@ var SvgTransformRotate = function SvgTransformRotate(props) {
25719
26440
  })))));
25720
26441
  };
25721
26442
 
25722
- var _g$4, _defs$4;
25723
- function _extends$5() { return _extends$5 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$5.apply(null, arguments); }
26443
+ var _g$1, _defs$1;
26444
+ function _extends$2() { return _extends$2 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$2.apply(null, arguments); }
25724
26445
  var SvgTransformScale = function SvgTransformScale(props) {
25725
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$5({
26446
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$2({
25726
26447
  xmlns: "http://www.w3.org/2000/svg",
25727
26448
  width: 21,
25728
26449
  height: 20,
25729
26450
  fill: "none"
25730
- }, props), _g$4 || (_g$4 = /*#__PURE__*/React__namespace.createElement("g", {
26451
+ }, props), _g$1 || (_g$1 = /*#__PURE__*/React__namespace.createElement("g", {
25731
26452
  clipPath: "url(#transform-scale_svg__a)"
25732
26453
  }, /*#__PURE__*/React__namespace.createElement("path", {
25733
26454
  fill: "url(#transform-scale_svg__b)",
@@ -25735,7 +26456,7 @@ var SvgTransformScale = function SvgTransformScale(props) {
25735
26456
  }), /*#__PURE__*/React__namespace.createElement("path", {
25736
26457
  fill: "#fff",
25737
26458
  d: "M5.078 8.265a.8.8 0 0 0 1.6.001v-.544l5.69 5.69h-.494a.8.8 0 0 0 0 1.6l2.474-.002a.8.8 0 0 0 .311-.062.8.8 0 0 0 .49-.738v-2.474a.8.8 0 1 0-1.6 0v.594L7.81 6.59h.544a.8.8 0 0 0 0-1.6H5.879a.8.8 0 0 0-.8.8z"
25738
- }))), _defs$4 || (_defs$4 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
26459
+ }))), _defs$1 || (_defs$1 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
25739
26460
  id: "transform-scale_svg__b",
25740
26461
  x1: 17.84,
25741
26462
  x2: -1.103,
@@ -25755,15 +26476,15 @@ var SvgTransformScale = function SvgTransformScale(props) {
25755
26476
  })))));
25756
26477
  };
25757
26478
 
25758
- var _g$3, _defs$3;
25759
- function _extends$4() { return _extends$4 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$4.apply(null, arguments); }
26479
+ var _g, _defs;
26480
+ function _extends$1() { return _extends$1 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$1.apply(null, arguments); }
25760
26481
  var SvgTransformTranslate = function SvgTransformTranslate(props) {
25761
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$4({
26482
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$1({
25762
26483
  xmlns: "http://www.w3.org/2000/svg",
25763
26484
  width: 20,
25764
26485
  height: 20,
25765
26486
  fill: "none"
25766
- }, props), _g$3 || (_g$3 = /*#__PURE__*/React__namespace.createElement("g", {
26487
+ }, props), _g || (_g = /*#__PURE__*/React__namespace.createElement("g", {
25767
26488
  clipPath: "url(#transform-translate_svg__a)"
25768
26489
  }, /*#__PURE__*/React__namespace.createElement("circle", {
25769
26490
  cx: 10,
@@ -25773,7 +26494,7 @@ var SvgTransformTranslate = function SvgTransformTranslate(props) {
25773
26494
  }), /*#__PURE__*/React__namespace.createElement("path", {
25774
26495
  fill: "#fff",
25775
26496
  d: "M16.18 10.758a.8.8 0 0 0-.105-1.218l-1.66-1.66a.801.801 0 0 0-1.132 1.132l.383.381h-2.96V6.418l.385.386a.8.8 0 1 0 1.131-1.13l-1.75-1.75a.8.8 0 0 0-1.222.11L7.594 5.689a.8.8 0 0 0 1.131 1.13l.381-.38v2.954H6.137l.381-.38a.8.8 0 1 0-1.13-1.132l-1.752 1.75a.8.8 0 0 0 .106 1.22l1.66 1.66a.801.801 0 0 0 1.132-1.133l-.386-.385h2.958v2.972l-.38-.38a.8.8 0 0 0-1.132 1.131l1.751 1.75a.8.8 0 0 0 1.223-.11l1.654-1.655a.8.8 0 1 0-1.13-1.131l-.386.386v-2.963h2.978l-.385.384a.8.8 0 1 0 1.131 1.131z"
25776
- }))), _defs$3 || (_defs$3 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
26497
+ }))), _defs || (_defs = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("linearGradient", {
25777
26498
  id: "transform-translate_svg__b",
25778
26499
  x1: 17.727,
25779
26500
  x2: -1.215,
@@ -25808,6 +26529,7 @@ const VisionOffTransformWrapper = ({ data, isSelected = false, isHover = false,
25808
26529
  // 使用自定义hook管理所有变换逻辑
25809
26530
  const { currentPoints, isDragging, isRotating, isScaling, dimensions, initializePoints, calculateSelectionBoxPoints, handleMouseDown, handleRotateStart, handleScaleStart, handleMouseMove, handleMouseUp, containerRef, // 从hook获取containerRef
25810
26531
  } = useVisionOffTransform(dataPoints);
26532
+ const [hoverType, setHoverType] = React.useState(null);
25811
26533
  const visionOffData = React.useMemo(() => {
25812
26534
  return {
25813
26535
  ...data,
@@ -25968,7 +26690,7 @@ const VisionOffTransformWrapper = ({ data, isSelected = false, isHover = false,
25968
26690
  }
25969
26691
  : {
25970
26692
  onMouseDown: handleCancel,
25971
- }), transform: `translate(${selectionBoxPoints[0].x}, ${selectionBoxPoints[0].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformDelete, {}) }) })), jsxRuntime.jsx("g", { className: "control-point rotate-point", ...(platform === exports.PlatformType.H5
26693
+ }), transform: `translate(${selectionBoxPoints[0].x}, ${selectionBoxPoints[0].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Delete ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Delete), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformDelete, {}) }) })), jsxRuntime.jsx("g", { className: "control-point rotate-point", ...(platform === exports.PlatformType.H5
25972
26694
  ? {
25973
26695
  onTouchStart: createReactEventHandler((e) => {
25974
26696
  handleRotateStartWithDisabled(e);
@@ -25976,7 +26698,7 @@ const VisionOffTransformWrapper = ({ data, isSelected = false, isHover = false,
25976
26698
  }
25977
26699
  : {
25978
26700
  onMouseDown: handleRotateStartWithDisabled,
25979
- }), transform: `translate(${selectionBoxPoints[3].x}, ${selectionBoxPoints[3].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformRotate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point move-point", ...(platform === exports.PlatformType.H5
26701
+ }), transform: `translate(${selectionBoxPoints[3].x}, ${selectionBoxPoints[3].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Rotate ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Rotate), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformRotate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point move-point", ...(platform === exports.PlatformType.H5
25980
26702
  ? {
25981
26703
  onTouchStart: createReactEventHandler((e) => {
25982
26704
  handleMouseDownWithDisabled(e);
@@ -25984,7 +26706,7 @@ const VisionOffTransformWrapper = ({ data, isSelected = false, isHover = false,
25984
26706
  }
25985
26707
  : {
25986
26708
  onMouseDown: handleMouseDownWithDisabled,
25987
- }), transform: `translate(${selectionBoxPoints[1].x}, ${selectionBoxPoints[1].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformTranslate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point scale-point", ...(platform === exports.PlatformType.H5
26709
+ }), transform: `translate(${selectionBoxPoints[1].x}, ${selectionBoxPoints[1].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Translate ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Translate), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformTranslate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point scale-point", ...(platform === exports.PlatformType.H5
25988
26710
  ? {
25989
26711
  onTouchStart: createReactEventHandler((e) => {
25990
26712
  handleScaleStartWithDisabled(e);
@@ -25992,7 +26714,7 @@ const VisionOffTransformWrapper = ({ data, isSelected = false, isHover = false,
25992
26714
  }
25993
26715
  : {
25994
26716
  onMouseDown: handleScaleStartWithDisabled,
25995
- }), transform: `translate(${selectionBoxPoints[2].x}, ${selectionBoxPoints[2].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformScale, {}) }) })] }));
26717
+ }), transform: `translate(${selectionBoxPoints[2].x}, ${selectionBoxPoints[2].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Scale ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Scale), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformScale, {}) }) })] }));
25996
26718
  })()] }));
25997
26719
  };
25998
26720
 
@@ -26446,100 +27168,9 @@ const useDoodleTransform = (data, onTransformChange, options) => {
26446
27168
  };
26447
27169
  };
26448
27170
 
26449
- var _g$2, _defs$2;
26450
- function _extends$3() { return _extends$3 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$3.apply(null, arguments); }
26451
- var SvgDisabledRotate = function SvgDisabledRotate(props) {
26452
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$3({
26453
- xmlns: "http://www.w3.org/2000/svg",
26454
- width: 16,
26455
- height: 16,
26456
- fill: "none"
26457
- }, props), _g$2 || (_g$2 = /*#__PURE__*/React__namespace.createElement("g", {
26458
- clipPath: "url(#disabled-rotate_svg__a)"
26459
- }, /*#__PURE__*/React__namespace.createElement("path", {
26460
- fill: "#fff",
26461
- d: "M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"
26462
- }), /*#__PURE__*/React__namespace.createElement("path", {
26463
- fill: "#211F1F",
26464
- fillOpacity: 0.4,
26465
- d: "M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"
26466
- }), /*#__PURE__*/React__namespace.createElement("path", {
26467
- fill: "#fff",
26468
- fillOpacity: 0.7,
26469
- d: "M9.192 11.775a4.36 4.36 0 0 1-5.277-3.93l-.013-.326v-.002a.655.655 0 0 1 1.31 0v.002a3.05 3.05 0 0 0 1.71 2.738l.208.091a3.05 3.05 0 0 0 1.781.147l.22-.057q.234-.07.451-.175l-.919-.354a.653.653 0 1 1 .47-1.22l2.033.782a.654.654 0 0 1 .251 1.047l.002.001a4.36 4.36 0 0 1-2.227 1.256M6.809 3.639a4.36 4.36 0 0 1 5.289 4.256v.002a.654.654 0 1 1-1.309 0v-.002a3.05 3.05 0 0 0-1.71-2.738l-.208-.091a3.05 3.05 0 0 0-1.781-.147l-.22.057a3 3 0 0 0-.451.175l.919.354a.654.654 0 0 1-.47 1.22l-2.033-.782a.654.654 0 0 1-.251-1.046l-.002-.002a4.36 4.36 0 0 1 2.227-1.256"
26470
- }))), _defs$2 || (_defs$2 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("clipPath", {
26471
- id: "disabled-rotate_svg__a"
26472
- }, /*#__PURE__*/React__namespace.createElement("path", {
26473
- fill: "#fff",
26474
- d: "M16 0H0v16h16z"
26475
- })))));
26476
- };
26477
-
26478
- var _g$1, _defs$1;
26479
- function _extends$2() { return _extends$2 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$2.apply(null, arguments); }
26480
- var SvgDisabledScale = function SvgDisabledScale(props) {
26481
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$2({
26482
- xmlns: "http://www.w3.org/2000/svg",
26483
- width: 16,
26484
- height: 16,
26485
- fill: "none"
26486
- }, props), _g$1 || (_g$1 = /*#__PURE__*/React__namespace.createElement("g", {
26487
- clipPath: "url(#disabled-scale_svg__a)"
26488
- }, /*#__PURE__*/React__namespace.createElement("path", {
26489
- fill: "#fff",
26490
- d: "M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"
26491
- }), /*#__PURE__*/React__namespace.createElement("path", {
26492
- fill: "#211F1F",
26493
- fillOpacity: 0.4,
26494
- d: "M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"
26495
- }), /*#__PURE__*/React__namespace.createElement("path", {
26496
- fill: "#fff",
26497
- fillOpacity: 0.7,
26498
- d: "M3.972 6.612a.64.64 0 0 0 1.28 0v-.437l4.554 4.554H9.41a.64.64 0 0 0 0 1.28h1.979a.64.64 0 0 0 .636-.606l.003-.036.001-1.98a.64.64 0 1 0-1.28.001v.474L6.158 5.27h.435a.64.64 0 0 0 0-1.28h-1.98a.64.64 0 0 0-.64.64z"
26499
- }))), _defs$1 || (_defs$1 = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("clipPath", {
26500
- id: "disabled-scale_svg__a"
26501
- }, /*#__PURE__*/React__namespace.createElement("path", {
26502
- fill: "#fff",
26503
- d: "M0 16h16V0H0z"
26504
- })))));
26505
- };
26506
-
26507
- var _g, _defs;
26508
- function _extends$1() { return _extends$1 = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends$1.apply(null, arguments); }
26509
- var SvgDisabledTranslate = function SvgDisabledTranslate(props) {
26510
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$1({
26511
- xmlns: "http://www.w3.org/2000/svg",
26512
- width: 16,
26513
- height: 16,
26514
- fill: "none"
26515
- }, props), _g || (_g = /*#__PURE__*/React__namespace.createElement("g", {
26516
- clipPath: "url(#disabled-translate_svg__a)"
26517
- }, /*#__PURE__*/React__namespace.createElement("circle", {
26518
- cx: 8,
26519
- cy: 8,
26520
- r: 8,
26521
- fill: "#fff"
26522
- }), /*#__PURE__*/React__namespace.createElement("circle", {
26523
- cx: 8,
26524
- cy: 8,
26525
- r: 8,
26526
- fill: "#211F1F",
26527
- fillOpacity: 0.4
26528
- }), /*#__PURE__*/React__namespace.createElement("path", {
26529
- fill: "#fff",
26530
- fillOpacity: 0.7,
26531
- d: "M12.944 8.608a.64.64 0 0 0-.079-.97l-1.333-1.334a.64.64 0 0 0-.905.905l.305.305H8.564V5.136l.308.308a.642.642 0 0 0 .906-.906l-1.4-1.399a.64.64 0 0 0-.906 0q-.036.037-.064.08L6.075 4.55a.64.64 0 0 0 .905.905l.305-.304v2.362H4.91l.306-.306a.64.64 0 0 0-.904-.904l-1.4 1.4a.64.64 0 0 0 .077.97l1.334 1.333a.64.64 0 0 0 .906-.905l-.31-.309h2.367v2.38l-.304-.305a.64.64 0 1 0-.905.906l1.4 1.4a.64.64 0 0 0 .97-.08l1.332-1.333a.64.64 0 0 0-.905-.905l-.309.308v-2.37h2.385l-.31.309a.64.64 0 1 0 .905.904z"
26532
- }))), _defs || (_defs = /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("clipPath", {
26533
- id: "disabled-translate_svg__a"
26534
- }, /*#__PURE__*/React__namespace.createElement("path", {
26535
- fill: "#fff",
26536
- d: "M16 0H0v16h16z"
26537
- })))));
26538
- };
26539
-
26540
- var css_248z$5 = ".index-module_doodleHover__jIZHV path {\n fill: #2EC1B2;\n}";
26541
- var styles$5 = {"doodleHover":"index-module_doodleHover__jIZHV"};
26542
- styleInject(css_248z$5);
27171
+ var css_248z$6 = ".index-module_doodleHover__jIZHV path {\n fill: #00B3A1;\n}";
27172
+ var styles$6 = {"doodleHover":"index-module_doodleHover__jIZHV"};
27173
+ styleInject(css_248z$6);
26543
27174
 
26544
27175
  const DoodleTransform = ({ data, isSelected: _isSelected, isHover, onSelect }) => {
26545
27176
  const { editMapInfo } = useMapEditContext();
@@ -26598,7 +27229,7 @@ const DoodleTransform = ({ data, isSelected: _isSelected, isHover, onSelect }) =
26598
27229
  return (jsxRuntime.jsx("g", { children: jsxRuntime.jsx("g", { style: {
26599
27230
  cursor: canSelect ? 'pointer' : 'inherit',
26600
27231
  pointerEvents: canSelect ? 'auto' : 'none',
26601
- }, className: isHover ? styles$5.doodleHover : '', transform: transform, opacity: data?.style?.opacity || 1, dangerouslySetInnerHTML: { __html: svgString }, onClick: onDoodleClick }) }));
27232
+ }, className: isHover ? styles$6.doodleHover : '', transform: transform, opacity: data?.style?.opacity || 1, dangerouslySetInnerHTML: { __html: svgString }, onClick: onDoodleClick }) }));
26602
27233
  }
26603
27234
  }
26604
27235
  catch (error) {
@@ -26608,12 +27239,13 @@ const DoodleTransform = ({ data, isSelected: _isSelected, isHover, onSelect }) =
26608
27239
  };
26609
27240
 
26610
27241
  const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOperation = false, isHover = false, onSelect, onCancel, onTransformChange, disabled = false, className = '', minScale, onMinScaleReached, onDragMove, onDragEnd, showInfo = true, onClickInfo, }) => {
26611
- const { platform, overlayScale, unitType } = useCommonContext();
27242
+ const { platform, overlayScale, unitType, locale } = useCommonContext();
26612
27243
  // 使用自定义hook管理所有变换逻辑
26613
27244
  const { currentCenter, currentScale, currentDirection, isDragging, isRotating, isScaling, initializeTransform, calculateSelectionBoxPoints, handleMouseDown, handleRotateStart, handleScaleStart, handleMouseMove, handleMouseUp, containerRef, dimensions, } = useDoodleTransform(data, onTransformChange, {
26614
27245
  minScale,
26615
27246
  onMinScaleReached,
26616
27247
  });
27248
+ const [hoverType, setHoverType] = React.useState(null);
26617
27249
  // 创建变换后的数据对象 - 需要将转换后的坐标转换回原始格式
26618
27250
  const transformedData = React.useMemo(() => {
26619
27251
  // 将转换后的坐标转换回原始格式(除以 SCALE_FACTOR 并翻转 Y 轴)
@@ -26735,7 +27367,6 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26735
27367
  const infoBoxHeight = 40 * overlayScale; // 信息框高度
26736
27368
  const infoBoxX = centerX - infoBoxWidth / 2; // 居中对齐
26737
27369
  const circleScale = platform === exports.PlatformType.H5 ? 1.5 * overlayScale : 2 * overlayScale;
26738
- const disacledScale = circleScale * 1.2;
26739
27370
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("polygon", { points: selectionBoxPoints.map((point) => `${point.x},${point.y}`).join(' '), fill: "none", stroke: "#B2B4B9", strokeWidth: "2", strokeDasharray: "5,5", vectorEffect: "non-scaling-stroke", ...(platform === exports.PlatformType.H5
26740
27371
  ? {
26741
27372
  onTouchStart: createReactEventHandler((e) => {
@@ -26744,7 +27375,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26744
27375
  }
26745
27376
  : {
26746
27377
  // onMouseDown: handleMouseDownWithDisabled,
26747
- }) }), jsxRuntime.jsx("g", { transform: `translate(${selectionBoxPoints[3].x}, ${selectionBoxPoints[3].y})`, children: jsxRuntime.jsx("g", { transformOrigin: "center", transform: `translate(-${10 * circleScale}, -${10 * circleScale}) scale(${disacledScale})`, children: jsxRuntime.jsx(SvgDisabledRotate, {}) }) }), jsxRuntime.jsx("g", { transform: `translate(${selectionBoxPoints[1].x}, ${selectionBoxPoints[1].y})`, children: jsxRuntime.jsx("g", { transformOrigin: "center", transform: `translate(-${10 * circleScale}, -${10 * circleScale}) scale(${disacledScale})`, children: jsxRuntime.jsx(SvgDisabledTranslate, {}) }) }), jsxRuntime.jsx("g", { transform: `translate(${selectionBoxPoints[2].x}, ${selectionBoxPoints[2].y})`, children: jsxRuntime.jsx("g", { transformOrigin: "center", transform: `translate(-${10 * circleScale}, -${10 * circleScale}) scale(${disacledScale})`, children: jsxRuntime.jsx(SvgDisabledScale, {}) }) }), platform === exports.PlatformType.H5 && (jsxRuntime.jsx("foreignObject", { x: infoBoxX, y: infoBoxY, width: infoBoxWidth, height: infoBoxHeight, style: { textAlign: 'center' }, onClick: () => {
27378
+ }) }), jsxRuntime.jsx("circle", { cx: selectionBoxPoints[0].x, cy: selectionBoxPoints[0].y, r: 10 * circleScale, fill: "#B2B4B9", stroke: "none" }), jsxRuntime.jsx("circle", { cx: selectionBoxPoints[3].x, cy: selectionBoxPoints[3].y, r: 10 * circleScale, fill: "#B2B4B9", stroke: "none" }), jsxRuntime.jsx("circle", { cx: selectionBoxPoints[1].x, cy: selectionBoxPoints[1].y, r: 10 * circleScale, fill: "#B2B4B9", stroke: "none" }), jsxRuntime.jsx("circle", { cx: selectionBoxPoints[2].x, cy: selectionBoxPoints[2].y, r: 10 * circleScale, fill: "#B2B4B9", stroke: "none" }), platform === exports.PlatformType.H5 && (jsxRuntime.jsx("foreignObject", { x: infoBoxX, y: infoBoxY, width: infoBoxWidth, height: infoBoxHeight, style: { textAlign: 'center' }, onClick: () => {
26748
27379
  onClickInfo?.();
26749
27380
  }, children: jsxRuntime.jsx("div", { style: {
26750
27381
  padding: `5px ${10 * overlayScale}px`,
@@ -26764,7 +27395,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26764
27395
  justifyContent: 'center',
26765
27396
  gap: '10px',
26766
27397
  color: '#325069',
26767
- }, children: jsxRuntime.jsxs("div", { children: ["Remaining time: ", remainingTime] }) }) }) }))] }));
27398
+ }, children: jsxRuntime.jsxs("div", { children: [locale?.['map.renderer.remainTime'] || 'Remaining time', ": ", remainingTime] }) }) }) }))] }));
26768
27399
  };
26769
27400
  React.useEffect(() => {
26770
27401
  const isMove = isDragging || isRotating || isScaling;
@@ -26816,7 +27447,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26816
27447
  const infoBoxWidth = 300 * overlayScale; // 信息框宽度
26817
27448
  const infoBoxHeight = 100 * overlayScale; // 信息框高度
26818
27449
  const infoBoxX = centerX - infoBoxWidth / 2; // 居中对齐
26819
- const scale = platform === exports.PlatformType.H5 ? 1.8 * overlayScale : 2 * overlayScale;
27450
+ const scale = platform === exports.PlatformType.H5 ? 1.8 * overlayScale : 3 * overlayScale;
26820
27451
  const offsetLeft = 10 * scale;
26821
27452
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [showInfo && (jsxRuntime.jsx("foreignObject", { x: infoBoxX, y: infoBoxY, width: infoBoxWidth, height: infoBoxHeight, style: { textAlign: 'center' }, onClick: () => {
26822
27453
  onClickInfo?.();
@@ -26837,7 +27468,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26837
27468
  alignItems: 'center',
26838
27469
  justifyContent: 'center',
26839
27470
  gap: `${10 * overlayScale}px`,
26840
- }, children: jsxRuntime.jsxs("div", { children: ["Active Time: ", activeTime, " >"] }) })] }) })), jsxRuntime.jsx("polygon", { points: selectionBoxPoints.map((point) => `${point.x},${point.y}`).join(' '), fill: "none", stroke: "#9EA6BA", strokeWidth: "2", strokeDasharray: "5,5", vectorEffect: "non-scaling-stroke", style: { cursor: 'move' }, ...(platform === exports.PlatformType.H5
27471
+ }, children: jsxRuntime.jsxs("div", { children: [locale?.['map.renderer.activeTime'] || 'Active Time', ": ", activeTime, " >"] }) })] }) })), jsxRuntime.jsx("polygon", { points: selectionBoxPoints.map((point) => `${point.x},${point.y}`).join(' '), fill: "none", stroke: "#9EA6BA", strokeWidth: "2", strokeDasharray: "5,5", vectorEffect: "non-scaling-stroke", style: { cursor: 'move' }, ...(platform === exports.PlatformType.H5
26841
27472
  ? {
26842
27473
  onTouchStart: createReactEventHandler((e) => {
26843
27474
  handleMouseDownWithDisabled(e);
@@ -26853,7 +27484,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26853
27484
  }
26854
27485
  : {
26855
27486
  onMouseDown: handleCancel,
26856
- }), transform: `translate(${selectionBoxPoints[0].x}, ${selectionBoxPoints[0].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformDelete, {}) }) })), jsxRuntime.jsx("g", { className: "control-point rotate-point", ...(platform === exports.PlatformType.H5
27487
+ }), transform: `translate(${selectionBoxPoints[0].x}, ${selectionBoxPoints[0].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Delete ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Delete), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformDelete, {}) }) })), jsxRuntime.jsx("g", { className: "control-point rotate-point", ...(platform === exports.PlatformType.H5
26857
27488
  ? {
26858
27489
  onTouchStart: createReactEventHandler((e) => {
26859
27490
  handleRotateStartWithDisabled(e);
@@ -26861,7 +27492,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26861
27492
  }
26862
27493
  : {
26863
27494
  onMouseDown: handleRotateStartWithDisabled,
26864
- }), transform: `translate(${selectionBoxPoints[3].x}, ${selectionBoxPoints[3].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformRotate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point move-point", ...(platform === exports.PlatformType.H5
27495
+ }), transform: `translate(${selectionBoxPoints[3].x}, ${selectionBoxPoints[3].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Rotate ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Rotate), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformRotate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point move-point", ...(platform === exports.PlatformType.H5
26865
27496
  ? {
26866
27497
  onTouchStart: createReactEventHandler((e) => {
26867
27498
  handleMouseDownWithDisabled(e);
@@ -26869,7 +27500,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26869
27500
  }
26870
27501
  : {
26871
27502
  onMouseDown: handleMouseDownWithDisabled,
26872
- }), transform: `translate(${selectionBoxPoints[1].x}, ${selectionBoxPoints[1].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformTranslate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point scale-point", ...(platform === exports.PlatformType.H5
27503
+ }), transform: `translate(${selectionBoxPoints[1].x}, ${selectionBoxPoints[1].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Translate ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Translate), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformTranslate, {}) }) }), jsxRuntime.jsx("g", { className: "control-point scale-point", ...(platform === exports.PlatformType.H5
26873
27504
  ? {
26874
27505
  onTouchStart: createReactEventHandler((e) => {
26875
27506
  handleScaleStartWithDisabled(e);
@@ -26877,7 +27508,7 @@ const DoodleTransformWrapper = ({ data, isSelected = false, isSelectedWithoutOpe
26877
27508
  }
26878
27509
  : {
26879
27510
  onMouseDown: handleScaleStartWithDisabled,
26880
- }), transform: `translate(${selectionBoxPoints[2].x}, ${selectionBoxPoints[2].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale})`, children: jsxRuntime.jsx(SvgTransformScale, {}) }) })] }));
27511
+ }), transform: `translate(${selectionBoxPoints[2].x}, ${selectionBoxPoints[2].y})`, children: jsxRuntime.jsx("g", { style: { cursor: 'pointer' }, transformOrigin: "center", transform: `translate(-${offsetLeft}, -${offsetLeft}) scale(${scale * (hoverType === HoverType.Scale ? 1.15 : 1)})`, onMouseEnter: () => setHoverType(HoverType.Scale), onMouseLeave: () => setHoverType(null), children: jsxRuntime.jsx(SvgTransformScale, {}) }) })] }));
26881
27512
  })()] }));
26882
27513
  };
26883
27514
 
@@ -27318,10 +27949,7 @@ const PathElement = React.forwardRef(({ mapConfig, pathData, mowPartitionData },
27318
27949
  return;
27319
27950
  }
27320
27951
  // 构建路径数据
27321
- let pathData = `M ${points[0][0]} ${points[0][1]}`;
27322
- for (let i = 1; i < points.length; i++) {
27323
- pathData += ` L ${points[i][0]} ${points[i][1]}`;
27324
- }
27952
+ const pathData = getPathData(points);
27325
27953
  // 根据路径类型设置不同的颜色
27326
27954
  let lineColor;
27327
27955
  if (pathType === PathSegmentType.TRANS) {
@@ -27354,7 +27982,7 @@ const PathElement = React.forwardRef(({ mapConfig, pathData, mowPartitionData },
27354
27982
  const result = [];
27355
27983
  partitionTypeGroups.forEach((groupData, groupKey) => {
27356
27984
  const { pathData, style } = groupData;
27357
- if (pathData.length === 0)
27985
+ if (!pathData || pathData.length === 0)
27358
27986
  return;
27359
27987
  // 从groupKey中提取分区ID
27360
27988
  const partitionId = groupKey.split('-')[0];
@@ -27362,8 +27990,8 @@ const PathElement = React.forwardRef(({ mapConfig, pathData, mowPartitionData },
27362
27990
  const clipPathId = clipPathData[partitionId]?.id;
27363
27991
  if (!clipPathId)
27364
27992
  return;
27365
- return result.push({
27366
- path: (pathData || []).join(' '),
27993
+ result.push({
27994
+ path: pathData.join(' '),
27367
27995
  style,
27368
27996
  clipPathId: clipPathId,
27369
27997
  });
@@ -27378,7 +28006,7 @@ const PathElement = React.forwardRef(({ mapConfig, pathData, mowPartitionData },
27378
28006
  return (jsxRuntime.jsxs("g", { id: "path", children: [jsxRuntime.jsx("defs", { children: Object.values(clipPathData).map((item) => {
27379
28007
  return (jsxRuntime.jsx("clipPath", { id: item.id, clipRule: "evenodd", children: jsxRuntime.jsx("path", { d: item.path }) }, item.id));
27380
28008
  }) }), Object.values(pathDataObj).map((item) => {
27381
- return (jsxRuntime.jsx("g", { "clip-path": `url(#${item.clipPathId})`, opacity: "0.5", children: jsxRuntime.jsx("path", { className: "vector-path", d: item.path, fill: "none", "mix-blend-mode": "normal", "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": item.style.lineWidth, stroke: item.style.lineColor }) }));
28009
+ return (jsxRuntime.jsx("g", { "clip-path": `url(#${item.clipPathId})`, opacity: "0.5", children: jsxRuntime.jsx("path", { className: "vector-path", d: item.path, fill: "none", "mix-blend-mode": "normal", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: item.style.lineWidth, stroke: item.style.lineColor }) }));
27382
28010
  })] }));
27383
28011
  });
27384
28012
 
@@ -27435,9 +28063,9 @@ const SvgMapComponent = React.forwardRef(({ editMap, pathData, mapConfig = {}, m
27435
28063
  // 设置显示名称以便调试
27436
28064
  SvgMapComponent.displayName = 'SvgMapComponent';
27437
28065
 
27438
- var css_248z$4 = ".index-module_mowerPosition__yLpIU {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 100%;\n}";
27439
- var styles$4 = {"mowerPosition":"index-module_mowerPosition__yLpIU"};
27440
- styleInject(css_248z$4);
28066
+ var css_248z$5 = ".index-module_mowerPosition__yLpIU {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n width: 100%;\n height: 100%;\n}";
28067
+ var styles$5 = {"mowerPosition":"index-module_mowerPosition__yLpIU"};
28068
+ styleInject(css_248z$5);
27441
28069
 
27442
28070
  const usePosition = ({ mowerPositionData, viewBox, modelType, hasEdger, mapData, realTimeData, onMowingPositionChange, }) => {
27443
28071
  // DOM 元素引用,用于直接操作
@@ -27596,10 +28224,10 @@ const usePosition = ({ mowerPositionData, viewBox, modelType, hasEdger, mapData,
27596
28224
  }
27597
28225
  else {
27598
28226
  // 检查位置是否有效
27599
- const positionOutOfRange = isOutOfRange(mowerPositionData);
28227
+ // const positionOutOfRange = isOutOfRange(mowerPositionData);
27600
28228
  const positionInvalid = isInvalidPosition(mowerPositionData);
27601
28229
  const isStandby = mowerPositionData.vehicleState === RobotStatus.STANDBY;
27602
- if (positionOutOfRange || positionInvalid || isOffLine) {
28230
+ if (positionInvalid || isOffLine) {
27603
28231
  // 位置无效时,尝试使用上次位置或充电桩位置
27604
28232
  updatePositionByLastPosition(mowerPositionData);
27605
28233
  }
@@ -27663,7 +28291,7 @@ const MowerPosition = React.memo(({ editMap, mowerPositionData, viewBox, modelTy
27663
28291
  zIndex: 1000,
27664
28292
  opacity: isHighlight ? SvgMapOpacity.HIGHLIGHT : SvgMapOpacity.UN_HIGHLIGHT,
27665
28293
  }), [isHighlight]);
27666
- return (jsxRuntime.jsx("div", { className: styles$4.mowerPosition, style: containerStyle, children: jsxRuntime.jsx("div", { ref: elementRef, className: "mower-position", style: {
28294
+ return (jsxRuntime.jsx("div", { className: styles$5.mowerPosition, style: containerStyle, children: jsxRuntime.jsx("div", { ref: elementRef, className: "mower-position", style: {
27667
28295
  position: 'absolute',
27668
28296
  width: 30,
27669
28297
  height: 30,
@@ -27675,11 +28303,12 @@ const MowerPosition = React.memo(({ editMap, mowerPositionData, viewBox, modelTy
27675
28303
  }, children: jsxRuntime.jsx("img", { src: mowerImage, style: { width: '100%', height: '100%', objectFit: 'contain' } }) }) }));
27676
28304
  });
27677
28305
 
27678
- var css_248z$3 = ".index-module_container__7IatW {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n\n.index-module_boundaryLabel__GDHFn {\n position: absolute;\n background-color: rgba(30, 30, 31, 0.6);\n color: rgb(255, 255, 255);\n padding: 6px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: bold;\n max-width: 220px;\n pointer-events: auto;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n}\n.index-module_boundaryLabel__GDHFn .index-module_isolatedIcon__1pRXN {\n position: absolute;\n width: 24px;\n height: 24px;\n top: -30px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.index-module_base__qy2SG {\n font-size: 14px;\n}\n\n.index-module_extended__0TMAt {\n margin-top: 6px;\n font-size: 11px;\n opacity: 0.9;\n border-top: 1px solid rgba(255, 255, 255, 0.2);\n padding-top: 6px;\n}";
27679
- var styles$3 = {"container":"index-module_container__7IatW","boundaryLabel":"index-module_boundaryLabel__GDHFn","isolatedIcon":"index-module_isolatedIcon__1pRXN","base":"index-module_base__qy2SG","extended":"index-module_extended__0TMAt"};
27680
- styleInject(css_248z$3);
28306
+ var css_248z$4 = ".index-module_container__7IatW {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n\n.index-module_boundaryLabel__GDHFn {\n position: absolute;\n background-color: rgba(30, 30, 31, 0.6);\n color: rgb(255, 255, 255);\n padding: 6px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: bold;\n max-width: 220px;\n pointer-events: auto;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n}\n.index-module_boundaryLabel__GDHFn .index-module_isolatedIcon__1pRXN {\n position: absolute;\n width: 24px;\n height: 24px;\n top: -30px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.index-module_base__qy2SG {\n font-size: 14px;\n}\n\n.index-module_extended__0TMAt {\n margin-top: 6px;\n font-size: 11px;\n opacity: 0.9;\n border-top: 1px solid rgba(255, 255, 255, 0.2);\n padding-top: 6px;\n}";
28307
+ var styles$4 = {"container":"index-module_container__7IatW","boundaryLabel":"index-module_boundaryLabel__GDHFn","isolatedIcon":"index-module_isolatedIcon__1pRXN","base":"index-module_base__qy2SG","extended":"index-module_extended__0TMAt"};
28308
+ styleInject(css_248z$4);
27681
28309
 
27682
28310
  const useBoundaryLabels = ({ mapData, pathData, unitType, viewBox, mowPartitionData, realTimeData = [], onlyRead = false, }) => {
28311
+ const { locale } = useCommonContext();
27683
28312
  const [processStateIsMowing, updateProcessStateIsMowing] = React.useState(false);
27684
28313
  // 处理地图分区边界
27685
28314
  React.useMemo(() => {
@@ -27719,6 +28348,9 @@ const useBoundaryLabels = ({ mapData, pathData, unitType, viewBox, mowPartitionD
27719
28348
  return generateBoundaryData(mapData, newPathData);
27720
28349
  }
27721
28350
  }, [mapData, pathData, realTimeData]);
28351
+ const coverageStr = React.useMemo(() => {
28352
+ return locale?.['map.renderer.coverage'] || 'Coverage';
28353
+ }, [locale]);
27722
28354
  const items = React.useMemo(() => {
27723
28355
  // console.log('boundartItme->', viewBox, mapData)
27724
28356
  if (!mapData || !viewBox)
@@ -27728,7 +28360,7 @@ const useBoundaryLabels = ({ mapData, pathData, unitType, viewBox, mowPartitionD
27728
28360
  const results = [];
27729
28361
  for (const boundary of boundaryData) {
27730
28362
  // 多边形重心
27731
- const centroid = computePolygonCentroid(boundary.points);
28363
+ const centroid = computePolygonCentroid$1(boundary.points);
27732
28364
  if (!centroid)
27733
28365
  continue;
27734
28366
  const relX = (centroid.x - viewBox.x) / viewBox.width;
@@ -27739,9 +28371,9 @@ const useBoundaryLabels = ({ mapData, pathData, unitType, viewBox, mowPartitionD
27739
28371
  const baseText = onlyRead ? `${boundary.name}` : `${boundary.name} (${progress})`;
27740
28372
  const totalArea = convertAreaByUnits(boundary.area || 0, unitType);
27741
28373
  const finishedArea = convertAreaByUnits(boundary.finishedArea || 0, unitType);
27742
- const coverageText = `Coverage: ${finishedArea.value}/${totalArea.value}`;
28374
+ const coverageText = `${coverageStr}: ${finishedArea.value}/${totalArea.value}`;
27743
28375
  const isMowing = mowingIds.includes(boundary.id);
27744
- const dateText = formatBoundaryDateText(isMowing ? Date.now() / 1000 : boundary.endTime || 0);
28376
+ const dateText = formatBoundaryDateText(isMowing ? Date.now() / 1000 : boundary.endTime || 0, locale);
27745
28377
  results.push({
27746
28378
  id: boundary.id,
27747
28379
  name: boundary.name,
@@ -27765,11 +28397,13 @@ const useBoundaryLabels = ({ mapData, pathData, unitType, viewBox, mowPartitionD
27765
28397
  mowPartitionData,
27766
28398
  boundaryData,
27767
28399
  onlyRead,
28400
+ coverageStr,
28401
+ locale,
27768
28402
  ]);
27769
28403
  const containerZIndex = 900;
27770
28404
  return { items, containerZIndex };
27771
28405
  };
27772
- function computePolygonCentroid(points) {
28406
+ function computePolygonCentroid$1(points) {
27773
28407
  if (!points || points.length < 3)
27774
28408
  return null;
27775
28409
  const valid = points.filter((p) => p.length >= 2);
@@ -27842,7 +28476,7 @@ const BoundaryLabels = React.memo(({ editMap, mapData, pathData, unitType, langu
27842
28476
  display: editMap ? 'none' : 'block',
27843
28477
  opacity: isHighlight ? SvgMapOpacity.HIGHLIGHT : SvgMapOpacity.UN_HIGHLIGHT,
27844
28478
  }), [containerZIndex, expandedId, editMap, isHighlight]);
27845
- return (jsxRuntime.jsx("div", { className: styles$3.container, style: containerStyle, children: items.map((item) => (jsxRuntime.jsxs("div", { className: `${styles$3.boundaryLabel} boundary-label`, "data-boundary-id": item.id, style: {
28479
+ return (jsxRuntime.jsx("div", { className: styles$4.container, style: containerStyle, children: items.map((item) => (jsxRuntime.jsxs("div", { className: `${styles$4.boundaryLabel} boundary-label`, "data-boundary-id": item.id, style: {
27846
28480
  transform: `translate(-50%, -50%) rotate(${-rotation}deg)`,
27847
28481
  left: `${item.leftPct}%`,
27848
28482
  top: `${item.topPct}%`,
@@ -27853,12 +28487,12 @@ const BoundaryLabels = React.memo(({ editMap, mapData, pathData, unitType, langu
27853
28487
  if (onlyRead)
27854
28488
  return;
27855
28489
  handleClick(item.id);
27856
- }, children: [jsxRuntime.jsx("div", { className: styles$3.base, children: item.baseText }), jsxRuntime.jsxs("div", { className: styles$3.extended, style: { display: expandedId === item.id ? 'block' : 'none' }, children: [jsxRuntime.jsx("div", { style: { marginBottom: 3, fontWeight: 'bold' }, children: item.coverageText }), jsxRuntime.jsx("div", { children: item.dateText })] }), item.isIsolated && (jsxRuntime.jsx("div", { className: styles$3.isolatedIcon, dangerouslySetInnerHTML: { __html: ISOLATED_BOUNDARY_SVG } }))] }, item.id))) }));
28490
+ }, children: [jsxRuntime.jsx("div", { className: styles$4.base, children: item.baseText }), jsxRuntime.jsxs("div", { className: styles$4.extended, style: { display: expandedId === item.id ? 'block' : 'none' }, children: [jsxRuntime.jsx("div", { style: { marginBottom: 3, fontWeight: 'bold' }, children: item.coverageText }), jsxRuntime.jsx("div", { children: item.dateText })] }), item.isIsolated && (jsxRuntime.jsx("div", { className: styles$4.isolatedIcon, dangerouslySetInnerHTML: { __html: ISOLATED_BOUNDARY_SVG } }))] }, item.id))) }));
27857
28491
  });
27858
28492
 
27859
- var css_248z$2 = ".index-module_container__gB52e {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n\n.index-module_item__zHoZP {\n position: absolute;\n pointer-events: auto;\n}\n\n.index-module_icon__CS56A {\n position: relative;\n width: 24px;\n height: 24px;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n\n.index-module_tooltip__NMCmR {\n position: absolute;\n bottom: 40px;\n left: 0;\n background-color: rgb(247, 251, 255);\n color: rgb(65, 93, 116);\n padding: 10px;\n border-radius: 16px;\n font-size: 14px;\n text-align: left;\n}";
27860
- var styles$2 = {"container":"index-module_container__gB52e","item":"index-module_item__zHoZP","icon":"index-module_icon__CS56A","tooltip":"index-module_tooltip__NMCmR"};
27861
- styleInject(css_248z$2);
28493
+ var css_248z$3 = ".index-module_container__gB52e {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n\n.index-module_item__zHoZP {\n position: absolute;\n pointer-events: auto;\n}\n\n.index-module_icon__CS56A {\n position: relative;\n width: 24px;\n height: 24px;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n\n.index-module_tooltip__NMCmR {\n position: absolute;\n bottom: 40px;\n left: 0;\n background-color: rgb(247, 251, 255);\n color: rgb(65, 93, 116);\n padding: 10px;\n border-radius: 16px;\n font-size: 14px;\n text-align: left;\n}";
28494
+ var styles$3 = {"container":"index-module_container__gB52e","item":"index-module_item__zHoZP","icon":"index-module_icon__CS56A","tooltip":"index-module_tooltip__NMCmR"};
28495
+ styleInject(css_248z$3);
27862
28496
 
27863
28497
  var antennaOneOnline = "data:image/png;base64,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";
27864
28498
 
@@ -27870,6 +28504,7 @@ var antennaTwoOffline = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgC
27870
28504
 
27871
28505
  const useAntennaInfo = (data = [], options = {}) => {
27872
28506
  const { viewBox } = options;
28507
+ const { locale } = useCommonContext();
27873
28508
  const items = React.useMemo(() => {
27874
28509
  if (!data || data.length === 0 || !viewBox)
27875
28510
  return [];
@@ -27900,8 +28535,8 @@ const useAntennaInfo = (data = [], options = {}) => {
27900
28535
  isOnline: antenna1.status === 1,
27901
28536
  imageSrc: getImage(1, antenna1.status === 1),
27902
28537
  tooltip: {
27903
- title: 'Primary Antenna',
27904
- statusText: antenna1.status === 1 ? 'Online' : 'Offline',
28538
+ title: locale?.['map.renderer.primaryAntenna'] || 'Primary Antenna',
28539
+ statusText: antenna1.status === 1 ? (locale?.['map.renderer.online'] || 'Online') : (locale?.['map.renderer.online'] || 'Offline'),
27905
28540
  minWidth: 130,
27906
28541
  },
27907
28542
  });
@@ -27940,15 +28575,15 @@ const useAntennaInfo = (data = [], options = {}) => {
27940
28575
  isOnline: antenna2.status === 1,
27941
28576
  imageSrc: getImage(2, antenna2.status === 1),
27942
28577
  tooltip: {
27943
- title: 'Signal Enhancement Antenna',
27944
- statusText: antenna2.status === 1 ? 'Online' : 'Offline',
27945
- syncText: antenna2.status === 1 ? ', Synced' : ', To be synced',
28578
+ title: locale?.['map.renderer.signalAntenna'] || 'Signal Enhancement Antenna',
28579
+ statusText: antenna2.status === 1 ? (locale?.['map.renderer.online'] || 'Online') : (locale?.['map.renderer.offline'] || 'Offline'),
28580
+ syncText: antenna2.status === 1 ? `, ${locale?.['map.renderer.synced'] || 'Synced'}` : `, ${locale?.['map.renderer.toBeSynced'] || 'To be synced'}`,
27946
28581
  minWidth: 195,
27947
28582
  },
27948
28583
  });
27949
28584
  }
27950
28585
  return results;
27951
- }, [data, viewBox?.x, viewBox?.y, viewBox?.width, viewBox?.height]);
28586
+ }, [data, viewBox?.x, viewBox?.y, viewBox?.width, viewBox?.height, locale]);
27952
28587
  // default stacking order (slightly higher than boundary labels' 900)
27953
28588
  const containerZIndex = 950;
27954
28589
  return { items, containerZIndex };
@@ -27976,13 +28611,13 @@ const Antennas = React.memo(({ editMap, antennaConfig = [], viewBox, rotation =
27976
28611
  document.addEventListener('pointerdown', handleOutside);
27977
28612
  return () => document.removeEventListener('pointerdown', handleOutside);
27978
28613
  }, []);
27979
- return (jsxRuntime.jsx("div", { className: styles$2.container, style: containerStyle, children: items.map((item) => (jsxRuntime.jsxs("div", { className: `antenna-container-item ${styles$2.item}`, style: {
28614
+ return (jsxRuntime.jsx("div", { className: styles$3.container, style: containerStyle, children: items.map((item) => (jsxRuntime.jsxs("div", { className: `antenna-container-item ${styles$3.item}`, style: {
27980
28615
  left: `${item.leftPct}%`,
27981
28616
  top: `${item.topPct}%`,
27982
28617
  transform: `translate(calc(-50% + ${item.offsetX || 0}px), calc(-50% + ${item.offsetY || 0}px)) rotate(${-rotation}deg)`,
27983
28618
  zIndex: openType === item.type ? 10000 : containerZIndex,
27984
28619
  pointerEvents: onlyRead ? 'none' : 'auto',
27985
- }, children: [jsxRuntime.jsx("div", { className: `antenna vector-antenna antenna-${item.type} ${item.isOnline ? 'antenna-online' : 'antenna-offline'} ${styles$2.icon}`, style: {}, onClick: (e) => {
28620
+ }, children: [jsxRuntime.jsx("div", { className: `antenna vector-antenna antenna-${item.type} ${item.isOnline ? 'antenna-online' : 'antenna-offline'} ${styles$3.icon}`, style: {}, onClick: (e) => {
27986
28621
  e.stopPropagation();
27987
28622
  if (onlyRead)
27988
28623
  return;
@@ -27997,95 +28632,15 @@ const Antennas = React.memo(({ editMap, antennaConfig = [], viewBox, rotation =
27997
28632
  return;
27998
28633
  e.currentTarget.style.transform =
27999
28634
  'scale(1)';
28000
- }, children: jsxRuntime.jsx("img", { src: item.imageSrc, style: { width: '100%', height: '100%', objectFit: 'contain', opacity: 1 } }) }), jsxRuntime.jsxs("div", { className: styles$2.tooltip, style: {
28635
+ }, children: jsxRuntime.jsx("img", { src: item.imageSrc, style: { width: '100%', height: '100%', objectFit: 'contain', opacity: 1 } }) }), jsxRuntime.jsxs("div", { className: styles$3.tooltip, style: {
28001
28636
  display: openType === item.type ? 'block' : 'none',
28002
28637
  minWidth: item.tooltip.minWidth,
28003
28638
  }, children: [jsxRuntime.jsx("div", { style: { marginBottom: 4, fontWeight: 600 }, children: item.tooltip.title }), jsxRuntime.jsxs("div", { style: { color: 'rgba(65, 93, 116, 1)', fontWeight: 400 }, children: [item.tooltip.statusText, item.tooltip.syncText || ''] })] })] }, `antenna-${item.type}`))) }));
28004
28639
  });
28005
28640
 
28006
- var css_248z$1 = ".index-module_mapEdit__XC5Gw {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n width: 100%;\n height: 100%;\n cursor: default;\n min-width: 1px;\n min-height: 1px;\n}\n.index-module_mapEdit__XC5Gw.index-module_createMode__14EPH {\n cursor: crosshair;\n}\n.index-module_mapEdit__XC5Gw.index-module_notCreateCursor__1-OjF {\n cursor: no-drop;\n}";
28007
- var styles$1 = {"mapEdit":"index-module_mapEdit__XC5Gw","createMode":"index-module_createMode__14EPH","notCreateCursor":"index-module_notCreateCursor__1-OjF"};
28008
- styleInject(css_248z$1);
28009
-
28010
- var classnames = {exports: {}};
28011
-
28012
- /*!
28013
- Copyright (c) 2018 Jed Watson.
28014
- Licensed under the MIT License (MIT), see
28015
- http://jedwatson.github.io/classnames
28016
- */
28017
-
28018
- (function (module) {
28019
- /* global define */
28020
-
28021
- (function () {
28022
-
28023
- var hasOwn = {}.hasOwnProperty;
28024
-
28025
- function classNames () {
28026
- var classes = '';
28027
-
28028
- for (var i = 0; i < arguments.length; i++) {
28029
- var arg = arguments[i];
28030
- if (arg) {
28031
- classes = appendClass(classes, parseValue(arg));
28032
- }
28033
- }
28034
-
28035
- return classes;
28036
- }
28037
-
28038
- function parseValue (arg) {
28039
- if (typeof arg === 'string' || typeof arg === 'number') {
28040
- return arg;
28041
- }
28042
-
28043
- if (typeof arg !== 'object') {
28044
- return '';
28045
- }
28046
-
28047
- if (Array.isArray(arg)) {
28048
- return classNames.apply(null, arg);
28049
- }
28050
-
28051
- if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
28052
- return arg.toString();
28053
- }
28054
-
28055
- var classes = '';
28056
-
28057
- for (var key in arg) {
28058
- if (hasOwn.call(arg, key) && arg[key]) {
28059
- classes = appendClass(classes, key);
28060
- }
28061
- }
28062
-
28063
- return classes;
28064
- }
28065
-
28066
- function appendClass (value, newClass) {
28067
- if (!newClass) {
28068
- return value;
28069
- }
28070
-
28071
- if (value) {
28072
- return value + ' ' + newClass;
28073
- }
28074
-
28075
- return value + newClass;
28076
- }
28077
-
28078
- if (module.exports) {
28079
- classNames.default = classNames;
28080
- module.exports = classNames;
28081
- } else {
28082
- window.classNames = classNames;
28083
- }
28084
- }());
28085
- } (classnames));
28086
-
28087
- var classnamesExports = classnames.exports;
28088
- var classNames = /*@__PURE__*/getDefaultExportFromCjs(classnamesExports);
28641
+ var css_248z$2 = ".index-module_mapEdit__XC5Gw {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n width: 100%;\n height: 100%;\n cursor: default;\n min-width: 1px;\n min-height: 1px;\n}\n.index-module_mapEdit__XC5Gw.index-module_createMode__14EPH {\n cursor: crosshair;\n}\n.index-module_mapEdit__XC5Gw.index-module_notCreateCursor__1-OjF {\n cursor: no-drop;\n}";
28642
+ var styles$2 = {"mapEdit":"index-module_mapEdit__XC5Gw","createMode":"index-module_createMode__14EPH","notCreateCursor":"index-module_notCreateCursor__1-OjF"};
28643
+ styleInject(css_248z$2);
28089
28644
 
28090
28645
  /******************************************************************************
28091
28646
  Copyright (c) Microsoft Corporation.
@@ -29085,7 +29640,7 @@ const CreateObstacleElement = React.forwardRef(({ enabled = false, svgElement, o
29085
29640
  if (!enabled || points.length === 0) {
29086
29641
  return null;
29087
29642
  }
29088
- return (jsxRuntime.jsx(PolygonElement, { points: points.map((p) => [p.x, p.y, 2]), fillColor: styles.fillColor, fillOpacity: 0.3, strokeColor: styles.strokeColor, strokeWidth: 1, strokeOpacity: 1, createMode: true, showPoints: true, editMode: false, completed: true, draggable: false }));
29643
+ return (jsxRuntime.jsx(PolygonElement, { points: points.map((p) => [p.x, p.y, 2]), fillColor: styles.fillColor, fillOpacity: 0.3, strokeColor: styles.strokeColor, strokeWidth: 1.8, strokeOpacity: 1, createMode: true, showPoints: true, editMode: false, completed: true, draggable: false }));
29089
29644
  });
29090
29645
  CreateObstacleElement.displayName = 'CreateObstacleElement';
29091
29646
 
@@ -29123,7 +29678,7 @@ const useCreateVisionOffElement = () => {
29123
29678
  [maxX, minY],
29124
29679
  ];
29125
29680
  return elementPoints;
29126
- }, [centerPoint, elementWidth, editMapInfo.allVisionOffPoints]);
29681
+ }, [centerPoint, elementWidth, editMapInfo.allVisionOffPoints, svgViewBox]);
29127
29682
  return {
29128
29683
  centerPoint,
29129
29684
  getVisionOffPoints,
@@ -29358,9 +29913,9 @@ const SvgEditMap = React.forwardRef(({ mapConfig, onEditInfoMapChange, editMap }
29358
29913
  const onMouseLeave = () => {
29359
29914
  setHoverDataId('');
29360
29915
  };
29361
- return (jsxRuntime.jsx("div", { ref: containerRef, className: classNames(styles$1.mapEdit, {
29362
- [styles$1.createMode]: isCreating,
29363
- [styles$1.notCreateCursor]: showNotCreateCursor,
29916
+ return (jsxRuntime.jsx("div", { ref: containerRef, className: classNames(styles$2.mapEdit, {
29917
+ [styles$2.createMode]: isCreating,
29918
+ [styles$2.notCreateCursor]: showNotCreateCursor,
29364
29919
  }),
29365
29920
  // style={{
29366
29921
  // position: 'absolute',
@@ -29466,6 +30021,22 @@ const SvgEditMap = React.forwardRef(({ mapConfig, onEditInfoMapChange, editMap }
29466
30021
  });
29467
30022
  SvgEditMap.displayName = 'SvgEditMap';
29468
30023
 
30024
+ /** 清理为合法的 SVG/HTML id 片段 */
30025
+ function sanitizeSvgIdPart(value) {
30026
+ return value.replace(/[^a-zA-Z0-9_.-]/g, '_');
30027
+ }
30028
+ /**
30029
+ * 通道 clipPath 的 SVG id
30030
+ * 组合 sn、useId、时间戳、随机数,避免同页多地图实例 id 冲突
30031
+ */
30032
+ function createChannelClipPathId(params) {
30033
+ const snPart = sanitizeSvgIdPart(params.sn || 'map');
30034
+ const reactPart = sanitizeSvgIdPart(params.reactId.replace(/:/g, ''));
30035
+ const timestamp = Date.now();
30036
+ const randomPart = Math.random().toString(36).slice(2, 10);
30037
+ return `channel-clip-path-${snPart}-${reactPart}-${timestamp}-${randomPart}`;
30038
+ }
30039
+
29469
30040
  var _path;
29470
30041
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
29471
30042
  var SvgMobileAddPoint = function SvgMobileAddPoint(props) {
@@ -29483,9 +30054,9 @@ var SvgMobileAddPoint = function SvgMobileAddPoint(props) {
29483
30054
  })));
29484
30055
  };
29485
30056
 
29486
- var css_248z = ".index-module_crosshair__etey6 {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 40px;\n height: 40px;\n pointer-events: none;\n z-index: 99999;\n display: flex;\n justify-content: center;\n align-items: center;\n}";
29487
- var styles = {"crosshair":"index-module_crosshair__etey6"};
29488
- styleInject(css_248z);
30057
+ var css_248z$1 = ".index-module_crosshair__etey6 {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 40px;\n height: 40px;\n pointer-events: none;\n z-index: 99999;\n display: flex;\n justify-content: center;\n align-items: center;\n}";
30058
+ var styles$1 = {"crosshair":"index-module_crosshair__etey6"};
30059
+ styleInject(css_248z$1);
29489
30060
 
29490
30061
  /**
29491
30062
  * 十字准心组件
@@ -29502,19 +30073,19 @@ const Crosshair = () => {
29502
30073
  }
29503
30074
  return false;
29504
30075
  }, [platform, editMapInfo]);
29505
- return isVisible ? (jsxRuntime.jsx("div", { className: styles.crosshair, children: jsxRuntime.jsx(SvgMobileAddPoint, {}) })) : null;
30076
+ return isVisible ? (jsxRuntime.jsx("div", { className: styles$1.crosshair, children: jsxRuntime.jsx(SvgMobileAddPoint, {}) })) : null;
29506
30077
  };
29507
30078
 
29508
- const WGS84 = 'EPSG:4326';
29509
- const WEB_MERCATOR = 'EPSG:3857';
29510
- proj4.defs(WGS84, '+proj=longlat +datum=WGS84 +no_defs');
29511
- proj4.defs(WEB_MERCATOR, '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
30079
+ const WGS84$1 = 'EPSG:4326';
30080
+ const WEB_MERCATOR$1 = 'EPSG:3857';
30081
+ proj4.defs(WGS84$1, '+proj=longlat +datum=WGS84 +no_defs');
30082
+ proj4.defs(WEB_MERCATOR$1, '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
29512
30083
  // 默认配置
29513
30084
  const defaultMapConfig$1 = DEFAULT_STYLES;
29514
30085
  // 地图渲染器组件
29515
30086
  const MowerMapRenderer = React.forwardRef(({ sn, mowerIconConfig = {}, platform = exports.PlatformType.WEB, mapType = exports.MapType.NORMAL, edger = false, unitType = UnitsType.Imperial, language = 'en', onlyRead = false, isHighlight = true, mapConfig,
29516
30087
  // pathConfig,
29517
- modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onError, className, style, zIndex, googleMapInstance: _googleMapInstance, dragMap = false, canRotateMap = false, dragCallbacks, defaultTransform, debug: _debug = false, doodleList = [], editMap = false, unStructMapData, disabledObstacles = false, onHandleEnterRecord, onHandleCompleteRecord, onHandleStopRecord, onHandleDeleteElement, onSaveBoundaryData, onSaveMap, onEditInfoMapChange, onUpdateBoundary, heightOptions, globalHeight, googleMapStaticApiKey, onHandleEvent, onSelectElement, onH5FirstSelectObstaclePoint, }, ref) => {
30088
+ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTimeData, antennaConfig, onError, className, style, zIndex, googleMapInstance: _googleMapInstance, dragMap = false, canRotateMap = false, dragCallbacks, defaultTransform, debug: _debug = false, doodleList = [], editMap = false, unStructMapData, disabledObstacles = false, onHandleEnterRecord, onHandleCompleteRecord, onHandleStopRecord, onHandleDeleteElement, onSaveBoundaryData, onSaveMap, onEditInfoMapChange, onUpdateBoundary, heightOptions, globalHeight, googleMapStaticApiKey, googleMapStaticUrlSigningSecret, signGoogleStaticMapUrl, onHandleEvent, onSelectElement, onH5FirstSelectObstaclePoint, locale, }, ref) => {
29518
30089
  const [currentError, setCurrentError] = React.useState(null);
29519
30090
  // const mapRef = useMap();
29520
30091
  const [processStateIsMowing, setProcessStateIsMowing] = React.useState(false);
@@ -29542,6 +30113,8 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29542
30113
  ...INIT_EDIT_MAP_INFO,
29543
30114
  });
29544
30115
  const [mowingPartitions, setMowingPartitions] = React.useState([]);
30116
+ const reactInstanceId = React.useId();
30117
+ const channelClipPathId = React.useMemo(() => createChannelClipPathId({ sn, reactId: reactInstanceId }), [sn, reactInstanceId]);
29545
30118
  const minDistance = React.useMemo(() => {
29546
30119
  return getMinSvgDistanceByModel(modelType);
29547
30120
  }, [modelType]);
@@ -29632,7 +30205,6 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29632
30205
  const newSvgElementDatas = { ...svgElementDatas };
29633
30206
  Object.keys(newSvgElementDatas).forEach((key) => {
29634
30207
  if (key === exports.DataType.OBSTACLE) {
29635
- console.error('newSvgElementDatas[key] 000---->', newSvgElementDatas[key]);
29636
30208
  newSvgElementDatas[key] =
29637
30209
  newSvgElementDatas[key].filter((item) => {
29638
30210
  return (item.status === 1 &&
@@ -29675,13 +30247,25 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29675
30247
  const mowerPositionData = React.useMemo(() => {
29676
30248
  // realTimeData 中包含三个种类的数据,之需要实时坐标的数据即可。
29677
30249
  // 在初始的状态按照正常图标进行渲染,所以构造一个基本在rtk桩的一个数据
29678
- if (!realTimeData || realTimeData.length === 0)
29679
- return {
29680
- postureX: 0.01,
29681
- postureY: 0.1,
29682
- postureTheta: 0.01,
29683
- vehicleState: RobotStatus.PARKED,
29684
- };
30250
+ if (!realTimeData || realTimeData.length === 0) {
30251
+ const chargingPiles = svgElementDatas?.[exports.DataType.CHARGING_PILE]?.[0];
30252
+ if (chargingPiles?.position) {
30253
+ return {
30254
+ postureX: chargingPiles.position?.[0],
30255
+ postureY: chargingPiles.position?.[1],
30256
+ postureTheta: chargingPiles.direction - Math.PI || 0,
30257
+ vehicleState: RobotStatus.PARKED,
30258
+ };
30259
+ }
30260
+ else {
30261
+ return {
30262
+ postureX: 0.01,
30263
+ postureY: 0.1,
30264
+ postureTheta: 0.01,
30265
+ vehicleState: RobotStatus.PARKED,
30266
+ };
30267
+ }
30268
+ }
29685
30269
  let currentPositionData;
29686
30270
  if (realTimeData.length === 1 && realTimeData[0].type === RealTimeDataType.LOCATION) {
29687
30271
  currentPositionData = realTimeData[0];
@@ -29725,14 +30309,14 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29725
30309
  const validBounds = getValidGpsBounds(mapJson, 0);
29726
30310
  const [swLng0, swLat0] = validBounds.sw;
29727
30311
  const [neLng0, neLat0] = validBounds.ne;
29728
- const [swX0, swY0] = proj4(WGS84, WEB_MERCATOR, [swLng0, swLat0]);
29729
- const [neX0, neY0] = proj4(WGS84, WEB_MERCATOR, [neLng0, neLat0]);
30312
+ const [swX0, swY0] = proj4(WGS84$1, WEB_MERCATOR$1, [swLng0, swLat0]);
30313
+ const [neX0, neY0] = proj4(WGS84$1, WEB_MERCATOR$1, [neLng0, neLat0]);
29730
30314
  const swX = swX0 + drag.x;
29731
30315
  const swY = swY0 + drag.y;
29732
30316
  const neX = neX0 + drag.x;
29733
30317
  const neY = neY0 + drag.y;
29734
- const [swLng, swLat] = proj4(WEB_MERCATOR, WGS84, [swX, swY]);
29735
- const [neLng, neLat] = proj4(WEB_MERCATOR, WGS84, [neX, neY]);
30318
+ const [swLng, swLat] = proj4(WEB_MERCATOR$1, WGS84$1, [swX, swY]);
30319
+ const [neLng, neLat] = proj4(WEB_MERCATOR$1, WGS84$1, [neX, neY]);
29736
30320
  return new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), new window.google.maps.LatLng(neLat, neLng));
29737
30321
  }, [mapJson, drag.x, drag.y]);
29738
30322
  const commonValue = React.useMemo(() => {
@@ -29760,7 +30344,11 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29760
30344
  showStraddleBoundaryBorder,
29761
30345
  overlayLayout,
29762
30346
  googleMapStaticApiKey,
30347
+ googleMapStaticUrlSigningSecret,
30348
+ locale,
30349
+ signGoogleStaticMapUrl,
29763
30350
  onH5FirstSelectObstaclePoint,
30351
+ channelClipPathId,
29764
30352
  };
29765
30353
  }, [
29766
30354
  sn,
@@ -29783,13 +30371,17 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29783
30371
  showStraddleBoundaryBorder,
29784
30372
  overlayLayout,
29785
30373
  googleMapStaticApiKey,
30374
+ googleMapStaticUrlSigningSecret,
30375
+ locale,
30376
+ signGoogleStaticMapUrl,
29786
30377
  onH5FirstSelectObstaclePoint,
30378
+ channelClipPathId,
29787
30379
  ]);
29788
30380
  /**
29789
30381
  * 这里做了一个配置,以防以后如果有多种不同的类型的情况,需要展示不同的元素
29790
30382
  */
29791
30383
  const mapHasElements = React.useMemo(() => {
29792
- // 地理围栏地图,没有充电桩,没有天线,没有割草机位置,没有边界标签
30384
+ // 地理围栏地图,有充电桩,没有天线,没有割草机位置,没有分区名称气泡
29793
30385
  if (mapType === exports.MapType.GEO_FENCE) {
29794
30386
  return {
29795
30387
  hasChargingPile: true,
@@ -29820,14 +30412,14 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29820
30412
  // 地图数据中的坐标格式是 [longitude, latitude]
29821
30413
  const [swLng0, swLat0] = validBounds.sw;
29822
30414
  const [neLng0, neLat0] = validBounds.ne;
29823
- const [swX0, swY0] = proj4(WGS84, WEB_MERCATOR, [swLng0, swLat0]);
29824
- const [neX0, neY0] = proj4(WGS84, WEB_MERCATOR, [neLng0, neLat0]);
30415
+ const [swX0, swY0] = proj4(WGS84$1, WEB_MERCATOR$1, [swLng0, swLat0]);
30416
+ const [neX0, neY0] = proj4(WGS84$1, WEB_MERCATOR$1, [neLng0, neLat0]);
29825
30417
  const swX = swX0 + defaultTransform.x;
29826
30418
  const swY = swY0 + defaultTransform.y;
29827
30419
  const neX = neX0 + defaultTransform.x;
29828
30420
  const neY = neY0 + defaultTransform.y;
29829
- const [swLng, swLat] = proj4(WEB_MERCATOR, WGS84, [swX, swY]);
29830
- const [neLng, neLat] = proj4(WEB_MERCATOR, WGS84, [neX, neY]);
30421
+ const [swLng, swLat] = proj4(WEB_MERCATOR$1, WGS84$1, [swX, swY]);
30422
+ const [neLng, neLat] = proj4(WEB_MERCATOR$1, WGS84$1, [neX, neY]);
29831
30423
  const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), // 西南角
29832
30424
  new window.google.maps.LatLng(neLat, neLng) // 东北角
29833
30425
  );
@@ -29854,8 +30446,8 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29854
30446
  const offsetLatLng = overlayProjection.fromDivPixelToLatLng(offsetPixel);
29855
30447
  if (!offsetLatLng)
29856
30448
  return;
29857
- const [cx, cy] = proj4(WGS84, WEB_MERCATOR, [centerLatLng.lng(), centerLatLng.lat()]);
29858
- const [ox, oy] = proj4(WGS84, WEB_MERCATOR, [offsetLatLng.lng(), offsetLatLng.lat()]);
30449
+ const [cx, cy] = proj4(WGS84$1, WEB_MERCATOR$1, [centerLatLng.lng(), centerLatLng.lat()]);
30450
+ const [ox, oy] = proj4(WGS84$1, WEB_MERCATOR$1, [offsetLatLng.lng(), offsetLatLng.lat()]);
29859
30451
  const dx = ox - cx;
29860
30452
  const dy = oy - cy;
29861
30453
  setDrag((prev) => ({
@@ -29898,7 +30490,10 @@ modelType, showStraddleBoundaryBorder = true, mapRef, mapJson, pathJson, realTim
29898
30490
  // 如果当前是taskDelay的状态,或者状态为mowing或者standby,则指定的地块需要高亮,或者全局高亮
29899
30491
  if ((isTaskDelayRef.current ||
29900
30492
  positionData?.vehicleState === RobotStatus.MOWING ||
29901
- positionData?.vehicleState === RobotStatus.STANDBY) &&
30493
+ positionData?.vehicleState === RobotStatus.STANDBY ||
30494
+ (!positionData &&
30495
+ (currentVehicleStateRef.current === RobotStatus.MOWING ||
30496
+ currentVehicleStateRef.current === RobotStatus.STANDBY))) &&
29902
30497
  mowPartitionDataRef.current &&
29903
30498
  !mowPartitionDataRef.current?.partitionIds) {
29904
30499
  // 设置全局高亮
@@ -30125,7 +30720,9 @@ MowerMapRenderer.displayName = 'MowerMapRenderer';
30125
30720
 
30126
30721
  // 默认配置
30127
30722
  const defaultMapConfig = DEFAULT_STYLES;
30128
- const BoundarySvgRender = React.memo(({ mapJson, unStructMapData, id, maxWidth = 300, mapConfig, sn }) => {
30723
+ const BoundarySvgRender = React.memo(({ mapJson, unStructMapData, id, maxWidth = 300, maxHeight, boundaryPadding = 50, mapConfig, sn, }) => {
30724
+ const reactInstanceId = React.useId();
30725
+ const channelClipPathId = React.useMemo(() => createChannelClipPathId({ sn, reactId: reactInstanceId }), [sn, reactInstanceId]);
30129
30726
  // 合并配置
30130
30727
  const mergedMapConfig = React.useMemo(() => {
30131
30728
  return merge$1(defaultMapConfig, mapConfig);
@@ -30171,7 +30768,7 @@ const BoundarySvgRender = React.memo(({ mapJson, unStructMapData, id, maxWidth =
30171
30768
  maxY = Math.max(maxY, point[1]);
30172
30769
  });
30173
30770
  // 添加边距
30174
- const padding = 50;
30771
+ const padding = boundaryPadding;
30175
30772
  const width = maxX - minX + padding * 2;
30176
30773
  const height = maxY - minY + padding * 2;
30177
30774
  return {
@@ -30180,25 +30777,40 @@ const BoundarySvgRender = React.memo(({ mapJson, unStructMapData, id, maxWidth =
30180
30777
  width,
30181
30778
  height,
30182
30779
  };
30183
- }, [boundaryInfo]);
30780
+ }, [boundaryInfo, boundaryPadding]);
30184
30781
  const commonValue = React.useMemo(() => {
30185
30782
  return {
30186
30783
  sn,
30187
30784
  svgViewBox: boundaryViewBox,
30785
+ channelClipPathId,
30188
30786
  };
30189
- }, [sn, boundaryViewBox]);
30787
+ }, [sn, boundaryViewBox, channelClipPathId]);
30190
30788
  const style = React.useMemo(() => {
30191
- if (boundaryViewBox?.width > boundaryViewBox?.height) {
30789
+ if (!maxHeight) {
30790
+ if (boundaryViewBox?.width > boundaryViewBox?.height) {
30791
+ return {
30792
+ width: maxWidth,
30793
+ height: maxWidth * (boundaryViewBox.height / boundaryViewBox.width),
30794
+ };
30795
+ }
30796
+ return {
30797
+ width: maxWidth * (boundaryViewBox.width / boundaryViewBox.height),
30798
+ height: maxWidth,
30799
+ };
30800
+ }
30801
+ const ratio = maxWidth / maxHeight;
30802
+ const boundaryRatio = boundaryViewBox.width / boundaryViewBox.height;
30803
+ if (ratio > boundaryRatio) {
30192
30804
  return {
30193
- width: maxWidth,
30194
- height: maxWidth * (boundaryViewBox.height / boundaryViewBox.width),
30805
+ width: Math.floor(maxHeight * boundaryRatio),
30806
+ height: maxHeight,
30195
30807
  };
30196
30808
  }
30197
30809
  return {
30198
- width: maxWidth * (boundaryViewBox.width / boundaryViewBox.height),
30199
- height: maxWidth,
30810
+ width: maxWidth,
30811
+ height: Math.floor(maxWidth / boundaryRatio),
30200
30812
  };
30201
- }, [boundaryViewBox, maxWidth]);
30813
+ }, [boundaryViewBox, maxWidth, maxHeight]);
30202
30814
  return (jsxRuntime.jsx(CommonContextProvider, { value: commonValue, children: jsxRuntime.jsx(SvgEditContextProvider, { value: svgEditValue, children: jsxRuntime.jsxs("div", { style: {
30203
30815
  position: 'relative',
30204
30816
  width: style.width,
@@ -30214,6 +30826,1512 @@ const BoundarySvgRender = React.memo(({ mapJson, unStructMapData, id, maxWidth =
30214
30826
  }) }), jsxRuntime.jsx(CharginPile, { viewBox: boundaryViewBox || null, rotation: 0 })] }) }) }));
30215
30827
  });
30216
30828
 
30829
+ const ISOLATED_BOUNDARY_SVG_2 = `<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
30830
+ <rect width="20" height="20" rx="10" fill="#8498A9" fill-opacity="0.7"/>
30831
+ <path d="M11.7178 12.4307C12.2201 11.9284 12.9629 11.8149 13.5752 12.0879L12.9756 12.6865C12.7427 12.6649 12.5034 12.7432 12.3223 12.917C12.3176 12.9214 12.3122 12.9251 12.3076 12.9297L11.7344 13.5029C11.4091 13.8284 11.409 14.3563 11.7344 14.6816L12.0879 15.0342C12.4133 15.3595 12.9412 15.3596 13.2666 15.0342L13.5527 14.7461L13.8408 14.46C14.011 14.2896 14.091 14.0629 14.083 13.8398L14.7002 13.2236C14.9614 13.8318 14.8443 14.5639 14.3477 15.0605L13.8613 15.5459C13.2105 16.1963 12.1556 16.1964 11.5049 15.5459L11.2314 15.2725C10.5816 14.6218 10.5815 13.5677 11.2314 12.917L11.7178 12.4307Z" fill="white"/>
30832
+ <path d="M12.1738 4.00488C11.6587 4.98031 10.6742 7.45647 10.8555 9.55762C11.3152 9.02665 12.5281 7.88542 13.7002 7.56934C13.2369 8.10318 12.5313 9.25837 12.1445 10.7949C11.6656 10.9223 11.2115 11.1723 10.835 11.5488L10.3496 12.0352C9.77429 12.6108 9.49095 13.367 9.49707 14.1211H4.72656C5.04472 13.1579 5.14564 10.847 3.00586 9.30957C4.04607 9.35761 5.61481 9.31003 6.83691 10.4141C6.66589 9.30985 6.83631 8.53141 4.90137 5.78711C5.69833 5.98284 7.68952 6.32958 8.74414 7.96875C9.20789 6.88364 10.4356 4.93597 12.1738 4.00488Z" fill="white"/>
30833
+ <path d="M14.8838 9.2666C15.5345 8.61618 16.5894 8.6153 17.2402 9.26562L17.5137 9.53906C18.164 10.19 18.1633 11.2458 17.5127 11.8965L17.0264 12.3828C16.5297 12.879 15.7985 12.9947 15.1904 12.7334L15.8115 12.1104C16.0351 12.1187 16.2619 12.0398 16.4326 11.8691L17.0068 11.2949C17.332 10.9697 17.3327 10.4417 17.0078 10.1162L16.6543 9.7627C16.3491 9.45791 15.8654 9.43902 15.5381 9.70605C15.5158 9.72417 15.4944 9.74388 15.4736 9.76465L14.9004 10.3379C14.7179 10.5204 14.6369 10.7665 14.6592 11.0049L14.0547 11.6084C13.7823 10.9964 13.8955 10.255 14.3975 9.75293L14.8838 9.2666Z" fill="white"/>
30834
+ </svg>`;
30835
+ /** ChannelClipPath 等内容裁剪使用的 SVG 坐标边距(与用户传入的屏幕 padding 无关) */
30836
+ const PARTITION_PICKER_CONTENT_CLIP_PADDING = 0;
30837
+ /** 可选中地块 polygon / 标签 data 属性 */
30838
+ const PARTITION_BOUNDARY_ID_ATTR = 'data-partition-boundary-id';
30839
+ /** 孤立子地块命中标记(不可选中) */
30840
+ const PARTITION_ISOLATED_BOUNDARY_ID_ATTR = 'data-partition-isolated-boundary-id';
30841
+ /** H5:子区域屏幕宽度超过该值才展示名称标签(px);与气泡宽度一致 */
30842
+ const H5_PARTITION_LABEL_MIN_SCREEN_WIDTH_PX = 72;
30843
+ /** H5:名称气泡总宽度(px) */
30844
+ const H5_PARTITION_LABEL_WIDTH_PX = 72;
30845
+ /** Web:序号徽章与名称标签间距(px) */
30846
+ const WEB_SELECTION_ORDER_GAP_PX = 8;
30847
+ /** H5:序号徽章与名称标签间距(px) */
30848
+ const H5_SELECTION_ORDER_GAP_PX = 4;
30849
+ /** Standalone:充电桩图标最小屏幕尺寸(px),缩小全图 fit 时避免过小;放大时随 viewBox 同比缩放 */
30850
+ const PARTITION_PICKER_CHARGING_PILE_MIN_DISPLAY_PX = 16;
30851
+ /** Web 地块描边屏幕像素宽度:未选中 2px,选中 3px */
30852
+ const WEB_PARTITION_BOUNDARY_STROKE_PX = {
30853
+ default: 2,
30854
+ selected: 3,
30855
+ };
30856
+ /** H5 地块描边屏幕像素宽度:选中与否均为 1px */
30857
+ const H5_PARTITION_BOUNDARY_STROKE_PX = {
30858
+ default: 1,
30859
+ selected: 1,
30860
+ };
30861
+ /**
30862
+ * Figma bg/mapping 地图网格背景(Standalone SVG 层)
30863
+ * @see https://www.figma.com/design/j0DdUYf8VFkXQ8ikkA6ZzP — node 20490:84886
30864
+ */
30865
+ /** 网格区域底色,也用于容器 letterbox 留白 */
30866
+ const PARTITION_PICKER_GRID_BASE_COLOR = '#E3E5EB';
30867
+ /** 网格单元边长(SVG 地图坐标,与 viewBox 一致;zoom 时间距同比缩放) */
30868
+ const PARTITION_PICKER_GRID_TILE_SIZE = 10;
30869
+ /** 网格线宽(地图坐标;随 zoom 同比缩放,不做屏幕像素恒定) */
30870
+ const PARTITION_PICKER_GRID_LINE_WIDTH = 0.2;
30871
+ /** 网格交点圆点半径(地图坐标) */
30872
+ const PARTITION_PICKER_GRID_DOT_RADIUS = 0.6;
30873
+ /** 网格层整体不透明度(Figma 约 80%) */
30874
+ const PARTITION_PICKER_GRID_OPACITY = 0.4;
30875
+ /** 网格线与交点颜色 */
30876
+ const PARTITION_PICKER_GRID_STROKE = '#FFFFFF';
30877
+ /** 可见范围内交点超过该数量时跳过圆点,仅保留网格线(缩小视野时减轻 DOM 压力) */
30878
+ const PARTITION_PICKER_GRID_MAX_INTERSECTION_DOTS = 2000;
30879
+ /**
30880
+ * 分区选择地图专用样式
30881
+ *
30882
+ * Web:Figma 灰蓝未选中地块(Vector 360)
30883
+ * H5:高对比度绿色,面向卫星/深色底图
30884
+ */
30885
+ const PARTITION_PICKER_MAP_CONFIG_WEB = {
30886
+ boundary: {
30887
+ lineColor: '#91A1B4',
30888
+ fillColor: '#D7DEE6',
30889
+ lineWidth: 2,
30890
+ },
30891
+ };
30892
+ const PARTITION_PICKER_MAP_CONFIG = {
30893
+ boundary: {
30894
+ lineColor: '#91A1B4',
30895
+ fillColor: '#D7DEE6',
30896
+ lineWidth: 2,
30897
+ },
30898
+ };
30899
+ /** Web 端地块配色(Figma Vector 360 / 361 / 99788) */
30900
+ const PARTITION_PICKER_COLORS_WEB = {
30901
+ defaultFill: 'rgba(222, 225, 231, 1)',
30902
+ defaultStroke: 'rgba(132, 152, 169, 1)',
30903
+ hoverFill: 'rgba(211, 216, 226, 1)',
30904
+ hoverStroke: 'rgba(132, 152, 169, 1)',
30905
+ selectedFill: 'rgba(202, 214, 238, 1)',
30906
+ selectedStroke: 'rgba(132, 152, 169, 1)',
30907
+ selectedHoverFill: 'rgba(202, 214, 238, 1)',
30908
+ selectedHoverStroke: 'rgba(132, 152, 169, 1)',
30909
+ isolatedFill: 'rgba(225, 227, 233, 1)',
30910
+ isolatedStroke: 'rgba(189, 198, 208, 1)',
30911
+ };
30912
+ /** H5 端地块配色 */
30913
+ const PARTITION_PICKER_COLORS_H5 = {
30914
+ defaultFill: 'rgba(222, 225, 231, 1)',
30915
+ defaultStroke: 'rgba(132, 152, 169, 1)',
30916
+ hoverFill: 'rgba(222, 225, 231, 1)',
30917
+ hoverStroke: 'rgba(132, 152, 169, 1)',
30918
+ selectedFill: 'rgba(202, 214, 238, 1)',
30919
+ selectedStroke: 'rgba(132, 152, 169, 1)',
30920
+ selectedHoverFill: 'rgba(202, 214, 238, 1)',
30921
+ selectedHoverStroke: 'rgba(132, 152, 169, 1)',
30922
+ isolatedFill: 'rgba(225, 227, 233, 1)',
30923
+ isolatedStroke: 'rgba(189, 198, 208, 1)',
30924
+ };
30925
+ function getPartitionPickerMapConfig(platform = exports.PlatformType.WEB) {
30926
+ return platform === exports.PlatformType.WEB
30927
+ ? PARTITION_PICKER_MAP_CONFIG_WEB
30928
+ : PARTITION_PICKER_MAP_CONFIG;
30929
+ }
30930
+ function getPartitionPickerColors(platform = exports.PlatformType.WEB) {
30931
+ return platform === exports.PlatformType.WEB ? PARTITION_PICKER_COLORS_WEB : PARTITION_PICKER_COLORS_H5;
30932
+ }
30933
+ function getPartitionPickerBoundaryStrokePx(platform = exports.PlatformType.WEB) {
30934
+ return platform === exports.PlatformType.WEB
30935
+ ? { ...WEB_PARTITION_BOUNDARY_STROKE_PX }
30936
+ : { ...H5_PARTITION_BOUNDARY_STROKE_PX };
30937
+ }
30938
+ /** Web 分区名气泡(Figma「地块名称」/ 选中区域) */
30939
+ const PARTITION_PICKER_LABEL_STYLES_WEB = {
30940
+ default: {
30941
+ backgroundColor: 'rgba(255, 255, 255, 0.15)',
30942
+ color: 'rgba(65, 93, 116, 0.7)',
30943
+ fontSize: 12,
30944
+ fontWeight: 500,
30945
+ padding: '4px 8px',
30946
+ borderRadius: 14,
30947
+ lineHeight: '20px',
30948
+ },
30949
+ selected: {
30950
+ backgroundColor: 'rgba(255, 90, 0, 1)',
30951
+ color: 'rgba(255, 255, 255, 1)',
30952
+ fontSize: 12,
30953
+ fontWeight: 500,
30954
+ padding: '4px 8px',
30955
+ borderRadius: 20,
30956
+ lineHeight: '20px',
30957
+ },
30958
+ selectionOrderBadge: {
30959
+ width: '24px',
30960
+ height: '24px',
30961
+ background: '#FF5A00',
30962
+ border: '1px solid #FFFFFF',
30963
+ color: '#FFFFFF',
30964
+ fontSize: 14,
30965
+ fontWeight: 500,
30966
+ lineHeight: '22px',
30967
+ },
30968
+ };
30969
+ /** H5 分区名气泡(深色底图 / 橙色渐变选中) */
30970
+ const PARTITION_PICKER_LABEL_STYLES_H5 = {
30971
+ default: {
30972
+ backgroundColor: 'rgba(255, 255, 255, 0.15)',
30973
+ color: 'rgba(65, 93, 116, 0.7)',
30974
+ fontSize: 12,
30975
+ fontWeight: 400,
30976
+ padding: '6px',
30977
+ width: `${H5_PARTITION_LABEL_WIDTH_PX}px`,
30978
+ maxWidth: `${H5_PARTITION_LABEL_WIDTH_PX}px`,
30979
+ borderRadius: 100,
30980
+ boxSizing: 'border-box',
30981
+ },
30982
+ selected: {
30983
+ background: 'linear-gradient(266.64deg, #F2942A 4.1%, #F26A2A 86.42%)',
30984
+ color: 'rgba(255, 255, 255, 1)',
30985
+ fontSize: 12,
30986
+ fontWeight: 400,
30987
+ padding: '6px',
30988
+ width: `${H5_PARTITION_LABEL_WIDTH_PX}px`,
30989
+ maxWidth: `${H5_PARTITION_LABEL_WIDTH_PX}px`,
30990
+ borderRadius: 100,
30991
+ boxSizing: 'border-box',
30992
+ },
30993
+ selectionOrderBadge: {
30994
+ width: '24px',
30995
+ height: '24px',
30996
+ background: 'linear-gradient(310.82deg, #F16629 14.52%, #F1A129 103.26%)',
30997
+ border: '1px solid #FFFFFF',
30998
+ color: '#FFFFFF',
30999
+ fontSize: 14,
31000
+ fontWeight: 600,
31001
+ lineHeight: '22px',
31002
+ },
31003
+ };
31004
+ /** 将 PartitionPickerLabelAppearance 转为 React 内联样式 */
31005
+ function labelAppearanceToStyle(appearance) {
31006
+ const { fontSize, borderRadius, ...rest } = appearance;
31007
+ return {
31008
+ ...rest,
31009
+ fontSize: `${fontSize}px`,
31010
+ borderRadius: `${borderRadius}px`,
31011
+ };
31012
+ }
31013
+ function getPartitionPickerLabelStyles(platform = exports.PlatformType.WEB) {
31014
+ return platform === exports.PlatformType.WEB
31015
+ ? PARTITION_PICKER_LABEL_STYLES_WEB
31016
+ : PARTITION_PICKER_LABEL_STYLES_H5;
31017
+ }
31018
+ const DEFAULT_PARTITION_PICKER_GRID_STYLE = {
31019
+ baseColor: PARTITION_PICKER_GRID_BASE_COLOR,
31020
+ strokeColor: PARTITION_PICKER_GRID_STROKE,
31021
+ tileSize: PARTITION_PICKER_GRID_TILE_SIZE,
31022
+ lineWidth: PARTITION_PICKER_GRID_LINE_WIDTH,
31023
+ dotRadius: PARTITION_PICKER_GRID_DOT_RADIUS,
31024
+ opacity: PARTITION_PICKER_GRID_OPACITY,
31025
+ maxIntersectionDots: PARTITION_PICKER_GRID_MAX_INTERSECTION_DOTS,
31026
+ };
31027
+ /**
31028
+ * 将 platform 内置默认值与外部 pickerStyleConfig 深度合并。
31029
+ * 调用方只需覆盖变化的字段,无需改组件源码。
31030
+ */
31031
+ function resolvePartitionPickerStyles(platform = exports.PlatformType.WEB, config) {
31032
+ const baseLabels = getPartitionPickerLabelStyles(platform);
31033
+ return {
31034
+ boundary: merge$1({}, getPartitionPickerColors(platform), config?.boundary),
31035
+ labels: config?.labels
31036
+ ? {
31037
+ default: merge$1({}, baseLabels.default, config.labels.default),
31038
+ selected: merge$1({}, baseLabels.selected, config.labels.selected),
31039
+ selectionOrderBadge: merge$1({}, baseLabels.selectionOrderBadge, config.labels.selectionOrderBadge),
31040
+ }
31041
+ : baseLabels,
31042
+ grid: merge$1({}, DEFAULT_PARTITION_PICKER_GRID_STYLE, config?.grid),
31043
+ boundaryStrokePx: merge$1({}, getPartitionPickerBoundaryStrokePx(platform), config?.boundaryStrokePx),
31044
+ };
31045
+ }
31046
+
31047
+ /**
31048
+ * Standalone 模式下的视口控制:平移、滚轮缩放、H5 双指 pinch
31049
+ *
31050
+ * 核心思路:通过修改 SVG viewBox 实现平移/缩放,而非 CSS transform
31051
+ * - fitViewBox:初始 fit 状态,resetView 回到此状态
31052
+ * - viewBox:地块/通道/overlay 的可见区域
31053
+ * - gridViewBox:网格底图专用;有地图旋转时平移按屏幕方向(不逆旋转),与地块跟手平移解耦
31054
+ *
31055
+ * 平移可在地块/分区名等任意区域发起;仅当位移小于阈值时才视为点击
31056
+ */
31057
+ function useMapViewport({ mapJson, containerRef: externalContainerRef, width, height, padding, minZoom = 0.5, maxZoom = 3, enablePanZoom = true, refitOnResize = true, onViewportTap, mapRotationDeg = 0, }) {
31058
+ /** 地图容器 DOM,用于绑定 wheel/pointer/touch 事件 */
31059
+ const internalContainerRef = React.useRef(null);
31060
+ const containerRef = externalContainerRef ?? internalContainerRef;
31061
+ /** 始终指向最新 padding,供 fitToView 异步调用时使用 */
31062
+ const paddingRef = React.useRef(padding);
31063
+ paddingRef.current = padding;
31064
+ /** padding 序列化 key,用于 useMemo 依赖(数组/对象形式 padding 变化时触发重算) */
31065
+ const paddingKey = typeof padding === 'number' ? padding : JSON.stringify(padding);
31066
+ /** 始终指向最新 onViewportTap,避免 effect 闭包过期 */
31067
+ const onViewportTapRef = React.useRef(onViewportTap);
31068
+ onViewportTapRef.current = onViewportTap;
31069
+ /** 始终指向最新 mapRotationDeg,供平移回调读取 */
31070
+ const mapRotationDegRef = React.useRef(mapRotationDeg);
31071
+ mapRotationDegRef.current = mapRotationDeg;
31072
+ /**
31073
+ * 内容 viewBox:地图包围盒 + 固定 SVG padding
31074
+ * 用于 ChannelClipPath 等裁剪,不随平移/缩放变化
31075
+ */
31076
+ const contentViewBox = React.useMemo(() => {
31077
+ const bounds = calculateMapBounds(mapJson);
31078
+ return boundsToContentViewBox(bounds, PARTITION_PICKER_CONTENT_CLIP_PADDING);
31079
+ }, [mapJson]);
31080
+ /**
31081
+ * 初始 fit viewBox:在 contentViewBox 基础上适配容器宽高比与 screen padding
31082
+ * resetView 会回到此状态
31083
+ */
31084
+ const fitViewBox = React.useMemo(() => computeFitViewBox(contentViewBox, width, height, padding), [contentViewBox, width, height, paddingKey]);
31085
+ /** fitViewBox 的 ref 副本,供事件回调中读取最新值(zoom 限制基准) */
31086
+ const fitViewBoxRef = React.useRef(fitViewBox);
31087
+ fitViewBoxRef.current = fitViewBox;
31088
+ /** 地块/通道/overlay 使用的 viewBox */
31089
+ const [viewBox, setViewBox] = React.useState(fitViewBox);
31090
+ /** 网格底图 viewBox:平移时按屏幕方向,避免与旋转后的地块反向滑动 */
31091
+ const [gridViewBox, setGridViewBox] = React.useState(fitViewBox);
31092
+ const viewBoxRef = React.useRef(viewBox);
31093
+ viewBoxRef.current = viewBox;
31094
+ const gridViewBoxRef = React.useRef(gridViewBox);
31095
+ gridViewBoxRef.current = gridViewBox;
31096
+ const pendingViewBoxRef = React.useRef(null);
31097
+ const pendingGridViewBoxRef = React.useRef(null);
31098
+ const viewBoxRafRef = React.useRef(null);
31099
+ const flushScheduledViewBox = React.useCallback(() => {
31100
+ viewBoxRafRef.current = null;
31101
+ const mapPending = pendingViewBoxRef.current;
31102
+ const gridPending = pendingGridViewBoxRef.current;
31103
+ if (mapPending) {
31104
+ pendingViewBoxRef.current = null;
31105
+ viewBoxRef.current = mapPending;
31106
+ setViewBox(mapPending);
31107
+ }
31108
+ if (gridPending) {
31109
+ pendingGridViewBoxRef.current = null;
31110
+ gridViewBoxRef.current = gridPending;
31111
+ setGridViewBox(gridPending);
31112
+ }
31113
+ }, []);
31114
+ const cancelScheduledViewBox = React.useCallback(() => {
31115
+ pendingViewBoxRef.current = null;
31116
+ pendingGridViewBoxRef.current = null;
31117
+ if (viewBoxRafRef.current != null) {
31118
+ cancelAnimationFrame(viewBoxRafRef.current);
31119
+ viewBoxRafRef.current = null;
31120
+ }
31121
+ }, []);
31122
+ const scheduleViewBox = React.useCallback((updater) => {
31123
+ const base = pendingViewBoxRef.current ?? viewBoxRef.current;
31124
+ const next = updater(base);
31125
+ pendingViewBoxRef.current = next;
31126
+ pendingGridViewBoxRef.current = next;
31127
+ viewBoxRef.current = next;
31128
+ gridViewBoxRef.current = next;
31129
+ if (viewBoxRafRef.current == null) {
31130
+ viewBoxRafRef.current = requestAnimationFrame(flushScheduledViewBox);
31131
+ }
31132
+ }, [flushScheduledViewBox]);
31133
+ const commitViewBox = React.useCallback((next) => {
31134
+ cancelScheduledViewBox();
31135
+ viewBoxRef.current = next;
31136
+ gridViewBoxRef.current = next;
31137
+ setViewBox(next);
31138
+ setGridViewBox(next);
31139
+ }, [cancelScheduledViewBox]);
31140
+ React.useEffect(() => () => {
31141
+ cancelScheduledViewBox();
31142
+ }, [cancelScheduledViewBox]);
31143
+ // 地图数据 / padding 变化时始终重新 fit(width/height 取触发时刻的实测值,不作为 resize 依赖)
31144
+ React.useEffect(() => {
31145
+ if (width <= 0 || height <= 0)
31146
+ return;
31147
+ commitViewBox(computeFitViewBox(contentViewBox, width, height, padding));
31148
+ }, [commitViewBox, contentViewBox, paddingKey]);
31149
+ const prevSizeRef = React.useRef({ width: 0, height: 0 });
31150
+ // 容器首次获得有效尺寸,或 refitOnResize 下的尺寸变化
31151
+ React.useEffect(() => {
31152
+ if (width <= 0 || height <= 0)
31153
+ return;
31154
+ const prev = prevSizeRef.current;
31155
+ const sizeChanged = prev.width !== width || prev.height !== height;
31156
+ const isInitialSize = prev.width <= 0 || prev.height <= 0;
31157
+ prevSizeRef.current = { width, height };
31158
+ if (isInitialSize || (refitOnResize && sizeChanged)) {
31159
+ commitViewBox(fitViewBox);
31160
+ }
31161
+ }, [
31162
+ commitViewBox,
31163
+ refitOnResize,
31164
+ width,
31165
+ height,
31166
+ fitViewBox.x,
31167
+ fitViewBox.y,
31168
+ fitViewBox.width,
31169
+ fitViewBox.height,
31170
+ ]);
31171
+ /**
31172
+ * 以屏幕焦点为中心缩放
31173
+ * @param factor 缩放因子,>1 放大,<1 缩小
31174
+ * @param focalX 焦点相对容器左上角的 X(px)
31175
+ * @param focalY 焦点相对容器左上角的 Y(px)
31176
+ */
31177
+ const applyZoom = React.useCallback((factor, focalX, focalY) => {
31178
+ scheduleViewBox((current) => {
31179
+ const targetWidth = clampViewBoxWidth(current.width / factor, fitViewBoxRef.current, minZoom, maxZoom);
31180
+ return resizeViewBoxAroundPoint(current, targetWidth, width, height, focalX, focalY);
31181
+ });
31182
+ }, [height, maxZoom, minZoom, scheduleViewBox, width]);
31183
+ /**
31184
+ * 平移视口:地块使用逆旋转后的 viewBox 增量(跟手);网格使用屏幕方向增量(同向滑动)
31185
+ */
31186
+ const applyPan = React.useCallback((deltaXPx, deltaYPx) => {
31187
+ const mapBase = pendingViewBoxRef.current ?? viewBoxRef.current;
31188
+ const gridBase = pendingGridViewBoxRef.current ?? gridViewBoxRef.current;
31189
+ const mapDelta = containerDeltaToViewBoxDelta(deltaXPx, deltaYPx, mapBase, width, height, mapRotationDegRef.current);
31190
+ const gridDelta = containerDeltaToViewBoxDelta(deltaXPx, deltaYPx, gridBase, width, height, 0);
31191
+ const nextMap = {
31192
+ ...mapBase,
31193
+ x: mapBase.x - mapDelta.dx,
31194
+ y: mapBase.y - mapDelta.dy,
31195
+ };
31196
+ const nextGrid = {
31197
+ ...gridBase,
31198
+ x: gridBase.x - gridDelta.dx,
31199
+ y: gridBase.y - gridDelta.dy,
31200
+ width: nextMap.width,
31201
+ height: nextMap.height,
31202
+ };
31203
+ pendingViewBoxRef.current = nextMap;
31204
+ pendingGridViewBoxRef.current = nextGrid;
31205
+ viewBoxRef.current = nextMap;
31206
+ gridViewBoxRef.current = nextGrid;
31207
+ if (viewBoxRafRef.current == null) {
31208
+ viewBoxRafRef.current = requestAnimationFrame(flushScheduledViewBox);
31209
+ }
31210
+ }, [flushScheduledViewBox, height, width]);
31211
+ /** 恢复到初始 fitViewBox(ref API resetView) */
31212
+ const resetView = React.useCallback(() => {
31213
+ commitViewBox(fitViewBoxRef.current);
31214
+ }, [commitViewBox]);
31215
+ /**
31216
+ * 重新计算并应用 fit viewBox(ref API fitToView)
31217
+ * @param nextPadding 可选的新 padding,不传则使用当前 paddingRef
31218
+ */
31219
+ const fitToView = React.useCallback((nextPadding) => {
31220
+ const bounds = calculateMapBounds(mapJson);
31221
+ const content = boundsToContentViewBox(bounds, PARTITION_PICKER_CONTENT_CLIP_PADDING);
31222
+ const fit = computeFitViewBox(content, width, height, nextPadding ?? paddingRef.current);
31223
+ fitViewBoxRef.current = fit;
31224
+ commitViewBox(fit);
31225
+ }, [commitViewBox, height, mapJson, width]);
31226
+ /** 桌面端:按 pointerId 追踪多个指针(预留多指,当前仅单指平移) */
31227
+ const pointerMapRef = React.useRef(new Map());
31228
+ /** H5:双指 pinch 缩放状态 */
31229
+ const pinchRef = React.useRef(null);
31230
+ /** 单指/单指针平移状态(桌面 pointer 与 H5 touch 共用) */
31231
+ const singlePanRef = React.useRef(null);
31232
+ /** 当前正在驱动平移的 pointer id(桌面端) */
31233
+ const activePanPointerIdRef = React.useRef(null);
31234
+ /** 清空所有平移/pinch 相关 ref,防止 pointer 丢失导致状态残留 */
31235
+ const clearPanState = React.useCallback(() => {
31236
+ pointerMapRef.current.clear();
31237
+ singlePanRef.current = null;
31238
+ activePanPointerIdRef.current = null;
31239
+ }, []);
31240
+ /** 计算 touch 列表中前两指间距 */
31241
+ const getTouchDistance = (touches) => {
31242
+ if (touches.length < 2)
31243
+ return 0;
31244
+ const dx = touches[0].clientX - touches[1].clientX;
31245
+ const dy = touches[0].clientY - touches[1].clientY;
31246
+ return Math.hypot(dx, dy);
31247
+ };
31248
+ /** 计算 touch 列表中前两指中心点,坐标相对容器左上角 */
31249
+ const getTouchCenter = (touches, rect) => {
31250
+ const x = (touches[0].clientX + touches[1].clientX) / 2 - rect.left;
31251
+ const y = (touches[0].clientY + touches[1].clientY) / 2 - rect.top;
31252
+ return { x, y };
31253
+ };
31254
+ /** 判断事件目标是否在地图容器内 */
31255
+ const isInsideContainer = (target, container) => {
31256
+ if (!(target instanceof Element))
31257
+ return false;
31258
+ return container.contains(target);
31259
+ };
31260
+ /** 位移是否超过 PAN_CLICK_THRESHOLD_PX,超过则视为拖拽 */
31261
+ const markPanMoved = (startX, startY, currentX, currentY) => Math.hypot(currentX - startX, currentY - startY) > PAN_CLICK_THRESHOLD_PX;
31262
+ /** 触发点击回调(仅当未发生拖拽时) */
31263
+ const emitViewportTap = (tapTarget, clientX, clientY) => {
31264
+ if (tapTarget) {
31265
+ onViewportTapRef.current?.({ target: tapTarget, clientX, clientY });
31266
+ }
31267
+ };
31268
+ // ── 绑定容器交互事件:滚轮缩放 / 桌面拖拽 / H5 pinch+单指平移 ──
31269
+ React.useEffect(() => {
31270
+ const container = containerRef.current;
31271
+ if (!container || !enablePanZoom)
31272
+ return;
31273
+ /** 滚轮缩放,以鼠标位置为焦点 */
31274
+ const handleWheel = (event) => {
31275
+ event.preventDefault();
31276
+ const rect = container.getBoundingClientRect();
31277
+ const focalX = event.clientX - rect.left;
31278
+ const focalY = event.clientY - rect.top;
31279
+ const factor = event.deltaY < 0 ? 1.1 : 1 / 1.1;
31280
+ applyZoom(factor, focalX, focalY);
31281
+ };
31282
+ /** 结束平移:未拖拽则触发 tap;释放 pointer capture 并清理状态 */
31283
+ const endPan = (event) => {
31284
+ // touch 由 touchend 统一处理,避免 pointerup 抢先清空 singlePanRef
31285
+ if (event?.pointerType === 'touch') {
31286
+ return;
31287
+ }
31288
+ if (event) {
31289
+ const state = pointerMapRef.current.get(event.pointerId);
31290
+ if (state && !state.moved && state.tapTarget) {
31291
+ emitViewportTap(state.tapTarget, event.clientX, event.clientY);
31292
+ }
31293
+ pointerMapRef.current.delete(event.pointerId);
31294
+ try {
31295
+ if (container.hasPointerCapture(event.pointerId)) {
31296
+ container.releasePointerCapture(event.pointerId);
31297
+ }
31298
+ }
31299
+ catch {
31300
+ // ignore
31301
+ }
31302
+ }
31303
+ if (!event || pointerMapRef.current.size === 0) {
31304
+ clearPanState();
31305
+ }
31306
+ };
31307
+ // 桌面端:pointer 事件处理平移(touch 交给下方 touch 事件,避免重复)
31308
+ const handlePointerDown = (event) => {
31309
+ if (!isInsideContainer(event.target, container))
31310
+ return;
31311
+ if (event.pointerType === 'touch') {
31312
+ return;
31313
+ }
31314
+ // 仅响应主键(左键)
31315
+ if (event.pointerType === 'mouse' && event.button !== 0) {
31316
+ return;
31317
+ }
31318
+ pointerMapRef.current.set(event.pointerId, {
31319
+ pointerId: event.pointerId,
31320
+ startX: event.clientX,
31321
+ startY: event.clientY,
31322
+ moved: false,
31323
+ tapTarget: event.target instanceof Element ? event.target : null,
31324
+ });
31325
+ activePanPointerIdRef.current = event.pointerId;
31326
+ singlePanRef.current = {
31327
+ startX: event.clientX,
31328
+ startY: event.clientY,
31329
+ viewBox: viewBoxRef.current,
31330
+ moved: false,
31331
+ tapTarget: event.target instanceof Element ? event.target : null,
31332
+ };
31333
+ container.setPointerCapture(event.pointerId);
31334
+ };
31335
+ const handlePointerMove = (event) => {
31336
+ // 主键已松开但 pointerup 未触发时(如释放在容器外),避免继续平移
31337
+ if (event.pointerType === 'mouse' && event.buttons === 0) {
31338
+ endPan(event);
31339
+ return;
31340
+ }
31341
+ if (activePanPointerIdRef.current !== event.pointerId)
31342
+ return;
31343
+ const state = pointerMapRef.current.get(event.pointerId);
31344
+ if (!state || !singlePanRef.current)
31345
+ return;
31346
+ const dx = event.clientX - state.startX;
31347
+ const dy = event.clientY - state.startY;
31348
+ if (markPanMoved(state.startX, state.startY, event.clientX, event.clientY)) {
31349
+ state.moved = true;
31350
+ singlePanRef.current.moved = true;
31351
+ }
31352
+ applyPan(dx, dy);
31353
+ pointerMapRef.current.set(event.pointerId, {
31354
+ ...state,
31355
+ startX: event.clientX,
31356
+ startY: event.clientY,
31357
+ });
31358
+ };
31359
+ const handlePointerUp = (event) => {
31360
+ endPan(event);
31361
+ };
31362
+ const handleLostPointerCapture = (event) => {
31363
+ if (activePanPointerIdRef.current === event.pointerId) {
31364
+ endPan(event);
31365
+ }
31366
+ };
31367
+ const handleWindowPointerUp = (event) => {
31368
+ if (event instanceof PointerEvent && event.pointerType === 'touch') {
31369
+ return;
31370
+ }
31371
+ clearPanState();
31372
+ };
31373
+ // H5:双指 pinch 缩放、单指平移
31374
+ const handleTouchStart = (event) => {
31375
+ if (event.touches.length === 2) {
31376
+ event.preventDefault();
31377
+ const rect = container.getBoundingClientRect();
31378
+ const center = getTouchCenter(event.touches, rect);
31379
+ pinchRef.current = {
31380
+ initialDistance: getTouchDistance(event.touches),
31381
+ initialViewBox: viewBoxRef.current,
31382
+ centerX: center.x,
31383
+ centerY: center.y,
31384
+ };
31385
+ singlePanRef.current = null;
31386
+ return;
31387
+ }
31388
+ if (event.touches.length === 1 && isInsideContainer(event.target, container)) {
31389
+ singlePanRef.current = {
31390
+ startX: event.touches[0].clientX,
31391
+ startY: event.touches[0].clientY,
31392
+ viewBox: viewBoxRef.current,
31393
+ moved: false,
31394
+ tapTarget: event.target instanceof Element ? event.target : null,
31395
+ };
31396
+ }
31397
+ };
31398
+ const handleTouchMove = (event) => {
31399
+ if (event.touches.length === 2 && pinchRef.current) {
31400
+ event.preventDefault();
31401
+ const rect = container.getBoundingClientRect();
31402
+ const distance = getTouchDistance(event.touches);
31403
+ if (distance <= 0 || pinchRef.current.initialDistance <= 0)
31404
+ return;
31405
+ const scale = distance / pinchRef.current.initialDistance;
31406
+ const center = getTouchCenter(event.touches, rect);
31407
+ const targetWidth = clampViewBoxWidth(pinchRef.current.initialViewBox.width / scale, fitViewBoxRef.current, minZoom, maxZoom);
31408
+ scheduleViewBox(() => resizeViewBoxAroundPoint(pinchRef.current.initialViewBox, targetWidth, width, height, center.x, center.y));
31409
+ return;
31410
+ }
31411
+ if (event.touches.length === 1 && singlePanRef.current) {
31412
+ const panState = singlePanRef.current;
31413
+ const touch = event.touches[0];
31414
+ if (markPanMoved(panState.startX, panState.startY, touch.clientX, touch.clientY)) {
31415
+ panState.moved = true;
31416
+ }
31417
+ if (panState.moved) {
31418
+ event.preventDefault();
31419
+ }
31420
+ const dx = touch.clientX - panState.startX;
31421
+ const dy = touch.clientY - panState.startY;
31422
+ applyPan(dx, dy);
31423
+ singlePanRef.current = {
31424
+ ...panState,
31425
+ startX: touch.clientX,
31426
+ startY: touch.clientY,
31427
+ };
31428
+ }
31429
+ };
31430
+ const handleTouchEnd = (event) => {
31431
+ const touchState = singlePanRef.current;
31432
+ if (touchState && !touchState.moved) {
31433
+ let tapTarget = touchState.tapTarget;
31434
+ const touch = event.changedTouches[0];
31435
+ let clientX = touchState.startX;
31436
+ let clientY = touchState.startY;
31437
+ if (touch) {
31438
+ clientX = touch.clientX;
31439
+ clientY = touch.clientY;
31440
+ const hitTarget = document.elementFromPoint(touch.clientX, touch.clientY);
31441
+ if (hitTarget instanceof Element) {
31442
+ tapTarget = hitTarget;
31443
+ }
31444
+ }
31445
+ emitViewportTap(tapTarget, clientX, clientY);
31446
+ }
31447
+ if (pinchRef.current) {
31448
+ pinchRef.current = null;
31449
+ }
31450
+ clearPanState();
31451
+ };
31452
+ container.addEventListener('wheel', handleWheel, { passive: false });
31453
+ container.addEventListener('pointerdown', handlePointerDown);
31454
+ container.addEventListener('pointermove', handlePointerMove);
31455
+ container.addEventListener('pointerup', handlePointerUp);
31456
+ container.addEventListener('pointercancel', handlePointerUp);
31457
+ container.addEventListener('lostpointercapture', handleLostPointerCapture);
31458
+ container.addEventListener('touchstart', handleTouchStart, { passive: false });
31459
+ container.addEventListener('touchmove', handleTouchMove, { passive: false });
31460
+ container.addEventListener('touchend', handleTouchEnd);
31461
+ container.addEventListener('touchcancel', handleTouchEnd);
31462
+ window.addEventListener('pointerup', handleWindowPointerUp);
31463
+ window.addEventListener('mouseup', handleWindowPointerUp);
31464
+ window.addEventListener('blur', handleWindowPointerUp);
31465
+ return () => {
31466
+ container.removeEventListener('wheel', handleWheel);
31467
+ container.removeEventListener('pointerdown', handlePointerDown);
31468
+ container.removeEventListener('pointermove', handlePointerMove);
31469
+ container.removeEventListener('pointerup', handlePointerUp);
31470
+ container.removeEventListener('pointercancel', handlePointerUp);
31471
+ container.removeEventListener('lostpointercapture', handleLostPointerCapture);
31472
+ container.removeEventListener('touchstart', handleTouchStart);
31473
+ container.removeEventListener('touchmove', handleTouchMove);
31474
+ container.removeEventListener('touchend', handleTouchEnd);
31475
+ container.removeEventListener('touchcancel', handleTouchEnd);
31476
+ window.removeEventListener('pointerup', handleWindowPointerUp);
31477
+ window.removeEventListener('mouseup', handleWindowPointerUp);
31478
+ window.removeEventListener('blur', handleWindowPointerUp);
31479
+ };
31480
+ }, [
31481
+ applyPan,
31482
+ applyZoom,
31483
+ clearPanState,
31484
+ enablePanZoom,
31485
+ height,
31486
+ maxZoom,
31487
+ minZoom,
31488
+ scheduleViewBox,
31489
+ width,
31490
+ ]);
31491
+ return {
31492
+ /** 绑定到地图外层容器的 ref */
31493
+ containerRef,
31494
+ /** 地块/通道/overlay 使用的 viewBox */
31495
+ viewBox,
31496
+ /** 网格底图 viewBox(平移按屏幕方向,与旋转地块解耦) */
31497
+ gridViewBox,
31498
+ /** 初始 fit viewBox,供 clip path 等需要稳定坐标系的场景 */
31499
+ fitViewBox,
31500
+ /** 恢复到初始 fit 状态 */
31501
+ resetView,
31502
+ /** 重新 fit 地图到容器(可传新 padding) */
31503
+ fitToView,
31504
+ };
31505
+ }
31506
+
31507
+ /** WGS84 经纬度坐标系 */
31508
+ const WGS84 = 'EPSG:4326';
31509
+ /** Web Mercator 平面坐标(米),与 MowerMapRenderer / defaultTransform.x|y 一致 */
31510
+ const WEB_MERCATOR = 'EPSG:3857';
31511
+ proj4.defs(WGS84, '+proj=longlat +datum=WGS84 +no_defs');
31512
+ proj4.defs(WEB_MERCATOR, '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
31513
+ /**
31514
+ * Map 模式专用:管理 OverlayViewF 所需的 bounds、viewBox、缩放与 fitBounds。
31515
+ * 逻辑对齐 MowerMapRenderer 的 Google Maps 叠加层。
31516
+ */
31517
+ function useGoogleMapsOverlay({ mapJson, mapRef, defaultTransform, originNorthRotate, platform = exports.PlatformType.WEB, padding = 50, }) {
31518
+ /** 仅当外部传入 mapRef 时执行 fitBounds / zoom 监听等副作用 */
31519
+ const enabled = Boolean(mapRef);
31520
+ const transformX = defaultTransform?.x ?? 0;
31521
+ const transformY = defaultTransform?.y ?? 0;
31522
+ const transformRotation = defaultTransform?.rotation ?? 0;
31523
+ /** 叠加层实际旋转 = 地图北向偏移 + 用户 rotation */
31524
+ const actureRotate = originNorthRotate + transformRotation;
31525
+ /** OverlayViewF onDraw 回调写入的 overlay 像素宽高 */
31526
+ const [overlayLayout, setOverlayLayout] = React.useState(null);
31527
+ const mapBounds = React.useMemo(() => calculateMapBounds(mapJson), [mapJson]);
31528
+ /**
31529
+ * SVG viewBox:地图 JSON 平面坐标全量范围。
31530
+ * 需等待 overlayLayout 就绪后才可计算(与 MowerMapRenderer svgViewBox 一致)。
31531
+ */
31532
+ const svgViewBox = React.useMemo(() => {
31533
+ if (!overlayLayout?.width || !overlayLayout?.height)
31534
+ return null;
31535
+ const boundWidth = mapBounds.maxX - mapBounds.minX;
31536
+ const boundHeight = mapBounds.maxY - mapBounds.minY;
31537
+ if (boundWidth <= 0 || boundHeight <= 0)
31538
+ return null;
31539
+ return {
31540
+ x: mapBounds.minX,
31541
+ y: mapBounds.minY,
31542
+ width: boundWidth,
31543
+ height: boundHeight,
31544
+ };
31545
+ }, [mapBounds, overlayLayout?.height, overlayLayout?.width]);
31546
+ /**
31547
+ * OverlayViewF 的 LatLngBounds:
31548
+ * GPS 包围盒 + defaultTransform 在 Web Mercator 下的平移偏移。
31549
+ * 仅 Map 模式(enabled)且 Google Maps 已加载时才计算。
31550
+ */
31551
+ const bounds = React.useMemo(() => {
31552
+ if (!enabled || !mapJson || typeof window === 'undefined' || !window.google?.maps) {
31553
+ return null;
31554
+ }
31555
+ const validBounds = getValidGpsBounds(mapJson, 0);
31556
+ const [swLng0, swLat0] = validBounds.sw;
31557
+ const [neLng0, neLat0] = validBounds.ne;
31558
+ const [swX0, swY0] = proj4(WGS84, WEB_MERCATOR, [swLng0, swLat0]);
31559
+ const [neX0, neY0] = proj4(WGS84, WEB_MERCATOR, [neLng0, neLat0]);
31560
+ const swX = swX0 + transformX;
31561
+ const swY = swY0 + transformY;
31562
+ const neX = neX0 + transformX;
31563
+ const neY = neY0 + transformY;
31564
+ const [swLng, swLat] = proj4(WEB_MERCATOR, WGS84, [swX, swY]);
31565
+ const [neLng, neLat] = proj4(WEB_MERCATOR, WGS84, [neX, neY]);
31566
+ return new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), new window.google.maps.LatLng(neLat, neLng));
31567
+ }, [enabled, mapJson, transformX, transformY]);
31568
+ /** OverlayViewF onDraw:解析 overlay 容器 style,得到像素 layout */
31569
+ const handleOverlayDraw = React.useCallback((style) => {
31570
+ const layout = Object.keys(style).reduce((acc, key) => {
31571
+ if (!key)
31572
+ return acc;
31573
+ acc[key] = parseFloat(style[key]);
31574
+ return acc;
31575
+ }, {});
31576
+ if (layout.width > 0 && layout.height > 0) {
31577
+ setOverlayLayout({ width: layout.width, height: layout.height });
31578
+ }
31579
+ }, []);
31580
+ /** Map 模式 fitToView:调用 google.maps.Map.fitBounds,考虑旋转后的 GPS 包围盒 */
31581
+ const fitToView = React.useCallback((nextPadding) => {
31582
+ if (!enabled || !mapRef || !mapJson || typeof window === 'undefined' || !window.google?.maps) {
31583
+ return;
31584
+ }
31585
+ const rotate = transformRotation + originNorthRotate;
31586
+ const validBounds = getValidGpsBounds(mapJson, rotate);
31587
+ const [swLng0, swLat0] = validBounds.sw;
31588
+ const [neLng0, neLat0] = validBounds.ne;
31589
+ const [swX0, swY0] = proj4(WGS84, WEB_MERCATOR, [swLng0, swLat0]);
31590
+ const [neX0, neY0] = proj4(WGS84, WEB_MERCATOR, [neLng0, neLat0]);
31591
+ const swX = swX0 + transformX;
31592
+ const swY = swY0 + transformY;
31593
+ const neX = neX0 + transformX;
31594
+ const neY = neY0 + transformY;
31595
+ const [swLng, swLat] = proj4(WEB_MERCATOR, WGS84, [swX, swY]);
31596
+ const [neLng, neLat] = proj4(WEB_MERCATOR, WGS84, [neX, neY]);
31597
+ const googleBounds = new window.google.maps.LatLngBounds(new window.google.maps.LatLng(swLat, swLng), new window.google.maps.LatLng(neLat, neLng));
31598
+ const pad = normalizeScreenPadding(nextPadding ?? padding, 0);
31599
+ const paddingArg = pad.top === pad.right && pad.right === pad.bottom && pad.bottom === pad.left
31600
+ ? pad.top
31601
+ : { top: pad.top, right: pad.right, bottom: pad.bottom, left: pad.left };
31602
+ mapRef.fitBounds(googleBounds, paddingArg);
31603
+ }, [enabled, mapJson, mapRef, originNorthRotate, padding, transformRotation, transformX, transformY]);
31604
+ const resetView = React.useCallback(() => {
31605
+ fitToView();
31606
+ }, [fitToView]);
31607
+ /** 参考缩放级别,用于计算 overlayScale(通道线宽视觉恒定,与 MowerMapRenderer 一致) */
31608
+ const REFERENCE_ZOOM = platform === exports.PlatformType.WEB ? 21 : 21;
31609
+ const [overlayScale, setOverlayScale] = React.useState(1);
31610
+ React.useEffect(() => {
31611
+ if (!enabled || !mapRef)
31612
+ return;
31613
+ const updateScale = () => {
31614
+ const currentZoom = mapRef.getZoom();
31615
+ if (currentZoom === undefined)
31616
+ return;
31617
+ const zoomDiff = currentZoom - REFERENCE_ZOOM;
31618
+ const scale = Math.pow(2, -zoomDiff);
31619
+ if (scale < 1) {
31620
+ setOverlayScale(1);
31621
+ }
31622
+ else if (scale > 200) {
31623
+ setOverlayScale(platform === exports.PlatformType.H5 ? 300 : 200);
31624
+ }
31625
+ else {
31626
+ setOverlayScale(platform === exports.PlatformType.H5 ? 1.5 * scale : scale);
31627
+ }
31628
+ };
31629
+ const handleZoomChanged = throttle$2(updateScale, 50);
31630
+ const listener = mapRef.addListener('zoom_changed', handleZoomChanged);
31631
+ updateScale();
31632
+ return () => {
31633
+ if (listener && window.google?.maps?.event) {
31634
+ window.google.maps.event.removeListener(listener);
31635
+ }
31636
+ handleZoomChanged.cancel();
31637
+ };
31638
+ }, [enabled, mapRef, platform, REFERENCE_ZOOM]);
31639
+ const overlayWidth = overlayLayout?.width ?? 0;
31640
+ const overlayHeight = overlayLayout?.height ?? 0;
31641
+ const isOverlayReady = overlayWidth > 0 && overlayHeight > 0 && svgViewBox != null;
31642
+ return {
31643
+ actureRotate,
31644
+ bounds,
31645
+ svgViewBox,
31646
+ fitViewBox: svgViewBox,
31647
+ overlayWidth,
31648
+ overlayHeight,
31649
+ overlayScale,
31650
+ isOverlayReady,
31651
+ handleOverlayDraw,
31652
+ fitToView,
31653
+ resetView,
31654
+ };
31655
+ }
31656
+
31657
+ function getInitialSize(width, height) {
31658
+ return {
31659
+ width: typeof width === 'number' && width > 0 ? width : 0,
31660
+ height: typeof height === 'number' && height > 0 ? height : 0,
31661
+ };
31662
+ }
31663
+ function cssLengthToStyle(value) {
31664
+ return value;
31665
+ }
31666
+ /**
31667
+ * 将 width/height(px 或 CSS length)应用到容器,并用 ResizeObserver 测量实际像素尺寸
31668
+ * 供 fit、标签定位、线宽换算等逻辑使用
31669
+ */
31670
+ function useContainerSize(width, height) {
31671
+ const containerRef = React.useRef(null);
31672
+ const [size, setSize] = React.useState(() => getInitialSize(width, height));
31673
+ const layoutStyle = React.useMemo(() => ({
31674
+ width: cssLengthToStyle(width),
31675
+ height: cssLengthToStyle(height),
31676
+ }), [width, height]);
31677
+ React.useLayoutEffect(() => {
31678
+ const element = containerRef.current;
31679
+ if (!element)
31680
+ return;
31681
+ const updateSize = () => {
31682
+ const { width: nextWidth, height: nextHeight } = element.getBoundingClientRect();
31683
+ if (nextWidth <= 0 || nextHeight <= 0)
31684
+ return;
31685
+ setSize((prev) => prev.width === nextWidth && prev.height === nextHeight
31686
+ ? prev
31687
+ : { width: nextWidth, height: nextHeight });
31688
+ };
31689
+ updateSize();
31690
+ const observer = new ResizeObserver(updateSize);
31691
+ observer.observe(element);
31692
+ return () => observer.disconnect();
31693
+ }, []);
31694
+ const isReady = size.width > 0 && size.height > 0;
31695
+ return {
31696
+ containerRef,
31697
+ layoutStyle,
31698
+ width: size.width,
31699
+ height: size.height,
31700
+ isReady,
31701
+ };
31702
+ }
31703
+
31704
+ /**
31705
+ * 地块 hover 状态管理(受控 / 非受控)
31706
+ *
31707
+ * 用于地图与外部表格等 UI 双向联动:
31708
+ * - 地图 hover → onBoundaryHoverChange → 宿主更新表格行高亮
31709
+ * - 表格 hover → 宿主传入 hoveredBoundaryId → 地图同步高亮
31710
+ */
31711
+ function useBoundaryHover({ hoveredBoundaryId, defaultHoveredBoundaryId = null, onBoundaryHoverChange, }) {
31712
+ const isControlled = hoveredBoundaryId !== undefined;
31713
+ const [internalHoveredId, setInternalHoveredId] = React.useState(defaultHoveredBoundaryId);
31714
+ const hoveredId = isControlled ? hoveredBoundaryId : internalHoveredId;
31715
+ const setHoveredId = React.useCallback((id) => {
31716
+ if (!isControlled) {
31717
+ setInternalHoveredId(id);
31718
+ }
31719
+ onBoundaryHoverChange?.(id);
31720
+ }, [isControlled, onBoundaryHoverChange]);
31721
+ return {
31722
+ hoveredId,
31723
+ setHoveredId,
31724
+ };
31725
+ }
31726
+
31727
+ function filterSelectableIds(ids, disabledBoundaryIds) {
31728
+ if (!disabledBoundaryIds?.size)
31729
+ return ids;
31730
+ return ids.filter((id) => !disabledBoundaryIds.has(id));
31731
+ }
31732
+ /**
31733
+ * 地块多选状态管理(受控 / 非受控)
31734
+ *
31735
+ * - 传入 selectedBoundaryIds → 受控,由宿主维护选中列表(如表格勾选)
31736
+ * - 未传入 → 非受控,内部维护,仍可通过 onSelectionChange 感知变化
31737
+ */
31738
+ function useBoundarySelection({ selectedBoundaryIds, defaultSelectedBoundaryIds = [], onSelectionChange, disabledBoundaryIds, }) {
31739
+ const isControlled = selectedBoundaryIds !== undefined;
31740
+ const [internalIds, setInternalIds] = React.useState(filterSelectableIds(defaultSelectedBoundaryIds, disabledBoundaryIds));
31741
+ const selectedIds = isControlled
31742
+ ? filterSelectableIds(selectedBoundaryIds, disabledBoundaryIds)
31743
+ : internalIds;
31744
+ const setSelectedIds = React.useCallback((ids) => {
31745
+ const next = filterSelectableIds(ids, disabledBoundaryIds);
31746
+ if (!isControlled) {
31747
+ setInternalIds(next);
31748
+ }
31749
+ onSelectionChange?.(next);
31750
+ }, [disabledBoundaryIds, isControlled, onSelectionChange]);
31751
+ /** 点击同一地块时 toggle,支持多选;孤立子地块不可选 */
31752
+ const toggleBoundary = React.useCallback((id) => {
31753
+ if (disabledBoundaryIds?.has(id))
31754
+ return;
31755
+ const next = selectedIds.includes(id)
31756
+ ? selectedIds.filter((item) => item !== id)
31757
+ : [...selectedIds, id];
31758
+ setSelectedIds(next);
31759
+ }, [disabledBoundaryIds, selectedIds, setSelectedIds]);
31760
+ return {
31761
+ selectedIds,
31762
+ toggleBoundary,
31763
+ setSelectedIds,
31764
+ };
31765
+ }
31766
+
31767
+ /** 计算地图中不可选中的孤立子地块 id 集合 */
31768
+ function useIsolatedBoundaryIds(mapJson) {
31769
+ return React.useMemo(() => {
31770
+ const boundaries = generateBoundaryData(mapJson);
31771
+ const ids = new Set();
31772
+ for (const boundary of boundaries) {
31773
+ if (boundary.isIsolated) {
31774
+ ids.add(boundary.id);
31775
+ }
31776
+ }
31777
+ return ids;
31778
+ }, [mapJson]);
31779
+ }
31780
+ /** 供宿主(如表格)判断某地块是否不可选 */
31781
+ function getIsolatedBoundaryIds(mapJson) {
31782
+ return generateBoundaryData(mapJson)
31783
+ .filter((boundary) => boundary.isIsolated)
31784
+ .map((boundary) => boundary.id);
31785
+ }
31786
+
31787
+ /** 计算多边形重心,作为分区名气泡的锚点 */
31788
+ function computePolygonCentroid(points) {
31789
+ if (!points || points.length < 3)
31790
+ return null;
31791
+ const valid = points.filter((p) => p.length >= 2);
31792
+ if (valid.length < 3)
31793
+ return null;
31794
+ const polygon = [...valid];
31795
+ const first = polygon[0];
31796
+ const last = polygon[polygon.length - 1];
31797
+ if (first[0] !== last[0] || first[1] !== last[1]) {
31798
+ polygon.push([first[0], first[1]]);
31799
+ }
31800
+ let area = 0;
31801
+ let cx = 0;
31802
+ let cy = 0;
31803
+ for (let i = 0; i < polygon.length - 1; i++) {
31804
+ const x0 = polygon[i][0];
31805
+ const y0 = polygon[i][1];
31806
+ const x1 = polygon[i + 1][0];
31807
+ const y1 = polygon[i + 1][1];
31808
+ const cross = x0 * y1 - x1 * y0;
31809
+ area += cross;
31810
+ cx += (x0 + x1) * cross;
31811
+ cy += (y0 + y1) * cross;
31812
+ }
31813
+ area = area / 2;
31814
+ if (Math.abs(area) < 1e-10) {
31815
+ let sx = 0;
31816
+ let sy = 0;
31817
+ for (const [x, y] of valid) {
31818
+ sx += x;
31819
+ sy += y;
31820
+ }
31821
+ return { x: sx / valid.length, y: sy / valid.length };
31822
+ }
31823
+ cx = cx / (6 * area);
31824
+ cy = cy / (6 * area);
31825
+ return { x: cx, y: cy };
31826
+ }
31827
+ function buildBoundaryLabelGeometry(mapData) {
31828
+ const boundaryData = generateBoundaryData(mapData);
31829
+ const results = [];
31830
+ for (const boundary of boundaryData) {
31831
+ const centroid = computePolygonCentroid(boundary.points);
31832
+ if (!centroid)
31833
+ continue;
31834
+ results.push({
31835
+ id: boundary.id,
31836
+ name: boundary.name,
31837
+ points: boundary.points,
31838
+ centroid,
31839
+ isIsolated: boundary.isIsolated,
31840
+ });
31841
+ }
31842
+ return results;
31843
+ }
31844
+ /**
31845
+ * 根据当前「显示 viewBox」计算分区名气泡位置(不预旋转;由 mapOverlayLayer CSS rotate 处理)
31846
+ */
31847
+ function usePartitionLabelItems(mapData, viewBox, containerWidth, containerHeight, options) {
31848
+ const positionMode = options?.positionMode ?? 'meet';
31849
+ const boundaryGeometry = React.useMemo(() => (mapData ? buildBoundaryLabelGeometry(mapData) : []), [mapData]);
31850
+ return React.useMemo(() => {
31851
+ if (!viewBox || containerWidth <= 0 || containerHeight <= 0)
31852
+ return [];
31853
+ const results = [];
31854
+ for (const boundary of boundaryGeometry) {
31855
+ let leftPct;
31856
+ let topPct;
31857
+ if (positionMode === 'overlay') {
31858
+ const relX = (boundary.centroid.x - viewBox.x) / viewBox.width;
31859
+ const relY = (boundary.centroid.y - viewBox.y) / viewBox.height;
31860
+ leftPct = relX * 100;
31861
+ topPct = relY * 100;
31862
+ }
31863
+ else {
31864
+ const position = mapPointToContainerPercent(boundary.centroid.x, boundary.centroid.y, viewBox, containerWidth, containerHeight);
31865
+ leftPct = position.leftPct;
31866
+ topPct = position.topPct;
31867
+ }
31868
+ if (!Number.isFinite(leftPct) || !Number.isFinite(topPct))
31869
+ continue;
31870
+ const screenWidthPx = computePolygonScreenWidthPx(boundary.points, viewBox, containerWidth, containerHeight);
31871
+ results.push({
31872
+ id: boundary.id,
31873
+ name: boundary.name,
31874
+ leftPct,
31875
+ topPct,
31876
+ screenWidthPx,
31877
+ isIsolated: boundary.isIsolated,
31878
+ });
31879
+ }
31880
+ return results;
31881
+ }, [
31882
+ boundaryGeometry,
31883
+ containerWidth,
31884
+ containerHeight,
31885
+ viewBox?.x,
31886
+ viewBox?.y,
31887
+ viewBox?.width,
31888
+ viewBox?.height,
31889
+ positionMode,
31890
+ ]);
31891
+ }
31892
+
31893
+ var css_248z = "@charset \"UTF-8\";\n/* 地图容器:裁剪溢出,禁用浏览器默认 touch 手势以便自定义 pinch */\n.index-module_container__nNYv7 {\n position: relative;\n overflow: hidden;\n touch-action: none;\n user-select: none;\n cursor: grab;\n /* H5 点击时不出现矩形 tap 高亮 */\n -webkit-tap-highlight-color: transparent;\n tap-highlight-color: transparent;\n}\n\n/* 网格 + 地块双层 SVG 叠放,共用 viewBox 保证平移/缩放同步 */\n.index-module_mapSvgStack__nz2s2 {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 1;\n}\n\n.index-module_gridSvg__MoJD6 {\n position: absolute;\n inset: 0;\n display: block;\n pointer-events: none;\n}\n\n/* 地图 SVG:地块/通道,可绕 viewBox 中心旋转 */\n.index-module_mapSvg__vN0LV {\n position: absolute;\n inset: 0;\n display: block;\n pointer-events: none;\n -webkit-tap-highlight-color: transparent;\n tap-highlight-color: transparent;\n}\n\n/* 可点击地块:悬停显示 pointer,与地图容器 grab 区分 */\n.index-module_boundaryPolygon__uHe-E {\n cursor: pointer;\n pointer-events: auto;\n -webkit-tap-highlight-color: transparent;\n tap-highlight-color: transparent;\n outline: none;\n -webkit-touch-callout: none;\n}\n\n.index-module_container__nNYv7:active {\n cursor: grabbing;\n}\n\n/* Map 模式:OverlayViewF 内根节点,铺满 overlay 像素区域 */\n.index-module_googleOverlayRoot__TQ3II {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n overflow: visible;\n}\n\n/* HTML overlay 层:CSS rotate 与 MowerMapRenderer OverlayViewF 一致,子元素自行反向补偿 */\n.index-module_mapOverlayLayer__7jTDy {\n position: absolute;\n inset: 0;\n transform-origin: 50% 50%;\n pointer-events: none;\n z-index: 2;\n}\n\n/* 分区名气泡层,绝对定位覆盖在 SVG 之上 */\n.index-module_labelsLayer__Rcikw {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n.index-module_label__xo3Kc {\n position: absolute;\n display: flex;\n flex-direction: column;\n align-items: center;\n pointer-events: auto;\n cursor: pointer;\n white-space: nowrap;\n -webkit-tap-highlight-color: transparent;\n tap-highlight-color: transparent;\n outline: none;\n -webkit-touch-callout: none;\n}\n\n.index-module_labelOrderOnly__TDLBU {\n gap: 0;\n}\n\n/* H5 名称标签:气泡宽度 / 文案最大宽度由 constants.ts 内联样式控制 */\n.index-module_labelH5Wrap__Es5cA {\n white-space: normal;\n}\n\n.index-module_labelPill__7jnOo {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n}\n\n.index-module_labelPillH5__iPeHi {\n box-sizing: border-box;\n}\n\n.index-module_labelText__ialST {\n word-break: break-word;\n text-align: center;\n}\n\n.index-module_labelTextH5__jz8SO {\n max-width: 100%;\n white-space: normal;\n overflow-wrap: anywhere;\n line-height: 1.33;\n}\n\n/* 选中序号徽章:尺寸与颜色由 constants 按 platform 注入 */\n.index-module_selectionOrderBadge__uj7LX {\n flex-shrink: 0;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.index-module_boundaryPolygonIsolated__syN4V {\n cursor: not-allowed;\n}\n\n.index-module_boundaryPolygonIsolated__syN4V:active {\n cursor: grabbing;\n}\n\n.index-module_labelIsolated__K3WAB {\n cursor: not-allowed;\n pointer-events: auto;\n}\n\n.index-module_labelIsolated__K3WAB:active {\n cursor: grabbing;\n}\n\n.index-module_labelIsolatedLayout__k-rwI {\n gap: 4px;\n}\n\n.index-module_isolatedIconBelow__PuVIF {\n position: static;\n transform: none;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n pointer-events: none;\n}";
31894
+ var styles = {"container":"index-module_container__nNYv7","mapSvgStack":"index-module_mapSvgStack__nz2s2","gridSvg":"index-module_gridSvg__MoJD6","mapSvg":"index-module_mapSvg__vN0LV","boundaryPolygon":"index-module_boundaryPolygon__uHe-E","googleOverlayRoot":"index-module_googleOverlayRoot__TQ3II","mapOverlayLayer":"index-module_mapOverlayLayer__7jTDy","labelsLayer":"index-module_labelsLayer__Rcikw","label":"index-module_label__xo3Kc","labelOrderOnly":"index-module_labelOrderOnly__TDLBU","labelH5Wrap":"index-module_labelH5Wrap__Es5cA","labelPill":"index-module_labelPill__7jnOo","labelPillH5":"index-module_labelPillH5__iPeHi","labelText":"index-module_labelText__ialST","labelTextH5":"index-module_labelTextH5__jz8SO","selectionOrderBadge":"index-module_selectionOrderBadge__uj7LX","boundaryPolygonIsolated":"index-module_boundaryPolygonIsolated__syN4V","labelIsolated":"index-module_labelIsolated__K3WAB","labelIsolatedLayout":"index-module_labelIsolatedLayout__k-rwI","isolatedIconBelow":"index-module_isolatedIconBelow__PuVIF"};
31895
+ styleInject(css_248z);
31896
+
31897
+ const SelectableBoundaryElement = ({ data, boundaryColors, boundaryStrokePx, isIsolated = false, selected, hovered, viewBoxWidth, viewBoxHeight, containerWidth, containerHeight, onHoverChange, onBoundaryClick, onIsolatedTap, }) => {
31898
+ const style = data.style || {};
31899
+ const boundaryId = Number(data.id);
31900
+ const colors = boundaryColors;
31901
+ const strokePx = boundaryStrokePx ?? { ...WEB_PARTITION_BOUNDARY_STROKE_PX };
31902
+ const pointsString = React.useMemo(() => data.points.map((point) => `${point[0]},${point[1]}`).join(' '), [data.points]);
31903
+ const fillColor = React.useMemo(() => {
31904
+ if (isIsolated)
31905
+ return colors.isolatedFill;
31906
+ if (selected) {
31907
+ return hovered ? colors.selectedHoverFill : colors.selectedFill;
31908
+ }
31909
+ if (hovered)
31910
+ return colors.hoverFill;
31911
+ return style.fillColor || colors.defaultFill;
31912
+ }, [colors, hovered, isIsolated, selected, style.fillColor]);
31913
+ const strokeColor = React.useMemo(() => {
31914
+ if (isIsolated)
31915
+ return colors.isolatedStroke;
31916
+ if (selected) {
31917
+ return hovered ? colors.selectedHoverStroke : colors.selectedStroke;
31918
+ }
31919
+ if (hovered)
31920
+ return colors.hoverStroke;
31921
+ return style.lineColor || colors.defaultStroke;
31922
+ }, [colors, hovered, isIsolated, selected, style.lineColor]);
31923
+ const strokeWidth = React.useMemo(() => {
31924
+ const viewBox = { width: viewBoxWidth, height: viewBoxHeight };
31925
+ const targetScreenStrokePx = !isIsolated && (selected || hovered) ? strokePx.selected : strokePx.default;
31926
+ return scaleStrokeWidthForViewBox(viewBox, {
31927
+ containerWidth,
31928
+ containerHeight,
31929
+ targetScreenStrokePx,
31930
+ });
31931
+ }, [
31932
+ containerHeight,
31933
+ containerWidth,
31934
+ hovered,
31935
+ isIsolated,
31936
+ selected,
31937
+ strokePx,
31938
+ viewBoxHeight,
31939
+ viewBoxWidth,
31940
+ ]);
31941
+ if (isIsolated) {
31942
+ return (jsxRuntime.jsx("polygon", { className: `${styles.boundaryPolygon} ${styles.boundaryPolygonIsolated}`, points: pointsString, fill: fillColor, stroke: strokeColor, strokeWidth: strokeWidth, strokeLinejoin: "round", [PARTITION_ISOLATED_BOUNDARY_ID_ATTR]: boundaryId, onClick: onIsolatedTap
31943
+ ? (event) => {
31944
+ event.stopPropagation();
31945
+ onIsolatedTap(boundaryId, event.clientX, event.clientY);
31946
+ }
31947
+ : undefined }));
31948
+ }
31949
+ return (jsxRuntime.jsx("polygon", { className: styles.boundaryPolygon, points: pointsString, fill: fillColor, stroke: strokeColor, strokeWidth: strokeWidth, strokeLinejoin: "round", [PARTITION_BOUNDARY_ID_ATTR]: boundaryId, onMouseEnter: () => onHoverChange(boundaryId), onMouseLeave: () => onHoverChange(null), onClick: onBoundaryClick
31950
+ ? (event) => {
31951
+ event.stopPropagation();
31952
+ onBoundaryClick(boundaryId);
31953
+ }
31954
+ : undefined }));
31955
+ };
31956
+ var SelectableBoundaryElement$1 = React.memo(SelectableBoundaryElement);
31957
+
31958
+ const PartitionPickerSvg = ({ svgElementDatas, viewBox, containerWidth, containerHeight, mapRotation = 0, boundaryColors, boundaryStrokePx, isolatedBoundaryIds, selectedIds, hoveredId, onHoverChange, onBoundaryClick, onIsolatedTap, }) => {
31959
+ const boundaries = (svgElementDatas[exports.DataType.BOUNDARY] || []);
31960
+ const channels = (svgElementDatas[exports.DataType.CHANNEL] || []);
31961
+ const selectedIdSet = React.useMemo(() => new Set(selectedIds), [selectedIds]);
31962
+ const mapContentTransform = React.useMemo(() => {
31963
+ if (mapRotation === 0)
31964
+ return undefined;
31965
+ const { x: cx, y: cy } = getViewBoxCenter(viewBox);
31966
+ return `rotate(${mapRotation}, ${cx}, ${cy})`;
31967
+ }, [mapRotation, viewBox.x, viewBox.y, viewBox.width, viewBox.height]);
31968
+ const mapContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(GElement, { type: "boundary", children: boundaries.map((item) => {
31969
+ const boundaryId = Number(item.id);
31970
+ return (jsxRuntime.jsx(SelectableBoundaryElement$1, { data: item, boundaryColors: boundaryColors, boundaryStrokePx: boundaryStrokePx, isIsolated: isolatedBoundaryIds.has(boundaryId), selected: selectedIdSet.has(boundaryId), hovered: hoveredId === boundaryId, viewBoxWidth: viewBox.width, viewBoxHeight: viewBox.height, containerWidth: containerWidth, containerHeight: containerHeight, onHoverChange: onHoverChange, onBoundaryClick: onBoundaryClick, onIsolatedTap: onIsolatedTap }, item.id));
31971
+ }) }), jsxRuntime.jsxs(GElement, { type: "channel", children: [jsxRuntime.jsx(ChannelClipPath, {}), channels.map((item) => (jsxRuntime.jsx(ChannelElement, { data: item }, item.id)))] })] }));
31972
+ return (jsxRuntime.jsx("svg", { className: styles.mapSvg, xmlns: "http://www.w3.org/2000/svg", viewBox: viewBoxToString(viewBox), width: "100%", height: "100%", preserveAspectRatio: "xMidYMid meet", shapeRendering: "geometricPrecision", children: mapContentTransform ? (jsxRuntime.jsx("g", { transform: mapContentTransform, children: mapContent })) : (mapContent) }));
31973
+ };
31974
+
31975
+ function estimateGridIntersectionCount(viewBox, tileSize) {
31976
+ if (tileSize <= 0)
31977
+ return 0;
31978
+ const cols = Math.floor((viewBox.width + 1e-6) / tileSize) + 1;
31979
+ const rows = Math.floor((viewBox.height + 1e-6) / tileSize) + 1;
31980
+ return cols * rows;
31981
+ }
31982
+ /** 计算当前 viewBox 可见范围内的网格交点(每个交点只渲染一次圆点) */
31983
+ function getVisibleGridIntersections(viewBox, tileSize, maxDots) {
31984
+ if (tileSize <= 0 || estimateGridIntersectionCount(viewBox, tileSize) > maxDots)
31985
+ return [];
31986
+ const points = [];
31987
+ const minX = viewBox.x;
31988
+ const minY = viewBox.y;
31989
+ const maxX = viewBox.x + viewBox.width;
31990
+ const maxY = viewBox.y + viewBox.height;
31991
+ const startX = Math.floor(minX / tileSize) * tileSize;
31992
+ const startY = Math.floor(minY / tileSize) * tileSize;
31993
+ for (let x = startX; x <= maxX + 1e-6; x += tileSize) {
31994
+ for (let y = startY; y <= maxY + 1e-6; y += tileSize) {
31995
+ points.push({ x, y });
31996
+ }
31997
+ }
31998
+ return points;
31999
+ }
32000
+ const MapGridBackground = ({ viewBox, gridStyle }) => {
32001
+ const reactId = React.useId();
32002
+ const patternId = React.useMemo(() => `partition-picker-grid-lines-${reactId.replace(/:/g, '-')}`, [reactId]);
32003
+ const { baseColor, strokeColor, tileSize: tile, lineWidth, dotRadius, opacity, maxIntersectionDots, } = gridStyle;
32004
+ const intersections = React.useMemo(() => getVisibleGridIntersections(viewBox, tile, maxIntersectionDots), [maxIntersectionDots, tile, viewBox.x, viewBox.y, viewBox.width, viewBox.height]);
32005
+ /**
32006
+ * SVG stroke 以路径为中心;线在 tile 的 0 边绘制时,四格交点的视觉中心会偏向路径右下。
32007
+ * 线条内缩 lineWidth/2,圆点同步偏移,使圆心与网格线交叉中心对齐。
32008
+ */
32009
+ const lineHalf = lineWidth / 2;
32010
+ return (jsxRuntime.jsxs("g", { className: "partition-picker-grid-bg", "aria-hidden": true, pointerEvents: "none", children: [jsxRuntime.jsx("defs", { children: jsxRuntime.jsxs("pattern", { id: patternId, patternUnits: "userSpaceOnUse", width: tile, height: tile, children: [jsxRuntime.jsx("line", { x1: 0, y1: lineHalf, x2: tile, y2: lineHalf, stroke: strokeColor, strokeWidth: lineWidth }), jsxRuntime.jsx("line", { x1: lineHalf, y1: 0, x2: lineHalf, y2: tile, stroke: strokeColor, strokeWidth: lineWidth })] }) }), jsxRuntime.jsx("rect", { x: viewBox.x, y: viewBox.y, width: viewBox.width, height: viewBox.height, fill: baseColor }), jsxRuntime.jsx("rect", { x: viewBox.x, y: viewBox.y, width: viewBox.width, height: viewBox.height, fill: `url(#${patternId})`, opacity: opacity }), jsxRuntime.jsx("g", { opacity: opacity, children: intersections.map((point, index) => (jsxRuntime.jsx("circle", { cx: point.x + lineHalf, cy: point.y + lineHalf, r: dotRadius, fill: strokeColor }, `${point.x}-${point.y}-${index}`))) })] }));
32011
+ };
32012
+ var MapGridBackground$1 = React.memo(MapGridBackground);
32013
+
32014
+ const PartitionPickerGridSvg = ({ viewBox, gridStyle }) => {
32015
+ return (jsxRuntime.jsx("svg", { className: styles.gridSvg, "data-viewport-background": "true", xmlns: "http://www.w3.org/2000/svg", viewBox: viewBoxToString(viewBox), width: "100%", height: "100%",
32016
+ // 设置缩放的规则
32017
+ //等比缩放 viewBox 内容,使其完整落在容器内;
32018
+ //水平、垂直都居中;
32019
+ //比例不一致时,上下或左右留空(letterboxing),不裁切、不拉伸变形。
32020
+ preserveAspectRatio: "xMidYMid meet",
32021
+ /**
32022
+ * 控制浏览器如何绘制路径/线条:
32023
+ * geometricPrecision:优先几何精度,线条更平滑、对齐更稳,缩放时不易发虚或抖动。
32024
+ * 网格是大量细线,用这个属性能让缩放/平移时网格更清晰稳定。
32025
+ */
32026
+ shapeRendering: "geometricPrecision", "aria-hidden": true, children: jsxRuntime.jsx(MapGridBackground$1, { viewBox: viewBox, gridStyle: gridStyle }) }));
32027
+ };
32028
+
32029
+ const PartitionNameLabels = ({ items, selectedIds, labelStyles, platform = exports.PlatformType.WEB, showSelectionOrder = false, rotation = 0, onHoverChange, onBoundaryClick, onIsolatedTap, }) => {
32030
+ const isWeb = platform === exports.PlatformType.WEB;
32031
+ const orderBadgeStyle = React.useMemo(() => ({
32032
+ width: labelStyles.selectionOrderBadge.width,
32033
+ height: labelStyles.selectionOrderBadge.height,
32034
+ background: labelStyles.selectionOrderBadge.background,
32035
+ border: labelStyles.selectionOrderBadge.border,
32036
+ color: labelStyles.selectionOrderBadge.color,
32037
+ fontSize: `${labelStyles.selectionOrderBadge.fontSize}px`,
32038
+ fontWeight: labelStyles.selectionOrderBadge.fontWeight,
32039
+ fontFamily: labelStyles.selectionOrderBadge.fontFamily,
32040
+ lineHeight: labelStyles.selectionOrderBadge.lineHeight,
32041
+ }), [labelStyles]);
32042
+ return (jsxRuntime.jsx("div", { className: styles.labelsLayer, children: items.map((item) => {
32043
+ const isIsolated = item.isIsolated === true;
32044
+ const selected = !isIsolated && selectedIds.includes(item.id);
32045
+ const selectionOrder = selected ? selectedIds.indexOf(item.id) + 1 : null;
32046
+ const isLargeEnough = item.screenWidthPx > H5_PARTITION_LABEL_MIN_SCREEN_WIDTH_PX;
32047
+ // Web 始终展示名称;H5 按屏幕宽度决定
32048
+ const showNameLabel = isWeb || isLargeEnough;
32049
+ const showOrderBadge = showSelectionOrder && selected && selectionOrder != null;
32050
+ const h5OrderOnly = !isWeb && showOrderBadge && !isLargeEnough;
32051
+ if (!showNameLabel && !showOrderBadge && !isIsolated) {
32052
+ return null;
32053
+ }
32054
+ const pillStyle = labelAppearanceToStyle(selected ? labelStyles.selected : labelStyles.default);
32055
+ const labelClassName = [
32056
+ styles.label,
32057
+ !isWeb && showNameLabel ? styles.labelH5Wrap : '',
32058
+ isIsolated ? styles.labelIsolated : '',
32059
+ h5OrderOnly ? styles.labelOrderOnly : '',
32060
+ isIsolated ? styles.labelIsolatedLayout : '',
32061
+ ]
32062
+ .filter(Boolean)
32063
+ .join(' ');
32064
+ const showOrderWithName = showOrderBadge && showNameLabel;
32065
+ const selectionOrderGapPx = isWeb ? WEB_SELECTION_ORDER_GAP_PX : H5_SELECTION_ORDER_GAP_PX;
32066
+ const boundaryIdProps = isIsolated
32067
+ ? { [PARTITION_ISOLATED_BOUNDARY_ID_ATTR]: item.id }
32068
+ : { [PARTITION_BOUNDARY_ID_ATTR]: item.id };
32069
+ return (jsxRuntime.jsxs("div", { className: labelClassName, style: {
32070
+ left: `${item.leftPct}%`,
32071
+ top: `${item.topPct}%`,
32072
+ transform: `translate(-50%, -50%) rotate(${-rotation}deg)`,
32073
+ ...(showOrderWithName ? { gap: selectionOrderGapPx } : undefined),
32074
+ }, ...boundaryIdProps, onMouseEnter: isIsolated ? undefined : () => onHoverChange(item.id), onMouseLeave: isIsolated ? undefined : () => onHoverChange(null), onClick: isIsolated
32075
+ ? onIsolatedTap
32076
+ ? (event) => {
32077
+ event.stopPropagation();
32078
+ onIsolatedTap(item.id, event.clientX, event.clientY);
32079
+ }
32080
+ : undefined
32081
+ : onBoundaryClick
32082
+ ? (event) => {
32083
+ event.stopPropagation();
32084
+ onBoundaryClick(item.id);
32085
+ }
32086
+ : undefined, children: [showOrderBadge && (jsxRuntime.jsx("div", { className: styles.selectionOrderBadge, style: orderBadgeStyle, children: selectionOrder })), showNameLabel && (jsxRuntime.jsx("div", { className: `${styles.labelPill} ${!isWeb ? styles.labelPillH5 : ''}`, style: pillStyle, children: jsxRuntime.jsx("span", { className: `${styles.labelText} ${!isWeb ? styles.labelTextH5 : ''}`, children: item.name }) })), isIsolated && (jsxRuntime.jsx("div", { className: `${styles.isolatedIconBelow}`, dangerouslySetInnerHTML: { __html: ISOLATED_BOUNDARY_SVG_2 } }))] }, item.id));
32087
+ }) }));
32088
+ };
32089
+
32090
+ const DEFAULT_BACKGROUND = PARTITION_PICKER_GRID_BASE_COLOR;
32091
+ /** Map 模式下 OverlayViewF 默认 z-index */
32092
+ const DEFAULT_OVERLAY_Z_INDEX = 100;
32093
+ const MowerPartitionPickerMap = React.forwardRef(({ mapJson, mapConfig, pickerStyleConfig, sn = '', platform = exports.PlatformType.WEB, width, height, padding = 0, backgroundColor = DEFAULT_BACKGROUND, defaultTransform, mapRef, minZoom = 0.5, maxZoom = 3, enablePanZoom = true, refitOnResize = true, onSizeChange, selectedBoundaryIds, defaultSelectedBoundaryIds, onSelectionChange, hoveredBoundaryId, defaultHoveredBoundaryId, onBoundaryHoverChange, showSelectionOrder = false, onIsolatedBoundaryTap, zIndex = DEFAULT_OVERLAY_Z_INDEX, className, style, }, ref) => {
32094
+ /**
32095
+ * 是否为 Map 模式(传入 mapRef 时为 true):
32096
+ * - true:Google Maps 底图 + OverlayViewF,平移/缩放/fit 由底图负责
32097
+ * - false:Standalone 固定容器 + 网格 SVG + useMapViewport
32098
+ */
32099
+ const isMapMode = Boolean(mapRef);
32100
+ const reactInstanceId = React.useId();
32101
+ const channelClipPathId = React.useMemo(() => createChannelClipPathId({ sn, reactId: reactInstanceId }), [sn, reactInstanceId]);
32102
+ /** 外部 pickerStyleConfig 与内置 platform 默认值合并后的最终外观(地块/标签/网格等) */
32103
+ const mergedPickerStyles = React.useMemo(() => resolvePartitionPickerStyles(platform, pickerStyleConfig), [pickerStyleConfig, platform]);
32104
+ /** Standalone 容器背景与 grid.baseColor 对齐(合并后的最终值) */
32105
+ const resolvedBackgroundColor = React.useMemo(() => {
32106
+ if (isMapMode)
32107
+ return undefined;
32108
+ if (pickerStyleConfig?.grid?.baseColor != null) {
32109
+ return mergedPickerStyles.grid.baseColor;
32110
+ }
32111
+ return backgroundColor ?? mergedPickerStyles.grid.baseColor;
32112
+ }, [
32113
+ backgroundColor,
32114
+ isMapMode,
32115
+ mergedPickerStyles.grid.baseColor,
32116
+ pickerStyleConfig?.grid?.baseColor,
32117
+ ]);
32118
+ /** Standalone 网格层:backgroundColor 未显式指定 grid.baseColor 时与容器背景同步 */
32119
+ const standaloneGridStyle = React.useMemo(() => {
32120
+ if (pickerStyleConfig?.grid?.baseColor != null) {
32121
+ return mergedPickerStyles.grid;
32122
+ }
32123
+ if (backgroundColor != null) {
32124
+ return { ...mergedPickerStyles.grid, baseColor: backgroundColor };
32125
+ }
32126
+ return mergedPickerStyles.grid;
32127
+ }, [
32128
+ backgroundColor,
32129
+ mergedPickerStyles.grid,
32130
+ pickerStyleConfig?.grid?.baseColor,
32131
+ ]);
32132
+ const mergedMapConfig = React.useMemo(() => merge$1({}, DEFAULT_STYLES, getPartitionPickerMapConfig(platform), mapConfig), [mapConfig, platform]);
32133
+ const svgElementDatas = React.useMemo(() => UnifiedMapDataProcessor.processMapData(mapJson, mergedMapConfig) || {}, [mapJson, mergedMapConfig]);
32134
+ /** 地图 Y 轴相对正北的偏移(度),与 MowerMapRenderer.originNorthRotate 一致 */
32135
+ const originNorthRotate = React.useMemo(() => {
32136
+ if (mapJson.map_north_offset)
32137
+ return (mapJson.map_north_offset * 180) / Math.PI;
32138
+ return 0;
32139
+ }, [mapJson.map_north_offset]);
32140
+ /** Standalone 模式:叠加层实际旋转角(度)= 地图北向偏移 + 外部传入 rotation */
32141
+ const standaloneActureRotate = React.useMemo(() => originNorthRotate + (defaultTransform?.rotation ?? 0), [originNorthRotate, defaultTransform?.rotation]);
32142
+ /**
32143
+ * 稳定的内容 viewBox(地图全量范围 + SVG 裁剪边距)
32144
+ * 供 ChannelClipPath 使用,不随用户平移/缩放变化
32145
+ */
32146
+ const contentViewBox = React.useMemo(() => {
32147
+ const bounds = calculateMapBounds(mapJson);
32148
+ return boundsToContentViewBox(bounds, PARTITION_PICKER_CONTENT_CLIP_PADDING);
32149
+ }, [mapJson]);
32150
+ /** 孤立地块 id 列表(未连接到主地块的子地块) */
32151
+ const isolatedBoundaryIds = useIsolatedBoundaryIds(mapJson);
32152
+ const boundaryNameById = React.useMemo(() => {
32153
+ const names = new Map();
32154
+ for (const boundary of generateBoundaryData(mapJson)) {
32155
+ names.set(boundary.id, boundary.name);
32156
+ }
32157
+ return names;
32158
+ }, [mapJson]);
32159
+ const handleIsolatedTap = React.useCallback((boundaryId, clientX, clientY) => {
32160
+ if (!onIsolatedBoundaryTap || !isolatedBoundaryIds.has(boundaryId))
32161
+ return;
32162
+ onIsolatedBoundaryTap({
32163
+ boundaryId,
32164
+ boundaryName: boundaryNameById.get(boundaryId),
32165
+ clientX,
32166
+ clientY,
32167
+ });
32168
+ }, [boundaryNameById, isolatedBoundaryIds, onIsolatedBoundaryTap]);
32169
+ const isolatedTapHandler = onIsolatedBoundaryTap ? handleIsolatedTap : undefined;
32170
+ /** 选中地块 id 列表(受控/非受控)与切换选中回调 */
32171
+ const { selectedIds, toggleBoundary, setSelectedIds } = useBoundarySelection({
32172
+ selectedBoundaryIds,
32173
+ defaultSelectedBoundaryIds,
32174
+ onSelectionChange,
32175
+ disabledBoundaryIds: isolatedBoundaryIds,
32176
+ });
32177
+ /** 当前 hover 的地块 id(受控/非受控)与切换 hover 回调 */
32178
+ const { hoveredId, setHoveredId } = useBoundaryHover({
32179
+ hoveredBoundaryId,
32180
+ defaultHoveredBoundaryId,
32181
+ onBoundaryHoverChange,
32182
+ });
32183
+ const handleHoverChange = React.useCallback((id) => {
32184
+ if (id != null && isolatedBoundaryIds.has(id))
32185
+ return;
32186
+ setHoveredId(id);
32187
+ }, [isolatedBoundaryIds, setHoveredId]);
32188
+ /** Map 模式:地块 polygon / 标签直接 onClick(无底图拖拽抢占) */
32189
+ const handleBoundaryClick = React.useCallback((id) => {
32190
+ if (isolatedBoundaryIds.has(id))
32191
+ return;
32192
+ toggleBoundary(id);
32193
+ }, [isolatedBoundaryIds, toggleBoundary]);
32194
+ /** Standalone 模式:由 viewport 统一处理点击(支持在地块上拖拽平移) */
32195
+ const handleViewportTap = React.useCallback(({ target, clientX, clientY }) => {
32196
+ const isolatedEl = target.closest(`[${PARTITION_ISOLATED_BOUNDARY_ID_ATTR}]`);
32197
+ if (isolatedEl) {
32198
+ const isolatedId = Number(isolatedEl.getAttribute(PARTITION_ISOLATED_BOUNDARY_ID_ATTR));
32199
+ if (Number.isFinite(isolatedId) && isolatedBoundaryIds.has(isolatedId)) {
32200
+ handleIsolatedTap(isolatedId, clientX, clientY);
32201
+ }
32202
+ return;
32203
+ }
32204
+ const boundaryEl = target.closest(`[${PARTITION_BOUNDARY_ID_ATTR}]`);
32205
+ if (!boundaryEl)
32206
+ return;
32207
+ const id = Number(boundaryEl.getAttribute(PARTITION_BOUNDARY_ID_ATTR));
32208
+ if (!Number.isFinite(id) || isolatedBoundaryIds.has(id))
32209
+ return;
32210
+ toggleBoundary(id);
32211
+ }, [handleIsolatedTap, isolatedBoundaryIds, toggleBoundary]);
32212
+ /**
32213
+ * Map 模式 overlay 状态(bounds / viewBox / fitBounds / overlayScale)。
32214
+ * 无 mapRef 时 hook 内部 enabled=false,返回值不参与渲染。
32215
+ */
32216
+ const googleOverlay = useGoogleMapsOverlay({
32217
+ mapJson,
32218
+ mapRef,
32219
+ defaultTransform,
32220
+ originNorthRotate,
32221
+ platform,
32222
+ padding,
32223
+ });
32224
+ /** Standalone 才测量容器;Map 模式尺寸由 OverlayViewF onDraw 提供 */
32225
+ const { containerRef, layoutStyle, width: pxWidth, height: pxHeight, isReady, } = useContainerSize(isMapMode ? 0 : width, isMapMode ? 0 : height);
32226
+ React.useEffect(() => {
32227
+ if (!isMapMode && isReady) {
32228
+ onSizeChange?.(pxWidth, pxHeight);
32229
+ }
32230
+ }, [isMapMode, isReady, onSizeChange, pxWidth, pxHeight]);
32231
+ /** Standalone:平移/缩放 viewBox;Map 模式 enablePanZoom 关闭,仅保留 hook 结构 */
32232
+ const { viewBox: standaloneViewBox, gridViewBox, resetView: standaloneResetView, fitToView: standaloneFitToView, } = useMapViewport({
32233
+ mapJson,
32234
+ containerRef,
32235
+ width: pxWidth,
32236
+ height: pxHeight,
32237
+ padding,
32238
+ minZoom,
32239
+ maxZoom,
32240
+ refitOnResize,
32241
+ enablePanZoom: enablePanZoom && !isMapMode,
32242
+ onViewportTap: handleViewportTap,
32243
+ mapRotationDeg: standaloneActureRotate,
32244
+ });
32245
+ /** 按模式选取 viewBox / 尺寸 / 旋转 / 就绪状态(Map 与 Standalone 数据源不同) */
32246
+ const viewBox = isMapMode ? googleOverlay.svgViewBox : standaloneViewBox;
32247
+ const overlayWidth = isMapMode ? googleOverlay.overlayWidth : pxWidth;
32248
+ const overlayHeight = isMapMode ? googleOverlay.overlayHeight : pxHeight;
32249
+ const actureRotate = isMapMode ? googleOverlay.actureRotate : standaloneActureRotate;
32250
+ const isLayersReady = isMapMode ? googleOverlay.isOverlayReady : isReady;
32251
+ /**
32252
+ * Standalone:标签/充电桩置于 mapOverlayLayer,CSS rotate 与 MowerMapRenderer 一致。
32253
+ * Map 模式:旋转由 OverlayViewF 承担,此处不需要额外包裹层。
32254
+ */
32255
+ const mapOverlayLayerStyle = React.useMemo(() => !isMapMode && actureRotate !== 0 ? { transform: `rotate(${actureRotate}deg)` } : undefined, [actureRotate, isMapMode]);
32256
+ /**
32257
+ * 分区名定位:
32258
+ * - Map 模式 overlay:相对 overlay 宽高百分比(与 BoundaryLabels 一致)
32259
+ * - Standalone meet:按 preserveAspectRatio 换算容器百分比
32260
+ */
32261
+ const labelItems = usePartitionLabelItems(mapJson, viewBox, overlayWidth, overlayHeight, {
32262
+ positionMode: isMapMode ? 'overlay' : 'meet',
32263
+ });
32264
+ /**
32265
+ * 复用 ChannelClipPath / ChannelElement / CharginPile 所需的最小 Context
32266
+ * - svgViewBox:稳定 contentViewBox(见上)
32267
+ * - channelClipPathId:同页多实例时避免 SVG id 冲突
32268
+ * - platform:供子组件读取 Web / H5 差异
32269
+ * - Map 模式额外注入 mapRef / bounds / overlayScale(通道线宽等)
32270
+ */
32271
+ const commonValue = React.useMemo(() => ({
32272
+ sn,
32273
+ platform,
32274
+ svgViewBox: contentViewBox,
32275
+ channelClipPathId,
32276
+ ...(isMapMode && mapRef
32277
+ ? {
32278
+ mapRef,
32279
+ bounds: googleOverlay.bounds,
32280
+ overlayScale: googleOverlay.overlayScale,
32281
+ }
32282
+ : {}),
32283
+ }), [
32284
+ sn,
32285
+ platform,
32286
+ contentViewBox,
32287
+ channelClipPathId,
32288
+ isMapMode,
32289
+ mapRef,
32290
+ googleOverlay.bounds,
32291
+ googleOverlay.overlayScale,
32292
+ ]);
32293
+ const svgEditValue = React.useMemo(() => ({
32294
+ svgElementDatas,
32295
+ }), [svgElementDatas]);
32296
+ React.useImperativeHandle(ref, () => ({
32297
+ fitToView: isMapMode ? googleOverlay.fitToView : standaloneFitToView,
32298
+ resetView: isMapMode ? googleOverlay.resetView : standaloneResetView,
32299
+ getSelectedBoundaryIds: () => selectedIds,
32300
+ setSelectedBoundaryIds: setSelectedIds,
32301
+ getHoveredBoundaryId: () => hoveredId,
32302
+ setHoveredBoundaryId: setHoveredId,
32303
+ }), [
32304
+ googleOverlay.fitToView,
32305
+ googleOverlay.resetView,
32306
+ hoveredId,
32307
+ isMapMode,
32308
+ selectedIds,
32309
+ setHoveredId,
32310
+ setSelectedIds,
32311
+ standaloneFitToView,
32312
+ standaloneResetView,
32313
+ ]);
32314
+ const containerStyle = React.useMemo(() => ({
32315
+ backgroundColor: isMapMode ? 'transparent' : resolvedBackgroundColor,
32316
+ ...style,
32317
+ ...(isMapMode ? {} : layoutStyle),
32318
+ }), [isMapMode, layoutStyle, resolvedBackgroundColor, style]);
32319
+ /** 地块 + 通道 SVG;Map 模式不在 SVG 内旋转(由 OverlayViewF rotate 处理) */
32320
+ const mapSvgLayer = viewBox && isLayersReady ? (jsxRuntime.jsx(PartitionPickerSvg, { svgElementDatas: svgElementDatas, viewBox: viewBox, containerWidth: overlayWidth, containerHeight: overlayHeight, mapRotation: isMapMode ? 0 : actureRotate, boundaryColors: mergedPickerStyles.boundary, boundaryStrokePx: mergedPickerStyles.boundaryStrokePx, isolatedBoundaryIds: isolatedBoundaryIds, selectedIds: selectedIds, hoveredId: hoveredId, onHoverChange: handleHoverChange, onBoundaryClick: isMapMode ? handleBoundaryClick : undefined, onIsolatedTap: isMapMode ? isolatedTapHandler : undefined })) : null;
32321
+ /**
32322
+ * 分区标签 + 充电桩 HTML 层。
32323
+ * Map 模式:平铺于 overlay 根节点;Standalone:包在 mapOverlayLayer 内做 CSS 旋转。
32324
+ */
32325
+ const overlayLayers = viewBox && isLayersReady ? (isMapMode ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(PartitionNameLabels, { items: labelItems, selectedIds: selectedIds, labelStyles: mergedPickerStyles.labels, platform: platform, showSelectionOrder: showSelectionOrder, rotation: actureRotate, onHoverChange: handleHoverChange, onBoundaryClick: handleBoundaryClick, onIsolatedTap: isolatedTapHandler }), jsxRuntime.jsx(CharginPile, { viewBox: viewBox, rotation: actureRotate })] })) : (jsxRuntime.jsxs("div", { className: styles.mapOverlayLayer, style: mapOverlayLayerStyle, children: [jsxRuntime.jsx(PartitionNameLabels, { items: labelItems, selectedIds: selectedIds, labelStyles: mergedPickerStyles.labels, platform: platform, showSelectionOrder: showSelectionOrder, rotation: actureRotate, onHoverChange: handleHoverChange, onIsolatedTap: isolatedTapHandler }), jsxRuntime.jsx(CharginPile, { viewBox: viewBox, rotation: actureRotate, viewRotationMode: "overlay-parent", containerWidth: overlayWidth, containerHeight: overlayHeight, sizeInSvgUnits: true, minDisplaySizePx: PARTITION_PICKER_CHARGING_PILE_MIN_DISPLAY_PX })] }))) : null;
32326
+ /** Map 模式:渲染 OverlayViewF,底图为 Google Maps */
32327
+ if (isMapMode && mapRef) {
32328
+ return (jsxRuntime.jsx(CommonContextProvider, { value: commonValue, children: jsxRuntime.jsx(SvgEditContextProvider, { value: svgEditValue, children: jsxRuntime.jsx(OverlayViewF, { map: mapRef, mapPaneName: "overlayMouseTarget", bounds: googleOverlay.bounds ?? undefined, rotate: actureRotate, zIndex: zIndex, onDraw: googleOverlay.handleOverlayDraw, children: jsxRuntime.jsxs("div", { className: `${styles.googleOverlayRoot} ${className || ''}`, style: containerStyle, onMouseLeave: () => setHoveredId(null), children: [mapSvgLayer, overlayLayers] }) }) }) }));
32329
+ }
32330
+ /** Standalone 模式:固定容器 + 网格底图 + 双层 SVG / HTML overlay */
32331
+ return (jsxRuntime.jsx(CommonContextProvider, { value: commonValue, children: jsxRuntime.jsx(SvgEditContextProvider, { value: svgEditValue, children: jsxRuntime.jsx("div", { ref: containerRef, className: `${styles.container} ${className || ''}`, "data-viewport-background": "true", style: containerStyle, onMouseLeave: () => setHoveredId(null), children: isLayersReady && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: styles.mapSvgStack, children: [jsxRuntime.jsx(PartitionPickerGridSvg, { viewBox: gridViewBox, gridStyle: standaloneGridStyle }), mapSvgLayer] }), overlayLayers] })) }) }) }));
32332
+ });
32333
+ MowerPartitionPickerMap.displayName = 'MowerPartitionPickerMap';
32334
+
30217
32335
  exports.ALL_DIRECTION_SELECTED = ALL_DIRECTION_SELECTED;
30218
32336
  exports.AntennaData = AntennaData;
30219
32337
  exports.BaseData = BaseData;
@@ -30221,12 +32339,18 @@ exports.BoundaryData = BoundaryData;
30221
32339
  exports.BoundarySvgRender = BoundarySvgRender;
30222
32340
  exports.ChannelData = ChannelData;
30223
32341
  exports.ChargingPileData = ChargingPileData;
32342
+ exports.DEFAULT_SCREEN_PADDING = DEFAULT_SCREEN_PADDING;
30224
32343
  exports.DoodleData = DoodleData;
30225
32344
  exports.MAX_DIRECTION_ANGLE = MAX_DIRECTION_ANGLE;
30226
32345
  exports.MIN_DIRECTION_ANGLE = MIN_DIRECTION_ANGLE;
30227
32346
  exports.MowGateData = MowGateData;
30228
32347
  exports.MowerMapRenderer = MowerMapRenderer;
32348
+ exports.MowerPartitionPickerMap = MowerPartitionPickerMap;
30229
32349
  exports.ObstacleData = ObstacleData;
32350
+ exports.PARTITION_PICKER_COLORS_H5 = PARTITION_PICKER_COLORS_H5;
32351
+ exports.PARTITION_PICKER_COLORS_WEB = PARTITION_PICKER_COLORS_WEB;
32352
+ exports.PARTITION_PICKER_LABEL_STYLES_H5 = PARTITION_PICKER_LABEL_STYLES_H5;
32353
+ exports.PARTITION_PICKER_LABEL_STYLES_WEB = PARTITION_PICKER_LABEL_STYLES_WEB;
30230
32354
  exports.PathData = PathData;
30231
32355
  exports.SCALE_FACTOR = SCALE_FACTOR;
30232
32356
  exports.SvgParserNative = SvgParserNative;
@@ -30234,10 +32358,17 @@ exports.UnifiedMapDataProcessor = UnifiedMapDataProcessor;
30234
32358
  exports.VisionOffData = VisionOffData;
30235
32359
  exports.calculateMapGpsCenter = calculateMapGpsCenter;
30236
32360
  exports.estimateGpsFromMapBounds = estimateGpsFromMapBounds;
32361
+ exports.getIsolatedBoundaryIds = getIsolatedBoundaryIds;
32362
+ exports.getPartitionPickerColors = getPartitionPickerColors;
32363
+ exports.getPartitionPickerLabelStyles = getPartitionPickerLabelStyles;
30237
32364
  exports.getValidGpsBounds = getValidGpsBounds;
30238
32365
  exports.initBoundary = initBoundary;
30239
32366
  exports.initChannel = initChannel;
30240
32367
  exports.initDoodle = initDoodle;
30241
32368
  exports.initObstacle = initObstacle;
30242
32369
  exports.initVisionOff = initVisionOff;
32370
+ exports.normalizeScreenPadding = normalizeScreenPadding;
32371
+ exports.resolvePartitionPickerStyles = resolvePartitionPickerStyles;
30243
32372
  exports.restorePointsFormat = restorePointsFormat;
32373
+ exports.signGoogleStaticMapUrl = signGoogleStaticMapUrl;
32374
+ exports.signGoogleStaticMapUrlSync = signGoogleStaticMapUrlSync;