261 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | ||
|   <section class="AiMap" :class="{mask}">
 | ||
|     <div ref="amap" class="map"/>
 | ||
|     <div v-if="mask" class="mask"/>
 | ||
|   </section>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import AMapLoader from "@amap/amap-jsapi-loader";
 | ||
| 
 | ||
| export default {
 | ||
|   name: "AiMap",
 | ||
|   props: {
 | ||
|     plugins: {default: () => ['AMap.DistrictSearch', 'AMap.LineSearch']},
 | ||
|     map: Object,
 | ||
|     lib: Object,
 | ||
|     mapStyle: String,
 | ||
|     areaId: String,
 | ||
|     is3d: Boolean,
 | ||
|     ops: {default: () => ({})},
 | ||
|     markers: {default: () => []},
 | ||
|     mask: Boolean,
 | ||
|     searchBus: {default: "2"},
 | ||
|     pulseLines: Boolean,
 | ||
|     onlyShowArea: Boolean
 | ||
|   },
 | ||
|   computed: {
 | ||
|     viewMode() {
 | ||
|       return this.is3d ? '3D' : '2D'
 | ||
|     }
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       amap: null,
 | ||
|       mapLib: null,
 | ||
|       loca: null
 | ||
|     }
 | ||
|   },
 | ||
|   watch: {
 | ||
|     markers: {
 | ||
|       deep: true, handler() {
 | ||
|         this.addMarkers()
 | ||
|       }
 | ||
|     }
 | ||
|   },
 | ||
|   methods: {
 | ||
|     initMap() {
 | ||
|       let {plugins, viewMode, mapStyle} = this
 | ||
|       AMapLoader.load({
 | ||
|         key: '54a02a43d9828a8f9cd4f26fe281e74e',
 | ||
|         version: '2.0',
 | ||
|         plugins,
 | ||
|         Loca: {version: '2.0.0'}
 | ||
|       }).then(AMap => {
 | ||
|         this.mapLib = AMap
 | ||
|         this.$emit('update:lib', AMap)
 | ||
|         if (this.$refs.amap) {
 | ||
|           this.amap = new AMap.Map(this.$refs.amap, {
 | ||
|             mapStyle,
 | ||
|             viewMode,
 | ||
|             terrain: true,
 | ||
|             resizeEnable: true,
 | ||
|             skyColor: "#082243",
 | ||
|             zoom: 11,
 | ||
|             ...this.ops
 | ||
|           })
 | ||
|           this.amap.on('complete', () => {
 | ||
|             this.amap.setFitView();//视口自适应
 | ||
|           })
 | ||
| 
 | ||
|           this.$emit('update:map', this.amap)
 | ||
|           this.$emit("loaded")
 | ||
|           this.mapLoaded()
 | ||
|         }
 | ||
|       }).catch(this.initMap)
 | ||
|     },
 | ||
|     getMapArea() {
 | ||
|       const {mapLib: AMap} = this
 | ||
|       if (!!AMap) {
 | ||
|         new AMap.DistrictSearch({
 | ||
|           subdistrict: 0,   //获取边界不需要返回下级行政区
 | ||
|           extensions: 'all',  //返回行政区边界坐标组等具体信息
 | ||
|           level: 'district'  //查询行政级别为 市
 | ||
|         }).search(this.areaId.substring(0, 6), (status, result) => {
 | ||
|           const area = result?.districtList?.[0],
 | ||
|               bounds = area?.boundaries || [];
 | ||
|           let polygons = []
 | ||
|           if (this.onlyShowArea) {
 | ||
|             const mask = bounds.map(e => [e])
 | ||
|             polygons = bounds.map(path => new AMap.Polygon({
 | ||
|               path: path.map(e => [e.lng, e.lat]),
 | ||
|               strokeWeight: 1,
 | ||
|               fillOpacity: 0,
 | ||
|               strokeStyle: 'dashed',
 | ||
|               strokeColor: '#0091ea'
 | ||
|             }))
 | ||
|             this.amap.setMask(mask)
 | ||
|             this.amap.setPitch(65)
 | ||
|           } else {
 | ||
|             polygons = bounds.map(path => new AMap.Polygon({
 | ||
|               strokeWeight: 1,
 | ||
|               path: path.map(e => [e.lng, e.lat]),
 | ||
|               strokeStyle: 'dashed',
 | ||
|               fillOpacity: 0.1,
 | ||
|               fillColor: '#80d8ff',
 | ||
|               strokeColor: '#0091ea'
 | ||
|             }))
 | ||
|           }
 | ||
|           this.amap.add(polygons)
 | ||
|           this.amap.setCenter(area.center, true)
 | ||
|         })
 | ||
|       }
 | ||
|     },
 | ||
|     mapLoaded() {
 | ||
|       this.areaId && this.getMapArea()
 | ||
|       this.addPulseLines(this.areaId?.substring(0, 6))
 | ||
|       this.addMarkers()
 | ||
|     },
 | ||
|     addMarkers() {
 | ||
|       if (this.markers.length > 0 && this.mapLib && this.amap) {
 | ||
|         let markers = this.markers.map(e => {
 | ||
|           let {label, icon = "https://cdn.cunwuyun.cn/dvcp/h5/Location2.png"} = e
 | ||
|           return new this.mapLib.Marker({
 | ||
|             content: e.content || `<div class="marker">
 | ||
|                         <img src="${icon}"/>
 | ||
|                         <span>${label}</span>
 | ||
|                       </div>`,
 | ||
|             position: [e.lng, e.lat]
 | ||
|           })
 | ||
|         })
 | ||
|         this.amap.add(markers)
 | ||
|       }
 | ||
|     },
 | ||
|     addPulseLines(city) {
 | ||
|       let {amap: map, mapLib: lib, pulseLines} = this
 | ||
|       if (pulseLines && lib && map) {
 | ||
|         this.loca = new Loca.Container({map: this.amap})
 | ||
|         let ls = new lib.LineSearch({pageSize: 1, pageNum: 1, city}), lines = {
 | ||
|           type: "FeatureCollection",
 | ||
|           features: []
 | ||
|         }
 | ||
|         Promise.all(Array.from("0123456789").map(i => new Promise((resolve) => {
 | ||
|           ls.search(i, (e, res) => {
 | ||
|             if (e == "complete" && res.info == "OK") {
 | ||
|               res.lineInfo?.map(line => {
 | ||
|                 lines.features.push({
 | ||
|                   type: "Feature",
 | ||
|                   properties: {},
 | ||
|                   geometry: {
 | ||
|                     type: "LineString",
 | ||
|                     coordinates: line.path?.map(p => [p.lng, p.lat]) || []
 | ||
|                   }
 | ||
|                 })
 | ||
|               })
 | ||
|             }
 | ||
|             resolve()
 | ||
|           })
 | ||
|         }))).then(() => {
 | ||
|           let layer = new Loca.PulseLineLayer({
 | ||
|             zIndex: 10,
 | ||
|             opacity: 1,
 | ||
|             visible: true,
 | ||
|             zooms: [2, 22],
 | ||
|           })
 | ||
|           let geo = new Loca.GeoJSONSource({data: lines})
 | ||
|           layer.setSource(geo)
 | ||
|           layer.setStyle({
 | ||
|             altitude: 0,
 | ||
|             lineWidth: 2,
 | ||
|             // 脉冲头颜色
 | ||
|             headColor: "#B5FBFF",
 | ||
|             // 脉冲尾颜色
 | ||
|             trailColor: 'rgba(0,0,0,0)',
 | ||
|             // 脉冲长度,0.25 表示一段脉冲占整条路的 1/4
 | ||
|             interval: 0.25,
 | ||
|             // 脉冲线的速度,几秒钟跑完整段路
 | ||
|             duration: 15000,
 | ||
|           })
 | ||
|           this.loca.add(layer)
 | ||
|           this.loca.animate.start()
 | ||
|         })
 | ||
|       }
 | ||
|     }
 | ||
|   },
 | ||
|   mounted() {
 | ||
|     this.initMap()
 | ||
|   },
 | ||
|   destroyed() {
 | ||
|     this.amap?.destroy()
 | ||
|   }
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .AiMap {
 | ||
|   width: 100%;
 | ||
|   height: 100%;
 | ||
|   flex: 1;
 | ||
|   min-width: 0;
 | ||
|   min-height: 0;
 | ||
|   position: relative;
 | ||
| 
 | ||
|   &.mask {
 | ||
|     box-shadow: 0 0 40px 20px rgba(#000, .8);
 | ||
|   }
 | ||
| 
 | ||
|   .map {
 | ||
|     height: 100%;
 | ||
|   }
 | ||
| 
 | ||
|   .mask {
 | ||
|     pointer-events: none;
 | ||
|     position: absolute;
 | ||
|     left: 0;
 | ||
|     right: 0;
 | ||
|     top: 0;
 | ||
|     bottom: 0;
 | ||
|     z-index: 8;
 | ||
|     background: radial-gradient(rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .6) 40%, #000 100%);
 | ||
|   }
 | ||
| 
 | ||
|   :deep( .marker ) {
 | ||
|     position: relative;
 | ||
| 
 | ||
|     & > img {
 | ||
|       width: 50px;
 | ||
|       height: 50px;
 | ||
|     }
 | ||
| 
 | ||
|     & > span {
 | ||
|       display: none;
 | ||
|     }
 | ||
| 
 | ||
|     &:hover > span {
 | ||
|       position: absolute;
 | ||
|       left: 50%;
 | ||
|       bottom: 0;
 | ||
|       transform: translate(-50%, 100%);
 | ||
|       display: block;
 | ||
|       color: #fff;
 | ||
|       font-size: 14px;
 | ||
|       white-space: nowrap;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   :deep( .amap-logo), :deep( .amap-copyright ) {
 | ||
|     display: none !important;
 | ||
|   }
 | ||
| 
 | ||
|   :deep( .amap-icon ) {
 | ||
|     width: 40px !important;
 | ||
|     height: 40px !important;
 | ||
| 
 | ||
|     img {
 | ||
|       width: 100%;
 | ||
|       height: 100%;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |