Bladeren bron

Merge branch 'master' of http://121.40.40.223:3000/zizhong.wang/dtScreen

hebotao 2 weken geleden
bovenliggende
commit
32e6c23fde

+ 0 - 3
.gitignore

@@ -26,6 +26,3 @@ pnpm-debug.log*
 .history
 pyscripts
 pyscripts/*
-src/components/TongzhouTrafficMap copy.vue
-src/components/TongzhouTrafficMap_实时交通版.vue
-src/components/TongzhouTrafficMap_bak.vue

BIN
public/beijing-gov-satellite.jpg


+ 2 - 2
public/index.html

@@ -10,12 +10,12 @@
     
     <title><%= htmlWebpackPlugin.options.title %></title>
     
-    <script>
+    <!-- <script>
       window.CESIUM_BASE_URL = 'https://cdn.staticfile.org/cesium/1.105.1/';
     </script>
     
     <script src="https://cdn.staticfile.org/cesium/1.105.1/Cesium.js"></script>
-    <link href="https://cdn.staticfile.org/cesium/1.105.1/Widgets/widgets.min.css" rel="stylesheet">
+    <link href="https://cdn.staticfile.org/cesium/1.105.1/Widgets/widgets.min.css" rel="stylesheet"> -->
   </head>
   <body>
     <noscript>

BIN
src/assets/login/login-bg-bak.mp4


BIN
src/assets/login/login-bg.mp4


BIN
src/assets/login/title.png


+ 35 - 3
src/components/CesiumTransition.vue

@@ -1,6 +1,12 @@
 <template>
   <div class="cesium-transition-wrapper">
     <div ref="cesiumContainer" class="cesium-container"></div>
+    <img
+      v-if="microImage"
+      ref="microOverlay"
+      :src="microImage"
+      class="micro-overlay"
+    />
     <div class="ui-layer">
       <div
         v-if="poi"
@@ -94,6 +100,11 @@ export default {
     microViewRange: {
       type: Number,
       default: 10000
+    },
+    // 微观覆盖图片路径(淡入淡出展示)
+    microImage: {
+      type: String,
+      default: null
     }
   },
   data() {
@@ -224,8 +235,8 @@ export default {
       cam.enableTilt = false;
       cam.enableLook = false;
 
-      // 微观区域高清卫星底图
-      if (this.satelliteImage && this.satelliteImage.url) {
+      // 微观区域高清卫星底图(有 microImage 覆盖层时跳过,避免重复)
+      if (!this.microImage && this.satelliteImage && this.satelliteImage.url) {
         const b = this.satelliteImage.bounds;
         this._viewer.imageryLayers.addImageryProvider(
           new Cesium.SingleTileImageryProvider({
@@ -460,7 +471,15 @@ export default {
       this._microEffectsSource.show = true;
       if (this.poi && this.$refs.poiLabel) this.$refs.poiLabel.style.opacity = 1;
 
-      await this._safeDelay(1000);
+      // 阶段4: 微观图片淡入
+      if (this.microImage && this.$refs.microOverlay) {
+        await this._safeDelay(500);
+        if (this._destroyed) return;
+        this.$refs.microOverlay.style.opacity = 1;
+        await this._safeDelay(2000); // 等待淡入完成 + 停留展示
+      } else {
+        await this._safeDelay(1000);
+      }
       if (this._destroyed) return;
       this.$emit('complete');
     }
@@ -488,6 +507,19 @@ export default {
   padding: 0;
 }
 
+.micro-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  opacity: 0;
+  transition: opacity 1.5s ease;
+  z-index: 2;
+  pointer-events: none;
+}
+
 .ui-layer {
   position: absolute;
   top: 0;

+ 209 - 80
src/components/TongzhouTrafficMap.vue

@@ -56,23 +56,19 @@ export default {
       AMap: null,
       map: null,
       infoWindow: null,
-
-
-
       routeGroups: {},
+      dotMarkers: [],
       privateStyle: {
         legend: {}
       },
       legendVisible: true,
       activeLegends: ["中心计划", "干线协调", "勤务路线", "定周期控制", "感应控制", "自适应控制", "手动控制", "特殊控制", "离线", "降级", "故障"],
-      // 核心修正:增加生命周期标识,防止组件销毁后异步回调继续执行
       isComponentDestroyed: false,
       drawSeq: 0,
       driving: null,
       infoCloseTimer: null,
       activeInfoWindowId: null,
       isInfoWindowHovered: false,
-      // 状态类型配置
       statusConfig: [
         { name: "中心计划", color: "#004CDE", type: "normal" },
         { name: "干线协调", color: "#13C373", type: "route" },
@@ -86,20 +82,16 @@ export default {
         { name: "降级", color: "#D9C13B", type: "abnormal" },
         { name: "故障", color: "#FF3938", type: "abnormal" }
       ],
-      // 真实路口数据
       intersectionData: [],
-      // 按状态分类的路口数据
-      statusIntersections: {},
-
+      statusIntersections: {}
     };
   },
   mounted() {
-    this.isComponentDestroyed = false; // 重置标识
+    this.isComponentDestroyed = false;
     this.loadMapData().then(() => {
       this.classifyIntersectionsByStatus();
       this.updateMapByMode();
       this.initAMap();
-      // 在数据加载完成后存储坐标
       this.storeStatusCoordsToLocalStorage();
     });
 
@@ -188,7 +180,10 @@ export default {
     }
   },
   methods: {
-    // 动态加载地图数据
+    /**
+     * 动态加载地图数据
+     * @returns {Promise<void>}
+     */
     async loadMapData() {
       try {
         const mapDataModule = await import('@/mock/map_data_gaode.json');
@@ -200,16 +195,20 @@ export default {
       }
     },
 
-    // 检查地图环境是否安全可用
+    /**
+     * 检查地图环境是否安全可用
+     * @returns {boolean}
+     */
     isMapReady() {
       return !this.isComponentDestroyed && this.map && typeof this.map.add === 'function';
     },
 
-    // 将真实路口数据按状态类型分类
+    /**
+     * 将真实路口数据按状态类型分类
+     */
     classifyIntersectionsByStatus() {
       const remainingData = this.intersectionData;
       const maxAbnormalCount = 4;
-      // 异常状态各取4个,剩余全部按比例分配给6个正常状态
       const abnormalTotal = maxAbnormalCount * 3;
       const normalData = remainingData.slice(0, remainingData.length - abnormalTotal);
       const abnormalData = remainingData.slice(remainingData.length - abnormalTotal);
@@ -217,8 +216,8 @@ export default {
 
       this.statusIntersections = {
         "中心计划": normalData.slice(0, normalChunk),
-        "干线协调": [], // 清空,改为动态生成
-        "勤务路线": [], // 清空,改为动态生成
+        "干线协调": [],
+        "勤务路线": [],
         "定周期控制": normalData.slice(normalChunk, normalChunk * 2),
         "感应控制": normalData.slice(normalChunk * 2, normalChunk * 3),
         "自适应控制": normalData.slice(normalChunk * 3, normalChunk * 4),
@@ -230,6 +229,9 @@ export default {
       };
     },
 
+    /**
+     * 根据模式更新地图显示
+     */
     updateMapByMode() {
       switch (this.mode) {
         case '路口':
@@ -246,6 +248,9 @@ export default {
       }
     },
 
+    /**
+     * 更新地图显示状态
+     */
     updateMapDisplay() {
       if (this.infoWindow) this.infoWindow.close();
       if (!this.isMapReady()) return;
@@ -262,17 +267,19 @@ export default {
       });
     },
 
+    /**
+     * 初始化高德地图
+     * @returns {Promise<void>}
+     */
     async initAMap() {
       if (this.isComponentDestroyed) return;
 
-      // 确保在加载前注入
       window._AMapSecurityConfig = { securityJsCode: this.securityJsCode };
 
       try {
         const AMap = await AMapLoader.load({
           key: this.amapKey,
           version: "2.0",
-          // 核心:确保 Driving 插件在此加载
           plugins: ['AMap.Driving']
         });
 
@@ -282,14 +289,11 @@ export default {
         this.map = new AMap.Map(this.$refs.mapContainer, {
           zoom: 15,
           mapStyle: "amap://styles/darkblue",
-          center: [116.663, 39.905], // 通州区中心
+          center: [116.663, 39.905]
         });
 
         this.driving = new AMap.Driving({ map: null, hideMarkers: true });
 
-
-
-        // 建议在地图加载完成后再画线
         this.map.on('complete', () => {
           if (!this.isComponentDestroyed) {
             this.drawStaticRoutes();
@@ -300,9 +304,10 @@ export default {
       }
     },
 
-    // 绘制静态路线和标记
-    // 对于普通状态,从 mock 数据加载路口标记
-    // 对于路线类(干线协调和勤务路线),使用高德地图路线规划绘制路线和密集点位
+    /**
+     * 绘制静态路线和标记
+     * @returns {Promise<void>}
+     */
     async drawStaticRoutes() {
       if (!this.isMapReady()) return;
 
@@ -321,11 +326,8 @@ export default {
           { start: [116.66325, 39.9171], end: [116.6833, 39.9171], color: "#13C373", trunkId: 'trunk_6', trunkName: '张台路与湖亦路路口' },
         ],
         "勤务路线": [
-          // 第一段:未执行(全红)— 通州大街西段,干线协调下方约600m
           { start: [116.6445, 39.8980], end: [116.6850, 39.8980], color: "#BC301D", dutyState: 'pending' },
-          // 第二段:执行中(进度45%)— 通州大街中段
           { start: [116.6850, 39.8980], end: [116.7250, 39.8980], color: "#BC301D", dutyState: 'active', progress: 0.45 },
-          // 第三段:已执行完(全灰)— 通州大街东段
           { start: [116.7250, 39.8980], end: [116.7650, 39.8980], color: "#BC301D", dutyState: 'done' }
         ]
       };
@@ -333,7 +335,6 @@ export default {
       for (const config of this.statusConfig) {
         if (this.isComponentDestroyed || drawSeq !== this.drawSeq) return;
 
-        // 1. 处理普通非路线状态(从 mock 数据加载)
         if (!realRouteConfigs[config.name]) {
           const intersections = this.statusIntersections[config.name] || [];
           const markers = intersections.map(item =>
@@ -348,7 +349,6 @@ export default {
           continue;
         }
 
-        // 2. 处理路线类(干线/特勤)
         this.routeGroups[config.name] = [];
         const lines = realRouteConfigs[config.name] || [];
         const trunkSegments = [];
@@ -372,7 +372,6 @@ export default {
             path
           });
 
-          // 每条路线配置对应一条干线菜单项
           if (config.name === '干线协调' && overlays.length > 0) {
             const name = line.trunkName || ('干线' + (lineIdx + 1));
             trunkSegments.push({
@@ -394,13 +393,15 @@ export default {
           await this.sleep(80);
         }
 
-        // 干线协调绘制完成后,通知父组件用于菜单渲染
         if (config.name === '干线协调' && trunkSegments.length > 0) {
           this.$emit('bindTrunkMenuTree', trunkSegments);
         }
       }
     },
 
+    /**
+     * 清除所有路线覆盖物
+     */
     clearAllRouteOverlays() {
       if (!this.isMapReady()) return;
       try {
@@ -426,12 +427,24 @@ export default {
       });
 
       this.routeGroups = {};
+      this.dotMarkers = [];
     },
 
+    /**
+     * 休眠函数
+     * @param {number} ms - 休眠毫秒数
+     * @returns {Promise<void>}
+     */
     sleep(ms) {
       return new Promise(resolve => setTimeout(resolve, ms));
     },
 
+    /**
+     * 带重试机制的路径规划
+     * @param {Array<number>} start - 起点坐标 [lng, lat]
+     * @param {Array<number>} end - 终点坐标 [lng, lat]
+     * @returns {Promise<Array>} 路径点数组
+     */
     async searchDrivingPathWithRetry(start, end) {
       if (!this.AMap || !this.driving || typeof this.driving.search !== 'function') {
         throw new Error('Driving not ready');
@@ -450,6 +463,12 @@ export default {
       throw lastErr || new Error('Driving search failed');
     },
 
+    /**
+     * 单次路径规划
+     * @param {Array<number>} start - 起点坐标 [lng, lat]
+     * @param {Array<number>} end - 终点坐标 [lng, lat]
+     * @returns {Promise<Array>} 路径点数组
+     */
     searchDrivingPathOnce(start, end) {
       return new Promise((resolve, reject) => {
         this.driving.search(start, end, (status, result) => {
@@ -468,6 +487,12 @@ export default {
       });
     },
 
+    /**
+     * 为Promise添加超时机制
+     * @param {Promise} promise - 要执行的Promise
+     * @param {number} timeoutMs - 超时毫秒数
+     * @returns {Promise} 带超时的Promise
+     */
     withTimeout(promise, timeoutMs) {
       return Promise.race([
         promise,
@@ -475,6 +500,16 @@ export default {
       ]);
     },
 
+    /**
+     * 从路径构建路线覆盖物
+     * @param {Object} params - 参数对象
+     * @param {Object} params.config - 配置对象
+     * @param {string} params.configName - 配置名称
+     * @param {Object} params.line - 路线配置
+     * @param {number} params.lineIdx - 路线索引
+     * @param {Array} params.path - 路径点数组
+     * @returns {Array} 覆盖物数组
+     */
     buildRouteOverlaysFromPath({ config, configName, line, lineIdx, path }) {
       if (!this.AMap || !this.map) return [];
 
@@ -483,8 +518,6 @@ export default {
         basePath = this.buildFallbackLinePath(line.start, line.end, 30);
       }
 
-      // --- 核心优化:确保线路准确贴合真实道路 ---
-      // 取消原本的微小物理解散偏移,以保证所有线路(包含但不限单条调整后)严格绘制在导航真实道路上
       const offsetVal = 0;
       const applyOffset = (p) => {
         const lng = p.lng || (p.getLng ? p.getLng() : (Array.isArray(p) ? Number(p[0]) : 0));
@@ -493,7 +526,6 @@ export default {
       };
 
       const allSegments = this.extractMainStraightSegments(basePath);
-      // 干线协调每条路线只取最长的1段,保证6条配置 = 6条线
       const segments = configName === '干线协调'
         ? allSegments.slice(0, 1)
         : allSegments;
@@ -502,10 +534,8 @@ export default {
       segments.forEach((rawSegmentPath, segmentIdx) => {
         if (!Array.isArray(rawSegmentPath) || rawSegmentPath.length < 2) return;
 
-        // 应用偏移到当前段的所有点
         const segmentPath = rawSegmentPath.map(p => applyOffset(p));
 
-        // --- 勤务路线:根据 line.dutyState 决定渲染样式 ---
         if (configName === '勤务路线' && line.dutyState) {
           const state = line.dutyState; // 'pending' | 'active' | 'done'
           const progress = (state === 'active') ? Math.min(Math.max(Number(line.progress) || 0.5, 0), 1) : (state === 'done' ? 1 : 0);
@@ -711,6 +741,12 @@ export default {
       return overlays;
     },
 
+    /**
+     * 均匀选取点的索引
+     * @param {number} totalPoints - 总点数
+     * @param {number} count - 要选取的点数
+     * @returns {Array<number>} 索引数组
+     */
     pickEvenlySpacedIndices(totalPoints, count) {
       const total = Math.max(Number(totalPoints) || 0, 0);
       const target = Math.max(Number(count) || 0, 0);
@@ -721,6 +757,13 @@ export default {
       return Array.from({ length: target }, (_, k) => Math.round((k * (total - 1)) / (target - 1)));
     },
 
+    /**
+     * 构建备选线路路径
+     * @param {Array<number>} start - 起点坐标 [lng, lat]
+     * @param {Array<number>} end - 终点坐标 [lng, lat]
+     * @param {number} pointCount - 点数量
+     * @returns {Array} 路径点数组
+     */
     buildFallbackLinePath(start, end, pointCount) {
       const sLng = Number(start && start[0]);
       const sLat = Number(start && start[1]);
@@ -740,6 +783,11 @@ export default {
       return path;
     },
 
+    /**
+     * 提取主要直线段
+     * @param {Array} path - 路径点数组
+     * @returns {Array} 直线段数组
+     */
     extractMainStraightSegments(path) {
       const getCoord = (p) => {
         if (!p) return { lng: NaN, lat: NaN };
@@ -802,7 +850,11 @@ export default {
       return finalSegments.length > 0 ? finalSegments : [points];
     },
 
-    // 内部通用工具:从不同格式的点中提取经纬度数组 [lng, lat]
+    /**
+     * 从不同格式的点中提取经纬度数组 [lng, lat]
+     * @param {Object|Array} p - 点对象或数组
+     * @returns {Array<number>} 经纬度数组
+     */
     _getCoords(p) {
       if (!p) return [0, 0];
       const lng = p.lng || (p.getLng ? p.getLng() : (Array.isArray(p) ? Number(p[0]) : 0));
@@ -810,6 +862,12 @@ export default {
       return [lng, lat];
     },
 
+    /**
+     * 计算两点之间的方位角(度)
+     * @param {Object|Array} a - 起点
+     * @param {Object|Array} b - 终点
+     * @returns {number} 方位角
+     */
     calcBearingDeg(a, b) {
       const [alng, alat] = this._getCoords(a);
       const [blng, blat] = this._getCoords(b);
@@ -822,12 +880,24 @@ export default {
       return (90 - mathAngle + 360) % 360;
     },
 
+    /**
+     * 计算两个角度的差值
+     * @param {number} a - 角度1
+     * @param {number} b - 角度2
+     * @returns {number} 角度差
+     */
     calcAngleDiffDeg(a, b) {
       let diff = Math.abs(a - b);
       if (diff > 180) diff = 360 - diff;
       return diff;
     },
 
+    /**
+     * 计算两点之间的近似距离
+     * @param {Object|Array} a - 点1
+     * @param {Object|Array} b - 点2
+     * @returns {number} 距离
+     */
     calcApproxDistance(a, b) {
       const [alng, alat] = this._getCoords(a);
       const [blng, blat] = this._getCoords(b);
@@ -838,6 +908,46 @@ export default {
       return Math.sqrt(dx * dx + dy * dy);
     },
 
+    /**
+     * 根据当前缩放级别计算普通圆点的尺寸(px)
+     * @returns {number} 圆点尺寸
+     */
+    getDotSizeByZoom() {
+      if (!this.map) return 14;
+      const zoom = this.map.getZoom();
+      const base = 14;
+      const extra = Math.max(0, zoom - 15) * 3;
+      return Math.min(base + extra, 28);
+    },
+
+    /**
+     * 遍历所有已注册的普通圆点 marker,更新其 content 和 offset
+     */
+    updateDotMarkerSizes() {
+      if (!this.isMapReady()) return;
+      const size = this.getDotSizeByZoom();
+      const half = Math.round(size / 2) + 2;
+      this.dotMarkers.forEach(({ marker, config, isRoute }) => {
+        if (!marker || typeof marker.setContent !== 'function') return;
+        const displayText = config.name ? config.name.charAt(0) : '';
+        const border = isRoute ? 'none' : '1.5px solid rgba(255,255,255,0.7)';
+        const boxShadow = isRoute ? 'none' : `0 0 8px ${config.color}`;
+        marker.setContent(`
+          <div class="pure-light-node ${isRoute ? 'route-node' : ''}" style="width:${size}px;height:${size}px;background:${config.color || '#999'};box-shadow:${boxShadow};border:${border};box-sizing:content-box;display:flex;justify-content:center;align-items:center;color:#fff;border-radius:50%;cursor:pointer;padding:2px;">
+            <span style="transform:scale(0.8);font-weight:bold;font-size:${Math.max(10, size - 2)}px;">${displayText}</span>
+          </div>
+        `);
+        marker.setOffset(new this.AMap.Pixel(-half, -half));
+      });
+    },
+
+    /**
+     * 创建交通信号灯标记
+     * @param {Array<number>|Object} position - 位置坐标
+     * @param {Object} config - 配置对象
+     * @param {string} [type='normal'] - 标记类型
+     * @returns {Object|null} 标记对象
+     */
     createTrafficLightMarker(position, config, type = 'normal') {
       if (!position || !config) return null;
 
@@ -846,7 +956,6 @@ export default {
         const lat = position.getLat ? position.getLat() : Number(position[1] !== undefined ? position[1] : position.lat);
         if (isNaN(lng) || isNaN(lat)) return null;
 
-        // 状态文字:起、终、或者状态配置的首字母
         let displayText = config.name ? config.name.charAt(0) : '';
         if (type === 'start') displayText = '起';
         if (type === 'end') displayText = '终';
@@ -856,7 +965,6 @@ export default {
         const isStartEnd = type === 'start' || type === 'end';
         const isPassed = type === 'passed';
 
-        // 核心配置映射:减少嵌套逻辑
         const markerStyle = isStartEnd ? {
           size: '24px',
           height: '30px',
@@ -870,14 +978,13 @@ export default {
           zIndex: 110,
           border: 'none'
         } : {
-          size: '14px',
-          height: '14px',
-          offset: [-9, -9], // 14px + 2px padding * 2 = 18px total
+          size: '18px',
+          height: '18px',
+          offset: [-11, -11],
           zIndex: 100,
           border: isRoute ? 'none' : '1.5px solid rgba(255,255,255,0.7)'
         });
 
-        // 生成标记内容
         let markerContent = '';
         if (isStartEnd) {
           markerContent = `
@@ -901,7 +1008,7 @@ export default {
         } else {
           markerContent = `
             <div class="pure-light-node ${isRoute ? 'route-node' : ''}" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: ${config.color || '#999'}; box-shadow: ${isRoute ? 'none' : `0 0 8px ${config.color}`}; border: ${markerStyle.border}; box-sizing: content-box; display: flex; justify-content: center; align-items: center; color: #fff; border-radius: 50%; cursor: pointer; padding: 2px;">
-              <span style="transform: scale(0.8); font-weight: bold; font-size: 12px;">${displayText}</span>
+              <span style="transform: scale(0.8); font-weight: bold; font-size: 14px;">${displayText}</span>
             </div>
           `;
         }
@@ -922,6 +1029,11 @@ export default {
           }
         });
 
+        if (!isStartEnd && !isAbnormal && !isPassed) {
+          const isRoute = ["干线协调", "勤务路线"].includes(config.name);
+          this.dotMarkers.push({ marker, config, isRoute });
+        }
+
         marker.on('click', (e) => {
           if (this.isComponentDestroyed) return;
           const extData = e.target.getExtData();
@@ -929,7 +1041,6 @@ export default {
             this.cancelCloseInfoWindow();
             this.openLightInfo(extData, e.lnglat);
           }
-          // 获取像素坐标
           const pixel = this.map.lngLatToContainer(e.lnglat);
           this.$emit('map-crossing-click', extData, e.lnglat, pixel);
         });
@@ -940,9 +1051,7 @@ export default {
             this.cancelCloseInfoWindow();
             this.openLightInfo(e.target.getExtData(), e.lnglat);
           }
-          // 获取像素坐标
           const pixel = this.map.lngLatToContainer(e.lnglat);
-          // 传递路口鼠标滑入事件
           this.$emit('map-crossing-mouseover', e.target.getExtData(), e.lnglat, pixel);
         });
 
@@ -951,7 +1060,6 @@ export default {
           if (this.$route && this.$route.path === '/home') {
             this.scheduleCloseInfoWindow();
           }
-          // 传递路口鼠标滑出事件
           this.$emit('map-crossing-mouseout', e.target.getExtData());
         });
 
@@ -962,6 +1070,11 @@ export default {
       }
     },
 
+    /**
+     * 打开信号灯信息窗口
+     * @param {Object} data - 信号灯数据
+     * @param {Array<number>} position - 位置坐标
+     */
     openLightInfo(data, position) {
       if (!this.isMapReady()) return;
 
@@ -1010,7 +1123,7 @@ export default {
         this.infoWindow = new this.AMap.InfoWindow({
           isCustom: true,
           offset: new this.AMap.Pixel(0, -20),
-          autoMove: false // 防止弹窗自动平移导致的中心点偏移
+          autoMove: false
         });
       }
 
@@ -1042,6 +1155,9 @@ export default {
       }, 100);
     },
 
+    /**
+     * 取消关闭信息窗口
+     */
     cancelCloseInfoWindow() {
       if (this.infoCloseTimer) {
         clearTimeout(this.infoCloseTimer);
@@ -1049,6 +1165,9 @@ export default {
       }
     },
 
+    /**
+     * 安排关闭信息窗口
+     */
     scheduleCloseInfoWindow() {
       this.cancelCloseInfoWindow();
       this.infoCloseTimer = setTimeout(() => {
@@ -1059,6 +1178,11 @@ export default {
       }, 160);
     },
 
+    /**
+     * 获取报警信息文本
+     * @param {string} statusName - 状态名称
+     * @returns {string} 报警信息
+     */
     getAlarmInfoText(statusName) {
       if (statusName === '离线') return '通讯中断设备离线';
       if (statusName === '降级') return '降级定周期控制';
@@ -1066,6 +1190,11 @@ export default {
       return '设备异常';
     },
 
+    /**
+     * 格式化事件时间
+     * @param {Date|number|string} input - 时间输入
+     * @returns {string} 格式化后的时间
+     */
     formatEventTime(input) {
       let d = null;
       if (input instanceof Date) d = input;
@@ -1083,6 +1212,9 @@ export default {
       return `${y}.${m}.${day} ${hh}:${mm}`;
     },
 
+    /**
+     * 切换所有图例的可见性
+     */
     toggleAll() {
       const targetState = !this.isAllSelected;
       if (!this.isMapReady()) return;
@@ -1101,6 +1233,10 @@ export default {
       }
     },
 
+    /**
+     * 切换指定路线的可见性
+     * @param {string} name - 路线名称
+     */
     toggleRouteVisible(name) {
       if (!this.isMapReady()) return;
 
@@ -1116,10 +1252,13 @@ export default {
       }
     },
 
+    /**
+     * 根据位置聚焦地图
+     * @param {string|Array<number>} targetPos - 目标位置
+     */
     focusByLocation(targetPos) {
       if (!this.isMapReady() || !targetPos) return;
 
-      // 如果是字符串坐标 "lng,lat",则解析
       let pos = targetPos;
       if (typeof targetPos === 'string') {
         pos = targetPos.split(',').map(Number);
@@ -1130,7 +1269,6 @@ export default {
       let bestMarker = null;
       let minDistanceSq = Infinity;
 
-      // 遍历所有路由组,寻找离坐标点最近的标记
       Object.values(this.routeGroups).forEach(group => {
         if (!Array.isArray(group)) return;
         group.forEach(item => {
@@ -1143,9 +1281,7 @@ export default {
           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;
 
@@ -1164,12 +1300,15 @@ export default {
           if (!this.isComponentDestroyed) this.openLightInfo(bestMarker.getExtData(), finalPos);
         }, 600);
       } else {
-        // 如果找不到对应的标记,直接使用传入的坐标设置地图中心
         this.map.setZoomAndCenter(17, [targetLng, targetLat], false, 500);
       }
     },
 
-    // 通过路口编号定位到对应的 marker,弹出信息窗
+    /**
+     * 通过路口编号定位到对应的标记
+     * @param {string} id - 路口编号
+     * @returns {Array<number>|null} 位置坐标
+     */
     focusById(id) {
       if (!this.isMapReady() || !id) return null;
 
@@ -1194,11 +1333,16 @@ export default {
       return null;
     },
 
+    /**
+     * 切换图例的可见性
+     */
     toggleLegend() {
       this.legendVisible = !this.legendVisible;
     },
 
-    // 按4:4:4比例提取故障、离线、降级路口信息并存储到localStorage
+    /**
+     * 按4:4:4比例提取故障、离线、降级路口信息并存储到localStorage
+     */
     storeStatusCoordsToLocalStorage() {
       const alarmTypes = [
         { status: "故障", titles: ["通讯中断", "灯组故障", "相位冲突", "绿冲突"], level: "high", type: "error" },
@@ -1224,7 +1368,6 @@ export default {
             description: `${name}-${titles[i] || titles[0]}`,
             position: [lng, lat],
           });
-          // 保持原有的 pos1-pos12 存储,兼容其他可能的引用
           localStorage.setItem(`pos${id}`, `${lng},${lat}`);
           id++;
         });
@@ -1233,7 +1376,13 @@ export default {
       localStorage.setItem("alarmListFromMap", JSON.stringify(alarmList));
       console.log('状态坐标及告警数据已存储到localStorage');
     },
-    // 公开方法:将经纬度转换为像素坐标
+
+    /**
+     * 将经纬度转换为像素坐标
+     * @param {number} lng - 经度
+     * @param {number} lat - 纬度
+     * @returns {Object|null} 像素坐标
+     */
     lngLatToPixel(lng, lat) {
         if (!this.map) return null;
         return this.map.lngLatToContainer([lng, lat]);
@@ -1614,30 +1763,10 @@ export default {
   z-index: 1;
 }
 
-/* 异常状态增加稍微剧烈一点的呼吸感,但缩小范围 */
-@keyframes light-breathe {
-  0% {
-    transform: scale(0.9);
-    opacity: 0.8;
-  }
-
-  50% {
-    transform: scale(1.1);
-    opacity: 1;
-  }
-
-  100% {
-    transform: scale(0.9);
-    opacity: 0.8;
-  }
-}
-
-/* 首页展示时,如果觉得还是太密,可以给非异常节点降权 */
 ::v-deep .pure-light-node:not(.abnormal-node) {
   border: 1px solid rgba(255, 255, 255, 0.4);
 }
 
-/* 勤务路线执行进度点脉冲动画 */
 ::v-deep .duty-progress-node {
   animation: duty-pulse 1.6s infinite ease-in-out;
 }

+ 5 - 1
src/views/TransitionPage.vue

@@ -10,6 +10,7 @@
         :satellite-image="satelliteImage"
         :boundary-url="boundaryUrl"
         :micro-view-range="microViewRange"
+        :micro-image="microImage"
         @complete="onTransitionComplete"
       />
     </transition>
@@ -41,7 +42,10 @@ export default {
       // 路网数据和卫星底图(因为不同城市的数据不同,如果是动态全国切换,建议默认清空)
       // 如果你需要保留北京的默认路网,可以在 if(targetCity === '北京市') 里面再单独赋回来
       roads: [],
-      satelliteImage: null
+      satelliteImage: null,
+
+      // 微观覆盖图片(过渡动画最后淡入展示)
+      microImage: './beijing-gov-satellite.jpg'
     };
   },
   created() {