ly-front/src/views/contentData/LeftSidebar/index.vue

958 lines
32 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>
<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>