微信小程序地图,定位,仿多多自提点

1,866 阅读5分钟

一、获取用户当前定位 wx.getLocation wx.openLocation

相关公告和官网 developers.weixin.qq.com/miniprogram… developers.weixin.qq.com/community/d…

	uni.getLocation({
			type: 'gcj02', //返回可以用于uni.openLocation的经纬度
			success: function(res) {
				const latitude = res.latitude;
				const longitude = res.longitude;
				uni.openLocation({
					latitude: latitude,
					longitude: longitude,
					success: function() {
						console.log('success');
					}
				});
			},
			fail:function(error){
				console.log(error,"错误");
			}
		});

把微信的api框放上去一写 报错了

getLocation:fail the api need to be declared in the requiredPrivateInfos fie

经过查询资料发现得在app.json中,即uniapp的manifest中配置一些东西

	"mp-weixin": {
		"appid": "",
		"setting": {
			"urlCheck": false
		},
		"usingComponents": true,
		"permission": {
			"scope.userLocation": {
				"desc": "你的位置信息将用于小程序位置接口的效果展示"
			},
			"scope.userFuzzyLocation": {
				"desc": "你的位置信息将用于小程序位置接口的效果展示"
			}
		},
		"requiredPrivateInfos": ["getLocation"]
	},

然后就好了,ding~~ 注意重新编译才生效噢

1737123428602.png

手贱点了个拒绝,又报错了,不过没关系,清除开发者工具的缓存即可解决下面的问题

getLocation:fail auth deny

点击确定,到了这个页面,定位一点都不准确,离我现在的地方将近二十公里呢

1737123854832.png

但此时,一打开真机调试,手机上超级准,准到楼栋了,ok,手机上准就没事。

ding~~~ 注意pc端真机预览是打不开这个授权弹窗的,pc端不行就搞个提示吧,

ok,此时用户当前定位我们拿到了,我想做个类似多多买菜选择自提点的功能, 拆解一下功能点: 1.将用户的定位和我们的定位匹配,比如匹配个两公里范围内的自提点。

疑问了,没有自提点咋办,多多是这么干的 e12f80101d5881e37c352cf9c9f7764.jpg

  1. 2公里范围内自提点在地图上聚合显示,或许这里两公里范围还可以更大一点,多显示一点

  2. 列表显示用户附近两公里的所有自提点,并且计算出每个自提点距离用户的多远,--这个距离貌似多多算的也是直线距离呢

二、将用户的定位和我们的定位匹配

这个功能应该是要放在后端做的,流程大概是这样的,前端调用接口,传入用户当前的经纬度,然后后端根据某种公式计算,匹配出直线距离为两公里范围内的数据列表,这个上面还得要加个省份的匹配吧,不然整个数据库的自提点肯定太多了。然后呢,后端把每个计算的直线距离,自提点地址,地址点经纬度,等返回给前端。

这个公式运用到了数学中的平面三角学,当然我数学不好,我选择文心一言

function calculateDistance(lat1, lon1, lat2, lon2) {
  const earthRadius = 6371; // 地球半径(单位:千米)
 
  const dLat = toRadians(lat2 - lat1);
  const dLon = toRadians(lon2 - lon1);
 
  const a = Math.pow(Math.sin(dLat / 2), 2) +
            Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
            Math.pow(Math.sin(dLon / 2), 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  const distance = earthRadius * c;
 
  return distance;
}
 
function toRadians(degrees) {
  return degrees * (Math.PI / 180);
}
 
const distance = calculateDistance(39.9042, 116.4074, 31.2304, 121.4737);
console.log(distance);

网页打开高德地图搜这是哪,点击更多,分享,地址前面就有经纬度了 通过对比还是比较准确的 单位是km

ok,这个功能搞定了

三、2公里范围内自提点在地图上聚合显示

先捏造一点自提点数据吧,

<template>
	<view>
		<!-- 顶部导航栏 -->
		<view class="nav-bar">
			<uni-nav-bar @clickLeft="back" left-icon="back" title="附近自提点" :border="false" :fixed="false" color="#383838"
				background-color="#fff" status-bar>
			</uni-nav-bar>
		</view>


		<!-- 地图 聚合附近自提点 -->
		<view class="map-box">
			<map :longitude="userLon" :latitude="userLat" @callouttap="clickMarker" :data-flag="clickFlag"
				bindregionchange="change" :scale="scale" :markers="nearList">
				<cover-view slot="callout">
					<cover-view :marker-id="item.id" v-for="item in nearList" :key="item.id">
						<cover-view class="marker-box">
							<cover-view class="marker-default-icon">
								<cover-image class="qipao" src="../../static/mall-icon/shop-marker.png" mode="">
								</cover-image>
							</cover-view>
							<!-- 下方这个label点击才显示 -->
							<cover-view v-if="selectedPickUp == item.id" class="marker-label">
								{{item.name}}
							</cover-view>
						</cover-view>
					</cover-view>
				</cover-view>
			</map>
		</view>

		<!-- 附近的自提点列表 -->
		<view class="bottom">
			<view class="title">附近自提点</view>
			<view class="list-area">
				<view class="list-item">
					<view class="left">
						<image src="../../static/my-icon/logo.png" mode=""></image>
						<view class="pick-up-info">
							<view class="name">繁荣苑自提点</view>
							<view class="address">
								上海市松江区九亭镇繁荣苑上海市松江区九亭镇繁荣苑
							</view>
							<view class="distance">
								<view class="value">直线距离250m</view>
								<view class="tag">
									距离最近
								</view>
							</view>
						</view>
					</view>

					<view class="right">
						<u-button color="#FF2A17" shape="circle">选这个</u-button>
					</view>
				</view>
			</view>
		</view>


	</view>
</template>

<script setup>
	import {
		onLoad,
		onShow
	} from "@dcloudio/uni-app"
	import {
		ref,
		reactive
	} from "vue"


	const userLat = ref()
	const userLon = ref()
	const scale = ref(10)
	const clickFlag = ref(true)

	//选择自提点
	function selectPickup() {
		uni.getLocation({
			type: 'gcj02', //返回可以用于uni.openLocation的经纬度
			success: function(res) {
				const latitude = res.latitude;
				const longitude = res.longitude;
				console.log(res, "坐标");
				userLat.value = res.latitude
				userLon.value = res.longitude
				getNearList()

				console.log(nearList.value, "值");
			},
			fail: function(error) {
				console.log(error, "错误");
			}
		});
	}

	onLoad(() => {
		selectPickup()
	})



	//捏造自提点数据 (名称,地址,省,市,区,经度,纬度,直线距离) 按照直线距离有大到小排序
	const selfPickUpPoint = ref([{
			id: 1,
			name: "繁荣苑自提点",
			address: "上海市繁荣苑",
			province: "上海市",
			city: "直辖市",
			area: "松江区",
			distance: "",
			latitude: "31.120165",
			longitude: "121.329818",
			width: '1px',
			height: '1px',
			customCallout: {
				display: 'ALWAYS',
				anchorX: 0,
				anchorY: 0
			}
		},
		{
			id: 2,
			name: "金色家园自提点",
			address: "上海市繁荣苑",
			province: "上海市",
			city: "直辖市",
			area: "松江区",
			distance: "",
			latitude: "31.238028",
			longitude: "121.467941",
			alpha: 1,
			width: '1px',
			height: '1px',
			customCallout: {
				display: 'ALWAYS',
				anchorX: 0,
				anchorY: 0
			}
		}
	])


	//计算两个坐标点之间的距离
	function calculateDistance(lat1, lon1, lat2, lon2) {
		const earthRadius = 6371; // 地球半径(单位:千米)

		const dLat = toRadians(lat2 - lat1);
		const dLon = toRadians(lon2 - lon1);

		const a = Math.pow(Math.sin(dLat / 2), 2) +
			Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
			Math.pow(Math.sin(dLon / 2), 2);
		const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
		const distance = earthRadius * c;

		return distance;
	}

	function toRadians(degrees) {
		return degrees * (Math.PI / 180);
	}


	const nearList = ref([]) //附近数组

	//匹配所有的地址中距离用户两公里的数据
	function getNearList() {
		selfPickUpPoint.value.forEach(item => {
			let distance = calculateDistance(item.latitude, item.longitude, userLat.value, userLon.value);
			console.log(distance, item.latitude, item.longitude, "距离");
			if (distance < 200000) {
				item.distance = distance
				nearList.value.push(item)
			}
		})
	}

	const selectedPickUp = ref()

	//点击气泡
	function clickMarker(e) {
		console.log(e);
		let detailId = e.detail.markerId
		//在此处调用查询自提点详情的后端接口
		selectedPickUp.value = e.detail.markerId
	}

	function back() {
		uni.navigateBack()
	}
</script>

<style lang="scss">
	.map-box {
		width: 100vw;
		height: 40vh;

		map {
			width: 100%;
			height: 100%;
		}

		.marker-box {
			display: flex;
			justify-content: center;
			align-items: center;
			flex-direction: column;
		}

		.marker-default-icon {
			// position: relative;

			.qipao {
				position: relative;
				left: 0;
				top: 0;
				width: 96rpx;
				height: 96rpx;
			}

			.shop {
				width: 48rpx;
				height: 48rpx;
			}
		}

		.marker-label {
			background-color: #fff;
			padding: 12rpx 16rpx;
			border-radius: 12rpx;
			margin-top: 12rpx;
		}
	}

	.bottom {
		padding: 0 24rpx;
		margin-top: 24rpx;

		.title {
			font-size: 32rpx;
			font-weight: 500;
			color: #101010;
			padding: 16rpx;
			margin-bottom: 12rpx;
		}

		.list-item {
			display: flex;
			align-items: center;
			justify-content: space-between;

			.left {
				width: 80%;
				display: flex;
				align-items: center;
			}

			.right {
				width: 20%;
			}

			image {
				width: 120rpx;
				height: 120rpx;
				margin-right: 12rpx;
				flex-shrink: 0;
			}

			.pick-up-info {
				.name {
					font-size: 32rpx;
					font-weight: 500;
					color: #101010;
					overflow: hidden;
					text-overflow: ellipsis;
					display: -webkit-box;
					-webkit-line-clamp: 1;
				}

				.address {
					margin: 8rpx 0;
					width: 100%;
					font-size: 24rpx;
					color: #88878C;
					overflow: hidden;
					text-overflow: ellipsis;
					display: -webkit-box;
					-webkit-line-clamp: 1;
				}

				.distance {
					display: flex;
					align-items: center;

					.value {
						font-size: 24rpx;
						color: #454545;
					}

					.tag {
						font-size: 20rpx;
						font-weight: 500;
						color: #FF2A17;
						border-radius: 4rpx;
						background: rgba(255, 185, 179, 0.2);
						padding: 4rpx 12rpx;
					}
				}
			}


		}
	}
</style>

af5d62d31bbd2d786ee3eebe81d0c56.png

五、最后声明

就不做那个输入地址查地址和设定地址的功能了,那个貌似得用到收费的地图api了, 感觉勉强够用的,再加就要加钱了