uniapp(vue3)小程序地图导航

1,106 阅读4分钟

前言

本项目使用的是vue3,地图小程序原生组件,高德服务端API,文中经纬度格式都是gcj02,高德API返回和接收的格式也是这个,高德API中需要的AMAP_WEBAPI_KEY, 请自行前往高德开发者中心注册获取。 image.png

<template>
	<view class="content">
		<map class="order-map" 
                    :latitude="startPoint.latitude"
                    :longitude="startPoint.longitude" 
                    show-location
                    :scale='scale'
                    :polyline="polyline"
                    :key="polyline.length + new Date().getTime()"
                    :markers="markers">
			<cover-view slot="callout">
				<block v-for="(item,index) in markers" :key="index">
					<cover-view :marker-id="item.id">
						<cover-view>
							{{item.title}}
						</cover-view>
					</cover-view>
				</block>
			</cover-view>
		</map>
		<view class="order-box">
			<view class="search-start" v-if="distance || duration">
				<view class="start-name">
					距离:{{distance}} 时间:{{duration}}
				</view>
			</view>

			<view class="search-start" v-if="endPoint.name">
				<u-icon name="map-fill" color="#ee642b" size="17"></u-icon>
				<view class="start-name">
					{{endPoint.name|'未知地址'}}
				</view>
			</view>
			<view class="search-box" @click="openChooseLocation">
				<u-icon name="search" color="#ffa602" size="23"></u-icon>
				<view class="search-placeholder">
					请选择目拖车地点
				</view>
			</view>
			<view 
                            v-if="startPoint.name"
                            @click="submitToDriver" 
                            class="send-btn">
				发送给拖挂司机
			</view>
		</view>
	</view>
</template>

<script setup>
	import { reactive, ref } from "vue";
	import { onLoad } from '@dcloudio/uni-app'
	import amapFile from "../../libs/amap-wx.js";

	const myAmapFunT = new amapFile({
		key: '填写key值'
	})
	const scale = ref(16)   // 地图的缩放比例
	const markers = ref([]) // 标记点
	const polyline = ref([]) // 导航线路
	const distance = ref(0) // 距离
	const duration = ref(0) // 时间
	const endPoint = ref({}) // 终点
	const startPoint = reactive({
		latitude: 0, //纬度
		longitude: 0, //经度
		name: '',   // 地点名称
		address: ''
	})

	onLoad(() => {
		//获取当前定位
		uni.getLocation({
			type: 'gcj02',
			success: function(res) {
				startPoint.longitude = res.longitude;
				startPoint.latitude = res.latitude;
				getAddress(res.longitude + ',' + res.latitude)
			},
			fail() {
				console.log('获取当前定位失败');
			}
		});
	})

	// 解析地址
	function getAddress(loc) {
		loc && myAmapFunT.getRegeo({
			iconPath: 'http://img.wisdomtaxi.com/amap_icon.png',
			width: '37.5rpx',
			height: '37.5rpx',
			location: loc,
			success: function(data) {
				startPoint.name = data[0].name
				startPoint.address = data[0].desc
				initMap()
			},
			fail: function(err) {
				console.log('解析地址失败' + err)
			},
		});
	}

	//初始化地图数据
	function initMap() {
		markers.value = [{
			id: 1,
			latitude: startPoint.latitude, //纬度
			longitude: startPoint.longitude, //经度
			// iconPath: '/static/images/home/start.png', //显示的图标
			rotate: 0, // 旋转度数
			width: 30, //宽
			height: 40, //高
			title: startPoint.name, //标注点名
			// alpha: 0.5, //透明度
			joinCluster: true,
			customCallout: {
				anchorY: 0,
				anchorX: 0,
				display: "ALWAYS",
			}
		}]
	}

	/**
	 *@author ZY
	 *@date 2023/1/9
	 *@Description:选择位置
	 *@param {Object} opt https://uniapp.dcloud.net.cn/api/location/location.html
	 *
	 opt : {
	  latitude	Number	否	目标地纬度
	  longitude	Number	否	目标地经度
	 }
	 */
	function openChooseLocation(opt) {
		uni.chooseLocation({
			latitude: opt?.latitude || startPoint.latitude,
			longitude: opt?.longitude || startPoint.longitude,
			success: function(res) {
				if (!res.name) {
					return uni.showToast({
						title: '请重新选择位置',
						icon: 'none'
					});
				}
				console.log(res);
				startPoint.latitude = res.latitude;
				startPoint.longitude = res.longitude;
				getAddress(res.longitude + ',' + res.latitude)
			},
			fail: function(info) {
				console.log('调取路线失败' + info)
			}
		})
	}

	/**
	 *@author ZY
	 *@date 2023/1/9
	 *@Description:生成规划路线
	 *@param {string} start 开始位置
	 *@param {string} end 结束位置
	 *@param {number} strategy 10 默认多策略 策略 https://lbs.amap.com/api/webservice/guide/api/direction#driving
	 *
	 10,返回结果会躲避拥堵,路程较短,尽量缩短时间,与高德地图的默认策略也就是不进行任何勾选一致
	 */
	function getPlanningRoute(start, end, strategy = 10) {
		console.log(start, end);
		uni.showLoading({ title: '加载中' })
		myAmapFunT.getDrivingRoute({
			origin: start,
			destination: end,
			strategy: strategy, //备选方案
			success: function(data) {
				if (data.paths && data.paths[0] && data.paths[0].steps) {
					let goodRouter = data.paths.sort((a, b) => {
						return a.duration - b.duration
					})[0]
					
					scale.value = 10
					distance.value = (goodRouter.distance * 0.001).toFixed(2) + '公里'
					duration.value = '大约' + (goodRouter.duration / 60).toFixed(2) + '分钟'

					let steps = goodRouter.steps
					let points = []
					for (var i = 0; i < steps.length; i++) {
						var poLen = steps[i].polyline.split(';');
						for (var j = 0; j < poLen.length; j++) {
							points.push({
								longitude: parseFloat(poLen[j].split(',')[0]),
								latitude: parseFloat(poLen[j].split(',')[1])
							})
						}
					}
					polyline.value = [{
						points: points,
						color: '#0ee532',
						width: 8,
					}]


				}
				uni.hideLoading();
			},
			fail: function(info) {
				console.log('路线规划失败' + info)
				uni.hideLoading();
				uni.showToast({
					title: '路线规划失败',
					icon: 'error'
				});
			},
		})
	}

	// 提交给司机
	function submitToDriver() {
		uni.showLoading({
			title: '正在户籍拖挂司机'
		})

		setTimeout(() => {
			markers.value = [
				...markers.value,
				{
					id: 2,
					latitude: 30.60439,
					longitude: 103.943813,
					// iconPath: '/static/images/home/endd.png', //显示的图标
					rotate: 0, // 旋转度数
					width: 30, //宽
					height: 40, //高
					title: "四川省成都市金牛区兴盛西路2号", //标注点名
					// alpha: 0.5, //透明度
					joinCluster: true,
					customCallout: {
						anchorY: 0,
						anchorX: 0,
						display: "ALWAYS"
					}
				}
			]
			uni.hideLoading()
			getPlanningRoute(`${startPoint.longitude},${startPoint.latitude}`, `${103.943813},${30.60439}`)
		}, 1000)
	}
</script>
<style lang="scss" scoped>
	.content {
		display: flex;
		flex-direction: column;
		text-align: center;
		align-content: center;
		background: #f5f5f9;
		height: 1600rpx;
	}

	.order-map {
		width: 100%;
		height: 100%;
	}

	.order-box {
		position: fixed;
		bottom: 0rpx;
		left: 0rpx;
		width: 100%;
		//height: 435rpx;
		text-align: left;
		background-color: #FFFFFF;
		border-top-right-radius: 10rpx;
		border-top-left-radius: 10rpx;
		box-sizing: border-box;
		padding: 18rpx;
		padding-bottom: 80rpx;
		box-shadow: 0 9.375rpx 28.125rpx 9.375rpx rgba(106, 66, 0, 0.2);

		.send-btn {
			margin-top: 30rpx;
			width: 100%;
			color: white;
			background-color: red;
			padding: 0 24rpx;
			font-size: 28rpx;
			height: 80rpx;
			line-height: 80rpx;
			box-sizing: border-box;
			border-radius: 12rpx;
			text-align: center;
		}

		.search-box {
			background-color: #f3f3f3;
			border-radius: 36rpx;
			height: 80rpx;
			display: flex;
			justify-content: flex-start;
			align-items: center;
			box-sizing: border-box;
			padding: 0 25rpx;

			.search-placeholder {
				font-size: 28rpx;
				color: #bbb9b9;
				padding-left: 15rpx;
			}
		}
	}

	.addH {
		height: 420rpx;
	}

	.row {
		display: flex;
		flex-direction: row;
	}

	.bot {
		margin-bottom: 10rpx;
	}

	.license {
		position: relative;
		left: 30rpx;
		height: 110rpx;
		font-weight: bold;
		font-size: 38rpx;
		line-height: 130rpx;
		letter-spacing: 1.125rpx;
	}

	.time-icon {
		height: 16rpx;
		width: 16rpx;
		position: relative;
		left: 190rpx;
		top: 59rpx;
	}

	.time-text {
		height: 110rpx;
		line-height: 130rpx;
		position: relative;
		left: 200rpx;
		font-size: 30rpx;
		color: #666666;
	}

	.route-icon {
		height: 12rpx;
		width: 12rpx;
		position: relative;
		left: 42rpx;
		top: 30rpx;
	}

	.route-text {
		height: 65rpx;
		width: 478rpx;
		line-height: 65rpx;
		position: relative;
		left: 50rpx;
		font-size: 30rpx;
		color: #666666;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.amt-box {
		width: calc(100% - 558rpx);
		margin-left: 40rpx;
		display: flex;
		flex-direction: row;
		justify-content: flex-start;
		align-items: center;
	}

	.amt-icon {
		margin-top: 5rpx;
		line-height: 65rpx;
		height: 20rpx;
		width: 20rpx;
	}

	.amt {
		margin-left: 10rpx;
		line-height: 65rpx;
		font-size: 30rpx;
		color: #666666;
	}

	.todo {
		position: relative;
		height: 165rpx;
		width: 640rpx;
		margin-left: 30rpx;
		border-top: 0.375rpx solid #E9EAEC;
	}

	.todo-item {
		height: 100%;
		width: 50%;
		text-align: center;
		align-content: center;
	}

	.todo-item>image {
		height: 60rpx;
		width: 60rpx;
		margin-top: 30rpx;
	}

	.todo-item>view {
		color: #3F3F3F;
		font-size: 30rpx;
		letter-spacing: 0.375rpx;
	}
</style>

})