From c08d0ebace5e9f20eb442ad7cb1db05d61ecbd0d Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期一, 21 四月 2025 14:56:28 +0800
Subject: [PATCH] 修改bug

---
 src/components/MapPanel.vue |  542 ++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 386 insertions(+), 156 deletions(-)

diff --git a/src/components/MapPanel.vue b/src/components/MapPanel.vue
index ba1e8dd..9e147e9 100644
--- a/src/components/MapPanel.vue
+++ b/src/components/MapPanel.vue
@@ -1,11 +1,13 @@
 <template>
   <div class="center-panel">
-    <div class="map-container" ref="mapContainer"></div>
+    <div id="map-container" class="map-container"></div>
   </div>
 </template>
 
 <script>
 import AMapLoader from '@amap/amap-jsapi-loader'
+import { mapState } from 'vuex'
+import { getHouseMapDistribution } from './service'
 
 export default {
   name: 'MapPanel',
@@ -13,183 +15,291 @@
     return {
       map: null,
       markers: [],
-      infoWindow: null
+      currentMakers: [],
+      infoWindow: null,
+      updateTimer: null,
+      markerObjects: {} // Store marker objects by ID
     }
+  },
+  watch: {
+    mapMarkerStatus(newVal) {
+      if (newVal === 'all') {
+        this.markers = this.currentMakers
+        this.$nextTick(() => {
+          this.map.clearMap()
+          this.addMapMarkers()
+        })
+      } else {
+        let arr = this.currentMakers.filter(item => item.info.statusText == newVal)
+        this.markers = arr
+        this.$nextTick(() => {
+          this.map.clearMap()
+          this.addMapMarkers()
+        })
+      }
+
+    }
+  },
+  computed: {
+    ...mapState([
+      'mapMarkerStatus'
+    ]),
   },
   mounted() {
-    this.$nextTick(() => {
-      this.initMap()
+    this.fetchMapData()
+    this.startUpdateTimer()
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', () => {
+      this.map && this.map.resize()
     })
+    if (this.updateTimer) {
+      clearInterval(this.updateTimer)
+    }
   },
   methods: {
-    async initMap() {
-      try {
-        const AMap = await AMapLoader.load({
-          key: '526e04b30ceba8f217c5def5a92392f9',
-          version: '2.0',
-          plugins: ['AMap.ToolBar', 'AMap.Scale']
-        })
-
-        this.map = new AMap.Map(this.$refs.mapContainer, {
-          zoom: 16,
-          center: [91.1172, 29.6487],
-          viewMode: '3D',
-          pitch: 35,
-          mapStyle: 'amap://styles/normal',
-          features: ['bg', 'road', 'building', 'point'],
-          buildingAnimation: true
-        })
-
-        this.map.addControl(new AMap.ToolBar({
-          position: 'RB'
-        }))
-        this.map.addControl(new AMap.Scale({
-          position: 'RB'
-        }))
-
-        this.infoWindow = new AMap.InfoWindow({
-          offset: new AMap.Pixel(0, -30),
-          closeWhenClickMap: true,
-          autoMove: true,
-          anchor: 'bottom-center'
-        })
-
-        this.map.on('click', () => {
-          if (this.infoWindow) {
-            this.infoWindow.close()
-          }
-        })
-
-        this.addMarkers()
-      } catch (error) {
-        console.error('地图加载失败:', error)
-      }
+    startUpdateTimer() {
+      this.updateTimer = setInterval(() => {
+        this.fetchMapData()
+      }, 3000)
     },
-    addMarkers() {
-      const houses = [
-        {
-          position: [91.1172, 29.6487],
-          title: '新城大道房源',
-          status: 'waiting',
-          content: `
-            <div class="info-window" data-status="waiting">
-              <div class="info-title">房屋状态:待租出</div>
-              <div class="info-content">
-                <p>房屋编号:新城大道</p>
-                <p>房屋状态:待租出</p>
-                <p>租赁面积:1000/2000㎡</p>
-                <p>本季租金:500/2000元/月</p>
-              </div>
-            </div>
-          `
-        },
-        {
-          position: [91.1272, 29.6587],
-          title: '拉萨路房源',
-          status: 'rented',
-          content: `
-            <div class="info-window" data-status="rented">
-              <div class="info-title">房屋状态:已租出</div>
-              <div class="info-content">
-                <p>房屋编号:拉萨路</p>
-                <p>房屋状态:已租出</p>
-                <p>租赁面积:800/1500㎡</p>
-                <p>本季租金:400/1800元/月</p>
-              </div>
-            </div>
-          `
-        }
-      ]
-
-      houses.forEach(house => {
-        const circles = []
-        const baseRadius = 15
-        const numCircles = 3
-
-        const centerCircle = new AMap.Circle({
-          center: house.position,
-          radius: baseRadius / 2,
-          fillColor: house.status === 'waiting' ? '#ff9800' : '#4CAF50',
-          fillOpacity: 0.8,
-          strokeWeight: 0,
-          zIndex: numCircles + 2,
-          bubble: true,
-          cursor: 'pointer'
-        })
-
-        for (let i = 0; i < numCircles; i++) {
-          circles.push(new AMap.Circle({
-            center: house.position,
-            radius: baseRadius * (i + 1),
-            fillColor: house.status === 'waiting' ? '#ff9800' : '#4CAF50',
-            fillOpacity: 0.3 - (i * 0.05),
-            strokeColor: house.status === 'waiting' ? '#ff9800' : '#4CAF50',
-            strokeWeight: 2,
-            strokeOpacity: 0.5 - (i * 0.08),
-            zIndex: numCircles - i,
-            bubble: true,
-            cursor: 'pointer'
-          }))
-        }
-
-        const clickHandler = () => {
-          if (this.infoWindow) {
-            this.infoWindow.setContent(house.content)
-            this.infoWindow.open(this.map, house.position)
-          }
-        }
-
-        centerCircle.on('click', clickHandler)
-        circles.forEach(circle => circle.on('click', clickHandler))
-
-        this.map.add([centerCircle, ...circles])
-        this.markers.push({ center: centerCircle, rings: circles })
-
-        let phase = 0
-        const animate = () => {
-          phase += 0.15
-          const scale = 1 + 0.6 * Math.sin(phase)
-
-          circles.forEach((circle, index) => {
-            const currentRadius = baseRadius * (index + 1)
-            circle.setRadius(currentRadius * scale)
-
-            const baseOpacity = 0.3 - (index * 0.05)
-            const opacityScale = 1 + 0.5 * Math.sin(phase)
-            circle.setOptions({
-              fillOpacity: baseOpacity * opacityScale,
-              strokeOpacity: (0.5 - (index * 0.08)) * opacityScale
-            })
+    fetchMapData() {
+      getHouseMapDistribution().then(res => {
+        if (res.code === 200) {
+          const newData = res.data.map(item => {
+            item.position = [item.longitude, item.latitude]
+            item.info = {
+              address: item.houseAddress,
+              name: item.houseName,
+              status: item.houseStatus,
+              rentStatus: item.rentStatus,
+              statusText: item.houseStatus == 1 ? '待出租' : item.houseStatus == 2 ? '已出租' : item.houseStatus == 3 ? '维修中' : '欠费',
+              tenant: item.tenant,
+              rent: item.rent,
+              color: item.houseStatus == 1 ? '#618CE9' : item.houseStatus == 2 ? '#FDAE03' : item.houseStatus == 3 ? '#F64E4F' : '#0FBE6B'
+            }
+            return item
           })
 
-          requestAnimationFrame(animate)
+          if (!this.map) {
+            this.currentMakers = JSON.parse(JSON.stringify(newData))
+            this.markers = newData
+            this.$nextTick(() => {
+              this.initMap()
+              window.addEventListener('resize', () => {
+                this.map && this.map.resize()
+              })
+            })
+          } else {
+            this.updateMarkers(newData)
+          }
         }
-        animate()
       })
-    }
+    },
+    updateMarkers(newData) {
+      // Update existing markers and add new ones
+      newData.forEach(newMarker => {
+        const markerId = `${newMarker.longitude}-${newMarker.latitude}`
+        const existingMarker = this.markerObjects[markerId]
+
+        if (existingMarker) {
+          // Update existing marker content
+          const content = this.generateMarkerContent(newMarker)
+          existingMarker.setContent(content)
+        } else {
+          // Create new marker
+          const content = this.generateMarkerContent(newMarker)
+          const markerObj = new AMap.Marker({
+            position: newMarker.position,
+            content: content,
+            anchor: 'center',
+            offset: new AMap.Pixel(0, 0),
+            zIndex: 100
+          })
+
+          markerObj.on('click', () => {
+            this.showInfoWindow(markerObj, newMarker.info)
+          })
+
+          markerObj.setMap(this.map)
+          this.markerObjects[markerId] = markerObj
+        }
+      })
+
+      // Remove markers that no longer exist
+      Object.keys(this.markerObjects).forEach(markerId => {
+        const [longitude, latitude] = markerId.split('-')
+        const markerExists = newData.some(marker =>
+          marker.longitude === parseFloat(longitude) &&
+          marker.latitude === parseFloat(latitude)
+        )
+
+        if (!markerExists) {
+          this.markerObjects[markerId].setMap(null)
+          delete this.markerObjects[markerId]
+        }
+      })
+
+      this.currentMakers = JSON.parse(JSON.stringify(newData))
+      this.markers = newData
+    },
+    generateMarkerContent(marker) {
+      return `
+        <div class="marker-container">
+          <svg width="120" height="120" viewBox="0 0 120 120">
+            <defs>
+              <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
+                <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
+                <feMerge>
+                  <feMergeNode in="coloredBlur"/>
+                  <feMergeNode in="SourceGraphic"/>
+                </feMerge>
+              </filter>
+            </defs>
+            <circle class="ripple" cx="60" cy="60" r="12" fill="none" stroke="${marker.info.color}" stroke-width="3" style="opacity: 0.4">
+              <animate attributeName="r" from="12" to="45" dur="3s" begin="0s" repeatCount="indefinite" />
+              <animate attributeName="opacity" from="0.8" to="0" dur="3s" begin="0s" repeatCount="indefinite" />
+            </circle>
+            <circle class="ripple" cx="60" cy="60" r="12" fill="none" stroke="${marker.info.color}" stroke-width="3" style="opacity: 0.4">
+              <animate attributeName="r" from="12" to="45" dur="3s" begin="1s" repeatCount="indefinite" />
+              <animate attributeName="opacity" from="0.8" to="0" dur="3s" begin="1s" repeatCount="indefinite" />
+            </circle>
+            <circle class="ripple" cx="60" cy="60" r="12" fill="none" stroke="${marker.info.color}" stroke-width="3" style="opacity: 0.4">
+              <animate attributeName="r" from="12" to="45" dur="3s" begin="2s" repeatCount="indefinite" />
+              <animate attributeName="opacity" from="0.8" to="0" dur="3s" begin="2s" repeatCount="indefinite" />
+            </circle>
+            <circle class="marker" cx="60" cy="60" r="8" fill="${marker.info.color}" filter="url(#glow)">
+              <animate attributeName="r" values="8;10;8" dur="2s" repeatCount="indefinite" />
+              <animate attributeName="fill-opacity" values="1;0.8;1" dur="2s" repeatCount="indefinite" />
+            </circle>
+          </svg>
+        </div>
+      `
+    },
+    async initMap() {
+      const map = await AMapLoader.load({
+        key: '526e04b30ceba8f217c5def5a92392f9',
+        version: '2.0',
+        plugins: ['AMap.MarkerClusterer']
+      })
+
+      this.map = new map.Map('map-container', {
+        zoom: 14,
+        center: [91.172119, 29.652941],
+        mapStyle: 'amap://styles/normal'
+      })
+
+      this.map.on('click', () => {
+        this.markers = this.currentMakers
+        this.$nextTick(() => {
+          this.map.clearMap()
+          this.addMapMarkers()
+        })
+      })
+
+      this.addMapMarkers()
+    },
+    addMapMarkers() {
+      this.markers.forEach(marker => {
+        const content = `
+          <div class="marker-container">
+            <svg width="120" height="120" viewBox="0 0 120 120">
+              <defs>
+                <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
+                  <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
+                  <feMerge>
+                    <feMergeNode in="coloredBlur"/>
+                    <feMergeNode in="SourceGraphic"/>
+                  </feMerge>
+                </filter>
+              </defs>
+              <circle class="ripple" cx="60" cy="60" r="12" fill="none" stroke="${marker.info.color}" stroke-width="3" style="opacity: 0.4">
+                <animate attributeName="r" from="12" to="45" dur="3s" begin="0s" repeatCount="indefinite" />
+                <animate attributeName="opacity" from="0.8" to="0" dur="3s" begin="0s" repeatCount="indefinite" />
+              </circle>
+              <circle class="ripple" cx="60" cy="60" r="12" fill="none" stroke="${marker.info.color}" stroke-width="3" style="opacity: 0.4">
+                <animate attributeName="r" from="12" to="45" dur="3s" begin="1s" repeatCount="indefinite" />
+                <animate attributeName="opacity" from="0.8" to="0" dur="3s" begin="1s" repeatCount="indefinite" />
+              </circle>
+              <circle class="ripple" cx="60" cy="60" r="12" fill="none" stroke="${marker.info.color}" stroke-width="3" style="opacity: 0.4">
+                <animate attributeName="r" from="12" to="45" dur="3s" begin="2s" repeatCount="indefinite" />
+                <animate attributeName="opacity" from="0.8" to="0" dur="3s" begin="2s" repeatCount="indefinite" />
+              </circle>
+              <circle class="marker" cx="60" cy="60" r="8" fill="${marker.info.color}" filter="url(#glow)">
+                <animate attributeName="r" values="8;10;8" dur="2s" repeatCount="indefinite" />
+                <animate attributeName="fill-opacity" values="1;0.8;1" dur="2s" repeatCount="indefinite" />
+              </circle>
+            </svg>
+          </div>
+        `
+
+        const markerObj = new AMap.Marker({
+          position: marker.position,
+          content: content,
+          anchor: 'center',
+          offset: new AMap.Pixel(0, 0),
+          zIndex: 100
+        })
+
+        markerObj.on('click', () => {
+          this.showInfoWindow(markerObj, marker.info)
+        })
+
+        markerObj.setMap(this.map)
+      })
+    },
+    showInfoWindow(marker, info) {
+      const content = `
+        <div class="map-info-card" style="min-width:300px !important;color:#fff;background-color: rgba(146, 146, 146, 0.7) !important;padding: 10px;border-radius: 12px;box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);border-radius:0px 35px 35px 0px;">
+          <div class="info-body">
+            <div class="info-item">房屋地址:${info.address}</div>
+            <div class="info-item">房屋名称:${info.name}</div>
+            <div class="info-item">房屋状态:<span class="status-tag" style="background: rgba(${info.color.replace(/[^\d,]/g, '')}, 0.1); color: ${info.color};">${info.statusText}</span></div>
+            <div class="info-item">租户:${info.tenant}</div>
+            <div class="info-item">
+              租金状态:${info.rentStatus}
+            </div>
+            <div class="info-item">
+              本季租金:${info.rent}
+            </div>
+          </div>
+        </div>
+      `
+
+      const infoWindow = new AMap.InfoWindow({
+        content: content,
+        offset: new AMap.Pixel(0, -20),
+        closeWhenClickMap: true,
+        anchor: 'bottom-center',
+        isCustom: true
+      })
+
+      infoWindow.open(this.map, marker.getPosition())
+    },
   }
 }
 </script>
 
-<style scoped>
+<style scoped lang="scss">
 .center-panel {
   flex: 1;
   display: flex;
   flex-direction: column;
-  gap: 10px;
+  position: relative;
+  height: 100%;
+  min-height: 0;
 }
 
 .map-container {
-  border-radius: 8px;
-  overflow: hidden;
-  background: rgba(255, 255, 255, 0.05);
   width: 100%;
-  height: 100vh;
+  height: 100%;
+  flex: 1;
+  position: relative;
 }
 
 .info-window {
   padding: 10px;
-  background: rgba(146, 146, 146, 0.5);
+  background: rgba(146, 146, 146, 0.7);
   border-radius: 8px;
   z-index: 10003;
   min-width: 200px;
@@ -198,7 +308,7 @@
 .info-title {
   font-size: 16px;
   font-weight: bold;
-  color: #fff;
+  color: #fff !important;
   margin-bottom: 12px;
   padding-bottom: 8px;
   border-bottom: 1px solid #eee;
@@ -215,11 +325,19 @@
 }
 
 .info-window[data-status="waiting"] .info-title {
-  color: #fff;
+  color: #2196F3;
 }
 
 .info-window[data-status="rented"] .info-title {
-  color: #fff;
+  color: #FF9800;
+}
+
+.info-window[data-status="repairing"] .info-title {
+  color: #F44336;
+}
+
+.info-window[data-status="overdue"] .info-title {
+  color: #39C5BB;
 }
 
 .amap-info {
@@ -228,11 +346,123 @@
 }
 
 .amap-info-content {
-  background: rgba(146, 146, 146, 0.5);
+  background: rgba(146, 146, 146, 0.7);
   padding: 0 !important;
 }
 
 .amap-info-sharp {
   display: none !important;
 }
-</style> 
\ No newline at end of file
+
+.marker-container {
+  width: 120px;
+  height: 120px;
+  transform: translate(-50%, -50%);
+  pointer-events: all;
+  filter: drop-shadow(0 4px 12px rgba(79, 217, 255, 0.4));
+
+}
+
+svg {
+  width: 100%;
+  height: 100%;
+}
+
+.ripple {
+  transform-origin: center;
+  stroke-width: 3;
+}
+
+.marker {
+  filter: drop-shadow(0 0 8px #4fd9ff);
+}
+
+.map-info-card {
+  border: 1px solid rgba(79, 217, 255, 0.2);
+  overflow: hidden;
+  width: 300px !important;
+  backdrop-filter: blur(12px);
+
+  .info-header {
+    padding: 12px 15px;
+    border-bottom: 1px solid rgba(79, 217, 255, 0.15);
+
+    .info-title {
+      color: #fff;
+      font-size: 15px;
+      font-weight: 500;
+      position: relative;
+      padding-left: 12px;
+
+      &::before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 50%;
+        transform: translateY(-50%);
+        width: 3px;
+        height: 14px;
+        background: linear-gradient(to bottom, #4fd9ff, #568aea);
+        border-radius: 2px;
+      }
+    }
+  }
+
+  .info-body {
+    padding: 15px;
+    background: rgba(15, 19, 37, 0.95);
+
+    .info-item {
+      font-size: 14px;
+      color: rgba(255, 255, 255, 0.9);
+      margin-bottom: 12px;
+      line-height: 1.5;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .status-tag {
+        display: inline-block;
+        padding: 2px 8px;
+        border-radius: 4px;
+        font-size: 13px;
+      }
+
+      .rent-status {
+        display: inline-flex;
+        align-items: center;
+        gap: 8px;
+        margin-top: 4px;
+        width: 100%;
+
+        .progress-bar {
+          flex: 1;
+          height: 4px;
+          background: rgba(255, 255, 255, 0.1);
+          border-radius: 2px;
+          overflow: hidden;
+          position: relative;
+
+          .progress {
+            position: absolute;
+            left: 0;
+            top: 0;
+            height: 100%;
+            border-radius: 2px;
+            transition: width 0.3s ease;
+          }
+        }
+
+        .rent-text {
+          font-size: 13px;
+          color: rgba(255, 255, 255, 0.9);
+          white-space: nowrap;
+          min-width: 80px;
+          text-align: right;
+        }
+      }
+    }
+  }
+}
+</style>
\ No newline at end of file

--
Gitblit v1.7.1