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