需求背景
uniapp+nvue+高德地图开发APP,不是小程序,效果图如下,该效果图是教程二实现的。
-----------------------自定义marker请直接看教程二
-----------------------自定义marker请直接看教程二
-----------------------自定义marker请直接看教程二
前期准备
html
// 高德key申请和hbuilder的manifest.json配置教程
https://blog.csdn.net/pleasantsheep_/article/details/132469712
教程一
使用uniapp的map内置组件,适合标记比较少,可以一次性加载完的情况。在自己的VUE页面中增加map标签就可以了。
html
// 官方参照地址
https://uniapp.dcloud.net.cn/component/map.html
html
<template>
<view>
<view class="page-body">
<view class="page-section page-section-gap">
<map style="width: 100%; height: 300px;"
:latitude="latitude"
:longitude="longitude"
:markers="covers"
@markertap="markertapHander"
@callouttap="callouttapHander"
>
</map>
</view>
</view>
</view>
</template>
html
<script>
export default {
data() {
return {
id:0, // 使用 marker点击事件 需要填写id
title: 'map',
latitude: 39.909,
longitude: 116.39742,
covers: [{
latitude: 39.909,
longitude: 116.39742,
iconPath: '../../../static/location.png',
id:1,
callout: {
content: "自己的弹出框内容1"
}
}, {
latitude: 39.90,
longitude: 116.39,
iconPath: '../../../static/location.png',
id:2,
callout: {
content: "自己的弹出框内容2"
}
}]
}
},
methods: {
markertapHander(e){
console.log("用户点击了marker");
let id = e.detail.markerId;
this.covers.forEach(function(item,index,array) {
if(item.id == id){
item.callout.content="新的值";
}
});
// 如果要刷新content的内容,请使用下面的方法,直接赋值是没有用的
this.covers = this.covers.slice(0);
// 这玩意有个BUG,第一次点击还是会显示以前的值,再点击一次才会显示新的值
// 所以这种方法就适合一次性加载完所有的marker,直接显示就行
},
callouttapHander(){
console.log("用户点击了callout");
}
}
}
</script>
教程二
适合marker点比较多,每次点击marker然后调用后台接口获取新的数据赋值的那种。这种技术的关键字是renderjs。
html
<template>
<view class="app-container" style="padding-bottom: 0;">
<view class="myBody">
<uni-row>
<uni-col :span="24">
<view style="display: none;">
<view :msg="userLanguage" :change:msg="renderJS.reviceLanguage"></view>
<view :msg="markers" :change:msg="renderJS.reviceMarkers"></view>
<view :msg="deviceInfo" :change:msg="renderJS.reviceDeviceInfo"></view>
</view>
<div id="mapDiv" ref="mapDiv"></div>
</uni-col>
</uni-row>
</view>
</view>
</template>
<script module="renderJS" lang="renderjs">
export default {
data() {
return {
key:"31cf847a026b0b297c6ba84a3b54fb92",// 自己高德的key
renderLanguage: null,
renderMarkers: null,
renderDeviceInfo: null,
infoWindow: null,
};
},
mounted() {
if (window.AMap) {
setTimeout(() => {
this.initAmap();
}, 20);
} else {
const script = document.createElement('script');
script.src = "https://webapi.amap.com/maps?v=1.4.15&key="+this.key;
script.onload = () => {
setTimeout(() => {
this.initAmap();
}, 20);
}
document.head.appendChild(script);
}
},
methods: {
reviceLanguage(newValue, oldValue, ownerVm, vm) {
this.renderLanguage = newValue;
},
reviceMarkers(newValue, oldValue, ownerVm, vm) {
this.renderMarkers = newValue;
},
reviceDeviceInfo(newValue, oldValue, ownerVm, vm) {
this.renderDeviceInfo = newValue;
},
initAmap() {
let _this = this;
const map = new AMap.Map('mapDiv', {
lang: "en",
zoom: 0,
viewMode: '2D',
buildingAnimation: false,
center: [115.3974770000, 35.9086920000],
// mapStyle: "amap://styles/fresh"
});
var language = this.renderLanguage;
if (language == "zh-CN") {
map.setLang("zh_cn");
} else if (language == "zh-TW") {
map.setLang("zh_en");
} else {
map.setLang("en");
}
map.clearMap(); // 清除地图覆盖物
var markerList = [] //用来装标记点
this.renderMarkers.forEach(function(item, index) {
const pngColorName = "map_pink";
if (item.onlineFlag == 0) {
pngColorName = "map_gree";
}
const pngColorPath = "static/assets/images/" + pngColorName + ".png";
let marker = new AMap.Marker({
map: map,
icon: new AMap.Icon({
image: pngColorPath,
imageSize: new AMap.Size(40, 40),
}),
position: [item.longitude, item.latitude],
offset: new AMap.Pixel(-13, -30),
extData: {
index,
id:item.id,
longitude:item.longitude,
latitude:item.latitude
},
});
markerList.push(marker);
marker.on("click", function(e) {
let index1 = e.target.getExtData().index;
let id = e.target.getExtData().id;
let longitude = e.target.getExtData().longitude;
let latitude = e.target.getExtData().latitude;
_this.$ownerInstance.callMethod("markertap",id);
setTimeout(function(){
if (_this.infoWindow != null) {
_this.infoWindow.close();
}
_this.infoWindow = new AMap.InfoWindow({
isCustom: true,
content: _this.renderDeviceInfo,
closeWhenClickMap: true,
offset: new AMap.Pixel(16, -45)
});
_this.infoWindow.open(map, [longitude,latitude]);
},400);
})
// 将标记点数组放入Map
map.add(markerList);
});
},
}
}
</script>
<script>
export default {
name: "IndexPhone",
components: {
},
data() {
return {
userLanguage: null,
deviceInfo: null,
markers: [],
};
},
created() {
},
onReady() {
},
onLoad() {
this.userLanguage = uni.getStorageSync("language");
this.getAllDeviceList();
},
methods: {
getAllDeviceList() {
var _this = this;
uni.request({
url: _this.api + '/listMap',
method: 'get',
data: _this.queryParams,
header: {
'Content-Type': 'application/json',
'Authorization': "Bearer " + uni.getStorageSync("loginToken"),
'language': uni.getStorageSync("language")
},
success: function(res) {
var response = res.data;
if (response != null && response.rows != null) {
for (let i = 0; i < response.rows.length; i++) {
const markerObj = {
id: response.rows[i].id,
onlineFlag: response.rows[i].onlineFlag,
latitude: response.rows[i].latitude,
longitude: response.rows[i].longitude,
};
_this.markers.push(markerObj);
}
}
},
fail: function(err) {
console.error(err);
}
});
},
markertap(deviceId) {
var _this = this;
uni.request({
url: _this.api + '/getIndexDeviceInfoByOne?id=' + deviceId,
method: 'get',
data: '',
header: {
'Content-Type': 'application/json',
'Authorization': "Bearer " + uni.getStorageSync("loginToken"),
'language': uni.getStorageSync("language")
},
success: function(res) {
var response = res.data;
if (response != null && response.data != null) {
if (_this.infoWindow != null) {
_this.infoWindow.close();
}
let onlineStatus = _this.$t('wordOffline');
if (response.data.online == 0) {
onlineStatus = _this.$t('wordOnline');
}
let deviceStatus = _this.$t('wordFault');
if (response.data.devStatus == 0) {
deviceStatus = _this.$t('wordNormal');
}
// 注意renderjs中不识别view标签,用div
let deviceContent = "<div class=\"map_div_css\">";
deviceContent += "<div style=\"border-bottom:1px solid #ffffff; margin-bottom: 5px;\">" +
_this.$t('indexTableDeviceInfo') + "</div>";
deviceContent += "<div>" + _this.$t('indexTableCorporation') + ":" + response.data.companyName + "</div>";
deviceContent += "<div>" + _this.$t('indexTablePrimaryKey') + ":" + response.data.devId + "</div>";
deviceContent += "<div>" + _this.$t('indexTableDeviceName') + ":" + response.data.name + "</div>";
deviceContent += "<div>" + _this.$t('indexTableDeviceOnlineStatus') + ":" +
onlineStatus + "</div>";
deviceContent += "<div>" + _this.$t('indexTableDeviceStatus') + ":" + deviceStatus +
"</div>";
deviceContent += "</div>";
_this.deviceInfo = deviceContent;
}
},
fail: function(err) {
console.error(err);
}
});
},
},
mounted() {
},
};
</script>
<style rel="stylesheet/scss">
.app-container {
padding: 0px;
width: 100%;
height: 100%;
}
</style>
<style scoped lang="scss">
#mapDiv {
padding: 0px;
margin: 0px;
width: 100%;
height: 48vh;
}
::v-deep .map_div_css {
background: #304156;
padding: 7px;
font-size: 14px;
color: #ffffff;
border-radius: 6px;
font-family: initial;
}
</style>
renderjs数据交互
renderjs如何与uniapp数据交互,请看下面教程
html
https://blog.csdn.net/weixin_43907110/article/details/127827179
https://blog.csdn.net/Smile_ping/article/details/131991041
吐槽一下
hbuilder的map官方封装的真的是一坨 S H I,没见过这么烂的开发。本人也是第一次接触uniapp。最简单的封装就是用户传递一个html标签给你,你直接显示就完了。非在那里封装一堆没有用的标签,结果功能还没实现。不知道招的都是些什么牛鬼蛇神。
备注
凡是在网上看到slot="callout",那就是小程序的写法,不要浪费时间。就像下面的写法
html
<map :style="mapStyle" show-location="true" :latitude="latitude" id="map" :longitude="longitude":markers="markers">
<cover-view slot="callout">
<block v-for="(item,index) in markers" :key="index">
<cover-view class="customCallout" :marker-id="item.id">
<cover-view class="content">
{{item.id}} 内容
</cover-view>
</cover-view>
</block>
</cover-view>
</map>