前言✨
技术栈:
写法:vue2 + @vue/composition-api ≈ vue3
ui库:element 2.14
最终完成结果:
1.弹窗内嵌入高德地图,支持点击获取省市县名称、code、经纬度、具体地址,并插入标记
2.支持搜索并支持输入提示,点击直接跳转定位到选中地址,并获取以上信息。
开发前准备:
1.首先,注册开发者账号,成为高德开放平台开发者
2.登陆之后,在进入应用管理页面,创建新应用,为应用添加 Key和安全密钥lbs.amap.com/api/javascr…
高德地图开发者平台地址:
安装依赖
1.安装高德地图loader
$ npm i @amap/amap-jsapi-loader
2.安装Vue3的shallowRef方法
使用 shallowRef 进行非深度监听,因为在 Vue3 所使用的 Proxy 拦截操作会改变 JSAPI 原生对象,所以此处需要区别 Vue2 使用方式对地图对象行非深度监听,否则会出现问题,建议 JS API 相关对象采用非响应式的普通对象来存储。
& npm i @vue/reactivity
vue3中在setup函数中声明map对象
setup(){
const map = shallowRef(null);
return{
map,
}
},
如果是vue2就不用安装了直接声明即可,如下
data(){
return{
map:null,
}
},
弹窗组件代码
CommonDialog组件是基于el-dialog封装的,直接改成el-dailog即可
// MapContainer.vue
<template>
<CommonDialog
:visible.sync="visible"
@confirm="confirm"
width="900px"
@handleClose="handleClose"
>
<div v-loading="loading" class="map-dialog-warp">
<div id="container"></div>
<div class="info-card">
<div class="tip">左击获取位置信息:</div>
<div v-if="lng">{{ lng }}, {{ lat }}</div>
<div>{{ addressLabel }}</div>
</div>
<div class="info-card search-card">
<div class="tip">地址搜索:</div>
<el-select
v-model="keyword"
filterable
clearable
placeholder="请输入地址"
size="small"
:filter-method="searchInput"
@clear="clearSelect"
>
<el-option
v-for="(item, index) in searchList"
:key="index"
:label="item.name"
:value="item.id"
@click.native="selectClick(item)"
>
</el-option>
</el-select>
</div>
</div>
</CommonDialog>
</template>
<script>
import {
defineComponent,
onMounted,
ref,
reactive,
toRefs,
} from "@vue/composition-api";
import AMapLoader from "@amap/amap-jsapi-loader";
import { shallowRef } from "@vue/reactivity";
import { Message } from "element-ui";
export default defineComponent({
name: "MapContainer",
props: {
visible: {
type: Boolean,
default: false,
},
},
setup(props, context) {
onMounted(() => {
initMap();
});
const keyword = ref(""); // 搜索关键词
const searchList = ref([]);// 搜索提示下拉列表
const loading = ref(false);
const map = shallowRef(null);// map示例
const marker = ref(null);// 标注
// 经纬度
const lnglat = reactive({
lng: "",
lat: "",
});
// 地址信息 省、市、区、详情
const addressInfo = reactive({
addressLabel: "",
provinceCode: "",
cityCode: "",
districtCode: "",
});
// 初始化地图
const initMap = () => {
loading.value = true;
AMapLoader.load({
key: "xxxxxxx", // 开发者Key
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugin: [
"AMap.Scale", // 地图比例尺插件
"AMap.AutoComplete", // 输入提示插件
"AMap.Geolocation", // 定位插件,整合了浏览器定位、精确IP定位、sdk辅助定位多种手段
"AMap.Geocoder", // 地址编码服务(地理编码和逆地理编码)
"AMap.Marker", // 点标记插件
"AMap.DistrictSearch", // 地区查询插件
],
})
.then((AMap) => {
map.value = new AMap.Map("container", {
//设置地图容器id
viewMode: "3D", //是否为3D地图模式
zoom: 10, //初始化地图级别
// resizeEnable: true,// 默认定位当前城市
});
marker.value = new AMap.Marker({
map: map.value,
cursor: "move",
raiseOnDrag: true,
title: "中心",
});
autocomplete(AMap);
getGeolocation();
mapClick();
loading.value = false;
})
.catch((e) => {
loading.value = false;
});
};
// 获取当前定位和地址信息(pc端会优先获取ip定位所以不准确)
const getGeolocation = () => {
let geolocation;
map.value.plugin(["AMap.Geolocation"], function () {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true, // 是否使用高精度定位,默认:true
timeout: 10000, // 超过10秒后停止定位,默认:无穷大
maximumAge: 0, // 定位结果缓存0毫秒,默认:0
convert: true, // 自动偏移坐标,偏移后的坐标为高德坐标,默认:true
showButton: true, // 显示定位按钮,默认:true
buttonPosition: "LB", // 定位按钮停靠位置,默认:'LB',左下角
buttonOffset: new AMap.Pixel(10, 20), // 定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
showMarker: true, // 定位成功后在定位到的位置显示点标记,默认:true
showCircle: true, // 定位成功后用圆圈表示定位精度范围,默认:true
panToLocation: true, // 定位成功后将定位到的位置作为地图中心点,默认:true
zoomToAccuracy: true, // 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
});
map.value.addControl(geolocation);
// 初始化当前信息
geolocation.getCurrentPosition(function (status, result) {
if (status == "complete") {
lnglat.lng = result.position.lng;
lnglat.lat = result.position.lat;
getGeocoder();
}
});
});
};
// 地图点击获取信息
const mapClick = () => {
map.value.on("click", function (e) {
lnglat.lng = e.lnglat.getLng();
lnglat.lat = e.lnglat.getLat();
getGeocoder();
// 添加标记
marker.value.setPosition([lnglat.lng, lnglat.lat]);
});
};
// 获取地址信息
const getGeocoder = () => {
map.value.plugin(["AMap.Geocoder"], function () {
let geocoder = new AMap.Geocoder({
city: "全国", //默认:全国
radius: 500, //范围,默认:500
extensions: "all",
});
geocoder.getAddress(lnglat, function (status, result) {
if (status === "complete" && result.regeocode) {
addressInfo.addressLabel = result.regeocode.formattedAddress;
getCityCode("province", result.regeocode.addressComponent.province); // 省级
getCityCode("district", result.regeocode.addressComponent.district); // 区级、县级
// 判断是否是直辖市
if (result.regeocode.addressComponent.city) {
getCityCode("city", result.regeocode.addressComponent.city); // 市级
} else {
// 特殊处理直辖市
getCityCode("only", result.regeocode.addressComponent.province); // 市级
}
} else {
Message.error("地址超出范围");
}
});
});
};
// 获取省市县编码
const getCityCode = (level, name) => {
map.value.plugin("AMap.DistrictSearch", function () {
let districtSearch = new AMap.DistrictSearch({
level: level, // 关键字对应的行政区级别
subdistrict: 1, // 显示下级行政区级数,1表示返回下一级行政区
});
// 搜索所有省/直辖市信息
districtSearch.search(name, function (status, result) {
try {
if (status == "complete") {
if (level === "province") {
addressInfo.provinceCode = result.districtList[0].adcode;
} else if (level === "city") {
addressInfo.cityCode = result.districtList[0].adcode;
} else if (level === "district") {
addressInfo.districtCode = result.districtList[0].adcode;
} else if (level === "only") {
addressInfo.cityCode =
result.districtList[0].districtList[0].adcode;
}
}
} catch (error) {
console.error(error);
}
});
});
};
// 搜索提示
const autoComplete = ref(null);
const autocomplete = (AMap) => {
map.value.plugin("AMap.AutoComplete", function () {
const autoOptions = {
city: "全国",
input: "tipinput",
};
autoComplete.value = new AMap.AutoComplete(autoOptions);
});
};
// 地址搜索提示
const searchInput = (val) => {
if (val) {
keyword.value = val;
autoComplete.value.search(keyword.value, function (status, result) {
if (status === "complete") {
searchList.value = result.tips;
}
});
} else {
searchList.value = [];
}
};
// 选中下拉框
const selectClick = (item) => {
try {
if (item.location) {
lnglat.lng = item.location.lng;
lnglat.lat = item.location.lat;
getGeocoder();
marker.value.setPosition([item.location.lng, item.location.lat]);
map.value.setFitView(marker.value);
} else {
Message.error("请输入准确地址");
}
} catch (error) {}
};
// 清除输入框
const clearSelect = () => {
keyword.value = "";
searchList.value = [];
};
// 确定,组件返回数据
const confirm = () => {
if (!addressInfo.addressLabel) {
Message.error("请选择地址");
return;
}
const info = {
areaArr: [
addressInfo.provinceCode,
addressInfo.cityCode,
addressInfo.districtCode,
],
addressLabel: addressInfo.addressLabel, // 详细地址
lng: lnglat.lng, // 经度
lat: lnglat.lat, // 维度
};
context.emit("returnAddress", info);
handleClose();
};
// 取消
const handleClose = () => {
context.emit("update:visible", false);
};
return {
map,
confirm,
handleClose,
...toRefs(lnglat),
loading,
...toRefs(addressInfo),
keyword,
searchList,
searchInput,
clearSelect,
selectClick,
};
},
});
</script>
<style lang="less" scoped>
.map-dialog-warp {
position: relative;
#container {
padding: 0px;
width: 100%;
height: 500px;
}
.info-card {
position: absolute;
top: 10px;
left: 10px;
background-color: #fff;
background-clip: border-box;
border-radius: 4px;
width: 200px;
border-width: 0;
box-shadow: 0 2px 6px 0 rgb(114 124 245 / 50%);
padding: 10px;
opacity: 0.9;
color: #333;
.tip {
font-weight: bold;
}
div {
margin-bottom: 5px;
&:last-child {
margin-bottom: 0;
}
}
}
.search-card {
right: 10px !important;
left: auto !important;
opacity: 1;
}
}
</style>
组件使用
returnAddress是组件返回的信息
<MapContainer
v-if="visible"
:visible.sync="visible"
@returnAddress="returnAddress"
></MapContainer>
踩坑
1.AMap.Geolocation定位失败
由于Chrome、IOS10等已不再支持非安全域的浏览器定位请求,为保证定位成功率和精度,请尽快升级您的站点到HTTPS。