Compare commits

...

2 Commits

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

@ -0,0 +1,60 @@
<template>
<div class="titlesbox">所属项目<span class="cursor">{{ `${props.controlItems.workSpaceName}` || '--' }}</span></div>
<div class="contentBox">
<JiChang :deviceInfoList="props.deviceInfoList" :controlSN="props.controlSN" :controlItems="props.controlItems" @openWainInfo="openWainInfo" />
</div>
<div class="contentBox">
<FeiXingQi :deviceInfoList="props.deviceInfoList" :controlSN="props.controlSN"
:controlChildSN="props.controlChildSN" :controlItems="props.controlItems.uavList[0]" @openWainInfo="openWainInfo" />
</div>
<div class="contentBox">
<YuanChengTiaoShi :deviceInfoList="props.deviceInfoList" :controlSN="props.controlSN" :controlChildSN="props.controlChildSN" />
</div>
</template>
<script setup lang="ts">
import { defineProps } from "vue"
import JiChang from './widget/JiChang.vue'
import FeiXingQi from './widget/FeiXingQi.vue'
import YuanChengTiaoShi from './widget/YuanChengTiaoShi.vue'
const emits = defineEmits(['openWainInfo'])
const props = defineProps(['deviceInfoList', 'controlSN', 'controlChildSN', 'controlItems'])
const openWainInfo = () =>{
emits('openWainInfo')
}
</script>
<style lang="scss" scoped>
.cursor{
cursor: pointer;
}
.flex{
display: flex;
}
.ai-c{
align-items: center;
}
.jc-c{
justify-content: center;
}
.titlesbox{
position: absolute;
left: 110px;
top: 20px;
color:#595959;
span{
color: #2d8cf0;
}
}
.contentBox{
border-radius: 2px;
margin-bottom: 16px;
background: #fff;
padding: 8px 0 24px 8px;
}
</style>

@ -0,0 +1,401 @@
<template>
<div class="flex sectionbox ai-c">
<div class="title">飞行器</div>
<div class="line"></div>
<div class="status flex ai-c">
<div class="pointer"></div>
<div class="color1">飞行器已连接</div>
</div>
<div class="line"></div>
<div class="descbox cursor">
<span> N/A </span>
<div class="hoverbox flex column">
<div class="flex ai-c jc-c column statusbox">
<FileTextOutlined :style="{fontSize: '20px'}" />
<div>当前状态正常</div>
</div>
<div class="flex jc-sb fz-12">
<span>24h 历史告警</span>
<span class="cursor beforewarn" @click="openWainInfo"></span>
</div>
<div class="flex ai-c jc-c column statusbox">
<FileTextOutlined :style="{fontSize: '20px'}" />
<div>暂无历史告警</div>
</div>
</div>
</div>
</div>
<div class="flex">
<div class="leftbox">
<div class="flex commonbox column ai-c pb-1">
<img class="img" src="/public/operation/feixingqi.png" />
<div class="textbox"> {{ props.controlItems.typeId }} </div>
<div class="textbox"> 飞行器名称: {{ props.controlItems.name }} </div>
<div class="textbox"> 飞行器SN: <span>{{fxqsn}}</span>
<a-button size="small" type="text" class="cursor" @click="copyToClipboard">
<template #icon><CopyOutlined /></template>
</a-button>
</div>
<div class="textbox"> 电池SN: -- </div>
</div>
<div class="commonbox heightbox flex jc-sb ai-c">
<div class="fw-b">保养服务</div>
<div class="textbox cursor" title="距下次常规保养496航时/326天"> 496航时/326 <RightOutlined /> </div>
</div>
<div class="commonbox heightbox flex jc-sb ai-c">
<div class="fw-b">行业无忧</div>
<div class="textbox cursor" title="行业无忧到期日2026-06-29"> 2026-06-29 <RightOutlined /> </div>
</div>
</div>
<div class="flex-1">
<div class="flex ai-c device-container">
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ props.deviceInfoList[props.controlChildSN]?.wind_speed || '--' }}m/s</div>
<div> 累计飞行时长 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ props.deviceInfoList[props.controlChildSN]?.total_flight_sorties || '--' }}</div>
<div> 飞行架次 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div"> -- </div>
<div> 图传 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ isFixedOptions[props.deviceInfoList[props.controlChildSN]?.position_state?.is_fixed] || '--' }}</div>
<div> 搜星状态 </div>
</div>
</div>
<div class="flex middlebox">
<div class="flex-1 flex column ai-c jc-c">
<div class="fw-b valbox">电池</div>
<div></div>
</div>
<div class="flex-1 flex column ai-c jc-c">
<div class="valbox">{{ props.deviceInfoList[props.controlChildSN]?.backup_battery.switch || '--' }}</div>
<div>循环次数</div>
</div>
<div class="flex-1 flex column ai-c jc-c">
<div class="valbox">{{ props.deviceInfoList[props.controlChildSN]?.battery_store_mode || '--' }}</div>
<div>高电量存储</div>
</div>
<div class="flex-1 flex column ai-c jc-c">
<div class="valbox">{{ props.deviceInfoList[props.controlChildSN]?.backup_battery.voltage || '--' }}</div>
<div>电压</div>
</div>
<div class="flex-1 flex column ai-c jc-c">
<div class="valbox">{{ props.deviceInfoList[props.controlChildSN]?.backup_battery.temperature || '--' }}</div>
<div>温度</div>
</div>
<div class="flex-1 flex column ai-c jc-c">
<div class="valbox"> -- </div>
<div>电量</div>
</div>
</div>
<div class="flex wrap jc-sb ml-1 mt-1 mr-1">
<div class="flex ai-c jc-sb feixingqibox">
<div>飞行器夜航灯</div>
<div class="fw-b color2">{{ nightLightsStateOptions[props.deviceInfoList[props.controlChildSN]?.night_lights_state] || '--' }}</div>
</div>
<div class="flex ai-c jc-sb feixingqibox">
<div class="flex ai-c">
<span>备降转移高</span>
<div class="iconsbox cursor">
<InfoCircleOutlined />
<div class="hoverbox">
<div>备降转移高20 - 100m是飞行器从机场上方飞向备降点时相对机场的高度</div>
<img class="img" src="/public/operation/bgimg1.png" />
</div>
</div>
</div>
<div class="fw-b color2">{{ props.deviceInfoList[props.controlSN]?.alternate_land_point?.safe_land_height || '--' }}</div>
</div>
<div class="flex ai-c jc-sb feixingqibox">
<div class="flex ai-c">
<span>限高</span>
<div class="iconsbox cursor">
<InfoCircleOutlined />
<div class="hoverbox">
<div>限高是约束飞行器相对机场的最大作业高度限高范围20 - 1500m</div>
</div>
</div>
</div>
<div class="fw-b color2">{{ props.deviceInfoList[props.controlSN]?.height_limit || '--' }}</div>
</div>
<div class="flex ai-c jc-sb feixingqibox">
<div class="flex ai-c">
<span>限远</span>
<div class="iconsbox cursor">
<InfoCircleOutlined />
<div class="hoverbox">
<div>限远15 - 8000m是约束飞行器相对机场的最大作业距离</div>
</div>
</div>
</div>
<div class="fw-b color2">关闭</div>
</div>
<div class="flex ai-c jc-sb feixingqibox">
<div class="flex ai-c">
<span>避障</span>
<div class="iconsbox cursor">
<InfoCircleOutlined />
<div class="hoverbox">
<div>飞行器的避障工作状态显示可以快速开启/关闭飞行器避障</div>
</div>
</div>
</div>
<div class="fw-b color2">{{ (
props.deviceInfoList[props.controlChildSN]?.obstacle_avoidance?.horizon ||
props.deviceInfoList[props.controlChildSN]?.obstacle_avoidance?.upside ||
props.deviceInfoList[props.controlChildSN]?.obstacle_avoidance?.downside
)? '开启': '关闭' }}
</div>
</div>
<div class="flex ai-c jc-sb feixingqibox">
<div class="flex ai-c">
<span>运行模式</span>
<div class="iconsbox cursor">
<InfoCircleOutlined />
<div class="hoverbox">
<div>运行模式即飞行器电池充电管理模式请根据您实际作业场景选择运行模式</div>
</div>
</div>
</div>
<div class="fw-b color2">
{{ childModeCodeOptions[props.deviceInfoList[props.controlChildSN]?.mode_code] || '--' }}
<!-- <EditOutlined class="cursor" :style="{color: '#2d8cf0'}" /> -->
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, onMounted, watch, nextTick } from "vue"
import { RightOutlined, InfoCircleOutlined, CopyOutlined, FileTextOutlined, EditOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { modeCodeOptions, isFixedOptions, nightLightsStateOptions, childModeCodeOptions } from './util'
const props = defineProps(['deviceInfoList', 'controlSN', 'controlChildSN', 'controlItems'])
const emits = defineEmits(['openWainInfo'])
const fxqsn = props.controlChildSN
const copyToClipboard = () => {
try {
navigator.clipboard.writeText(fxqsn.value);
message.success('复制成功!');
} catch (err) {
message.error('复制失败,请重试');
}
}
const openWainInfo = () =>{
emits('openWainInfo')
}
</script>
<style lang="scss" scoped>
.cursor{
cursor: pointer;
}
.flex{
display: flex;
}
.flex-1{
flex: 1;
}
.column{
flex-direction: column
}
.wrap{
flex-wrap: wrap;
}
.ai-c{
align-items: center;
}
.jc-c{
justify-content: center;
}
.jc-sb{
justify-content: space-between
}
.pb-1{
padding-bottom: 15px;
}
.ml-1{
margin-left: 20px;
}
.mt-1{
margin-top: 10px;
}
.mr-1{
margin-right: 30px;
}
.fz-12{
font-size: 12px;
}
.line{
width: 1px;
height: 20px;
background: #e8e8e8;
margin-left: 8px;
}
.color1{
color:#595959;
}
.color2{
color: rgba(0,0,0,.85);
}
.fw-b{
font-weight: 600;
}
.sectionbox{
height: 60px;
.title{
min-width: 42px;
font-weight: 600;
}
.status{
margin-right: 28px;
.pointer{
margin: 0 8px;
width: 12px;
height: 12px;
border-radius: 12px;
background: #0DED57;
}
}
.descbox{
margin-left: 8px;
width: 200px;
height: 20px;
line-height: 20px;
background: #bfbfbf;
color: #fff;
padding-left: 10px;
position: relative;
&:hover .hoverbox{
display: block;
}
.hoverbox{
box-shadow: 0 2px 8px rgba(0,0,0,.15);
padding: 12px;
color: #515a6e;
background: #fff;
width: 204px;
position: absolute;
left: 50%;
top: 25px;
margin-left: -102px;
line-height: 1.5;
z-index: 2;
cursor: default;
display: none;
.statusbox{
padding: 16px 0;
}
.beforewarn{
color: #2d8cf0;
}
&::before {
content: ""; /* 必须要有内容 */
position: absolute;
top: -5px; /* 向上偏移以使其位于容器上方 */
left: 13px; /* 水平居中 */
width: 0;
height: 0;
border-left: 10px solid transparent; /* 左透明边框 */
border-right: 10px solid transparent; /* 右透明边框 */
border-bottom: 10px solid white; /* 底部边框为三角形的高度和颜色 */
}
}
}
}
.leftbox{
width: 260px;
.heightbox{
height: 40px;
padding: 0 5px;
}
.commonbox{
border-bottom: 1px solid #e8e8e8;
.img{
width: 88px;
height: 88px;
margin-bottom: 8px;
}
.textbox{
font-size: 12px;
color: rgba(0,0,0,.45);
text-align: center;
line-height: 20px;
}
}
}
.device-container{
height: 86px;
border-bottom: 1px solid #e8e8e8;
.device-item{
width: 25%;
color: rgba(0,0,0,.45);
.div{
line-height: 20px;
}
}
}
.middlebox{
color: rgba(0,0,0,.45);
height: 50px;
margin-top: 15px;
.valbox{
line-height: 20px;
}
}
.feixingqibox{
color: rgba(0,0,0,.45);
height: 36px;
background: #f7f9fa;
border-radius: 2px;
padding: 8px;
width: 48%;
margin-top: 8px;
.iconsbox{
margin-left: 8px;
position: relative;
&:hover .hoverbox {
display: block;
}
.hoverbox{
box-shadow: 0 2px 8px rgba(0,0,0,.15);
padding: 6px 12px;
color: #515a6e;
background: #fff;
width: 250px;
position: absolute;
left: 50%;
top: 22px;
margin-left: -125px;
line-height: 1.5;
z-index: 2;
display: none;
&::before {
content: ""; /* 必须要有内容 */
position: absolute;
top: -5px; /* 向上偏移以使其位于容器上方 */
left: 50%; /* 水平居中 */
transform: translateX(-50%); /* 根据需要调整以完全居中 */
width: 0;
height: 0;
border-left: 10px solid transparent; /* 左透明边框 */
border-right: 10px solid transparent; /* 右透明边框 */
border-bottom: 10px solid white; /* 底部边框为三角形的高度和颜色 */
}
.img{
width: 200px;
margin-top: 10px;
}
}
}
}
</style>

@ -0,0 +1,310 @@
<template>
<div class="flex sectionbox ai-c">
<div class="title">机场</div>
<div class="line"></div>
<div class="status flex ai-c">
<div class="pointer"></div>
<div class="color1">设备在线</div>
</div>
<div class="line"></div>
<div class="descbox cursor">
<span > N/A </span>
<div class="hoverbox flex column">
<div class="flex ai-c jc-c column statusbox">
<FileTextOutlined :style="{fontSize: '20px'}" />
<div>当前状态正常</div>
</div>
<div class="flex jc-sb fz-12">
<span>24h 历史告警</span>
<span class="cursor beforewarn" @click="openWainInfo"></span>
</div>
<div class="flex ai-c jc-c column statusbox">
<FileTextOutlined :style="{fontSize: '20px'}" />
<div>暂无历史告警</div>
</div>
</div>
</div>
</div>
<div class="flex">
<div class="leftbox">
<div class="flex commonbox column ai-c pb-1">
<img class="img" src="/public/operation/jichang.png" />
<div class="textbox"> {{props.controlItems.typeId }} </div>
<div class="textbox"> 机场名称: {{props.controlItems.name }} </div>
<div class="textbox"> 主控SN: <span>{{zksn}}</span>
<a-button size="small" type="text" class="cursor" @click="copyToClipboard">
<template #icon><CopyOutlined /></template>
</a-button>
</div>
<div class="textbox"> 铭牌SN: <span>{{mpsn}}</span>
<a-button size="small" type="text" class="cursor" @click="copyToClipboard1">
<template #icon><CopyOutlined /></template>
</a-button>
</div>
</div>
<div class="commonbox heightbox flex jc-sb ai-c">
<div class="fw-b">保养服务</div>
<div class="textbox cursor" title="距下次基础保养144天"> 144 <RightOutlined /> </div>
</div>
<div class="commonbox heightbox flex jc-sb ai-c">
<div class="fw-b">行业无忧</div>
<div class="textbox cursor" title="行业无忧到期日2026-06-29"> 2026-06-29 <RightOutlined /> </div>
</div>
</div>
<div class="flex-1 flex wrap">
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${(props.deviceInfoList[props.controlSN]?.acc_time / 60 / 60 / 24).toFixed(1)}` || '--' }}</div>
<div> 累计运行时长 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${props.deviceInfoList[props.controlSN]?.job_number}` || '--' }}</div>
<div> 作业架次 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">
{{ `${(props.deviceInfoList[props.controlSN]?.position_state?.gps_number + props.deviceInfoList[props.controlSN]?.position_state?.rtk_number)}` || '--' }}
</div>
<div> 机场搜星 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ sourceTypeOptions[props.deviceInfoList[props.controlSN]?.rtcm_info?.source_type] || '--'}}</div>
<div> 标定状态 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${(props.deviceInfoList[props.controlSN].working_voltage / 1000).toFixed(1)}V` || '--'}}</div>
<div> 供电电压 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div"> <GlobalOutlined />
{{ qualityOptions[props.deviceInfoList[props.controlSN]?.network_state?.quality] || '--'}}
</div>
<div> 网络 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ isConfiguredOptions[props.deviceInfoList[props.controlSN]?.alternate_land_point?.is_configured] || '--'}}</div>
<div> 备降点 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div"> 未设置 </div>
<div> 进离场航线 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ airConditionerStateOptions[props.deviceInfoList[props.controlSN]?.air_conditioner?.air_conditioner_state] || '--'}}</div>
<div> 空调状态 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${(props.deviceInfoList[props.controlSN]?.drone_battery_maintenance_info?.batteries[0].voltage / 1000).toFixed(1)}V` || '--' }}</div>
<div> 蓄电池电压 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${props.deviceInfoList[props.controlSN]?.drone_battery_maintenance_info?.batteries[0].temperature}` || '--' }}</div>
<div> 蓄电池温度 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${props.deviceInfoList[props.controlSN]?.temperature}` || '--' }}</div>
<div> 舱内温度 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${props.deviceInfoList[props.controlSN]?.humidity}%RH` || '--' }}</div>
<div> 舱内湿度 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${props.deviceInfoList[props.controlSN]?.environment_temperature}` || '--' }}</div>
<div> 舱外温度 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ rainfallOptions[props.deviceInfoList[props.controlSN]?.rainfall] || '--' }}</div>
<div> 雨量 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ `${props.deviceInfoList[props.controlSN]?.wind_speed}m/s` || '--' }}</div>
<div> 风速 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div"> -- </div>
<div> 倾斜角度 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ rainfallOptions[props.deviceInfoList[props.controlSN]?.poe_link_status] || '--' }}</div>
<div> PoE 输出接口 </div>
</div>
<div class="device-item flex ai-c column">
<div class="color2 fw-b div">{{ rainfallOptions[props.deviceInfoList[props.controlSN]?.poe_power_output] || '--' }}</div>
<div> PoE 输出功率 </div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, onMounted, watch, nextTick } from "vue"
import { RightOutlined, DingtalkOutlined, GlobalOutlined, CopyOutlined,FileTextOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { airConditionerStateOptions, silentModeOptions, sourceTypeOptions, qualityOptions, isConfiguredOptions, rainfallOptions } from './util'
const props = defineProps(['deviceInfoList', 'controlSN', 'controlItems'])
const emits = defineEmits(['openWainInfo'])
const zksn = props.controlItems.sn
const mpsn = props.controlItems.gateWay
const copyToClipboard = () => {
try {
navigator.clipboard.writeText(zksn.value);
message.success('复制成功!');
} catch (err) {
message.error('复制失败,请重试');
}
}
const copyToClipboard1 = () => {
try {
navigator.clipboard.writeText(mpsn.value);
message.success('复制成功!');
} catch (err) {
message.error('复制失败,请重试');
}
}
const openWainInfo = () =>{
emits('openWainInfo')
}
</script>
<style lang="scss" scoped>
.cursor{
cursor: pointer;
}
.flex{
display: flex;
}
.flex-1{
flex: 1;
}
.wrap{
flex-wrap: wrap
}
.column{
flex-direction: column
}
.ai-c{
align-items: center;
}
.jc-c{
justify-content: center;
}
.jc-sb{
justify-content: space-between
}
.pb-1{
padding-bottom: 15px;
}
.fz-12{
font-size: 12px;
}
.line{
width: 1px;
height: 20px;
background: #e8e8e8;
margin-left: 8px;
}
.color1{
color:#595959;
}
.color2{
color: rgba(0,0,0,.85);
}
.fw-b{
font-weight: 600;
}
.sectionbox{
height: 60px;
.title{
min-width: 42px;
font-weight: 600;
}
.status{
margin-right: 28px;
.pointer{
margin: 0 8px;
width: 12px;
height: 12px;
border-radius: 12px;
background: #0DED57;
}
}
.descbox{
margin-left: 8px;
width: 200px;
height: 20px;
line-height: 20px;
background: #bfbfbf;
color: #fff;
padding-left: 10px;
position: relative;
&:hover .hoverbox{
display: block;
}
.hoverbox{
box-shadow: 0 2px 8px rgba(0,0,0,.15);
padding: 12px;
color: #515a6e;
background: #fff;
width: 204px;
position: absolute;
left: 50%;
top: 25px;
margin-left: -102px;
line-height: 1.5;
z-index: 2;
cursor: default;
display: none;
.statusbox{
padding: 16px 0;
}
.beforewarn{
color: #2d8cf0;
}
&::before {
content: ""; /* 必须要有内容 */
position: absolute;
top: -5px; /* 向上偏移以使其位于容器上方 */
left: 13px; /* 水平居中 */
width: 0;
height: 0;
border-left: 10px solid transparent; /* 左透明边框 */
border-right: 10px solid transparent; /* 右透明边框 */
border-bottom: 10px solid white; /* 底部边框为三角形的高度和颜色 */
}
}
}
}
.leftbox{
width: 260px;
.heightbox{
height: 40px;
padding: 0 5px;
}
.commonbox{
border-bottom: 1px solid #e8e8e8;
.img{
width: 120px;
height: 120px;
margin-bottom: 8px;
}
.textbox{
font-size: 12px;
color: rgba(0,0,0,.45);
text-align: center;
line-height: 20px;
}
}
}
.device-item{
width: 25%;
height: 80px;
color: rgba(0,0,0,.45);
.div{
line-height: 20px;
}
}
</style>

@ -0,0 +1,477 @@
<template>
<div class="flex jc-sb ai-c controlcontainer">
<div class="flex ai-c">
<span class="mr-1 fw-b color2">远程调试</span>
<a-switch :checked="props.deviceInfoList[props.controlSN].mode_code == 2" @change="openDebug"/>
</div>
</div>
<div class="sectionbox flex column">
<div class="videocontainer flex ai-c">
<div class="mr-1 fw-b color2">机场控制</div>
<a-button size="small" class="ml-4 cursor">
<template #icon><VideoCameraOutlined /></template>
<span class="fz-12">直播</span>
</a-button>
</div>
<div class="flex wrap sectioncontainer">
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<TranslationOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
modeCodeOptions[props.deviceInfoList[props.controlSN].mode_code]
}}</span>
<span class="fz-12 color3">机场系统</span>
</div>
</div>
<a-button size="small" @click="changeState('device_reboot')" :disabled="props.deviceInfoList[props.controlSN].mode_code != 2">重启</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<InsertRowAboveOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
getCoverState(props.deviceInfoList[props.controlSN].cover_state)
}}</span>
<span class="fz-12 color3">舱盖</span>
</div>
</div>
<a-button
type="small"
@click="changeState(props.deviceInfoList[props.controlSN].cover_state == 0? 'cover_open': 'cover_close')"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
{{props.deviceInfoList[props.controlSN].cover_state == 0? '打开': '关闭'}}
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<CloudDownloadOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22"><div class="show-span">
{{
props.deviceInfoList[props.controlSN].air_conditioner && airConditionerStateOptions[props.deviceInfoList[props.controlSN].air_conditioner.air_conditioner_state]
}}
</div></span>
<span class="fz-12 color3">空调</span>
</div>
</div>
<a-button
v-if="props.deviceInfoList[props.controlSN]?.air_conditioner.air_conditioner_state != 0"
type="small"
@click="changeState('air_conditioner_mode_switch',0)"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
停止
</a-button>
<a-dropdown v-else placement="bottom" :disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
<a-button type="small">开启</a-button>
<template #overlay>
<a-menu>
<a-menu-item @click="changeState('air_conditioner_mode_switch',1)"></a-menu-item>
<a-menu-item @click="changeState('air_conditioner_mode_switch',2)"></a-menu-item>
<a-menu-item @click="changeState('air_conditioner_mode_switch',3)">湿</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<AudioMutedOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
silentModeOptions[props.deviceInfoList[props.controlSN].silent_mode]
}}</span>
<span class="fz-12 color3">静音模式</span>
</div>
</div>
<a-button
type="small"
@click=""
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
{{props.deviceInfoList[props.controlSN].silent_mode == 0? '开启': '关闭'}}
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<AlertOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
alarmStateOptions[props.deviceInfoList[props.controlSN].alarm_state]
}}</span>
<span class="fz-12 color3">机场声光报警</span>
</div>
</div>
<a-button
type="small"
@click="changeState('alarm_state_switch',props.deviceInfoList[props.controlSN].alarm_state == 0? 1: 0)"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
{{props.deviceInfoList[props.controlSN].alarm_state == 0? '开启': '关闭'}}
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<FileProtectOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">
{{
props.deviceInfoList[props.controlSN].storage &&
`${(props.deviceInfoList[props.controlSN].storage.used / 1000 / 1024).toFixed(1)}/${(props.deviceInfoList[props.controlSN].storage.total / 1000 / 1024).toFixed(1)}GB`
}}
</span>
<span class="fz-12 color3">机场存储</span>
</div>
</div>
<a-button
type="small"
@click="changeState('device_format')"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
格式化
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<ControlOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">--</span>
<span class="fz-12 color3">机场增强图传</span>
</div>
</div>
<a-button
type="small"
@click=""
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
设置
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<VerifiedOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">解禁证书</span>
<span class="fz-12 color3">限飞解禁证书</span>
</div>
</div>
<a-button
type="small"
@click=""
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
设置
</a-button>
</div>
</div>
</div>
<div class="sectionbox flex column border-t">
<div class="videocontainer flex ai-c">
<div class="mr-1 fw-b color2">飞行器控制</div>
</div>
<div class="flex wrap sectioncontainer">
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<PoweroffOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
deviceOnlineStatusOptions[props.deviceInfoList[props.controlSN]?.sub_device?.device_online_status] || '--'
}}</span>
<span class="fz-12 color3">飞行器电源</span>
</div>
</div>
<a-button
type="small"
@click="changeDeviceState(props.deviceInfoList[props.controlSN]?.sub_device?.device_online_status == 0? 'drone_open': 'drone_close')"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
{{props.deviceInfoList[props.controlSN]?.sub_device?.device_online_status == 0? '开机': '关机'}}
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<ApiOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
droneChargeStateOptions[props.deviceInfoList[props.controlSN]?.drone_charge_state?.state] || '--'
}}</span>
<span class="fz-12 color3">飞行器充电</span>
</div>
</div>
<a-button
type="small"
@click="changeDeviceState(props.deviceInfoList[props.controlSN]?.drone_charge_state?.state == 0? 'charge_open': 'charge_close')"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
{{props.deviceInfoList[props.controlSN]?.drone_charge_state?.state == 0? '充电': '停止'}}
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<Icon icon="mdi:signal-4g" style="font-size: 21px;"/>
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
fourgLinkStateOptions[props.deviceInfoList[props.controlSN]?.wireless_link?.['4g_link_state']] || '--'
}}</span>
<span class="fz-12 color3">增强图传</span>
</div>
</div>
<a-button
type="small"
@click="changeDeviceState('sdr_workmode_switch',props.deviceInfoList[props.controlSN]?.wireless_link?.['4g_link_state'] == 0? 1: 0, 'link_workmode')"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
{{props.deviceInfoList[props.controlSN]?.wireless_link?.['4g_link_state'] == 0? '开启': '关闭'}}
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<FileSyncOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22"> -- </span>
<span class="fz-12 color3">飞行器存储</span>
</div>
</div>
<a-button
type="small"
@click="changeDeviceState('drone_format')"
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
格式化
</a-button>
</div>
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<InteractionOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">{{
linkWorkmodeOptions[props.deviceInfoList[props.controlSN]?.wireless_link?.link_workmode] || '--'
}}</span>
<span class="fz-12 color3">飞行器增强图传</span>
</div>
</div>
<a-button
type="small"
@click=""
:disabled="props.deviceInfoList[props.controlSN].mode_code != 2">
设置
</a-button>
</div>
</div>
</div>
<div class="sectionbox flex column border-t">
<div class="videocontainer flex ai-c">
<div class="mr-1 fw-b color2 flex">
<span>中继站控制</span>
<div class="iconsbox cursor">
<InfoCircleOutlined />
<div class="hoverconatiner">
<div class="hoverbox flex">
<img class="img" src="/public/operation/bgimg2.png" />
<div>可通过中继发出的远程接入信号远程连接中继满足复杂场景下飞行器的图传要求</div>
</div>
</div>
</div>
</div>
</div>
<div class="flex wrap sectioncontainer">
<div class="sectonsitem flex ai-c jc-sb">
<div class="flex ai-c">
<LinkOutlined />
<div class="flex column ml-1">
<span class="fw-b color1 lineh22">未连接</span>
<span class="fz-12 color3">连接状态</span>
</div>
</div>
<a-button size="small" disabled>远程连接</a-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps } from "vue"
import Icon from '@/components/Icon/Icon.vue';
import {
VideoCameraOutlined,
TranslationOutlined,
InsertRowAboveOutlined,
CloudDownloadOutlined,
AudioMutedOutlined,
AlertOutlined,
FileProtectOutlined,
ControlOutlined,
VerifiedOutlined,
PoweroffOutlined,
ApiOutlined,
FileSyncOutlined,
InteractionOutlined,
LinkOutlined,
InfoCircleOutlined
} from '@ant-design/icons-vue';
import { clientPublish } from '@/utils/mqtt'
import { buildGUID } from '@/utils/uuid';
import { modeCodeOptions, deviceOnlineStatusOptions, coverStateOptions, airConditionerStateOptions, silentModeOptions,
alarmStateOptions, droneChargeStateOptions, fourgLinkStateOptions, linkWorkmodeOptions, sourceTypeOptions, qualityOptions,
isConfiguredOptions, rainfallOptions, isFixedOptions, nightLightsStateOptions, childModeCodeOptions } from './util'
const props = defineProps(['deviceInfoList', 'controlSN', 'controlChildSN'])
const openDebug = () => {
let publishUrl = `thing/product/${props.controlSN}/services`
let params = {
method: props.deviceInfoList[props.controlSN].mode_code == 2? 'debug_mode_close': 'debug_mode_open',
data: null,
};
clientPublish(publishUrl,params)
}
const getCoverState = (value) => {
const options = {
0: '关闭',
1: '打开',
2: '半开',
3: '舱盖状态异常'
}
return options[value]
}
const changeState = (method,action?) => {
let publishUrl = `thing/product/${props.controlSN}/services`
let params = {
method: method,
bid: buildGUID(),
tid: buildGUID(),
timestamp: new Date().getTime(),
data: action? { action }: null,
};
clientPublish(publishUrl,params)
}
const changeDeviceState = (method, action?, type?) => {
let publishUrl = `thing/product/${props.controlSN}/services`
let params = {
method,
bid: buildGUID(),
tid: buildGUID(),
timestamp: new Date().getTime(),
data: type? { [type]: action }: action? { action }: null,
};
clientPublish(publishUrl,params)
}
</script>
<style lang="scss" scoped>
.cursor{
cursor: pointer;
}
.flex{
display: flex;
}
.wrap{
flex-wrap: wrap
}
.column{
flex-direction: column
}
.ai-c{
align-items: center;
}
.jc-c{
justify-content: center;
}
.jc-sb{
justify-content: space-between;
}
.jc-sa{
justify-content: space-around;
}
.ml-4{
margin-left: 40px;
}
.ml-1{
margin-left: 6px;
}
.mr-1{
margin-right: 10px;
}
.mr-2{
margin-right: 20px;
}
.fz-12{
font-size: 12px;
}
.color1{
color:#595959;
}
.color2{
color: rgba(0,0,0,.85);
}
.color3{
color: rgba(0,0,0,.45);
}
.fw-b{
font-weight: 600;
}
.border-t{
border-top: 1px solid #e8e8e8;
}
.controlcontainer{
height: 54px;
border-bottom: 1px solid #e8e8e8;
padding: 0 16px;
}
.sectionbox{
padding-bottom: 16px;
.videocontainer{
height: 46px;
margin: 0 16px;
.iconsbox{
margin-left: 8px;
position: relative;
&:hover .hoverconatiner {
display: block;
}
.hoverconatiner{
display: none;
.hoverbox{
box-shadow: 0 2px 8px rgba(0,0,0,.15);
padding: 6px 12px;
color: #515a6e;
background: #fff;
width: 400px;
position: absolute;
left: 50%;
top: -118px;
margin-left: -100px;
line-height: 1.5;
font-weight: 100;
z-index: 2;
&::before {
content: ""; /* 必须要有内容 */
position: absolute;
top: 105px; /* 向上偏移以使其位于容器上方 */
left: 25%; /* 水平居中 */
transform: translateX(-50%); /* 根据需要调整以完全居中 */
width: 0;
height: 0;
border-left: 10px solid transparent; /* 左透明边框 */
border-right: 10px solid transparent; /* 右透明边框 */
border-top: 10px solid #fff; /* 底部边框为三角形的高度和颜色 */
}
.img{
width: 156px;
height: 95px;
margin-right: 8px;
}
}
}
}
}
.sectonsitem{
width: 32%;
padding: 0 8px;
height: 58px;
border-radius: 2px;
background: #f7f9fa;
margin-bottom: 8px;
margin-right: 1%;
.lineh22{
line-height: 22px;
}
}
}
.sectioncontainer{
padding-left: 15px;
margin-top: 8px;
}
</style>

@ -0,0 +1,112 @@
export const modeCodeOptions = {
0: '空闲中',
1: '现场调试',
2: '远程调试',
3: '固件升级中',
4: '作业中',
5: '待标定',
}
export const deviceOnlineStatusOptions = {
0: '关机',
1: '开机'
}
export const coverStateOptions = {
0: '关闭',
1: '打开',
2: '半开',
3: '舱盖状态异常',
}
export const airConditionerStateOptions = {
0: '空闲模式',
1: '制冷模式',
2: '制热模式',
3: '除湿模式',
4: '制冷退出模式',
5: '制热退出模式',
6: '除湿退出模式',
7: '制冷准备模式',
8: '制热准备模式',
9: '除湿准备模式',
10: '风冷准备中',
11: '风冷中',
12: '风冷退出中',
13: '除雾准备中',
14: '除雾中',
15: '除雾退出中',
}
export const silentModeOptions = {
0: '非静音模式',
1: '静音模式',
}
export const alarmStateOptions = {
0: '关闭',
1: '开启',
}
export const droneChargeStateOptions = {
0: '空闲',
1: '充电中',
}
export const fourgLinkStateOptions = {
0: '断开',
1: '连接',
}
export const linkWorkmodeOptions = {
0: 'SDR 模式',
1: '4G 融合模式',
}
export const sourceTypeOptions = {
0: '未标定',
1: '自收敛标定',
2: '手动标定',
3: '网络RTK标定',
}
export const qualityOptions = {
0: '无信号',
1: '差',
2: '较差',
3: '一般',
4: '较好',
5: '好',
}
export const isConfiguredOptions = {
0: '未设置',
1: '已设置',
}
export const rainfallOptions = {
0: '无雨',
1: '小雨',
2: '中雨',
3: '大雨',
}
export const isFixedOptions = {
0: '未开始',
1: '收敛中',
2: '收敛成功',
3: '收敛失败',
}
export const nightLightsStateOptions = {
0: '关闭',
1: '打开',
}
export const childModeCodeOptions = {
0: '待机',
1: '起飞准备',
2: '起飞准备完毕',
3: '手动飞行',
4: '自动起飞',
5: '航线飞行',
6: '全景拍照',
7: '智能跟随',
8: 'ADS-B 躲避',
9: '自动返航',
10: '自动降落',
11: '强制降落',
12: '三桨叶降落',
13: '升级中',
14: '未连接',
15: 'APAS',
16: '虚拟摇杆状态',
17: '指令飞行',
18: '空中 RTK 收敛模式',
19: '机场选址中',
}

@ -57,8 +57,10 @@
<a-drawer class="device-warning" v-model:open="deviceWarningDrawer" title="告警信息" width="70%" :closable="false">
<DeviceWarning />
</a-drawer>
<a-drawer class="feedback-drawer" title="设备运维" v-model:open="deviceControl" width="45%" :closable="false">
<DeviceControl :deviceInfoList="deviceInfoList" :controlSN="controlSN" :controlChildSN="controlChildSN"/>
<a-drawer class="feedback-drawer" title="设备运维" v-model:open="deviceControl" width="45%" style="background:#f0f1f2" :closable="false">
<!-- <DeviceControl :deviceInfoList="deviceInfoList" :controlSN="controlSN" :controlChildSN="controlChildSN"/> -->
<DeviceOperation :deviceInfoList="deviceInfoList" :controlSN="controlSN"
:controlChildSN="controlChildSN" :controlItems="controlItemDetsils" @openWainInfo="deviceWarningDrawer = true" />
</a-drawer>
<a-modal v-model:open="deviceBindingModal" title="设备绑定码" @ok="handleOk">
<DeviceBindModal />
@ -75,6 +77,7 @@ import { columns, searchFormSchema, servicesReplyMessage } from './utils'
import FeedbackDrawer from './FeedbackDrawer/index.vue'
import DeviceBindModal from './DeviceBindModal/index.vue'
import DeviceControl from './DeviceControl/index.vue'
import DeviceOperation from './DeviceOperation/index.vue'
import DeviceEdit from './DeviceEdit/index.vue'
import DeviceWarning from './DeviceWarning/index.vue'
import { GetDataList, DeleteDronePort } from '@/api/demo/device'
@ -95,7 +98,7 @@ watch(() => props.projectList, () => {
})
watch(() => props.connected, () => {
getClient().on('message', (topic, msg) => {
console.log('topic',topic)
// console.log('topic',topic)
if (topic.endsWith("osd")) {
const rs = JSON.parse(msg)
let list = getDataSource()
@ -132,9 +135,10 @@ const controlSN = ref()
const editDeviceModal = ref(false)
const editDeviceDate = ref({})
const controlChildSN = ref()
const controlItemDetsils = ref()
watch(() => [afterFetch.value, props.connected], ([newAfterFetch, newConnected], [oldAfterFetch, oldConnected]) => {
console.log(newAfterFetch,newConnected)
console.log(getDataSource())
// console.log(newAfterFetch,newConnected)
// console.log(getDataSource())
if(newConnected && newAfterFetch){
nextTick(() => {
getDataSource().forEach(item => {
@ -222,6 +226,8 @@ const workStatus = (record) => {
const openDeviceControl = (record) => {
controlSN.value = record.sn
controlChildSN.value = record.uavList[0].sn
controlItemDetsils.value = record
controlItemDetsils.value.workSpaceName = props.projectList.find(item => item.value == record.workSpaceId)?.label
let check = Object.keys(deviceInfoList.value).includes(controlSN.value) && Object.keys(deviceInfoList.value[controlSN.value]).includes('mode_code')
if(!check){
return message.warning('未获取到机场信息')

Loading…
Cancel
Save