236 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <section class="AiTMap">
 | |
|     <div ref="tmap" class="map"/>
 | |
|     <div class="mapLayerSwitcher" flex>
 | |
|       <div class="item" :class="{current:mapType=='vector'}" @click="mapType='vector'"/>
 | |
|       <div class="item satellite" :class="{current:mapType=='satellite'}" @click="mapType='satellite'"/>
 | |
|     </div>
 | |
|   </section>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| 
 | |
| export default {
 | |
|   name: "AiTMap",
 | |
|   props: {
 | |
|     /**
 | |
|      * 地区id
 | |
|      */
 | |
|     areaId: String,
 | |
|     /**
 | |
|      * 地图参数,详情见https://lbs.qq.com/webApi/javascriptGL/glDoc/docIndexMap
 | |
|      */
 | |
|     ops: {default: () => ({})},
 | |
|     /**
 | |
|      * 加载三方库,以腾讯地图文档要求进行添加
 | |
|      */
 | |
|     libraries: {default: () => ["service"]},
 | |
|     /**
 | |
|      * 地图实例,支持用.sync绑定获取
 | |
|      */
 | |
|     map: Object,
 | |
|     /**
 | |
|      * 地图库,支持用.sync绑定获取
 | |
|      */
 | |
|     lib: Object,
 | |
|   },
 | |
|   data() {
 | |
|     return {
 | |
|       tmap: null,
 | |
|       mapLib: null,
 | |
|       mapType: 'vector'
 | |
|     }
 | |
|   },
 | |
|   watch: {
 | |
|     mapType(type) {
 | |
|       this.tmap?.setBaseMap({type})
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     key() {
 | |
|       return process.env.NODE_ENV == "production" ?
 | |
|           "RWWBZ-64BEJ-MVLFJ-FTHLQ-JTR6J-SAB2S" :
 | |
|           "3RZBZ-LZUCF-CT6J5-NWKZH-FCWOQ-UUFKY"
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     injectLib(url, cb) {
 | |
|       const script = document.createElement('script')
 | |
|       script.type = 'text/javascript';
 | |
|       script.id = "aitmap";
 | |
|       script.src = url;
 | |
|       script.addEventListener('load', () => {
 | |
|         cb && cb()
 | |
|       })
 | |
|       document.body.appendChild(script);
 | |
|     },
 | |
|     initTMap() {
 | |
|       const TMap = window?.TMap
 | |
|       this.mapLib = TMap
 | |
|       this.tmap = new TMap.Map(this.$refs.tmap, {
 | |
|         zoom: 11,
 | |
|         ...this.ops
 | |
|       })
 | |
|       this.$emit('loaded')
 | |
|       this.$emit('update:lib', TMap)
 | |
|       this.$emit('update:map', this.tmap)
 | |
|       this.areaId && this.getMapArea()
 | |
|     },
 | |
|     getMapArea() {
 | |
|       let {mapLib: TMap, areaId, tmap: map} = this, keyword = areaId.substring(0, 6)
 | |
|       const fitBounds = (latLngList) => {
 | |
|         // 由多边形顶点坐标数组计算能完整呈现该多边形的最小矩形范围
 | |
|         if (latLngList.length === 0) {
 | |
|           return null;
 | |
|         }
 | |
|         let boundsN = latLngList[0].getLat();
 | |
|         let boundsS = boundsN;
 | |
|         let boundsW = latLngList[0].getLng();
 | |
|         let boundsE = boundsW;
 | |
|         latLngList.forEach((point) => {
 | |
|           point.getLat() > boundsN && (boundsN = point.getLat());
 | |
|           point.getLat() < boundsS && (boundsS = point.getLat());
 | |
|           point.getLng() > boundsE && (boundsE = point.getLng());
 | |
|           point.getLng() < boundsW && (boundsW = point.getLng());
 | |
|         });
 | |
|         return new TMap.LatLngBounds(
 | |
|             new TMap.LatLng(boundsS, boundsW),
 | |
|             new TMap.LatLng(boundsN, boundsE)
 | |
|         );
 | |
|       }
 | |
|       let polygons = new TMap.MultiPolygon({map, geometries: []});
 | |
|       new TMap.service.District({
 | |
|         polygon: 2,
 | |
|         maxOffset: 100
 | |
|       }).search({keyword}).then(res => {
 | |
|         if (res?.result) {
 | |
|           let center = res.result?.[0]?.[0]?.location
 | |
|           this.tmap.setCenter(center)
 | |
|           res.result.forEach((level) => {
 | |
|             level.forEach((place) => {
 | |
|               let bounds = [];
 | |
|               let newGeometries = place.polygon.map((polygon, index) => {
 | |
|                 bounds.push(fitBounds(polygon)); // 计算能完整呈现行政区边界的最小矩形范围
 | |
|                 return {
 | |
|                   id: `${place.id}_${index}`,
 | |
|                   paths: polygon, // 将得到的行政区划边界用多边形标注在地图上
 | |
|                 };
 | |
|               });
 | |
|               bounds = bounds.reduce((a, b) => {
 | |
|                 return fitBounds([
 | |
|                   a.getNorthEast(),
 | |
|                   a.getSouthWest(),
 | |
|                   b.getNorthEast(),
 | |
|                   b.getSouthWest(),
 | |
|                 ]);
 | |
|               }); // 若一行政区有多个多边形边界,应计算能包含所有多边形边界的范围。
 | |
|               polygons.updateGeometries(newGeometries);
 | |
|               this.tmap.fitBounds(bounds);
 | |
|             });
 | |
|           });
 | |
|         }
 | |
|       })
 | |
|     }
 | |
|   },
 | |
|   mounted() {
 | |
|     if (!window?.TMap) {
 | |
|       window.initTMap = this.initTMap
 | |
|       this.injectLib(`https://map.qq.com/api/gljs?v=1.exp&key=${this.key}&libraries=${this.libraries.toString()}&callback=initTMap`)
 | |
|     } else {
 | |
|       this.initTMap()
 | |
|     }
 | |
|   },
 | |
|   destroyed() {
 | |
|     this.map?.destroy()
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped>
 | |
| .AiTMap {
 | |
|   width: 100%;
 | |
|   height: 100%;
 | |
|   flex: 1;
 | |
|   min-width: 0;
 | |
|   min-height: 0;
 | |
|   position: relative;
 | |
| 
 | |
|   :deep(.map ) {
 | |
|     height: 100%;
 | |
| 
 | |
|     & > div > div {
 | |
|       &:nth-of-type(2), &:nth-of-type(3) {
 | |
|         display: none;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .mapLayerSwitcher {
 | |
|     position: absolute;
 | |
|     z-index: 202304061607;
 | |
|     bottom: 20px;
 | |
|     right: 12px;
 | |
|     background-color: #fff;
 | |
|     padding: 5px;
 | |
|     width: fit-content;
 | |
|     height: 56px;
 | |
|     box-sizing: content-box;
 | |
|     overflow: hidden;
 | |
|     transition: width 2s ease;
 | |
|     gap: 10px;
 | |
| 
 | |
|     &:hover {
 | |
|       .item {
 | |
|         display: block;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .item {
 | |
|       position: relative;
 | |
|       width: 80px;
 | |
|       height: 56px;
 | |
|       border: 1px dashed #ddd;
 | |
|       cursor: pointer;
 | |
|       flex-shrink: 0;
 | |
|       background-image: url("https://cdn.cunwuyun.cn/map/defaultMap.png");
 | |
|       box-sizing: border-box;
 | |
|       color: #fff;
 | |
|       background-size: 100%;
 | |
|       display: none;
 | |
| 
 | |
|       &.current {
 | |
|         border: 1px solid #366FFF;
 | |
|         display: block !important;
 | |
| 
 | |
|         &:before {
 | |
|           background-color: #366FFF;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       &:hover {
 | |
|         border-color: #333;
 | |
|       }
 | |
| 
 | |
|       &:before {
 | |
|         font-size: 12px;
 | |
|         line-height: 18px;
 | |
|         padding: 0 3px;
 | |
|         position: absolute;
 | |
|         bottom: 0;
 | |
|         right: 0;
 | |
|         content: "地图";
 | |
|         user-select: none;
 | |
|       }
 | |
| 
 | |
|       &.satellite {
 | |
|         background-image: url("https://cdn.cunwuyun.cn/map/satelliteMap.png");
 | |
| 
 | |
|         &:before {
 | |
|           content: "卫星"
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </style>
 |