2025-03-31 15:26:29 +00:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
<div class="header-left">
|
2025-06-18 15:08:37 +00:00
|
|
|
|
<div class="time_date">
|
|
|
|
|
|
{{ currentDate }} <span>{{ currentTime }}</span>
|
|
|
|
|
|
</div>
|
2025-03-31 15:26:29 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="header-main">
|
2025-06-21 12:00:17 +00:00
|
|
|
|
<div class="textTile"></div>
|
2025-03-31 15:26:29 +00:00
|
|
|
|
</div>
|
2025-06-19 15:41:07 +00:00
|
|
|
|
<div class="header-menu">
|
|
|
|
|
|
<span>{{ leftText }}</span>
|
|
|
|
|
|
<span style="width: 20px"> | </span>
|
|
|
|
|
|
<span>{{ rightText }}</span>
|
|
|
|
|
|
</div>
|
2025-06-21 12:00:17 +00:00
|
|
|
|
<div v-if="showPermissionPrompt" class="permission-prompt">
|
|
|
|
|
|
<p>请允许地理位置权限以启用定位功能</p>
|
|
|
|
|
|
<p>您可以在浏览器地址栏设置中启用位置访问,或点击下方按钮重试</p>
|
|
|
|
|
|
<button @click="retryTracking">重试定位</button>
|
|
|
|
|
|
</div>
|
2025-03-31 15:26:29 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-06-21 12:00:17 +00:00
|
|
|
|
import { mapGetters } from "vuex";
|
|
|
|
|
|
import { HomeSyncLocation } from "@/api/home";
|
2025-06-24 15:33:44 +00:00
|
|
|
|
import Geolocation from "ol/Geolocation";
|
|
|
|
|
|
import { fromLonLat, transform } from "ol/proj";
|
|
|
|
|
|
import { Vector as VectorLayer } from "ol/layer";
|
|
|
|
|
|
import VectorSource from "ol/source/Vector";
|
|
|
|
|
|
import Point from "ol/geom/Point";
|
|
|
|
|
|
import { Style, Icon, Stroke } from "ol/style";
|
|
|
|
|
|
import Feature from "ol/Feature";
|
2025-03-31 15:26:29 +00:00
|
|
|
|
export default {
|
|
|
|
|
|
name: "header-top",
|
|
|
|
|
|
props: {
|
|
|
|
|
|
homeData: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: () => ({})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
currentTime: "",
|
|
|
|
|
|
currentDate: "",
|
2025-06-19 15:41:07 +00:00
|
|
|
|
isAdmins: false,
|
|
|
|
|
|
leftText: "平台已连接",
|
2025-06-21 12:00:17 +00:00
|
|
|
|
rightText: "定位正常",
|
2025-06-22 09:42:09 +00:00
|
|
|
|
latitude: "",
|
|
|
|
|
|
longitude: "",
|
2025-06-21 12:00:17 +00:00
|
|
|
|
lastUpdated: "",
|
|
|
|
|
|
timer: null,
|
2025-06-24 15:33:44 +00:00
|
|
|
|
locationTimer: null,
|
|
|
|
|
|
geolocation: null,
|
2025-06-22 09:42:09 +00:00
|
|
|
|
showPermissionPrompt: false,
|
2025-06-24 15:33:44 +00:00
|
|
|
|
retryCount: 0,
|
|
|
|
|
|
positionLayer: null
|
2025-03-31 15:26:29 +00:00
|
|
|
|
};
|
|
|
|
|
|
},
|
2025-06-21 12:00:17 +00:00
|
|
|
|
computed: {
|
|
|
|
|
|
...mapGetters(["positionPoint"])
|
2025-03-31 15:26:29 +00:00
|
|
|
|
},
|
|
|
|
|
|
mounted() {
|
2025-06-21 12:00:17 +00:00
|
|
|
|
console.log("组件挂载,启动定位");
|
2025-04-02 14:51:32 +00:00
|
|
|
|
this.isAdmins = JSON.parse(localStorage.getItem("isAdmin"));
|
2025-03-31 15:26:29 +00:00
|
|
|
|
this.updateTime();
|
2025-06-24 15:33:44 +00:00
|
|
|
|
|
2025-06-21 12:00:17 +00:00
|
|
|
|
this.timer = setInterval(() => {
|
|
|
|
|
|
this.updateTime();
|
|
|
|
|
|
}, 1000);
|
2025-06-24 15:33:44 +00:00
|
|
|
|
this.startTracking();
|
2025-06-21 12:00:17 +00:00
|
|
|
|
},
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
positionPoint: {
|
2025-06-22 09:42:09 +00:00
|
|
|
|
handler(newVal) {
|
2025-06-25 14:27:05 +00:00
|
|
|
|
if (newVal) {
|
|
|
|
|
|
// this.$message.success(this.latitude + ", " + this.longitude);
|
|
|
|
|
|
console.log("位置更新", newVal, this.latitude, this.longitude);
|
|
|
|
|
|
if (newVal && this.latitude && this.longitude && window.olMap) {
|
|
|
|
|
|
const newLocation = fromLonLat(
|
|
|
|
|
|
[this.longitude, this.latitude],
|
|
|
|
|
|
"EPSG:3857"
|
|
|
|
|
|
);
|
|
|
|
|
|
this.$message.success("定位成功");
|
|
|
|
|
|
this.updateMapPosition(this.longitude, this.latitude);
|
|
|
|
|
|
const zoomLevel = 13;
|
|
|
|
|
|
window.olMap.getView().animate({
|
|
|
|
|
|
center: newLocation,
|
|
|
|
|
|
zoom: zoomLevel
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.error("定位失败,请稍后重试");
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$store.commit("SET_POSITIONPOINT", false);
|
2025-06-21 12:00:17 +00:00
|
|
|
|
}
|
2025-06-22 09:42:09 +00:00
|
|
|
|
},
|
|
|
|
|
|
deep: true
|
|
|
|
|
|
}
|
2025-03-31 15:26:29 +00:00
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
2025-06-24 15:33:44 +00:00
|
|
|
|
updateMapPosition(lng, lat) {
|
2025-06-24 16:12:35 +00:00
|
|
|
|
// 移除旧的 positionLayer(如果存在)
|
|
|
|
|
|
if (this.positionLayer) {
|
|
|
|
|
|
window.olMap.removeLayer(this.positionLayer);
|
|
|
|
|
|
this.positionLayer = null;
|
|
|
|
|
|
}
|
2025-06-24 15:33:44 +00:00
|
|
|
|
let center = fromLonLat([lng, lat]);
|
|
|
|
|
|
var iconFeature = new Feature({
|
|
|
|
|
|
geometry: new Point(center),
|
|
|
|
|
|
name: "当前位置",
|
|
|
|
|
|
population: 4000,
|
|
|
|
|
|
rainfall: 500
|
|
|
|
|
|
});
|
|
|
|
|
|
var iconStyle = new Style({
|
|
|
|
|
|
image: new Icon({
|
|
|
|
|
|
anchor: [0.5, 46],
|
|
|
|
|
|
scale: 0.4,
|
|
|
|
|
|
anchorXUnits: "fraction",
|
|
|
|
|
|
anchorYUnits: "pixels",
|
|
|
|
|
|
src: require("@/assets/img/icon_dev.png")
|
|
|
|
|
|
})
|
|
|
|
|
|
});
|
|
|
|
|
|
iconFeature.setStyle(iconStyle);
|
|
|
|
|
|
var vectorSource = new VectorSource({
|
|
|
|
|
|
features: [iconFeature]
|
|
|
|
|
|
});
|
|
|
|
|
|
this.positionLayer = new VectorLayer({
|
|
|
|
|
|
source: vectorSource
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
window.olMap.addLayer(this.positionLayer);
|
|
|
|
|
|
},
|
2025-03-31 15:26:29 +00:00
|
|
|
|
updateTime() {
|
|
|
|
|
|
const now = new Date();
|
2025-06-21 12:00:17 +00:00
|
|
|
|
this.currentTime = now.toLocaleTimeString();
|
|
|
|
|
|
this.currentDate = now.toLocaleDateString();
|
|
|
|
|
|
},
|
|
|
|
|
|
startTracking() {
|
2025-06-24 15:33:44 +00:00
|
|
|
|
if (!window.olMap) {
|
|
|
|
|
|
console.error("OpenLayers 地图未传入,延迟重试");
|
|
|
|
|
|
setTimeout(() => this.startTracking(), 1000);
|
2025-06-21 12:00:17 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-24 15:33:44 +00:00
|
|
|
|
|
|
|
|
|
|
// 清除旧的 locationTimer
|
|
|
|
|
|
if (this.locationTimer) {
|
|
|
|
|
|
clearInterval(this.locationTimer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化 Geolocation
|
|
|
|
|
|
this.geolocation = new Geolocation({
|
|
|
|
|
|
trackingOptions: {
|
2025-06-22 09:42:09 +00:00
|
|
|
|
enableHighAccuracy: true,
|
|
|
|
|
|
timeout: 30000,
|
|
|
|
|
|
maximumAge: 10000
|
2025-06-24 15:33:44 +00:00
|
|
|
|
},
|
|
|
|
|
|
projection: window.olMap.getView().getProjection()
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听定位错误
|
|
|
|
|
|
this.geolocation.on("error", (error) => {
|
|
|
|
|
|
let message = "";
|
|
|
|
|
|
this.retryCount = this.retryCount || 0;
|
|
|
|
|
|
switch (error.code) {
|
|
|
|
|
|
case error.PERMISSION_DENIED:
|
|
|
|
|
|
message = "请允许地理位置权限";
|
|
|
|
|
|
this.showPermissionPrompt = true;
|
|
|
|
|
|
this.$message.error("请在浏览器设置中启用地理位置权限");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case error.POSITION_UNAVAILABLE:
|
|
|
|
|
|
message = "无法获取位置信息";
|
|
|
|
|
|
if (this.retryCount < 3) {
|
|
|
|
|
|
this.retryCount++;
|
|
|
|
|
|
setTimeout(() => this.startTracking(), 5000);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.error("多次尝试后仍无法定位,请检查设备设置");
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case error.TIMEOUT:
|
|
|
|
|
|
message = "获取位置超时";
|
|
|
|
|
|
if (this.retryCount < 3) {
|
|
|
|
|
|
this.retryCount++;
|
|
|
|
|
|
setTimeout(() => this.startTracking(), 5000);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.error("定位超时,请检查网络或 GPS 信号");
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
message = "未知错误";
|
2025-06-21 12:00:17 +00:00
|
|
|
|
}
|
2025-06-24 15:33:44 +00:00
|
|
|
|
this.leftText = message;
|
|
|
|
|
|
this.rightText = "定位异常";
|
2025-06-21 12:00:17 +00:00
|
|
|
|
});
|
2025-06-24 15:33:44 +00:00
|
|
|
|
|
|
|
|
|
|
// 开启定位
|
|
|
|
|
|
this.geolocation.setTracking(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 每 2 秒传递一次经纬度
|
|
|
|
|
|
this.locationTimer = setInterval(() => {
|
|
|
|
|
|
const coordinates = this.geolocation.getPosition();
|
|
|
|
|
|
if (coordinates) {
|
|
|
|
|
|
this.handleSuccess(coordinates);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 2000);
|
|
|
|
|
|
},
|
|
|
|
|
|
handleSuccess(coordinates) {
|
|
|
|
|
|
// 转换为 WGS84 坐标
|
|
|
|
|
|
const wgs84 = transform(coordinates, "EPSG:3857", "EPSG:4326");
|
|
|
|
|
|
this.longitude = wgs84[0];
|
|
|
|
|
|
this.latitude = wgs84[1];
|
|
|
|
|
|
this.lastUpdated = new Date().toLocaleString(); //EPSG:3857 转 EPSG:4326
|
|
|
|
|
|
this.showPermissionPrompt = false;
|
|
|
|
|
|
this.retryCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 传递经纬度到 API
|
2025-06-21 12:00:17 +00:00
|
|
|
|
let params = {
|
2025-06-24 15:33:44 +00:00
|
|
|
|
lat: this.latitude,
|
|
|
|
|
|
lon: this.longitude,
|
2025-06-21 12:00:17 +00:00
|
|
|
|
userId: localStorage.getItem("userId")
|
|
|
|
|
|
};
|
2025-06-24 15:33:44 +00:00
|
|
|
|
this.updateMapPosition(this.longitude, this.latitude);
|
|
|
|
|
|
// console.log("触发上报:", {
|
|
|
|
|
|
// latitude: this.latitude,
|
|
|
|
|
|
// longitude: this.longitude,
|
|
|
|
|
|
// time: this.lastUpdated
|
|
|
|
|
|
// });
|
2025-06-21 12:00:17 +00:00
|
|
|
|
HomeSyncLocation(params)
|
|
|
|
|
|
.then((res) => {
|
|
|
|
|
|
if (res.code === 0) {
|
|
|
|
|
|
this.leftText = "平台已连接";
|
|
|
|
|
|
this.rightText = "定位正常";
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.leftText = "平台连接失败";
|
|
|
|
|
|
this.rightText = "定位异常";
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
|
this.leftText = "平台连接失败";
|
|
|
|
|
|
this.rightText = "定位异常";
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
retryTracking() {
|
|
|
|
|
|
this.showPermissionPrompt = false;
|
2025-06-22 09:42:09 +00:00
|
|
|
|
this.retryCount = 0;
|
2025-06-21 12:00:17 +00:00
|
|
|
|
this.startTracking();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
2025-06-24 15:33:44 +00:00
|
|
|
|
if (this.geolocation) {
|
|
|
|
|
|
this.geolocation.setTracking(false);
|
2025-06-21 12:00:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
if (this.timer) {
|
|
|
|
|
|
clearInterval(this.timer);
|
2025-03-31 15:26:29 +00:00
|
|
|
|
}
|
2025-06-24 15:33:44 +00:00
|
|
|
|
if (this.locationTimer) {
|
|
|
|
|
|
clearInterval(this.locationTimer);
|
|
|
|
|
|
}
|
2025-03-31 15:26:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.logo {
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
.time {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.actions span {
|
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
2025-06-21 12:00:17 +00:00
|
|
|
|
.permission-prompt {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 20px;
|
|
|
|
|
|
right: 20px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
padding: 15px;
|
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
2025-06-22 09:42:09 +00:00
|
|
|
|
z-index: 10000;
|
|
|
|
|
|
pointer-events: auto;
|
2025-06-21 12:00:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
.permission-prompt p {
|
|
|
|
|
|
margin: 0 0 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.permission-prompt button {
|
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
|
background: #409eff;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
2025-03-31 15:26:29 +00:00
|
|
|
|
</style>
|