高德地图模糊搜索地址
注册账号并申请Key
-
首先,注册开发者账号,成为高德开放平台开发者
-
登陆之后,在进入「应用管理」 页面「创建新应用」
-
为应用添加 Key,「服务平台」一项请选择「 Web 端 ( JSAPI ) 」,设置域名白名单,可选(建议设置域名白名单)。
-
添加成功后,可获取到key值和安全密钥jscode ( 自2021年12月02日升级,升级之后所申请的 key 必须配备安全密钥 jscode 一起使用)
注意:此次升级不会影响之前已获得 key 的使用;升级之后的新增的key必须要配备安全密钥一起使用, (本次key升级新增安全密钥,是为了提升广大用户的对自己的key安全有效管理,降低明文传输被窃取的风险 。)
引入AMap
1、在index.html引入AMap
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode:'安全密钥jscode',
}
</script>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.6&key=[key值]&plugin=AMap.Autocomplete, AMap.Scale, AMap.OverView, AMap.ToolBar, AMap.MapType, AMap.PlaceSearch, AMap.Geolocation, AMap.Geocoder"></script>
<script src="https://webapi.amap.com/ui/1.0/main.js"></script>
2、在vue.config.js中找到configureWebpack,添加AMap和AMapUI
configureWebpack: config => {
// 生产环境相关配置
if (isProduction) {
config.plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
//生产环境自动删除console
compress: {
// drop_debugger: true,
// drop_console: true,
// pure_funcs: ['console.log']
}
},
sourceMap: false,
parallel: true
})
)
config.externals = {
'AMap': 'AMap',
'AMapUI':'AMapUI',
'echarts': 'echarts'
}
}else{
config.externals = {
'AMap': 'AMap',
'AMapUI':'AMapUI',
}
}
}
3、在需要调用地图的文件中导入AMap,就可以直接调用AMap的API 注意:
本例中用到了AMap.Autocomplete, AMap.Scale, AMap.OverView, AMap.ToolBar, AMap.MapType, AMap.PlaceSearch, AMap.Geolocation, AMap.Geocoder插件,如果要调用其他的plugin,如AMap.Driving,需要在index.html相应加载(多个plugin用逗号隔开):
<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.6&key=[key值]&plugin=AMap.Autocomplete, AMap.Scale, AMap.OverView, AMap.ToolBar, AMap.MapType, AMap.PlaceSearch, AMap.Geolocation, AMap.Geocoder"></script>
<template>
<div style="margin: 50px;width: 300px;">
<el-form ref="addForm" v-model="addForm" :rules="addRules">
<el-form-item label="上车地点:" prop="sname">
<el-input id="sname" v-model.trim="addForm.sname" type="text"
@input="placeAutoInput('sname')" @keyup.delete.native="deletePlace('sname')"
placeholder="请输入上车地点">
<i
class="el-icon-location-outline el-input__icon"
slot="suffix" title="上车地点">
</i>
</el-input>
<div v-show="snameMapShow" class="map-wrapper">
<div>
<el-button type="text" size="mini" @click.stop="snameMapShow = false">收起<i class="el-icon-caret-top"></i></el-button>
</div>
<div id="sNameMap" class="map-self"></div></div>
</el-form-item>
</el-form>
<!--地址模糊搜索子组件-->
<place-search class="place-wrap"
ref="placeSearch"
v-if="resultVisible"
:result="result"
:left="offsetLeft"
:top="offsetTop"
:width="inputWidth"
:height="inputHeight"
@getLocation="getPlaceLocation"></place-search>
</div>
</template>
<script>
import AMap from 'AMap'
import AMapUI from 'AMapUI'
import placeSearch from './placeSearch'
export default {
data() {
let validatePlace = (rules, value, callback) => {
if (rules.field === 'sname') {
if (value === '') {
callback(new Error('请输入上车地点'));
} else {
if (!this.addForm.slat || this.addForm.slat === 0) {
callback(new Error('请搜索并选择有经纬度的地点'));
} else {
callback();
}
}
}
};
return {
addForm: {
sname: '', // 上车地点
slat: 0, // 上车地点纬度
slon: 0 // 上车地点经度
},
addRules: {
sname: [{required: true, validator: validatePlace, trigger: 'change'}]
},
inputId: '', // 地址搜索input对应的id
result: [], // 地址搜索结果
resultVisible: false, // 地址搜索结果显示标识
inputWidth: 0, // 搜索框宽度
inputHeight: 0, // 搜索框高度
offsetLeft: 0, // 搜索框的左偏移值
offsetTop: 0, // 搜索框的上偏移值
snameMap: null, // 上车地点地图选址
snameMapShow: false, // 上车地点地图选址显示
}
},
components: {
'place-search': placeSearch
},
mounted() {
// document添加onclick监听,点击时隐藏地址下拉浮窗
document.addEventListener("click", this.hidePlaces, false);
// window添加onresize监听,当改变窗口大小时同时修改地址下拉浮窗的位置
window.addEventListener("resize", this.changePos, false)
},
methods: {
placeAutoInput(inputId) {
let currentDom = document.getElementById(inputId);// 获取input对象
let keywords = currentDom.value;
if(keywords.trim().length === 0) {
this.resultVisible = false;
}
AMap.plugin('AMap.Autocomplete', () => {
// 实例化Autocomplete
let autoOptions = {
city: '全国'
};
let autoComplete = new AMap.Autocomplete(autoOptions); // 初始化autocomplete
// 开始搜索
autoComplete.search(keywords, (status, result) => {
// 搜索成功时,result便是对应的匹配数据
if(result.info === 'OK') {
let sizeObj = currentDom.getBoundingClientRect(); // 取得元素距离窗口的绝对位置
this.inputWidth = currentDom.clientWidth;// input的宽度
this.inputHeight = currentDom.clientHeight + 2;// input的高度,2是上下border的宽
// input元素相对于页面的绝对位置 = 元素相对于窗口的绝对位置
this.offsetTop = sizeObj.top + this.inputHeight; // 距顶部
this.offsetLeft = sizeObj.left; // 距左侧
this.result = result.tips;
this.inputId = inputId;
this.resultVisible = true;
}
})
})
},
// 隐藏搜索地址下拉框
hidePlaces(event) {
let target = event.target;
// 排除点击地址搜索下拉框
if(target.classList.contains("address")) {
return;
}
this.resultVisible = false;
},
// 修改搜索地址下拉框的位置
changePos() {
if(this.inputId && this.$refs['placeSearch']) {
let currentDom = document.getElementById(this.inputId);
let sizeObj = currentDom.getBoundingClientRect(); // 取得元素距离窗口的绝对位置
// 元素相对于页面的绝对位置 = 元素相对于窗口的绝对位置
let inputWidth = currentDom.clientWidth;// input的宽度
let inputHeight = currentDom.clientHeight + 2;// input的高度,2是上下border的宽
let offsetTop = sizeObj.top + inputHeight; // 距顶部
let offsetLeft = sizeObj.left; // 距左侧
this.$refs['placeSearch'].changePost(offsetLeft, offsetTop, inputWidth, inputHeight);
}
},
// 获取子组件返回的位置信息
getPlaceLocation(item) {
if(item) {
this.resultVisible = false;
if(item.location && item.location.getLat()) {
this.pickAddress(this.inputId, item.location.getLng(), item.location.getLat());
this.$refs.addForm.validateField(this.inputId);
} else {
this.geocoder(item.name, this.inputId);
}
}
},
// 地图选址
pickAddress(inputId, lon, lat) {
if(inputId === "sname") {
this.snameMapShow = true;
AMapUI.loadUI(['misc/PositionPicker'], (PositionPicker) => {
this.snameMap = new AMap.Map('sNameMap', {
zoom: 16,
scrollWheel: false,
center: [lon,lat]
});
let positionPicker = new PositionPicker({
mode: 'dragMap',
map: this.snameMap
});
positionPicker.on('success', (positionResult) => {
this.addForm.slat = positionResult.position.lat;
this.addForm.slon = positionResult.position.lng;
this.addForm.sname = positionResult.address;
});
positionPicker.on('fail', () => {
this.$message.error("地址选取失败");
});
positionPicker.start();
this.snameMap.addControl(new AMap.ToolBar({
liteStyle: true
}));
});
}
},
// 地理编码
geocoder(keyword, inputValue) {
let geocoder = new AMap.Geocoder({
//city: "010", //城市,默认:“全国”
radius: 1000 //范围,默认:500
});
//地理编码,返回地理编码结果
geocoder.getLocation(keyword, (status, result) => {
if (status === 'complete' && result.info === 'OK') {
let geocode = result.geocodes;
if (geocode && geocode.length > 0) {
if (inputValue === "sname") {
this.addForm.slat = geocode[0].location.getLat();
this.addForm.slon = geocode[0].location.getLng();
this.addForm.sname = keyword;
// 若是地理编码返回的粗略经纬度数据不须要在地图上显示,就不须要调用地图选址,且要隐藏地图
// this.pickAddress("sname", geocode[0].location.getLng(), geocode[0].location.getLat());
this.snameMapShow = false;
this.$refs.addForm.validateField("sname");
}
}
}
});
},
// 作删除操做时还原经纬度并验证字段
deletePlace(inputId) {
if (inputId === "sname") {
this.addForm.slat = 0;
this.addForm.slon = 0;
this.$refs.addForm.validateField("sname");
}
}
},
beforeDestroy() {
document.removeEventListener("click", this.hidePlaces, false);
}
}
</script>
<style lang="scss" scoped>
.map-wrapper .map-self{
height: 150px;
}
</style>
子组件代码
<template>
<div class="result-list-wrapper" ref="resultWrapper">
<ul class="result-list address" :data="result">
<li class="result-item address"
v-for="(item, index) in result"
:key="item.index"
@click="setLocation(item)"
ref="resultItem">
<!-- <p class="result-name address" :class="{'active': index === activeIndex}">{{item.name}}</p> -->
<template v-if="item.address instanceof Array"><p class="result-adress address" :class="{'active': index === activeIndex}">{{item.name + item.district}}</p></template>
<template v-else><p class="result-adress address" :class="{'active': index === activeIndex}">{{item.name + item.address}}</p></template>
</li>
</ul>
</div>
</template>
<script type="text/ecmascript-6">
export default {
props: {
result: {
type: Array,
default: null
},
left: { // 输入框的offsetLeft
type: Number,
default: 0
},
top: { // 输入框的offsetTop
type: Number,
default: 0
},
width: { // 输入框的宽
type: Number,
default: 0
},
height: { // 输入框的高
type: Number,
default: 0
}
},
data() {
return {
activeIndex: 0 // 激活项
}
},
methods: {
// 选择下拉的地址
setLocation(item) {
this.$emit('getLocation', item)
},
// 初始化地址搜索下拉框位置
initPos() {
let dom = this.$refs['resultWrapper'];
let body = document.getElementsByTagName("body");
if(body) {
body[0].appendChild(dom);
let clientHeight = document.documentElement.clientHeight;
let wrapHeight = 0;
if(this.result && this.result.length>5) {
wrapHeight = 250;
} else if(this.result && this.result.length<=5) {
wrapHeight = this.result.length * 50;
}
if(clientHeight - this.top < wrapHeight) {
// 若是div高度超出底部,div往上移(减去div高度+input高度)
dom.style.top = this.top - wrapHeight - this.height + 'px';
} else {
dom.style.top = this.top + 'px';
}
dom.style.left = this.left + 'px';
dom.style.width = this.width + 'px'
}
},
// 窗口resize时改变下拉框的位置
changePost(left, top, width, height) {
let dom = this.$refs['resultWrapper'];
let clientHeight = document.documentElement.clientHeight;
let wrapHeight = 0;
if(this.result && this.result.length>5) {
wrapHeight = 250;
} else if(this.result && this.result.length<=5) {
wrapHeight = this.result.length * 50;
}
if(clientHeight - top < wrapHeight) {
// 若是div高度超出底部,div往上移(减去div高度+input高度)
dom.style.top = top - wrapHeight - height + 'px';
} else {
dom.style.top = top + 'px';
}
dom.style.left = left + 'px';
dom.style.width = width + 'px'
},
// 监听键盘上下方向键并激活当前选项
keydownSelect(event) {
let e = event || window.event || arguments.callee.caller.arguments[0];
if(e && e.keyCode === 38){//上
if(this.$refs['resultWrapper']) {
let items = this.$refs['resultWrapper'].querySelectorAll(".result-item");
if(items && items.length>0) {
this.activeIndex--;
// 滚动条往上滚动
if(this.activeIndex < 5) {
this.$refs['resultWrapper'].scrollTop = 0
}
if(this.activeIndex === 5) {
this.$refs['resultWrapper'].scrollTop = 250
}
if(this.activeIndex === -1) {
this.activeIndex = 0;
}
}
}
} else if(e && e.keyCode === 40) {//下
if(this.$refs['resultWrapper']) {
let items = this.$refs['resultWrapper'].querySelectorAll(".result-item");
if(items && items.length>0) {
this.activeIndex++;
// 滚动条往下滚动
if(this.activeIndex === 5) {
this.$refs['resultWrapper'].scrollTop = 250
}
if(this.activeIndex === 9) { // 防止最后一条数据显示不全
this.$refs['resultWrapper'].scrollTop = 300
}
if(this.activeIndex === items.length) {
this.activeIndex = 0;
this.$refs['resultWrapper'].scrollTop = 0
}
}
}
} else if(e && e.keyCode === 13) { // 监听回车事件,并获取当前选中的地址的经纬度等信息
if(this.result && this.result.length > this.activeIndex) {
this.setLocation(this.result[this.activeIndex]);
}
}
}
},
mounted() {
this.initPos();
document.addEventListener("keydown", this.keydownSelect, false);
},
beforeDestroy() {
document.removeEventListener("keydown", this.keydownSelect, false);
}
}
</script>
<style lang="scss" scoped>
.result-list-wrapper{
position: absolute;
max-height: 250px;
overflow: auto;
z-index: 9999;
border: 1px solid #ccc;
background-color: #fff;
.result-list{
.result-item{
padding: 5px;
color: #666;
border-bottom: 1px solid #ccc;
&:hover{
background-color: #f5f5f5;
cursor: pointer;
}
&:last-child{
border-bottom: none;
}
.result-name{
font-size: 12px;
margin-bottom: 0.5rem;
&.active{
color: #259bff;
}
}
.result-adress{
font-size: 12px;
colorL: #bbb;
}
}
}
}
</style>
效果如下:
参考博客: