需求:实现点击获取经纬度、定位、对特殊位置标点及自定义信息窗体功能。
高德地图的官网API:概述-地图 JS API 1.4 | 高德地图API
vue-amap的中文文档:组件 | vue-amap
实现:
1、安装vue-amap插件
npm install vue-amap --save
2、项目引入
在main.js中引入
需要提前申请高德key,步骤附在最后了
// 高德地图配置
import VueAMap from 'vue-amap' // 引入插件
Vue.use(VueAMap) // 使用插件
VueAMap.initAMapApiLoader({ // 初始化插件
key: '高德申请的key', // 高德key
plugin: ['AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.Geolocation', 'AMap.CitySearch'], // 插件集合,根据自己的需求添加
uiVersion: '1.0.11', // 不加会报错,加上吧
v: '1.4.15' // 默认高德 sdk 版本为 1.4.4
})
// 申请的Web端(JS API)的需要写上下面这段话
window._AMapSecurityConfig = {
securityJsCode: '高德申请的kedy对应的安全密钥' // 高德Web端安全密钥
}
3、新建一个Map.vue文件
这里直接放我封装的组件
<template>
<div class="Map">
<!-- 地图组件 -->
<el-amap
vid="amapContainer"
class="amapClass"
:zoom="zoom"
:plugin="plugin"
:center="center"
:events="events"
>
<!-- 这里用于点击时的标点 -->
<el-amap-marker
v-for="(marker, index) in markers"
:key="index + '1'"
:position="marker"
/>
<!-- 这里用于特殊位置的标点 -->
<el-amap-marker
v-for="(marker, index) in markerList"
:key="index + '2'"
:position="marker.lnglats"
:icon="getIcon(marker)"
:events="marker.events"
:offset="[-24, -24]"
/>
<!-- 信息窗体 -->
<el-amap-info-window
v-if="currentWindow.visible"
:position="currentWindow.position"
:visible.sync="currentWindow.visible"
:content="currentWindow.content"
:close-when-click-map="false"
:events="currentWindow.events"
:offset="[0, -24]"
/>
</el-amap>
</div>
</template>
<script>
import LED0 from '@/assets/imgs/LED-0.png'
import LED1 from '@/assets/imgs/LED-1.png'
import lamppost0 from '@/assets/imgs/lamppost-0.png'
import lamppost1 from '@/assets/imgs/lamppost-1.png'
import tourGuide0 from '@/assets/imgs/tourGuide-0.png'
import tourGuide1 from '@/assets/imgs/tourGuide-1.png'
export default {
name: 'Map',
props: {
zoom: { // 地图缩放比例。zoom值越小,越宏观、越大,越微观。
type: Number,
default: 14
},
center: {
type: Array,
default: () => [120.147076, 30.245426] // 西湖风景区的经纬度
},
markers: {
type: Array,
default: () => []
},
markerList: {
type: Array,
default: () => []
},
currentWindow: {
type: Object,
default: () => ({
position: [],
visible: false,
content: '测试'
})
}
},
data() {
const self = this
return {
LED0,
LED1,
lamppost0,
lamppost1,
tourGuide0,
tourGuide1,
address: null,
loaded: false,
events: {
init(o) {
console.log('地图初始化', o)
// ... 初始地图的一些操作
},
complete(){
// ... 地图渲染完成后的一些操作
}
// 点击地图
click: self.click
},
// 一些工具插件
plugin: [
{
pName: 'Geocoder',
events: {
init(o) {
console.log('Geocoder', o.getAddress())
}
}
},
{
// 定位
pName: 'Geolocation',
events: {
init(o) {
// o是高德地图定位插件实例
o.getCurrentPosition((status, result) => {
// 会存在失败的时候,官网回答:https://lbs.amap.com/faq/js-api/map-js-api/position-related/43361/
console.log('查询成功定位信息', status, result)
if (result && result.position) {
// 根据经纬度设置坐标 --不想替换掉默认的center的可以先注释
self.$emit('update:center', [result.position.lng, result.position.lat])
// self.$emit('update:markers', [[result.position.lng, result.position.lat]])
// load
self.loaded = true
// 页面渲染好后
self.$nextTick()
}
})
}
}
},
{
// 搜索 --我这里没用到
pName: 'PlaceSearch',
events: {
init(instance) {
console.log('搜索', instance)
}
}
},
{
// 定位城市
pName: 'CitySearch',
events: {
init(o) {
// o是高德地图定位插件实例
o.getLocalCity(function(status, result) {
if (status === 'complete' && result.info === 'OK') {
// 查询成功,result即为当前所在城市信息
console.log('查询成功,当前所在城市信息', result)
}
})
}
}
}
]
}
},
methods: {
// 点击地图获取经纬度和具体位置
click(e) {
const { lng, lat } = e.lnglat
this.$emit('update:center', [lng, lat])
this.$emit('update:markers', [[lng, lat]])
console.log('[lng, lat]', [lng, lat])
// 这里通过高德 SDK 完成。获取具体地址的
const geocoder = new AMap.Geocoder({
radius: 1000,
extensions: 'all'
})
const that = this
geocoder.getAddress([lng, lat], function(status, result) {
if (status === 'complete' && result.info === 'OK') {
if (result && result.regeocode) {
that.address = result.regeocode.formattedAddress
console.log('具体位置:', result.regeocode.formattedAddress)
that.$nextTick()
}
}
that.$emit('getLocation', { lng, lat, address: that.address })
})
},
getIcon(item) {
if (item.mediaTypeName === '灯杆屏') {
return item.status ? this.lamppost0 : this.lamppost1
} else if (item.mediaTypeName === '导览屏') {
return item.status ? this.tourGuide0 : this.tourGuide1
} else return item.status ? this.LED0 : this.LED1
}
}
}
</script>
<style lang="scss" scoped>
/* 指定地图的宽高 */
.amapClass, .Map {
width: 100%;
height: 100%;
}
</style>
关于el-amap-marker的属性:
1、position:为marker点的坐标(经纬度),接收数组
3、events:事件集合对象,click、dblclick、rightclick、mouseover、mouseout等
events: {
click: (e) => {
// ...点击操作
},
dblclick: (e) => {
// ...双击操作
},
...
}
4、offset:偏移量
其他我没用到的还有:
5、clickable:true允许用户可点击marker点(默认也是true)
6、animation:marker点的动画效果
marker点弹跳效果:设置值为 AMAP_ANIMATION_BOUNCE
7、label:点显示的简略信息
8、content可以是文字字符串也可以是html
关于el-amap-info-window信息窗体的属性
静态属性
名称 | 类型 | 说明 |
---|---|---|
vid | String | 组件的ID。 |
isCustom | Boolean | 是否自定义窗体。设为true时,信息窗体外框及内容完全按照content所设的值添加(默认为false,即在系统默认的信息窗体外框中显示content内容) |
autoMove | Boolean | 是否自动调整窗体到视野内(当信息窗体超出视野范围时,通过该属性设置是否自动平移地图,使信息窗体完全显示) |
closeWhenClickMap | Boolean | 控制是否在鼠标点击地图后关闭信息窗体,默认false,鼠标点击地图后不关闭信息窗体 |
showShadow | Boolean | Boolean 控制是否显示信息窗体阴影,取值false时不显示窗体阴影,取值true时显示窗体阴影默认值:false |
offset | Array | 相对于基点的偏移量。默认情况是信息窗体的底部中心点(BOTTOM_CENTER) 和基点之间的偏移量 |
动态属性
名称 | 类型 | 说明 |
---|---|---|
content | String/HTML | 显示内容。支持字符串和HTML。 |
template | String | 支持传入 Vue 模板。v0.4.0 开始支持。 |
vnode | VNode 或 Funtion: (Instance) => VNode | 支持 VNode 渲染。v0.4.2 开始支持 |
contentRender | Function: (createElement: () => VNode, instance) => VNode | 支持 VNode render 渲染。v0.4.3 开始支持 |
size | Size | 信息窗体尺寸(isCustom为true时,该属性无效) |
position | Array | 信息窗体显示基点位置(自v1.2 新增) |
visible | Boolean | 信息窗体是否显示。这里需要注意的是,高德地图只支持同时一个信息窗体的显示。所以一旦有窗体显示切换的场景,visible数组的状态需要自行维护。 |
ref 可用方法
提供无副作用的同步帮助方法
函数 | 返回 | 说明 |
---|---|---|
$$getInstance() | AMap.InfoWindow | 获取infoWindow 实例 |
事件(也是events里的)
事件 | 参数 | 说明 |
---|---|---|
init | Object | 高德组件实例 |
change | 属性发生变化时 | |
open | 信息窗体打开之后触发事件 | |
close | 信息窗体关闭之后触发事件 |
4、使用地图组件
我把使用的页面也全贴上了,但其实search-box里面的东西大家可以忽略,只是针对markerList的查询过滤
<template>
<div class="box">
<div class="search-box">
<el-form ref="queryForm" label-width="auto" class="searchForm" :model="searchForm" style="padding: 16px 0 0;">
<el-form-item label="类型" prop="mediaType">
<el-select v-model="searchForm.mediaType">
<el-option v-for="item in digitalTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="区域" prop="region">
<el-select v-model="searchForm.region" clearable>
<el-option v-for="item in regionOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label-width="20px">
<el-button v-permission="['ResourceDistribution:query']" class="search-btn" icon="el-icon-search" type="primary" @click="getDetail">
查询
</el-button>
<el-button class="reset-btn" icon="el-icon-refresh-left" @click="resetForm('queryForm')">
重置
</el-button>
</el-form-item>
<el-form-item label="设备名称" prop="">
<el-select v-model="searchKey" filterable clearable placeholder="请输入关键字" @change="quickOrientation">
<el-option v-for="item in markerList" :key="item.mediaCode" :label="item.mediaName" :value="item.mediaCode" />
</el-select>
<!-- <el-input id="search" v-model="searchKey" placeholder="请输入设备名称" /> -->
</el-form-item>
<el-form-item label-width="20px" style="width: 100px;">
<el-button v-permission="['ResourceDistribution:query']" class="search-btn" type="primary" @click="quickOrientation">
快速定位
</el-button>
</el-form-item>
</el-form>
<div class="rightType">
<div v-for="item in digitalTypeOptions" :key="item.value" class="iconItem">
<svg-icon v-if="item.label === '灯杆屏'" icon-class="lamppost" />
<svg-icon v-else-if="item.label === '导览屏'" icon-class="tourGuide" />
<svg-icon v-else icon-class="LED" />
<span>{{ item.label }}</span>
</div>
</div>
</div>
<div class="rightBottom">
<span class="successStatus">已启用</span>
<span class="errorStatus">已禁用</span>
</div>
<!-- 地图组件 -->
<Map
:center.sync="center"
:markers.sync="markers"
:zoom.sync="zoom"
:marker-list="markerList"
:current-window="currentWindow"
@getLocation="getLocation"
/>
</div>
</template>
<script>
import Map from '@/components/Map'
import { dictList, deviceFilter } from '@/api/resource'
export default {
name: 'GaodeDituAmap',
components: { Map },
data() {
return {
searchForm: {
mediaType: '',
region: ''
},
searchKey: '',
digitalTypeOptions: [],
regionOptions: [],
currentWindow: {
position: [],
visible: false,
content: '',
events: {}
},
markerList: [],
markers: [],
center: [121.133858, 30.606042],
zoom: 14 // 地图缩放比例。zoom值越小,越宏观、越大,越微观。
}
},
mounted() {
this.getDict()
this.getDetail()
},
methods: {
// 获取标点信息
async getDetail() {
this.currentWindow.visible = false
const that = this
const { data } = await deviceFilter(this.searchForm)
data.forEach(item => {
item.lnglats = [item.longitude, item.latitude]
item.events = {
click(e) {
console.log('点击标点', e)
that.center = item.lnglats
that.currentWindow.content = that.createWinContent(item)
that.currentWindow.position = item.lnglats
that.currentWindow.events = {
close(e) {
console.log('窗体关闭', e)
that.currentWindow.visible = false
}
}
that.currentWindow.visible = true
}
}
})
this.markerList = data
},
/**
* 基础配置信息
*/
async getDict() {
const { data: { area, digitalType }} = await dictList('dictNames=area&dictNames=digitalType')
this.regionOptions = area
this.digitalTypeOptions = digitalType
},
// 重置
resetForm(formName) {
this.$refs[formName].resetFields()
this.zoom = this.$options.data.call(this).zoom
this.getDetail()
},
// 快速定位
quickOrientation() {
if (!this.searchKey) {
this.markers = []
this.currentWindow.visible = false
return
}
const cur = this.markerList.find(f => f.mediaCode === this.searchKey)
if (cur) {
this.center = [cur.longitude, cur.latitude]
// this.markers = [this.center]
this.currentWindow.content = this.createWinContent(cur)
this.currentWindow.position = [cur.longitude, cur.latitude]
this.currentWindow.visible = true
this.zoom = 15
}
},
// 获取经纬度及地理位置
getLocation({ lng, lat, address }) {
console.log('获取到经纬度和地址啦:', lng, lat, address)
},
// 画信息窗
createWinContent(item) {
return `
<div>
<div style="display: flex;margin-bottom: 12px">
${item.src ? `<img src="${item.src}" style="width: 120px;margin-right: 16px;">` : ''}
<div style="display: flex;flex-direction: column;justify-content: space-around;">
<strong style="font-size: 18px">${item.mediaName}</strong>
<div style="margin-top: 6px">
<span style="font-size: 14px;color: #0082FF;padding: 0px 6px;background: rgba(0,130,255,0.15);border-radius: 4px;">${item.mediaTypeName}</span>
<span style="font-size: 14px;color: ${!item.status ? '#46A77B' : '#E30000'};padding: 0px 6px;background: ${!item.status ? '#46A77B26' : '#E3000026'};border-radius: 4px;">${!item.status ? '已启用' : '已禁用'}</span>
</div>
<div style="margin-top: 8px">
<i class="el-icon-location" style="color: #0082FF"></i>
<span style="font-size: 14px;">${item.regionName}</span>
</div>
</div>
</div>
<div style="font-size: 14px;color: #666666;line-height: 20px;">
<span>${item.specification},</span>
<span>${item.supplier},</span>
<span>${item.supplierContact},</span>
<span>${item.supplierPhone}</span>
</div>
</div>`
}
}
}
</script>
<style lang="scss" scoped>
.box {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0px;
position: relative;
.search-box {
position: absolute;
background-color: #FFFFFF;
width: calc(100% - 32px);
top: 16px;
left: 16px;
z-index: 999;
border-radius: 4px;
.searchForm .el-form-item {
margin: 0 0px 16px;
}
.tip-box {
width: 100%;
max-height: 260px;
position: absolute;
top: 30px;
overflow-y: auto;
background-color: #fff;
}
.rightType{
position: absolute;
right: 0px;
bottom: -24px;
transform: translateY(100%);
z-index: 999;
background: #FFFFFF;
border-radius: 4px;
padding: 16px;
display: flex;
align-items: center;
flex-direction: column;
.iconItem{
font-size: 14px;
color: #4A4A4A;
display: flex;
align-items: center;
flex-direction: column;
.svg-icon{
width: 24px;
height: 24px;
}
}
.iconItem+.iconItem{
margin-top: 24px;
}
}
}
.rightBottom{
position: absolute;
right: 16px;
bottom: 16px;
z-index: 999;
padding: 16px;
font-size: 14px;
color: #4A4A4A;
background-color: #FFFFFF;
border-radius: 4px;
.successStatus{
margin-right: 24px;
}
.successStatus::before{
content: '';
display: inline-block;
height: 8px;
width: 8px;
background-color: #46A77B;
border-radius: 50%;
margin-right: 5px;
}
.errorStatus::before{
content: '';
display: inline-block;
height: 8px;
width: 8px;
background-color: #FF4747;
border-radius: 50%;
margin-right: 5px;
}
}
}
/* 指定地图的宽高 */
.amapClass {
width: 100%;
height: 100%;
}
</style>
成果大致样式如下:
附:高德Key申请
登录后到这个地址:我的应用 | 高德控制台
1、点击添加key
2、根据自己的需求选择服务平台,可以看看对应的“可使用服务” 来决定,我这里选择Web端
3、提交后就有一条新增的记录,复制放进代码使用就好啦
参考地址:
vue-amap基于vue的高德地图使用_amap vue-CSDN博客
1.5.1 信息窗体 - vue-amap 中文文档 - 开发文档 - 文江博客