262 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						||
  <section class="AiMap" :class="{mask}">
 | 
						||
    <div ref="amap" class="map"/>
 | 
						||
    <div v-if="mask" class="mask"/>
 | 
						||
    <slot/>
 | 
						||
  </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,
 | 
						||
    satellite: Boolean
 | 
						||
  },
 | 
						||
  data() {
 | 
						||
    return {
 | 
						||
      amap: null,
 | 
						||
      mapLib: null,
 | 
						||
      loca: null,
 | 
						||
    }
 | 
						||
  },
 | 
						||
  watch: {
 | 
						||
    markers: {
 | 
						||
      deep: true, handler() {
 | 
						||
        this.addMarkers()
 | 
						||
      }
 | 
						||
    }
 | 
						||
  },
 | 
						||
  methods: {
 | 
						||
    initMap(c = 0) {
 | 
						||
      let {plugins, 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: "3D",
 | 
						||
            terrain: true,
 | 
						||
            resizeEnable: true,
 | 
						||
            skyColor: "#082243",
 | 
						||
            zoom: 11,
 | 
						||
            ...this.ops
 | 
						||
          })
 | 
						||
          this.amap.on('complete', () => {
 | 
						||
            this.amap.setFitView();//视口自适应
 | 
						||
          })
 | 
						||
          /*增加卫星图层*/
 | 
						||
          if (this.satellite) {
 | 
						||
            this.amap.add([new AMap.TileLayer.RoadNet({zIndex: 11}), new AMap.TileLayer.Satellite({zIndex: 10})])
 | 
						||
          }
 | 
						||
          /* end */
 | 
						||
          this.$emit('update:map', this.amap)
 | 
						||
          this.$emit("loaded")
 | 
						||
          this.mapLoaded()
 | 
						||
        }
 | 
						||
      }).catch(() => c < 10 ? this.initMap(++c) : new Error("地图加载有问题"))
 | 
						||
    },
 | 
						||
    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.is3d && 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 && city) {
 | 
						||
        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: 30px;
 | 
						||
      height: 30px;
 | 
						||
    }
 | 
						||
 | 
						||
    & > 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>
 |