Files

439 lines
14 KiB
Vue
Raw Permalink Normal View History

2026-03-11 14:52:32 +08:00
<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>