问题详细描述:
目前公司业务需要可以通过小程序定位到员工的位置,但是员工微信小程序不可能一直前台运行着,大部分情况下还是后台运行,为此申请开通了wx.onLocationChange、wx.startLocationUpdate、wx.startLocationUpdateBackground接口,但是发现ios系统没问题,可以后台运行,安卓手机超过一定时间后,小程序就会重置,以下是代码片段
<template>
<view>
<custom-tab-bar>
<view class="container">
<map id="map" :longitude="longitude" :latitude="latitude" :scale="17"
:style="{ width: '100%', height: mapHeight + 'px' }" show-location :polyline="polyline"
:markers="markers" @regionchange="regionChange"></map>
<view class="toggle-info" @click="toggleInfo">
<image v-if="showInfo" src="@/static/collapse.png" />
<image v-else src="@/static/expand.png" />
</view>
<view class="banner">
<view class="info">
<text>轨迹距离: {{ (distance / 1000).toFixed(2) }} 公里</text>
<text>海拔高度: {{ altitude }} 米</text>
</view>
<view class="controls" v-show="showInfo">
<image v-if="!tracking && !paused" src="@/static/start.png" @click="startTracking" />
<image v-if="paused" src="@/static/continue.png" @click="resumeTracking" />
<image v-if="tracking" src="@/static/stop.png" @click="stopTracking" />
<image v-if="paused" src="@/static/pause.png" @click="confirmTerminateTracking" />
</view>
<!-- <text v-if="elapsedTime">经过时间: {{ Math.floor(elapsedTime / 60) }} 分 {{ elapsedTime % 60 }} 秒</text> -->
</view>
</view>
</custom-tab-bar>
</view>
</template>
<script>
import {
startInspection,
endInspection
} from '@/api/track.js'
import {
getFormattedDate,
} from '@/utils/common.js'
import CustomTabBar from '@/pages/components/customTabbar.vue';
export default {
components: {
CustomTabBar
},
data() {
return {
longitude: 0,
latitude: 0,
altitude: 0,
distance: 0,
tracking: false,
paused: false,
path: [], // 存储移动轨迹
polyline: [], // 生成移动轨迹
lastLocation: null, // 存储上一个有效位置
isStart: false, // 是否满足调用开始接口
isEnd: false, // 是否满足调用结束接口
startId: null, // ID获取
startTime: 0, // 记录开始时间
// elapsedTime: 0, // 添加经过时间字段,以秒为单位
// timerId: null // 定时器ID
locationChangeHandler: null,
inspectionSortKey: 0,
showInfo: true,
mapHeight: 520,
systemInfo: null,
markers: [],
};
},
mounted() {
this.systemInfo = uni.getSystemInfoSync();
this.mapHeight = this.systemInfo.platform === 'ios' ? 520 : 480; // 根据需要调整高度
},
onLoad() {
this.restoreState();
this.requestLocationPermission();
},
onUnload() {
// this.saveState();
},
onHide() {
this.saveState();
},
methods: {
requestLocationPermission() {
uni.authorize({
scope: 'scope.userLocationBackground',
success: () => {
this.getCurrentLocation();
},
fail: () => {
uni.showModal({
title: '提示',
content: '请授权获取位置信息,否则无法使用该功能',
showCancel: false
});
}
});
},
getCurrentLocation() {
uni.getLocation({
type: 'gcj02',
altitude: true,
success: (res) => {
if (res && res.longitude !== undefined && res.latitude !== undefined) {
this.longitude = res.longitude;
this.latitude = res.latitude;
this.altitude = (res.altitude || 0).toFixed(2);
this.lastLocation = {
longitude: res.longitude,
latitude: res.latitude
};
} else {
console.error("获取位置返回值不正确", res);
}
},
fail: (err) => {
console.error("获取位置失败", err);
}
});
},
startTracking() {
wx.startLocationUpdateBackground({
type: 'wgs84',
success: (res) => {
// console.log(res, 'startLocationUpdateBackgroundstartLocationUpdateBackground')
if (this.tracking) return;
this.isStart = true;
this.tracking = true;
this.paused = false;
this.path = [];
this.polyline = [];
this.getLocationAndUpdate();
// this.timerId = setInterval(() => {
// this.elapsedTime++;
// }, 1000);
},
fail: (err) => {
console.error('开始后台定位失败', err);
}
});
},
getLocationAndUpdate() {
this.locationChangeHandler = async (res) => {
const {
longitude,
latitude,
altitude,
accuracy,
} = res;
console.log(res, 'resresresres')
this.altitude = (altitude || 0).toFixed(2);
if (this.isSignificantChange(longitude, latitude) && this.startId !== null && accuracy < 40) {
this.inspectionSortKey++
this.path.push({
longitude,
latitude,
height: (altitude || 0).toFixed(2),
inspectionSortKey: this.inspectionSortKey
});
this.updatePolyline();
this.calculateDistance();
this.longitude = longitude;
this.latitude = latitude;
this.lastLocation = {
longitude,
latitude
}; // 更新上一个位置
} else if (this.startId === null) {
if (this.isStart) {
await this.reverseGeocode(longitude, latitude, res);
}
}
};
wx.onLocationChange(this.locationChangeHandler);
},
async stopTracking() {
if (!this.tracking) return;
this.tracking = false;
this.paused = true;
// if (this.timerId !== null) {
// clearInterval(this.timerId);
// this.timerId = null;
// }
await this.getLocationAndUpdate();
if (this.locationChangeHandler) {
wx.offLocationChange(this.locationChangeHandler);
this.locationChangeHandler = null; // 清空引用
}
},
resumeTracking() {
wx.startLocationUpdateBackground({
type: 'wgs84',
success: (res) => {
if (this.tracking) return;
this.tracking = true;
this.paused = false;
this.getLocationAndUpdate();
},
fail: (err) => {
console.error('开始后台定位失败', err);
}
});
// this.timerId = setInterval(() => {
// this.elapsedTime++;
// }, 1000);
},
confirmTerminateTracking() {
uni.showModal({
title: '确认终止',
content: '请确认本次轨迹定位是否结束',
success: (res) => {
if (res.confirm) {
this.isEnd = true;
this.isStart = false;
uni.getLocation({
type: 'gcj02',
altitude: true,
success: async (res) => {
const {
longitude,
latitude,
altitude
} = res;
await this.reverseGeocode(longitude, latitude, res);
this.markers.push({
id: 2,
latitude,
longitude,
iconPath: '/static/road-stop.png', // 开始图标路径
width: 60,
height: 60,
});
setTimeout(() => {
this.markers = [];
}, 2000)
},
fail: (err) => {
console.error("获取位置失败", err)
}
});
}
}
});
},
updatePolyline() {
this.polyline = [{
points: this.path,
color: "#FF0000DD",
width: 5,
dottedLine: false
}];
},
calculateDistance() {
if (this.path.length < 2) return;
const lastPoint = this.path[this.path.length - 2];
const currentPoint = this.path[this.path.length - 1];
const distance = this.getDistance(lastPoint, currentPoint);
this.distance += distance;
},
getDistance(point1, point2) {
const radLat1 = (point1.latitude * Math.PI) / 180.0;
const radLat2 = (point2.latitude * Math.PI) / 180.0;
const a = radLat1 - radLat2;
const b = (point1.longitude * Math.PI) / 180.0 - (point2.longitude * Math.PI) / 180.0;
let s = 2 * Math.asin(
Math.sqrt(
Math.pow(Math.sin(a / 2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
)
);
s = s * 6378137.0; // 地球半径
s = Math.round(s * 10000) / 10000.0;
return s;
},
isSignificantChange(longitude, latitude) {
if (!this.lastLocation) return true;
const deltaLongitude = Math.abs(longitude - this.lastLocation.longitude);
const deltaLatitude = Math.abs(latitude - this.lastLocation.latitude);
return deltaLongitude > 0 || deltaLatitude > 0;
},
regionChange(e) {
// console.log('region change', e);
},
async reverseGeocode(longitude, latitude, data) {
// const key = '6b7fd0d3e900c4be1279cb705e103953';
const key = '2f2f22f5109d0b452c754da47fd45f6d';
const url = `https://restapi.amap.com/v3/geocode/regeo?location=${longitude},${latitude}&key=${key}`;
// const url = `https://restapi.amap.com/v3/geocode/regeo?location=119.146617,33.618107&key=${key}`;
try {
let params;
const res = await new Promise((resolve, reject) => {
uni.request({
url: url,
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
}
});
});
if (this.isStart) {
const formattedDate = getFormattedDate();
if (!this.startId) this.startTime = formattedDate;
const {
city,
province,
district,
township
} = res.data.regeocode.addressComponent;
params = {
id: this.startId,
longitude,
latitude,
city: Array.isArray(city) ? null : city,
province,
district,
township,
address: res.data.regeocode.formatted_address,
height: (data.altitude || 0).toFixed(2),
inspectionSortKey: this.inspectionSortKey,
inspectionStartTime: formattedDate
};
const startRes = await startInspection(params);
this.startId = startRes.data;
this.markers.push({
id: 1,
latitude,
longitude,
iconPath: '/static/road-start.png', // 开始图标路径
width: 60,
height: 60,
});
if (this.path.length === 0) {
this.inspectionSortKey++;
this.path.push({
longitude,
latitude,
height: (data.altitude || 0).toFixed(2),
inspectionSortKey: this.inspectionSortKey
});
}
this.isStart = false;
}
if (this.isEnd) {
params = {
id: this.startId,
userInspectionDtoList: this.path,
totalDistance: this.distance.toFixed(2),
}
await endInspection(params);
this.paused = false;
this.tracking = false;
this.isEnd = false;
this.distance = 0;
this.altitude = 0;
this.startId = null;
this.path = [];
this.polyline = [];
this.inspectionSortKey = 0;
uni.removeStorageSync('trackingState');
wx.stopLocationUpdate({
success: () => {
console.log('成功停止后台定位');
if (this.locationChangeHandler) {
wx.offLocationChange(this.locationChangeHandler);
this.locationChangeHandler = null; // 清空引用
}
},
fail: (err) => {
console.error('停止后台定位失败', err);
}
});
}
} catch (e) {
console.error("反向地理编码请求失败", e);
}
},
saveState() {
const state = {
startId: this.startId, // 开始运动ID
startTime: this.startTime, // 开始日期
isStart: this.isStart, // 开始状态
tracking: this.tracking, // 按钮显示状态
paused: this.paused, // 按钮显示状态
isEnd: this.isEnd, // 是否满足结束状态
lastLocation: this.lastLocation, // 存储上一个有效位置
path: this.path,
hideTime: getFormattedDate(),
inspectionSortKey: this.inspectionSortKey,
// distance: this.distance,
// polyline: this.polyline,
};
uni.setStorageSync('trackingState', state);
},
restoreState() {
this.constructor();
const state = uni.getStorageSync('trackingState');
if (state) {
if (state.startId) {
const currentDate = new Date();
const pastDateObj = new Date(state.hideTime);
const timeDifference = currentDate - pastDateObj;
if (timeDifference > 240000) {
uni.showModal({
title: '提示',
content: '检测到您上次的跑步还没有结束,是否继续?',
success: (res) => {
if (res.confirm) {
const {
startId,
startTime,
isStart,
tracking,
paused,
isEnd,
lastLocation,
path,
inspectionSortKey
} = state;
this.startId = startId; // 开始运动ID
this.startTime = startTime; // 开始日期
this.isStart = isStart; // 开始状态
this.tracking = tracking; // 按钮显示状态
this.paused = paused; // 按钮显示状态
this.isEnd = isEnd; // 是否满足结束状态
this.lastLocation = lastLocation; // 存储上一个有效位置
this.path = path;
this.inspectionSortKey = inspectionSortKey;
// if (tracking) {
// const pastDate = new Date(startTime); // 过去的日期
// const currentDate = new Date(); // 当前日期
// // 计算时间差(以秒为单位)
// this.elapsedTime = Math.floor((currentDate - pastDate) /
// 1000); // 转换为秒
// }
// this.timerId = setInterval(() => {
// this.elapsedTime++;
// }, 1000);
} else if (res.cancel) {
this.confirmTerminateTracking()
}
}
});
}
}
}
},
constructor() {
this.longitude = 0;
this.latitude = 0;
this.altitude = 0;
this.distance = 0;
this.tracking = false;
this.paused = false;
this.path = []; // 存储移动轨迹
this.polyline = []; // 生成移动轨迹
this.lastLocation = null; // 存储上一个有效位置
this.isStart = false; // 是否满足调用开始接口
this.isEnd = false; // 是否满足调用结束接口
this.startId = null; // ID获取
this.startTime = 0; // 记录开始时间
this.inspectionSortKey = 0;
// this.elapsedTime = 0; // 添加经过时间字段,以秒为单位
// this.timerId = null; // 定时器ID
},
toggleInfo() {
this.showInfo = !this.showInfo; // 切换信息的显示状态
if (this.showInfo) {
this.mapHeight = this.systemInfo.platform === 'ios' ? 520 : 480; // 根据需要调整高度
} else {
this.mapHeight = this.systemInfo.platform === 'ios' ? 600 : 550; // 根据需要调整高度
}
},
}
};
</script>
<style>
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.controls {
margin-top: 20px;
display: flex;
flex-direction: row;
justify-content: center;
gap: 15px;
}
.controls image {
width: 50px;
height: 50px;
cursor: pointer;
}
.toggle-info {
width: 100%;
padding-bottom: 10px;
text-align: center;
}
.toggle-info image {
width: 25px;
height: 25px;
}
.banner {
width: 100%;
}
.info {
display: flex;
align-items: center;
justify-content: center;
padding: 0 30px;
}
.info text {
flex: 1
}
.info text:last-child {
text-align: right;
}
</style>