958 lines
32 KiB
Vue
958 lines
32 KiB
Vue
<template>
|
||
<div class="left-sidebar" :class="{ contracted: isContracted }">
|
||
<div class="alarm-border warningAnimatBox" v-if="iswarning"></div>
|
||
<div class="stats">
|
||
<div class="stat-item" v-for="(item, index) in warningDay" :key="index">
|
||
<div class="stat-name">{{ item.name }}</div>
|
||
<div class="stat-name">
|
||
<span>{{ item.value }}</span> 次
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="drone-list">
|
||
<ul>
|
||
<li
|
||
v-for="(drone, index) in drones"
|
||
:key="index"
|
||
:style="{
|
||
height: droneStates[drone.BatchId] ? '60px' : '372px',
|
||
padding: 0
|
||
}"
|
||
>
|
||
<div
|
||
class="details-one"
|
||
v-if="droneStates[drone.BatchId]"
|
||
@click="handleDroneClick(drone)"
|
||
:style="{
|
||
backgroundImage: !drone.IsWhitelist
|
||
? `url(${require('@/assets/img/uavBackred.png')})`
|
||
: `url(${require('@/assets/img/uavBackfff.png')})`
|
||
}"
|
||
>
|
||
<div class="top">
|
||
<div class="left">
|
||
{{ index < 9 ? "0" + (index + 1) : index + 1 }}
|
||
</div>
|
||
<div class="text" style="font-size: 14px">
|
||
{{ drone.serial_number }}
|
||
</div>
|
||
<div class="img-vector">
|
||
<img src="@/assets/img/Vector.png" alt="" />
|
||
</div>
|
||
</div>
|
||
<div class="main">
|
||
<div class="top_main">
|
||
<div class="text">
|
||
<span class="color-ef">距离:</span>
|
||
<span class="text-fff">{{ drone.distance || 0 }}米</span>
|
||
</div>
|
||
<div class="text">
|
||
<span class="color-ef">高度:</span>
|
||
<span class="text-fff">{{ drone.height }}米</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="details-all"
|
||
v-else
|
||
@click="handleDroneClick(drone)"
|
||
:style="{
|
||
backgroundImage: !drone.IsWhitelist
|
||
? `url(${require('@/assets/img/uavBackred-big.png')})`
|
||
: `url(${require('@/assets/img/uavBackfff-big.png')})`
|
||
}"
|
||
>
|
||
<div class="top">
|
||
<div class="left">
|
||
{{ index < 9 ? "0" + (index + 1) : index + 1 }}
|
||
</div>
|
||
<div class="text" style="font-size: 14px">
|
||
{{ drone.serial_number }}
|
||
</div>
|
||
<div class="img-vector">
|
||
<img src="@/assets/img/Vector.png" alt="" />
|
||
</div>
|
||
</div>
|
||
<div class="content-main">
|
||
<div class="content-top">
|
||
<div class="top-left">
|
||
<span class="color-ef"> 距离:</span>
|
||
<span class="text-fff">{{ drone.distance || 0 }}米</span>
|
||
</div>
|
||
<div class="top-right">
|
||
<span class="color-ef"> 高度:</span>
|
||
<span class="text-fff">{{ drone.height || 0 }}米</span>
|
||
</div>
|
||
</div>
|
||
<div class="content-serial">
|
||
<span class="color-ef"> 序列号:</span>
|
||
<span class="text-fff">{{ drone.serial_number }}</span>
|
||
</div>
|
||
<div class="content-serial">
|
||
<span class="color-ef"> 型号:</span>
|
||
<span class="text-fff">{{ drone.device_type }}</span>
|
||
</div>
|
||
<div class="content-serial">
|
||
<span class="color-ef"> 更新时间:</span>
|
||
<span class="text-fff-time">{{ drone.times }}</span>
|
||
</div>
|
||
<div class="content-serial">
|
||
<span class="color-ef-fw">无人机方位(经纬度):</span>
|
||
</div>
|
||
<div class="content-serial" style="height: 35%">
|
||
<div class="content-potions">
|
||
<div class="content-potions-lon">
|
||
<p class="characters">
|
||
{{ drone.drone_lon }}
|
||
<span v-if="drone.drone_lon !== null">,</span>
|
||
{{ drone.drone_lat }}
|
||
</p>
|
||
</div>
|
||
<div class="content-potions-bottom">
|
||
<div
|
||
class="content-text content-potions-lat"
|
||
style="width: 100%; display: flex; align-items: center"
|
||
>
|
||
<p class="text">
|
||
距本设备距离:
|
||
<span class="characters" v-if="drone.distanceMeters">
|
||
{{ drone.distanceMeters }}M
|
||
</span>
|
||
<span class="characters" v-else></span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="content-potions-lon">
|
||
<p class="text">速度:</p>
|
||
<p class="characters">
|
||
E:{{ drone.speed_E }} N:{{ drone.speed_N }} U:{{
|
||
drone.speed_U
|
||
}}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="content-serial">
|
||
<span class="color-ef-fw">飞手位置(经纬度):</span>
|
||
</div>
|
||
<div class="content-serial" style="height: 7%; margin-bottom: 5%">
|
||
<div class="content-fs">
|
||
<div class="content-fs-lon">
|
||
<p class="characters">
|
||
{{ drone.app_lon }}
|
||
<span v-if="drone.app_lon !== null">,</span>
|
||
{{ drone.app_lat }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="content-serial" style="height: 10%">
|
||
<div class="btns">
|
||
<div class="btn-trust">
|
||
<el-button
|
||
type="primary"
|
||
@click.stop="handlewhiteList(drone)"
|
||
>
|
||
{{ !drone.IsWhitelist ? "信任" : "不信任" }}
|
||
</el-button>
|
||
</div>
|
||
<div class="btn-navigation">
|
||
<el-button
|
||
type="primary"
|
||
@click.stop="handleNavigation(drone)"
|
||
>
|
||
导航
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="navigation-content"
|
||
v-if="navigationStates[drone.BatchId]"
|
||
>
|
||
<!-- <div class="navigation-content-text">
|
||
<span class="color-ef">距飞手:</span>
|
||
<span class="text-fff">{{ drone.height || 0 }}米</span>
|
||
</div> -->
|
||
<div class="navigation-content-text" style="text-align: center">
|
||
<span class="color-ef" style="font-weight: 800">
|
||
导航高德地图
|
||
</span>
|
||
</div>
|
||
<div class="navigation-content-qrcode">
|
||
<img
|
||
v-if="qrCodes[drone.BatchId]"
|
||
:src="qrCodes[drone.BatchId]"
|
||
alt="导航二维码"
|
||
/>
|
||
<span v-else>二维码生成中...</span>
|
||
</div>
|
||
<div class="navigation-content-text">
|
||
<span class="text-fff">跳转到导航系统</span>
|
||
</div>
|
||
<div class="navigation-content-btn">
|
||
<div class="btns">
|
||
<div class="btn-trust">
|
||
<el-button
|
||
type="primary"
|
||
@click.stop="closeNavigation(drone)"
|
||
>
|
||
是
|
||
</el-button>
|
||
</div>
|
||
<div class="btn-navigation">
|
||
<el-button type="primary" @click.stop="noNavigation(drone)">
|
||
否
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<audio
|
||
controls
|
||
loop
|
||
ref="uavAudio"
|
||
v-show="iswarning"
|
||
style="display: none; pointer-events: auto"
|
||
id="uavAudio"
|
||
>
|
||
<source src="@/assets/img/wargin.mp3" type="audio/mpeg" />
|
||
</audio>
|
||
<div class="audio-prompt" v-if="showAudioPrompt" @click="enableAudio">
|
||
<div class="prompt-content">
|
||
<p>因浏览器限制,点击打开声音</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { mapGetters } from "vuex";
|
||
import moment from "moment";
|
||
import { mapUavFiex } from "../index.js";
|
||
import Style from "ol/style/Style";
|
||
import Stroke from "ol/style/Stroke";
|
||
import { fromLonLat, toLonLat } from "ol/proj";
|
||
import { whitListAdd, whitListDelete } from "@/api/whitList.js";
|
||
import QRCode from "qrcode";
|
||
|
||
export default {
|
||
name: "LeftSidebar",
|
||
props: {
|
||
homeData: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
signaData: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
leftContract: require("@/assets/img/left-contract.png"),
|
||
rightContract: require("@/assets/img/right-contract.png"),
|
||
warningDay: [
|
||
{
|
||
id: "1",
|
||
name: "今日预警",
|
||
value: 0
|
||
},
|
||
{
|
||
id: "2",
|
||
name: "累计预警",
|
||
value: 0
|
||
}
|
||
],
|
||
drones: [],
|
||
droneStates: {}, // 存储 detailsShow 状态
|
||
navigationStates: {}, // 控制 navigation-content 显示
|
||
qrCodes: {}, // 存储每个无人机的二维码 URL
|
||
isContracted: false,
|
||
homeView: {},
|
||
droneTimers: new Map(),
|
||
iswarning: false,
|
||
showAudioPrompt: false,
|
||
startTime: "", // 初始化时间范围
|
||
endTime: "",
|
||
qrCodeUrl: "",
|
||
isAudioPlaying: false // 跟踪音频播放状态
|
||
};
|
||
},
|
||
computed: {
|
||
...mapGetters(["isZoomedIn", "SliderValue", "lonAndLat"])
|
||
},
|
||
mounted() {
|
||
this.endTime = moment.utc().format("YYYY-MM-DD HH:mm:ss");
|
||
this.startTime = moment
|
||
.utc()
|
||
.subtract(1, "month")
|
||
.format("YYYY-MM-DD HH:mm:ss");
|
||
this.enableTouchScroll();
|
||
},
|
||
watch: {
|
||
homeData: {
|
||
handler(newVal) {
|
||
this.homeView = newVal;
|
||
this.warningDay[0].value = newVal.alarmCount.todaywaring;
|
||
this.warningDay[1].value = newVal.alarmCount.totalcount;
|
||
},
|
||
deep: true
|
||
},
|
||
SliderValue: {
|
||
handler(newVal) {
|
||
if (newVal) {
|
||
console.log("SliderValue:", newVal);
|
||
const media = this.$refs.uavAudio;
|
||
if (media) {
|
||
media.volume = newVal / 100;
|
||
}
|
||
}
|
||
},
|
||
deep: true
|
||
},
|
||
signaData: {
|
||
handler(newVal) {
|
||
if (newVal) {
|
||
// newVal = [
|
||
// {
|
||
// BatchId: "1936279216064696320",
|
||
// serial_number: "1581F6CDC23B6003DTL2",
|
||
// device_type: "8",
|
||
// device_type_8: 91088,
|
||
// app_lat: 39.055,
|
||
// app_lon: 117.303466,
|
||
// drone_lat: 39.040924,
|
||
// drone_lon: 117.337103,
|
||
// height: 0.0,
|
||
// altitude: 0.0,
|
||
// home_lat: 0.0,
|
||
// home_lon: 0.0,
|
||
// distance: 3299.608332218683,
|
||
// centerdistance: 5023.652200054709,
|
||
// IsWhitelist: false,
|
||
// speed_E: 12210.0,
|
||
// speed_N: 3120.0,
|
||
// speed_U: 130.0,
|
||
// RSSI: 0.0,
|
||
// positionId: "1924768527160578048",
|
||
// PostionName: "louding",
|
||
// DeviceId: "1924798246178394112",
|
||
// DeviceName: "0007",
|
||
// freq: 0.0,
|
||
// alarmLevel: 0,
|
||
// Time: 1744872277,
|
||
// CreateTime: "2025-06-21T12:25:14.0948095+08:00",
|
||
// Id: "1936279217155215360"
|
||
// }
|
||
// ];
|
||
let positionID = JSON.parse(localStorage.getItem("PositionIds"));
|
||
if (positionID !== null || positionID !== undefined) {
|
||
const match = newVal.some((item) =>
|
||
positionID.includes(item.positionId)
|
||
);
|
||
console.log(match, "match");
|
||
if (match) {
|
||
newVal.forEach((newItem) => {
|
||
if (
|
||
newItem.drone_lon !== 0 &&
|
||
newItem.drone_lat !== 0 &&
|
||
newItem.app_lon !== 0 &&
|
||
newItem.app_lat !== 0
|
||
) {
|
||
if (this.lonAndLat && this.lonAndLat.length === 2) {
|
||
const [userLon, userLat] = this.lonAndLat;
|
||
newItem.distanceMeters = this.calculateDistance(
|
||
userLon,
|
||
userLat,
|
||
newItem.drone_lon,
|
||
newItem.drone_lat
|
||
).toFixed(0);
|
||
} else {
|
||
newItem.distanceMeters = newItem.distanceMeters || 0;
|
||
}
|
||
// 如果已经存在相同BatchId的数据,重置计时器
|
||
if (newItem.BatchId) {
|
||
const existingTimer = this.droneTimers.get(newItem.BatchId);
|
||
if (existingTimer) {
|
||
clearInterval(existingTimer); // 清除旧计时器
|
||
}
|
||
// 设置15秒初始时间
|
||
newItem.currTime = window.mapConfig.currTime;
|
||
// 创建新计时器
|
||
const timer = this.startTimer(newItem);
|
||
this.droneTimers.set(newItem.BatchId, timer);
|
||
|
||
// 初始化 detailsShow(仅首次)
|
||
if (!(newItem.BatchId in this.droneStates)) {
|
||
this.$set(this.droneStates, newItem.BatchId, true);
|
||
}
|
||
// 默认隐藏 navigation-content
|
||
if (!(newItem.BatchId in this.navigationStates)) {
|
||
this.$set(this.navigationStates, newItem.BatchId, false);
|
||
}
|
||
|
||
// 更新无人机列表
|
||
const existingIndex = this.drones.findIndex(
|
||
(d) => d.BatchId === newItem.BatchId
|
||
);
|
||
newItem.times = moment(newItem.CreateTime).format(
|
||
"HH:mm:ss"
|
||
);
|
||
newItem.distance = parseInt(newItem.distance.toFixed(0));
|
||
|
||
if (existingIndex !== -1) {
|
||
this.$set(this.drones, existingIndex, { ...newItem });
|
||
} else {
|
||
this.$set(this.drones, this.drones.length, {
|
||
...newItem
|
||
});
|
||
}
|
||
}
|
||
}
|
||
});
|
||
if (newVal.length === 0) {
|
||
this.iswarning = false;
|
||
this.showAudioPrompt = false; // 没有数据 不显示提示框
|
||
}
|
||
|
||
if (this.drones) {
|
||
mapUavFiex(this.drones, window.olMap);
|
||
}
|
||
const media = this.$refs.uavAudio;
|
||
let alarm = this.drones.find((d) => d.alarmLevel === 1);
|
||
if (alarm) {
|
||
this.iswarning = true;
|
||
this.$nextTick(() => {
|
||
if (media) {
|
||
media.muted = true; // 初始静音
|
||
const savedVolume = localStorage.getItem("soundValue");
|
||
media.volume = savedVolume !== null ? savedVolume / 100 : 1;
|
||
media.muted = this.isZoomedIn ? false : true;
|
||
|
||
// 如果音频未在播放,则开始播放
|
||
if (!this.isAudioPlaying) {
|
||
media
|
||
.play()
|
||
.then(() => {
|
||
console.log("播放音频");
|
||
this.isAudioPlaying = true;
|
||
// 监听音频结束事件,循环播放
|
||
media.onended = () => {
|
||
if (this.iswarning) {
|
||
media.play().catch((error) => {
|
||
console.log("循环播放失败:", error);
|
||
this.showAudioPrompt = true;
|
||
});
|
||
} else {
|
||
this.isAudioPlaying = false;
|
||
}
|
||
};
|
||
})
|
||
.catch((error) => {
|
||
console.log("播放失败:", error);
|
||
this.showAudioPrompt = true;
|
||
this.isAudioPlaying = false;
|
||
});
|
||
}
|
||
}
|
||
});
|
||
} else if (!alarm && this.iswarning) {
|
||
// 告警消失,停止音频
|
||
this.iswarning = false;
|
||
if (media) {
|
||
media.pause();
|
||
media.currentTime = 0;
|
||
this.isAudioPlaying = false;
|
||
media.onended = null; // 移除结束事件监听
|
||
this.showAudioPrompt = false;
|
||
}
|
||
}
|
||
// 无数据时重置
|
||
if (newVal.length === 0) {
|
||
this.iswarning = false;
|
||
this.showAudioPrompt = false;
|
||
this.isAudioPlaying = false;
|
||
if (media) {
|
||
media.pause();
|
||
media.currentTime = 0;
|
||
media.onended = null;
|
||
}
|
||
vectorSource.clear();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
deep: true
|
||
},
|
||
isZoomedIn: {
|
||
handler(newVal) {
|
||
if (this.iswarning) {
|
||
const media = this.$refs.uavAudio;
|
||
if (newVal) {
|
||
media.muted = false; // 取消静音
|
||
} else {
|
||
media.muted = true; // 静音
|
||
}
|
||
}
|
||
},
|
||
deep: true
|
||
}
|
||
},
|
||
methods: {
|
||
calculateDistance(lon1, lat1, lon2, lat2) {
|
||
const R = 6371e3; // 地球半径(米)
|
||
const a1 = (lat1 * Math.PI) / 180; // 将纬度转换为弧度
|
||
const a2 = (lat2 * Math.PI) / 180;
|
||
const ab = ((lat2 - lat1) * Math.PI) / 180;
|
||
const ac = ((lon2 - lon1) * Math.PI) / 180;
|
||
|
||
const a =
|
||
Math.sin(ab / 2) * Math.sin(ab / 2) +
|
||
Math.cos(a1) * Math.cos(a2) * Math.sin(ac / 2) * Math.sin(ac / 2);
|
||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||
|
||
return R * c; // 距离(米)
|
||
},
|
||
enableTouchScroll() {
|
||
const ulElement = this.$el.querySelector(".drone-list ul");
|
||
if (ulElement) {
|
||
let startY, startScrollTop;
|
||
|
||
ulElement.addEventListener("touchstart", (e) => {
|
||
startY = e.touches[0].clientY;
|
||
startScrollTop = ulElement.scrollTop;
|
||
});
|
||
|
||
ulElement.addEventListener("touchmove", (e) => {
|
||
const deltaY = e.touches[0].clientY - startY;
|
||
ulElement.scrollTop = startScrollTop - deltaY;
|
||
// 阻止默认滚动行为(视情况调整)
|
||
// e.preventDefault();
|
||
});
|
||
}
|
||
},
|
||
closeNavigation(drone) {
|
||
// 确保 drone.app_lon 和 drone.app_lat 存在
|
||
if (!drone || !drone.app_lon || !drone.app_lat) {
|
||
this.$message.error("无人机的经纬度信息无效");
|
||
return;
|
||
}
|
||
|
||
// 将WGS84坐标转换为GCJ02坐标(火星坐标系)
|
||
const gcj02Coords = this.wgs84ToGcj02(drone.app_lon, drone.app_lat);
|
||
|
||
window.location.href =
|
||
"amapuri://route/plan/?dlat=" +
|
||
gcj02Coords[1] +
|
||
"&dlon=" +
|
||
gcj02Coords[0] +
|
||
"&dname=飞手位置&dev=0&t=0";
|
||
|
||
// 可选:提供后备方案(如果 App 未安装,跳转到网页版)
|
||
// setTimeout(() => {
|
||
// const isAppOpened = document.hidden || document.webkitHidden;
|
||
// if (!isAppOpened) {
|
||
// this.$message.warning("未安装高德地图 App,将打开网页版导航");
|
||
// window.open(
|
||
// `https://uri.amap.com/navigation?to=${drone.app_lon},${drone.app_lat},飞手位置&callnative=1`,
|
||
// "_blank"
|
||
// );
|
||
// }
|
||
// }, 1000);
|
||
},
|
||
noNavigation(drone) {
|
||
this.$set(this.navigationStates, drone.BatchId, false);
|
||
},
|
||
async handleNavigation(drone) {
|
||
console.log("导航", drone);
|
||
this.$set(
|
||
this.navigationStates,
|
||
drone.BatchId,
|
||
!this.navigationStates[drone.BatchId]
|
||
);
|
||
if (!this.navigationStates[drone.BatchId]) {
|
||
return; // 如果关闭导航面板,直接返回
|
||
}
|
||
// 将WGS84坐标转换为GCJ02坐标(火星坐标系)
|
||
const gcj02Coords = this.wgs84ToGcj02(drone.app_lon, drone.app_lat);
|
||
|
||
// 使用 amapuri 协议,优先打开高德地图 App
|
||
const naviUrl = `https://uri.amap.com/navigation?to=${
|
||
gcj02Coords[0] + "," + gcj02Coords[1]
|
||
},飞手位置&callnative=1`;
|
||
try {
|
||
// 异步生成二维码并存储到 qrCodes 对象
|
||
const qrCodeDataUrl = await QRCode.toDataURL(naviUrl, {
|
||
width: 200,
|
||
margin: 2
|
||
});
|
||
this.$set(this.qrCodes, drone.BatchId, qrCodeDataUrl);
|
||
} catch (err) {
|
||
console.error("二维码生成失败:", err);
|
||
this.$message.error("二维码生成失败,请重试!");
|
||
}
|
||
},
|
||
handlewhiteList(drone) {
|
||
let params = {
|
||
model: " ",
|
||
sn: drone.serial_number,
|
||
allDay: true,
|
||
startTime: this.startTime,
|
||
endTime: this.endTime,
|
||
positionId: [drone.positionId],
|
||
company: " ",
|
||
mark: " "
|
||
};
|
||
if (!drone.IsWhitelist) {
|
||
whitListAdd(params)
|
||
.then((res) => {
|
||
if (res.code === 0) {
|
||
console.log(res, "添加成功");
|
||
this.$message.success("添加成功");
|
||
} else {
|
||
this.$message.error(res.msg);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
this.$message.error(err);
|
||
});
|
||
} else {
|
||
whitListDelete({ id: drone.WhiteListId })
|
||
.then((res) => {
|
||
if (res.code === 0) {
|
||
this.$message.success("移除成功");
|
||
} else {
|
||
this.$message.error(res.msg);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
this.$message.error(err);
|
||
console.log(err, "移除失败");
|
||
});
|
||
}
|
||
},
|
||
enableAudio() {
|
||
this.iswarning = true;
|
||
const media = this.$refs.uavAudio;
|
||
const savedVolume = localStorage.getItem("soundValue");
|
||
let time = setInterval(() => {
|
||
console.log("用户手动启用音频", media);
|
||
if (media !== undefined) {
|
||
clearInterval(time);
|
||
if (this.isZoomedIn) {
|
||
media.muted = false; // 播放成功后取消静音
|
||
} else {
|
||
media.muted = true; // 初始静音
|
||
}
|
||
if (savedVolume !== null) {
|
||
media.volume = savedVolume / 100;
|
||
} else {
|
||
media.volume = 1;
|
||
}
|
||
media
|
||
.play()
|
||
.then(() => {
|
||
console.log("用户手动启用音频成功");
|
||
this.showAudioPrompt = false; // 关闭提示框
|
||
})
|
||
.catch((error) => {
|
||
console.error("手动播放失败:", error);
|
||
});
|
||
}
|
||
}, 500);
|
||
},
|
||
startTimer(item) {
|
||
return setInterval(() => {
|
||
if (item.currTime > 0) {
|
||
item.currTime--;
|
||
console.log(item.currTime, "item.currTime");
|
||
// 更新显示时间(可选)
|
||
const index = this.drones.findIndex(
|
||
(d) => d.BatchId === item.BatchId
|
||
);
|
||
if (index !== -1) {
|
||
this.$set(this.drones, index, { ...item });
|
||
}
|
||
} else {
|
||
this.droneStates = {};
|
||
this.navigationStates = {};
|
||
this.qrCodes = {}; // 清理二维码
|
||
clearInterval(this.droneTimers.get(item.BatchId));
|
||
this.droneTimers.delete(item.BatchId);
|
||
this.handleTimerExpiration(item);
|
||
this.showAudioPrompt = false; // 关闭提示框
|
||
this.iswarning = false;
|
||
const media = this.$refs.uavAudio;
|
||
media.muted = true; // 静音
|
||
}
|
||
}, 1000);
|
||
},
|
||
|
||
handleTimerExpiration(item) {
|
||
// 如果drones也需要同步更新
|
||
if (this.drones.length === 1) {
|
||
this.drones = [];
|
||
} else {
|
||
this.drones = this.drones.filter((d) => d.BatchId !== item.BatchId);
|
||
}
|
||
this.$delete(this.droneStates, item.BatchId); // 清理状态
|
||
this.$delete(this.navigationStates, item.BatchId); // 清理状态
|
||
this.$delete(this.qrCodes, item.BatchId);
|
||
|
||
// 获取 OpenLayers 图层
|
||
const graphicLayer = window.olMap
|
||
.getLayers()
|
||
.getArray()
|
||
.find((layer) => layer.get("id") === "uavFiex");
|
||
const graphicLayerGJ = window.olMap
|
||
.getLayers()
|
||
.getArray()
|
||
.find((layer) => layer.get("id") === "guiji");
|
||
const graphicLayerFSGJX = window.olMap
|
||
.getLayers()
|
||
.getArray()
|
||
.find((layer) => layer.get("id") === "fsguiji");
|
||
|
||
// 清理无人机相关 Feature
|
||
if (graphicLayer) {
|
||
const graphic = graphicLayer.getSource().getFeatureById(item.BatchId); // 无人机
|
||
const appGraphic = graphicLayer
|
||
.getSource()
|
||
.getFeatureById(item.BatchId + "_app"); // 飞手
|
||
if (graphic) {
|
||
graphicLayer.getSource().removeFeature(graphic);
|
||
}
|
||
if (appGraphic) {
|
||
graphicLayer.getSource().removeFeature(appGraphic);
|
||
}
|
||
}
|
||
|
||
// 清理无人机轨迹
|
||
if (graphicLayerGJ) {
|
||
const track = graphicLayerGJ
|
||
.getSource()
|
||
.getFeatureById(item.BatchId + "_track");
|
||
if (track) {
|
||
graphicLayerGJ.getSource().removeFeature(track);
|
||
}
|
||
}
|
||
|
||
// 清理飞手轨迹
|
||
if (graphicLayerFSGJX) {
|
||
const appTrack = graphicLayerFSGJX
|
||
.getSource()
|
||
.getFeatureById(item.BatchId + "_app_track");
|
||
if (appTrack) {
|
||
graphicLayerFSGJX.getSource().removeFeature(appTrack);
|
||
}
|
||
}
|
||
|
||
// 如果 drones 为空,移除所有相关图层
|
||
if (this.drones.length === 0) {
|
||
if (graphicLayer) {
|
||
window.olMap.removeLayer(graphicLayer);
|
||
}
|
||
if (graphicLayerGJ) {
|
||
window.olMap.removeLayer(graphicLayerGJ);
|
||
}
|
||
if (graphicLayerFSGJX) {
|
||
window.olMap.removeLayer(graphicLayerFSGJX);
|
||
}
|
||
}
|
||
},
|
||
handleContractClick() {
|
||
this.isContracted = !this.isContracted; // 切换状态
|
||
},
|
||
handleDroneClick(drone) {
|
||
console.log(drone, "isVisible");
|
||
this.$set(
|
||
this.droneStates,
|
||
drone.BatchId,
|
||
!this.droneStates[drone.BatchId]
|
||
);
|
||
const isVisible = this.droneStates[drone.BatchId];
|
||
|
||
// 无人机轨迹线
|
||
// let graphicLayerGJ = window.olMap
|
||
// .getLayers()
|
||
// .getArray()
|
||
// .find((layer) => layer.get("id") === "guiji");
|
||
// let graphic = graphicLayerGJ
|
||
// .getSource()
|
||
// .getFeatureById(drone.BatchId + "_track");
|
||
// const trackStyle = new Style({
|
||
// stroke: new Stroke({
|
||
// color: this.getRandomColor(),
|
||
// width: 0,
|
||
// zIndex: 1
|
||
// })
|
||
// });
|
||
// if (!isVisible) {
|
||
// graphic.setStyle(trackStyle);
|
||
// } else {
|
||
// graphic.setStyle(null);
|
||
// }
|
||
|
||
// 飞手无人机连接线
|
||
let graphicLayerFSGJX = window.olMap
|
||
.getLayers()
|
||
.getArray()
|
||
.find((layer) => layer.get("id") === "fsguiji");
|
||
if (!graphicLayerFSGJX) {
|
||
graphicLayerFSGJX = new VectorLayer({
|
||
source: new VectorSource(),
|
||
zIndex: 10
|
||
});
|
||
graphicLayerFSGJX.set("id", "fsguiji");
|
||
map.addLayer(graphicLayerFSGJX);
|
||
}
|
||
let graphiclianjiexian = graphicLayerFSGJX
|
||
.getSource()
|
||
.getFeatureById(drone.BatchId + "_app_track");
|
||
const trackStyleljx = new Style({
|
||
stroke: new Stroke({
|
||
color: this.getRandomColor(),
|
||
width: 5,
|
||
lineDash: [4, 4],
|
||
zIndex: 1
|
||
})
|
||
});
|
||
if (!isVisible) {
|
||
graphiclianjiexian.setStyle(trackStyleljx);
|
||
} else {
|
||
graphiclianjiexian.setStyle(null);
|
||
}
|
||
|
||
if (drone.drone_lon !== 0 && drone.drone_lat !== 0) {
|
||
window.olMap.getView().animate({
|
||
center: fromLonLat([drone.drone_lon, drone.drone_lat]),
|
||
zoom: window.olMap.getView().getZoom()
|
||
});
|
||
}
|
||
},
|
||
// 随机颜色生成函数
|
||
getRandomColor() {
|
||
const letters = "0123456789ABCDEF";
|
||
let color = "#";
|
||
color += "0";
|
||
color += letters[Math.floor(Math.random() * 4)];
|
||
color += letters[Math.floor(Math.random() * 16)];
|
||
color += letters[Math.floor(Math.random() * 16)];
|
||
color += letters[Math.floor(Math.random() * 16)];
|
||
color += letters[Math.floor(Math.random() * 16)];
|
||
return color;
|
||
},
|
||
// WGS84坐标转换为GCJ02坐标(火星坐标系)
|
||
wgs84ToGcj02(lng, lat) {
|
||
const PI = 3.1415926535897932384626;
|
||
const a = 6378245.0;
|
||
const ee = 0.00669342162296594323;
|
||
|
||
function transformLat(lng, lat) {
|
||
let ret =
|
||
-100.0 +
|
||
2.0 * lng +
|
||
3.0 * lat +
|
||
0.2 * lat * lat +
|
||
0.1 * lng * lat +
|
||
0.2 * Math.sqrt(Math.abs(lng));
|
||
ret +=
|
||
((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
|
||
2.0) /
|
||
3.0;
|
||
ret +=
|
||
((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) *
|
||
2.0) /
|
||
3.0;
|
||
ret +=
|
||
((160.0 * Math.sin((lat / 12.0) * PI) +
|
||
320 * Math.sin((lat * PI) / 30.0)) *
|
||
2.0) /
|
||
3.0;
|
||
return ret;
|
||
}
|
||
|
||
function transformLng(lng, lat) {
|
||
let ret =
|
||
300.0 +
|
||
lng +
|
||
2.0 * lat +
|
||
0.1 * lng * lng +
|
||
0.1 * lng * lat +
|
||
0.1 * Math.sqrt(Math.abs(lng));
|
||
ret +=
|
||
((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
|
||
2.0) /
|
||
3.0;
|
||
ret +=
|
||
((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) *
|
||
2.0) /
|
||
3.0;
|
||
ret +=
|
||
((150.0 * Math.sin((lng / 12.0) * PI) +
|
||
300.0 * Math.sin((lng / 30.0) * PI)) *
|
||
2.0) /
|
||
3.0;
|
||
return ret;
|
||
}
|
||
|
||
const dlat = transformLat(lng - 105.0, lat - 35.0);
|
||
const dlng = transformLng(lng - 105.0, lat - 35.0);
|
||
const radlat = (lat / 180.0) * PI;
|
||
let magic = Math.sin(radlat);
|
||
magic = 1 - ee * magic * magic;
|
||
const sqrtmagic = Math.sqrt(magic);
|
||
const dlat2 =
|
||
(dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
|
||
const dlng2 = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
|
||
|
||
return [lng + dlng2, lat + dlat2];
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.left-sidebar {
|
||
transition: transform 0.5s;
|
||
}
|
||
|
||
.left-sidebar.contracted {
|
||
transform: translateX(-90%);
|
||
}
|
||
#uavAudio {
|
||
position: absolute;
|
||
bottom: -100%;
|
||
}
|
||
.alarm-border {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
border: 1px solid transparent;
|
||
pointer-events: none; /* Prevent interaction with the border */
|
||
z-index: 9999; /* Ensure it appears above other elements */
|
||
&.warningAnimatBox {
|
||
animation: greenanimated-shadow 2s infinite;
|
||
}
|
||
}
|
||
@keyframes greenanimated-shadow {
|
||
0% {
|
||
box-shadow: 0 0 0 0 #ff0000 inset;
|
||
}
|
||
|
||
85% {
|
||
box-shadow: 0px 0px 100px 0px #ff0000 inset;
|
||
}
|
||
}
|
||
</style>
|