From 04477a62f8966e9aabc31421bab138960eff323e Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期三, 26 三月 2025 15:57:29 +0800
Subject: [PATCH] 除地图外所有接口对接、样式调整

---
 src/components/MapPanel.vue |  433 ++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 293 insertions(+), 140 deletions(-)

diff --git a/src/components/MapPanel.vue b/src/components/MapPanel.vue
index ba1e8dd..38f4db6 100644
--- a/src/components/MapPanel.vue
+++ b/src/components/MapPanel.vue
@@ -1,11 +1,12 @@
 <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'
 
 export default {
   name: 'MapPanel',
@@ -16,175 +17,207 @@
       infoWindow: null
     }
   },
+  watch: {
+    mapMarkerStatus(newVal) {
+      console.log(newVal)
+    }
+  },
+  computed: {
+    ...mapState([
+      'mapMarkerStatus'
+    ]),
+  },
   mounted() {
     this.$nextTick(() => {
       this.initMap()
+      window.addEventListener('resize', () => {
+        this.map && this.map.resize()
+      })
+    })
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', () => {
+      this.map && this.map.resize()
     })
   },
   methods: {
     async initMap() {
-      try {
-        const AMap = await AMapLoader.load({
-          key: '526e04b30ceba8f217c5def5a92392f9',
-          version: '2.0',
-          plugins: ['AMap.ToolBar', 'AMap.Scale']
-        })
+      const map = await AMapLoader.load({
+        key: '67968c82f27c7e2cb9f40c1a9aa3042b',
+        version: '2.0',
+        plugins: ['AMap.MarkerClusterer']
+      })
 
-        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 = new map.Map('map-container', {
+        zoom: 13,
+        center: [91.172119, 29.652941],
+        mapStyle: 'amap://styles/normal'
+      })
 
-        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)
-      }
+      this.addMapMarkers()
     },
-    addMarkers() {
-      const houses = [
+    addMapMarkers() {
+      const markers = [
         {
-          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.172119, 29.652941],
+          info: {
+            address: '西花部汾罚藏医院',
+            name: '名称名称名称',
+            status: 'vacant',
+            statusText: '待出租',
+            tenant: '张三',
+            currentRent: 1600,
+            totalRent: 20000,
+            quarterlyPaid: 500,
+            quarterlyRent: 2000,
+            color: '#faad14'
+          }
         },
         {
-          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>
-          `
+          position: [91.182119, 29.662941],
+          info: {
+            address: '拉萨市城关区江苏路15号',
+            name: '泊江大道B栋',
+            status: 'rented',
+            statusText: '已出租',
+            tenant: '-',
+            currentRent: 3000,
+            totalRent: 15000,
+            quarterlyPaid: 1000,
+            quarterlyRent: 4500,
+            color: '#4fd9ff'
+          }
+        },
+        {
+          position: [91.162119, 29.642941],
+          info: {
+            address: '拉萨市城关区北京中路8号',
+            name: '新城大道C区',
+            status: 'maintenance',
+            statusText: '维修中',
+            tenant: '-',
+            currentRent: 0,
+            totalRent: 16000,
+            quarterlyPaid: 0,
+            quarterlyRent: 5400,
+            color: '#ff4d4f'
+          }
+        },
+        {
+          position: [91.192119, 29.672941],
+          info: {
+            address: '拉萨市城关区夺底路33号',
+            name: '高江路D座',
+            status: 'overdue',
+            statusText: '欠费',
+            tenant: '西藏联通',
+            currentRent: 12000,
+            totalRent: 20000,
+            quarterlyPaid: 4000,
+            quarterlyRent: 6600,
+            color: '#39c5bb'
+          }
         }
       ]
 
-      houses.forEach(house => {
-        const circles = []
-        const baseRadius = 15
-        const numCircles = 3
+      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 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'
+        const markerObj = new AMap.Marker({
+          position: marker.position,
+          content: content,
+          anchor: 'center',
+          offset: new AMap.Pixel(0, 0),
+          zIndex: 100
         })
 
-        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'
-          }))
-        }
+        markerObj.on('click', () => {
+          this.showInfoWindow(markerObj, marker.info)
+        })
 
-        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
-            })
-          })
-
-          requestAnimationFrame(animate)
-        }
-        animate()
+        markerObj.setMap(this.map)
       })
-    }
+    },
+    showInfoWindow(marker, info) {
+      const content = `
+        <div class="map-info-card" style="color:#000;background-color: #fff !important;padding: 10px;border-radius: 12px;box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);">
+          <div class="info-header">
+            <div class="info-title">房屋详情</div>
+          </div>
+          <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.currentRent}/${info.totalRent}
+            </div>
+            <div class="info-item">
+              本季租金:${info.quarterlyPaid}/${info.quarterlyRent}
+            </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 {
@@ -215,11 +248,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 {
@@ -235,4 +276,116 @@
 .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;
+  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