uniapp+高德地图开发APP自定义marker弹出框教程

2023-12-26 20:01:44

需求背景

uniapp+nvue+高德地图开发APP,不是小程序,效果图如下,该效果图是教程二实现的。

-----------------------自定义marker请直接看教程二

-----------------------自定义marker请直接看教程二

-----------------------自定义marker请直接看教程二

前期准备

// 高德key申请和hbuilder的manifest.json配置教程
https://blog.csdn.net/pleasantsheep_/article/details/132469712

教程一

使用uniapp的map内置组件,适合标记比较少,可以一次性加载完的情况。在自己的VUE页面中增加map标签就可以了。

// 官方参照地址
https://uniapp.dcloud.net.cn/component/map.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>
<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。

<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数据交互,请看下面教程

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",那就是小程序的写法,不要浪费时间。就像下面的写法

<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>

文章来源:https://blog.csdn.net/renkai721/article/details/135228467
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。