Skip to content
当前页大纲

说明

组件是基于vue2以及高德地图版本1.4.15

组件样式

map.png

组件功能

  • 搜索功能
  • 点击地图进行定位功能

组件代码

vue
<template>
  <div class="AMap">
    <el-dialog
      class="AMap__dialog"
      width="80%"
      append-to-body
      modal-append-to-body
      :title="title"
      :visible.sync="showAMapDialog"
    >
      <template v-if="showAMapDialog">
        <el-input
          id="map__input"
          v-model="address"
          class="AMap__content-input"
          :readonly="disabled"
          clearable
          placeholder="输入关键字选取地点"
          @input="handleInput"
        />
        <div class="AMap__content-box">
          <div id="map__container" class="AMap__content-container" tabindex="0" />
          <div id="map__result" class="AMap__content-result">
            <h3 class="w100 tc">暂无搜索结果</h3>
          </div>
        </div>
      </template>
      <div slot="footer" class="dialog-footer">
        <el-button @click="showAMapDialog = false">取 消</el-button>
        <el-button type="primary" @click="submitInfo">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import { addAMap, removeAMap } from './config'
import { throttle } from '@/hua5-lib/utils/common-utils'
export default {
  name: 'MapDialog',
  props: {
    disabled: {
      type: Boolean,
      default: false,
    },
    value: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 双向数据绑定的值 组件使用 place.sync = form.xxx
    place: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      address: '', // 地址
      poi: {}, // 地点信息
      marker: null, // 标记点
      map: null, // 地图实例
      showAMapDialog: false // 是否展示地图弹窗
    }
  },
  computed: {
    title() {
      return this.disabled ? '查看坐标' : '选择坐标'
    },
  },
  watch: {
    value: {
      handler(val) {
        this.poi = val
      },
      deep: true,
      immediate: true
    },
    poi: {
      handler(val) {
        this.address = val.formattedAddress
        this.$emit('input', val)
      },
      deep: true
    },
    showAMapDialog(newValue) {
      newValue ? this.setupMap() : this.tearDownMap()
    }
  },
  methods: {
    // 加载地图
    setupMap() {
      this.$nextTick(() =>
        this.init(() => {
          // 在打开地图的时候判断是否有地址信息,如果有地址信息需要回显地址信息
          if (this.poi.longitude && this.poi.latitude) {
            this.addMarker(this.poi.longitude, this.poi.latitude)
            this.getAddress(this.longitude, this.poi.latitude)
          }
          if (this.poi.location) {
            this.addMarker(this.poi.location.lng, this.poi.location.lat)
            this.getAddress(this.poi.location.lng, this.poi.location.lat)
          }
        })
      )
    },
    // 卸载地图
    tearDownMap() {
      window.poiPicker.clearSearchResults()
      window.poiPicker.off('poiPicked')
      window.poiPicker = null
      this.map.destroy('click')
      // 解绑地图的点击事件
      this.map.off('click')
      // 销毁地图,并清空地图容器
      this.map.destroy()
      // 地图对象赋值为null
      this.map = null
      // 清除地图容器的 DOM 元素
      document.getElementById('map__container').remove()
      removeAMap()
    },
    handleInput() {
      throttle(this.updateSearchResults, 200)
    },
    updateSearchResults() {
      // 控制弹窗右侧搜索结果容器 没有搜索结果的时候显示暂无搜索结果
      const resultDiv = document.getElementById('map__result')
      setTimeout(() => {
        const searchDiv = document.querySelector('.amap_lib_placeSearch')
        if (!searchDiv) {
          resultDiv.innerHTML = `<h3 class="w100 tc">暂无搜索结果</h3>`
        }
      }, 200)
    },
    // 提交事件
    submitInfo() {
      if (this.testUtils.isNotEmpty(this.poi)) {
        this.$emit('handConfirm', this.poi)
        this.$emit('update:place', this.poi.formattedAddress) // 触发双向数据绑定的方法
        this.showAMapDialog = false
      } else {
        this.hua5UI.err('请选择地址')
      }
    },
    // 新增坐标
    addMarker(R, P) {
      this.clearMarker()
      this.marker = new window.AMap.Marker({
        position: [R, P]
      })
      this.marker.setMap(this.map)
    },
    // 清空坐标
    clearMarker() {
      this.marker?.setMap(null)
      this.marker = null
    },
    // 获取坐标
    getAddress(R, P) {
      // eslint-disable-next-line new-cap
      new window.AMap.service('AMap.Geocoder', () => {
        // 回调函数
        const geocoder = new window.AMap.Geocoder({})
        geocoder.getAddress([R, P], (status, result) => {
          if (status === 'complete' && result.info === 'OK') {
            const regeocode = result.regeocode
            this.poi = Object.assign(regeocode, {
              longitude: R,
              latitude: P
            })
            // 自定义点标记内容
            const markerContent = document.createElement('div')
            // 点标记中的图标
            const markerImg = document.createElement('img')
            markerImg.src =
              '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png'
            markerContent.appendChild(markerImg)
            // 点标记中的文本
            const markerSpan = document.createElement('span')
            markerSpan.className = 'AMap__marker'
            markerSpan.innerHTML = this.poi.formattedAddress
            markerContent.appendChild(markerSpan)
            this.marker.setContent(markerContent) // 更新点标记内容
          }
        })
      })
    },
    addClick() {
      this.map.on('click', e => {
        const lnglat = e.lnglat
        const P = lnglat.P || lnglat.Q
        const R = lnglat.R
        this.addMarker(R, P)
        this.getAddress(R, P)
      })
    },
    init(callback) {
      this.map = new window.AMap.Map('map__container', {
        // zoom 控制地图缩放比例 有地址信息的时候需要放大地图
        // center 地图的中心点
        zoom: this.testUtils.isNotEmpty(this.poi) ? 23 : 13,
        center: (() => {
          if (this.poi.longitude && this.poi.latitude) {
            return [this.poi.longitude, this.poi.latitude]
          }
          if (this.poi.location) {
            return [this.poi.location.lng, this.poi.location.lat]
          }
        })()
      })
      this.initPoip()
      this.addClick()
      callback()
    },
    initPoip() {
      window.AMapUI.loadUI(['misc/PoiPicker'], PoiPicker => {
        // 初始化poiPicker
        window.poiPicker = new PoiPicker({
          input: 'map__input',
          placeSearchOptions: {
            map: this.map,
            pageSize: 10
          },
          searchResultsContainer: 'map__result'
        })
        window.poiPicker.on('poiPicked', this.handlePoiPicked)
      })
    },
    handlePoiPicked(poiResult) {
      this.clearMarker()
      const source = poiResult.source
      const poi = poiResult.item
      this.poi = Object.assign(poi, {
        formattedAddress: poi.name,
        longitude: poi.location.R,
        latitude: poi.location.P,
      })
      if (source !== 'search') {
        window.poiPicker.searchByKeyword(poi.name)
      }
    },
    // 打开弹窗的时候将动态加载地图需要的js标签
    open() {
      addAMap().then(() => {
        this.showAMapDialog = true
      })
    }
  }
}
</script>

<style lang="scss">
.AMap {
  &__marker {
    position: absolute;
    top: -20px;
    right: -118px;
    color: #fff;
    padding: 4px 10px;
    box-shadow: 1px 1px 1px rgba(10, 10, 10, 0.2);
    white-space: nowrap;
    font-size: 12px;
    background-color: #25a5f7;
    border-radius: 3px;
  }
  &__content {
    &-input {
      margin-bottom: 10px;
    }
    &-box {
      display: flex;
    }
    &-container {
      width: 100%;
      height: 450px;
    }
    &-result {
      display: block !important;
      width: 250px;
      height: 450px;
      overflow-y: auto;
    }
  }
}
</style>

其中引入的js

js
import { AMapProxyHost } from '@/config/env'
export const addAMap = function() {
  window._AMapSecurityConfig = {
    // securityJsCode: '您的密钥',
    serviceHost: AMapProxyHost
  }
  const key = '您的key'
  return new Promise((resolve, reject) => {
    var script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = `https://webapi.amap.com/maps?v=1.4.15&key=${key}&plugin=AMap.PlaceSearch`
    script.onload = function() {
      var scriptMain = document.createElement('script')
      scriptMain.type = 'text/javascript'
      scriptMain.src = `https://webapi.amap.com/ui/1.1/main.js`
      document.head.appendChild(scriptMain)
      scriptMain.onload = function() {
        resolve()
      }
    }
    document.head.appendChild(script)
  })
}

export const removeAMap = function(str = 'webapi') {
  const scriptTags = document.querySelectorAll('script')
  scriptTags.forEach((scriptTag) => {
    if (scriptTag.src.includes(str)) {
      scriptTag.parentNode.removeChild(scriptTag)
    }
  })
}

这个js的用途是动态添加页面的scipt标签以及移除

页面使用

vue
<mapDialog ref="mapDialog" v-model="addressInfo" :place.sync="form.receiverAddress" @handConfirm="handConfirm" />

MIT License.