完成后的具体样子
改组件实现点击地图返回详细的坐标与具体地址,若有具体地址则父组件传给改组件地址与左边,组件实现回显。组件内具体代码如下:
<template>
<el-dialog
title="请选择详细地址"
:visible.sync="mapVisible"
:fullscreen="false"
id="mapDialog"
:close-on-click-modal="false"
@closed="closeDialog"
>
<div class="well-map">
<div class="selected-info">
<div>
<span class="label">所选地址:</span>
<span>{{
locData.address ? locData.address : selectedValue.address
}}</span>
</div>
</div>
<div class="search-row" v-clickoutside="handleBlur">
<el-row class="input-wrap" :gutter="20">
<el-col :span="20">
<el-input
v-model="searchKey"
@click="handleFoucus"
@input="handleSearch()"
placeholder="请输入要搜索的地址"
></el-input>
</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>
<div slot="footer" class="dialog-footer">
<el-button
type="default"
size="small"
icon="el-icon-close"
@click.native="closeDialog"
>取消</el-button
>
<el-button
type="primary"
size="small"
icon="el-icon-check"
@click.native="findLocation"
>确定</el-button
>
</div>
</el-dialog>
</template>
<script>
/**
* 函数:
* 1. selected-change:当选中的点发生变化时,触发
* **/
import mapApi from "../../api/class/sign";
export default {
props: {
selectedValue: {
type: Object,
required: false,
},
},
data() {
return {
mapVisible: true,
timeout: null, //搜索防抖
searchKey: "", //搜索Key
addressList: [], //搜索结果
mapKey: "HSUBZ-*****-*****-*****-*****-*****", //腾讯地图mapKey
map: null,
markerLayer: null,
showAddressList: false,
locData: {
longitude: "",
latitude: "",
address: "",
},
};
},
mounted() {
this.$nextTick(() => {
this.initMap();
this.resetPoint();
});
},
created() {
console.log(this.selectedValue);
if (this.selectedValue.latitude) {
this.locData.latitude = this.selectedValue.latitude;
}
if (this.selectedValue.longitude) {
this.locData.longitude = this.selectedValue.longitude;
}
if (this.selectedValue.address) {
this.locData.address = this.selectedValue.address;
}
if (!this.selectedValue.region) {
this.selectedValue.region = "杭州市"
}
},
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__;
},
},
},
methods: {
/**
* 初始化地图
* **/
initMap() {
//初始化地图
this.map = new TMap.Map("well-container", {
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;
},
/**
* 搜索框内容发生变化
* timeout是为了防抖
* **/
handleSearch() {
this.showAddressList = true;
clearTimeout(this.timeout);
if (!this.searchKey) {
this.addressList = [];
} else {
this.timeout = setTimeout(() => {
mapApi.placeSuggest({keyword: this.searchKey,region: this.selectedValue.region,}).then((res) => {
if (res.code === 200) {
this.addressList = res.data;
} else {
this.$message(res.msg);
}
}).catch((err) => {
console.log(err);
});
}, 300);
}
},
handleSelect(row) {
console.log(JSON.stringify(row));
this.searchKey = row.title;
//searchKey发生变化了,需触发搜索
this.handleSearch();
this.locData.address = row.address;
this.locData.latitude = row.latitude;
this.locData.longitude = row.longitude;
//selectedValue发生变化了,需重新绘制点
this._drawPoint(row.latitude, row.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("点击地图:");
this.searchKey = "";
this._drawPoint(evt.latLng.lat, evt.latLng.lng, false);
mapApi
.codeToaddress({ longitude: evt.latLng.lng, latitude: evt.latLng.lat })
.then((res) => {
console.log("逆地址解析结果:", res);
if (res.code === 200) {
this.locData.address = res.data.address;
this.locData.longitude = evt.latLng.lng;
this.locData.latitude = evt.latLng.lat;
} else {
this.$message.error(res.msg);
}
})
.catch((err) => {
console.log("逆地址解析失败", err);
});
},
// 进入地图弹窗重新定位
resetPoint() {
if (this.selectedValue.latitude && this.selectedValue.longitude) {
this._drawPoint(
this.selectedValue.latitude,
this.selectedValue.longitude,
true
);
} else {
mapApi.addressTocode({ address: this.selectedValue.region }).then((res) => {
if (res.code === 200) {
this._drawPoint(res.data.latitude, res.data.longitude, true);
} else {
this.$message.error(res.msg);
}
});
}
},
closeDialog() {
this.mapVisible = false;
this.$emit("closeDialog", false);
},
//返回选中点的位置
findLocation() {
if (
!this.locData.latitude &&
!this.locData.latitude &&
!this.locData.address
) {
this.$message.warning("请选择地址");
} else {
this.$emit("getLocation", this.locData);
this.$emit("closeDialog", false);
this.mapVisible = false;
}
},
},
};
</script>
<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>
地图组件为一个dialog弹窗,所以父组件通过v-if控制其开关,getLocation获取子组件传递过来的详细地址与坐标,closeDialog子组件弹窗关闭,父组件中也要设置showMap关闭,selectedValue来传递已选的详细地址或坐标(注:腾讯组件中的地址解析接口需要一个region字段,也可以通过该对象传递给子组件,此对象内所有值可以为null)。
本文参考博客:https://www.jianshu.com/p/a138889e8ea2