Vue3 + Element-Plus 对接高德地图实现搜索提示选址、点击地图选址、自我定位功能(最新)

news2024/11/26 10:00:32

Vue3 + Element-Plus 对接高德地图实现搜索提示选址、点击地图选址、自我定位功能(最新)

  • 1、效果展示
  • 2、实现代码
    • 2.1 GaoDeMap.vue
    • 2.2 SystemDialog.vue
    • 2.3 UnusedList.vue.vue

1、效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、实现代码

2.1 GaoDeMap.vue


<template>
  <div style="width: 100%;">
    <div class="search-box">
      <el-select v-model="address" clearable placeholder="请输入位置关键词" style="width: 240px;" :remote-method="remoteMethod"
        filterable remote @change="currentSelect" class="one-text" size="default">
        <el-option v-for="(item, index) in areaList" :key="index" :label="item.district + item.name"
          :value="item.district + item.name">
          <span>{{ item.district }}</span> <span>{{ item.name }}</span>
        </el-option>
      </el-select>

      <el-button type="success" v-if="addressGet" class="address">{{ addressGet.split(",")[0] }}</el-button>
    </div>
    <div id="container" class="map"></div>
  </div>
</template>

<script setup>
import { reactive, ref, toRefs, onMounted, nextTick, defineProps, defineEmits } from 'vue';
import AMapLoader from "@amap/amap-jsapi-loader";
// 定义 props 和 emits
const emits = defineEmits(['address']);
const props = defineProps({
  addressClick: Function,
})
onMounted(() => {
  window._AMapSecurityConfig = {
    securityJsCode: '你的安全密钥', // https://console.amap.com/dev/key/app   绑定的服务为 Web端
  }
  initMap()
})
const addressGet = ref("")

const state = reactive({
  map: null,
  placeSearch: null,
  autoComplete: null,
  marker: null,
  form: {
    address: '',
    lng: '',
    lat: '',
  },
  areaList: [],
  address: ''
})

const { areaList, address } = toRefs(state)
function initMap(arr) {
  AMapLoader.load({
    key: "安全密钥左边的key的值",	// https://console.amap.com/dev/key/app   绑定的服务为 Web端
    version: "2.0",
    plugins: ["AMap.ToolBar", "AMap.ControlBar", 'AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.Geocoder', 'AMap.Marker', 'AMap.Geolocation'],
  }).then((AMap) => {
    state.map = new AMap.Map('container', {
      viewMode: "3D",  //  是否为3D地图模式
      zoom: 15,
      center: arr,
      resizeEnable: true
    });
    // 地图放大缩小插件
    let toolBar = new AMap.ToolBar({
      position: {
        top: '120px',
        right: '51px'
      }
    })
    // 3D地图插件
    let controlBar = new AMap.ControlBar({
      position: {
        top: '20px',
        right: '20px',
      },
    });

    state.geoCoder = new AMap.Geocoder({
      city: '010', //城市设为北京,默认:“全国”
      radius: 1000 //范围,默认:500
    });

    // 正向地理编码
    state.geocoder = new AMap.Geocoder({
      city: state.address
    })
    // 定位
    let geolocation = new AMap.Geolocation({
      enableHighAccuracy: true, // 是否使用高精度定位,默认:true
      timeout: 10000, // 超过10秒后停止定位,默认:5s
      position: {
        top: '300px',
        right: '30px',
      }, // 定位按钮的停靠位置
      buttonOffset: new AMap.Pixel(10, 20), // 定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
      zoomToAccuracy: true, // 定位成功后是否自动调整地图视野到定位点
    });
    // 监听定位按钮的点击事件
    geolocation.on('complete', (result) => {
      console.log('定位成功:', result);
      // 处理定位成功的逻辑
      console.log(result.position.lng, result.position.lat);

      // 使用高德地图 JavaScript API 的坐标转换服务将 WGS84 坐标转换为 GCJ02 坐标
      AMap.convertFrom([result.position.lng, result.position.lat], 'gps', (status, result) => {
        if (status === 'complete' && result.locations.length > 0) {
          const gcj02Lng = result.locations[0].lng;
          const gcj02Lat = result.locations[0].lat;
          // 解析定位结果为中文地址
          let lnglat = [gcj02Lng, gcj02Lat];
          console.log(gcj02Lng, gcj02Lat);
          state.geoCoder.getAddress(lnglat, (status, addressResult) => {
            if (status === 'complete' && addressResult.regeocode) {
              let formattedAddress = addressResult.regeocode.formattedAddress;
              console.log('解析后的地址:', formattedAddress);
              // 在这里处理解析后的地址
              addressGet.value = formattedAddress;
              state.address = formattedAddress;

              // 返回给父组件的值
              emits("address", `${formattedAddress},${gcj02Lng},${gcj02Lat}`);
            }
          });
          // 这里可以继续处理转换后的坐标数据,例如显示标记点等
        } else {
          console.error('Coordinate conversion failed.');
        }
      });
    });

    geolocation.on('error', (error) => {
      console.error('定位失败:', error);
      // 处理定位失败的逻辑
    });

    state.autoComplete = new AMap.AutoComplete({ city: '全国' });
    state.map.setDefaultCursor("pointer"); // 使用CSS默认样式定义地图上的鼠标样式
    state.map.on('click', (e) => { // 点击地图事件
      if (!e && !e.lnglat) {
        return
      }
      state.form.lng = e.lnglat.lng
      state.form.lat = e.lnglat.lat
      removeMarker() // 先删除地图上标记点
      setMapMarker() // 在添加新的标记点
    })
    state.map.addControl(toolBar);   // 添加右上角的放大缩小
    state.map.addControl(controlBar);   // 添加右上角的放大缩小
    state.map.addControl(geolocation);   // 添加右上角的放大缩小
    // 监听定位成功事件
  }).catch((e) => {
    console.error(e);  //加载错误提示
  }).finally(() => {
    removeMarker()
    setMapMarker()
  })

}

function setMapMarker() {
  if (state.form.lng == '' && state.form.lat == '') {
    return
  }
  state.map.setFitView()
  state.marker = new AMap.Marker({
    map: state.map,
    position: [state.form.lng, state.form.lat],
  })
  toGetAddress()
  state.map.setFitView()
  state.map.add(state.marker)
}

function removeMarker() {
  if (state.marker) {
    state.map.remove(state.marker)
  }
}

function toGetAddress() {
  let lnglat = [state.form.lng, state.form.lat]
  state.geoCoder.getAddress(lnglat, (status, result) => {
    if (status === 'complete' && result.regeocode) {
      props.addressClick(result, state.form.lng, state.form.lat) // 返回位置信息以及经纬度
      addressGet.value = `${result.regeocode.formattedAddress},${state.form.lng},${state.form.lat}`
    }
  })
}

function remoteMethod(query) {
  if (query !== '') {
    setTimeout(() => {
      state.autoComplete.search(query, (status, result) => {
        state.areaList = result.tips
      })
    }, 500)
  } else {
    state.areaList = []
  }
}
function currentSelect(val) {
  if (!val) {
    return
  }
  toGetCoordinate(val)
}

function toGetCoordinate(address) {
  state.geocoder.getLocation(address, function (status, result) {
    if (status === 'complete' && result.info === 'OK') {
      initMap([result.geocodes[0].location.lng, result.geocodes[0].location.lat])
      state.form.lng = result.geocodes[0].location.lng
      state.form.lat = result.geocodes[0].location.lat
      state.form.address = `${result.geocodes[0].formattedAddress}`

      // 返回给父组件的值
      emits("address", `${result.geocodes[0].formattedAddress},${result.geocodes[0].location.lng},${result.geocodes[0].location.lat}`);
    }
  })
  nextTick(function () {
    removeMarker()
    setMapMarker()
  })
}

function fixed(lng, lat) {
  initMap([lng, lat])
  state.form.lng = lng
  state.form.lat = lat
}


// 暴露方法
defineExpose({
  fixed,
  toGetCoordinate
});
</script>



<style scoped lang="scss">
.map {
  width: 100%;
  height: 400px;
  padding-top: 20px;
}

#container {
  margin-top: 20px;
  border-radius: 5px;
}

.search-box {
  display: flex;

}

.address {
  margin-left: 20px;
}
</style>

2.2 SystemDialog.vue

<!--
 * @Date: 2024-03-25 16:55:53
 * @LastEditors: zhong
 * @LastEditTime: 2024-04-18 11:21:23
 * @FilePath: \app-admin\src\components\SystemDialog\SystemDialog.vue
-->
<template>
    <div>
        <el-dialog :model-value="props.visible" :title="props.title" :width="props.width + 'px'"
            :height="props.height + 'px'" :before-close="onClose" :close-on-click-modal="false">
            <!-- 内容显示区 -->
            <div class="container" :style="{ height: height + 'px' }">
                <slot name="content"></slot>
            </div>
            <template #footer>
                <div class="dialog-footer">
                    <el-button @click="onClose" type="danger">取消</el-button>
                    <el-button type="success" @click="onConfirm">
                        确认
                    </el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
// 定义弹窗数据类型
interface dialogProps {
    title: string,
    visible: boolean,
    width: number,
    height: number
}

// 接收父组件传递的参数
const props = withDefaults(defineProps<dialogProps>(), {
    title: "新增",
    visible: false,
    width: 630,
    height: 280
})
// 注册事件
const emit = defineEmits(["onClose", "onConfirm"])

// 关闭事件
const onClose = () => {
    emit("onClose");
}
// 提交事件
const onConfirm = () => {
    emit("onConfirm");
}
</script>


<style lang="scss" scoped>
.container {
    overflow-x: inherit;
    overflow-y: auto;
}

:deep(.el-dialog) {
    padding: 0;
    border-top-left-radius: 7px !important;
    border-top-right-radius: 7px !important;

    .el-dialog__header {
        margin-right: 0px;
        padding-top: 10px;
        border-top-left-radius: 7px !important;
        border-top-right-radius: 7px !important;
        background-color: #009688 !important;

        .el-dialog__title {
            padding: 16px;
            color: #FFF;
            font-size: 16px;
            font-weight: 600;
        }
    }

    .el-dialog__headerbtn {
        .el-dialog__close {
            color: #FFF;
        }
    }

    .el-dialog__body {
        padding: 10px;
    }

    .el-dialog__footer {
        border-top: 1px solid #e8eaec !important;
        padding: 10px;
    }
}
</style>

2.3 UnusedList.vue.vue

<template>
    <SystemDialog :title="name" :visible="mapOpen" :height="400" :width="800" @on-close="closeAddress" @on-confirm="commitAddress">
        <template v-slot:content>
              <el-form-item label="宝贝位置">
                  <GaoDeMap :addressClick="addressClick" ref="mapRef" @address="getAddress"></GaoDeMap>
              </el-form-item>
          </template>
        </SystemDialog>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue';
import SystemDialog from '@/components/SystemDialog/SystemDialog.vue';
import GaoDeMap from '@/components/Map/GaoDeMap.vue'

const mapRef = ref('') as any
const name = ref("宝贝位置选取");
const mapOpen = ref(false)

const address = ref("");
const getAddress = (data: string) => {
    if (data != "") {
        address.value = data;
        addGoodParm.address = data;
    }
}

// 提交地图地址
const commitAddress = () => {
    // 提交地图地址
    addGoodParm.address = address.value;
    // 关闭地图
    mapOpen.value = false;
    console.log(address, addGoodParm.address);

}

// 关闭地图
const closeAddress = () => {
    addGoodParm.address = "";
    address.value = "";
    // 关闭地图
    mapOpen.value = false;
}
// 地图选位置
function addressClick(item: { regeocode: { addressComponent: { city: string; province: string; district: string; }; formattedAddress: string; }; }, lng: number, lat: number) {
    address.value = `${item.regeocode.formattedAddress}, ${lng}, ${lat}`;
}

onMounted(() => {
    // 这里传后台获取的经纬度
    mapRef.value.fixed(100.179253, 27.096143)
})
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1625955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于C++如何导出dll用于C++和C#的研究(内含有YOLOV5调用接口的制作完整代码)

文章目录 一、VS2019复现视频的易错点。二、[补充一个C接口的知识](https://www.bilibili.com/video/BV1tA411N7HB/?spm_id_from333.337.search-card.all.click&vd_sourcef99e21db912f182fe051b1b6b156e0e3)三、回归正题如何调用dll&#xff1f;前面第一个视频教会我们导出…

07 流量回放实现自动化回归测试

在本模块的前四讲里&#xff0c;我向你介绍了可以直接落地的、能够支撑百万并发的读服务的系统架构&#xff0c;包含懒加载缓存、全量缓存&#xff0c;以及数据同步等方案的技术细节。 基于上述方案及细节&#xff0c;你可以直接对你所负责的读服务进行架构升级&#xff0c;将…

SpringBoot引入Layui样式总是出现404

一般出现Layui样式文件如css&#xff0c;js404的错误 解决方案 &#xff08;1&#xff09;首先将其中的静态资源下载resources/static中 &#xff08;2&#xff09;在启动类中重写方法 package com.gq.booksystem;import org.mybatis.spring.annotation.MapperScan; import …

Java本地缓存技术选型(Guava Cache、Caffeine、EhCache)

前言 对一个java开发者而言&#xff0c;提到缓存&#xff0c;第一反应就是Redis。利用这类缓存足以解决大多数的性能问题了&#xff0c;我们也要知道&#xff0c;这种属于remote cache&#xff08;分布式缓存&#xff09;&#xff0c;应用的进程和缓存的进程通常分布在不同的服…

nvm下载的node没有npm

nvm下载的node没有npm 相信大家最近可能发现自己使用的nvm下载nodejs没有npm了。 会出现这种情况&#xff1a; C:\Users\89121>nvm install 15 Downloading node.js version 15.14.0 (64-bit)... Complete Downloading npm version 7.7.6... Download failed. Rolling Bac…

编写一个函数,该函数可以统计一个长度为2的字符串在另一个字符串中出现的次数。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 编写…

SpringBoot Aop使用篇

Getting Started SpringBoot AOP的实践 AOP相关的概念&#xff1a; Aspect&#xff08;切面&#xff09;&#xff1a; Aspect 声明类似于 Java 中的类声明&#xff0c;在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。就是抽离出来的逻辑类&#xff0c;比如日志、权限…

【Redis 开发】全局ID生成器

全局ID生成器 为了增加ID的安全性&#xff0c;我们可以不直接使用Redis自增的数值&#xff0c;而是拼接一些其他信息&#xff1a; ID的组成部分&#xff1a; 符号位:1bit,一直为0 时间戳:31bit&#xff0c;一秒为单位&#xff0c;可以使用69年 序列号:32bit&#xff0c;秒内…

深度学习系列64:数字人wav2lip详解

1. 整体流程 第一步&#xff0c;加载视频/图片和音频/tts。用melspectrogram将wav文件拆分成mel_chunks。 第二步&#xff0c;调用face_detect模型&#xff0c;给出人脸检测结果&#xff08;可以改造成从文件中读取&#xff09;&#xff0c;包装成4个数组batch&#xff1a;img…

一次违法网站的渗透经历

0x01 前言 在一次攻防演练中&#xff0c;我发现了一个有趣的渗透路径。在信息收集阶段&#xff0c;我注意到目标网站和用户资产网站共享相同的IP网段。这意味着它们可能在同一台服务器上托管&#xff0c;或者至少由同一家互联网服务提供商管理。这种情况为我们的渗透测试提供了…

MySQL数据库运维:运行监控及解决sql执行死锁问题

前言 在现代数据密集型应用程序的开发和部署中&#xff0c;MySQL数据库的运维是至关重要的环节之一。一个良好设计和维护的MySQL数据库系统可以确保数据的准确性、可靠性和高效的访问&#xff0c;从而支持业务的顺利运行。然而&#xff0c;随着业务规模的增长和复杂性增加&…

【threejs教程7】threejs聚光灯、摄影机灯和汽车运动效果

【图片完整效果代码位于文章末】 在上一篇文章中我们实现了汽车模型的加载&#xff0c;这篇文章主要讲如何让汽车看起来像在运动。同时列出聚光灯和摄像机灯光的加载方法。 查看上一篇&#x1f449;【threejs教程6】threejs加载glb模型文件&#xff08;小米su7&#xff09;&…

5分钟——测试搭建的springboot接口(二)

5分钟——测试搭建的springboot接口&#xff08;二&#xff09; 1. 查看数据库字段2. 测试getAll接口3. 测试add接口4. 测试update接口5. 测试deleteById接口 1. 查看数据库字段 2. 测试getAll接口 3. 测试add接口 4. 测试update接口 5. 测试deleteById接口

RocketMQ 消息重复消费

现象 触发消息后&#xff0c;在1s内收到了两次消息消费的日志。 消息消费日志重复&#xff0c;reconsumeTimes0&#xff0c;主机实例也不同&#xff0c;说明是同一条消息被消费了两次 分析 生产者发送消息的时候使用了重试机制&#xff0c;发送消息后由于网络原因没有收到MQ…

【vue,unapi】UniApp引入全局js实现全局方法,全局变量

创建一个全局文件utils.js export const baseUrl "https://www.baidu.com/"export const fn () > {console.log("demo"); } export const obj {baseUrl : "https://www.baidu.com/",demo(){console.log("demo2");} }第一种&#…

2024 OceanBase 开发者大会:OceanBase 4.3正式发布,打造近PB级实时分析数据库

4月20日&#xff0c;2024 OceanBase开发者大会盛大召开&#xff0c;吸引了50余位业界知名的数据库专家和爱好者&#xff0c;以及来自全国各地的近600名开发者齐聚一堂。他们围绕一体化、多模、TP与AP融合等前沿技术趋势展开深入讨论&#xff0c;分享场景探索的经验和最佳实践&a…

人事管理软件全面盘点:2024年十款热门推荐!

随着企业规模的扩大和业务的多元化&#xff0c;人事管理变得越来越复杂。为了帮助企业更有效地管理人力资源&#xff0c;市场上出现了一系列主流的人事管理软件。 本篇文章为您盘点十款2024年值得关注的人事管理软件&#xff1a;Zoho People、北森、i人事、薪人薪事、肯耐珂萨…

鸿蒙开发使用云数据库的坑

在使用华为官网提供的CloudDBZoneWrapper.js有个坑&#xff0c; CloudDBZoneWrapper.js需要使用两个包 const clouddb require(hw-agconnect/database-server/dist/index.js); const agconnect require(agconnect/common-server); const path require(path);/*配置区域 */…

Java后台开发的前置说明

1.知识点逻辑 一个部分 都是先挑重点知识点讲解 然后根据这些重点知识点去完成一个项目的开发 然后在到返回来解决这个部分其他细枝末节的知识点 2.软件开发的分工 我们大致可以将软件开发分成四块&#xff1a; 1.前端开发(比如开发电脑中的京东 htmlcssjavascript) 2.移动开…

HarmonyOS开发实战(黑马健康系列一:欢迎页)

系列文章目录 &#xff08;零&#xff09;鸿蒙HarmonyOS入门&#xff1a;如何配置环境&#xff0c;输出“Hello World“ &#xff08;一&#xff09;鸿蒙HarmonyOS开发基础 &#xff08;二&#xff09;鸿蒙HarmonyOS主力开发语言ArkTS-基本语法 &#xff08;三&#xff09;鸿蒙…