Bladeren bron

修复总览菜单点击与地图标记不匹配的问题

  1. classifyIntersectionsByStatus重写分配逻辑,确保所有622条路口数据
     全部绑定marker(之前约377条有marker,其余落在切片间隙中)
  2. 创建marker时存入路口编号(id)到extData,支持按ID查找
  3. 新增focusById方法,通过路口编号精确定位marker并弹出信息窗
     (菜单坐标与地图坐标系不同,偏差约500米,无法用坐标匹配)
  4. handleMenuClick改用focusById替代原localStorage随机取点逻辑,
     等地图动画完成后再计算像素位置并打开弹窗
  5. 点击新路口时先关闭上一个总览小弹窗,再移动地图,最后打开新弹窗
画安 2 weken geleden
bovenliggende
commit
7e41135d76
2 gewijzigde bestanden met toevoegingen van 86 en 36 verwijderingen
  1. 45 14
      src/components/TongzhouTrafficMap.vue
  2. 41 22
      src/views/StatusMonitoring.vue

+ 45 - 14
src/components/TongzhouTrafficMap.vue

@@ -208,23 +208,25 @@ export default {
     // 将真实路口数据按状态类型分类
     classifyIntersectionsByStatus() {
       const remainingData = this.intersectionData;
-      const normalStatusCount = 6;
-      const abnormalStatusCount = 3;
-      const chunkSize = Math.floor(remainingData.length / (normalStatusCount + abnormalStatusCount));
       const maxAbnormalCount = 4;
+      // 异常状态各取4个,剩余全部按比例分配给6个正常状态
+      const abnormalTotal = maxAbnormalCount * 3;
+      const normalData = remainingData.slice(0, remainingData.length - abnormalTotal);
+      const abnormalData = remainingData.slice(remainingData.length - abnormalTotal);
+      const normalChunk = Math.ceil(normalData.length / 6);
 
       this.statusIntersections = {
-        "中心计划": remainingData.slice(0, 20),
+        "中心计划": normalData.slice(0, normalChunk),
         "干线协调": [], // 清空,改为动态生成
         "勤务路线": [], // 清空,改为动态生成
-        "定周期控制": remainingData.slice(chunkSize, chunkSize * 2),
-        "感应控制": remainingData.slice(chunkSize * 2, chunkSize * 3),
-        "自适应控制": remainingData.slice(chunkSize * 3, chunkSize * 4),
-        "手动控制": remainingData.slice(chunkSize * 4, chunkSize * 5),
-        "特殊控制": remainingData.slice(chunkSize * 5, chunkSize * 6),
-        "离线": remainingData.slice(chunkSize * 6, Math.min(chunkSize * 7, chunkSize * 6 + maxAbnormalCount)),
-        "降级": remainingData.slice(chunkSize * 7, Math.min(chunkSize * 8, chunkSize * 7 + maxAbnormalCount)),
-        "故障": remainingData.slice(chunkSize * 8, Math.min(chunkSize * 9, chunkSize * 8 + maxAbnormalCount))
+        "定周期控制": normalData.slice(normalChunk, normalChunk * 2),
+        "感应控制": normalData.slice(normalChunk * 2, normalChunk * 3),
+        "自适应控制": normalData.slice(normalChunk * 3, normalChunk * 4),
+        "手动控制": normalData.slice(normalChunk * 4, normalChunk * 5),
+        "特殊控制": normalData.slice(normalChunk * 5),
+        "离线": abnormalData.slice(0, maxAbnormalCount),
+        "降级": abnormalData.slice(maxAbnormalCount, maxAbnormalCount * 2),
+        "故障": abnormalData.slice(maxAbnormalCount * 2, maxAbnormalCount * 3)
       };
     },
 
@@ -336,6 +338,7 @@ export default {
           const markers = intersections.map(item =>
             this.createTrafficLightMarker([item["位置-经度"], item["位置-纬度"]], {
               ...config,
+              id: item["路口编号"],
               road: item["路口名称"] || '规划路口'
             })
           ).filter(Boolean);
@@ -1135,13 +1138,13 @@ export default {
           const dx = markerPos[0] - targetLng;
           const dy = markerPos[1] - targetLat;
           const distSq = dx * dx + dy * dy;
-          
+
           // 容差范围内(约 20 米),寻找最接近的点
           if (distSq < 0.000001) {
             // 优先规则:如果距离相同或非常接近,优先选择异常状态点(离线/降级/故障)
             const isAbnormal = ["离线", "降级", "故障"].includes(markerExt.name);
             const currentIsAbnormal = bestMarker ? ["离线", "降级", "故障"].includes(bestMarker.getExtData().name) : false;
-            
+
             if (distSq < minDistanceSq || (isAbnormal && !currentIsAbnormal)) {
               minDistanceSq = distSq;
               bestMarker = item;
@@ -1162,6 +1165,34 @@ export default {
       }
     },
 
+    // 通过路口编号定位到对应的 marker,弹出信息窗
+    focusById(id) {
+      if (!this.isMapReady() || !id) return null;
+
+      let bestMarker = null;
+      Object.values(this.routeGroups).forEach(group => {
+        if (!Array.isArray(group) || bestMarker) return;
+        group.forEach(item => {
+          if (bestMarker) return;
+          if (!(item instanceof this.AMap.Marker)) return;
+          const ext = item.getExtData();
+          if (ext.id === id || ext['路口编号'] === id) {
+            bestMarker = item;
+          }
+        });
+      });
+
+      if (bestMarker) {
+        const finalPos = bestMarker.getPosition();
+        this.map.setZoomAndCenter(17, finalPos, false, 500);
+        setTimeout(() => {
+          if (!this.isComponentDestroyed) this.openLightInfo(bestMarker.getExtData(), finalPos);
+        }, 600);
+        return finalPos;
+      }
+      return null;
+    },
+
     toggleLegend() {
       this.legendVisible = !this.legendVisible;
     },

+ 41 - 22
src/views/StatusMonitoring.vue

@@ -314,28 +314,45 @@ export default {
         // 处理菜单点击
         handleMenuClick(nodeData) {
             console.log('父组件接收到了最底层路口点击事件:', nodeData);
-            // 通过地图组件获取像素坐标(如果有经纬度的话)
-            // if (nodeData.lng && nodeData.lat && this.$refs.trafficMapRef) {
-            //     // 地图联动
-            //     this.$refs.trafficMapRef.focusByLocation([nodeData.lng, nodeData.lat]);
-
-            //     const pixel = this.$refs.trafficMapRef.lngLatToPixel(nodeData.lng, nodeData.lat);
-            //     if (pixel) {
-            //         const scale = window.innerWidth / 1920;
-            //         nodeData.pixelX = Math.round(pixel.x / scale) + 20;
-            //         nodeData.pixelY = Math.round(pixel.y / scale);
-            //     }
-            // }
-
-            // 根据Tab来显示不同的弹窗内容
-            if (this.activeLeftTab === 'overview') { // 总览
-                // 临时逻辑,有真实接口后可以删除
-                const index = Math.floor(Math.random() * 10);
-                const position = localStorage.getItem(`pos${index + 1}`).split(',');
 
-                // 地图联动
-                this.$refs.trafficMapRef.focusByLocation([Number(position[0]), Number(position[1])]);
-                
+            const showDialog = () => {
+                if (this.activeLeftTab === 'overview') {
+                    this.showOverviewDalogs(nodeData);
+                } else if (this.activeLeftTab === 'crossing') {
+                    this.showCrossingDalogs(nodeData);
+                } else if (this.activeLeftTab === 'trunkLine') {
+                    this.showTrunkLineDalogs(nodeData);
+                } else if (this.activeLeftTab === 'specialDuty') {
+                    this.showSpecialDutyDalogs(nodeData);
+                }
+            };
+
+            // 先关闭上一个总览弹窗
+            if (this._lastOverviewDialogId) {
+                this.$refs.layout.handleDialogClose(this._lastOverviewDialogId);
+                this._lastOverviewDialogId = null;
+            }
+
+            // 通过路口编号定位地图 marker,动画完成后计算像素位置再弹窗
+            if (this.$refs.trafficMapRef) {
+                const mapPos = this.$refs.trafficMapRef.focusById(nodeData.id);
+                if (mapPos) {
+                    const lngLat = mapPos.getLng ? [mapPos.getLng(), mapPos.getLat()] : mapPos;
+                    setTimeout(() => {
+                        const pixel = this.$refs.trafficMapRef.lngLatToPixel(lngLat[0], lngLat[1]);
+                        if (pixel) {
+                            const scale = window.innerWidth / 1920;
+                            nodeData.pixelX = Math.round(pixel.x / scale) + 20;
+                            nodeData.pixelY = Math.round(pixel.y / scale);
+                        }
+                        showDialog();
+                    }, 600);
+                    return;
+                }
+            }
+
+            // fallback: 没有地图或找不到 marker 时直接弹窗
+            if (this.activeLeftTab === 'overview') { // 总览
                 this.showOverviewDalogs(nodeData);
             } else if (this.activeLeftTab === 'crossing') { // 路口
                 this.showCrossingDalogs(nodeData);
@@ -361,9 +378,11 @@ export default {
         // 显示总览弹窗组
         showOverviewDalogs(nodeData) {
             console.log('显示总览弹窗组', nodeData.id, nodeData.label);
+            const dialogId = 'crossing3_' + nodeData.id;
+            this._lastOverviewDialogId = dialogId;
             // 路口弹窗
             this.$refs.layout.openDialog({
-                id: 'crossing3_' + nodeData.id, // 这里的 ID 可以根据实际业务场景动态生成
+                id: dialogId, // 这里的 ID 可以根据实际业务场景动态生成
                 title: nodeData.label,
                 component: 'CrossingPanel',
                 width: 260,