高德地图模糊搜索地址

703 阅读1分钟

高德地图模糊搜索地址

注册账号并申请Key

  1. 首先,注册开发者账号,成为高德开放平台开发者

  2. 登陆之后,在进入「应用管理」 页面「创建新应用」

  3. 为应用添加 Key,「服务平台」一项请选择「 Web 端 ( JSAPI ) 」,设置域名白名单,可选(建议设置域名白名单)。

  4. 添加成功后,可获取到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>

效果如下:

微信图片_20221026100044.png

参考博客:

  1. lbs.amap.com/api/javascr…
  2. www.wjhsh.net/yeqrblog-p-…