Files
delivery_admin_app/pages/index/map.vue
2026-03-11 14:52:32 +08:00

439 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- {{ polyline[0]?.points.length }} - {{ pointArr.length }} -->
<!-- <button @tap="startWatch()">{{ polyline[0].points.length }}开始监听</button>
<button @click="fileWriter">存储</button> -->
<!-- 地图容器 -->
<view class="map-container">
<map id="amap" ref="mapRef" :latitude="latitude" :longitude="longitude" :scale="scale" :polyline="polyline"
:markers="markers" style="width: 100%; height: 100%;" />
<!-- <cover-image class="center-img" :src="center" v-show="reportParams.type == 'location'"></cover-image> -->
</view>
</view>
</template>
<script setup>
import { ref, onUnmounted, getCurrentInstance } from 'vue';
import { onLoad, onUnload, onReady } from '@dcloudio/uni-app';
import center from "../../static/mark.png";
import { locationReport, historyLocation } from '@/api/index'
// 地图相关变量
const mapRef = ref(null);
const latitude = ref(null); // 默认北京纬度
const longitude = ref(null); // 默认北京经度
const scale = ref(16)
// const scale = ref(20)
let reportParams = ref({})
const polyline = ref([
{
points: [],
color: '#007AFF', // 高德蓝,更醒目
width: 15, // 适当加粗
}
]);
const markers = ref([])
let mapContext = null;
onLoad((options) => {
reportParams.value = options
uni.setKeepScreenOn({
keepScreenOn: true
})
})
onReady(() => {
if (reportParams.value.type == 'location') {
startWatch()
} else if (reportParams.value.type == 'history') {
historyLocation(reportParams.value).then(async (res) => {
mapContext = uni.createMapContext('amap');
let arr = []
res.data.forEach(item => {
arr.push({
latitude: item.lat,
longitude: item.lng
})
})
polyline.value[0].points = JSON.parse(JSON.stringify(arr))
console.log("!!!!!",polyline)
// const centerPoint = await getCenterPoint(JSON.parse(JSON.stringify(arr)))
// console.log('centerPoint', centerPoint)
// const autoScale = await calculateScaleByPoints(JSON.parse(JSON.stringify(arr))); // 调用计算方法
// scale.value = autoScale.scale; // 替换固定的scale:20
mapContext.moveToLocation({
latitude: polyline.value[0].points[0].latitude,
longitude: polyline.value[0].points[0].longitude
});
})
}
})
const fileWriter = () => {
// 请求本地系统文件对象 plus.io.PRIVATE_WWW应用运行资源目录常量
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, (fobject) => {
// fs.root是根目录操作对象DirectoryEntry
fobject.root.getFile(formatTime(new Date()) + '.txt', {
create: true
}, (fileEntry) => {
fileEntry.file((file) => {
console.log('file :>> ', file)
// create a FileWriter to write to the file
fileEntry.createWriter((writer) => {
console.log('writer :>> ', writer)
// Write data to file.
// 返回页面的数据
writer.seek(file.size)
// 写入可以加入需要写入的数据
let content = "{points:" + JSON.stringify(polyline.value[0].points) + ",pointArr:" + JSON.stringify(pointArr.value) + "}"
writer.write(content)
const fileReader = new plus.io.FileReader()
fileReader.readAsText(file, 'utf-8')
fileReader.onloadend = (evt) => {
console.log('evt :>> ', evt)
}
console.log('file info :>> ', file)
// 获取对象
plus.io.resolveLocalFileSystemURL(file.fullPath, (res) => {
res.file((localFile) => {
const localFilePath = localFile.fullPath; // 本地文件路径
console.log("localFilePath :>> ", localFilePath)
// 直接保存并打开本地文件
uni.openDocument({
filePath: localFilePath,
success: () => console.log('打开成功')
});
});
})
}, (e) => {
console.log('e :>> ', e)
})
})
})
})
}
// 1. 计算中心点(经纬度平均值)
// const getCenterPoint = (points) => {
// const sumLng = points.reduce((sum, p) => sum + p.longitude, 0);
// const sumLat = points.reduce((sum, p) => sum + p.latitude, 0);
// return {
// longitude: sumLng / points.length,
// latitude: sumLat / points.length
// };
// };
// const calculateScaleByPoints = async (points) => {
// if (points.length <= 1) return 20; // 单点默认缩放18
// // 1. 获取经纬度极值
// const extremes = getLatLngExtremes(points);
// const centerLat = (extremes.minLat + extremes.maxLat) / 2; // 轨迹中心纬度
// // 2. 计算范围宽度(米)
// const rangeWidth = getRangeWidth(extremes);
// // 3. 获取地图容器宽度(像素)
// const containerWidth = await getMapContainerWidth();
// // 4. 目标让范围宽度占容器80%(留边距,避免轨迹贴边)
// const targetMeterPerPixel = rangeWidth / (containerWidth * 0.9);
// const scale = calculatePreciseScale(targetMeterPerPixel, centerLat);
// return { centerLat, scale };
// };
// const getLatLngExtremes = (points) => {
// if (!points.length) return null;
// const lats = points.map(p => p.latitude);
// const lngs = points.map(p => p.longitude);
// return {
// minLat: Math.min(...lats), // 最小纬度
// maxLat: Math.max(...lats), // 最大纬度
// minLng: Math.min(...lngs), // 最小经度
// maxLng: Math.max(...lngs), // 最大经度
// };
// };
// // 计算范围的东西向实际宽度(米)
// const getRangeWidth = (extremes) => {
// // 取纬度中点(减少纬度对经度距离计算的影响)
// const centerLat = (extremes.minLat + extremes.maxLat) / 2;
// // 计算同一纬度下,最小和最大经度的距离(米)
// return haversineDistance(centerLat, extremes.minLng, centerLat, extremes.maxLng);
// };
// // 2. Haversine公式计算两点经纬度的球面距离单位
// const haversineDistance = (lat1, lon1, lat2, lon2) => {
// const R = 6371000; // 地球半径(米)
// const φ1 = (lat1 * Math.PI) / 180;
// const φ2 = (lat2 * Math.PI) / 180;
// const Δφ = ((lat2 - lat1) * Math.PI) / 180;
// const Δλ = ((lon2 - lon1) * Math.PI) / 180;
// const a = Math.sin(Δφ / 2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) ** 2;
// const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
// return R * c;
// };
// // 获取地图容器宽度(像素)- 适配uni-app
// const getMapContainerWidth = () => {
// // 获取容器节点信息
// return new Promise((resolve) => {
// uni.createSelectorQuery().in(getCurrentInstance())
// .select('.map-container')
// .boundingClientRect(rect => {
// resolve(rect.width); // 返回容器宽度(像素)
// })
// .exec();
// });
// };
// const calculatePreciseScale = (targetMeterPerPixel, centerLat) => {
// const earthRadius = 6378137; // 地球半径高德用的WGS84坐标系半径6378137米
// const centerLatRad = (centerLat * Math.PI) / 180; // 中心纬度转弧度
// // 高德官方公式:米/像素 = (2 * π * R * cos(lat)) / (2^(scale + 8))
// // 反向推导scale = log2( (2 * π * R * cos(lat)) / 米/像素 ) - 8
// const numerator = 2 * Math.PI * earthRadius * Math.cos(centerLatRad);
// const scaleLog = Math.log2(numerator / targetMeterPerPixel);
// let scale = scaleLog - 8;
// // scale必须是整数地图scale只支持整数同时限制范围10-20避免过大/过小)
// scale = Math.round(scale);
// console.log(scale, "scale")
// return Math.max(10, Math.min(20, scale));
// };
function formatTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 补 0 为两位数(如 09
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
const second = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
let pointArr = ref([
// { longitude: 116.478935, latitude: 39.997761 },
// { longitude: 116.478939, latitude: 39.997825 },
// // {longitude:116.478912,latitude: 39.998549},
// { longitude: 116.478912, latitude: 39.998549 },
// // {longitude:116.478998,latitude: 39.998555},
// { longitude: 116.478998, latitude: 39.998555 },
// { longitude: 116.479282, latitude: 39.99856 },
// { longitude: 116.479658, latitude: 39.998528 },
// { longitude: 116.480151, latitude: 39.998453 },
// // {longitude:116.480784,latitude: 39.998302},
// { longitude: 116.480784, latitude: 39.998302 },
// { longitude: 116.481149, latitude: 39.998184 },
// { longitude: 116.481573, latitude: 39.997997 },
// { longitude: 116.481863, latitude: 39.997846 },
// { longitude:116.482072,latitude: 39.997718 },
// { longitude: 116.482362, latitude: 39.997718 },
// { longitude: 116.483633, latitude: 39.998935 },
// { longitude: 116.48367, latitude: 39.998968 },
// { longitude: 116.484648, latitude: 39.999861 }
])
// 检查并请求定位权限
const requestLocationPermission = async () => {
if (plus.os.name === "Android") {
// 检查后台定位权限Android 10+
const hasBgPermission = await plus.android.requestPermissions(
["android.permission.ACCESS_BACKGROUND_LOCATION"]
);
// if (!hasBgPermission.granted) {
// uni.showToast({ title: "请授予后台定位权限", icon: "none" });
// return false;
// }
} else if (plus.os.name === "iOS") {
// iOS 需引导用户开启"始终允许"定位
const authStatus = await uni.getLocationAuthStatus();
if (authStatus.authStatus !== 3) { // 3 表示"始终允许"
uni.showModal({
title: "提示",
content: "请在设置中开启始终允许定位,否则锁屏后无法跟踪",
success: () => {
plus.runtime.openURL("app-settings:"); // 打开应用设置页
}
});
return false;
}
}
return true;
};
let locationWatchId = null
// 初始化地图
const startWatch = (async () => {
// 先请求权限
const hasPermission = await requestLocationPermission();
if (!hasPermission) return;
// 获取地图上下文
mapContext = uni.createMapContext('amap');
try {
// 直接在组件中实现定位功能,不再依赖外部工具类
uni.startLocationUpdate({
type: 'gcj02',
// needFullAccuracy: true,
success: res => {
console.log('开启应用接收位置消息成功')
locationWatchId = uni.onLocationChange(function (res) {
let time = formatTime(new Date())
// 检查是否已有最后一个点,且与当前点经纬度相同
const points = polyline.value[0].points;
res.time = time
// console.log('points', formatTime(new Date()));
let hasSameLastPoint = false
if (points.length) {
hasSameLastPoint = points[points.length - 1].latitude === res.latitude && points[points.length - 1].longitude === res.longitude;
}
if (markers.value.length == 0) {
markers.value = [{
id: 1,
latitude: res.latitude,
longitude: res.longitude,
iconPath: center,
width: 10, // 小车图标宽度rpx/px根据需求调整
height: 10, // 小车图标高度
}]
} else {
markers.value[0].latitude = res.latitude
markers.value[0].longitude = res.longitude
}
// console.log('hasSameLastPoint', hasSameLastPoint);
if (!hasSameLastPoint) {
polyline.value[0].points.push({
latitude: res.latitude,
longitude: res.longitude,
accuracy: res.accuracy,
speed: res.speed,
altitude: res.altitude,
time: time
});
console.log(polyline.value[0].points)
latitude.value = res.latitude;
longitude.value = res.longitude;
mapContext.moveToLocation({
latitude: latitude.value,
longitude: longitude.value
});
reportParams.value.lat = res.latitude
reportParams.value.lng = res.longitude
// console.log('reportParams', reportParams.value);
locationReport(reportParams.value).then((res) => {
console.log("页面", res)
if (res.code == 401) {
console.log("停止")
stopLocationUpdate();
}
})
// console.log('纬度:' + res.latitude);
// console.log('经度:' + res.longitude);
}
// pointArr.value.push(res);
// console.log(pointArr.value)
});
},
fail: err => {
console.error('开启应用接收位置消息失败:', err)
},
complete: msg => console.log('调用开启应用接收位置消息 API 完成')
});
} catch (error) {
console.error('获取位置失败:', error);
uni.showToast({
title: '获取位置失败,使用默认位置',
icon: 'none'
});
}
});
// 页面卸载时清理资源
onUnmounted(() => {
stopLocationUpdate();
});
onUnload(() => {
stopLocationUpdate();
});
const stopLocationUpdate = () => {
if (locationWatchId) {
uni.offLocationChange();
locationWatchId = null;
}
uni.stopLocationUpdate({
success: () => console.log('已停止位置更新'),
fail: (err) => console.error('停止位置更新失败:', err)
});
};
</script>
<style scoped>
.container {
display: flex;
flex-direction: column;
height: calc(100vh - env(safe-area-inset-bottom));
}
.map-container {
flex: 1;
position: relative;
}
.center-img {
height: 80rpx;
left: 50%;
margin-left: -40rpx;
margin-top: -88rpx;
top: 50%;
width: 80rpx;
z-index: 9999;
position: absolute;
}
.position-img {
bottom: 32rpx;
height: 66rpx;
right: 16rpx;
width: 66rpx;
border-radius: 50%;
background-color: #fff;
overflow: hidden;
z-index: 9999;
position: absolute;
}
</style>