首页 > 其他分享 >vue3调用高德地图,实现地址,经纬度填写

vue3调用高德地图,实现地址,经纬度填写

时间:2023-12-22 11:23:57浏览次数:26  
标签:const 经纬度 point value AMap vue3 import ref 高德

父组件引用高德地图:

 1 <template>
 2   <div class="wrapper">
 3     <div class="box">
 4       <div class="form-box">
 5         <el-form
 6           label-position="top"
 7           :inline="true"
 8           :model="addDeviceForm"
 9           ref="addDeviceFormRef"
10           :rules="rules"
11         >
12           <el-form-item label="设备安装位置" prop="indatallAddress">
13             <el-input
14               @click="showMap"
15               :suffix-icon="MapLocation"
16               v-model="addDeviceForm.indatallAddress"
17             />
18           </el-form-item>
19           <el-form-item label="设备经度" prop="longitude">
20             <el-input-number
21               disabled
22               :controls="false"
23               v-model="addDeviceForm.longitude"
24               placeholder="请输入经度"
25             />
26           </el-form-item>
27           <el-form-item label="设备纬度" prop="latitude">
28             <el-input-number
29               disabled
30               :controls="false"
31               v-model="addDeviceForm.latitude"
32               placeholder="请输入纬度"
33             />
34           </el-form-item>
35         </el-form>
36       </div>
37     </div>
38   </div>
39   <!-- 地图组件 -->
40   <gao-amap
41     :isShow="amapVisible"
42     :defaultAddress="defaultAddress"
43     :defaultPoint="defaultPoint"
44     @getPosition="getPosition"
45   ></gao-amap>
46 </template>
47 
48 <script setup>
49 import { ref } from 'vue'
50 import { ElMessage } from 'element-plus'
51 import { useStore } from 'vuex'
52 import { MapLocation } from '@element-plus/icons-vue'
53 import GaoAmap from '@/components/Amap'
54 
55 const store = useStore()
56 
57 const addDeviceForm = ref({
58   indatallAddress: '',
59   longitude: null,
60   latitude: null,
61 })
62 
63 const amapVisible = ref(false)
64 const defaultAddress = ref('')
65 const defaultPoint = ref([])
66 const showMap = () => {
67   defaultAddress.value = addDeviceForm.value.indatallAddress
68   defaultPoint.value = [
69     addDeviceForm.value.longitude,
70     addDeviceForm.value.latitude
71   ]
72   amapVisible.value = true
73 }
74 const getPosition = (infoObj) => {
75   amapVisible.value = false
76   if (infoObj.address) {
77     addDeviceForm.value.indatallAddress = infoObj.address
78     addDeviceForm.value.longitude = infoObj.point[0]
79     addDeviceForm.value.latitude = infoObj.point[1]
80   }
81 }
82 
83 </script>

子组件封装高德地图:

<template>
  <el-dialog
    title="选择地点"
    width="740px"
    class="select-map-dialog"
    v-model="dialogShow"
    :close-on-click-modal="false"
    :modal-orgend-to-body="false"
    :append-to-body="true"
    @close="cancel"
  >
    <div class="amap-box">
      <el-input
        clearable
        id="keyword"
        v-model="tipInput"
        placeholder="关键词搜索"
        :prefix-icon="Search"
      ></el-input>
      <div id="custom-amap"></div>
      <div class="info-list" v-if="address">
        <div class="info-item">已选择地点:{{ address }}</div>
        <div class="info-item">地点经纬度:{{ point }}</div>
      </div>
    </div>
    <template #footer>
      <div class="dialog-footer">
        <button class="btn btn-deault" @click="handelSave">确定</button>
        <button class="btn btn-plain" @click="cancel">取消</button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, onUnmounted, watch, computed, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import AMapLoader from '@amap/amap-jsapi-loader'
import { Search } from '@element-plus/icons-vue'

const map = ref(null) // 地图对象
const address = ref('') // 地址
const point = ref([]) // 地址对应的经纬度信息

const marker = ref('') // 地图标记
const geocoder = ref('') // 地址解析(正向)

const tipInput = ref('') // 检索关键字
const autoComplete = ref(null)
const placeSearch = ref(null)

const props = defineProps({
  defaultAddress: {
    type: String,
    default: ''
  },
  defaultPoint: {
    type: Array
  },
  isShow: {
    type: Boolean,
    default: false
  }
})

const emits = defineEmits(['update:isShow', 'getPosition'])
const dialogShow = computed({
  get: () => props.isShow,
  set: (value) => {
    emits('update:isShow', value)
  }
})
watch(
  () => dialogShow,
  async () => {
    await nextTick()
    // 初始化地图页面
    initMap()
  },
  { deep: true },
  { immediate: true }
)

//   初始化地图页面
const initMap = () => {
  AMapLoader.load({
    key: 'c6f04d87fd44069967d71742e2507919', // 申请好的Web端开发者Key,首次调用 load 时必填
    version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    plugins: [
      'AMap.ToolBar',
      'AMap.Scale',
      'AMap.HawkEye',
      'AMap.MapType',
      'AMap.Geolocation',
      'AMap.AutoComplete',
      'AMap.PlaceSearch',
      'AMap.Geocoder'
    ] // 需要使用的的插件列表,如比例尺'AMap.Scale'等
  })
    .then((AMap) => {
      const tempCenter = props.defaultPoint[0]
        ? props.defaultPoint
        : [118.784136, 32.041806]
      map.value = new AMap.Map('custom-amap', {
        // 设置地图容器id
        viewMode: '2D', // 是否为3D地图模式
        zoom: 12, // 初始化地图级别
        showLabel: true, // 是否展示地图文字和 POI 信息。
        resizeEnable: true,
        center: tempCenter // 初始化地图中心点位置
      })
      // 如果父组件传入了有效值 回显一个icon
      if (props.defaultPoint.length > 0) {
        address.value = props.defaultAddress
        point.value = props.defaultPoint
        addMarker()
      }
      // 地图点击事件
      map.value.on('click', clickMapHandler)

      // 引入高德地图的空间绑定使用
      map.value.addControl(new AMap.Scale())
      map.value.addControl(new AMap.ToolBar())
      map.value.addControl(new AMap.HawkEye())
      map.value.addControl(new AMap.MapType())
      map.value.addControl(new AMap.Geolocation())

      // 搜索框自动完成类
      autoComplete.value = new AMap.AutoComplete({
        input: 'keyword' // input 为绑定输入提示功能的input的DOM ID
      })
      // 构造地点查询类
      placeSearch.value = new AMap.PlaceSearch({
        map: map.value
      })
      // 当选中某条搜索记录时触发
      autoComplete.value.on('select', selectHandler)
      // poi覆盖物点击事件
      placeSearch.value.on('markerClick', clickMarkerHandler)
    })
    .catch((e) => {
      console.log(e)
    })
}

const clickMapHandler = (e) => {
  const lng = e.lnglat.lng
  const lat = e.lnglat.lat
  point.value = [lng, lat]
  // 增加点标记
  addMarker()
  // 获取地址
  getAddress()
}

// 增加点标记
const addMarker = () => {
  // 清除其他icon
  if (marker.value) {
    marker.value.setMap(null)
    marker.value = null
  }
  // 重新渲染icon
  marker.value = new AMap.Marker({
    position: point.value, // icon经纬度
    offset: new AMap.Pixel(-13, -30), // icon中心点的偏移量
    icon: new AMap.Icon({
      image:
        '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',
      size: new AMap.Size(30, 40), // 图标大小
      imageSize: new AMap.Size(30, 40)
    })
  })
  marker.value.setMap(map.value) // 设置icon
}

// 将经纬度转换为地址
const getAddress = () => {
  // 获取地址,这里通过高德 SDK 完成。
  geocoder.value = new AMap.Geocoder()
  // 调用逆解析方法, 个人开发者调用量上限5000(次/日)
  geocoder.value.getAddress(point.value, (status, result) => {
    if (status === 'complete' && result.info === 'OK') {
      if (result && result.regeocode) {
        address.value = result.regeocode.formattedAddress
      }
    }
  })
}
// 当选中某条记录时会触发
const selectHandler = (e) => {
  placeSearch.value.setCity(e.poi.adcode)
  placeSearch.value.search(e.poi.name) // 关键字查询查询
}
// 点击poi覆盖物事件
const clickMarkerHandler = (e) => {
  point.value = [e.data.location.lng, e.data.location.lat]
  getAddress()
}

// 保存当前选择的地址,分发事件
const handelSave = () => {
  if (address.value && point.value.length && point.value.length > 0) {
    const infoObj = {
      address: address.value,
      point: point.value
    }
    tipInput.value = ''
    emits('getPosition', infoObj)
  } else {
    ElMessage.error('请选择地址获取经纬度')
  }
}
const cancel = () => {
  tipInput.value = ''
  emits('getPosition', {})
}

onUnmounted(() => {
  map.value && map.value.destroy()
})
</script>

<style lang="scss" scoped>
.amap-box {
  padding: 5px 0 0;
  #custom-amap {
    width: 700px;
    height: 400px;
    margin-top: 10px;
    border: 1px solid #ccc;
  }

  .input-with {
    width: 580px;
    z-index: 1;
  }

  .address {
    color: #373737;
  }
  .info-list {
    padding: 5px 5px 0;
    line-height: 24px;
  }
}
</style>

<style lang="scss">
.select-map-dialog {
  .el-dialog__body {
    padding-top: 3px;
    padding-bottom: 3px;
  }
}
.amap-sug-result {
  z-index: 2024;
}
</style>

 

 

 

<template>   <div class="wrapper">     <div class="box">       <div class="title">         <svg-icon           class="svg-icon-return-back"           icon="return-back1"           @click="returnBack"         ></svg-icon         >新建设备实体       </div>
      <div class="form-box">         <el-form           label-position="top"           :inline="true"           :model="addDeviceForm"           ref="addDeviceFormRef"           :rules="rules"         >           <el-form-item label="设备名称" prop="name">             <el-input               v-model="addDeviceForm.name"               placeholder="请输入设备名称"             />           </el-form-item>           <el-form-item label="设备类型:" prop="type">             <el-select               v-model="addDeviceForm.type"               placeholder="全部"               clearable             >               <el-option label="设备类型1" value="1" />               <el-option label="设备类型2" value="2" />             </el-select>           </el-form-item>           <el-form-item label="设备分组:">             <el-select               v-model="addDeviceForm.group"               placeholder="全部"               clearable             >               <el-option label="设备分组1" value="1" />               <el-option label="设备分组2" value="2" />             </el-select>           </el-form-item>           <el-form-item label="设备垂直分辨率(px)" prop="verticalResolution">             <el-input-number               v-model="addDeviceForm.verticalResolution"               :min="0"               :precision="2"               :step="0.01"               placeholder="请输入"               controls-position="right"             />           </el-form-item>           <el-form-item label="设备水平分辨率(px)" prop="levelResolution">             <el-input-number               v-model="addDeviceForm.levelResolution"               :min="0"               :precision="2"               :step="0.01"               placeholder="请输入"               controls-position="right"             />           </el-form-item>
          <el-form-item label="设备安装日期" prop="installDate">             <el-date-picker               v-model="addDeviceForm.installDate"               type="date"               placeholder="请选择安装日期"               format="YYYY-MM-DD"               value-format="YYYY-MM-DD"               clearable             />           </el-form-item>           <el-form-item label="设备安装位置" prop="indatallAddress">             <el-input               @click="showMap"               :suffix-icon="MapLocation"               v-model="addDeviceForm.indatallAddress"             />           </el-form-item>           <el-form-item label="设备经度" prop="longitude">             <el-input-number               disabled               :controls="false"               v-model="addDeviceForm.longitude"               placeholder="请输入经度"             />           </el-form-item>           <el-form-item label="设备纬度" prop="latitude">             <el-input-number               disabled               :controls="false"               v-model="addDeviceForm.latitude"               placeholder="请输入纬度"             />           </el-form-item>
          <el-form-item label="设备照片">             <el-upload               name="file"               :show-file-list="false"               class="avatar-uploader"               :headers="upload.headerToken"               :action="upload.imgAction"               :on-success="handleImgSuccess"               :before-upload="beforeImgUpload"             >               <el-image                 v-if="imageUrl"                 :src="imageUrl"                 class="avatar"                 :fit="fit"               />               <el-icon v-if="!imageUrl" class="avatar-uploader-icon"                 ><CirclePlus               /></el-icon>               <p v-if="!imageUrl" class="tip">请添加jpg/png/gif格式文件</p>             </el-upload>           </el-form-item>         </el-form>       </div>     </div>     <div class="btn-wrapper">       <span class="btn btn-deault" @click="onSubmit(addDeviceFormRef)">         提交       </span>       <span class="btn btn-plain" @click="onCancel(addDeviceFormRef)">         取消       </span>     </div>   </div>   <!-- 地图组件 -->   <gao-amap     :isShow="amapVisible"     :defaultAddress="defaultAddress"     :defaultPoint="defaultPoint"     @getPosition="getPosition"   ></gao-amap> </template>
<script setup> import { addLamp } from '@/api/light' import { ref } from 'vue' import { ElMessage } from 'element-plus' import { useStore } from 'vuex' import { MapLocation } from '@element-plus/icons-vue' import GaoAmap from '@/components/Amap'
const store = useStore() const emits = defineEmits(['closeEntityDevicePage'])
const addDeviceFormRef = ref() const imageUrl = ref('') const props = defineProps({   mode: {     type: String   } }) const addDeviceForm = ref({   name: '',   type: '',   group: '',
  verticalResolution: null,   levelResolution: null,
  indatallAddress: '',   longitude: null,   latitude: null,
  installDate: '' })
const rules = ref({   name: [     { required: true, message: '设备名称不能为空', trigger: 'blur' },     { min: 1, max: 16, message: '设备名称不允许超过16个字符' }   ],   type: [{ required: true, message: '设备类型不能为空', trigger: 'change' }],
  installDate: [     { required: true, message: '设备安装日期不能为空', trigger: 'change' }   ],   indatallAddress: [     { required: true, message: '安装地址不能为空', trigger: 'change' }   ] })
const upload = ref({   headerToken: {     Authorization: `Bearer ${store.getters.token}`   },   imgAction: '' }) // 自动上传 const beforeImgUpload = (rawFile) => {   const formatArr = ['image/png', 'image/jpg', 'image/jpeg', 'image/gif']   const isPic = formatArr.indexOf(rawFile.type.toLowerCase()) !== -1 // 是否为图片   if (!isPic) {     ElMessage.error('图片格式错误!')     return false   } else if (rawFile.size / 1024 / 1024 > 5) {     ElMessage.error('图片大小不能超过5MB!')     return false   }   return true }
const handleImgSuccess = (res) => {   imageUrl.value = res.data.url   ElMessage({     type: 'success',     message: '上传成功'   }) } const onSubmit = async (formEl) => {   if (!formEl) return   await formEl.validate(async (valid, fields) => {     if (valid) {       const res = await addLamp(addLightForm.value)       if (res.data.code === 500) {         ElMessage({           type: 'warning',           message: '这个灯杆已存在'         })         return       }       emits('closeEntityDevicePage')     } else {       console.log('error submit!', fields)     }   }) } const onCancel = () => {   emits('closeEntityDevicePage') } const amapVisible = ref(false) const defaultAddress = ref('') const defaultPoint = ref([]) const showMap = () => {   defaultAddress.value = addDeviceForm.value.indatallAddress   defaultPoint.value = [     addDeviceForm.value.longitude,     addDeviceForm.value.latitude   ]   amapVisible.value = true } const getPosition = (infoObj) => {   amapVisible.value = false   if (infoObj.address) {     addDeviceForm.value.indatallAddress = infoObj.address     addDeviceForm.value.longitude = infoObj.point[0]     addDeviceForm.value.latitude = infoObj.point[1]   } } const returnBack = () => {   emits('closeEntityDevicePage') } </script>
<style lang="scss" scoped> .wrapper {   width: 100%;   height: 100%; } .box {   width: 100%;   height: calc(100% - 60px);   background: #fff;   padding: 0 20px;   .title {     height: 60px;     line-height: 60px;     border-bottom: 1px solid #e9e9e9;   }   .form-box {     height: calc(100% - 120px);     overflow: auto;     padding-top: 15px;   } } .btn-wrapper {   position: fixed;   right: 0;   bottom: 0;   text-align: right;   width: calc(100% - 240px);   height: 60px;   background: #fff;   padding: 10px 20px; } </style>

标签:const,经纬度,point,value,AMap,vue3,import,ref,高德
From: https://www.cnblogs.com/guwufeiyang/p/17920881.html

相关文章

  • Vue3实现电商放大镜效果
    效果实现:功能拆解:左侧滑块跟随鼠标移动右则大图放大效果实现鼠标移入控制滑块和大图显示隐藏滑块跟随鼠标移动思路:获取到当前的鼠标在盒子内的相对位置(useMouseInElement),控制滑块跟随鼠标移动(left/top)获取鼠标相对位置控制滑块跟随移动有效移动范围内的计算逻辑......
  • vue3 + vant4 :form表单中,搭配 Popup 和 Field 实现vant-picker组件,设置默认值及默认选
    环境:vue3,vant4背景:Picker作为用于辅助表单填写,搭配Popup和Field。页面需要给picker设置默认值,city为温州,但是在popup弹出时,picker没有选中温州这个选项,还时停留在杭州。解决方案:看了很多解决方案,设置default-indexset,ColumnIndex。都尝试了,还是不行。而且这些方法,其实在v......
  • vue3--使用ref获取Dom元素
    vue2中,ref使用:在div元素上绑定了ref属性,并命名为hello,接下来我们直接使用this.$refs.hello的方式就可以获取到该DOM元素了。<template><divid="app"><divref="hello">Vue2,ref获取dom元素</div></div></template><script>......
  • 基于vue3和elementplus实现的自定义table组件
    基于vue3和elementplus实现的自定义table组件,集成查询工具栏分页,可通过配置直接实现基础的列表页基于vue3和elementplus实现的自定义table组件,集成查询工具栏分页,可通过配置直接实现基础的列表页目录结构如下:类型声明:declaretypeDictType={value:string|boolean|n......
  • vue3 + xlsx 实现 excel 导入web页面解析成json数据
    vue3+xlsx实现excel导入web页面并解析成json数据fileIipt动态创建的标签,一定要用户点击事件触发,不然文件选择框的弹出会被拦截,无法弹出。意思就是下面这段关键代码要用一个事件区触发执行,不能主动执行(比如:vue的钩子)import*asXLSXfrom'xlsx'//v:"^0.18.5"letfil......
  • 一文教你Vue3中的useDialog,让你的代码更加优雅!
    在日常开发时,弹窗是一个经常使用的功能,而且重复性极高,你可能会遇到下面这些问题:1、一个页面内多个弹窗,要维护多套弹窗状态,看的眼花缭乱2、弹窗内容比较简单,声明变量+模板语法的方式写起来比较麻烦关于这些问题,我首先想到的是应该弄一个即用即走的Dialog,不用去单独维护它......
  • vue3+vite动态引入图片(import.meta.glob)
    Vite官方提供的 import.meta.glob API。这个方法一般用于批量引入js或者ts文件,但实际上这个方法就是很多import语句的集合而已,import是可以引入图片的,所以import.meta.glob也同样可以引入图片资源,只不过需要加入配置项as:'url'就可以了。 通常来说,我们可以用ES提供的......
  • 边做笔试边查缺补漏——算法、js基础、vue3官方文档、八股文
    边投简历边完善自己的知识库。最近这些天一直在面试和笔试+投简历中,每次面试或笔试完后其实最重要的不是结果,而是做题的过程或者说对话的过程。因为只有这些才让我知道自己哪里还有不足,比如一碰到算法题就歇菜、vue3和vue2混用、js基础知识不牢固等等。那我痛定思痛,有缺点咱就认......
  • vue vue3 jsx tsx
    0.安装插件(@vitejs/plugin-vue-jsx)vite官方提供了官方的插件来支持在vue3中使用jsx/tsx,直接安装就行。yarn add @vitejs/plugin-vue-jsx -D安装完之后在vite.config.ts中插入一下代码import vueJsx from "@vitejs/plugin-vue-jsx";export default defineConfig({......
  • Vue3
    1、ref和reactive原来reactive是一个对象类型,使用reactive重新赋值一个对象不起作用<scriptlang="ts"setup>import{reactive}from"vue";letuser=reactive({name:'小明',age:10});constdj=()=>{user={name:'大白',age:1......