Kaynağa Gözat

fix: 修复地图红绿灯点详情弹窗样式内容错误问题

sequoia tungfang 1 ay önce
ebeveyn
işleme
90bc088df9
1 değiştirilmiş dosya ile 174 ekleme ve 70 silme
  1. 174 70
      src/components/TongzhouTrafficMap.vue

+ 174 - 70
src/components/TongzhouTrafficMap.vue

@@ -16,13 +16,8 @@
           <div class="legend-label" style="font-weight: bold;">全选</div>
         </div>
 
-        <div
-          v-for="item in legendConfig"
-          class="legend-item"
-          @click="toggleRouteVisible(item.name)"
-          :key="item.name"
-          :class="{ 'is-inactive': !activeLegends.includes(item.name) }"
-        >
+        <div v-for="item in legendConfig" class="legend-item" @click="toggleRouteVisible(item.name)" :key="item.name"
+          :class="{ 'is-inactive': !activeLegends.includes(item.name) }">
           <div class="legend-dot" :style="{ backgroundColor: item.color }">
             <span>{{ item.name.charAt(0) }}</span>
           </div>
@@ -206,30 +201,58 @@ export default {
     },
 
     // 创建交通灯点
+    // createTrafficLightMarker(position, config) {
+    //   console.log(config.name);
+    //   // Mock 红绿灯实时数据
+    //   const states = [
+    //     { color: '#ff4d4f', label: '红灯锁定' },
+    //     { color: '#3ee68d', label: '绿灯通行' },
+    //     { color: '#D9C13B', label: '降级黄闪' }
+    //   ];
+    //   const currentState = states[Math.floor(Math.random() * states.length)];
+    //   const countdown = Math.floor(Math.random() * 50) + 10;
+
+    //   const marker = new this.AMap.Marker({
+    //     position: position,
+    //     // 去掉了数字显示,仅保留呼吸灯效果
+    //     content: `<div class="pure-light-node ${['离线', '降级', '故障'].includes(config.name) ? 'breathe' : ''}" style="background: ${config.color}; box-shadow: 0 0 15px ${config.color}; font-size: 12px; display: flex; justify-content: center; align-items: center; color: #fff; padding: 8px;"><span>${config?.name?.charAt(0)}</span></div>`,
+    //     offset: new this.AMap.Pixel(-10, -10),
+    //     extData: {
+    //       ...config,
+    //       statusColor: currentState.color,
+    //       statusLabel: currentState.label,
+    //       road: '北京路与南京路',
+    //       time: '2026.1.23.12:00'
+    //     }
+    //   });
+
+    //   marker.on('click', (e) => this.openLightInfo(e.target.getExtData(), e.lnglat));
+    //   return marker;
+    // },
+
     createTrafficLightMarker(position, config) {
-      console.log(config.name);
-      // Mock 红绿灯实时数据
-      const states = [
-        { color: '#ff4d4f', label: '红灯锁定', code: 'RED' },
-        { color: '#3ee68d', label: '绿灯通行', code: 'GREEN' },
-        { color: '#ffcc33', label: '黄灯警示', code: 'YELLOW' }
-      ];
-      const currentState = states[Math.floor(Math.random() * states.length)];
-      const countdown = Math.floor(Math.random() * 50) + 10;
+      // 根据业务需求:离线、降级、故障需要闪烁,其他保持静止
+      const needsFlash = ["离线", "降级", "故障"].includes(config.name);
+
+      // 统一颜色:直接使用图例配置的 config.color
+      // 弹窗状态文本:如果不是异常状态,统一显示为“正常运行”或根据业务自定义
+      const displayStatus = needsFlash ? config.name : "正常运行";
 
       const marker = new this.AMap.Marker({
         position: position,
-        // 去掉了数字显示,仅保留呼吸灯效果
-        content: `<div class="pure-light-node ${['离线', '降级', '故障'].includes(config.name) ? 'breathe' : ''}" style="background: ${config.color}; box-shadow: 0 0 15px ${config.color}; font-size: 12px; display: flex; justify-content: center; align-items: center; color: #fff; padding: 8px;"><span>${config?.name?.charAt(0)}</span></div>`,
+        content: `
+          <div class="pure-light-node ${needsFlash ? 'breathe' : ''}" 
+              style="background: ${config.color}; box-shadow: 0 0 15px ${config.color}; font-size: 12px; display: flex; justify-content: center; align-items: center; color: #fff; padding: 8px;">
+            <span>${config.name.charAt(0)}</span>
+          </div>
+        `,
         offset: new this.AMap.Pixel(-10, -10),
         extData: {
           ...config,
-          lightDetail: {
-            status: currentState.label,
-            color: currentState.color,
-            timeLeft: countdown,
-            sn: 'TL-' + Math.random().toString(36).substr(2, 7).toUpperCase()
-          }
+          statusColor: config.color, // 统一弹窗小圆点颜色
+          statusLabel: displayStatus, // 统一弹窗状态文字
+          road: '北京路与南京路',
+          time: '2026.1.23.12:00'
         }
       });
 
@@ -237,29 +260,68 @@ export default {
       return marker;
     },
 
+    // openLightInfo(data, position) {
+    //   const content = `
+    //     <div class="traffic-window">
+    //       <div class="window-header" style="background: ${data.lightDetail.color}">
+    //         <span class="title">路口信号机: ${data.name}</span>
+    //       </div>
+    //       <div class="window-body">
+    //         <div class="data-row"><span class="label">设备序列:</span><span>${data.lightDetail.sn}</span></div>
+    //         <div class="data-row">
+    //           <span class="label">当前相位:</span>
+    //           <span style="color: ${data.lightDetail.color}; font-weight:bold">${data.lightDetail.status}</span>
+    //         </div>
+    //         <div class="data-row"><span class="label">相位余时:</span><span class="highlight">${data.lightDetail.timeLeft}s</span></div>
+    //         <div class="data-row"><span class="label">运行模式:</span><span>智能感应</span></div>
+    //         <div class="progress-container">
+    //            <div class="progress-bar" style="width: ${(data.lightDetail.timeLeft / 60) * 100}%; background: ${data.lightDetail.color}"></div>
+    //         </div>
+    //       </div>
+    //     </div>
+    //   `;
+
+    //   if (!this.infoWindow) {
+    //     this.infoWindow = new this.AMap.InfoWindow({ isCustom: true, offset: new this.AMap.Pixel(0, -20) });
+    //   }
+    //   this.infoWindow.setContent(content);
+    //   this.infoWindow.open(this.map, position);
+    // },
     openLightInfo(data, position) {
+      // 在 openLightInfo 内部
       const content = `
-        <div class="traffic-window">
-          <div class="window-header" style="background: ${data.lightDetail.color}">
-            <span class="title">路口信号机: ${data.name}</span>
+        <div class="custom-info-card">
+          <div class="close-btn" onclick="window.closeMapInfoWindow()">✕</div>
+          
+          <div class="card-header">
+            <div class="status-dot" style="background: ${data.statusColor}">
+              <span>${data.name.charAt(0)}</span>
+            </div>
+            <span class="status-text">${data.statusLabel}</span>
           </div>
-          <div class="window-body">
-            <div class="data-row"><span class="label">设备序列:</span><span>${data.lightDetail.sn}</span></div>
-            <div class="data-row">
-              <span class="label">当前相位:</span>
-              <span style="color: ${data.lightDetail.color}; font-weight:bold">${data.lightDetail.status}</span>
+          <div class="card-body">
+            <div class="info-line">
+              <span class="label">路口:</span>
+              <span class="value">${data.road}</span>
             </div>
-            <div class="data-row"><span class="label">相位余时:</span><span class="highlight">${data.lightDetail.timeLeft}s</span></div>
-            <div class="data-row"><span class="label">运行模式:</span><span>智能感应</span></div>
-            <div class="progress-container">
-               <div class="progress-bar" style="width: ${(data.lightDetail.timeLeft / 60) * 100}%; background: ${data.lightDetail.color}"></div>
+            <div class="info-line">
+              <span class="label">发生时间:</span>
+              <span class="value digital">${data.time}</span>
             </div>
           </div>
         </div>
       `;
 
+      // 定义全局关闭方法(因为 isCustom:true 下 Vue 事件会失效)
+      window.closeMapInfoWindow = () => {
+        if (this.infoWindow) this.infoWindow.close();
+      };
+
       if (!this.infoWindow) {
-        this.infoWindow = new this.AMap.InfoWindow({ isCustom: true, offset: new this.AMap.Pixel(0, -20) });
+        this.infoWindow = new this.AMap.InfoWindow({
+          isCustom: true,
+          offset: new this.AMap.Pixel(0, -20)
+        });
       }
       this.infoWindow.setContent(content);
       this.infoWindow.open(this.map, position);
@@ -351,55 +413,97 @@ export default {
   }
 }
 
-/* --- 仿真弹窗样式 --- */
-::v-deep .traffic-window {
-  width: 240px;
-  background: rgba(7, 21, 43, 0.95);
-  border: 1px solid #32c5ff;
-  border-radius: 4px;
+/* 关闭按钮样式 */
+::v-deep .close-btn {
+  position: absolute;
+  top: 10px;
+  right: 12px;
+  color: #8da6c7;
+  cursor: pointer;
+  font-size: 16px;
+  transition: color 0.3s;
+  line-height: 1;
+  z-index: 10;
+}
+
+::v-deep .close-btn:hover {
+  color: #ffffff;
+}
+
+/* 确保容器相对定位,以便按钮定位 */
+::v-deep .custom-info-card {
+  position: relative;
+  background: rgba(10, 15, 24, 0.95);
+  border-radius: 10px;
+  padding: 12px 16px;
+  min-width: 200px;
+  border: 1px solid rgba(255, 255, 255, 0.1);
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
+}
+
+/* --- 彻底还原图片的自定义弹窗 --- */
+::v-deep .custom-info-card {
+  background: rgba(10, 15, 24, 0.95);
+  /* 极深色背景 */
+  border-radius: 10px;
+  /* 较大的圆角 */
+  padding: 12px 16px;
+  min-width: 200px;
+  border: 1px solid rgba(255, 255, 255, 0.1);
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
   color: #fff;
-  overflow: hidden;
-  box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
 }
 
-.window-header {
-  padding: 8px 12px;
-  font-size: 13px;
+::v-deep .card-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+::v-deep .status-dot {
+  width: 18px;
+  height: 18px;
+  border-radius: 50%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-right: 8px;
+  font-size: 11px;
+  color: #000;
+  /* 图标内文字为黑色 */
   font-weight: bold;
-  clip-path: polygon(0 0, 100% 0, 92% 100%, 0% 100%);
 }
 
-.window-body {
-  padding: 15px;
-  font-size: 12px;
+::v-deep .status-dot span {
+  transform: scale(0.75);
 }
 
-.data-row {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 8px;
+::v-deep .status-text {
+  font-size: 15px;
+  font-weight: bold;
 }
 
-.label {
-  color: #8da6c7;
+::v-deep .info-line {
+  display: flex;
+  margin-bottom: 6px;
+  font-size: 13px;
+  align-items: center;
 }
 
-.highlight {
-  color: #32c5ff;
-  font-family: 'Digital-7', sans-serif;
-  font-size: 14px;
+::v-deep .label {
+  color: #8da6c7;
+  /* 标签灰色 */
+  white-space: nowrap;
 }
 
-.progress-container {
-  height: 3px;
-  background: #1a2b45;
-  margin-top: 10px;
-  border-radius: 2px;
+::v-deep .value {
+  color: #ffffff;
+  /* 内容白色 */
 }
 
-.progress-bar {
-  height: 100%;
-  transition: width 0.3s;
+::v-deep .digital {
+  font-family: 'Consolas', monospace;
+  /* 模拟数字字体 */
 }
 
 .map-legend {