ソースを参照

状态监控总览:点击子区时在地图上绘制圆形蒙层

   - TongzhouTrafficMap 新增 drawSubAreaCircle(leaves) 方法:
     以子区内路口坐标均值为圆心,最大距离 ×1.3 为半径,
     绘制半透明圆形蒙层(AMap.Circle,fillOpacity 0.15,虚线描边)
   - 切换子区时自动清除上一个圆(clearSubAreaOverlays)
   - beforeDestroy 中随地图一起清理覆盖物
   - StatusMonitoring handleFolderClick 聚焦地图后调用 drawSubAreaCircle,
     初始不显示,仅在点击子区菜单时触发
画安 1 週間 前
コミット
31cd2ad885
共有3 個のファイルを変更した65 個の追加3 個の削除を含む
  1. 3 0
      src/api/index.js
  2. 60 3
      src/components/TongzhouTrafficMap.vue
  3. 2 0
      src/views/StatusMonitoring.vue

+ 3 - 0
src/api/index.js

@@ -109,3 +109,6 @@ export const apiGetCrossingTopCharts = () =>
 
 export const apiGetOverviewTopCharts = () =>
   http.get('/overview/top-charts')
+
+export const apiGetMapLegendConfig = () =>
+  http.get('/map/legend-config')

+ 60 - 3
src/components/TongzhouTrafficMap.vue

@@ -86,6 +86,7 @@ export default {
       statusIntersections: {},
       currentZoomSize: 14, // 保存当前的动态尺寸
       markerById: {}, // ID → marker 索引,加速 focusById 查找
+      subAreaOverlays: [], // 子区蒙层覆盖物
     };
   },
   mounted() {
@@ -141,7 +142,10 @@ export default {
       this.routeGroups = {};
     }
 
-    // 4. 销毁地图实例并清空引用
+    // 4. 清理子区蒙层
+    this.clearSubAreaOverlays();
+
+    // 6. 销毁地图实例并清空引用
     if (this.map) {
       try {
         this.map.destroy();
@@ -151,7 +155,7 @@ export default {
       this.map = null;
     }
 
-    // 5. 清理其他引用
+    // 7. 清理其他引用
     this.AMap = null;
     this.driving = null;
     if (this.infoCloseTimer) {
@@ -1355,7 +1359,60 @@ export default {
     lngLatToPixel(lng, lat) {
         if (!this.map) return null;
         return this.map.lngLatToContainer([lng, lat]);
-    }
+    },
+
+    /**
+     * 对外暴露:为指定子区路口列表绘制圆形蒙层,点击不同子区时自动替换上一个
+     * @param {Array<{lng: number, lat: number}>} leaves - 子区内所有叶子路口节点
+     */
+    drawSubAreaCircle(leaves) {
+      if (!this.isMapReady() || !leaves || leaves.length === 0) return;
+
+      this.clearSubAreaOverlays();
+
+      // 计算圆心(路口坐标均值)
+      const cx = leaves.reduce((s, n) => s + Number(n.lng), 0) / leaves.length;
+      const cy = leaves.reduce((s, n) => s + Number(n.lat), 0) / leaves.length;
+
+      // 计算各路口到圆心的最大距离(米),cosLat 修正经度方向的比例
+      const cosLat = Math.cos(cy * Math.PI / 180);
+      let maxDist = 0;
+      for (const n of leaves) {
+        const dx = (Number(n.lng) - cx) * 111000 * cosLat;
+        const dy = (Number(n.lat) - cy) * 111000;
+        const d = Math.sqrt(dx * dx + dy * dy);
+        if (d > maxDist) maxDist = d;
+      }
+      // 半径 = 最大距离 × 1.3,最小保底 500m
+      const radius = Math.max(maxDist * 1.3, 500);
+
+      const circle = new this.AMap.Circle({
+        center: [cx, cy],
+        radius,
+        fillColor: '#4A9EFF',
+        fillOpacity: 0.15,
+        strokeColor: '#4A9EFF',
+        strokeOpacity: 0.6,
+        strokeWeight: 1,
+        strokeStyle: 'dashed',
+        strokeDasharray: [4, 4],
+        zIndex: 10,
+        bubble: true,
+      });
+
+      this.subAreaOverlays.push(circle);
+      this.map.add(circle);
+    },
+
+    /**
+     * 清除子区圆形蒙层
+     */
+    clearSubAreaOverlays() {
+      if (this.map && this.subAreaOverlays.length > 0) {
+        try { this.map.remove(this.subAreaOverlays); } catch (e) { void e; }
+        this.subAreaOverlays = [];
+      }
+    },
   }
 };
 </script>

+ 2 - 0
src/views/StatusMonitoring.vue

@@ -327,6 +327,8 @@ export default {
             const zoom = leaves.length <= 6 ? 15 : 14;
             const map = this.$refs.trafficMapRef.map;
             if (map) map.setZoomAndCenter(zoom, [avgLng, avgLat], false, 500);
+            // 绘制该子区的圆形蒙层(自动替换上一个)
+            this.$refs.trafficMapRef.drawSubAreaCircle(leaves);
         },
         // 处理菜单点击
         handleMenuClick(nodeData) {