提交 9975a12b 作者: 翁进城

feat: 机库操控开发,剩下定时任务与周期任务未完成

上级 12e16c26
流水线 #9278 已失败 于阶段
<!-- 飞控中心电池-->
<template>
<div v-if="device.network === 1">
<div v-if="batteryList.length>0" class="cpt-observe-mspace-dashboard-battery jcsb">
<div>
<div class="cpt-observe-mspace-dashboard-battery jcsb">
<div
class="battery-inner pr"
:class="setClass(index,batteryList.length)"
v-for="(item,index) in batteryList"
:style="batteryList.length>1 && index==1 ? 'left:-128px':''"
class="battery-inner"
:class="setClass(index,_batteryList.length)"
v-for="(item,index) in _batteryList"
:key="index"
>
<div class="dec mr9 f12">{{index+1}}</div>
<div class="size-wrap ml20">
<div class="size-wrap">
<div class="size-box">
<div class="size-inner">
<div class="dianchigai"></div>
<div
v-if="ModeStatus !='离线' && item.statusType !=3"
v-if="item.statusType !=3"
class="size-inner-box"
:style="`height: ${item.size || 100}%; ${setColor(item.size || 100)}`"
:style="`height: ${item.chargeRemaining || 100}%; ${setColor(item.chargeRemaining || 100)}`"
></div>
</div>
</div>
</div>
<span
v-if="ModeStatus !='离线' && item.statusType !=3"
class="pa top28 f11 left43 cf"
>{{item.size || 100}}%</span>
<span v-if="ModeStatus =='离线'" class="pa top32 f11 left43 cf">{{"已关机"}}</span>
<div style="marin-left: 12px">
<div class="voltage-box">
<div class="voltage-value f12 ml10 jcsb">
{{ item.voltage||0 }}
<span class="cf ml2">v</span>
</div>
</div>
</div>
</div>
</div>
<div v-else>
<div class="cpt-observe-mspace-dashboard-battery jcsb">
<div class="battery-inner pr mt9" v-if="batteryList.length==0">
<div class="dec mr9 top7 f12">1</div>
<div class="size-wrap ml20">
<div class="size-box">
<div class="size-inner">
<div class="dianchigai"></div>
<!-- <div
class="size-inner-box"
:style="`height: 100%; ${setColor(100)}`"
></div>-->
</div>
</div>
</div>
<span class="pa top27 f11 left43 cf">已关机</span>
<div style="marin-left: 12px">
<div class="voltage-box">
<div class="voltage-value f12 ml10 jcsb">
45
<span class="cf ml2">v</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else>
<div class="cpt-observe-mspace-dashboard-battery jcsb">
<div class="battery-inner pr" :class="setClass(1,1)">
<div class="dec mr9 f12">{{1}}</div>
<div class="size-wrap ml20">
<div class="size-box">
<div class="size-inner">
<div class="dianchigai"></div>
<div
class="size-inner-box"
:style="`height: ${battery.size || 100}%; ${setColor(battery.size || 100)}`"
></div>
</div>
</div>
<span v-if="ModeStatus !='离线'" class="battery-size">{{battery.size || 100}}%</span>
<span v-if="ModeStatus =='离线'" class="battery-size">{{"已关机"}}</span>
<span v-if="item.statusType == 3" class="size__text">{{"已关机"}}</span>
<span v-else class="size__text">{{item.chargeRemaining || 100}}%</span>
</div>
<div style="marin-left: 12px">
<div class="voltage-box">
<div class="voltage-value f12 ml10 jcsb">
{{ battery.voltage||0 }}
{{ item.voltage === -1 ? '-' : Number(item.voltage).toFixed(0) }}
<span class="cf ml2">v</span>
</div>
</div>
......@@ -99,43 +40,44 @@
<script>
export default {
props: {
uavBattery: {
type: Object,
default: () => ({}),
},
wsShow: {
type: Boolean,
default: () => {
false;
},
},
device: {
type: Object,
default: () => ({}),
},
ModeStatus: {
type: String,
default: () => "",
// 电池列表
batteryList: {
type: Array,
default: () => [
/* {
id: -1, // 电池id,0普通电池,52左智能电池,53右智能电池
active: 0, // 电池激活类型(鹰巢无人机,这个参数才生效)
chargeRemaining: -1, // 电池中的剩余能量百分比
current: 0, // 电池的实时电流消耗(A)。负值表示电池正在放电,正值表示正在充电
cycleIndex: -1, // 充电循环次数
fullChargeCapacity: -1, // 电池完全充电时存储在电池中的总电量 100%
lowVoltWarnValue: -1, // 低电压报警阈值(V)
temperature: -1, // 电池温度(℃)
voltage: -1, // 返回当前电池电压(V)
statusType: 3, // 电池状态 0无效值,1开机,2充电中,3关机
}, */
],
},
},
data() {
return {
battery: {},
state: {
1: { label: "良好", color: "#76c230" },
2: { label: "一般", color: "#FFBD36" },
3: { label: "低电量", color: "#f73939" },
4: { label: "已关机", color: "#f73939" },
},
batteryList: [
// {
// state: { label: "良好", color: "#76c230" },
// size: 80,
// voltage: 20,
// },
],
};
return {};
},
computed: {
_batteryList() {
if (this.batteryList.length > 0) {
return this.batteryList;
} else {
return [
{
statusType: 3, // 电池状态 0无效值,1开机,2充电中,3关机
chargeRemaining: -1, // 电池中的剩余能量百分比
voltage: -1, // 返回当前电池电压(V)
},
];
}
},
},
mounted() {},
methods: {
setColor(size) {
......@@ -160,122 +102,13 @@ export default {
return str;
},
},
watch: {
ModeStatus(val) {
if (val == "离线") {
this.batteryList.forEach((val) => {
val.size = "已关机";
});
}
},
device: {
handler(val) {
this.batteryList = [];
},
},
uavBattery: {
// 低电量,良好,
handler(value) {
let uavInfo = value;
let size = null;
if (!this.wsShow) {
// console.log(uavInfo,"uavInfouavInfouavInfouavInfo");
if (uavInfo?.statusType != 3) {
size = uavInfo?.chargeRemaining;
} else {
size = "已关机";
}
// console.log('电池id:',value.id,'百分比:',size,"%,电压:",uavInfo.voltage.toFixed(1),'v')
if (uavInfo?.voltage) {
let state = this.state["3"];
let id = null;
if (uavInfo.id) {
id = uavInfo.id;
}
if (uavInfo.statusType != 3) {
if (this.device.cateName == "px4") {
if (size > 40) {
state = this.state["1"];
} else if (size <= 40) {
state = this.state["3"];
} else if (size == -1) {
this.state(4);
size = "已关机";
}
} else {
if (size > 45) {
state = this.state["1"];
} else if (size <= 45) {
state = this.state["3"];
} else if (size == -1) {
this.state(4);
size = "已关机";
}
}
} else {
state = this.state["4"];
}
let statusType = uavInfo.statusType;
let voltage = uavInfo.voltage.toFixed(1);
let timeRemaining = uavInfo.timeRemaining;
this.battery = {
id,
size,
state,
voltage, //: voltage <= 42 ? 42 : voltage,
statusType,
timeRemaining,
};
if (value.id) {
if (this.batteryList.length > 0) {
let arr = [];
this.batteryList.forEach((val) => {
arr.push(val.id);
});
if (arr.indexOf(value.id) > -1) {
this.batteryList[arr.indexOf(value.id)] = this.battery;
} else {
this.batteryList.push(this.battery);
}
} else {
this.batteryList.push(this.battery);
}
} else if (value.id !== undefined) {
this.batteryList = [];
// this.batteryList.push(this.battery);
}
this.$forceUpdate();
}
} else {
// battaryRemain
// voltage
let state = null;
let id = 52;
size = uavInfo?.battaryRemain;
if (size > 40) {
state = this.state["1"];
} else if (size <= 40) {
state = this.state["3"];
}
let voltage = uavInfo?.voltage.toFixed(1);
this.battery = {
id,
size,
state,
voltage, //: voltage <= 42 ? 42 : voltage,
};
}
},
deep: true,
immediate: true,
},
},
watch: {},
};
</script>
<style lang="scss" scoped>
.cpt-observe-mspace-dashboard-battery {
width: 96px;
width: 110px;
height: 100%;
box-sizing: border-box;
......@@ -283,6 +116,8 @@ export default {
display: flex;
align-items: center;
position: relative;
margin: -38px 0px;
.dec {
flex-shrink: 0;
width: 18px;
......@@ -344,7 +179,8 @@ export default {
.size-wrap {
width: 68px;
flex-shrink: 0;
position: relative;
.size-box {
transform: rotate(90deg) translate(-3px, -15px);
box-sizing: border-box;
......@@ -399,6 +235,15 @@ export default {
}
}
}
.size__text {
position: absolute;
left: 10px;
top: 50%;
transform: translate(0%, -50%);
font-size: 12px;
color: #fff;
white-space: nowrap;
}
}
}
}
......
......@@ -2,21 +2,21 @@
<div class="nest_info_maxBox">
<div class="infoBox cf">
<div class="dib title">
{{ uavData.relativeAlt }}
{{ _uavData.gps.relativeAlt }}
</div>
m
<div class="cf infor">飞行高度</div>
</div>
<div class="infoBox cf">
<div class="dib title">
{{ uavData.absoluteAlt }}
{{ _uavData.gps.absoluteAlt }}
</div>
m
<div class="cf">海拔高度</div>
</div>
<div class="infoBox cf">
<div class="dib title">
{{ uavData.flyDistance }}
{{ _uavData.flyDistance }}
</div>
m
<div class="cf">飞行里程</div>
......@@ -24,28 +24,28 @@
<div class="infoBox cf">
<div class="dib title">
{{ uavData.distanceToHome }}
{{ _uavData.distanceToHome }}
</div>
m
<div class="cf">起点距离</div>
</div>
<div class="infoBox cf">
<div class="dib title">
{{ uavData.flyTime }}
{{ _uavData.flyTime }}
</div>
min
<div class="cf">飞行时间</div>
</div>
<div class="infoBox cf">
<div class="dib title">
{{uavData.groundSpeed }}
{{_uavData.groundSpeed }}
</div>
m/s
<div class="cf">飞行速度</div>
</div>
<div class="infoBox cf">
<div class="dib title" :style="up ? 'color: #ff2626':'color: #00f5ff'">
{{ uavData.velocityZ }}
{{ _uavData.velocityZ }}
</div>
m/s
<div class="cf">爬升率</div>
......@@ -62,10 +62,25 @@ export default {
},
data() {
return {
// stationType:0
up: false,
};
},
computed: {
_uavData() {
return {
...this.uavData,
gps: {
relativeAlt: Number(this.uavData?.gps?.relativeAlt || 0).toFixed(0),
absoluteAlt: Number(this.uavData?.gps?.absoluteAlt || 0).toFixed(0),
},
flyDistance: Number(this.uavData?.flyDistance || 0).toFixed(0),
distanceToHome: Number(this.uavData?.distanceToHome || 0).toFixed(0),
flyTime: Number(this.uavData?.flyTime || 0).toFixed(0),
groundSpeed: Number(this.uavData?.groundSpeed || 0).toFixed(0),
velocityZ: Number(this.uavData?.velocityZ || 0).toFixed(0),
};
},
},
watch: {
uavData: function (val, old) {
if (!val) {
......
......@@ -26,7 +26,7 @@ export default {
},
throttleValue: {
type: Number,
default: 500,
default: 0,
},
},
};
......
<!-- 飞控新版鹰视1027 -->
<template>
<div class="nest_controlBox">
<throttleGauge :uavData="uavData" :throttleValue="throttleValue" style="transform: translateX(35px);"></throttleGauge>
<throttleGauge
v-if="uavData"
:uavData="uavData"
:throttleValue="uavData.accelerator || 0"
style="transform: translateX(35px);"
></throttleGauge>
<div class="nest_control">
<Info class="" :uav-data="uavData"></Info>
<Info class :uav-data="uavData"></Info>
<div class="xian"></div>
<battery v-if="uavBattery" :uav-battery="uavBattery" :device="device"></battery>
<battery :batteryList="uavData.batteryList"></battery>
</div>
<obstacle :uav-data="uavData" style="transform: translateX(-35px);"></obstacle>
</div>
</template>
<script>
import throttleGauge from './components/throttleGauge';
import throttleGauge from "./components/throttleGauge";
import Info from "./components/info/index";
import battery from "./components/battery/index";
import obstacle from "./components/obstacle";
export default {
name: 'MMCDataTransferPanel',
name: "MMCDataTransferPanel",
data() {
return {};
},
props: {
// 无人机实时数据
uavData: {
type: Object,
default: () => ({})
},
uavBattery: {
type: Object,
default: () => ({})
},
throttleValue: {
type: Number,
default: 0
default: () => ({
gps: {
relativeAlt: 0, //相对地面高度
absoluteAlt: 0, //海拔
},
flyDistance: 0, //飞行距离
flyTime: 0, //飞行时间
groundSpeed: 0, //飞行速度
distanceToHome: 0, //离起飞点距离
velocityX: 0, // 使用NED(北-东-下)坐标系,飞机在x方向上的当前速度,以米
velocityY: 0, // 使用NED(北-东-下)坐标系,飞机在y方向上的当前速度,以米
velocityZ: 0, //爬升率
accelerator: this.uavRealTimeData.accelerator, //油门值
batteryList: this.uavRealTimeData.batteryList, //电池列表
}),
},
// 无人机设备信息
device: {
type: Object,
default: () => ({})
default: () => ({}),
},
},
components: { Info, battery, obstacle, throttleGauge },
mounted(){
},
mounted() {},
computed: {
props_obj() {
let { data } = this;
}
}
},
},
};
</script>
<style scoped lang="scss">
......@@ -64,7 +76,6 @@ export default {
background: rgba(9, 32, 87, 0.7);
padding: 0 60px 0 40px;
}
}
.xian {
width: 0.3px;
......
export { default as Control_API } from './modules/uav_control';
export { default as flightTaskAPI } from './modules/flightTask';
\ No newline at end of file
export { default as flightTaskAPI } from './modules/flightTask';
export { default as AirLine } from './modules/air-line';
\ No newline at end of file
import request from '../request';
class AirLine {
/**
* 航线列表
* @param {*} params
* @returns
*/
static lineList(params) {
return request({
url: '/dms/route/page',
method: 'get',
params
});
}
/**
* 航线规划
* @param {*} data
* @returns
*/
static Edit(data) {
return request({
url: '/dms/route/add',
method: 'post',
data
});
}
static routeDelete(id) {
return request({
url: `/dms/route/delete/${id}`,
method: 'delete'
});
}
/**
* 航线修改
* @param {*} data
* @returns
*/
static Change(data) {
return request({
url: '/dms/route/update',
method: 'put',
data
});
}
/**
* 航线详情
* @param {*} data
* @returns
*/
static details(params) {
return request({
url: `/tmj/route/getRouteDetail/${params}`,
method: 'get',
params
});
}
/**
* 上传航线
* @param {*} data
* @returns
*/
static upload(data) {
return request({
headers: {
'Content-Type': 'multipart/form-data'
},
url: '/dms/route/upload',
method: 'post',
data
});
}
/**
* 上传航线
* @param {*} data
* @returns
*/
static uploadFile(data) {
return request({
headers: {
'Content-Type': 'multipart/form-data'
},
url: '/crm/dimensionMark/uploadFile',
method: 'post',
data
});
}
/**
* 上传多条航线
* @param {*} data
* @returns
*/
static uploadRoutes(data) {
return request({
headers: {
'Content-Type': 'multipart/form-data'
},
url: '/dms/route/upload',
method: 'post',
data
});
}
/**
* 航线删除
* @param {*} data
* @returns
*/
static deleteRoute(params) {
return request({
url: `/dms/route/delete/${params}`,
method: 'delete'
});
}
/**
* 航线详情
* @param {*} params
* @returns
*/
static routeDetail(params) {
return request({
url: '/dms/route/id',
method: 'get',
params
});
}
/**
* 飞行日志
* @param {*} params
* @returns
*/
static getSorties(params) {
return request({
url: '/dms/route/id',
method: 'get',
params
});
}
/**
* 飞行日志-历史视频
* @param {*} params
* @returns
*/
static getSortiesHistoryVideo(params) {
return request({
url: '/dms/sortie/history-video',
method: 'get',
params
});
}
/**
* 飞行日志-架次-历史轨迹
* @param {*} params
* @returns
*/
static getSortiesTrajectory(params) {
return request({
url: '/dms/sortie/data',
method: 'get',
params
});
}
}
export default AirLine;
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 58</title>
<g id="鹰视子页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="无人机应用3" transform="translate(-437.000000, -91.000000)">
<g id="编组-44" transform="translate(424.000000, 84.000000)">
<g id="编组-58" transform="translate(13.000000, 7.000000)">
<rect id="矩形" x="0" y="0" width="22" height="22"></rect>
<g id="编组-24" transform="translate(1.000000, 0.000000)">
<g id="编组">
<path d="M3.88923078,2.41768162 L3.88923078,2.90699556 C3.89135237,3.71997902 4.54351573,4.37741013 5.34615385,4.37568669 L14.6538461,4.37568669 C15.0394305,4.37672127 15.409628,4.22256551 15.6829986,3.9471322 C15.9563693,3.67169889 16.1105197,3.2975503 16.1115385,2.90699556 L16.1115385,2.41768162 L18.0561538,2.41768162 C18.5702664,2.41624304 19.0638901,2.62171794 19.4284324,2.98891369 C19.7929747,3.35610944 19.9985739,3.85494697 20,4.37568669 L20,20.0419851 C19.9985741,20.5628602 19.7928693,21.0618161 19.4281605,21.4290343 C19.0634517,21.7962524 18.5696302,22.0016358 18.0553846,21.9999902 L1.94461539,21.9999902 C1.42923078,21.9999902 0.933846162,21.7942856 0.569230765,21.4265284 C0.205080492,21.0601733 0.000204616439,20.5618526 0,20.0419851 L0,4.37568669 C0.00142586731,3.85481157 0.207130678,3.35585567 0.57183949,2.98863751 C0.936548301,2.62141936 1.43036975,2.41603597 1.94461539,2.41768162 L3.88923078,2.41768162 Z M8.03089086,7.05007484 C7.25556571,7.05007484 6.50823684,7.43670355 5.98144997,8.10900158 C5.48894424,8.73770348 5.21812322,9.5643088 5.21812322,10.4345102 L5.21812322,13.5757252 L5.23069297,13.8051785 C5.22326539,14.8021528 6.753347,14.6570236 6.75220429,13.7460942 L6.7413486,13.5349973 L6.7413486,10.4345102 C6.7413486,9.42893139 7.33212692,8.57938074 8.03089086,8.57938074 C8.73022614,8.57938074 9.32100448,9.42835775 9.32100448,10.4345102 L9.32100448,15.6161382 C9.32100448,16.4863397 9.59182549,17.3117977 10.0849026,17.9404996 C10.6122608,18.6133713 11.358447,19 12.1337721,19 C12.9096686,19 13.6564261,18.6133713 14.1837844,17.9404996 C14.6762901,17.3117977 14.9476825,16.4863397 14.9476825,15.6161382 L14.9476825,13.7460942 C14.9476825,13.3237881 14.6066968,12.9814413 14.1860698,12.9814413 C13.7654427,12.9814413 13.4244571,13.3237881 13.4244571,13.7460942 L13.4244571,15.6161382 C13.4244571,16.6217171 12.8331074,17.4706941 12.1337721,17.4706941 C11.4344368,17.4706941 10.8442299,16.6217171 10.8442299,15.6161382 L10.8442299,10.4345102 C10.8442299,9.56373516 10.5728375,8.73885074 10.0803318,8.10900158 C9.55297353,7.43670356 8.80621601,7.05007484 8.03089086,7.05007484 Z M5.97516509,14.9455611 C4.88461082,14.9468261 4.00094457,15.8342765 4,16.9291844 C4.0018887,18.0248844 4.8855348,18.9115448 5.97573645,18.9128078 C7.06664416,18.9118582 7.95058664,18.0238736 7.95090153,16.9286108 C7.94995603,15.8335722 7.06585007,14.9461937 5.97516509,14.9455611 Z M5.97573645,15.8639477 C6.56202684,15.8639477 7.03730912,16.3411273 7.03730912,16.9297581 C7.03730912,17.5183889 6.56202684,17.9955684 5.97573645,17.9955684 C5.38944605,17.9955684 4.91416377,17.5183889 4.91416377,16.9297581 C4.91416377,16.3411273 5.38944605,15.8639477 5.97573645,15.8639477 Z M14.1032237,7 C13.9820029,7 13.8657981,7.04858666 13.7804096,7.13497254 L12.3286033,8.5925743 C12.2429834,8.6785294 12.1948821,8.79511348 12.1948821,8.91667701 C12.1948821,9.03824054 12.2429834,9.15482463 12.3286033,9.24077972 C12.4141875,9.32723452 12.5305812,9.37584903 12.6519887,9.37584903 C12.7733962,9.37584903 12.8897899,9.32723452 12.9753741,9.24077972 L13.3421824,8.87308089 L13.3421824,11.1498307 C13.3421824,11.5721369 13.683168,11.9144837 14.103795,11.9144837 C14.5244221,11.9144837 14.8654077,11.5721369 14.8654077,11.1498307 L14.8654077,8.87250726 L15.2327873,9.24077972 C15.4131049,9.40799186 15.692774,9.40235843 15.8662663,9.22801943 C16.0397585,9.05368044 16.0451222,8.77289011 15.8784155,8.59200067 L14.4260378,7.13497254 C14.3406493,7.04858666 14.2244445,7 14.1032237,7 Z M10,0 C10.8817357,0.000861025619 11.6408671,0.63065615 11.8153846,1.50609017 L14.44,1.50609017 C15.0560099,1.50609017 15.5553846,2.01190203 15.5553846,2.63585258 C15.5553846,3.25980313 15.0560099,3.76561499 14.44,3.76561499 L5.56000001,3.76561499 C4.9439901,3.76561499 4.4446154,3.25980313 4.4446154,2.63585258 C4.4446154,2.01190203 4.9439901,1.50609017 5.56000001,1.50609017 L8.18461538,1.50609017 C8.35913286,0.63065615 9.11826429,0.000861025619 10,0 Z" id="形状结合" fill="#FFFFFF" fill-rule="nonzero"></path>
<g id="编组-5" transform="translate(12.194882, 7.000000)"></g>
</g>
<path d="M8.18461538,1.50609017 C8.35913286,0.63065615 9.11826429,0.000861025619 10,3.40838469e-13 C10.8817357,0.000861025619 11.6408671,0.63065615 11.8153846,1.50609017 L14.44,1.50609017 C15.0560099,1.50609017 15.5553846,2.01190203 15.5553846,2.63585258 C15.5553846,3.25980313 15.0560099,3.76561499 14.44,3.76561499 L5.56000001,3.76561499 C4.9439901,3.76561499 4.4446154,3.25980313 4.4446154,2.63585258 C4.4446154,2.01190203 4.9439901,1.50609017 5.56000001,1.50609017 L8.18538463,1.50609017 L8.18461538,1.50609017 Z" id="路径" fill="#FFBD36"></path>
</g>
<path d="M8.03730912,16.9297581 C8.03730912,16.3411273 7.56202684,15.8639477 6.97573645,15.8639477 C6.38944605,15.8639477 5.91416377,16.3411273 5.91416377,16.9297581 C5.91416377,17.5183889 6.38944605,17.9955684 6.97573645,17.9955684 C7.56202684,17.9955684 8.03730912,17.5183889 8.03730912,16.9297581 Z" id="路径" fill="#FFBD36"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<div class="car dialog1027">
<div class="dialog-header" v-interact>
<div class="dialog-header__icon" />
<div class="dialog-header__title">
快速建模
</div>
<div class="dialog-header_close" style="color:#fff" @click="() => $emit('close')">关闭</div>
</div>
<div class="dialog-content w267">
<el-cascader size="mini" filterable popper-class="cpt-observe-mspace-dashboard-airway_popper" v-model="taskCateId"
:options="airway_list" clearable :show-all-levels="false" placeholder="请选择任务" :props="{
children: 'children',
label: 'taskTitle',
value: 'id',
emitPath: false,
}">
<template slot-scope="{ data }">
<el-tooltip :disabled="data.taskTitle.length < 11" class="item" effect="dark" :content="data.taskTitle"
placement="top-start">
<span>{{ data.taskTitle }}</span>
</el-tooltip>
</template>
</el-cascader>
<el-select class="mt10" v-model="type" size="mini" placeholder="请选择类型">
<el-option label="图片" :value="0"></el-option>
<el-option label="视频" :value="1"></el-option>
</el-select>
<div class="jcsb w267 h30 mt30">
<div class="dec mt5">1</div><span class="dib cf ml10 lh30"> 快速建模</span>
<div class="btn fr cf h30 lh30 w80 cp" @click="jmFn(1)">启动</div>
</div>
<div class="jcsb mt18 w267 h30">
<div class="dec mt5">2</div><span class="dib cf lh30 ml10">数据处理</span>
<div class="btn fr cf h30 lh30 w80 cp" @click="jmFn(2)">启动</div>
</div>
</div>
</div>
</template>
<script>
// import API from '@/api';
export default {
props: {
device: {
type: Object,
default: () => ({})
},
},
data() {
return {
imgUrl: process.env.VUE_APP_IMG_URL,
list: [],
type: null,
taskCateId: null,
airway_list: []
}
},
created() {
this.list_airway();
},
methods: {
jmFn(num) {
let { deviceHardId } = this.device
if (num == 1) {
let a = document.createElement("a");
a.href = `MMCEagleEye:// `
a.click()
}
else {
if(!this.taskCateId) return this.$message.warning('请选择任务!')
if(this.type==null ) return this.$message.warning('请选择类型!')
let a = document.createElement("a");
a.href = `MMCPosTool://&deviceId=${deviceHardId}enddeviceId&taskId=${this.taskCateId}endtaskId&type=${this.type}endtype`
a.click()
}
},
async list_airway() {
/* let res = await API.AIRWAY.getApprovedTask();
if (res) {
let jqList = [],
ctList = [],
lsList = [];
for (let i = 0; i < res.length; i++) {
if (res[i].taskCateId == 1) {
jqList.push(res[i]);
}
if (res[i].taskCateId == 2) {
ctList.push(res[i]);
}
if (res[i].taskCateId == 3) {
lsList.push(res[i]);
}
}
let airway_list = [
{
id: "警情任务",
taskTitle: "警情任务",
children: jqList,
},
{
id: "常态任务",
taskTitle: "常态任务",
children: ctList,
},
{
id: "临时任务",
taskTitle: "临时任务",
children: lsList,
},
];
this.airway_list = airway_list;
} */
},
}
}
</script>
<style lang="scss" scoped>
.car {
width: 268px;
}
.dialog-content {
padding: 20px 14px 12px;
max-height: 461px;
overflow: auto;
.dec {
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
border-radius: 50%;
background: #06199b;
}
.btn {
text-align: center;
border: 1px #315ec7 solid;
border-radius: 3px;
background-color: #02173d;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #D2D9FF;
cursor: pointer;
}
.btn:hover {
background: #06199b;
}
}
.car-img {
position: relative;
width: 100%;
height: 135px;
.car-img__img {
width: 100%;
height: 100%;
}
.car-img__label {
position: absolute;
bottom: 0;
width: 100%;
height: 28px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: #aee9ff;
font-family: MicrosoftYaHeiUI;
}
}
.car-form {
padding: 10px 0 0;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #fff;
>div {
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
}
}
.car-item {
margin-bottom: 24px;
}
</style>
\ No newline at end of file
<template>
<div class="car dialog1027">
<div class="dialog-header" v-interact>
<div class="dialog-header__icon" />
<div class="dialog-header__title">
车辆识别
</div>
<div class="dialog-header_close" style="color:#fff" @click="()=>$emit('close')">关闭</div>
</div>
<div class="dialog-content" v-infinite-scroll="load">
<template v-if="list.length > 0" >
<div v-for="(item, i) in list" :key="i" class="car-item">
<div class="car-img">
<img class="car-img__img" :src="imgUrl + item.imageUrl" />
<div class="car-img__label">{{ item.plateNum || '暂无' }}</div>
</div>
<div class="car-form">
<div>地点:{{ item.addr || item.findAddress || '暂无' }}</div>
<div>时间:{{ item.findTime || '暂无' }}</div>
</div>
</div>
</template>
<template v-else>
<div style="text-align: center; width: 100%;">暂无数据</div>
</template>
</div>
</div>
</template>
<script>
// import API from '@/api';
export default {
props: {
uavId: {
type: String,
default: ''
}
},
data(){
return {
imgUrl: process.env.VUE_APP_IMG_URL,
list: [],
pageNo: 1,
pageSize: 3,
}
},
created(){
this.getCarList()
},
methods: {
async load(){
this.pageNo++
/* let res = await API.HOME.getflightvideoMsg({
uavId: this.uavId,
pageNo: this.pageNo,
pageSize: this.pageSize
})
for(let i = 0; i < res.length; i++){
let item = res[i];
if(item.lon){
let address = await API.MAP.AiRegeo({
location: `${item.lon},${item.lat}`
})
item.addr = address.province.value + address.city.value + address.dist.value + address.road.roadname + address.poi;
}
this.list.push(res[i])
}
console.log(this.list,"list"); */
},
async getCarList(){
if(!this.uavId){
this.$message.error('请选择无人机');
return;
}
let res = await API.HOME.getflightvideoMsg({
uavId: this.uavId,
pageNo: this.pageNo,
pageSize: this.pageSize
})
for(let i = 0; i < res.length; i++){
let item = res[i];
if(item.lon){
let address = await API.MAP.AiRegeo({
location: `${item.lon},${item.lat}`
})
item.addr = address.province.value + address.city.value + address.dist.value + address.road.roadname + address.poi;
}
this.list.push(res[i])
}
// this.list = res || [];
}
}
}
</script>
<style lang="scss" scoped>
.car{
width: 268px;
}
.dialog-content{
padding: 20px 14px 12px;
max-height: 461px;
overflow: auto;
}
.car-img{
position: relative;
width: 100%;
height: 135px;
.car-img__img {
width: 100%;
height: 100%;
}
.car-img__label {
position: absolute;
bottom: 0;
width: 100%;
height: 28px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: #aee9ff;
font-family: MicrosoftYaHeiUI;
}
}
.car-form{
padding: 10px 0 0;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #fff;
>div {
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
}
}
.car-item{
margin-bottom: 24px;
}
</style>
\ No newline at end of file
<template>
<div class="face dialog1027">
<div class="dialog-header" v-interact>
<div class="dialog-header__icon" />
<div class="dialog-header__title">
人脸识别
</div>
<div class="dialog-header_close" style="color:#fff" @click="()=>$emit('close')">关闭</div>
</div>
<div class="dialog-content" v-infinite-scroll="load">
<template v-if="list.length > 0">
<div class="result" v-for="(item, i) in list" :key="i">
<div class="rate-box">
<div class="rate-img">
<img :src="imgUrl + item.recordImageUrl" />
</div>
<div class="rate-round">
<div class="rate-round__value">{{ Number(item.similarity).toFixed(2) }}%</div>
<div class="rate-routd__text" @click="onDetail(item)">详情</div>
</div>
<div class="rate-img rate-img--contrary">
<img :src="imgUrl + item.snapImageUrl" />
</div>
</div>
<div class="rate-form" v-if="item.show">
<div class="rate-form-item">
<div class="rate-form-item__label">
姓名:
</div>
<div class="rate=form-item__value">
{{ item.userName || '暂无' }}
</div>
</div>
<div class="rate-form-item">
<div class="rate-form-item__label">
出生年份:
</div>
<div class="rate=form-item__value">
{{ item.birthday || '暂无' }}
</div>
</div>
<div class="rate-form-item">
<div class="rate-form-item__label">
发现时间:
</div>
<div class="rate=form-item__value">
{{ item.occurTime || '暂无' }}
</div>
</div>
<div class="rate-form-item">
<div class="rate-form-item__label">
发现地点:
</div>
<div class="rate=form-item__value">
{{ item.addr || item.address || '暂无' }}
</div>
</div>
<div class="rate-form-item">
<div class="rate-form-item__label">
身份证号:
</div>
<div class="rate=form-item__value">
{{ item.idCard || '暂无' }}
</div>
</div>
<div class="rate-form-item">
<div class="rate-form-item__label">
AI识别:
</div>
<div class="rate=form-item__value">
<template v-if="item.labels && item.labels.length">
<div class="round-border" v-for="(label, j) in item.labels" :key="j" >
{{label}}
</div>
</template>
<template v-else>
暂无
</template>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div style="text-align: center; width: 100%;">暂无数据</div>
</template>
</div>
</div>
</template>
<script>
// import API from '@/api';
export default {
props: {
uavId: {
type: String,
default: ''
}
},
data(){
return {
imgUrl: process.env.VUE_APP_IMG_URL,
list: [
/* {
userName: '人热二',
birthday: '1888-02-11',
occurTime: '2022-02-30 08:22:14',
address: '深圳市南山区',
idCard: '112313123123',
labels: ['省内人员'],
similarity: '63',
img: '',
imgs: '',
show: true
} */
],
pageNo: 1,
pageSize: 4,
}
},
created(){
this.getFaceList();
},
methods: {
async load(){
this.pageNo++
/* let res = await API.HOME.getFaceuavvideoMsg({
uavId: this.uavId,
pageNo: this.pageNo,
pageSize: this.pageSize
})
for(let i = 0; i < res.length; i++){
let item = res[i];
if(item.longi){
let address = await API.MAP.AiRegeo({
location: `${item.longi},${item.lati}`
})
item.addr = address.province.value + address.city.value + address.dist.value + address.road.roadname + address.poi;
}
this.list.push(res[i])
}
console.log(this.list,"list"); */
},
async getFaceList(){
if(!this.uavId){
this.$message.error('请选择无人机');
return;
}
let res = await API.HOME.getFaceuavvideoMsg({
uavId: this.uavId,
pageNo: this.pageNo,
pageSize: this.pageSize
}) || [];
for(let i = 0; i < res.length; i++){
let item = res[i];
if(item.longi){
let address = await API.MAP.AiRegeo({
location: `${item.longi},${item.lati}`
})
item.addr = address.province.value + address.city.value + address.dist.value + address.road.roadname + address.poi;
}
this.list.push(res[i])
}
// this.list = res || [];
},
onDetail(item){
this.$set(item, 'show', !item.show);
}
}
}
</script>
<style lang="scss" scoped>
.face{
width: 268px;
}
.dialog-content{
padding: 14px;
cursor: initial;
max-height: 461px;
overflow-y: auto;
}
.result{
width: 100%;
margin-top: 27px;
&:first-child {
margin-top: 0;
}
}
.rate-box{
display: flex;
align-items: center;
position: relative;
width: 100%;
.rate-img{
flex: 1;
height: 75px;
background-image: url('../../assets/images/faceAI_border1.png');
background-size: 100% 100%;
padding: 7px;
display: flex;
align-items: center;
&.rate-img--contrary {
background-image: url('../../assets/images/faceAI_border2.png');
justify-content: right;
}
img {
width: 70px;
height: 70px;
border-radius: 7px;
overflow: hidden;
}
}
.rate-round{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.rate-round__value{
color: #fb799d;
font-size: 14px;
font-family: MicrosoftYaHei;
}
.rate-routd__text{
cursor: pointer;
font-size: 12px;
color: #b9d7f0;
font-family: MicrosoftYaHei;
transform: scale(0.9);
margin-top: 3px;
}
}
}
.rate-form{
margin-top: 10px;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #fff;
.rate-form-item {
margin-bottom: 12px;
display: flex;
&:last-child {
margin-bottom: 0;
}
.rate-form-item__label {
width: 70px;
text-align: right;
flex-shrink: 0;
}
.rate-form-item__value {
}
}
}
.round-border{
padding: 2px 8px;
border-radius: 11px;
border: 1px solid #ff4a4a;
color: #ff4a4a;
font-size: 14px;
font-family: MicrosoftYaHei;
}
</style>
\ No newline at end of file
<template>
<div class="livenvr">
<LivePlayer
:dblclick-fullscreen="false"
aspect="fulllscreen"
ref="livePlayer"
:videoUrl="url"
:controls="false"
:live="true"
/>
</div>
</template>
<script>
import LivePlayer from "@liveqing/liveplayer";
export default {
name: 'liveNVRPlayer',
props: {
url: {
type: String,
default: '',
},
},
components: { LivePlayer },
data() {
return {};
},
watch: {
url: {
handler(newVal, oldVal) {
if (newVal == oldVal) {
return;
}
// console.log('livenvr', value)
if (newVal ) {
this.$nextTick(() => {
this.$refs["livePlayer"].play();
});
}
},
deep: true,
immediate: true,
},
},
mounted() {
},
methods: {
init() {
},
fullScreen(){
this.$refs.livePlayer.fullScreen();
}
},
};
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<div class="qingliu" :class="classNames" id="qinglliu" ref="qingliu">
<div :id="'qingliu_' + name" class="qingcanvas" ref="qingcanvas">
</div>
</div>
</template>
<script>
export default {
name: "QingLiuPlayer",
props: {
url: {
type: String,
default: ''
}
},
data() {
return {
name: "QingLiuPlayer" + Date.now(),
};
},
watch: {
},
created() {},
mounted() {
this.init();
},
beforeDestroy() {
window.kbt_player_destroy("qingliu_" + this.name);
window.removeEventListener("resize", this.handResize);
},
methods: {
/**
* 初始化
*/
init() {
// 新
window.kbt_sdk_load(this.url, "qingliu_" + this.name);
// 监听窗口大小变化事件
window.addEventListener("resize", this.handResize);
var canvas_qinliu = document.getElementById("qingliu_" + this.name);
var resizeObserver = new ResizeObserver((e) => {
for (let i of e) {
window.kbt_player_resize("qingliu_" + this.name);
}
});
resizeObserver.observe(canvas_qinliu);
},
handResize() {
// 获取 Canvas 元素
var canvas_qinliu = document.getElementById("qingliu_" + this.name);
// 获取 Canvas 的初始宽度和高度
var initialWidth = canvas_qinliu.width;
var initialHeight = canvas_qinliu.height;
// 获取当前 Canvas 的宽度和高度
var currentWidth = canvas_qinliu.clientWidth;
var currentHeight = canvas_qinliu.clientHeight;
// 检查 Canvas 的大小是否发生变化
if (currentWidth !== initialWidth || currentHeight !== initialHeight) {
// 执行适应新尺寸的操作
window.kbt_player_resize("qingliu_" + this.name);
}
},
fullScreen(flag) {
if (flag) {
let dom = document.querySelector("#qingliu_" + this.name);
dom.requestFullscreen();
} else {
document.exitFullscreen();
}
},
},
};
</script>
<style scoped lang="scss">
.qingliu ::v-deep {
width: 100%;
height: calc(100% - 46px);
.qingcanvas {
width: 100%;
height: 100%;
}
canvas {
width: 100% !important;
}
}
</style>
<template>
<div class="cpt-player-webrtc">
<video id="rtc_media_player" ref="webrtc" controls autoplay></video>
</div>
</template>
<script>
import $ from "./jquery-1.10.2.min";
import {
SrsRtcPublisherAsync,
SrsRtcPlayerAsync,
SrsRtcFormatSenders,
} from "./srs.sdk";
window.$ = $;
export default {
props: {
data: {
type: Object,
default: () => ({}),
},
},
data() {
return {
sdk: null,
};
},
watch: {
data: {
handler(value) {
this.$nextTick(() => {
this.init();
});
},
deep: true,
immediate: true,
},
},
beforeDestroy() {
this.sdk.close();
},
methods: {
pause() {
if (this.$refs["webrtc"]) {
this.$refs["webrtc"].pause();
}
},
play() {
if (this.$refs["webrtc"]) {
this.$refs["webrtc"].play();
}
},
init() {
let _this = this;
if (this.$refs["webrtc"]) {
if (this.sdk) {
this.sdk.close();
}
this.sdk = new SrsRtcPlayerAsync();
this.$refs["webrtc"].srcObject = this.sdk.stream;
this.sdk
.play(this.data.vUrl)
.then(function (session) {})
.catch(function (reason) {
console.log('srs err', reason)
_this.sdk.close();
});
}
},
fullScreen() {
let video = this.$refs["webrtc"];
if (video.webkitRequestFullScreen) {
video.webkitRequestFullScreen();
} else if (video.requestFullscreen) {
video.requestFullscreen();
} else if (video.mozRequestFullScreen)
setTimeout(() => {
video.play();
}, 200);
},
},
};
</script>
<style lang="scss" scoped>
.cpt-player-webrtc {
height: 100%;
width: 100%;
background-color: #000;
video {
width: 100%;
height: 100%;
object-fit: fill;
}
}
</style>
\ No newline at end of file
//
// Copyright (c) 2013-2021 Winlin
//
// SPDX-License-Identifier: MIT
//
'use strict';
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher.
export function SrsRtcPublisherAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the publish:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(eip) of answer:
// webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.publish = async function (url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: track});
});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function (resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType: 'application/json', dataType: 'json'
}).done(function (data) {
if (data.code) {
reject(data);
return;
}
resolve(data);
}).fail(function (reason) {
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/publish/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-await-promise based SRS RTC Player.
export function SrsRtcPlayerAsync() {
var self = {};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the play:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(eip) of answer:
// webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.play = async function(url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function(resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType:'application/json', dataType: 'json'
}).done(function(data) {
if (data.code) {
reject(data); return;
}
resolve(data);
}).fail(function(reason){
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the player.
self.close = function() {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got remote track.
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
self.ontrack = function (event) {
// https://webrtc.org/getting-started/remote-streams
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/play/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
console.log("RTCPeerConnection");
self.pc = new RTCPeerConnection(null);
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
self.stream = new MediaStream();
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
export function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}
var s = '';
s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;
codecs.push(s);
});
});
return codecs.join(", ");
}
<template>
<div class="video-wrap" @dblclick="screen" ref="video">
<div class="cpt_video">
<div class="video-header">
<div class="title">
<span class="title-scroll">{{ name }}</span>
<span class="title-scroll">{{ name }}</span>
</div>
<span class="label">{{ label }}</span>
<div class="el-icon-close close" @click="close"></div>
</div>
<components :is="playerCom" ref="player" :url="url" :className="'fkLivePlayer'" />
<slot></slot>
</div>
</div>
</template>
<script>
import LiveNVRPlayer from "./components/livenvr/index.vue";
import QingLiuPlayer from "./components/qingliu";
import SRSPlayer from "./components/srs";
export default {
name: "Player",
components: {
QingLiuPlayer,
LiveNVRPlayer,
SRSPlayer,
},
props: {
url: {
type: String,
default: "",
},
brand: {
type: String,
default: "qingliu",
},
// 持续滚动的文字
name: {
type: String,
default: "",
},
// 关闭图标旁边的文字
label: {
type: String,
default: "",
},
},
data() {
return {
isFullScreen: false, //是否全屏
};
},
computed: {
/**
* 播放器组件名
*/
playerCom() {
switch (this.brand.toLowerCase()) {
case "flv":
return "LiveNVRPlayer";
case "srs":
return "SRSPlayer";
case "livenvr":
return "LiveNVRPlayer";
case "qingliu":
return "QingLiuPlayer";
case "webrtc":
// return "LiveNVRPlayer";
return "SRSPlayer";
default:
return "LiveNVRPlayer";
}
},
},
watch: {},
async mounted() {},
beforeDestroy() {},
methods: {
close(data) {
this.$emit("close");
},
screen() {
this.isFullScreen = !this.isFullScreen;
if (this.playerCom === "LiveNVRPlayer") {
this.$refs.player.fullScreen(this.isFullScreen);
} else {
this.$refs.player.fullScreen(this.isFullScreen);
}
},
play() {
if (this.playerCom === "LiveNVRPlayer") {
this.$refs.player.$refs["livePlayer"].pause();
}
},
pause() {
if (this.playerCom === "LiveNVRPlayer") {
this.$refs.player.$refs["livePlayer"].play();
}
},
async reset() {
this.showPlayer = false;
this.$nextTick(() => {
this.showPlayer = true;
});
},
},
};
</script>
<style lang="scss" scoped>
.video-wrap {
}
.cpt_video {
width: 100%;
height: 100%;
background: #333;
align-items: center;
position: relative;
.video-header {
background: rgba(0, 0, 0, 0.6);
box-sizing: border-box;
display: flex;
align-items: center;
position: absolute;
top: 5px;
width: 100%;
z-index: 1;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
.close {
cursor: pointer;
opacity: 0.6;
color: #ffffff;
}
.title {
font-family: Microsoft YaHei;
font-size: 14px;
color: #fff;
font-weight: 400;
white-space: nowrap;
line-height: 30px;
overflow: hidden;
.title-scroll {
display: inline-block;
animation: scrollTitle 15s linear infinite;
position: relative;
box-sizing: border-box;
padding-right: 50px;
}
}
.label {
white-space: nowrap;
margin-left: 5px;
color: #fff;
font-size: 12px;
}
}
}
@keyframes scrollTitle {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
</style>
<template>
<div>
<Player @close="$emit('close')" :url="src.url" :brand="src.brand" :name="name" label="舱内" />
</div>
</template>
<script>
import Player from "../components/player";
import { mapState } from "vuex";
export default {
name: "InnerPlayer",
components: { Player },
data() {
return {};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["hangar"]),
name() {
return `${this.hangar?.uav?.organizationName || ""}机库无人机`;
},
src() {
return (
this.hangar?.uav?.streamConfiguration
?.find((v) => v.videoType === 2)
?.streamUrlMessage.map((v) => ({
label: v.streamType.toLocaleLowerCase(),
url: v.streamUrl,
brand: v.streamType,
}))[0] || {
url: "",
brand: "",
}
);
},
},
};
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<div>
<Player @close="$emit('close')" :url="src.url" :brand="src.brand" :name="name" label="舱外" />
</div>
</template>
<script>
import Player from "../components/player";
import { mapState } from "vuex";
export default {
name: "OuterPlayer",
components: { Player },
data() {
return {};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["hangar"]),
name() {
return `${this.hangar?.uav?.organizationName || ""}机库无人机`;
},
src() {
return (
this.hangar?.uav?.streamConfiguration
?.find((v) => v.videoType === 3)
?.streamUrlMessage.map((v) => ({
label: v.streamType.toLocaleLowerCase(),
url: v.streamUrl,
brand: v.streamType,
}))[0] || {
url: "",
brand: "",
}
);
},
},
methods: {},
};
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<div>
<Player @close="$emit('close')" :url="selectStream.url" :brand="selectStream.brand" :name="name" :label="label" />
<el-select v-model="selectUrl" class="select" popper-class="mmc">
<el-option
v-for="item in streamConfiguration"
:label="item.brand"
:value="item.url"
:key="item.url"
></el-option>
</el-select>
</div>
</template>
<script>
import Player from "../components/player";
import { mapState } from "vuex";
import { coverStreamUrl } from "../utils";
export default {
name: "UAVPlayer",
components: { Player },
props: {},
data() {
return {
selectUrl: "", //选择的流的url
};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["hangar"]),
name() {
return `${this.hangar?.uav?.organizationName || ""}机库无人机`;
},
label() {
return this.hangar?.uav?.organizationName || "";
},
/**
* 可选流
*/
streamConfiguration() {
return (
this.hangar?.uav?.streamConfiguration
?.find((v) => v.videoType === 1)
?.streamUrlMessage.map((v) => ({
url: /* coverStreamUrl(v.streamUrl, this.hangar.uav.hardId) */ v.streamUrl,
brand: v.streamType,
})) || []
);
},
selectStream() {
return (
this.streamConfiguration.find(item => item.url === this.selectUrl) || {
url: "",
brand: "",
}
);
},
},
mounted() {
this.selectUrl = this.streamConfiguration[0]?.url || '';
},
methods: {},
};
</script>
<style lang="scss" scoped>
.select ::v-deep{
width: 95px;
margin-top: 5px;
input {
height: 30px;
}
.el-select__caret {
display: flex;
align-items: center;
width: initial;
}
}
</style>
\ No newline at end of file
import { Base64 } from "js-base64";
/**
* 转换流地址, 这样后端才可统计播放情况
* @param {*} url
* @param {*} hardId
* @returns
*/
export function coverStreamUrl(url, hardId) {
if (url.includes("https://live.mmcuav.cn")) {
const str = Base64.encode(
JSON.stringify({
token: localStorage.getItem("tmj_token"),
actionId: new Date().getTime().toString(),
deviceHardId: hardId,
environment: process.env.VUE_APP_ENV === "dev" ? "sit" : "prod",
})
);
return `${url}?${str}`;
} else {
return url;
}
}
<template>
<div class="cpt-command-airway-list">
<div class="hd" v-interact>
<div class="left ml8">
<img src="../../../../../../assets/images/mount_head.png" />
<div class="title">航空航线</div>
</div>
<div @click="close" class="close">关闭</div>
</div>
<div class="list-box">
<div class="tb-box">
<div class="tb-hd-box">
<div class="tb-hd">航线ID</div>
<div class="tb-hd">航线名称</div>
<div class="tb-hd">所属单位</div>
<div class="tb-hd">空域状态</div>
<div class="tb-hd">安全状态</div>
<div class="tb-hd">航线标签</div>
<div class="tb-hd last-tb-hd">操作</div>
</div>
<div class="tb-bd-box">
<div class="tb-tr" v-for="item in airwayData.records" :key="item.id">
<div class="td">{{ item.id || "暂无" }}</div>
<div class="td">
<div>{{ item.name || "暂无" }}</div>
</div>
<div class="td">{{ item.organizationName || "暂无" }}</div>
<!-- 空域状态 -->
<div class="td">
<div v-if="item.status == 1" class="status">可用</div>
<div v-else-if="item.status == 2" class="status" style="color: #2ca1e2">待申请</div>
<div v-else-if="item.status == 3" class="status" style="color: #ffbd36">待审批</div>
<div v-else-if="item.status == 4" class="status" style="color: #2bfdf1">通过</div>
<div v-else-if="item.status == 5" class="status" style="color: #fb4a2d">驳回</div>
<div v-else>暂无</div>
</div>
<!-- 模式 -->
<!-- <div class="td">{{ item.distance || "暂无" }}</div> -->
<!-- 安全状态 -->
<div
class="td"
:style="{ color: item.isSafe == 1 ? '#19D864' : '' }"
>{{ item.isSafe == 1 ? "安全" : "待确定" }}</div>
<!-- 航线标签 -->
<div class="td">
<!-- <span v-for="item2 in item.labelList"
:key="item2.labelId">{{ item2.labelName }}</span>-->
{{ item.labelName ||"暂无" }}
</div>
<div class="td last-td" style="width: 15%">
<div @click="changeLine(item)">选择航线</div>
</div>
</div>
</div>
</div>
</div>
<el-pagination
layout="prev, pager, next"
:total="airwayData.total"
:pageSize="airwayData.size"
:currentPage.sync="airwayData.current"
@current-change="getAirway"
></el-pagination>
</div>
</template>
<script>
import { mapState } from "vuex";
import { Control_API } from "../../../../../../../../../../../../api";
let point_index = null;
let isEditting = false;
let airline_entitys = [];
export default {
data() {
return {
keyword: null,
airwayData: {
current: 1,
records: [],
searchCount: true,
size: 10,
total: 0,
},
};
},
computed: {
// ...mapState("MMCFlightControlCenter", ["airwayList"]),
},
mounted() {
this.getAirway();
},
methods: {
async changeLine(item) {
try {
if (item.isSafe != 1) {
await this.$confirm(
"此航线为非安全航线,开始任务前请确认航线安全!",
"安全确认",
{ customClass: "uav_controlPane", showClose: false }
);
}
this.$emit("change", item);
this.close();
} catch (e) {}
},
close() {
this.$emit("close");
},
async getAirway() {
let res = await Control_API.getUavRouteList({
page: this.airwayData.current,
pageSize: this.airwayData.size,
});
if (res?.code === 200) {
this.airwayData = res.data || {
current: 1,
records: [],
searchCount: true,
size: 10,
total: 0,
};
}
},
},
};
</script>
<style lang="scss" scoped>
.cpt-command-airway-list {
width: 600px;
position: absolute;
right: -600px;
top: 300px;
// width: 1132px;
// height: 689px;
box-sizing: border-box;
// background: rgba(0, 23, 79, 0.7);
// box-shadow: 0 2px 4px 0 rgba(1, 162, 255, 0.35),
// inset 0 0 40px 0 rgba(0, 184, 255, 0.5);
// border-radius: 13px;
background: rgba(0, 23, 79, 0.7);
box-shadow: 0 2px 4px 0 rgba(1, 162, 255, 0.35),
inset 0 0 40px 0 rgba(0, 184, 255, 0.5);
border-radius: 10px;
.hd {
height: 32px;
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(16, 65, 215, 0.2);
box-shadow: inset 0 0 15px 0 rgba(0, 182, 255, 0.6);
border-radius: 10px 10px 0 0;
.left {
display: flex;
align-items: center;
.title {
font-size: 20px;
font-family: YouSheBiaoTiHei;
color: #14faff;
line-height: 26px;
text-shadow: 0px 1px 1px rgba(2, 32, 56, 0.2);
background: linear-gradient(
135deg,
#e3aa77 0%,
#f5cda9 38%,
#f9ecd3 58%,
#fcdbb1 79%,
#edb07a 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.close {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #d2dfff;
margin-right: 8px;
cursor: pointer;
}
}
.list-box {
// width: 1132px;
height: calc(100% - 56px);
box-sizing: border-box;
padding: 0 16px 0 16px;
.search-box {
margin-left: 0 !important;
// height: 80px;
height: auto;
margin: 24px 0 24px 0;
box-sizing: border-box;
display: flex;
align-items: center;
.item-plan {
width: 79px;
height: 32px;
background: rgba(28, 67, 191, 0.6) !important;
box-shadow: 0px 2px 4px 0px rgba(23, 33, 60, 0.5),
inset 0px 0px 16px 0px rgba(33, 137, 255, 0.4),
inset 0px 0px 4px 0px #00a7ff;
border: 1px solid;
border-image: linear-gradient(
180deg,
rgba(138, 218, 255, 1),
rgba(82, 179, 255, 0)
)
1 1;
border-radius: 0px;
font-size: 12px;
font-family: MicrosoftYaHei;
color: #43deff;
line-height: 16px;
}
.item-plan:hover {
opacity: 0.5;
}
.item-input {
width: 168px;
height: 40px;
margin-left: 10px;
// margin-right: 10px;
color: #08c2d1;
::v-deep .el-input__inner {
background: rgba(2, 31, 51, 0);
border: 0px solid #06b6e0;
border-radius: 4px;
font-family: MicrosoftYaHeiUI;
font-size: 16px;
color: #08c2d1;
font-weight: 400;
padding-left: 0;
&::placeholder {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #397c8b;
line-height: 19px;
}
}
}
.andLinlineBtn {
position: absolute;
top: 65px;
right: 160px;
width: 130px;
cursor: pointer;
height: 32px;
opacity: 0.8;
font-family: PangMenZhengDao;
font-size: 22px;
color: #00ffff;
text-align: center;
font-weight: 400;
line-height: 40px;
}
.routeLabelBtn {
position: absolute;
top: 65px;
right: 20px;
width: 130px;
cursor: pointer;
height: 40px;
opacity: 0.8;
font-family: PangMenZhengDao;
font-size: 22px;
color: #00ffff;
text-align: center;
font-weight: 400;
line-height: 40px;
}
.routeLabelBtnDefault {
background: rgba(0, 3, 36, 0.8);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5),
inset 0 0 15px 0 rgba(0, 182, 255, 0.9);
border-radius: 6px;
}
.routeLabelBtnActive {
background: rgba(0, 34, 140, 0.2);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5), inset 0 0 10px 0 #00b6ff;
border-radius: 6px;
}
}
.tb-box {
// overflow-x: scroll;
// overflow-y: hidden;
padding: 0 !important;
.tb-hd-box {
display: flex;
background: #081a3a;
// width: 1415.62px;
border-top: 1px solid rgba(207, 234, 255, 0.33);
border-bottom: 1px solid rgba(207, 234, 255, 0.33);
height: 37.6px;
.tb-hd {
flex: 1;
line-height: 37.6px;
width: calc(100% / 8);
white-space: nowrap;
height: 100%;
text-align: center;
font-family: MicrosoftYaHei-Bold;
font-size: 16px;
color: #b5e5ff;
letter-spacing: 0;
font-weight: 700;
}
}
.tb-bd-box {
width: 570px;
max-height: 280px;
overflow: hidden;
overflow-y: auto;
.tb-tr {
display: flex;
color: #fff;
align-items: center;
font-size: 14px;
width: 560px;
margin: 5px 0 0 0;
// border: 1px solid;
background: #081a3a;
cursor: pointer;
// background-image: url("~@/assets/newImage/tiaokaung.png") !important;
background-size: 100% 100%;
// &:hover {
// // background: rgba(2, 19, 96, 0.2);
// box-shadow: inset 0px 0px 10px 2px #3fcbff;
// // border: 1px solid #70f6f9;
// }
&:nth-of-type(2n - 1) {
background: rgba(73, 135, 210, 0.5);
}
.td {
// width: calc(100% / 7);
flex: 1;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
}
.last-td {
text-align: center;
width: 170px;
div {
background: url("../../../../../../assets/images/btn.png") no-repeat;
background-size: 100% 100%;
padding: 5px 12px;
}
.iconfont {
margin-right: 7px;
}
&:hover {
color: #43deff;
}
}
}
}
}
}
}
.toubu {
display: flex;
.tiao {
width: 3px !important;
height: 15px;
background: #ffbd36;
margin: 6px 5px 0 16px;
}
.hd-box {
font-size: 18px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: #ffffff;
line-height: 24px;
text-shadow: 0px 2px 4px #136791;
}
}
.cpt-command-airway-list .tb-box {
height: 100%;
}
.flex {
display: flex;
}
.uavImg {
width: 20px;
height: 20px;
margin: 0 6px 0 6px;
img {
width: 100%;
height: 100%;
}
}
.fangkuai {
border: 1px solid #43deff;
height: 32px;
background: rgba(13, 50, 92, 0.7);
}
.duanxian {
width: 1px;
height: 22px;
border-left: 1px solid;
border-image: linear-gradient(
180deg,
rgba(67, 222, 255, 0),
rgba(67, 222, 255, 1),
rgba(67, 222, 255, 0)
)
1 1;
}
.el-input__suffix-inner::v-deep {
i {
color: #43deff;
}
}
// 滚动动画
.animate {
padding-left: 20px;
// font-size: 12px;
// color: #000;
display: inline-block;
white-space: nowrap;
animation: 5s wordsLoop linear infinite normal;
}
@keyframes wordsLoop {
0% {
transform: translateX(100%);
-webkit-transform: translateX(100%);
}
100% {
transform: translateX(-100%);
// -webkit-transform: translateX(-100%);
}
}
// @-webkit-keyframes wordsLoop {
// 0% {
// transform: translateX(100%);
// -webkit-transform: translateX(100%);
// }
// 100% {
// transform: translateX(-100%);
// -webkit-transform: translateX(-100%);
// }
// }
.tb-tr::v-deep .td {
padding: 18px 0 !important;
}
// 提示框样式
.td::v-deep .el-tooltip {
background: rgba(2, 19, 96, 0);
border: 0px solid rgba(207, 234, 255, 0.33);
font-family: MicrosoftYaHei;
color: #ffffff;
line-height: 19px;
}
// 空域状态
.status {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #00d45c;
line-height: 19px;
}
//操作状态
.el-tooltip {
opacity: 0.7;
}
.el-tooltip:hover {
opacity: 1;
}
// 页签样式
.zongji {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #b5e5ff;
line-height: 16px;
.tiaoshu {
color: #43deff;
}
}
.douhao {
margin: 0 5px 0 5px;
}
.dangqianye {
margin: 0 20px 0 0;
}
.el-pager::v-deep .number:hover {
background: #00b6ff !important;
border-radius: 2px;
color: #000 !important;
width: 14px;
height: 22px;
line-height: 22px;
}
.el-pagination--small::v-deep .el-pager .number {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #889fb2;
}
.active::v-deep {
color: #000 !important;
}
.btnqueding {
margin: 0 0 0 8px;
width: 79px;
height: 32px;
background: rgba(28, 67, 191, 0.6) !important;
box-shadow: 0px 2px 4px 0px rgba(23, 33, 60, 0.5),
inset 0px 0px 16px 0px rgba(33, 137, 255, 0.4),
inset 0px 0px 4px 0px #00a7ff;
border-radius: 0px;
border: 1px solid;
border-image: linear-gradient(
180deg,
rgba(138, 218, 255, 1),
rgba(82, 179, 255, 0)
)
1 1;
line-height: 1px;
color: #43deff;
}
.btnqueding:hover {
opacity: 0.5 !important;
}
.tb-pagination {
margin: 0 0 19px 0;
}
.zhuan {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #889fb2;
line-height: 16px;
input {
width: 48px;
min-width: 48px;
max-width: auto;
text-align: center;
height: 28px;
background: rgba(12, 13, 20, 0.5);
border-radius: 2px;
border: 1px solid rgba(36, 146, 252, 0.3);
margin: 0 5px 0 5px;
color: #fff;
outline: 0px solid;
}
}
// 说明
.shuo {
margin: 0 0 29px 0;
}
.shuoming {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #43deff;
line-height: 16px;
}
.maohao {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #43deff;
line-height: 16px;
margin: 0 14px 0 5px;
}
.icons {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #b3bbc5;
line-height: 16px;
margin: 0 12px 0 0;
}
.cpt-command-flight-task-explain {
display: flex;
align-items: center;
margin-left: 4px;
// margin-top: 8px;
padding: 0;
margin: 28px 0 29px 0;
.explain_title {
font-family: MicrosoftYaHei;
font-size: 14px;
color: #08c2d1;
}
.explain_box {
display: flex;
}
.explain_box_detail {
margin-right: 15px;
font-size: 12px;
font-family: MicrosoftYaHei;
color: #b3bbc5;
}
}
.waixian {
// border-radius: 6px;
// border: 1px solid #8adaff;
}
.el-tooltip__popper::v-deep .is-dark {
z-index: -100;
}
.el-pagination::v-deep {
text-align: center;
.btn-prev,
.btn-next {
color: #fff;
background-color: transparent;
}
.el-pager {
color: #fff;
li {
background-color: transparent;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="normal-task">
<el-form label-width="60px">
<el-form-item label="任务库">
<el-select
v-model="selectedTaskId"
popper-class="mmc"
@change="onChangeTask"
placeholder="请选择任务"
clearable
>
<el-option v-for="item in taskListAll" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="航线">
<el-button
v-if="selectedTaskId !== -1"
:disabled="selectedTaskId === ''"
@click="onStartTask"
>一键任务</el-button>
<el-button v-else @click="showFlywayDialog = true">请选择航线</el-button>
</el-form-item>
</el-form>
<LineList v-if="showFlywayDialog" @close="showFlywayDialog = false;" @change="onChangeLine"></LineList>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
import LineList from "./components/lineList";
import { flightTaskAPI, AirLine } from "../../../../../../../../../../api";
import hangarStatusDict from "../../hangarStatusDict";
export default {
name: "normalTask",
components: {
LineList,
},
inject: ["rootNode"],
data() {
return {
// 选择的任务id
selectedTaskId: "",
// 选择的航线
selectedAirway: {
name: "",
id: -1,
},
// 航线选择窗口
showFlywayDialog: false,
};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", [
"hangarRealTimeData",
"hangar",
"taskList",
]),
taskListAll() {
return [
{
id: -1,
name: "选择航线自动生成任务",
},
...this.taskList.normal,
];
},
/**
* 是否选择任务
*/
isSelectTask() {
return this.selectedTaskId[0] !== -1;
},
/**
* 选择的任务数据
*/
selectedTask() {
let find = this.taskList.normal.find((item) => {
return item.id === this.selectedTaskId;
});
return find;
},
},
watch: {
selectedAirway() {
this.clearAirwayEntities();
if (this.selectedAirway.id !== -1) {
try {
let airway = JSON.parse(this.selectedAirway.content);
this.createAirwayEntities({
polyline: airway.content,
id: airway.id,
});
} catch (e) {
console.log("绘制航线失败", e);
}
}
},
},
created() {
this.rootNode.$emit("changeHangarTaskTab", {
type: 1,
});
},
beforeDestroy(){
this.clearAirwayEntities();
},
methods: {
...mapActions("MMCFlightControlCenter", [
"createAirwayEntities",
"clearAirwayEntities",
]),
/**
* 更改任务事件
*/
async onChangeTask() {
if (this.selectedTask?.airwayId) {
let res = await AirLine.routeDetail({
id: this.selectedTask?.airwayId,
});
if (res.code === 200) {
this.selectedAirway = res.data;
}
} else {
this.selectedAirway = {
name: "",
id: -1,
};
}
},
/**
* 更改航线事件
*/
onChangeLine(data) {
this.selectedAirway = data;
if (data) {
this.rootNode.$emit("addHangarTask", {
type: 1, //1: 日常任务 2.定时任务 3.周期任务
airway: data, //航线数据
callback: (id) => {
// 返回新增任务后的任务id
this.selectedTaskId = id;
},
}); // 根节点发送机库任务新增事件
}
},
/**
* 一键任务事件
*/
async onStartTask() {
// 判断是否选择了航线
if (this.selectedAirway.id === -1) {
this.$message.warning("请选择航线");
return;
}
try {
await this.$confirm("请确认是否进行一键任务操作?", "安全确认", {
cancelButtonText: "取消",
confirmButtonText: "确定",
customClass: "uav_controlPane",
showClose: false,
});
// 当前机库状态是否空闲
if ([0, 8].includes(this.hangarRealTimeData.processStatus)) {
this.$store.commit("MMCFlightControlCenter/hangar/state", {
key: "airlineData",
value: this.selectedAirway,
});
this.$store.dispatch("MMCFlightControlCenter/hangar/takeOff", {
callback: (status) => {
if (status) {
this.$message.success("一键任务指令发送成功");
} else {
this.$message.error("一键任务指令发送失败");
}
},
});
this.rootNode.$emit("hangarStartTask", {
type: 1, //1: 日常任务 2:定时任务 3:周期任务
hangar: this.hangar,
task: this.selectedTask,
airway: this.selectedAirway,
});
} else {
// 获取当前机库状态
const statusItem = hangarStatusDict.find(
(item) => item.value === this.hangarRealTimeData.processStatus
);
this.$message.warning(statusItem?.label || "");
}
} catch (e) {}
},
},
};
</script>
<style lang="scss" scoped>
.normal-task ::v-deep {
padding: 32px 16px;
.el-form-item__label {
color: #fff;
}
.el-form-item__content {
> * {
width: 100%;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="timed-task">
<div class="timed-task-header">
<div class="header__column flex2">名称</div>
<div class="header__column flex2">时间</div>
<div class="header__column">状态</div>
<div class="header__column flex2">操作</div>
</div>
<div class="timed-task-main">
<div class="row" v-for="item in taskListAll" :key="item.id">
<div class="row__column flex2">
<el-tooltip class="item" effect="dark" :content="item.name" placement="top-start">
<span>{{item.name}}</span>
</el-tooltip>
</div>
<div class="row__column flex2">{{item.time}}</div>
<div class="row__column" style="color: rgb(255, 189, 54);">{{item.status}}</div>
<div class="row__column flex2 ctrl">
<el-tooltip content="查看" placement="top">
<span
class="icon-chakan1 iconfont icon"
style="color: #ffffff; font-size: 10px;"
@click="onSwitchAirway(item)"
></span>
</el-tooltip>
<el-tooltip content="历史" placement="top">
<img class="icon" style="width: 15px;" src="../../assets/images/history.png" />
</el-tooltip>
<el-tooltip content="禁用" v-if="!item.enable" placement="top">
<img class="icon" style="width: 18px;" src="../../assets/images/enable.png" />
</el-tooltip>
<el-tooltip content="启用" v-if="item.enable" placement="top">
<img class="icon" style="width: 15px;" src="../../assets/images/able.png" />
</el-tooltip>
<el-tooltip content="删除" placement="top">
<span class="icon-shanchu iconfont icon"></span>
</el-tooltip>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from "vuex";
import { AirLine } from "../../../../../../../../../../api";
export default {
name: "hangarTimedTask",
data() {
return {};
},
computed: {
...mapState("MMCFlightControlCenter", ["airwayEntities"]),
...mapState("MMCFlightControlCenter/hangar", [
"hangarRealTimeData",
"hangar",
"taskList",
]),
taskListAll() {
return this.taskList.timed || [];
},
},
beforeDestroy(){
this.clearAirwayEntities();
},
methods: {
...mapActions("MMCFlightControlCenter", [
"createAirwayEntities",
"clearAirwayEntities",
]),
/**
* 将任务中航线对象补充完整
*/
async getAirway(item){
if (!item.airway) {
let res = await AirLine.routeDetail({
id: item?.airwayId,
});
if (res.code === 200) {
item.airway = res.data;
}
}
},
/**
* 显示或隐藏航线
*/
async onSwitchAirway(item) {
await this.getAirway(item);
let find = this.airwayEntities.find((item1) => item1.airwayId === item.airwayId);
if (find) {
this.clearAirwayEntities({ id: find.airwayId });
} else {
let airway = JSON.parse(item.airway.content);
this.createAirwayEntities({
polyline: airway.content,
id: item.airwayId,
});
}
},
},
};
</script>
<style lang="scss" scoped>
.timed-task {
height: 100%;
display: flex;
flex-direction: column;
padding: 5px 10px 5px;
gap: 8px;
box-sizing: border-box;
.timed-task-header {
display: flex;
flex-shrink: 0;
font-family: MicrosoftYaHei-Bold;
font-size: 14px;
color: #b5e5ff;
padding: 5px 0;
letter-spacing: 0;
font-weight: 700;
background: rgba(87, 96, 138, 0.2);
border: 1px solid rgba(207, 234, 255, 0.33);
gap: 3px;
.header__column {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
&.flex2 {
flex: 2;
}
}
}
.timed-task-main {
color: #fff;
overflow-y: auto;
flex: 1;
.row {
display: flex;
color: #fff;
background: url("../../assets/images/listBg.png");
background-repeat: no-repeat;
background-size: 100% 100%;
height: 33px;
line-height: 33px;
padding-left: 4px;
margin-bottom: 16px;
gap: 3px;
.row__column {
flex: 1;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&.flex2 {
flex: 2;
}
&.ctrl {
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
.icon {
cursor: pointer;
}
}
}
}
}
}
</style>
\ No newline at end of file
// 机库状态字典
export default [
{
value: 0,
label: "空闲 ",
},
{
value: 1,
label: "正在执行出仓待命 ",
},
{
value: 2,
label: "正在执行回收入仓 ",
},
{
value: 3,
label: "正在执行充电流程",
},
{
value: 4,
label: "正在结束充电流程",
},
{
value: 5,
label: "正在执行休眠流程",
},
{
value: 6,
label: "正在执行预热流程",
},
{
value: 7,
label: "正在执行初始化",
},
{
value: 8,
label: "未初始化",
},
{
value: 9,
label: "正在执行关舱流程 ",
},
{
value: 10,
label: "正在执行回中器操作 ",
},
{
value: 99,
label: "正在执行飞行任务",
},
];
<template>
<div class="task-list">
<div class="task-list-header">
<div
class="task-list-header__item"
:class="{active: tabIndex === 0}"
@click="tabIndex = 0"
>常态飞行</div>
<div
class="task-list-header__item"
:class="{active: tabIndex === 1}"
@click="tabIndex = 1"
>定时飞行</div>
<div
class="task-list-header__item"
:class="{active: tabIndex === 2}"
@click="tabIndex = 2"
>周期飞行</div>
</div>
<div class="task-list-main">
<!-- 常态任务 -->
<NormalTask v-if="tabIndex === 0"></NormalTask>
<!-- 定时任务 -->
<TimedTask v-else-if="tabIndex === 1"></TimedTask>
<!-- 周期任务 -->
<PeriodTask v-else-if="tabIndex === 2"></PeriodTask>
</div>
</div>
</template>
<script>
import NormalTask from './components/normalTask';
import TimedTask from './components/timedTask';
export default {
name: "hangarTaskList",
components: {
NormalTask,
TimedTask
},
data() {
return {
tabIndex: 0,
};
},
mounted(){
},
methods: {
}
};
</script>
<style lang="scss" scoped>
.task-list {
width: 416px;
height: 254px;
background: rgba(9, 32, 87, 0.7);
border-radius: 10px 10px 0 0;
display: flex;
flex-direction: column;
.task-list-header {
border-radius: 10px 10px 0 0;
height: 32px;
flex-shrink: 0;
display: flex;
.task-list-header__item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-family: YouSheBiaoTiHei;
font-size: 16px;
color: #fff;
cursor: pointer;
&.active {
box-shadow: inset 0 0 10px 2px #3f9dff;
font-size: 20px;
font-family: YouSheBiaoTiHei;
color: #14faff;
line-height: 32px;
text-shadow: 0 1px 1px rgba(2, 32, 56, 0.2);
background: linear-gradient(
135deg,
#e3aa77,
#f5cda9 38%,
#f9ecd3 58%,
#fcdbb1 79%,
#edb07a
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
}
.task-list-main {
flex: 1;
overflow: hidden;
}
}
</style>
\ No newline at end of file
<template>
<div class="traffic dialog1027">
<div class="dialog-header" v-interact>
<div class="dialog-header__icon" />
<div class="dialog-header__title">交通指引</div>
<div class="dialog-header_close" style="color:#fff" @click="()=>$emit('close')">关闭</div>
</div>
<div class="dialog-content">
<div class="step active">
<div class="step-box">
<div class="step-icon">1</div>
<div class="step-desc">无人机飞往交通事故现场进行拍摄取证</div>
</div>
<div class="btn-box1027">
<div class="btn1027" @click="onTakePhoto">取证</div>
</div>
<div class="imgList mt8" v-if="xcimg">
<el-image
style="width: 110px;height: 88px"
:src="imgApi + xcimg"
fit="cover"
:preview-src-list="[imgApi + xcimg]"
></el-image>
</div>
</div>
<div class="step">
<div class="step-box">
<div class="step-icon">2</div>
<div class="step-desc">激活事故绘制软件并上传事故图</div>
</div>
<div class="btn-box1027">
<div class="btn1027" @click="onActiveApp">激活软件</div>
<el-upload
class="dib"
accept=".png, .jpg, .image, .jpeg"
:action="upLoadApi + '/upload/v2_uploads'"
:on-success="onSuccess"
multiple
name="uploadFiles"
:show-file-list="false"
>
<div class="btn1027" style="margin-right: 0">上传事故图</div>
</el-upload>
</div>
<div class="imgList mt8" v-if="image">
<el-image
style="width: 110px;height: 88px"
:src="imgApi + image"
fit="cover"
:preview-src-list="[imgApi + image]"
></el-image>
</div>
</div>
<div class="step">
<div class="step-box">
<div class="step-icon">3</div>
<div class="step-desc">生成交通事故报告</div>
</div>
<div class="btn-box1027">
<div class="btn1027" @click="onExportReport">导出报告</div>
</div>
</div>
</div>
</div>
</template>
<script>
// import Bus from "@/assets/ligature.js";
import { saveAs } from "file-saver";
import Moment from "moment";
// import API from "@/api";
export default {
props: {
uavId: {
type: String,
default: ""
}
},
data() {
return {
upLoadApi: process.env.VUE_APP_UPLOADS_URL,
imgApi: process.env.VUE_APP_IMG_URL,
imageUrl: [],
imageName: [],
baseList: [],
image: null,
xcimg: null
};
},
computed: {
taskId() {
return this.$store.state.fckernel.taskId;
},
user_info() {
return this.$store.state.user.user_info;
}
},
methods: {
//拍照
async onTakePhoto() {
console.log("uav_take_photo emit");
Bus.$emit("uav_take_photo", {
callback: async blob => {
console.log("uav_take_photo callback");
const moment = new Moment();
const dateTime = moment.format("yyyyMMddhhmmss");
saveAs(blob, `${dateTime}.jpg`);
let fd2 = new FormData();
fd2.append("uploadFile", blob, `拍照.png`);
fd2.append("taskId", this.taskId);
fd2.append("deviceHardId", this.uavId);
let res2 = await API.FCKERNEL.Upload(fd2);
console.log(res2,"urlrel");
this.xcimg = '/uploads'+ res2.fileKey;
this.$message.success("取证成功!");
this.imageUrl.push(this.xcimg);
this.imageName.push('拍照.png');
let res1 = await API.ACCIDENT.createTaskWord({
imageUrl: this.imageUrl,
imageName: this.imageName,
taskId: this.taskId,
baseList: null
});
this.imageName = [];
this.imageUrl = [];
}
});
},
//激活程序
onActiveApp() {
window.open(`TrafficAccident://`, "_blank");
},
//导出报告
onExportReport() {
let url = `${process.env.VUE_APP_BASE_URL}/hawksystemserver/task/exportTaskWord?id=${this.taskId}&FLYINGSESSIONID=${this.user_info.FLYINGSESSIONID}&mmc-identity=${this.user_info["mmc-identity"]}`;
const fileName = url;
window.open(fileName);
},
//图片上传成功
async onSuccess(res, file, fileList) {
console.log("onSuccess", res);
if (res.status == 1) {
this.imageUrl.push(res.data[0].url);
this.imageName.push(res.data[0].name);
this.baseList.push(res.data[0].encode);
this.image = res.data[0].url;
let res1 = await API.ACCIDENT.createTaskWord({
imageUrl: this.imageUrl,
imageName: this.imageName,
taskId: this.taskId,
baseList: this.baseList
});
this.imageName = [];
this.imageUrl = [];
this.baseList = [];
this.$message.success("上传成功");
} else {
this.$message.error(res.msg || "上传失败");
}
}
}
};
</script>
<style lang="scss" scoped>
.traffic {
width: 268px;
}
.dialog-content {
padding: 16px !important;
height: initial !important;
}
.step {
font-size: 14px;
font-family: SourceHanSansCN-Medium, SourceHanSansCN;
font-weight: 500;
color: #fff;
width: 100%;
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
&.active {
.step-icon {
background: #1439ff;
}
}
.step-box {
display: flex;
// justify-content: space-between;
align-items: baseline;
}
.step-icon {
width: 18px;
height: 18px;
background: #515050;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
margin-right: 8px;
flex-shrink: 0;
}
.step-desc {
height: 40px;
line-height: 20px;
}
}
.btn-box1027 {
padding: 0 !important;
justify-content: right;
}
.btn1027 {
padding: 3px 8px;
min-width: 64px;
}
</style>
\ No newline at end of file
<template>
<div class="left-bar" :class="{ collapse: listCollapse }">
<div class="left-bar-item item" @click="onClickTask">
<img class="left-bar-item__icon" src="./assets/images/task.svg" />
<div class="left-bar-item__text">任务</div>
</div>
<TaskList class="task-list" v-if="openTask"></TaskList>
<div class="left-bar-item item" @click="onClickAI">
<img class="left-bar-item__icon" src="./assets/images/ai.png" />
<div class="left-bar-item__text">智能识别</div>
</div>
<div class="ai-list" :class="{ active: openAIList }">
<div class="left-bar-item item" @click.stop="switchAI(0)">
<img class="left-bar-item__icon" src="./assets/images/faceAI2.png" />
<div class="left-bar-item__text">人脸识别</div>
</div>
<div class="left-bar-item item" @click.stop="switchAI(1)">
<img class="left-bar-item__icon" src="./assets/images/carAI2.png" />
<div class="left-bar-item__text">车辆识别</div>
</div>
<Face v-if="openFace" class="ai-dialog" :uavId="uav.hardId" @close="openFace = false" />
<Car v-if="openPlate" class="ai-dialog" :uavId="uav.hardId" @close="openPlate = false" />
<Traffic
v-if="openTraffic"
class="ai-dialog"
:uavId="uav.hardId"
@close="openTraffic = false"
/>
<Jm v-if="openModeling" :device="device" class="jm-dialog" @close="openModeling = false"></Jm>
</div>
<!-- 展示视频 -->
<div class="left-video" :class="{collapse: playerCollapse}" v-if="showPanel && listCollapse">
<div class="collapse-btn" @click="playerCollapse = !playerCollapse">
<img src="./assets/images/hs.png" />
</div>
<div class="left-video-header">
<div class="left-video-header__title" @click="listCollapse = false">机库列表</div>
<div class="nest-name">
<span class="nest-name__text">{{ hangar.name }}</span>
<span class="nest-name__text">{{ hangar.name }}</span>
</div>
</div>
<div class="left-video-nest">
<div class="player1">
<PlayerInner />
</div>
<div class="player2">
<PlayerOuter />
</div>
<div class="player3">
<PlayerUav />
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
import Car from "./components/car";
import Face from "./components/face";
import Jm from "./components/Jm";
import TaskList from "./components/taskList";
import Traffic from "./components/traffic";
import PlayerInner from "./components/player/inner";
import PlayerOuter from "./components/player/outer";
import PlayerUav from "./components/player/uav";
export default {
name: "ControlLeft",
components: {
Car,
Jm,
TaskList,
Traffic,
Face,
PlayerInner,
PlayerOuter,
PlayerUav,
},
data() {
return {
openTask: false, //打开任务
openAIList: false, //打开AI列表
openTraffic: false, //打开交通指引
openFace: false, //打开人脸识别
openPlate: false, //车牌识别
openModeling: false, //打开建模
playerCollapse: false, //收起播放器
};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["hangar", "showPanel"]),
...mapState("MMCFlightControlCenter", ["listCollapse"]),
// 收起列表按钮
listCollapse: {
get() {
return this.$store.state.MMCFlightControlCenter.listCollapse;
},
set(val) {
this.$store.commit("MMCFlightControlCenter/setState", {
key: "listCollapse",
value: val,
});
},
},
},
methods: {
onClickTask() {
this.openTask = !this.openTask;
this.openAIList = false;
this.closeAI();
},
onClickAI() {
this.openTask = false;
this.openAIList = !this.openAIList;
this.closeAI();
},
/**
* 切换AI功能
*/
switchAI(type) {
this.openFace = false;
this.openPlate = false;
this.openTraffic = false;
this.openModeling = false;
switch (type) {
case 0:
this.openFace = !this.openFace;
break;
case 1:
this.openPlate = !this.openPlate;
break;
case 2:
if (this.taskId == null) {
return this.$message.error("暂无绑定任务!");
}
this.openTraffic = !this.openTraffic;
break;
case 3:
this.openModeling = !this.openModeling;
break;
}
},
/**
* 关闭所有ai
*/
closeAI() {
this.openFace = false;
this.openPlate = false;
this.openTraffic = false;
this.openModeling = false;
},
},
};
</script>
<style scoped lang="scss">
.task-list {
position: absolute;
left: 60px;
top: -30px;
cursor: initial;
}
.left-bar {
position: absolute;
left: 470px;
top: 13%;
transition: 0.3s;
&.collapse {
left: 10px;
}
.left-bar-item {
cursor: pointer;
position: relative;
width: 48px;
height: 48px;
background: rgba(9, 32, 87, 0.7);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-bottom: 2px;
.left-bar-item__icon {
width: 20px;
height: 20px;
margin-bottom: 2px;
}
.left-bar-item__text {
font-size: 16px;
transform: scale(0.6);
white-space: nowrap;
color: #fff;
}
}
.ai-list {
width: 0;
position: absolute;
left: 65px;
top: 62px;
display: none;
gap: 4px;
&.active {
display: flex;
}
.left-bar-item {
padding-top: 3px;
}
}
}
.ai-dialog {
position: absolute;
top: 56px;
left: 0;
}
.left-video {
width: 382px;
position: absolute;
left: -10px;
top: 308px;
z-index: 10;
background: rgba(12, 34, 73, 0.7);
border-radius: 10px;
border: 1px solid rgba(26, 92, 246, 0.5);
z-index: 1;
transition: 0.3s;
&.collapse {
transform: translateX(-100%);
}
.collapse-btn {
position: absolute;
top: 50%;
right: 0;
transform: translate(100%, -50%);
img {
transform: rotate(180deg);
cursor: pointer;
}
}
.left-video-header {
width: 100%;
height: 32px;
background-image: linear-gradient(
180deg,
#9198ff 0%,
rgba(45, 81, 153, 0.22) 40%,
#05091a 100%
);
border-radius: 10px 10px 0 0;
// border: 1px solid #70daf9;
box-shadow: inset 0 0 10px 2px #3f9dff;
box-sizing: inset 0px 0px 10px 2px #3f9dff;
background: url("./assets/images/line.png") no-repeat;
background-position: 60%;
font-size: 16px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: #70daf9;
display: flex;
// justify-content: center;
align-items: center;
.left-video-header__title {
cursor: pointer;
flex-shrink: 0;
margin-left: 16px;
width: 86px;
height: 22px;
line-height: 22px;
text-align: center;
font-size: 12px;
font-weight: 500;
font-family: SourceHanSansCN-Medium, SourceHanSansCN;
background: url("./assets/images/nestlist.png");
color: #fff;
}
.nest-name {
margin-left: 30px;
margin-right: 20px;
width: 250px;
overflow: hidden;
white-space: nowrap;
.nest-name__text {
display: inline-block;
white-space: nowrap;
animation: scrollTitle 15s linear infinite;
min-width: 100%;
padding-right: 50px;
box-sizing: border-box;
font-family: YouSheBiaoTiHei;
background-image: -webkit-linear-gradient(
right,
#e3aa77,
#f5cda9,
#f9ecd3,
#fcdbb1,
#edb07a
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 20px;
font-weight: 100;
}
}
}
@keyframes scrollTitle {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
.left-video-nest::v-deep {
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
gap: 10px;
padding: 16px 14px;
position: relative;
.player1,
.player2 {
flex: 1;
height: 96px;
}
.player3 {
width: 100%;
height: 240;
}
}
}
</style>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="19px" viewBox="0 0 22 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组</title>
<g id="鹰视2023" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="机库应用-任务" transform="translate(-1802.000000, -253.000000)" fill-rule="nonzero">
<g id="编组-27备份-2" transform="translate(1786.000000, 242.000000)">
<g id="编组" transform="translate(16.000000, 11.000000)">
<path d="M20.8209003,4.22199999 L20.8209003,15.815 C20.8209003,16.661 20.4889019,17.472 19.9009047,18.069 C19.3172481,18.6643442 18.5186337,19 17.6849153,19 L4.31497933,19 C3.48140658,19 2.68281312,18.6649768 2.09898995,18.07 C1.50874572,17.4678877 1.17839559,16.6581669 1.17899354,15.815 L1.17899354,4.22199999 L20.8209003,4.22199999 Z M6.79349433,5.5 C6.60949565,5.94622045 6.34828323,6.34415584 6.00985707,6.69380619 C6.08542796,6.94022644 6.14292755,7.17166167 6.18235584,7.38811189 C6.2135699,7.35814186 6.24478397,7.32733933 6.27599803,7.2957043 L6.27599803,8.12237762 C6.27599803,8.94155844 6.18399869,9.56426906 6,9.99050949 C6.14785609,10.1270396 6.30228356,10.2968698 6.46328241,10.5 C6.61278134,10.1719947 6.71217348,9.72577423 6.76145885,9.16133866 L7.05470675,9.16133866 L7.05470675,10.1053946 L7.43173977,10.1053946 L7.49334648,10.4200799 C7.65598817,10.4200799 7.8017907,10.4096737 7.93075407,10.3888611 C8.05971743,10.3680486 8.15705602,10.3039461 8.22276984,10.1965534 C8.28848365,10.0891608 8.32134056,9.93889444 8.32134056,9.74575425 L8.32134056,8.67932068 C8.46591096,8.74259074 8.62526696,8.82334332 8.79940858,8.92157842 C8.87826516,8.78671329 8.94890751,8.63686314 9.01133563,8.47202797 L9.47461804,8.47202797 L9.47461804,9.02147852 L8.45933958,9.02147852 L8.45933958,9.57092907 L9.47461804,9.57092907 L9.47461804,10.4400599 L10.0956136,10.4400599 L10.0956136,9.57092907 L10.898965,9.57092907 L10.898965,9.02147852 L10.0956136,9.02147852 L10.0956136,8.47202797 L10.7806801,8.47202797 L10.7806801,7.92257742 L10.0956136,7.92257742 L10.0956136,7.502997 L9.47461804,7.502997 L9.47461804,7.92257742 L9.17151306,7.92257742 C9.1945129,7.82434232 9.21504846,7.72277722 9.23311976,7.61788212 L8.69590931,7.54295704 C8.64169542,7.89260739 8.51683917,8.22893773 8.32134056,8.55194805 L8.32134056,6.7987013 L7.90734352,6.7987013 L8.15869887,6.34415584 L8.15869887,5.87462537 L7.26416954,5.87462537 C7.31016921,5.77805528 7.35370462,5.67982018 7.39477575,5.57992008 Z M15.9753573,8.62187812 L11.0763923,8.62187812 L11.0763923,9.15634366 L13.2942336,9.15634366 L13.2942336,9.64585415 C13.2942336,9.78904429 13.2318055,9.86063936 13.1069492,9.86063936 C12.9196649,9.86063936 12.7282734,9.85397935 12.5327748,9.84065934 C12.636274,9.78404928 12.7422376,9.72410922 12.8506654,9.66083916 L12.4760966,9.18131868 C11.9799573,9.47102897 11.5084607,9.69413919 11.0616067,9.85064935 C11.1700345,10.0038295 11.2866765,10.1869797 11.4115328,10.4000999 C11.7401019,10.2602398 12.1072778,10.0779221 12.5130606,9.85314685 C12.5508461,10.004662 12.5927386,10.2002997 12.6387383,10.4400599 C13.1184492,10.4400599 13.4186792,10.4292374 13.5394283,10.4075924 C13.6601774,10.3859474 13.760391,10.3276723 13.840069,10.2327672 C13.919747,10.1378621 13.959586,9.99050949 13.959586,9.79070929 L13.959586,9.15634366 L15.9753573,9.15634366 L15.9753573,8.62187812 Z M14.4721538,9.22127872 L14.1813701,9.71578422 C14.6775094,9.91558442 15.1621488,10.1436896 15.6352883,10.4000999 L15.9408576,9.86563437 C15.4217184,9.60922411 14.9321505,9.39443889 14.4721538,9.22127872 Z M7.80877279,9.16133866 L7.80877279,9.58591409 C7.80877279,9.69247419 7.79152292,9.76406926 7.75702316,9.8006993 C7.72252341,9.83732934 7.6461311,9.85314685 7.52784623,9.84815185 L7.52784623,9.16133866 L7.80877279,9.16133866 Z M7.80877279,8.22227772 L7.80877279,8.69180819 L7.52784623,8.69180819 L7.52784623,8.22227772 L7.80877279,8.22227772 Z M7.05470675,8.22227772 L7.05470675,8.69180819 L6.79103006,8.69180819 C6.7959586,8.4020979 6.79842287,8.24558775 6.79842287,8.22227772 L7.05470675,8.22227772 Z M15.6648595,7.84765235 L11.4657467,7.84765235 L11.4657467,8.35214785 L15.6648595,8.35214785 L15.6648595,7.84765235 Z M14.8910793,5.51998002 L14.299655,5.51998002 L14.299655,5.98951049 L13.5800887,5.98951049 L13.5800887,6.48901099 L14.1419418,6.48901099 C13.9053721,6.78871129 13.5965172,7.06343656 13.215377,7.31318681 C13.3698045,7.47635698 13.5012321,7.62454212 13.6096599,7.75774226 C13.8708724,7.52464202 14.1008707,7.25824176 14.299655,6.95854146 L14.299655,7.70779221 L14.8910793,7.70779221 L14.8910793,6.95104895 C15.1079349,7.24741925 15.3576474,7.501332 15.6402169,7.71278721 C15.7355019,7.56959707 15.8554296,7.40809191 16,7.22827173 C15.6254312,7.04179154 15.304255,6.7953713 15.0364712,6.48901099 L15.8077871,6.48901099 L15.8077871,5.98951049 L14.8910793,5.98951049 L14.8910793,5.51998002 Z M7.80877279,7.27822178 L7.80877279,7.75274725 L7.52784623,7.75274725 L7.52784623,7.27822178 L7.80877279,7.27822178 Z M7.05470675,7.27822178 L7.05470675,7.75274725 L6.79842287,7.75274725 L6.79842287,7.27822178 L7.05470675,7.27822178 Z M12.6436668,5.51998002 L12.0522425,5.51998002 L12.0522425,5.98951049 L11.1996057,5.98951049 L11.1996057,6.48901099 L11.9142435,6.48901099 C11.6678167,6.78704629 11.3737473,7.04012654 11.0320355,7.24825175 C11.1601774,7.3981019 11.281748,7.55461205 11.3967472,7.71778222 C11.6366026,7.52797203 11.855101,7.29237429 12.0522425,7.01098901 L12.0522425,7.70779221 L12.6436668,7.70779221 L12.6436668,7.05344655 C12.8095942,7.15667666 12.9673074,7.26490176 13.1168063,7.37812188 L13.4371612,6.94355644 C13.2235913,6.81035631 13.0231641,6.69214119 12.8358797,6.58891109 L12.6436668,6.84865135 L12.6436668,6.48901099 L13.3829473,6.48901099 L13.3829473,5.98951049 L12.6436668,5.98951049 L12.6436668,5.51998002 Z M10.7708231,5.70979021 L8.46426811,5.70979021 L8.46426811,6.25424575 L8.92262198,6.25424575 C8.89633645,6.56060606 8.69755216,6.80869131 8.3262691,6.9985015 C8.49383933,7.17499167 8.64169542,7.35314685 8.76983736,7.53296703 C9.09840644,7.32650683 9.3177263,7.09174159 9.42779694,6.82867133 C9.48365369,7.00516151 9.52718909,7.1966367 9.55840315,7.4030969 C9.98061442,7.4030969 10.2520946,7.37187812 10.3728438,7.30944056 C10.4935929,7.247003 10.5880565,7.14127539 10.6562346,6.99225774 C10.7244127,6.84324009 10.7626088,6.41575092 10.7708231,5.70979021 Z M10.1054707,6.25424575 C10.1054707,6.51065601 10.0853458,6.66883117 10.0450961,6.72877123 C10.0048464,6.78871129 9.92475768,6.81868132 9.80482997,6.81868132 C9.72433054,6.81868132 9.60193856,6.81451881 9.43765402,6.80619381 C9.4984393,6.64968365 9.53868901,6.46570097 9.55840315,6.25424575 Z M7.52291769,6.36413586 L7.28388369,6.7987013 L6.70478068,6.7987013 C6.81156563,6.65884116 6.91013636,6.51398601 7.00049285,6.36413586 L7.52291769,6.36413586 Z M10.9359476,12.7880004 C10.8032261,12.8023389 10.6743503,12.841341 10.5559494,12.9030004 C10.3359505,13.0230004 10.1539514,13.2070004 10.0329519,13.4310004 C10.0059521,13.4810004 9.97695222,13.4910003 9.92995245,13.4910004 C9.24995571,13.4890004 8.57195896,13.4890004 7.89296221,13.4890004 C7.84596244,13.4890004 7.77196278,13.5070004 7.75596285,13.4840004 C7.73296297,13.4470003 7.7449629,13.3840004 7.74196292,13.3330004 L7.74196292,12.9320004 L6.75996763,12.9320004 L6.75996763,13.4850004 C6.74996767,13.4850004 6.73996773,13.4870004 6.72996777,13.4890004 C6.60300689,13.4903407 6.48917776,13.5675401 6.44096916,13.6850004 C6.39696937,13.7870004 6.40596932,13.9630004 6.51396881,14.0380004 C6.58118676,14.0853107 6.65542661,14.1217559 6.73396775,14.1460004 C7.13596583,14.2690004 7.54096389,14.3860004 7.94496194,14.5040004 L8.60095882,14.6960004 C8.72095825,14.7320003 8.84095767,14.7700004 8.96695706,14.8100004 L8.69295836,15.0260004 L8.2679604,15.3660004 C8.11896111,15.4860004 7.97596179,15.6110004 7.82196253,15.7230004 C7.69966205,15.8058493 7.6280364,15.9453315 7.63196345,16.0930004 L7.63196345,17.0940003 C7.63196345,17.1380003 7.63896341,17.1840004 7.64696338,17.2260003 C7.70096313,17.5160004 8.02296158,17.6330003 8.25396047,17.4930004 C8.38214871,17.4147848 8.45762912,17.2730189 8.45095952,17.1230004 C8.45295951,16.8660004 8.45295951,16.6130004 8.45095952,16.3550003 C8.45095952,16.3070003 8.46195947,16.2770004 8.50495927,16.2520004 L9.24695571,15.8370004 L9.62995387,15.6200004 C10.024952,16.0160004 10.5039497,16.2040003 11.0519471,16.2020004 C11.5819445,16.2140004 12.0919421,16.0020004 12.4639403,15.6170004 L12.7509389,15.7830003 L13.6019349,16.2590004 C13.6399347,16.2810004 13.6579346,16.3070004 13.6569346,16.3550003 C13.6539346,16.6280003 13.6549346,16.9020004 13.6549346,17.1770004 C13.6549346,17.2010004 13.6589346,17.2250004 13.6649345,17.2490004 C13.7049344,17.4340004 13.9029334,17.5540004 14.0649326,17.552025 C14.2785775,17.537611 14.4477718,17.3658393 14.4589307,17.1520004 C14.4589307,16.7850004 14.4619307,16.4160004 14.4589307,16.0480004 C14.4582252,15.929436 14.4014898,15.8181896 14.3059315,15.7480004 C14.1689322,15.6440004 14.0359328,15.5360004 13.9029334,15.4280004 L13.3209362,14.9650004 C13.2589365,14.9150004 13.2009368,14.8630004 13.1289371,14.8030004 L13.1839369,14.7910004 C13.1903016,14.7894556 13.1966361,14.7877886 13.2029368,14.7860004 L14.2289319,14.4820004 C14.6299299,14.3620003 15.026928,14.2360004 15.4299261,14.1280004 C15.6319251,14.0720004 15.7489246,13.8330004 15.6339251,13.6460004 C15.5639254,13.5380004 15.4639259,13.4900004 15.3289266,13.4860004 L15.3289266,12.9320004 L14.3469313,12.9320004 L14.3469313,13.4890004 L12.1509418,13.4890004 C12.1259419,13.4890004 12.0909421,13.4790004 12.0759422,13.4610004 C12.0169424,13.3810004 11.9709427,13.2890004 11.906943,13.2130004 C11.790661,13.0662517 11.6407767,12.9496363 11.4699451,12.8730004 C11.3032468,12.7958008 11.1182963,12.7667074 10.9359476,12.7890004 L10.9359476,12.7880004 L10.9359476,12.7880004 Z M11.055947,13.968 C11.4211014,13.9740293 11.7128437,14.2738149 11.7089439,14.639 C11.697944,15.004 11.4309453,15.311 11.060947,15.302 C10.6829488,15.287 10.4219501,15.052 10.4149501,14.639 C10.4082971,14.4622529 10.4731888,14.2902889 10.5949493,14.162 C10.714496,14.036098 10.8813552,13.9660893 11.0549471,13.969 L11.056947,13.969 L11.055947,13.968 Z M14.8659288,12.219 L14.7379294,12.219 C14.5559303,12.315 14.4679307,12.481 14.3709312,12.683 C14.2669317,12.569 14.1659322,12.455 14.0609327,12.343 C14.0031482,12.2711559 13.9150973,12.2304599 13.8229338,12.233 C13.8159338,12.233 13.8089339,12.223 13.7999339,12.22 L12.5399399,12.22 C12.53094,12.225 12.52394,12.232 12.5159401,12.232 C12.3899053,12.2336457 12.277683,12.312162 12.2329414,12.43 C12.1895343,12.529416 12.1977591,12.6438159 12.2549413,12.736 C12.3113298,12.8270466 12.4098763,12.8834666 12.5169401,12.886 C14.0419328,12.884 15.5669254,12.884 17.0929181,12.886 C17.2309175,12.886 17.3529169,12.796 17.3989167,12.664 C17.4502137,12.5260384 17.3981148,12.3709914 17.2739173,12.292 C17.2139176,12.256 17.1439179,12.244 17.0779182,12.219 L15.8169243,12.219 C15.7639245,12.233 15.7079248,12.241 15.664925,12.267 C15.6014415,12.3095853 15.5421724,12.3581393 15.4879258,12.412 C15.3999262,12.496 15.3149266,12.582 15.2229271,12.676 C15.1359275,12.491 15.0469279,12.317 14.8659288,12.219 L14.8659288,12.219 Z M6.27796994,12.219 L5.01797597,12.219 C5.00897602,12.225 5.00297604,12.232 4.99497607,12.232 C4.88245973,12.2374354 4.78090303,12.3010981 4.72697737,12.4 C4.66540867,12.4954646 4.66346955,12.6176295 4.72197738,12.715 C4.77897711,12.818 4.86397671,12.886 4.99497607,12.886 L6.97096662,12.885 L9.57095415,12.885 C9.68295054,12.8893287 9.78766506,12.8296024 9.84095287,12.731 C9.91095253,12.616 9.90395257,12.501 9.83895287,12.385 C9.78395314,12.289 9.69195357,12.253 9.59495404,12.219 L8.29196029,12.219 C8.20196072,12.244 8.10996115,12.259 8.0399615,12.333 C7.95596191,12.427 7.86096237,12.514 7.77196278,12.604 C7.75096288,12.626 7.73396297,12.652 7.70896308,12.682 C7.63096346,12.484 7.5389639,12.316 7.35596478,12.22 L7.22796537,12.22 C7.06261546,12.3293183 6.9392291,12.491484 6.87796705,12.68 C6.85908647,12.6629103 6.84074429,12.645235 6.82296732,12.627 C6.70296788,12.513 6.58696845,12.392 6.45996905,12.285 L6.40596932,12.256 L6.27796994,12.22 L6.27796994,12.219 L6.27796994,12.219 Z M8.64295862,0 L8.64295862,2.448 L1.17899436,2.448 C0.527997471,2.448 0,1.9 0,1.22399999 C0,0.547999997 0.527997471,0 1.17899436,0 L8.64295862,0 Z M20.8199003,0 C21.4708972,0 21.9999483,0.547999997 21.9999483,1.22399999 C22.0028946,1.54499999 21.8798952,1.85499999 21.6598963,2.08399999 C21.440533,2.31318432 21.1381259,2.44438421 20.8209003,2.448 L13.356936,2.448 L13.356936,0 L20.8209003,0 L20.8199003,0 Z" id="形状" fill="#FFFFFF"></path>
<path d="M11.056947,13.969 C11.421315,13.9755706 11.7122962,14.2745865 11.7089439,14.639 C11.697944,15.004 11.4309453,15.311 11.060947,15.302 C10.6829488,15.287 10.4219501,15.052 10.4149501,14.639 C10.4082971,14.4622529 10.4731888,14.2902889 10.5949493,14.162 C10.714496,14.036098 10.8813552,13.9660893 11.0549471,13.969 L11.056947,13.969 L11.056947,13.969 Z M8.64295862,0 L8.64295862,2.448 L1.17899436,2.448 C0.527997471,2.448 0,1.9 0,1.22399999 C0,0.547999997 0.527997471,0 1.17899436,0 L8.64295862,0 Z M20.8209003,0 C21.4708972,0 21.9999483,0.547999997 21.9999483,1.22399999 C22.0028946,1.54499999 21.8798952,1.85499999 21.6598963,2.08399999 C21.440533,2.31318432 21.1381259,2.44438421 20.8209003,2.448 L13.356936,2.448 L13.356936,0 L20.8209003,0 L20.8209003,0 Z" id="形状" fill="#FFBD36"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="164px" height="31px" viewBox="0 0 164 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>路径 3</title>
<defs>
<path d="M229.988757,97.4375536 L211.442082,124.702406 C211.131451,125.159054 211.249822,125.781055 211.706469,126.091686 C211.872338,126.204517 212.068308,126.264852 212.268916,126.264852 L352.929687,126.264852 C353.2531,126.264852 353.556546,126.108443 353.744181,125.845025 L373.165219,98.5801724 C373.485639,98.1303399 373.38073,97.5059266 372.930898,97.1855063 C372.761502,97.0648444 372.558701,97 372.350725,97 L230.815591,97 C230.484544,97 230.174953,97.1638333 229.988757,97.4375536 Z" id="path-1"></path>
<filter x="-3.4%" y="-22.2%" width="106.8%" height="144.4%" filterUnits="objectBoundingBox" id="filter-2">
<feMorphology radius="2" operator="erode" in="SourceAlpha" result="shadowSpreadInner1"></feMorphology>
<feGaussianBlur stdDeviation="5" in="shadowSpreadInner1" result="shadowBlurInner1"></feGaussianBlur>
<feOffset dx="0" dy="0" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0.482352941 0 0 0 0 1 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
</filter>
</defs>
<g id="鹰视子页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="路径-3" transform="translate(-210.000000, -96.000000)">
<use fill-opacity="0.498497596" fill="#000000" fill-rule="evenodd" xlink:href="#path-1"></use>
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use stroke="#00B6FF" stroke-width="1" xlink:href="#path-1"></use>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="344px" height="38px" viewBox="0 0 344 38" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>路径 2备份</title>
<defs>
<radialGradient cx="50%" cy="0%" fx="50%" fy="0%" r="548.626887%" gradientTransform="translate(0.500000,0.000000),scale(0.096971,1.000000),rotate(82.515736),scale(1.000000,1.237046),translate(-0.500000,-0.000000)" id="radialGradient-1">
<stop stop-color="#00B6FF" offset="0%"></stop>
<stop stop-color="#00B6FF" offset="27.0225962%"></stop>
<stop stop-color="#00CAFF" stop-opacity="0" offset="73.0910044%"></stop>
<stop stop-color="#00D8FF" stop-opacity="0" offset="100%"></stop>
<stop stop-color="#00DCFF" stop-opacity="0" offset="100%"></stop>
</radialGradient>
</defs>
<g id="鹰视子页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="机库应用3" transform="translate(-18.000000, -89.000000)" stroke="url(#radialGradient-1)" stroke-width="2">
<polyline id="路径-2备份" points="6.83479976 92 6.83479976 125.264852 199.569948 125.264852 224.8348 90 370.5 90"></polyline>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<div class="cpt-observe-nest-power">
<!-- <div class="hd pr">
<div class="f20 pa left60 fw700 top7 cf">{{active? '运行监测':'运行状态'}}</div>
<div class="pa right0 h44 lh44 cp tc cf yxztDefault" @click="active=!active">
{{!active? '运行监测':'运行状态'}}</div>
</div>-->
<div class="titleBox">
<div class="tab-active">{{ !active ? "运行监测" : "运行状态" }}</div>
<div class="tab-no-active" @click="active = !active">{{ active ? "运行监测" : "运行状态" }}</div>
</div>
<div class="power-list wih100 mt14" v-if="active">
<template v-for="item in list">
<el-tooltip :content="item.status_map[item.status].title" placement="top" :key="item.id">
<div
class="power-item"
:style="
item.status_map[item.status] &&
item.status_map[item.status].style
"
>
<div class="icon-box">
<div :class="item.status_map[item.status].icon"></div>
</div>
<div class="text-box">{{ item.label }}</div>
</div>
</el-tooltip>
</template>
</div>
<div class="power-list f14 power-list--monitor" v-else>
<div class="list">
<div class="tc w50 h50">
<SymbolIcon :icon="rtkType[rtkPower].data" />
<div class="cf">{{ rtkType[rtkPower].title }}</div>
</div>
<div class="tc w50 h50">
<SymbolIcon :icon="uavMsg.code == 10000 ? 'zhunfei2' : 'jinfei'" />
<div class="cf">{{ uavMsg.code == 10000 ? "准飞" : "禁飞" }}</div>
</div>
<div class="tc w50 h50">
<SymbolIcon :icon="ycType[cover].data" />
<div class="cf">{{ ycType[cover].title }}</div>
</div>
<div class="tc w50 h50">
<SymbolIcon :icon="jjType[folder].data" />
<div class="cf">{{ jjType[folder].title }}</div>
</div>
<div class="tc w50 h50">
<SymbolIcon :icon="sjType[lifts].data" />
<div class="cf">{{ sjType[lifts].title }}</div>
</div>
</div>
<div class="temp">
<div class="power-list-data-item">
<div class>
<span>内温</span>
<span class="cfc power-list-data-num">{{ weatherStation.temp }}</span>
</div>
<div class="power-list-data-progress">
<el-progress
class
:show-text="false"
:text-inside="true"
:percentage="weatherStation.temp"
></el-progress>
</div>
</div>
<div class="power-list-data-item">
<div class>
<span>外温</span>
<span
style="color: #ffbd36"
class="power-list-data-num"
>{{ weatherStation.weatherTemp }}</span>
</div>
<div class="power-list-data-progress">
<el-progress
class
:show-text="false"
:text-inside="true"
color="#FFBD36"
:percentage="weatherStation ? weatherStation.weatherTemp : 0"
></el-progress>
</div>
</div>
<div class="power-list-data-item">
<div class>
<span>内湿度</span>
<span style="color: #ffbd36" class="power-list-data-num">
{{
weatherStation.humidity
}}
</span>
</div>
<div class="power-list-data-progress">
<el-progress
class="w120 h12"
color="#FFBD36"
:show-text="false"
:text-inside="true"
:percentage="weatherStation.humidity"
></el-progress>
</div>
</div>
<div class="power-list-data-item">
<div class>
<span>外湿度</span>
<span class="cfc power-list-data-num">
{{
weatherStation.weatherHumid
}}
</span>
</div>
<div class="power-list-data-progress">
<el-progress
class="w120 h12"
:show-text="false"
:text-inside="true"
:percentage="weatherStation ? weatherStation.weatherHumid : 0"
></el-progress>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
// 机库实时数据
hangarData: {
type: Object,
default: () => ({}),
},
// 2066气象站数据
weatherStation: {
type: Object,
default: () => ({}),
},
// 无人机消息type: 270的数据
uavMsg: {
type: Object,
default: () => ({}),
},
},
components: {},
data() {
return {
active: true,
rtkType: [
{ data: "jizhanweiqiyong", title: "未知" },
{ data: "jizhanqiyong", title: "启用" },
{ data: "jizhanweiqiyong", title: "关闭" },
],
ycType: [
{ data: "guanbi1", title: "未知" },
{ data: "guanbi1", title: "关闭" },
{ data: "dakai", title: "打开" },
{ data: "guanbi1", title: "正在关闭" },
{ data: "dakai", title: "正在打开" },
],
jjType: [
{ data: "fangsong1", title: "未知" },
{ data: "fangsong1", title: "加紧" },
{ data: "yundong", title: "放松" },
{ data: "fangsong1", title: "夹紧过程" },
{ data: "yundong", title: "放松过程" },
],
// lifts
sjType: [
{ data: "weizhi", title: "未知" },
{ data: "xiajiang", title: "降下" },
{ data: "shangsheng", title: "升起" },
{ data: "xiajiang", title: "降下过程" },
{ data: "shangsheng", title: "升起过程" },
],
list: [
{
id: 1,
label: "机库",
prop: "hangarPower",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 2,
label: "射频",
prop: "rF_Power",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 3,
label: "舱外摄像头",
prop: "outCameraPower",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 4,
label: "舱内摄像头",
prop: "inCameraPower",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 5,
label: "RTK电源",
prop: "rTK_Power",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 6,
label: "UAV充电器",
prop: "chargerPower",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 7,
label: "内部灯",
prop: "inLamp",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
{
id: 8,
label: "外部灯",
prop: "outLamp",
status: 0,
status_map: {
0: {
icon: "iconfont icon-weizhi1",
style: { color: "#eee", opacity: 0.3 },
title: "未知",
},
1: {
icon: "iconfont icon-dianyuan1",
style: { color: "#43DEFF", opacity: 1 },
title: "打开",
},
2: {
icon: "iconfont icon-dianyuan1",
style: { color: "#FFBD36" },
title: "关闭",
},
},
},
],
};
},
mounted() {},
computed: {
rtkPower() {
let { hangarData } = this;
if (hangarData.rtkPower) {
return hangarData.rtkPower;
}
return 0;
},
folder() {
let { hangarData } = this;
if (hangarData.folder) {
return hangarData.folder;
}
return 0;
},
lifts() {
let { hangarData } = this;
if (hangarData.lifts) {
return hangarData.lifts;
}
return 0;
},
cover() {
let { hangarData } = this;
if (hangarData.cover) {
return hangarData.cover;
}
return 0;
},
env_temp() {
let { weatherStation } = this;
return weatherStation ? weatherStation.temp : 0;
},
// 内温度
hangarTemp() {
let { hangarData } = this;
if (hangarData.temperature) {
return hangarData.temperature;
}
return 0;
},
hangarHumid() {
let { hangarData } = this;
if (hangarData.hangarHumid) {
return hangarData.hangarHumid;
}
return 0;
},
weather_temp() {
let { weatherStation } = this;
return weatherStation ? weatherStation.weatherTemp : 0;
},
env_humid() {
let { weatherStation } = this;
return weatherStation ? weatherStation.humidity : 0;
},
},
watch: {
hangarData: {
handler(value) {
this.list.forEach((item) => {
let prop = item.prop;
if (prop) {
item.status = value ? value[prop] || 0 : 0;
}
});
},
deep: true,
immediate: true,
},
},
};
</script>
<style lang="scss" scoped>
.cpt-observe-nest-power {
position: absolute;
width: 382px;
height: 203px;
left: -400px;
top: 20px;
background: rgba(12, 34, 73, 1);
border-radius: 10px;
border: 1px solid raba(26, 92, 246, 0.5);
backdrop-filter: blur(1px);
// border: 1px solid #70daf9;
z-index: 2;
.titleBox {
// background: rgba(4, 18, 50, 0.5);
position: relative;
color: #fff;
font-family: MicrosoftYaHei-Bold;
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
text-align: center;
font-weight: 700;
height: 40px;
background-image: url("./assets/images/power_tabs.svg");
background-size: 100% 90%;
background-position: 0px 5px;
background-repeat: no-repeat;
.tab-active {
color: #fff;
font-size: 19px;
position: absolute;
left: 71px;
top: 50%;
transform: translate(0, -50%);
}
.tab-no-active {
color: rgb(188, 233, 254);
font-size: 17px;
font-weight: 100;
position: absolute;
right: 8px;
bottom: 0;
width: 164px;
height: 31px;
display: flex;
justify-content: center;
align-items: center;
background-image: url("./assets/images/power_tab.svg");
background-size: 100% 100%;
cursor: pointer;
}
}
.fz {
transform: rotate(180deg);
}
.hd {
background: center url("./assets/images/yxjcbg.png");
background-size: contain;
height: 44px;
.yxztDefault {
width: 200px;
background: center url("./assets/images/yxztDefault.png");
background-repeat: 100% 100%;
background-repeat: no-repeat;
background-position-x: 30px;
}
.yxztDefault:hover {
width: 200px;
background: center url("./assets/images/yxztSelect.png");
background-repeat: 100% 100%;
background-repeat: no-repeat;
background-position-x: 30px;
}
.yxztDefault:hover {
width: 200px;
background: center url("./assets/images/yxztSelect.png");
background-repeat: 100% 100%;
background-repeat: no-repeat;
background-position-x: 30px;
}
.title-box {
font-family: MicrosoftYaHei-Bold;
font-size: 18px;
color: #43deff;
letter-spacing: 0;
font-weight: 700;
line-height: 44px;
margin-left: 20px;
}
}
.power-list {
display: flex;
flex-wrap: wrap;
margin-top: 14px;
&.power-list--monitor {
padding: 0 18px 0;
}
.list {
width: 100%;
display: flex;
justify-content: space-between;
color: #fff;
font-size: 14px;
}
.power-item {
color: #19e210;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 60px;
width: 25%;
.icon-box {
width: 22px;
height: 22px;
overflow: hidden;
margin-bottom: 4px;
.iconfont {
font-size: 22px;
}
}
.text-box {
font-family: MicrosoftYaHei;
font-size: 14px;
font-weight: 400;
}
}
.temp {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
row-gap: 20px;
margin-top: 10px;
.power-list-data-item {
width: 124px;
display: flex;
flex-direction: column;
color: #fff;
font-size: 12px;
.power-list-data-num {
margin-left: 6px;
}
}
.power-list-data-progress {
margin-top: 4px;
width: 124px;
}
}
}
}
</style>
<template>
<div
class="cpt-nest-logger"
:class="{ chang: dakai == true, }"
>
<div class="title-box" v-interact>
<div class="title">
<img src="../../../../../../../../assets/images/mount_head.png" />
<div class="font">运行监控日志</div>
<div effect="dark" class="status">
{{ getprocessStatus(hangarData.processStatus) }}
</div>
</div>
<div style="display: flex">
<div class="icon-box" @click="$emit('clearMsg')">
<span class="iconfont icon-qingchushuju"></span>
<!-- <span class="icon-text pr20">清除数据</span> -->
</div>
<!-- <div class="icon-box" @click="$emit('step')">
<span class="icon-text">运行流程</span>
</div> -->
<div class="close" @click="$emit('exit')">关闭</div>
</div>
</div>
<div class="liucBox">
<div class="default" :class="folderFn(folder)">
<span class="w10 mr4 h10 radius"></span>固定无人机
</div>
<div class="default" :class="coverFn(cover)">
<span class="line" :class="!productType ? 'w62' : 'w22'"></span>
<span class="w10 mr4 h10 radius"></span>打开舱盖
</div>
<div class="default" :class="liftsFn(lifts)" v-if="productType">
<span class="w22 line"></span>
<span class="w10 mr4 h10 radius"></span>升起平台
</div>
<div class="default" :class="folderFn3(folder)">
<span class="line" :class="!productType ? 'w62' : 'w22'"></span>
<span class="w10 mr4 h10 radius"></span>释放无人机
</div>
<div class="default" :class="rktFn(uavMsg)">
<span class="line" :class="!productType ? 'w62' : 'w22'"></span>
<span class="w10 mr4 h10 radius"></span>无人机RTK定位
</div>
</div>
<div class="ctx-box">
<div class="head mt7">
<div class="text">消息等级</div>
<div class="text con">消息内容</div>
<div class="time">时间</div>
</div>
<div class="list-box mt7">
<div class="item-box" v-for="(item, index) in _hangarMsgList" :key="index">
<div class="text-box">
<div
class="type-box"
:class="{ emerg: item.type === 1, ordinary: item.type === 8 }"
>
<span class="type" v-if="item.type === 1">紧急</span>
<span class="type" v-else-if="item.type === 8">普通</span>
<span class="type" v-else>未知</span>
</div>
<div class="text">{{ item.text }}</div>
<div class="time">{{ item.time }}</div>
</div>
</div>
</div>
</div>
<div class="jiantou" @click="jiantou" :class="{ zhaun: dakai == true }">
<img :src="jian" style="width: 100%; height: 100%" alt="" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
jian: require("./assets/images/jiantou.png"),
dakai: false, //变长变短
productType: false,
};
},
props: {
// 无人机消息type: 270的数据
uavMsg: {
type: Object,
default: () => ({}),
},
// 机库type为2063的日志消息
hangarMsgList: {
type: Array,
default: () => [],
},
// 机库mqtt实时数据
hangarData: {
type: Object,
default: () => ({}),
},
},
watch: {
hangarData: function (val) {
if (val?.productType == 302 || val?.productType == 305) {
this.productType = false; //翻盖
} else {
this.productType = true; //升降
}
},
},
computed: {
// 消息反序显示
_hangarMsgList(){
return this.hangarMsgList.reverse();
},
rtkPower() {
let { hangarData } = this;
if (hangarData.rtkPower) {
return hangarData.rtkPower;
}
return 0;
},
folder() {
let { hangarData } = this;
if (hangarData.folder) {
return hangarData.folder;
}
return 0;
},
lifts() {
let { hangarData } = this;
if (hangarData.lifts) {
return hangarData.lifts;
}
return 0;
},
cover() {
let { hangarData } = this;
if (hangarData.cover) {
return hangarData.cover;
}
return 0;
},
},
methods: {
getprocessStatus(val) {
let title = "";
if (val == 0) {
title = "待机中";
} else if (val == 1) {
title = "正在执行出仓待命";
} else if (val == 2) {
title = "正在执行回收入仓";
} else if (val == 3) {
title = "正在执行充电流程";
} else if (val == 4) {
title = "正在结束充电流程";
} else if (val == 5) {
title = "正在执行休眠流程";
} else if (val == 6) {
title = "正在执行预热流程";
} else if (val == 7) {
title = "正在执行飞行检查流程";
} else if (val == 8) {
title = "正在执行电池检查流程";
} else if (val == 9) {
title = "正在执行关仓流程";
} else {
title = "暂无";
}
return title;
},
jiantou() {
if (this.dakai == false) {
this.dakai = true;
} else {
this.dakai = false;
}
},
handle_clear() {
this.list = [];
},
folderFn(folder) {
// 已降落 夹紧中
if (this.lifts == 1 && folder == 3) {
return "current";
} else if (this.lifts == 1 && folder == 2) {
return "default";
} else {
return "end";
}
},
// 是否出仓
coverFn(cover) {
switch (cover) {
case 0:
return "default";
break;
case 4:
return "current";
break;
case 2:
return "end";
break;
default:
return "default";
break;
}
},
// 升降
liftsFn(lifts) {
switch (lifts) {
case 0:
return "default";
break;
case 4:
return "current";
break;
case 2:
return "end";
break;
default:
return "default";
break;
}
},
// 释放无人机
folderFn3(folder) {
if (this.productType) {
if (this.lifts == 2 && folder == 4) {
return "current";
} else if (this.lifts == 2 && folder == 2) {
return "end";
} else {
return "default";
}
} else {
if (folder == 4) {
return "current";
} else if (folder == 2) {
return "end";
} else {
return "default";
}
}
},
rktFn(data) {
if (this.productType) {
if (data.code == 1000 && this.folder == 2 && this.lifts == 2) {
return "end";
} else if (this.folder == 2 && this.lifts == 2) {
if (data.code != 1000) {
return "current";
} else {
return "end";
}
} else {
return "default";
}
} else {
if (data.code == 1000 && this.folder == 2) {
return "end";
} else if (this.folder == 2) {
if (data.code != 1000) {
return "current";
} else {
return "end";
}
} else {
return "default";
}
}
},
},
};
</script>
<style lang="scss" scoped>
.yidong {
left: 52% !important;
}
.cpt-nest-logger {
position: fixed;
width: 670px;
height: 143px;
top: 21%;
left: 32%;
display: flex;
// overflow-y: auto;
background: rgba(9, 32, 87, 0.7);
// border: 1px solid #70daf9;
border-radius: 10px 10px 0 0;
flex-direction: column;
.liucBox {
display: flex;
justify-content: center;
gap: 5px;
margin-top: 14px;
.default {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #afdcff;
letter-spacing: 1px;
font-weight: 400;
display: flex;
align-items: center;
.radius {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 50%;
border: 1px solid #afdcff;
margin-left: 5px;
margin-right: 3px;
margin-top: 2px;
}
.line {
width: 62px;
height: 0.5px;
display: inline-block;
border-top: 0.2px dashed #afdcff;
}
&.end {
color: rgba(67, 222, 255, 0.6);
.radius {
background: rgba(67, 222, 255, 0.7);
border: 0;
}
}
}
}
.title-box {
padding-left: 20px;
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
height: 32px;
background: linear-gradient(
180deg,
#9198ff 0%,
rgba(45, 81, 153, 0.45) 40%,
#05091a 100%
);
box-shadow: inset 0px 0px 10px 2px #3f9dff;
border-radius: 10px 10px 0px 0px;
border: 1px solid #427dff;
.title {
display: flex;
align-items: center;
.font {
font-size: 20px;
font-family: YouSheBiaoTiHei;
color: #14faff;
line-height: 26px;
text-shadow: 0px 1px 1px rgba(2, 32, 56, 0.2);
background: linear-gradient(
135deg,
#e3aa77 0%,
#f5cda9 38%,
#f9ecd3 58%,
#fcdbb1 79%,
#edb07a 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.status {
background: transparent;
border: none;
height: 25px;
line-height: 25px;
margin-left: 30px;
color: #fff;
}
}
.icon-box {
display: flex;
align-items: center;
cursor: pointer;
.iconfont {
font-size: 18px;
color: #00ffff;
margin-right: 4px;
}
.icon-text {
font-family: MicrosoftYaHeiUI;
font-size: 14px;
color: #ccedff;
font-weight: 400;
}
}
}
.ctx-box {
height: calc(100% - 120px);
// flex: 1;
// overflow: hidden;
display: flex;
flex-direction: column;
padding: 0 16px;
margin-top: 30px;
.head {
display: flex;
justify-content: space-between;
align-items: center;
font-family: SourceHanSansCN-Bold;
font-size: 16px;
color: #ffffff;
letter-spacing: 1px;
line-height: 22px;
font-weight: 700;
.text,
.time {
width: 20%;
text-align: center;
}
.time {
width: 35%;
}
.con {
width: 50%;
}
}
.list-box {
overflow: auto;
overflow-x: hidden;
box-sizing: border-box;
.item-box {
display: flex;
align-items: center;
color: #fff;
padding: 11px 0;
.text-box {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.type-box {
width: 20%;
height: 20px;
text-align: center;
font-family: SourceHanSansCN-Medium;
font-size: 14px;
color: #ebf9ff;
letter-spacing: 0;
text-align: center;
font-weight: 500;
display: flex;
justify-content: center;
align-items: center;
&.ordinary {
background-color: #298ad3;
}
&.emerg {
background-color: #ff3c3c;
}
.type {
width: 100px;
text-align: center;
font-size: 10px;
white-space: nowrap;
}
}
.text {
width: 50%;
text-align: center;
font-family: SourceHanSansCN-Medium;
font-size: 14px;
color: #ebf9ff;
letter-spacing: 0;
text-align: center;
font-weight: 500;
}
.time {
width: 35%;
text-align: center;
font-family: SourceHanSansCN-Medium;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: center;
font-weight: 500;
}
}
}
.item-box:nth-of-type(2n - 1) {
background: rgba(73, 135, 210, 0.2);
}
}
}
.xb {
transform: rotate(-180deg);
background: rgba(1, 10, 46, 0.63);
border-radius: 10px 10px 0 0;
}
}
.jiantou {
width: 22px;
height: 22px;
transform: rotate(90deg);
cursor: pointer;
margin: 0 auto;
}
.chang {
height: 326px !important;
}
.zhaun {
transform: rotate(270deg) !important;
}
.close {
color: #70daf9;
cursor: pointer;
margin: 0 10px 0 20px;
}
</style>
<template>
<ControlRight ref="controlRight" isHangar @switchCallback="showHangar = false; showMonitor = false;">
<div
slot="hangar"
class="control-item"
:class="showHangar ? 'active' : ''"
@click="onSwitchShow('showHangar')"
>
<!-- <img src="../../assets/images/hea.png" /> -->
<SymbolIcon icon="yingchao2" />
<span class="dib f8">机库</span>
</div>
<div slot="dialog">
<div class="hangar-ctrl" v-if="showHangar">
<div class="hangar-ctrl-header">
<span class="hangar-ctrl-header__title">
<img
class="hangar-ctrl-header__icon"
src="../../../../../../assets/images/mount_head.png"
/>操作区域
</span>
<span class="hangar-ctrl-header__close" @click="showHangar = false;">关闭</span>
</div>
<div class="hangar-ctrl-list">
<div
class="hangar-ctrl-item"
v-for="(item, i) in ctrlList"
:key="i"
@click="onClickCMD(i)"
>
<SymbolIcon v-if="item.icon" :icon="item.icon" class="hangar-ctrl-item__icon" />
<img v-else :src="item.img" class="hangar-ctrl-item__icon" />
<div class="hangar-ctrl-item__title">{{item.label}}</div>
</div>
</div>
</div>
<Logger :uavMsg="uavRealTimeData.msg" :hangarMsgList="hangarRealTimeData.msgList" :hangarData="hangarRealTimeData" class="logger" @exit="showLogger = false" @clearMsg="onClearMsg" v-if="showLogger"></Logger>
<HangarMonitor :uavMsg="uavRealTimeData.msg" :weatherStation="hangarRealTimeData.weatherStation" :hangarData="hangarRealTimeData" v-if="showMonitor"></HangarMonitor>
</div>
</ControlRight>
</template>
<script>
import ControlRight from "../../../../../uavApplications/components/controlPanel/components/controlRight";
import { mapActions, mapState } from "vuex";
import svgFXJJ from "./assets/images/fxjj.svg";
import Logger from './components/logger';
import HangarMonitor from './components/hangarMonitor';
export default {
name: "HangarControlRight",
components: {
ControlRight,
Logger,
HangarMonitor
},
data() {
return {
showHangar: false, // 显示机库列表
ctrlList: [
{
icon: "chucang1",
label: "出舱",
key: "out",
},
{
icon: "rucang1",
label: "入舱",
key: "in",
},
{
icon: "kaishichongdian",
label: "开始充电",
key: "chargeStart",
},
{
icon: "jieshuchongdian",
label: "结束充电",
key: "chargeEnd",
},
{
icon: "yure",
label: "预热",
key: "warmUp",
},
{
icon: "xiumian",
label: "休眠",
key: "dormancy",
},
{
icon: "yunhangrizhi2",
label: "运行日志",
key: "switchLogger",
noConfirm: true,
},
{
icon: "tuichuliucheng",
label: "退出流程",
key: "processExit",
},
{
icon: "guiji",
label: "运行监测",
key: "switchMonitor",
noConfirm: true,
},
{
img: svgFXJJ,
label: "飞行解禁",
key: "",
},
],
showLogger: true, // 显示日志
showMonitor: false, // 显示机库监测
};
},
computed: {
...mapState("MMCFlightControlCenter/uav", ['uavRealTimeData']),
...mapState('MMCFlightControlCenter/hangar', ['hangarRealTimeData'])
},
methods: {
...mapActions("MMCFlightControlCenter/hangar", [
"out",
"in",
"chargeStart",
"chargeEnd",
"warmUp",
"dormancy",
"processExit",
]),
/**
* 切换展示
*/
onSwitchShow(key) {
this.$refs.controlRight.hideAll(key);
this[key] = !this[key];
this.showMonitor = false;
},
/**
* 命令点击事件
* @param {number} i this.ctrlList被点击元素的索引
*/
async onClickCMD(i) {
let item = this.ctrlList[i];
let label = item.label;
let key = item.key;
let noConfirm = item.noConfirm;
try {
if (noConfirm) {
this[key]();
} else {
await this.$confirm(`请确认是否进行${label}操作?`, "安全确认", {
cancelButtonText: "取消",
confirmButtonText: "确定",
customClass: "uav_controlPane",
showClose: false,
});
this[key]();
}
} catch (e) {
console.log(e);
}
},
switchLogger(){
this.showLogger = !this.showLogger;
},
switchMonitor(){
this.showMonitor = !this.showMonitor;
if(this.showMonitor){
this.showHangar = false;
}
},
/**
* 清理日志
*/
onClearMsg(){
this.$store.commit('MMCFlightControlCenter/hangar/setState', {
key: 'hangarRealTimeData',
value: {
...this.hangarRealTimeData,
msgList: []
}
})
}
},
};
</script>
<style lang="scss" scoped>
.hangar-ctrl {
width: 416px;
min-height: 100px;
background: rgba(9, 32, 87, 0.7);
position: absolute;
right: 71px;
top: 50px;
bottom: auto !important;
width: 416px;
border-radius: 10px;
.hangar-ctrl-header {
display: flex;
justify-content: space-between;
height: 32px;
background: linear-gradient(
180deg,
#9198ff,
rgba(45, 81, 153, 0.45) 40%,
#05091a
);
box-shadow: inset 0 0 10px 2px #3f9dff;
border-radius: 10px 10px 0 0;
border: 1px solid #427dff;
.hangar-ctrl-header__title {
display: flex;
align-items: center;
font-size: 20px;
font-family: YouSheBiaoTiHei;
color: #14faff;
line-height: 26px;
text-shadow: 0 1px 1px rgba(2, 32, 56, 0.2);
background: linear-gradient(
135deg,
#e3aa77,
#f5cda9 38%,
#f9ecd3 58%,
#fcdbb1 79%,
#edb07a
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hangar-ctrl-header__close {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #d2dfff;
margin-right: 8px;
cursor: pointer;
display: flex;
align-items: center;
}
}
.hangar-ctrl-list {
box-sizing: border-box;
justify-content: center;
padding: 10px 0px;
display: flex;
width: 100%;
gap: 15px;
flex-wrap: wrap;
.hangar-ctrl-item {
width: 48px;
height: 48px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 5px;
background-image: linear-gradient(
179deg,
#1773b6,
#3484c3 36%,
#2a7abd 56%,
#084681
);
box-shadow: 0 0 5px 0 #0c1c47;
border-radius: 4.5px;
cursor: pointer;
.hangar-ctrl-item__icon {
width: 28px;
height: 28px;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
color: #fff;
}
.hangar-ctrl-item__title {
font-size: 10px;
color: #fff;
}
}
}
}
.logger {
position: absolute;
left: -1200px;
}
</style>
\ No newline at end of file
<template>
<div>
<MMCDataTransferPanel class="data-panel" :device="uav" :uav-data="uavRealTimeData"></MMCDataTransferPanel>
<ControlLeft></ControlLeft>
<ControlBottom isHangar></ControlBottom>
<ControlRight></ControlRight>
</div>
</template>
<script>
import ControlLeft from "./components/controlLeft";
import ControlBottom from "../../../uavApplications/components/controlPanel/components/controlBottom";
import ControlRight from "./components/controlRight";
import { mapState } from "vuex";
export default {
name: "HangarControlPanel",
components: {
ControlLeft,
ControlBottom,
ControlRight,
},
computed: {
...mapState("MMCFlightControlCenter/uav", ["uavRealTimeData", "uav"]),
},
};
</script>
<style lang="scss" scoped>
.data-panel {
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
z-index: 100;
}
</style>
\ No newline at end of file
......@@ -15,11 +15,11 @@
</div>
</div>
<div class="nest-children" :class="{ collapse: data.collapse }">
<template v-if="data && data.nestList && data.nestList.length">
<div class="nest-children" :class="{ collapse: listCollapse }">
<template v-if="data && data.nests && data.nests.length">
<div class="nest-device-list">
<div
v-for="device in data.nestList"
v-for="device in data.nests"
:key="`device_${device.id}`"
class="nest-device-item"
:class="{ online: device.online === 1 }"
......@@ -33,7 +33,7 @@
>
<el-checkbox
:disabled="device.online !== 1"
:value="device.changestatus"
:value="device.isCheck"
@change="(e) => handClick(e, device)"
></el-checkbox>
</el-tooltip>
......@@ -59,36 +59,22 @@
</div>
<div class="icon-box">
<el-popover
v-if="device.warnList && device.warnList.length > 0"
placement="right"
width="400"
trigger="hover"
>
<div style="display: flex; flex-direction: column">
<div
style="line-height: 20px; color: #fff; padding: 12px"
v-for="(warn, index) in device.warnList"
:key="index"
>{{ warn.content }}</div>
</div>
<span slot="reference" style="margin-right: 12px" class="iconfont icon-jiankang"></span>
</el-popover>
<span class="type fr" v-if="device.status">{{ typeName(device.status) }}</span>
<span @click="itemLocation(device)" class="iconfont fr icon-dingwei1"></span>
<span class="type fr">{{ typeName(device.status) }}</span>
</div>
</div>
</div>
</template>
<div class="nest-child_group_box" v-if="data.child && data.child.length">
<Item v-for="item in data.child" :data="item" :key="`device_child_${item.id}`" />
<div class="nest-child_group_box" v-if="data.childs && data.childs.length">
<Item v-for="(item, i) in data.childs" :data="item" :key="`device_child_${i}`" />
</div>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "Item",
props: {
......@@ -97,37 +83,104 @@ export default {
default: () => ({}),
},
},
inject: ['rootNode'],
data() {
return {
device: {},
};
},
computed: {
curr() {
return this.current();
},
...mapState("MMCFlightControlCenter", ["listCollapse"]),
...mapState("MMCFlightControlCenter/hangar", ["showPanel", "hangar"]),
...mapState("MMCFlightControlCenter/uav", ["uav"]),
},
methods: {
typeName(val) {
let name = "";
if (val == 6) {
name = "充电中";
} else if (val == 5) {
name = "飞行中";
} else if (val == 2 || val == 3 || val == 4) {
name = "出仓中";
} else if (val == 1) {
name = "回收中";
} else if (val == 0) {
name = "待机中";
switch (val) {
case 1:
name = "正常";
break;
case 2:
name = "维修中";
break;
case 3:
name = "损坏";
break;
case 4:
name = "保养中";
break;
}
return name;
},
handClick(e, device) {
this.itemClick(e, device);
handClick(isCheck, device) {
console.log("checkbox", isCheck, device);
this.data.nests.forEach((item) => {
this.$set(item, "isCheck", false);
});
if (isCheck) {
device.isCheck = true;
}
// 切换订阅的机库, 需要先取消订阅旧机库, 再重新订阅新机库
this.hangar &&
this.$store.dispatch("MMCFlightControlCenter/hangar/unsubscribe");
this.uav &&
this.$store.dispatch("MMCFlightControlCenter/uav/unsubscribe");
// todo 由于unsubscribe操作会将hangar设置为null, 所以unsubscribe操作要放在if判断之后再执行
if (isCheck) {
this.$store.commit("MMCFlightControlCenter/hangar/setState", {
key: "showPanel",
value: true,
});
this.$store.commit("MMCFlightControlCenter/hangar/setState", {
key: "hangar",
value: {
...device,
showPanel: true,
},
});
this.$store.dispatch("MMCFlightControlCenter/hangar/subscribe");
// 切换订阅的无人机, 需要先取消订阅旧无人机, 再重新订阅新无人机
this.uav &&
this.$store.dispatch("MMCFlightControlCenter/uav/unsubscribe");
if (device.uav) {
this.$store.commit("MMCFlightControlCenter/uav/setState", {
key: "uav",
value: device.uav,
});
this.$store.dispatch("MMCFlightControlCenter/uav/subscribe");
} else {
this.$message.error("该机库中没有无人机!");
}
this.rootNode.$emit('changeHangar', this.hangar); //根节点发出机库更换事件
} else {
this.$store.commit("MMCFlightControlCenter/hangar/setState", {
key: "showPanel",
value: false,
});
this.$store.commit("MMCFlightControlCenter/hangar/setState", {
key: "hangar",
value: null,
});
this.rootNode.$emit('changeHangar', null);
}
if (this.showPanel) {
this.$store.commit("MMCFlightControlCenter/setState", {
key: "listCollapse",
value: true,
});
}
},
},
inject: ["itemClick", "itemLocation"],
};
</script>
......@@ -176,7 +229,7 @@ export default {
}
.line-box {
width: 80px;
display: flex;
flex-shrink: 0;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
......@@ -202,7 +255,6 @@ export default {
line-height: 30px;
box-sizing: border-box;
padding-left: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
......@@ -280,14 +332,8 @@ export default {
}
.icon-box {
.icon-jiankang {
font-weight: bold;
color: #d54a15;
&.healthy {
color: #15d589;
}
}
display: flex;
align-items: center;
}
}
}
......@@ -309,6 +355,7 @@ export default {
font-size: 16px;
}
.type {
cursor: default;
font-weight: 400;
font-size: 10px;
height: 22px;
......
<!-- 飞控中心机库列表 -->
<template>
<div style="height: 100%">
<div class="uav-list-header">
<div class="list" :class="{ collapse: listCollapse }">
<div class="list-header">
<div>
<img
class="uav-list-header__icon"
src="./assets/images/uav_list_header.png"
/>
<span class="uav-list-header__text">机库列表</span>
<img class="list-header__icon" src="./assets/images/uav_list_header.png" />
<span class="list-header__text">机库列表</span>
</div>
<div class="uav-list-header__count">
<div class="list-header__count">
{{ count.sumCount }}架 /
<span class="online">{{ count.onlineCount }}在线</span> /
<span class="online">{{ count.onlineCount }}在线</span>
/
{{ count.offlineCount }}离线
</div>
</div>
......@@ -24,72 +22,104 @@
</div>
</div>
</div>
<img
@click="listCollapse = !listCollapse;"
style="transform: rotate(180deg)"
src="./assets/images/hs.png"
class="nsetLeftBox_btn"
/>
</div>
<!-- </Dialog> -->
</template>
<script>
import { Control_API } from "../../../../api";
import Item from "./components/item";
import { mapState } from "vuex";
export default {
props: {
value: {
type: Object,
default: () => ({}),
},
asyncList: {
type: Function,
default: () => () => {},
},
list: {
type: Array,
default: () => [],
},
},
name: "List",
props: {},
components: { Item },
data() {
return {
name: null,
curr_value: null,
list: [],
};
},
computed: {
...mapState("MMCFlightControlCenter", ["listCollapse"]),
count() {
if (this.list.length == 0) {
return {
onlineCount: 0,
offlineCount: 0,
sumCount: 0,
};
} else {
return {
onlineCount: this.list[0].onLineCount,
offlineCount: this.list[0].offLineCount,
sumCount: this.list[0].onLineCount + this.list[0].offLineCount,
};
}
let countObj = {
onlineCount: 0,
offlineCount: 0,
sumCount: 0,
};
this.list.forEach((item) => {
countObj.onlineCount += item?.online || 0;
countObj.offlineCount += item?.offline || 0;
});
countObj.sumCount = countObj.onlineCount + countObj.offlineCount;
return countObj;
},
},
watch: {
value: {
handler(value) {
this.curr_value = value;
// 收起列表按钮
listCollapse: {
get(){
return this.$store.state.MMCFlightControlCenter.listCollapse;
},
immediate: true,
deep: true,
},
set(val){
this.$store.commit('MMCFlightControlCenter/setState', {
key: 'listCollapse',
value: val
})
}
}
},
watch: {},
mounted() {
this.asyncList();
this.getList();
},
provide() {
methods: {
async getList() {
let res = await Control_API.getUavNestList({
name: "",
});
if (res.code === 200) {
this.list = res.data;
}
},
},
};
</script>
<style lang="scss" scoped>
.list {
display: flex;
flex-direction: column;
position: absolute;
width: 460px;
height: 85%;
left: 0;
top: 60px;
// height: 86vh;
z-index: 99;
transition: 0.3s;
border-radius: 10px 10px 0px 0px;
overflow: visible;
&.collapse {
display: none;
}
.nsetLeftBox_btn {
position: absolute;
cursor: pointer;
right: -22px;
top: 50%;
transform: translate(0, -50%);
}
}
.healthy {
box-sizing: border-box;
padding: 0 5px;
......@@ -113,9 +143,10 @@ export default {
}
.cpt-observe-nest-list {
display: flex;
flex: 1;
flex-direction: column;
height: 87vh;
background: rgba(12, 34, 73, 0.7);
overflow-y: auto;
.search-box {
flex-shrink: 0;
......@@ -169,7 +200,6 @@ export default {
.ctx-box {
flex: 1;
overflow: auto;
margin-bottom: 30px;
height: 100%;
/* backdrop-filter: blur(3px); */
position: relative;
......@@ -186,7 +216,7 @@ export default {
color: #70daf9 !important;
font-weight: 700 !important;
}
.uav-list-header {
.list-header {
box-sizing: border-box;
display: flex;
justify-content: space-between;
......@@ -203,7 +233,7 @@ export default {
border-radius: 10px 10px 0px 0px;
border: 1px solid #427dff;
.uav-list-header__text {
.list-header__text {
vertical-align: bottom;
font-size: 20px;
font-family: YouSheBiaoTiHei;
......@@ -221,12 +251,12 @@ export default {
-webkit-text-fill-color: transparent;
}
.uav-list-header__icon {
.list-header__icon {
width: 26px;
margin-left: 9px;
}
.uav-list-header__count {
.list-header__count {
color: #fff;
font-weight: 500;
margin-right: 16px;
......
<template>
<div>
<List></List>
<ControlPanel v-if="hangar && uav && showPanel"></ControlPanel>
</div>
</template>
<script>
import List from './components/list';
import { mapState } from "vuex";
import List from "./components/list";
import ControlPanel from "./components/controlPanel";
import linkFusion from "./mixins/linkFusion";
export default {
name: 'Hangar',
name: "Hangar",
mixins: [linkFusion],
components: {
List
}
}
List,
ControlPanel,
},
data() {
return {};
},
computed: {
...mapState("MMCFlightControlCenter/hangar", ["showPanel", "hangar"]),
...mapState("MMCFlightControlCenter/uav", ["uav"]),
},
beforeDestroy() {
this.$store.commit('MMCFlightControlCenter/setState', {
key: 'listCollapse',
value: false
})
if (this.uav) {
this.$store.dispatch("MMCFlightControlCenter/uav/unsubscribe");
}
if (this.hangar) {
this.$store.dispatch("MMCFlightControlCenter/hangar/unsubscribe");
}
this.$store.dispatch('MMCFlightControlCenter/uav/end');
},
};
</script>
<style>
</style>
\ No newline at end of file
// 无人机实时数据各链路融合
export default {
computed: {
// mqtt实时数据
mqttDataSet() {
return this.$store.state.MMCMQTT?.dataSet?.[this.hangar?.hardId];
},
mqttUrl() {
return this.$store.getters["MMCFlightControlCenter/mqttUrl"];
},
},
watch: {
mqttDataSet(newVal) {
if (newVal) {
this.$store.dispatch(
"MMCFlightControlCenter/hangar/updateByMQTT",
newVal
);
}
},
},
async created() {
// 启动mqtt服务
try {
this.$store
.dispatch("MMCMQTT/init", {
url: this.mqttUrl,
})
.then(() => {
console.log("mqtt连接成功");
});
} catch (e) {
console.log("mqtt失败", e);
}
},
};
......@@ -4,13 +4,15 @@
<SymbolIcon icon="yijianrenwu" />
<span class="dib f8">一键任务</span>
</div>
<!-- v-show="iconShow" -->
<div class="control-bottom-item" @click="onReturnFlight">
<SymbolIcon icon="yijianfanhang2" />
<span class="dib f8">一键返航</span>
</div>
<!-- v-show="iconShow" -->
<div class="control-bottom-item" @click="onTaskEnd">
<div class="control-bottom-item" @click="onLand" v-if="isHangar">
<SymbolIcon icon="anquanjiangla1" />
<span class="dib f8">紧急迫降</span>
</div>
<div class="control-bottom-item" @click="onTaskEnd" v-else>
<SymbolIcon icon="renwujieshu1" />
<span class="dib f8">任务结束</span>
</div>
......@@ -29,7 +31,6 @@
</div>
</template>
<!-- device.isPush =99 代表大疆 -->
<template v-else>
<div v-if="controlType === 0" class="control-bottom-item" @click="onModeManual">
<SymbolIcon icon="zidong" />
......@@ -39,7 +40,6 @@
<SymbolIcon icon="shoudong" />
<span class="dib f8">手动</span>
</div>
</template>
<!-- 键盘控制 -->
<div class="key-control" v-if="controlType === 2">
......@@ -203,29 +203,39 @@ import { flightTaskAPI, Control_API } from "../../../../../../api";
export default {
name: "ControlBottom",
inject:['bus'],
inject: ["bus"],
props: {
// 是否是机库组件
isHangar: {
type: Boolean,
default: false,
},
},
data() {
return {
controlType: 0, //控制类型, 0: 自动 1: 摇杆 2: 键盘
};
},
computed: {
...mapState("MMCFlightControlCenter/uav", [
"uav",
"airlineData",
]),
...mapState("MMCFlightControlCenter/uav", ["uav", "airlineData"]),
},
methods: {
...mapActions("MMCFlightControlCenter/uav", [
"isTakeOver",
]),
/**
* 接管判断, 机库模块中不需要判断接管
*/
async isTakeOver(){
if(this.isHangar){
return true;
}
return await this.$store.dispatch("MMCFlightControlCenter/uav/isTakeOver");
},
// 无人机接管更新状态
async updateTakeOver() {
await Control_API.updateTakeOver(this.uav.id);
},
// 一键任务
onStartTask() {
this.bus.$emit('startTask');
this.bus.$emit("startTask");
},
// 一键返航
async onReturnFlight() {
......@@ -242,14 +252,11 @@ export default {
}).then((res) => {
this.updateTakeOver();
this.$store.dispatch(
"MMCFlightControlCenter/uav/returnFlight",
{
callback: (isOk) => {
isOk && this.$message.success("发出返航指令成功");
},
}
);
this.$store.dispatch("MMCFlightControlCenter/uav/returnFlight", {
callback: (isOk) => {
isOk && this.$message.success("发出返航指令成功");
},
});
});
},
// 任务结束
......@@ -274,6 +281,28 @@ export default {
});
});
},
// 紧急降落
async onLand() {
// 判断是否已接管
if (!(await this.isTakeOver())) {
this.$message.warning("请先接管无人机");
return;
}
this.$confirm("请确认是否进行紧急降落操作?", "安全确认", {
cancelButtonText: "取消",
confirmButtonText: "确定",
customClass: "uav_controlPane",
showClose: false,
}).then((res) => {
this.updateTakeOver();
this.$store.dispatch("MMCFlightControlCenter/uav/land", {
callback: (isOk) => {
isOk && this.$message.success("发出紧急降落指令成功");
},
});
});
},
/**
* 手动模式
*/
......@@ -283,15 +312,13 @@ export default {
this.$message.warning("请先接管无人机");
return;
}
this.$store.dispatch(
"MMCFlightControlCenter/uav/modeManual",
{
callback: (isOk) => {
isOk && (this.controlType = 1);
},
}
);
this.$store.dispatch("MMCFlightControlCenter/uav/modeManual", {
callback: (isOk) => {
isOk && (this.controlType = 1);
},
});
},
/**
* 自动模式
*/
......@@ -301,45 +328,35 @@ export default {
this.$message.warning("请先接管无人机");
return;
}
this.$store.dispatch(
"MMCFlightControlCenter/uav/modeAuto",
{
callback: (isOk) => {
isOk && (this.controlType = 0);
},
}
);
this.$store.dispatch("MMCFlightControlCenter/uav/modeAuto", {
callback: (isOk) => {
isOk && (this.controlType = 0);
},
});
},
/**
* 键盘模式
*/
async onModeKeyboard(){
async onModeKeyboard() {
// 判断是否已接管
if (!(await this.isTakeOver())) {
this.$message.warning("请先接管无人机");
return;
}
this.$store.dispatch(
"MMCFlightControlCenter/uav/modeKeyboard",
{
callback: (isOk) => {
isOk && (this.controlType = 2);
},
}
);
this.$store.dispatch("MMCFlightControlCenter/uav/modeKeyboard", {
callback: (isOk) => {
isOk && (this.controlType = 2);
},
});
},
/**
* 键盘控制
*/
clickControl(){
},
clickControl() {},
/**
* 停止键盘控制
*/
cancelControl(){
}
cancelControl() {},
},
};
</script>
......
......@@ -8,32 +8,30 @@
</div>
<el-form class="task-main" label-width="60px">
<el-form-item label="任务库">
<el-tooltip :content="clew" placement="top">
<el-cascader
filterable
popper-class="mmc"
size="mini"
class
style="width: 100%"
v-model="selectedTaskId"
:options="taskListAll"
clearable
:show-all-levels="false"
placeholder="请选择任务"
:props="{
<el-cascader
filterable
popper-class="mmc"
size="mini"
class
style="width: 100%"
v-model="selectedTaskId"
:options="taskListAll"
clearable
:show-all-levels="false"
placeholder="请选择任务"
:props="{
children: 'children',
label: 'name',
value: 'id',
}"
@change="onChangeTask"
>
<template slot-scope="{ data }">
<el-tooltip class="item" effect="dark" :content="data.name" placement="top-start">
<span>{{ data.name }}</span>
</el-tooltip>
</template>
</el-cascader>
</el-tooltip>
@change="onChangeTask"
>
<template slot-scope="{ data }">
<el-tooltip class="item" effect="dark" :content="data.name" placement="top-start">
<span>{{ data.name }}</span>
</el-tooltip>
</template>
</el-cascader>
</el-form-item>
<el-form-item label="航线">
<el-tooltip content="航线名称" placement="top">
......@@ -78,9 +76,6 @@ export default {
selectedTaskId: [],
// 航线数据
flightList: [],
// 航线id
flightId: "",
flightIdv2: "",
// 选择的航线
selectedAirway: {
name: "",
......@@ -88,12 +83,11 @@ export default {
},
// 航线选择窗口
showFlywayDialog: false,
clew: "任务库", // 选中任务提示语
};
},
computed: {
...mapState("MMCFlightControlCenter", ["taskList", "cesiumViewer"]),
...mapState("MMCFlightControlCenter/uav", ["uav"]),
...mapState("MMCFlightControlCenter", ["cesiumViewer"]),
...mapState("MMCFlightControlCenter/uav", ["taskList", "uav"]),
taskListAll() {
return [{ name: "选择航线自动生成任务", id: -1 }, ...this.taskList];
},
......@@ -161,26 +155,21 @@ export default {
customClass: "uav_controlPane",
showClose: false,
});
if (this.selectedAirway.id === -1) {
this.$store.commit(
"MMCFlightControlCenter/uav/setState",
{
key: "airlineData",
value: null,
}
);
} else {
this.$store.commit(
"MMCFlightControlCenter/uav/setState",
{
key: "airlineData",
value: this.selectedAirway,
this.$store.commit("MMCFlightControlCenter/uav/setState", {
key: "airlineData",
value: this.selectedAirway,
});
this.$store.dispatch("MMCFlightControlCenter/uav/takeOff", {
callback(status) {
if (status) {
this.$message.success("一键任务指令发送成功");
} else {
this.$message.error("一键任务指令发送失败");
}
);
}
this.$store.dispatch("MMCFlightControlCenter/uav/takeOff");
},
});
this.rootNode.$emit(
"startTask",
"uavStartTask",
this.uav,
this.selectedTask,
this.selectedAirway
......@@ -245,11 +234,7 @@ export default {
item.alt = Number(item.alt);
pointPositions.push(item.longitude, item.latitude, item.alt);
label_arr.push(
Cesium.Cartesian3.fromDegrees(
item.longitude,
item.latitude,
item.alt
)
Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, item.alt)
);
point_entity = this.cesiumViewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
......
// import API from "@/api";
import { Utils } from "../../../../../../../../../lib/cesium";
let airline_entitys = [];
let air_line_preview = [];
export default {
// 飞控选择其他模块时,关闭航线
// 关键字:关闭预设航线
async close_the_router(item, state){
try {
let viewer = window.viewer;
if (airline_entitys?.length) {
airline_entitys.forEach((item) => {
viewer.entities.remove(item);
});
}
let res = {};
air_line_preview.forEach((item) => {
viewer.entities.remove(item);
});
if (state) {
res.flightCourseJson = item.flightCourseJson;
}
if (res?.flightCourseJson) {
let positions = [];
let center = [];
let polyline = JSON.parse(res.flightCourseJson);
let label_arr = [];
let _this = this;
polyline.points.forEach((item, index) => {
positions.push(item.lon, item.lat, item.alt);
label_arr.push(
Cesium.Cartesian3.fromDegrees(item.lon, item.lat, item.alt)
);
let point_entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
item.lon,
item.lat,
item.alt
),
name: "show_airline_point",
point: {
pixelSize: 10,
color: Cesium.Color.fromCssColorString("#00A9A9"),
},
label: {
text: String(item.alt) + "m",
scale: 0.6,
font: "bold 28px Microsoft YaHei",
// fillColor: Cesium.Color.BLUE,
fillColor: Cesium.Color.fromCssColorString("#FF7F09"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: false,
outlineWidth: 0,
pixelOffset: new Cesium.Cartesian2(25, -10),
},
});
airline_entitys.push(point_entity);
if (label_arr.length > 1) {
let before = label_arr[label_arr.length - 2];
let after = label_arr[label_arr.length - 1];
_this.create_label(before, after);
}
});
positions = Cesium.Cartesian3.fromDegreesArrayHeights(positions);
let redLine = viewer.entities.add({
name: "Red line on terrain",
polyline: {
positions,
width: 4,
// material: Cesium.Color.RED,
material: Cesium.Color.fromCssColorString("#00A9A9"),
// clampToGround: true,
},
});
viewer.flyTo(redLine);
airline_entitys.push(redLine);
}
} catch (error) {
}
},
async handleAirLinePreview(item, state) {
try {
let viewer = window.viewer;
if (airline_entitys?.length) {
airline_entitys.forEach((item) => {
viewer.entities.remove(item);
});
}
let res = {};
air_line_preview.forEach((item) => {
viewer.entities.remove(item);
});
if (state) {
res.flightCourseJson = item.flightCourseJson;
} else {
// res = await API.TASK.JingQingTrack({
// taskId: item.flightLineId,
// });
}
// 有航点高度和距离的代码
if (res?.flightCourseJson) {
let positions = [];
let center = [];
let polyline = JSON.parse(res.flightCourseJson);
let baseSpeed = polyline.line.baseSpeed;
let label_arr = [];
let _this = this;
polyline.points.forEach((item, index) => {
positions.push(item.lon, item.lat, item.alt);
label_arr.push(
Cesium.Cartesian3.fromDegrees(item.lon, item.lat, item.alt)
);
let point_entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
item.lon,
item.lat,
item.alt
),
name: "show_airline_point",
point: {
pixelSize: 10,
color: Cesium.Color.fromCssColorString("#00A9A9"),
},
label: {
text: String(item.alt) + "m",
scale: 0.6,
font: "bold 28px Microsoft YaHei",
// fillColor: Cesium.Color.BLUE,
fillColor: Cesium.Color.fromCssColorString("#FF7F09"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: false,
outlineWidth: 0,
pixelOffset: new Cesium.Cartesian2(25, -10),
},
});
let point_entity_num = viewer.entities.add({
name: "airline_point",
// id: uuid,
position: Cesium.Cartesian3.fromDegrees(
item.lon,
item.lat,
item.alt
),
point: {
pixelSize: 20,
color: Cesium.Color.red,
color: Cesium.Color.fromCssColorString("red"),
// fillColor: Cesium.Color.red,
// heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, // supermap版本会导致拖动显示错误
},
label: {
text: new Cesium.CallbackProperty(() => {
return String(index + 1);
}, false),
// text:index + 1,
font: "bold 14px Microsoft YaHei",
// fillColor: Cesium.Color.BLUE,
fillColor: Cesium.Color.fromCssColorString("white"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: false,
outlineWidth: 0,
pixelOffset: new Cesium.Cartesian2(0, 0),
},
});
airline_entitys.push(point_entity);
airline_entitys.push(point_entity_num);
if (label_arr.length > 1) {
let before = label_arr[label_arr.length - 2];
let after = label_arr[label_arr.length - 1];
_this.create_label(before, after, baseSpeed);
}
});
console.log(polyline,"航线数据")
_this.$store.commit('fckernel/SET_TASK_INFO', polyline);
positions = Cesium.Cartesian3.fromDegreesArrayHeights(positions);
let redLine = viewer.entities.add({
name: "Red line on terrain",
polyline: {
positions,
width: 4,
// material: Cesium.Color.RED,
material: Cesium.Color.fromCssColorString("#00A9A9"),
// clampToGround: true,
},
});
viewer.flyTo(redLine);
airline_entitys.push(redLine);
} else {
this.$el_message("暂无航线", () => { }, "error");
}
} catch (error) {
this.$el_message("暂无航线", () => { }, "error");
}
},
// 显示距离
create_label(before, after, baseSpeed) {
let viewer = window.viewer;
let before_wgs84 = Utils.transformCartesian2WGS84(before);
let after_wgs84 = Utils.transformCartesian2WGS84(after);
let center_lng = (before_wgs84.lng + after_wgs84.lng) / 2;
let cetner_lat = (before_wgs84.lat + after_wgs84.lat) / 2;
let alt = (after_wgs84.alt + before_wgs84.alt) / 2;
let position = Utils.transformWGS842Cartesian({
lng: center_lng,
lat: cetner_lat,
alt: alt + 10,
});
let text = `${Cesium.Cartesian3.distance(before, after).toFixed(2)} m | ${baseSpeed}m/s`;
let time = Cesium.Cartesian3.distance(before, after).toFixed(2)/baseSpeed
this.distanceLine += time
this.$emit("getDistanceLine",this.distanceLine)
let label_entity = viewer.entities.add({
id: `label_${before}`,
position: position,
label: {
text: text,
scale: 0.5,
font: "bold 30px Microsoft YaHei",
fillColor: Cesium.Color.fromCssColorString("#fff"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: true,
backgroundPadding: new Cesium.Cartesian2(10, 10),
},
});
airline_entitys.push(label_entity);
},
}
\ No newline at end of file
......@@ -10,6 +10,8 @@
<img src="../../assets/images/hea.png" />
<span class="dib f8">健康管理</span>
</div>
<!-- 机库信息 -->
<slot name="hangar"></slot>
<div
class="control-item"
:class="showMount ? 'active' : ''"
......@@ -27,7 +29,7 @@
<span class="dib f8">无人机</span>
</div>
<div
v-if="uav.network === 1"
v-if="!isHangar && uav.network === 1"
class="control-item"
:class="showAlarmLamp ? 'active' : ''"
@click="onSwitchShow('showAlarmLamp')"
......@@ -105,6 +107,7 @@
ref="MountControllerRef"
/>
<AlarmLamp v-if="showAlarmLamp" :uav="uav" @close="showAlarmLamp = false" />
<slot name="dialog"></slot>
</div>
</template>
......@@ -130,6 +133,13 @@ export default {
ViewLib,
AlarmLamp,
},
props: {
// 是否机库页面使用
isHangar: {
type: Boolean,
default: false
}
},
data() {
return {
showAlarmLamp: false, //展示警示灯面板
......@@ -141,10 +151,7 @@ export default {
};
},
computed: {
...mapState("MMCFlightControlCenter/uav", [
"uav",
"airlineData",
]),
...mapState("MMCFlightControlCenter/uav", ["uav", "airlineData"]),
},
methods: {
hideAll(key) {
......@@ -166,6 +173,7 @@ export default {
* 切换展示
*/
onSwitchShow(key) {
this.$emit("switchCallback");
this.hideAll(key);
this[key] = !this[key];
},
......@@ -180,7 +188,7 @@ export default {
right: 30px;
z-index: 1;
.control-list {
.control-list::v-deep {
display: flex;
flex-direction: column;
gap: 3px;
......
......@@ -2,10 +2,8 @@
<div>
<MMCDataTransferPanel
class="data-panel"
:device="device"
:throttleValue="throttleValue"
:uav-battery="battery"
:uav-data="uavData"
:device="uav"
:uav-data="uavRealTimeData"
></MMCDataTransferPanel>
<!-- 一键任务 返航 安全降落 手动 -->
<ControlBottom></ControlBottom>
......@@ -44,26 +42,7 @@ export default {
};
},
computed: {
...mapState("MMCFlightControlCenter/uav", ["uavRealTimeData"]),
uavData() {
// console.log("this.uavRealTimeData", this.uavRealTimeData);
return {
relativeAlt:
Number(this.uavRealTimeData?.gps?.relativeAlt)?.toFixed(0) || "-",
absoluteAlt:
Number(this.uavRealTimeData?.gps?.absoluteAlt)?.toFixed(0) || "-",
flyDistance:
Number(this.uavRealTimeData?.flyDistance)?.toFixed(0) || "-",
flyTime: Number(this.uavRealTimeData?.flyTime)?.toFixed(0) || "-", //飞行时间
groundSpeed:
Number(this.uavRealTimeData?.groundSpeed)?.toFixed(0) || "-", //飞行速度
distanceToHome:
Number(this.uavRealTimeData?.distanceToHome)?.toFixed(0) || "-",
velocityX: this.uavRealTimeData?.velocityX || 0,
velocityY: this.uavRealTimeData?.velocityY || 0,
velocityZ: Number(this.uavRealTimeData?.velocityZ)?.toFixed(0) || "-", //爬升率
};
},
...mapState("MMCFlightControlCenter/uav", ["uavRealTimeData", "uav"]),
},
};
</script>
......
......@@ -161,7 +161,6 @@ export default {
) {
if((value / 100).toFixed(2) == '0.00'){
// console.log('this.obstacle.distances', this.obstacle.distances)
// debugger
}
this.minDistance[block] = (value / 100).toFixed(2);
}
......
......@@ -284,8 +284,8 @@
<div class="search mr22" v-if="!isStatus">
<el-select class="video_type mr24" v-model="streamSelect" placeholder="切换源">
<el-option
v-for="item in streamOptions"
:key="item.label"
v-for="(item, i) in streamOptions"
:key="i"
:label="item.label"
:value="item.value"
></el-option>
......
......@@ -5,13 +5,13 @@
v-if="listCollapse"
style="transform: rotate(180deg)"
src="./assets/images/hs.png"
class="icon-collapse uav-list_btn"
class="uav-list_btn"
/>
<img
@click="collapseFlagfn"
v-else
src="./assets/images/collapse.png"
class="icon-collapse uav-list_btn"
class="uav-list_btn"
/>
<div class="uav-list-header">
<img class="uav-list-header__icon" src="./assets/images/uav_list_header.png" />
......
......@@ -9,7 +9,7 @@
:mountDatas="selectMount"
@close="onPlayerClose"
/>
<ControlPanel v-if="showPanel"></ControlPanel>
<ControlPanel v-if="uav && showPanel"></ControlPanel>
</div>
</template>
......@@ -44,26 +44,6 @@ export default {
"showPanel",
"selectMount"
]),
taskId() {
return this.$store.state.fckernel.taskId;
},
YJTaskInfo() {
return this.$store.state.fckernel.YJTaskInfo;
},
lineInfo() {
return this.$store.state.fckernel.lineInfo;
},
uav_mounts() {
let { mounts } = this.uav.control;
return mounts
.map((item) => {
let find_item = this.mount.list.find(
(m) => m.name === item.gimbalName
);
return find_item || undefined;
})
.filter((item) => item);
},
},
created() {
// 等待航线上传成功在执行
......@@ -73,7 +53,16 @@ export default {
}); */
},
mounted() {},
beforeDestroy() {},
beforeDestroy() {
this.$store.commit('MMCFlightControlCenter/setState', {
key: 'listCollapse',
value: false
})
if(this.uav){
this.$store.dispatch('MMCFlightControlCenter/uav/unsubscribe');
}
this.$store.dispatch('MMCFlightControlCenter/uav/end');
},
methods: {
getcanvas(val, item) {
let data = null;
......@@ -342,12 +331,4 @@ export default {
</script>
<style lang="scss" scoped>
.player {
/* position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 0;
height: 0; */
}
</style>
// 无人机实时数据各链路融合
export default {
computed: {
/* uav() {
return this.$store.state.MMCFlightControlCenter.uav.uav;
}, */
// mqtt实时数据
mqttDataSet() {
return this.$store.state.MMCMQTT?.dataSet?.[this.uav?.hardId];
......@@ -12,15 +9,6 @@ export default {
gsDataSet() {
return this.$store.state.MMCGroundStation?.dataSet?.[this.uav?.hardId];
},
token() {
return this.$store.state.MMCFlightControlCenter.token;
},
userInfo() {
return this.$store.state.MMCFlightControlCenter.userInfo;
},
wsUrl() {
return this.$store.getters["MMCFlightControlCenter/wsUrl"];
},
mqttUrl() {
return this.$store.getters["MMCFlightControlCenter/mqttUrl"];
},
......@@ -63,27 +51,6 @@ export default {
console.log("mqtt失败", e);
}
// 由于ws连接到地面站是使用广播形式获取无人机数据的,所以在组件创建时先连接ws,后续选择无人机时,通过广播数据,筛选出选择的无人机信息出来
try {
const username = this.userInfo.userName;
const token = this.token;
this.$store
.dispatch("MMCGroundStation/init", {
url: this.wsUrl,
userInfo: {
type: 100,
systemCode: "mmc",
state: 1,
username,
token,
appId: "40003",
},
})
.then(() => {
console.log("地面站连接成功");
});
} catch (e) {
console.log("地面站连接失败", e);
}
// 由于地面站是广播形式的数据传输, 非常消耗性能, 把连接地面站ws的操作挪到了订阅中去, 取消订阅时就断开ws
},
};
......@@ -17,6 +17,7 @@ import mapImageSwitch from "./components/mapImageSwitch";
import uavApplications from "./components/uavApplications";
import hangar from "./components/hangar";
import Vue from "vue";
import SymbolIcon from "../symbol-icon";
export default {
name: "MMCFlightControlCenter",
......@@ -37,8 +38,8 @@ export default {
type: Object,
default: null,
},
// 任务列表
taskList: {
// 无人机任务列表
uavTaskList: {
type: Array,
default() {
return [
......@@ -54,20 +55,51 @@ export default {
];
},
},
// 航线列表
airwayList: {
type: Array,
// 机库任务列表
hangarTaskList: {
type: Object,
default() {
return [
/* {
name: '航线名称',
id: 1,
organizationName: '所属单位',
status: 1 //空域状态 1: 可用 2: 待申请 3: 待审批 4: 通过 5: 驳回
isSafe: 1 //空域状态 1: 安全 2: 待确定
labelName: '航线标签'
} */
];
return {
// 常态
normal: [
/* {
name: '任务',
id: 1,
status: '', 任务状态
children: [],
airway: {
name: '航线名称',
id: 1
}
} */
],
// 定时
Timed: [
/* {
name: '任务',
id: 1,
time: '', //定时与周期的时间
status: '', 任务状态
airway: {
name: '航线名称',
id: 1
}
} */
],
// 周期
period: [
/* {
name: '任务',
id: 1,
time: '', //定时与周期的时间
status: '', 任务状态
airway: {
name: '航线名称',
id: 1
}
} */
],
};
},
},
// 场景 0: 无人机 1: 机库
......@@ -83,10 +115,19 @@ export default {
};
},
watch: {
taskList: {
uavTaskList: {
immediate: true,
handler(newVal) {
this.$store.commit("MMCFlightControlCenter/setState", {
this.$store.commit("MMCFlightControlCenter/uav/setState", {
key: "taskList",
value: newVal,
});
},
},
hangarTaskList: {
immediate: true,
handler(newVal) {
this.$store.commit("MMCFlightControlCenter/hangar/setState", {
key: "taskList",
value: newVal,
});
......@@ -94,10 +135,11 @@ export default {
},
},
beforeCreate() {
Vue.component("SymbolIcon", SymbolIcon);
if (!window.$mmc) {
window.$mmc = {};
}
window.$mmc.store = this.$store;
window.$mmc.$store = this.$store;
window.$mmc.state = () => {
return this.$store.state;
};
......@@ -116,9 +158,7 @@ export default {
value: this.userInfo,
});
},
methods: {
},
methods: {},
};
</script>
......@@ -389,6 +429,11 @@ export default {
background-color: #34466e !important;
}
}
.el-select-dropdown__empty {
background: #02173d;
border: 1px solid #315ec7;
}
}
&.el-cascader__dropdown {
......
......@@ -34,11 +34,11 @@ export default function main(elementOrSelector, options = {}){
if(!window.$mmc){
window.$mmc = {};
}
window.$mmc.store = new Vuex.Store(store);
window.$mmc.$store = new Vuex.Store(store);
return new Vue({
render: (h) => h(App),
router,
store: window.$mmc.store,
store: window.$mmc.$store,
}).$mount(elementOrSelector);
}
......
import Moment from "moment";
let hangarRealTimeData = {
chargerPower: 0, //充电电源,0-未知,1-打开,2-关闭
cover: 0, //舱盖,0-未知,1-关闭,2-打开,3-正在关闭,4-正在打开
driverPower: 0, // 伺服电源,0-未知,1-打开,2-关闭
folder: 0, // 回中器,0-未知,1-夹紧,2-放松,3-夹紧过程,4-放松过程
hangarHumid: 0, // 停机坪内湿度,单位:%
hangarMode: 0, // 停机坪模式,0-未知,1-正常模式,2-休眠模式
hangarPower: 0, // 停机坪电源,0-未知,1-打开,2-关闭
hangarTemp: 0, // 停机坪内温度,单位:度
inLamp: 0, // 内部灯,0-未知,1-打开,2-关闭
lifts: 0, // 起降台,0-未知,1-降下,2-升起,3-降下过程,4-升起过程
outCameraPower: 0, // 外置摄像头电源,0-未知,1-打开,2-关闭
outLamp: 0, // 外部灯,0-未知,1-打开,2-关闭
processStatus: 0, // 0-空闲 、1-正在执行出仓待命、2-正在执行回收入仓、3-正在执行充电流程 、4-正在结束充电流程、5-正在执行休眠流程、6-正在执行预热流程、7-正在执行初始化、8-未初始化 、9-正在执行关舱流程 、10-正在执行回中器操作、99-正在执行飞行任务
remotePower: 0, // 遥控电源,0-未知,1-打开,2-关闭
rfPower: 0, // 射频电源,0-未知,1-打开,2-关闭
rtkPower: 0, // RTK 基站电源,0-未知,1-打开,2-关闭
videoPower: 0, // 机载视频电源,0-未知,1-打开,2-关闭
waterWarn: 0, // 水浸报警,0-未知,1-报警,2-正常
msg: {
// 2063 飞控应答消息
number: 0, // 包序列号
text: "", // 文本内容
type: 1, // 系统消息数据:2063
},
msgList: [], //飞控应答消息记录
weatherStation: {
// 2066 气象站数据
rain: 0, // 雨量,0-无雨,1-有雨
homeLat: 0, // 停机坪纬度,单位 deg
homeLon: 0, // 停机坪经度,单位 deg
humidity: 0, // 湿度,单位:%
light: 0, // 光照,单位:lux
number: 0, // 包序列号
homesatellite: 0, // 停机坪卫星数
temp: 0, // 温度,单位:C
weatherHumid: 0, // 气象站湿度,单位:%
weatherTemp: 0, // 气象站温度,单位:度
windDirection: 0, // 风向,单位:deg
windSpeed: 0, // 风速,单位 m/s
},
};
const state = {
hangar: null, // 选择中的机库信息
showPanel: false, //显示数据面板
hangarRealTimeData, // 实时数据
airlineData: null, // 当前选择的航线数据
};
const mutations = {
/**
* 单纯的给state赋值
* @param {*} param0
* @param {*} data {key: '', value}
*/
setState(state, data) {
try {
state[data.key] = data.value;
} catch (e) {
console.log("setDate err", e);
}
},
};
const actions = {
// 通过mqtt更新实时采集数据
updateByMQTT({ commit, state, dispatch }, data) {
// mqtt链路
const type2065 = data[2065]?.data || {};
const type2063 = data[2063]?.data; //飞控应答消息
data[2063] && console.log("type2063", data[2063]);
const type2066 = data[2066]?.data || {}; //气象站数据
let msgList = state.hangarRealTimeData.msgList || [];
if (type2063) {
let moment = new Moment();
type2063.time = moment.format("yyyy-MM-DD hh:mm:ss");
msgList.push(type2063);
data[2063] = null;
}
commit("setState", {
key: "hangarRealTimeData",
value: {
...state.hangarRealTimeData,
...type2065,
msg: type2063,
msgList,
weatherStation: type2066,
},
});
},
/**
* 订阅机库数据
* @param {*} param0
* @param {object} data
*/
subscribe({ state, dispatch }) {
window.$mmc.$store.dispatch("MMCMQTT/subscribe", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
callback(ok) {
ok &&
console.log("mqtt订阅主题", "APRON/RECEIVE/" + state.hangar.hardId);
},
});
},
/**
* 取消订阅
* @param {} param0
*/
unsubscribe({ state, dispatch }) {
window.$mmc.$store.dispatch("MMCMQTT/unsubscribe", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
callback(ok) {
ok &&
console.log(
"mqtt取消订阅主题",
"APRON/RECEIVE/" + state.hangar.hardId
);
},
});
dispatch("destroy");
},
/**
* 销毁机库相关对象
* @param {} param0
* @param {*} data
*/
destroy({ commit, state, dispatch }, data) {
commit("setState", { key: "hangar", value: null });
commit("setState", {
key: "hangarRealTimeData",
value: hangarRealTimeData,
});
},
/**
* 显示面板
* @param {*} param0
* @param {Object} data 机库信息
*/
async showPanel({ state, commit, dispatch }, data) {
if (state.hangar?.id !== data.id) {
// 切换订阅的机库, 需要先取消订阅旧机库, 再重新订阅新机库
state.hangar && dispatch("unsubscribe");
commit("setState", {
key: "showPanel",
value: true,
});
commit("setState", {
key: "hangar",
value: {
...data,
showPanel: true,
},
});
dispatch("subscribe");
} else {
// 关闭机库订阅
dispatch("unsubscribe");
commit("setState", {
key: "showPanel",
value: false,
});
commit("setState", {
key: "hangar",
value: null,
});
}
},
/**
* 出库
* @param {*} param0
* @param {*} data
* @param {Function} data.callback
*/
out({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 2059,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 入库
* @param {*} param0
* @param {*} data
*/
in({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 2073,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 开始充电
* @param {*} param0
* @param {*} data
*/
chargeStart({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 2060,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 结束充电
* @param {*} param0
* @param {*} data
*/
chargeEnd({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 2071,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 预热
* @param {*} param0
* @param {*} data
*/
warmUp({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 2072,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 休眠
* @param {*} param0
* @param {*} data
*/
dormancy({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 2061,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 退出流程
* @param {*} param0
* @param {*} data
*/
processExit({ state, commit, dispatch }, data) {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "APRON/RECEIVE/" + state.hangar.hardId,
data: {
cmdControlType: 100001,
},
callback() {
data?.callback && data.callback();
},
});
},
/**
* 一键起飞
* @param {function} data.callback //完成回调
*/
async takeOff({ state }, data) {
try {
let uavHardId =
window.$mmc.$store.state.MMCFlightControlCenter.uav.uav.hardId;
// 生成架次号
const getFlightSortic = await Control_API.getFlightSortic({
taskId: state.airlineData.id,
deviceHardId: uavHardId,
});
// 上传航线指令
const flightObj = JSON.parse(state.airlineData.content);
const waypointList = flightObj.map((v) => ({
altitude: v.alt,
coordinate: {
latitude: v.latitude,
longitude: v.longitude,
},
frame: 3,
stay: 0.0,
speed: v.speed,
waypointActions: v.actions,
}));
this.$store.dispatch("MMCMQTT/publish", {
topic: "PROCESS/OBTAIN/" + state.hangar.hardId,
data: {
cmdControlType: 100004,
uavDeviceId: uavHardId,
wayLineObj: {
taskId: state.airlineData.id,
flightSortiesID: getFlightSortic.data,
autoFlightSpeed: state.airlineData.line.baseSpeed,
waypointList,
finishedAction: "GO_HOME",
headingMode: "AUTO",
isExitMissionOnRCSignalLostEnabled: true,
maxFlightSpeed: 12,
},
},
callback() {
data.callback && data.callback(true);
},
});
} catch (e) {
console.log(e);
data.callback && data.callback(false, e);
}
},
};
export default {
namespaced: true,
name: "hangar",
state,
mutations,
actions,
};
import uav from "./uav";
import hangar from "./hangar";
import { Control_API } from "../api";
import { Utils } from "../lib/cesium";
export default {
namespaced: true,
......@@ -27,21 +30,8 @@ export default {
},
},
cesium3DModels: [], //cesium的3D模型
listCollapse: false, //无人机或盈巢列表是否折叠
// 任务列表
taskList: [
/* {
name: "任务",
id: 1,
children: [],
airway: {
name: "航线名称1",
id: 1,
content:
'{"filename":"肇庆航线20240318","line":{"baseSpeed":3},"content":[{"uuid":"1nmI-Fo18IagbcVJsia7Q","latitude":23.178153411812204,"longitude":112.57807281336807,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]},{"uuid":"9pTbBPlF8iIwbUNqusyHK","latitude":23.17783116525969,"longitude":112.57797543441967,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]},{"uuid":"s91IhN22wuaeyG-UQs0XR","latitude":23.17786413506686,"longitude":112.57824336604547,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]},{"uuid":"xS_JIl3wxQrhMPdpcjcSn","latitude":23.17820934975604,"longitude":112.5781357731637,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]}],"baseSpeed":3,"gimbalYaw":0,"gimbalPitch":0,"alt":100}',
},
}, */
],
listCollapse: false, //无人机或机库列表是否折叠
airwayEntities: [], //航线实体集合, 元素为new Cesium.EntityCollection()创建
},
mutations: {
/**
......@@ -69,6 +59,182 @@ export default {
layer.visible = data.show;
});
},
/**
* 判断是否已接管
* @param { Number } data.id 无人机id
*/
async isTakeOver({ state }, data) {
let res = await Control_API.uavDetail({ id: data?.id || state.uav.id });
if (res.data.currentOperator !== state.userInfo.id) {
return false;
}
return true;
},
/**
* 绘制航线距离文字标签
* @param {*} param0
* @param {object} data
* @param {*} data.before 起点经纬度
* @parma {*} data.after 终点经纬度
*/
createDistanceLabel({ state }, data) {
const { before, after } = data;
const before_wgs84 = Utils.transformCartesian2WGS84(before);
const after_wgs84 = Utils.transformCartesian2WGS84(after);
const center_lng = (before_wgs84.lng + after_wgs84.lng) / 2;
const center_lat = (before_wgs84.lat + after_wgs84.lat) / 2;
const alt = (after_wgs84.alt + before_wgs84.alt) / 2;
const position = Utils.transformWGS842Cartesian({
lng: center_lng,
lat: center_lat,
alt: alt,
});
const text = `${Cesium.Cartesian3.distance(before, after).toFixed(2)} m`;
let label_entity = state.cesiumViewer.entities.add({
id: `label_${before}`,
position: position,
label: {
text: text,
scale: 0.5,
font: "bold 30px Microsoft YaHei",
fillColor: Cesium.Color.fromCssColorString("#fff"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
showBackground: true,
backgroundPadding: new Cesium.Cartesian2(10, 10),
},
});
return label_entity;
},
/**
* 清除航线
* @param {*} param0
* @param {object} data
* @param {number} data.id
*/
clearAirwayEntities({ state, commit }, data) {
if (data?.id) {
let find;
// 过滤掉要清除的航线对象集合
let list = state.airwayEntities.filter((item) => {
if (item.airwayId === data.id) {
find = item;
return false;
}
return true;
});
if (find) {
find.values.forEach((entity) => {
state.cesiumViewer.entities.remove(entity);
});
find.removeAll();
}
commit("setState", {
key: "airwayEntities",
value: list,
});
} else {
state.airwayEntities.forEach((item) => {
item.values.forEach((entity) => {
state.cesiumViewer.entities.remove(entity);
});
item.removeAll();
});
}
},
/**
* 创建航线
* @param {} param0
* @param {Object} data
* @param {number} data.id
* @param {array} data.polyline 航线数据
*/
createAirwayEntities({ state, dispatch }, data) {
const { polyline } = data;
let pointPositions = [];
const label_arr = [];
// 实体集合
var entityCollection = new Cesium.EntityCollection();
entityCollection.airwayId = data.id; // 由于id属性不允许更改,这里使用airwayId来使用
polyline.forEach(async (item, index) => {
item.longitude = Number(item.longitude);
item.latitude = Number(item.latitude);
item.alt = Number(item.alt);
pointPositions.push(item.longitude, item.latitude, item.alt);
label_arr.push(
Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude, item.alt)
);
let point_entity = state.cesiumViewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(
item.longitude,
item.latitude,
item.alt
),
name: "show_airline_point",
point: {
pixelSize: 20,
color: Cesium.Color.RED,
color: Cesium.Color.fromCssColorString("#1890FF"),
// fillColor: Cesium.Color.RED,
outlineWidth: 2,
outlineColor: Cesium.Color.fromCssColorString("#FFF"),
// heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
},
label: {
// text: String(item.altitude) + 'm',
text: String(index + 1),
scale: 0.5,
font: "bold 24px Microsoft YaHei",
// fillColor: Cesium.Color.BLUE,
fillColor: Cesium.Color.fromCssColorString("#fff"),
horizontalOrigin: Cesium.VerticalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
outlineWidth: 0,
// showBackground: true,
// backgroundColor: Cesium.Color.fromCssColorString("#1890FF")
// pixelOffset: new Cesium.Cartesian2(25, -10),
// backgroundPadding: new Cesium.Cartesian2(10, 10)
},
});
entityCollection.add(point_entity);
if (label_arr.length > 1) {
const before = label_arr[label_arr.length - 2];
const after = label_arr[label_arr.length - 1];
let labelEntity = await dispatch("createDistanceLabel", {
before,
after,
});
entityCollection.add(labelEntity);
}
});
pointPositions =
Cesium.Cartesian3.fromDegreesArrayHeights(pointPositions);
const redLine = state.cesiumViewer.entities.add({
name: "Red line on terrain",
polyline: {
positions: new Cesium.CallbackProperty(() => {
return pointPositions;
}, false),
width: 4,
material: Cesium.Color.fromCssColorString("#1890FF"),
},
});
entityCollection.add(redLine);
state.cesiumViewer.flyTo(redLine);
state.airwayEntities.push(entityCollection);
},
},
getters: {
//地面站websocket url
......@@ -84,12 +250,13 @@ export default {
if (!state.devMode) {
return "wss://fkzx.mmcuav.cn:8884/mqtt";
} else {
// return "wss://test.fkzx.mmcuav.cn:8884/mqtt";
return "wss://fkzx.mmcuav.cn:8884/mqtt";
return "wss://test.fkzx.mmcuav.cn:8884/mqtt";
// return "wss://fkzx.mmcuav.cn:8884/mqtt";
}
},
},
modules: {
uav,
hangar,
},
};
import Vue from "vue";
import { Control_API } from "../api";
import mount from "../components/mount";
if (!window.$mmc) {
window.$mmc = {};
}
window.$mmc.vue = Vue;
let positions = []; // 飞机走过的点, 会一直累计, 每n秒减半一次, 防止爆内存
setInterval(() => {
if (positions.length > 1000) {
......@@ -58,8 +52,6 @@ const uavRealTimeData = {
flyTime: 0, // 获取飞行时长,只记录飞机起飞到飞机降落的时间单位(毫秒),下次飞机起飞清零
groundSpeed: 0, // hud地速,单位m/s, 飞行速度
velocityZ: 0, // 使用NED(北-东-下)坐标系,飞机在z方向上的当前速度,以米/秒为单位, 爬升率
voltage: 0, // 返回当前电池电压(V)
chargeRemaining: 0, // 电池中的剩余能量百分比
rcChannelState: 0, // 0摇杆处于中位,-99摇杆未连接,-98为外场摇杆权限,1与-1俯仰,2与-2横滚,3与-3油门,4与-4偏航(大于0表示该通道处于高位,小于0表示该通道处于低位)
obstacle: null /* {
// 避障信息(#272)
......@@ -141,14 +133,32 @@ const uavRealTimeData = {
warningLevel: "NOTICE",
},
},
batteryList: [
// 无人机电池信息, 会有多个电池的情况
/* {
id: -1, // 电池id,0普通电池,52左智能电池,53右智能电池
active: 0, // 电池激活类型(鹰巢无人机,这个参数才生效)
chargeRemaining: -1, // 电池中的剩余能量百分比
current: 0, // 电池的实时电流消耗(A)。负值表示电池正在放电,正值表示正在充电
cycleIndex: -1, // 充电循环次数
fullChargeCapacity: -1, // 电池完全充电时存储在电池中的总电量 100%
lowVoltWarnValue: -1, // 低电压报警阈值(V)
temperature: -1, // 电池温度(℃)
voltage: -1, // 返回当前电池电压(V)
statusType: 3, // 电池状态 0无效值,1开机,2充电中,3关机
}, */
],
msg: { // 飞控应答消息
code: 0,
cmd: 0,
text: "",
},
msgList: [], // 飞控应答消息记录
};
const state = {
openTest: false, // 开启测试
cesiumViewer: null, // cesium的viewer实例
uav: null, // 选择中的无人机信息
uavLog: null,
// 无人机的实时采集数据
uavRealTimeData,
airlineEntity: null, // 航线实例
uavModelEntity: null, // 飞机模型实例
......@@ -164,16 +174,22 @@ const state = {
}, */
], // 挂载列表
selectMount: null, // 选中的挂载
// 任务列表数据
TaskList: {},
ygValue: null,
FlyModelFlag: true, // 飞行模式控制
getPageBasicPropertyInfosById: null, // 资源id
isPreventClick: false, // 是否可点击
mapRadio: 1, // 分屏模式时 代表当前控制的是第几个地图实例
dimensionReset: null, //关闭三维标注弹框,
showPlayer: false, //显示播放器
showPanel: false, //显示数据面板
// 任务列表
taskList: [
/* {
name: "任务",
id: 1,
children: [],
airway: {
name: "航线名称1",
id: 1,
content:
'{"filename":"肇庆航线20240318","line":{"baseSpeed":3},"content":[{"uuid":"1nmI-Fo18IagbcVJsia7Q","latitude":23.178153411812204,"longitude":112.57807281336807,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]},{"uuid":"9pTbBPlF8iIwbUNqusyHK","latitude":23.17783116525969,"longitude":112.57797543441967,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]},{"uuid":"s91IhN22wuaeyG-UQs0XR","latitude":23.17786413506686,"longitude":112.57824336604547,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]},{"uuid":"xS_JIl3wxQrhMPdpcjcSn","latitude":23.17820934975604,"longitude":112.5781357731637,"alt":100,"yawAngle":0,"pitchAngle":0,"speed":3,"actions":[]}],"baseSpeed":3,"gimbalYaw":0,"gimbalPitch":0,"alt":100}',
},
}, */
],
};
const mutations = {
......@@ -201,7 +217,6 @@ const actions = {
window.$mmc.viewer.entities.remove(state.airlineEntity);
window.$mmc.viewer.entities.remove(state.uavModelEntity);
commit("setState", { key: "uav", value: null });
commit("setState", { key: "uavLog", value: null });
commit("setState", { key: "airlineEntity", value: null });
commit("setState", { key: "uavModelEntity", value: null });
commit("setState", { key: "airlineData", value: null });
......@@ -209,9 +224,6 @@ const actions = {
commit("setState", { key: "mountList", value: [] });
commit("setState", { key: "selectMount", value: null });
commit("setState", { key: "showVideo", value: false });
commit("setState", { key: "showViewLibrary", value: false });
commit("setState", { key: "showAIVideo", value: false });
commit("setState", { key: "showMenu", value: false });
commit("setState", { key: "uavRealTimeData", value: uavRealTimeData });
positions = [];
......@@ -277,6 +289,10 @@ const actions = {
const type2017 = data[2017]?.data;
const type272 = data[272]?.data; // 避障信息
const type275 = data[275]?.data; // 健康管理
const type270 = data[270]?.data || {}; //飞控应答消息
let msgList = state.msgList || [];
msgList.push(type270);
// gps 需要判断使用哪个
if (type258.rtk?.isMainSensor) {
......@@ -284,7 +300,7 @@ const actions = {
}
// 257返回单次电池信息, 实际飞机可能有多个电池, 但是每次只返回其中一个信息, 通过id判断来组成电池列表
let batteryList = state.uavRealTimeData?.batteryList || [];
if (type257) {
if (type257?.id) {
let findIndex = -1;
batteryList.find((item, i) => {
if (item.id === type257.id) {
......@@ -309,6 +325,8 @@ const actions = {
obstacle: type272 || state.uavRealTimeData.obstacle,
healthData: type275 || state.uavRealTimeData.healthData,
batteryList,
msg: type270,
msgList,
},
});
......@@ -344,7 +362,7 @@ const actions = {
pitch: uavInfo.pitch, // 飞机的横滚值:正向为正,反向为负。单位为度。
yaw: uavInfo.yaw, // 飞机的偏航值,其中0对应于正北航向。顺时针偏航会增加偏航值。
},
accelerator: 0, // 飞机油门通道值
accelerator: 0, // 飞机油门通道值, 地面站链路获取不到
locationCoordinate3D: {
// 地理位置信息
altitude: uavInfo.height, // 相对高度,如果需要获取绝对高度 请查看Gps类
......@@ -415,17 +433,43 @@ const actions = {
*/
subscribe({ state, dispatch }) {
if (state.uav.network === 1) {
window.$mmc.store.dispatch("MMCMQTT/subscribe", {
window.$mmc.$store.dispatch("MMCMQTT/subscribe", {
topic: "PX4/RECEIVE/" + state.uav.hardId,
callback(ok) {
ok && console.log("mqtt订阅主题", "PX4/RECEIVE/" + state.uav.hardId);
},
});
} else {
window.$mmc.store.dispatch(
"MMCGroundStation/subscribe",
state.uav.hardId
);
// 由于ws连接到地面站是使用广播形式获取无人机数据的,所以在组件创建时先连接ws,后续选择无人机时,通过广播数据,筛选出选择的无人机信息出来
try {
const username =
window.$mmc.$store.state.MMCFlightControlCenter.userInfo.userName;
const token = window.$mmc.$store.state.MMCFlightControlCenter.token;
window.$mmc.$store
.dispatch("MMCGroundStation/init", {
url: window.$mmc.$store.getters["MMCFlightControlCenter/wsUrl"],
userInfo: {
type: 100,
systemCode: "mmc",
state: 1,
username,
token,
appId: "40003",
},
loginCallback() {
// 订阅取数据不一定有用, ws的订阅需要后端做了处理才生效, 未处理则会收到所有无人机的数据, 负担非常大
window.$mmc.$store.dispatch(
"MMCGroundStation/subscribe",
state.uav.hardId
);
},
})
.then(() => {
console.log("地面站连接成功");
});
} catch (e) {
console.log("地面站连接失败", e);
}
}
},
/**
......@@ -434,17 +478,33 @@ const actions = {
*/
unsubscribe({ state, dispatch }) {
if (state.uav.network === 1) {
window.$mmc.store.dispatch("MMCMQTT/unsubscribe", state.uav.hardId);
window.$mmc.$store.dispatch("MMCMQTT/unsubscribe", {
topic: "PX4/RECEIVE/" + state.uav.hardId,
callback(ok) {
ok &&
console.log("mqtt取消订阅主题", "PX4/RECEIVE/" + state.uav.hardId);
},
});
} else {
window.$mmc.store.dispatch(
// 地面站取消订阅后需要关闭ws连接
window.$mmc.$store.dispatch(
"MMCGroundStation/unsubscribe",
state.uav.hardId
);
window.$mmc.$store.dispatch("MMCGroundStation/end");
}
dispatch("destroy");
},
/**
* 断开ws连接
* @param {*} param0
*/
end({ state, dispatch }) {
window.$mmc.$store.dispatch("MMCMQTT/end");
window.$mmc.$store.dispatch("MMCGroundStation/end");
},
/**
* 创建飞机模型
* @param {*} param0
* @param {*} data
......@@ -546,10 +606,10 @@ const actions = {
});
// 上传航线指令
const waypointList = JSON.parse(state.airlineData?.content);
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.航线上传,
type: window.$mmc.$store.state.MMCMQTT.orders.航线上传,
data: {
taskId: state.airlineData.id,
flightSortiesID: getFlightSortic.data,
......@@ -565,10 +625,10 @@ const actions = {
});
// 告诉飞控开始任务,并且把架次号和 任务id传过去
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.绑定任务id,
type: window.$mmc.$store.state.MMCMQTT.orders.绑定任务id,
data: {
taskId: state.airlineData.id,
flightSortiesID: getFlightSortic.data,
......@@ -581,10 +641,10 @@ const actions = {
// 起飞指令
setTimeout(() => {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.航线一键起飞,
type: window.$mmc.$store.state.MMCMQTT.orders.航线一键起飞,
data: {
taskId: state.airlineData.id,
seq: 0,
......@@ -601,7 +661,7 @@ const actions = {
}
} else {
// 地面站链路
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "航线上传",
data: {
cmdValue: state.airlineData?.id,
......@@ -611,7 +671,7 @@ const actions = {
// 起飞指令
setTimeout(() => {
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "起飞",
data: {},
deviceHardId: state.uav.hardId,
......@@ -629,7 +689,7 @@ const actions = {
if (state.uav.network == 1) {
try {
// 结束航线指令
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: 525,
......@@ -646,7 +706,7 @@ const actions = {
} else {
try {
// //地面站链路
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "结束平台任务",
data: {
cmdFunction: 2280,
......@@ -661,7 +721,7 @@ const actions = {
// //起飞指令
// setTimeout(() => {
// window.$mmc.store.dispatch("MMCGroundStation/order", {
// window.$mmc.$store.dispatch("MMCGroundStation/order", {
// order: "起飞",
// data: {},
// deviceHardId: state.uav.hardId,
......@@ -677,10 +737,10 @@ const actions = {
*/
land({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.原地降落,
type: window.$mmc.$store.state.MMCMQTT.orders.原地降落,
},
callback() {
data?.callback && data.callback(true);
......@@ -688,7 +748,7 @@ const actions = {
});
} else {
// 地面站链路
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "降落",
data: {},
deviceHardId: state.uav.hardId,
......@@ -703,10 +763,10 @@ const actions = {
*/
pauseFly({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.暂停航线任务,
type: window.$mmc.$store.state.MMCMQTT.orders.暂停航线任务,
},
callback() {
data?.callback && data.callback(true);
......@@ -714,7 +774,7 @@ const actions = {
});
} else {
// 地面站链路
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "悬停",
data: {},
deviceHardId: state.uav.hardId,
......@@ -729,10 +789,10 @@ const actions = {
*/
continueFly({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.继续航线任务,
type: window.$mmc.$store.state.MMCMQTT.orders.继续航线任务,
},
callback() {
data?.callback && data.callback(true);
......@@ -740,7 +800,7 @@ const actions = {
});
} else {
// 地面站链路
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "航线模式",
data: {},
deviceHardId: state.uav.hardId,
......@@ -755,10 +815,10 @@ const actions = {
*/
returnFlight({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.返航,
type: window.$mmc.$store.state.MMCMQTT.orders.返航,
},
callback() {
data?.callback && data.callback(true);
......@@ -766,7 +826,7 @@ const actions = {
});
} else {
// 地面站链路
window.$mmc.store.dispatch("MMCGroundStation/order", {
window.$mmc.$store.dispatch("MMCGroundStation/order", {
order: "返航",
data: {},
deviceHardId: state.uav.hardId,
......@@ -781,10 +841,10 @@ const actions = {
*/
routeControl({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.航线控制,
type: window.$mmc.$store.state.MMCMQTT.orders.航线控制,
data: {
latitude: data.latitude,
longitude: data.longitude,
......@@ -807,10 +867,10 @@ const actions = {
*/
modeManual({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.无人机模式切换,
type: window.$mmc.$store.state.MMCMQTT.orders.无人机模式切换,
data: "POSITION",
},
callback() {
......@@ -827,10 +887,10 @@ const actions = {
*/
modeAuto({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.无人机模式切换,
type: window.$mmc.$store.state.MMCMQTT.orders.无人机模式切换,
data: "AUTO_MISSION",
},
callback() {
......@@ -854,7 +914,7 @@ const actions = {
"xxx"
);
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: 514,
......@@ -871,10 +931,10 @@ const actions = {
*/
modeLAND({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.无人机模式切换,
type: window.$mmc.$store.state.MMCMQTT.orders.无人机模式切换,
data: "LAND",
},
callback() {
......@@ -891,7 +951,7 @@ const actions = {
*/
changeYGPermissions({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: 534,
......@@ -918,10 +978,10 @@ const actions = {
*/
modeAUTO_RTL({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.无人机模式切换,
type: window.$mmc.$store.state.MMCMQTT.orders.无人机模式切换,
data: "AUTO_RTL",
},
callback() {
......@@ -936,10 +996,10 @@ const actions = {
*/
modeAUTO_MISSION({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.无人机模式切换,
type: window.$mmc.$store.state.MMCMQTT.orders.无人机模式切换,
data: "AUTO_MISSION",
},
callback() {
......@@ -955,10 +1015,10 @@ const actions = {
*/
modePOSITION({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.无人机模式切换,
type: window.$mmc.$store.state.MMCMQTT.orders.无人机模式切换,
data: "POSITION",
},
callback() {
......@@ -999,7 +1059,7 @@ const actions = {
streamData.data.messageID = 1006;
streamData.data.data.recordControl = data.recordControl;
}
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: streamData,
callback() {
......@@ -1026,7 +1086,7 @@ const actions = {
// streamData.data.messageID = 1006;
// streamData.data.data.recordControl = this.record;
// }
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: streamData,
callback() {
......@@ -1063,7 +1123,7 @@ const actions = {
} else {
streamData.data.messageID = 1007;
}
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: streamData,
callback() {
......@@ -1081,10 +1141,10 @@ const actions = {
*/
mountDirective({ state }, data) {
if (state.uav.network == 1) {
window.$mmc.store.dispatch("MMCMQTT/publish", {
window.$mmc.$store.dispatch("MMCMQTT/publish", {
topic: "PX4/OBTAIN/" + state.uav.hardId,
data: {
type: window.$mmc.store.state.MMCMQTT.orders.云台控制指令can包透传,
type: window.$mmc.$store.state.MMCMQTT.orders.云台控制指令can包透传,
data: {
mountId: data.mountId,
payload: data.buffer,
......@@ -1097,7 +1157,7 @@ const actions = {
} else {
// 地面站链路
const buff = data.buffer.join(",");
window.$mmc.store.dispatch("MMCGroundStation/publish", {
window.$mmc.$store.dispatch("MMCGroundStation/publish", {
type: 200,
cmdFunction: null,
data: {
......@@ -1245,7 +1305,7 @@ const actions = {
let res = await Control_API.uavDetail({ id: data?.id || state.uav.id });
if (
res.data.currentOperator !==
window.$mmc.store.state.MMCFlightControlCenter.userInfo.id
window.$mmc.$store.state.MMCFlightControlCenter.userInfo.id
) {
return false;
}
......
......@@ -50,47 +50,14 @@ export default {
* @param {string} param1.userInfo.username //用户名
* @param {string} param1.userInfo.token //用户token
* @param {string} param1.userInfo.appId //用户的appId
* @returns
* @param {Function} param1.loginCallback //登录成功回调
* @returns ws连接成功返回
*/
init({ commit, state }, { url, userInfo }) {
init({ commit, state }, { url, userInfo, loginCallback }) {
const ws = new WebSocket(url);
commit("setWs", ws);
commit("setUserInfo", userInfo);
ws.onmessage = (e) => {
let metadata = {};
try {
metadata = JSON.parse(e.data);
} catch (err) {
// console.log('ws:', e.data) //跳过心跳包
return;
}
if (metadata.deviceData) {
try {
if (typeof metadata.deviceData == 'string'){
metadata.deviceData = JSON.parse(metadata.deviceData);
}
const deviceHardId = metadata.deviceData.deviceHardId;
if (state.include.includes(deviceHardId)) {
if (!state.dataSet[deviceHardId]) {
state.dataSet[deviceHardId] = {};
}
state.dataSet[deviceHardId] = metadata;
commit("setDataSet", state.dataSet);
}
} catch (e) {
console.log(e);
}
}
state.callbackList.forEach((cb) => {
cb(metadata);
});
};
ws.onclose = (data) => {
console.log("onclose", data);
};
......@@ -110,6 +77,43 @@ export default {
console.log("error", error);
reject(error);
};
ws.onmessage = (e) => {
let metadata = {};
try {
metadata = JSON.parse(e.data);
} catch (err) {
// console.log('ws:', e.data) //跳过心跳包
return;
}
//登录成功事件
if (metadata.msgnum == 4118) {
loginCallback && loginCallback();
}
if (metadata.deviceData) {
try {
if (typeof metadata.deviceData == "string") {
metadata.deviceData = JSON.parse(metadata.deviceData);
}
const deviceHardId = metadata.deviceData.deviceHardId;
if (state.include.includes(deviceHardId)) {
if (!state.dataSet[deviceHardId]) {
state.dataSet[deviceHardId] = {};
}
state.dataSet[deviceHardId] = metadata;
commit("setDataSet", state.dataSet);
}
} catch (e) {
console.log(e);
}
}
state.callbackList.forEach((cb) => {
cb(metadata);
});
};
});
},
/**
......
......@@ -2,7 +2,7 @@
<div>
<el-form>
<el-form-item label="环境">
<el-switch v-model="devMode" active-text="测试环境" inactive-text="正式环境" @change="onSwitch"></el-switch>
<el-switch v-model="devMode" active-text="测试环境" inactive-text="正式环境"></el-switch>
</el-form-item>
<el-form-item label="场景">
<el-switch v-model="scene" active-text="无人机" inactive-text="机库"></el-switch>
......@@ -26,10 +26,15 @@
v-if="userInfo"
:userInfo="userInfo"
:devMode="devMode"
:taskList="taskList"
:uavTaskList="uavTaskList"
:hangarTaskList="hangarTaskList"
:airwayList="airwayList"
@startTask="onStartTask"
:scene="scene ? 0 : 1"
@uavStartTask="onUAVStartTask"
@changeHangar="onChangeHangar"
@changeHangarTaskTab="onChangeHangarTaskTab"
@hangarStartTask="hangarStartTask"
@addHangarTask="onAddHangarTask"
></MMCFlightControlCenter>
</div>
</div>
......@@ -43,12 +48,12 @@ export default {
data() {
return {
baseUrl: "https://test.tmj.mmcuav.cn",
devMode: true,
devMode: false,
scene: false, // 场景类型 true: 无人机 false: 机库
account: "mmctest@admin",
password: "test@Admin001",
userInfo: null,
taskList: [
uavTaskList: [
{
name: "任务1",
id: 1,
......@@ -82,6 +87,45 @@ export default {
},
},
],
hangarTaskList: {
// 常态
normal: [
/*
{
name: "任务1",
id: 1,
airwayId: 105,
},
{
name: "任务2",
id: 2,
airwayId: 106,
}, */
],
// 定时
timed: [
/* {
name: "任务",
id: 1,
time: "2024-04-28 01:00:00", //定时与周期的时间
status: "未执行", //任务状态
airwayId: 105,
}, */
],
// 周期
period: [
/* {
name: '任务',
id: 1,
time: '', //定时与周期的时间
status: '', 任务状态
airway: {
name: '航线名称',
id: 1
}
} */
],
},
airwayList: [
{
name: "航线名称1",
......@@ -107,23 +151,109 @@ export default {
};
},
computed: {},
watch: {
devMode: {
immediate: true,
handler() {
if (this.devMode) {
this.account = "mmctest@admin";
this.password = "test@Admin001";
this.baseUrl = "https://test.tmj.mmcuav.cn";
} else {
this.account = "mmcadmin@kbt001";
this.password = "TMJMMC@kbta0227&adm";
this.baseUrl = "https://tmj.mmcuav.cn";
}
},
},
},
async mounted() {},
methods: {
onSwitch() {
if (this.devMode) {
this.account = "mmctest@admin";
this.password = "test@Admin001";
this.baseUrl = "https://test.tmj.mmcuav.cn";
} else {
this.account = "mmcadmin@kbt001";
this.password = "TMJMMC@kbta0227&adm";
this.baseUrl = "https://tmj.mmcuav.cn";
}
},
onStartTask(uav, selectedTask, selectedAirway) {
onUAVStartTask(uav, selectedTask, selectedAirway) {
console.log("一键任务事件", uav, selectedTask, selectedAirway);
// alert("一键任务事件");
},
/**
* 更换机库事件
*/
onChangeHangar(hangar) {
console.log("onChangeHangar");
if (hangar) {
this.hangarTaskList = {
// 常态
normal: [
{
name: "任务1",
id: 1,
airwayId: 105,
},
{
name: "任务2",
id: 2,
airwayId: 106,
},
],
// 定时
timed: [
{
name: "任务",
id: 1,
time: "2024-04-28 01:00:00", //定时与周期的时间
status: "未执行", //任务状态
airwayId: 105,
},
],
// 周期
period: [
/* {
name: '任务',
id: 1,
time: '', //定时与周期的时间
status: '', 任务状态
airway: {
name: '航线名称',
id: 1
}
} */
],
};
}
},
/**
* 机库任务添加事件
* @param {Object} param
* @param {number} param.type 1: 日常任务 2: 定时任务 3:周期任务
* @param {Object} param.airway 航线数据
* @param {Function} params.callback 回调,参数为更新后新增的任务id
*/
onAddHangarTask({ type, airway, callback }) {
console.log("onAddHangarTask", type, airway);
let id = Date.now();
this.hangarTaskList.normal.push({
name: "任务" + id,
id: id,
airwayId: airway.id,
});
callback(id);
},
/**
* 机库任务tab更改事件
* @param {object} param
* @param {number} param.type 1: 日常任务 2: 定时任务 3:周期任务
*/
onChangeHangarTaskTab({ type }) {
console.log("onChangeHangarTaskTab", type);
},
/**
* 机库开始任务
* @param {object} param
* @parma {number} param.type 1: 日常任务 2: 定时任务 3:周期任务
* @param {object} param.task 任务对象
* @param {object} param.airway 航线信息
*/
hangarStartTask({ type, hangar, task, airway }) {
console.log("hangarStartTask", type, hangar, task, airway);
},
async login() {
let formData = new FormData();
formData.append("userAccount", this.account);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论