1、首先要先注册腾讯地图,去控制台创建应用,拿到key。
新建应用之后,要先给应用配置额度,否则请求相关接口的时候会报额度已满等相关提示。
-
关键词输入提示
请求url:https://apis.map.qq.com/ws/place/v1/suggestion
参数key是必须的,keyword是必须的(即关键词) -
逆地址解析
请求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>
当然了,你也可以选择异步加载的方式引入,参考文档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 })
},
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>
相关的样式
```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>
相关的方法
<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>