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

父组件引用高德地图:
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>
分类:
vue
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!