• Vue项目引入腾讯地图,实现可以根据关键词搜出相关位置,并定位到该位置


    1、首先要先注册腾讯地图,去控制台创建应用,拿到key。

    新建应用之后,要先给应用配置额度,否则请求相关接口的时候会报额度已满等相关提示。

    1. 关键词输入提示
      请求url:https://apis.map.qq.com/ws/place/v1/suggestion
      参数key是必须的,keyword是必须的(即关键词)

    2. 逆地址解析
      请求url: https://apis.map.qq.com/ws/geocoder/v1/?location=
      参数key是必须的,location也是必须的(即经纬度),格式lat,lng

    2、请求相关接口的时候,可能会出现跨域的问题

    解决跨域,可以使用jsonp的请求方式解决

    3、在index.html文件引入

    <script charset="utf-8" src="https://wemapvis.map.qq.com/api/gljs/v2?v=1.exp&key=您申请的key值">script>
    
    • 1

    当然了,你也可以选择异步加载的方式引入,参考文档https://wemap.qq.com/Vis/JavascriptAPI/APIGuide/overview/prepare

    3、这个自定义map组件传入的参数格式及回调函数

    // 参数
    selectedValue: {
    	location: {
        	lat: null,
            lng: null
        },
        address: null,
        province: null,
        city: null,
        district: null
    },
    /选择定位后的回调函数
    getLocation(data) {
    	this.selectedValue = {
        	location: {
            	lat: data.latitude || null,
              	lng: data.longitude || null
            },
            address: data.address || null,
            province: data.province || null,
            city: data.city || null,
            district: data.district || null
       }
       const temp = {
       		address: data.address,
            latitude: data.latitude,
            longitude: data.longitude
       }
       this.$set(this.form_tableData.tableData, this.editIndex, { ...this.form_tableData.tableData[this.editIndex], ...temp })
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3、绘制地图,这是自定义的map组件

    <template>
      <el-dialog
        v-if="dialogVisible"
        id="mapDialog"
        title="请选择详细地址"
        :visible.sync="dialogVisible"
        :close-on-click-modal="false"
        :close-on-press-escape="false"
        custom-class="map-Dialog"
        width="40%"
        @close="onClose"
        @closed="onClosed"
      >
        <div class="well-map">
          <div class="selected-info">
            <div>
              <span class="label">所选地址:</span>
              <span>{{
                locData.address ? locData.address : selectedValue.address
              }}</span>
            </div>
          </div>
          <div v-clickoutside="handleBlur" class="search-row">
            <el-row class="input-wrap" :gutter="20">
              <el-col :span="20">
                <el-input
                  v-model="searchKey"
                  placeholder="请输入要搜索的地址"
                  @click="handleFoucus"
                  @input="handleSearch()"
                />
              </el-col>
              <el-col :span="2">
                <el-button type="primary" @click="handleSearch()">搜索</el-button>
              </el-col>
            </el-row>
    
            <ul v-show="showAddressList && addressList.length">
              <li
                v-for="(item, index) in addressList"
                :key="index"
                @click.stop="handleSelect(item)"
              >
                <span class="title">{{ item.title }}</span>
                <span class="other-info">{{ item.address }}</span>
              </li>
            </ul>
          </div>
          <div id="well-container" class="well-map-container" />
        </div>
        <div slot="footer" class="dialog-footer">
          <el-button
            type="default"
            @click.native="dialogVisible = false"
          >取消</el-button>
          <el-button
            type="primary"
            @click.native="findLocation"
          >确定</el-button>
        </div>
      </el-dialog>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    相关的样式

    
    ```html
    <style lang="scss" scoped>
    #mapDialog {
      ::v-deep .el-dialog {
        min-width: 1000px;
        min-height: 800px;
        margin-top: 8vh !important;
        .el-dialog__body {
          padding-top: 10px;
        }
      }
    }
    .well-map {
      line-height: normal;
      .search-row {
        position: relative;
        width: 100%;
        margin: 12px 0;
        z-index: 99009;
    
        .input-wrap {
          height: 32px;
          line-height: 32px;
          display: flex;
    
          > input {
            box-sizing: border-box;
            margin: 0;
    
            position: relative;
            width: 100%;
            height: 100%;
            padding: 4px 11px;
            color: rgba(0, 0, 0, 0.65);
            font-size: 14px;
            background-color: #fff;
            /*background-image: none;*/
            border: 1px solid #d9d9d9;
            border-radius: 4px 0 0 4px;
            transition: all 0.3s;
            border-right: none;
    
            &:hover {
              border-color: #40a9ff;
              border-right-width: 1px !important;
            }
    
            &:focus {
              border-color: #40a9ff;
              border-right-width: 1px !important;
              outline: 0;
              box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
            }
          }
    
          > button {
            padding: 0 15px;
            border-radius: 0 4px 4px 0;
            color: #fff;
            background: #1890ff;
            border-color: #1890ff;
            box-shadow: none;
            border: none;
            width: 82px;
            height: 100%;
            cursor: pointer;
    
            &:hover,
            &:focus {
              color: #fff;
              background: #40a9ff;
              border-color: #40a9ff;
              outline: none;
            }
    
            &:active {
              color: #fff;
              background: #096dd9;
              border-color: #096dd9;
              outline: none;
            }
          }
        }
    
        > ul {
          position: absolute;
          top: 100%;
          left: 0;
          width: 100%;
          background: rgba(252, 250, 250, 0.918);
          border: 1px solid #f1f1f1;
          font-size: 13px;
          color: #5a5a5a;
          max-height: 280px;
          overflow-y: auto;
          list-style: none;
          padding: 0;
          margin: 0;
    
          > li {
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
            width: 100%;
            border-bottom: 1px solid #f1f1f1;
            padding: 10px;
            margin: 0;
            cursor: pointer;
    
            &:hover {
              background: #eff6fd;
            }
    
            .title {
              display: block;
              line-height: normal;
              margin-bottom: 4px;
            }
    
            .other-info {
              font-size: 12px;
              color: #b9b9b9;
              display: block;
              line-height: normal;
            }
          }
        }
      }
    
      .well-map-container {
        width: 100%;
        height: 550px;
        margin-top: 20px;
      }
    
      .selected-info {
        background: #ecf5ff;
        padding: 10px 14px;
        color: #565656;
        font-size: 13px;
    
        > div {
          margin-bottom: 4px;
    
          .label {
            color: #757575;
          }
    
          &:last-child {
            margin-bottom: 0;
          }
        }
      }
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156

    相关的方法

    <script>
    /**
     * 函数:
     * 1. selected-change:当选中的点发生变化时,触发
     * **/
    
    import { getLocaltion, codeToaddress } from '@/api/common'
    import editOperMixins from '@/mixins/dialog/edit-oper'
    import baseConfig from '@/config/base-config'
    import $script from 'scriptjs'
    import { debounce } from 'throttle-debounce'
    export default {
      directives: {
        /**
         * 封装指令,监听点击非目标元素之外的dom
         * ***/
        clickoutside: {
          bind(el, binding, vnode) {
            function documentHandler(e) {
              // 这里判断点击的元素是否是本身,是本身,则返回
              if (el.contains(e.target)) {
                return false
              }
              // 判断指令中是否绑定了函数
              if (binding.expression) {
                // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
                binding.value(e)
              }
            }
    
            // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
            el.__vueClickOutside__ = documentHandler
            document.addEventListener('click', documentHandler)
          },
          update() {},
          unbind(el, binding) {
            // 解除事件监听
            document.removeEventListener('click', el.__vueClickOutside__)
            delete el.__vueClickOutside__
          }
        }
      },
      mixins: [editOperMixins],
      props: {
        selectedValue: {
          type: Object,
          required: false,
          default: () => {}
        }
      },
      data() {
        return {
          mapKey: baseConfig.TXMAP_KEY,
          mapUrl: baseConfig.TXMAP_URL,
          searchKey: '', // 搜索Key
          addressList: [], // 搜索结果
          map: null,
          markerLayer: null,
          showAddressList: false,
    
          locData: {
            longitude: '',
            latitude: '',
            address: ''
          }
        }
      },
      watch: {
        dialogVisible(val) {
          if (val) {
            this.$nextTick(() => {
              $script(`https://map.qq.com/api/js?v=2.exp&key=${this.mapKey}`, () => {
                $script(`https://map.qq.com/api/gljs?v=1.exp&key=${this.mapKey}`, () => {
                  const { lat, lng } = this.selectedValue.location
                  this.initMap(lat || 39.984120, lng || 116.307484)
                  if (lat && lng) {
                    this._drawPoint(lat, lng, false)
                  }
                })
              })
            })
          } else {
            this.$emit('update:visible', false)
          }
        },
        locData: {
          handler(val) {
            // console.log(val)
            // this.resetPoint()
          },
          deep: true
        }
      },
      mounted() {
    
      },
      created() {
      },
      methods: {
        /**
         * 初始化地图
         * **/
        initMap(lat = 39.984120, lng = 116.307484) {
          // 初始化地图
          // 定义地图中心点坐标
          var center = new TMap.LatLng(lat, lng)
          this.map = new TMap.Map('well-container', {
            center: center, // 设置地图中心点坐标
            rotation: 20, // 设置地图旋转角度
            pitch: 0, // 设置俯仰角度(0~45)
            zoom: 16 // 设置地图缩放级别
          })
    
          // 初始化marker图层
          this.markerLayer = new TMap.MultiMarker({
            id: 'marker-layer',
            map: this.map
          })
    
          // 监听点击事件添加marker
          this.map.on('click', this._clickMap)
        },
        /**
         * 搜索框聚焦
         * **/
        handleFoucus(e) {
          this.showAddressList = true
        },
        /**
         * 搜索框失焦
         * **/
        handleBlur() {
          this.showAddressList = false
        },
        /**
         * 搜索框内容发生变化
         * debounc防抖函数
         * **/
        handleSearch: debounce(300, false, function() {
          this.showAddressList = true
          if (!this.searchKey) {
            this.addressList = []
          } else {
            getLocaltion({ keyword: this.searchKey, key: this.mapKey }).then((res) => {
              if (res.status === 0) {
                this.addressList = res.data
              } else {
                this.$message(res.message)
              }
            }).catch((err) => {
              console.log(err)
            })
          }
        }),
    
        async handleSelect(row) {
          // console.log(JSON.parse(JSON.stringify(row)))
          this.searchKey = row.title
          // searchKey发生变化了,需触发搜索
          this.handleSearch()
          // 比较两个地址,tempAdress为省市区拼接的
          const tempAdress = row.province + row.city + row.district + row.title
          this.locData.address = this.getAdress(tempAdress, row.address)
          this.locData.latitude = row.location.lat
          this.locData.longitude = row.location.lng
          this.locData.province = row.province
          this.locData.city = row.city
          this.locData.district = row.district
          // selectedValue发生变化了,需重新绘制点
          this._drawPoint(this.locData.latitude, this.locData.longitude, true)
          // 选中之后需触发失去焦点
          this.handleBlur()
        },
    
        /**
         * 根据经纬度在地图上标注
         * @param lat 纬度
         * @param lng 经度
         * @param isUpdateCenter 是否更新地图中心点,有以下三种情况:
         * 1. 外部传入的点信息发生变化时,需更新中心点
         * 2. 选中搜索列表的某一项时,需更新中心点
         * 3. 点击地图标记点时,不需要更新中心点
         * **/
        _drawPoint(lat, lng, isUpdateCenter) {
          // 先清空点
          this.markerLayer.setGeometries([])
          if (!lat || !lng) {
            return
          }
          // 更新地图中心位置
          if (isUpdateCenter) {
            this.map.setCenter(new TMap.LatLng(lat, lng))
          }
          this.markerLayer.add({
            position: new TMap.LatLng(lat, lng)
          })
        },
    
        /**
         * 点击地图
         * 1. 根据经纬度画点
         * 2, 根据经纬度逆向查询到地址详细信息
         * 3. 通知父组件
         * **/
        _clickMap(evt) {
          console.log('点击地图:', evt)
          this.searchKey = ''
          this._drawPoint(evt.latLng.lat, evt.latLng.lng, false)
          const location = evt.latLng.lat + ',' + evt.latLng.lng
          codeToaddress({ key: this.mapKey, location: location })
            .then((res) => {
              console.log('逆地址解析结果:', res)
              if (res.status === 0) {
                this.locData.address = res.result.address
                this.locData.longitude = evt.latLng.lng
                this.locData.latitude = evt.latLng.lat
                this.locData.province = res.result.ad_info.province
                this.locData.city = res.result.ad_info.city
                this.locData.district = res.result.ad_info.district
              } else {
                this.$message.error(res.message)
              }
            })
            .catch((err) => {
              console.log('逆地址解析失败', err)
            })
        },
    
        // 进入地图弹窗重新定位
        resetPoint() {
          if (this.locData.latitude && this.locData.longitude) {
            this._drawPoint(
              this.selectedValue.latitude,
              this.selectedValue.longitude,
              true
            )
          }
        },
        // 返回选中点的位置
        findLocation() {
          if (
            !this.locData.latitude &&
            !this.locData.latitude &&
            !this.locData.address
          ) {
            this.$message.warning('请选择地址')
          } else {
            this.$emit('getLocation', this.locData)
            this.dialogVisible = false
          }
        },
    
        /**
         * 根据经纬度在地图上标注
         * @param address1 地址1
         * @param address2 地址2
         * 1. 比较两个地址,返回其中一个
         * **/
        getAdress(address1, address2) {
          // console.log(address1, address2)
          if (address2.indexOf('号') > 0 && address2.indexOf('路') > 0) {
            return address2
          } else {
            return address1
          }
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
  • 相关阅读:
    stm32之智能小车总结
    阿里开源-JVM-SandBox
    Excel表格密码保护解除
    【Linux】基本指令(三)
    代码随想录刷题记录 4 - 字符串
    【亲测】简易商城小程序源码-易优CMS后台
    java计算机毕业设计springboot+vue青少年编程在线考试系统
    序列化方式介绍和性能比较 (kryo fastjson hessian jdk)
    【CSS】问题:为什么我的z-index不起作用
    自定义Flink kafka连接器Decoding和Serialization格式
  • 原文地址:https://blog.csdn.net/qq_45616003/article/details/133842313