徐景良 2 months ago
commit cd1e0cd8ba

@ -0,0 +1,78 @@
class LiveStreamPlugin {
constructor() {
console.log("LiveStreamPlugin constructor");
}
initSDK(token: String) {
/**
* SDKtokenak/sktoken
* 使
*/
console.log("LiveStreamPlugin initSDK", token);
}
setLiveStreamControl(divContainor: HTMLElement, deviceType: number) {
/**
*
* divContainordiv
* deviceType 0 1
*/
console.log("LiveStreamPlugin setLiveStreamControl", divContainor, deviceType);
}
startLiveStreamCall(serialNum: String, deviceType: number) {
/**
*
* serialNum
* deviceType 0 1
*/
console.log("LiveStreamPlugin startLiveStreamCall", serialNum, deviceType);
}
endLiveStreamCall(deviceType: number) {
/**
*
* deviceType 0 1
*/
console.log("LiveStreamPlugin endLiveStreamCall", deviceType);
}
takePicture(callback) {
/**
*
* callback
* issuccess
* true/false
* imgBlobBlob
* zpkzxx
*/
console.log("LiveStreamPlugin takePicture", callback);
}
startVideoRecording() {
/**
*
*/
console.log("LiveStreamPlugin startVideoRecording");
}
endVideoRecording(callback: Function) {
/**
*
* callback
* issuccess
* true/false
*/
console.log("LiveStreamPlugin endVideoRecording", callback);
}
disposeSDK() {
/**
* SDK
*/
console.log("LiveStreamPlugin disposeSDK");
}
getFlightTaskInfo(serialNum: String) {
/**
*
* serialNum
* FlightTaskInfo
*/
console.log("LiveStreamPlugin getFlightTaskInfo", serialNum);
}
}
export default LiveStreamPlugin;

@ -0,0 +1,12 @@
{
"name": "@it/docklivestreamplugin",
"version": "1.0.0",
"description": "该插件js插件包。功能包含SDK初始化、设置视频直播画面控件、发起和结束直播、拍照、视频录制、资源释放。",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

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,40 @@
import { defHttp } from '@/utils/http/axios';
enum Api {
// 获取地图作业区域列表
GetWorkAreaList = '/api/Manage/GetWorkAreaList',
// 添加地图作业区域
AddWorkArea = '/api/Manage/AddWorkArea',
// 更新地图作业区域
UpdateWorkArea = '/api/Manage/UpdateWorkArea',
// 删除地图作业区域
DeleteWorkArea = '/api/Manage/DeleteWorkArea',
}
// 获取地图作业区域列表
export function GetWorkAreaList(params) {
return defHttp.get({
url: Api.GetWorkAreaList,
params
});
}
// 添加地图作业区域
export function AddWorkArea(params) {
return defHttp.post({
url: Api.AddWorkArea,
data:params
});
}
// 更新地图作业区域
export function UpdateWorkArea(params) {
return defHttp.post({
url: Api.UpdateWorkArea,
data:params
});
}
// 删除地图作业区域
export function DeleteWorkArea(params) {
return defHttp.post({
url: Api.DeleteWorkArea + '?id=' + params.id,
data:params
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -3,20 +3,20 @@ import { wktToGeoJSON,geojsonToWKT } from "@terraformer/wkt"
const wktCollectionToGeoJson = (wktStr) => {
console.log("wktStr",wktStr);
// console.log("wktStr",wktStr);
let geojson = WKT.parse(wktStr);
console.log("geojson",geojson);
// console.log("geojson",geojson);
}
const WktToGeojson = (wktData)=> {
// return WKT.parse(wktData)
console.log("wktData",wktData);
// console.log("wktData",wktData);
return wktToGeoJSON(wktData);
}
const GeojsonToWkt = (geojsonData)=> {
// return WKT.convert(geojsonData)
console.log("geojsonData",geojsonData)
// console.log("geojsonData",geojsonData)
return geojsonToWKT(geojsonData)
}

@ -24,6 +24,7 @@
@setNowShowMarkData="setNowShowMarkData"
@setNowShowImageData="setNowShowImageData"
@setNowShowAreaData="setNowShowAreaData"
@setAllAreaData="setAllAreaData"
@deleteMark="deleteMark"
@deleteArea="deleteArea"
/>
@ -41,6 +42,9 @@
@setNowShowMarkData="setNowShowMarkData"
@setNowShowImageData="setNowShowImageData"
@setNowShowAreaData="setNowShowAreaData"
@setAllMarkData="setAllMarkData"
@setAllImageData="setAllImageData"
@setAllAreaData="setAllAreaData"
@closePathImageInfo="closePathImageInfo"
/>
</div>
@ -85,6 +89,13 @@
import markJson from './json/mark.json';
import areaJson from './json/area.json';
import imageJson from './json/image.json';
import {
GetWorkAreaList,
AddWorkArea,
UpdateWorkArea,
DeleteWorkArea,
} from '@/api/demo/mediaLibrary';
import { WktToGeojson, GeojsonToWkt } from '@/components/MapboxMaps/src/WktGeojsonTransform';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage, createConfirm } = useMessage();
@ -166,28 +177,41 @@
function closePathAreaInfo() {
areaInfoShow.value = false;
nowShowAreaData.value = {};
pathMapRef.value.areaRestoreDefault();
}
//
const nowShowAreaData = ref();
const allAreaDataList = ref(areaJson);
const allAreaDataList: any = ref(areaJson);
//
function setNowShowAreaData(value) {
function setNowShowAreaData(value, restore = true) {
if (value.id) {
areaInfoShow.value = true;
} else {
areaInfoShow.value = false;
}
if (restore) {
pathMapRef.value.areaRestoreDefault();
}
nowShowAreaData.value = value;
}
//
function setAllAreaData() {
//
getWorkAreaList();
}
//
function deleteArea(value) {
allAreaDataList.value = cloneDeep(allAreaDataList.value).filter((item) => item.id != value.id);
areaInfoShow.value = false;
nowShowAreaData.value = {};
setTimeout(() => {
pathLeftMenuRef.value.updateShowMenuInfoList('地图作业区域');
}, 50);
async function deleteArea(value) {
DeleteWorkArea({
id: value.id,
}).then((result) => {
if (result) {
//
getWorkAreaList();
areaInfoShow.value = false;
nowShowAreaData.value = {};
}
});
}
// ----------------------------------------------------
@ -222,7 +246,49 @@
pathMapRef.value.handlerLocation([position.lng, position.lat]);
}
//
async function getWorkAreaList() {
allAreaDataList.value = await GetWorkAreaList({ workspaceid: 1 });
allAreaDataList.value.forEach((area, index) => {
let geomjson = WktToGeojson(area.geom);
area = {
...area,
properties: JSON.parse(area.properties),
geomtype: getGeomType(area),
coordinates: geomjson.coordinates,
};
allAreaDataList.value[index] = area;
});
setTimeout(() => {
pathLeftMenuRef.value.updateShowMenuInfoList('地图作业区域');
}, 50);
}
// WKT
function getGeomType(area) {
let geom = area.geom;
let radiusFlag = area.properties.indexOf('radius') > -1 ? true : false;
//
if (geom.indexOf('POINT Z') > -1 && !radiusFlag) {
return 'Point';
}
// 线
if (geom.indexOf('LINESTRING Z') > -1 && !radiusFlag) {
return 'Polyline';
}
//
if (geom.indexOf('MULTIPOLYGON Z') > -1 && !radiusFlag) {
return 'Polygon';
}
//
if (geom.indexOf('MULTIPOLYGON Z') > -1 && radiusFlag) {
return 'Circle';
}
}
onMounted(() => {
//
getWorkAreaList();
nowShowMarkData.value = markJson[1];
});
</script>

@ -6,13 +6,13 @@
</a-col>
<a-col :span="24">
<div class="markTitle">
<!-- 自定义限飞-->
<!-- 自定义禁降-->
<div
v-if="props.nowShowAreaData.type == 'noland'"
:style="{
width: '15px',
height: '15px',
background: `linear-gradient(to bottom right, transparent 45%, ${props.nowShowAreaData.content.properties.color} 45%, ${props.nowShowAreaData.content.properties.color} 60%, transparent 60%), #ffffff00`,
background: `linear-gradient(to bottom right, transparent 45%, #FF9900 45%, #FF9900 60%, transparent 60%), #ffffff00`,
'margin-left': '2px',
'margin-right': '12px',
}"
@ -23,25 +23,23 @@
:style="{
width: '13px',
height: '13px',
outline: `2px solid ${props.nowShowAreaData.content.properties.color}`,
outline: `2px solid #00FF00`,
'margin-left': '2px',
'margin-right': '12px',
'border-radius':
props.nowShowAreaData.content.geometry.type == 'Circle' ? '6.5px' : '0px',
'border-radius': props.nowShowAreaData.geomtype == 'Circle' ? '6.5px' : '0px',
}"
/>
<!-- 自定义禁降-->
<!-- 自定义限飞-->
<div
v-if="props.nowShowAreaData.type == 'nfz'"
:style="{
width: '13px',
height: '13px',
outline: `2px solid ${props.nowShowAreaData.content.properties.color}`,
background: `${props.nowShowAreaData.content.properties.color}55`,
outline: `2px solid #FF0000`,
background: `#FF000055`,
'margin-left': '2px',
'margin-right': '12px',
'border-radius':
props.nowShowAreaData.content.geometry.type == 'Circle' ? '6.5px' : '0px',
'border-radius': props.nowShowAreaData.geomtype == 'Circle' ? '6.5px' : '0px',
}"
/>
<a-input v-model:value="nowAreaData.name" style="width: 65%" size="small"></a-input>
@ -73,7 +71,7 @@
</a-col>
<a-col :span="18">
<div class="markContent">
{{ props.nowShowAreaData.status == 'enable' ? '已启用' : '已禁用' }}
{{ props.nowShowAreaData.state == 0 ? '已启用' : '已禁用' }}
</div>
</a-col>
<!-- 水平距离 -->
@ -81,33 +79,29 @@
<div class="markTitle">水平距离</div>
</a-col>
<a-col :span="18" v-if="props.nowShowAreaData.type == 'noland'">
<div class="markContent"> {{ distance.toFixed(1) }}m</div>
<div class="markContent"> {{ nowAreaData.length.toFixed(1) }}m</div>
</a-col>
<!-- 水平面积 -->
<a-col :span="6">
<div class="markTitle">水平面积</div>
</a-col>
<a-col :span="18">
<div class="markContent"> {{ area.toFixed(2) }}</div>
<div class="markContent"> {{ nowAreaData.area.toFixed(2) }}</div>
</a-col>
<!-- 水平周长 -->
<a-col :span="6" v-if="props.nowShowAreaData.content.geometry.type == 'Polygon'">
<a-col :span="6" v-if="props.nowShowAreaData.geomtype == 'Polygon'">
<div class="markTitle">水平周长</div>
</a-col>
<a-col :span="18" v-if="props.nowShowAreaData.content.geometry.type == 'Polygon'">
<div class="markContent"> {{ distance2 }}m</div>
<a-col :span="18" v-if="props.nowShowAreaData.geomtype == 'Polygon'">
<div class="markContent"> {{ nowAreaData.length.toFixed(1) }}m</div>
</a-col>
<!-- 半径 -->
<a-col :span="6" v-if="props.nowShowAreaData.content.geometry.type == 'Circle'">
<a-col :span="6" v-if="props.nowShowAreaData.geomtype == 'Circle'">
<div class="markTitle">半径</div>
</a-col>
<a-col :span="18" v-if="props.nowShowAreaData.content.geometry.type == 'Circle'">
<a-col :span="18" v-if="props.nowShowAreaData.geomtype == 'Circle'">
<div class="markContent">
<a-input
v-model:value="nowAreaData.content.geometry.radius"
style="width: 100%"
size="small"
>
<a-input v-model:value="nowAreaData.properties.radius" style="width: 100%" size="small">
<template #addonAfter> <span style="color: white">m</span> </template>
</a-input>
</div>
@ -120,19 +114,19 @@
<div class="markContent">
<div
class="button"
:class="{ disabled: nowAreaData.content.properties.width == 10 }"
:class="{ disabled: nowAreaData.properties.width == 10 }"
@click="removeRecordNum"
>
<MinusOutlined style="font-size: 20px; color: #ffffff" />
</div>
<div class="numDiv">
<span v-if="!numInputFlag" class="numSpan" @click="focusInput">
{{ nowAreaData.content.properties.width }}
{{ nowAreaData.properties.width }}
</span>
<a-input
v-if="numInputFlag"
ref="focusInputRef"
v-model:value="nowAreaData.content.properties.width"
v-model:value="nowAreaData.properties.width"
style="width: 50%"
@blur="blurInput"
@keypress.enter="blurInput"
@ -141,7 +135,7 @@
</div>
<div
class="button"
:class="{ disabled: nowAreaData.content.properties.width == 100 }"
:class="{ disabled: nowAreaData.properties.width == 100 }"
@click="addRecordNum"
>
<PlusOutlined style="font-size: 20px; color: #ffffff" />
@ -153,7 +147,7 @@
<div class="markTitle">绘制者</div>
</a-col>
<a-col :span="18">
<div class="markContent"> {{ props.nowShowAreaData.created_nickname }}</div>
<div class="markContent"> {{ props.nowShowAreaData.createdUser }}</div>
</a-col>
<a-col :span="24">
<div class="area_buttons">
@ -182,7 +176,15 @@
MinusOutlined,
PlusOutlined,
} from '@ant-design/icons-vue';
import {
GetWorkAreaList,
AddWorkArea,
UpdateWorkArea,
DeleteWorkArea,
} from '@/api/demo/mediaLibrary';
import { cloneDeep } from 'lodash-es';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage, createConfirm } = useMessage();
const props = defineProps(['allAreaDataList', 'nowShowAreaData']);
const emits = defineEmits([
@ -193,6 +195,16 @@
]);
const nowAreaData: any = ref(props.nowShowAreaData);
watch(
() => nowAreaData.value,
() => {
emits('setNowShowAreaData', nowAreaData.value, false);
},
{
deep: true,
},
);
// ----------------------------------------------------------------------
const numInputFlag = ref(false);
const focusInputRef = ref();
@ -209,40 +221,40 @@
function blurInput() {
numInputFlag.value = false;
//
if (/^-?\d+(\.\d+)?$/.test(nowAreaData.value.content.properties.width.toString())) {
if (nowAreaData.value.content.properties.width > 100) {
if (/^-?\d+(\.\d+)?$/.test(nowAreaData.value.properties.width.toString())) {
if (nowAreaData.value.properties.width > 100) {
// 100
nowAreaData.value.content.properties.width = 100;
} else if (nowAreaData.value.content.properties.width < 10) {
nowAreaData.value.properties.width = 100;
} else if (nowAreaData.value.properties.width < 10) {
// 10
nowAreaData.value.content.properties.width = 10;
nowAreaData.value.properties.width = 10;
} else {
//
let newnum = cloneDeep(nowAreaData.value.content.properties.width);
nowAreaData.value.content.properties.width = parseFloat(parseFloat(newnum).toFixed(2));
let newnum = cloneDeep(nowAreaData.value.properties.width);
nowAreaData.value.properties.width = parseFloat(parseFloat(newnum).toFixed(2));
}
} else {
//
nowAreaData.value.content.properties.width = 10;
nowAreaData.value.properties.width = 10;
}
}
// -
function removeRecordNum() {
let newnum = cloneDeep(nowAreaData.value.content.properties.width);
let newnum = cloneDeep(nowAreaData.value.properties.width);
if (newnum == 10) {
return;
}
newnum -= 5;
nowAreaData.value.content.properties.width = parseFloat(newnum.toFixed(2));
nowAreaData.value.properties.width = parseFloat(newnum.toFixed(2));
}
// -
function addRecordNum() {
let newnum = cloneDeep(nowAreaData.value.content.properties.width);
let newnum = cloneDeep(nowAreaData.value.properties.width);
if (newnum == 100) {
return;
}
newnum += 5;
nowAreaData.value.content.properties.width = parseFloat(newnum.toFixed(2));
nowAreaData.value.properties.width = parseFloat(newnum.toFixed(2));
}
//
@ -251,52 +263,28 @@
}
//
function handlerLocation() {
let coordinates = props.nowShowAreaData.content.geometry.coordinates;
if (props.nowShowAreaData.content.geometry.type == 'Circle') {
if (props.nowShowAreaData.geomtype == 'Circle') {
emits('handlerLocation', {
lng: coordinates[0],
lat: coordinates[1],
lng: props.nowShowAreaData.properties.centerPoint[0],
lat: props.nowShowAreaData.properties.centerPoint[1],
});
} else {
emits('handlerLocation', {
lng: coordinates[0][0][0],
lat: coordinates[0][0][1],
lng: props.nowShowAreaData.properties.centerPoint[0],
lat: props.nowShowAreaData.properties.centerPoint[1],
});
}
}
//
function deleteArea() {
emits('deleteArea', props.nowShowAreaData);
}
//
const distance = ref(0);
async function getDistance() {
distance.value = mars3d.MeasureUtil.getSurfaceDistance(
props.nowShowAreaData.content.geometry.coordinates[0],
);
}
//
const area = ref(0);
async function getArea() {
if (props.nowShowAreaData.content.geometry.type == 'Circle') {
let radius = props.nowShowAreaData.content.geometry.radius;
area.value = Math.PI * radius * radius;
} else {
area.value = mars3d.MeasureUtil.getSurfaceArea(
props.nowShowAreaData.content.geometry.coordinates[0],
);
}
}
//
const distance2 = ref(0);
async function getDistance2() {
distance.value = mars3d.MeasureUtil.getDistance(
props.nowShowAreaData.content.geometry.coordinates[0],
true,
);
createConfirm({
iconType: 'info',
title: '提示',
content: '将会影响到项目内设备的作业范围,是否删除该区域?',
onOk: async () => {
emits('deleteArea', props.nowShowAreaData);
},
});
}
watch(
@ -304,14 +292,14 @@
() => {
nowAreaData.value = props.nowShowAreaData;
//
getArea();
// getArea();
if (props.nowShowAreaData.type == 'noland') {
//
getDistance();
// getDistance();
}
if (props.nowShowAreaData.content.geometry.type == 'Polygon') {
if (props.nowShowAreaData.geomtype == 'Polygon') {
//
getDistance2();
// getDistance();
}
},
{

@ -89,28 +89,17 @@
<a-select-option value="noland">全部禁降区</a-select-option>
</a-select>
<a-select
v-model:value="areastatus"
v-model:value="areastate"
style="width: 120px"
@change="handleChangeAreaSelect"
>
<a-select-option value="all">全部状态</a-select-option>
<a-select-option value="enable">已启用</a-select-option>
<a-select-option value="disable">已禁用</a-select-option>
<a-select-option value="0">已启用</a-select-option>
<a-select-option value="1">已禁用</a-select-option>
</a-select>
</div>
<!-- 列表 -->
<div
v-for="show in showMenuInfoList"
:key="show.id"
@mouseenter="
show.mouse = true;
show.deleteClickNum = 0;
"
@mouseleave="
show.mouse = false;
show.deleteClickNum = 0;
"
>
<div v-for="show in showMenuInfoList" :key="show.id">
<!-- 地图标注 -->
<div
v-if="showMenuInfoName == '地图标注'"
@ -123,20 +112,31 @@
? '#393939'
: '',
}"
@mouseenter="
show.mouse = true;
show.deleteClickNum = 0;
"
@mouseleave="
show.mouse = false;
show.deleteClickNum = 0;
"
>
<div @click="setNowShowMarkData(show)">
<AntDesignOutlined v-if="show.resource.type == '0'" />
<ExpandAltOutlined v-if="show.resource.type == '1'" />
<BorderOutlined v-if="show.resource.type == '2'" />
<LogoutOutlined v-if="show.resource.type == '3'" />
<AntDesignOutlined v-if="show.type == '0'" />
<ExpandAltOutlined v-if="show.type == '1'" />
<BorderOutlined v-if="show.type == '2'" />
<LogoutOutlined v-if="show.type == '3'" />
</div>
<div
class="eye"
@click="show.status = !show.status"
:style="{ background: show.status ? '#2d8cf0' : '#000000' }"
@click="show.status == 0 ? (show.status = 1) : (show.status = 0)"
:style="{ background: show.status == 1 ? '#2d8cf0' : '#000000' }"
>
<EyeOutlined v-if="show.status" style="color: #ffffff; font-size: 16px" />
<EyeInvisibleOutlined v-if="!show.status" style="color: #ffffff; font-size: 16px" />
<EyeOutlined v-if="show.status == 1" style="color: #ffffff; font-size: 16px" />
<EyeInvisibleOutlined
v-if="show.status == 0"
style="color: #ffffff; font-size: 16px"
/>
</div>
<div @click="setNowShowMarkData(show)">{{ show.name }}</div>
<div class="buttonRight2" v-if="show.mouse">
@ -260,33 +260,35 @@
props.nowShowAreaData && props.nowShowAreaData.id == show.id
? '2px solid #2D8CF0'
: '',
background:
props.nowShowAreaData && show.status == 'enable' ? '#3c3c3c' : '#3c3c3c55',
background: props.nowShowAreaData && show.state == 0 ? '#3c3c3c' : '#3c3c3c55',
}"
>
<div
class="name"
@click="setNowShowAreaData(show)"
:style="{
color: show.status == 'enable' ? '#ffffff' : '#ffffff55',
}"
>
{{ show.name }}
</div>
<a-tooltip placement="right">
<template #title> {{ show.name }} </template>
<div
class="name"
@click="setNowShowAreaData(show)"
:style="{
color: show.state == 0 ? '#ffffff' : '#ffffff55',
}"
>
{{ show.name }}
</div>
</a-tooltip>
<div
class="type"
@click="setNowShowAreaData(show)"
:style="{
color: show.status == 'enable' ? '#ffffff' : '#ffffff55',
color: show.state == 0 ? '#ffffff' : '#ffffff55',
}"
>
<!-- 自定义限飞-->
<!-- 自定义禁降-->
<div
v-if="show.type == 'noland'"
:style="{
width: '15px',
height: '15px',
background: `linear-gradient(to bottom right, transparent 45%, ${show.content.properties.color} 45%, ${show.content.properties.color} 60%, transparent 60%), #ffffff00`,
background: `linear-gradient(to bottom right, transparent 45%, #FF9900 45%, #FF9900 60%, transparent 60%), #ffffff00`,
'margin-right': '6px',
}"
/>
@ -296,27 +298,27 @@
:style="{
width: '13px',
height: '13px',
outline: `2px solid ${show.content.properties.color}`,
outline: `2px solid #00FF00`,
'margin-right': '6px',
'border-radius': show.content.geometry.type == 'Circle' ? '6.5px' : '0px',
'border-radius': show.geomtype == 'Circle' ? '6.5px' : '0px',
}"
/>
<!-- 自定义禁降-->
<!-- 自定义限飞-->
<div
v-if="show.type == 'nfz'"
:style="{
width: '13px',
height: '13px',
outline: `2px solid ${show.content.properties.color}`,
background: `${show.content.properties.color}55`,
outline: `2px solid #FF0000`,
background: `#FF000055`,
'margin-right': '6px',
'border-radius': show.content.geometry.type == 'Circle' ? '6.5px' : '0px',
'border-radius': show.geomtype == 'Circle' ? '6.5px' : '0px',
}"
/>
<span>{{ getType(show.type) }}</span>
</div>
<div class="buttonlist">
<div class="button" v-if="show.status == 'disable'">
<div class="button" v-if="show.state == 1">
<a-popconfirm
title="将会影响到项目内设备的作业范围,是否启用该区域?"
ok-text="启用"
@ -332,7 +334,7 @@
</a-tooltip>
</a-popconfirm>
</div>
<div class="button" v-if="show.status == 'enable'">
<div class="button" v-if="show.state == 0">
<a-popconfirm
title="将会影响到项目内设备的作业范围,是否禁用该区域?"
ok-text="禁用"
@ -386,7 +388,7 @@
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ref, watch } from 'vue';
import {
CloseOutlined,
AntDesignOutlined,
@ -405,6 +407,12 @@
CheckCircleOutlined,
StopOutlined,
} from '@ant-design/icons-vue';
import {
GetWorkAreaList,
AddWorkArea,
UpdateWorkArea,
DeleteWorkArea,
} from '@/api/demo/mediaLibrary';
import { cloneDeep } from 'lodash-es';
import { useMessage } from '@/hooks/web/useMessage';
const { createMessage, createConfirm } = useMessage();
@ -427,14 +435,13 @@
'setNowShowMarkData',
'setNowShowImageData',
'setNowShowAreaData',
'setAllAreaData',
'deleteMark',
'deleteArea',
]);
const showMenuInfoList = ref(props.allMarkDataList);
const showMenuInfoName = ref('地图标注');
function updateShowMenuInfoList(type) {
console.log(type);
if (type == '地图标注') {
showMenuInfoList.value = props.allMarkDataList;
}
@ -473,7 +480,7 @@
// -------------------------------------------------
// -
const areatype = ref('all');
const areastatus = ref('all');
const areastate = ref('all');
// -
function getType(type) {
let name = '';
@ -497,24 +504,49 @@
if (areatype.value !== 'all') {
filterAreaData = filterAreaData.filter((item) => item.type == areatype.value);
}
if (areastatus.value !== 'all') {
filterAreaData = filterAreaData.filter((item) => item.status == areastatus.value);
if (areastate.value !== 'all') {
filterAreaData = filterAreaData.filter((item) => item.state == areastate.value);
}
filterAfterAreaDataList.value = filterAreaData;
showMenuInfoList.value = filterAfterAreaDataList.value;
}
// -
function enableThisArea(value) {
setNowShowAreaData({
...value,
status: 'enable',
state: 0,
});
UpdateWorkArea({
...value,
// id: value.id,
// name: value.name,
// type: value.type,
properties: JSON.stringify(value.properties),
// geom: value.geom,
// workSpaceId: '1',
state: 0,
}).then((res) => {
emits('setAllAreaData');
});
}
// -
function disableThisArea(value) {
setNowShowAreaData({
...value,
status: 'disable',
state: 1,
});
UpdateWorkArea({
// id: value.id,
// name: value.name,
// type: value.type,
...value,
properties: JSON.stringify(value.properties),
// geom: value.geom,
// workSpaceId: '1',
state: 1,
}).then((res) => {
emits('setAllAreaData');
});
}
// -
@ -538,52 +570,51 @@
// ------------------------------------------------------------------
//
function handlerLocation(position) {
if (showMenuInfoName.value == '地图标注') {
let coordinates = position.resource.content.geometry.coordinates;
switch (position.resource.type) {
case 0:
emits('handlerLocation', {
lng: coordinates[0],
lat: coordinates[1],
});
break;
case 1:
emits('handlerLocation', {
lng: coordinates[0][0],
lat: coordinates[0][1],
});
break;
case 2:
emits('handlerLocation', {
lng: coordinates[0][0][0],
lat: coordinates[0][0][1],
});
break;
case 3:
emits('handlerLocation', {
lng: coordinates[0],
lat: coordinates[1],
});
break;
}
}
if (showMenuInfoName.value == '地图照片') {
emits('handlerLocation', {
lng: position.photo_position.lng,
lat: position.photo_position.lat,
});
}
// if (showMenuInfoName.value == '') {
// let coordinates = position.coordinates;
// switch (position.type) {
// case 0:
// emits('handlerLocation', {
// lng: coordinates[0],
// lat: coordinates[1],
// });
// break;
// case 1:
// emits('handlerLocation', {
// lng: coordinates[0][0],
// lat: coordinates[0][1],
// });
// break;
// case 2:
// emits('handlerLocation', {
// lng: coordinates[0][0][0],
// lat: coordinates[0][0][1],
// });
// break;
// case 3:
// emits('handlerLocation', {
// lng: coordinates[0],
// lat: coordinates[1],
// });
// break;
// }
// }
// if (showMenuInfoName.value == '') {
// emits('handlerLocation', {
// lng: position.photo_position.lng,
// lat: position.photo_position.lat,
// });
// }
if (showMenuInfoName.value == '地图作业区域') {
let coordinates = position.content.geometry.coordinates;
if (position.content.geometry.type == 'Circle') {
if (position.geomtype == 'Circle') {
emits('handlerLocation', {
lng: coordinates[0],
lat: coordinates[1],
lng: position.properties.centerPoint[0],
lat: position.properties.centerPoint[1],
});
} else {
emits('handlerLocation', {
lng: coordinates[0][0][0],
lat: coordinates[0][0][1],
lng: position.properties.centerPoint[0],
lat: position.properties.centerPoint[1],
});
}
}
@ -785,8 +816,12 @@
left: 6px;
color: #ffffff;
font-size: 15px;
width: 240px;
height: 29px;
width: 90%;
height: 30px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.type {
position: absolute;

File diff suppressed because it is too large Load Diff

@ -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('未获取到机场信息')

@ -172,7 +172,7 @@
console.log(value);
cameraType.value = value;
};
const msgData = ref();
const msgData = ref({});
const changeSelect = async (value?: any) => {
//
//
@ -211,9 +211,6 @@
airPort.value.latitude = rs.data.latitude;
airPort.value.longitude = rs.data.longitude;
}
// relative_alternate_land_point 0
// self_converge_coordinate
// alternate_land_point 0
if (topic == topicUAVUrl) {
if (rs.data.latitude && rs.data.longitude) {
uavTrack.value = rs.data;

@ -1,5 +1,5 @@
<template>
<div class="airport-information" v-if="airportVal">
<div class="airport-information">
<div class="title"
>机场信息<span v-if="flighttask_step_code">
<template v-if="flighttask_step_code == 0"></template>
@ -16,8 +16,6 @@
<div class="content">
<div class="content-title">
{{ time }}
<!-- 获取不到摄像头的内容 -->
<!-- <span>机场摄像头未开</span> -->
</div>
<div class="content-item">
<div class="item-div" title="风速">
@ -68,9 +66,6 @@
@click="emits('changeLive')"
>机场直播</a-button
>
<!-- <a-button type="primary" style="background: #0a99eb" @click="emits('changeRemote')"
>远程调试</a-button
> -->
</div>
</div>
</div>
@ -118,6 +113,12 @@
// console.log(val);
flighttask_step_code.value = val.message.data.flighttask_step_code;
time.value = timestampToFormattedDate(val.message.timestamp);
} else if (
val.topic == 'thing/product/' + airPort.sn + '/osd' &&
val.message.data.drone_charge_state
) {
airportVal.value = val.message.data;
time.value = timestampToFormattedDate(val.message.timestamp);
}
},
);

@ -93,6 +93,7 @@
};
const stopTid = buildGUID();
const closeLive = () => {
emits('changeAirportLive');
const querys = {
bid: buildGUID(),
method: 'live_stop_push',
@ -103,7 +104,6 @@
},
};
servicesTopic(querys);
emits('changeAirportLive');
};
const reloadLive = () => {
player.src(live_info.url + liveCode.value + '.flv');

@ -45,32 +45,76 @@
<div class="content-info">
<div class="info-item">
<!-- <img src="@/assets/images/flightoperation/flight_control.png" alt="" /> -->
<div class="info-item-top" title="上升" @click="changeDRC('throttle', 'up')">
<img src="@/assets/images/flightoperation/top.png" alt="" />
<div class="info-item-top" title="上升" @click="changeDRC('throttle', 'up', '上升')">
<img
src="@/assets/images/flightoperation/top-active.png"
alt=""
v-if="selectName == '上升'"
/>
<img src="@/assets/images/flightoperation/top.png" alt="" v-else />
</div>
<div class="info-item-right" title="右旋转" @click="changeDRC('yaw', 'up')">
<img src="@/assets/images/flightoperation/right.png" alt="" />
<div class="info-item-right" title="右旋转" @click="changeDRC('yaw', 'up', '右旋转')">
<img
src="@/assets/images/flightoperation/right-active.png"
alt=""
v-if="selectName == '右旋转'"
/>
<img src="@/assets/images/flightoperation/right.png" alt="" v-else />
</div>
<div class="info-item-bottom" title="下降" @click="changeDRC('throttle', 'down')">
<img src="@/assets/images/flightoperation/bottom.png" alt="" />
<div
class="info-item-bottom"
title="下降"
@click="changeDRC('throttle', 'down', '下降')"
>
<img
src="@/assets/images/flightoperation/bottom-active.png"
alt=""
v-if="selectName == '下降'"
/>
<img src="@/assets/images/flightoperation/bottom.png" alt="" v-else />
</div>
<div class="info-item-left" title="左旋转" @click="changeDRC('yaw', 'down')">
<img src="@/assets/images/flightoperation/left.png" alt="" />
<div class="info-item-left" title="左旋转" @click="changeDRC('yaw', 'down', '左旋转')">
<img
src="@/assets/images/flightoperation/left-active.png"
alt=""
v-if="selectName == '左旋转'"
/>
<img src="@/assets/images/flightoperation/left.png" alt="" v-else />
</div>
</div>
<div class="info-item">
<!-- <img src="@/assets/images/flightoperation/flight_control.png" alt="" /> -->
<div class="info-item-top" title="前进" @click="changeDRC('pitch', 'up')">
<img src="@/assets/images/flightoperation/top.png" alt="" />
<div class="info-item-top" title="前进" @click="changeDRC('pitch', 'up', '前进')">
<img
src="@/assets/images/flightoperation/top-active.png"
alt=""
v-if="selectName == '前进'"
/>
<img src="@/assets/images/flightoperation/top.png" alt="" v-else />
</div>
<div class="info-item-right" title="右移" @click="changeDRC('roll', 'up')">
<img src="@/assets/images/flightoperation/right.png" alt="" />
<div class="info-item-right" title="右移" @click="changeDRC('roll', 'up', '右移')">
<img
src="@/assets/images/flightoperation/right-active.png"
alt=""
v-if="selectName == '右移'"
/>
<img src="@/assets/images/flightoperation/right.png" alt="" v-else />
</div>
<div class="info-item-bottom" title="后退" @click="changeDRC('pitch', 'down')">
<img src="@/assets/images/flightoperation/bottom.png" alt="" />
<div class="info-item-bottom" title="后退" @click="changeDRC('pitch', 'down', '后退')">
<img
src="@/assets/images/flightoperation/bottom-active.png"
alt=""
v-if="selectName == '后退'"
/>
<img src="@/assets/images/flightoperation/bottom.png" alt="" v-else />
</div>
<div class="info-item-left" title="左移" @click="changeDRC('roll', 'down')">
<img src="@/assets/images/flightoperation/left.png" alt="" />
<div class="info-item-left" title="左移" @click="changeDRC('roll', 'down', '左移')">
<img
src="@/assets/images/flightoperation/left-active.png"
alt=""
v-if="selectName == '左移'"
/>
<img src="@/assets/images/flightoperation/left.png" alt="" v-else />
</div>
</div>
</div>
@ -129,6 +173,7 @@
const airportVal: any = ref({
mode_code: 0,
});
const selectName = ref('');
const bid = buildGUID();
//redis
const changeRedisUser = (val: boolean) => {
@ -153,7 +198,7 @@
//
const takeOff = () => {
if (isLocked.value) {
createMessage.success('当前有用户正在操作,请稍后再试');
createMessage.warning('当前有用户正在操作,请稍后再试');
return;
}
@ -166,7 +211,7 @@
// createConnection();
// }
if (isLocked.value) {
createMessage.success('当前有用户正在操作,请稍后再试');
createMessage.warning('当前有用户正在操作,请稍后再试');
return;
}
if (flightGrab.value) {
@ -212,11 +257,16 @@
});
};
const seq = ref(1);
const changeDRC = (type, value) => {
const changeDRC = (type, value, name) => {
selectName.value = name;
setTimeout(() => {
selectName.value = '';
}, 2000);
if (!modeEnter.value) {
createMessage.warning('请先进入指令飞行控制');
return;
}
createMessage.info(name + '指令已发送');
seq.value = seq.value + 1;
let data = {
roll: 1024,
@ -449,8 +499,6 @@
width: 36px;
}
.info-item-top {
width: 40px;
height: 40px;
position: absolute;
top: 8px;
left: 20px;
@ -460,16 +508,12 @@
}
}
.info-item-right {
width: 40px;
height: 40px;
position: absolute;
top: 30px;
right: 33px;
right: 36px;
cursor: pointer;
}
.info-item-bottom {
width: 40px;
height: 40px;
position: absolute;
bottom: 0px;
left: 21px;
@ -479,11 +523,9 @@
}
}
.info-item-left {
width: 40px;
height: 40px;
position: absolute;
top: 30px;
left: 0px;
left: -2px;
cursor: pointer;
}
}

@ -7,6 +7,7 @@
:isActive="true"
:parentLimitation="true"
:isResizable="false"
@clicked="activateEv"
>
<div class="takeoff-information">
<div class="title"
@ -84,6 +85,9 @@
},
],
});
const activateEv = (e) => {
e.target.focus();
};
const takeOff = () => {
if (
data.points[0].latitude == null ||

@ -243,7 +243,7 @@
// createConnection();
// }
if (isLocked.value) {
createMessage.success('当前有用户正在操作,请稍后再试');
createMessage.warning('当前有用户正在操作,请稍后再试');
return;
}
if (grabBtn.value) {

@ -7,6 +7,7 @@
:isActive="true"
:parentLimitation="true"
:isResizable="false"
@clicked="activateEv"
>
<div class="takeoff-information">
<div class="title"
@ -31,8 +32,11 @@
</div>
<div class="content-edit">
目标点高度
<div>
<div style="display: flex; flex-direction: column; align-items: center">
<a-input v-model:value="data.target_height" />
<span style="margin-top: 4px; font-size: 12px; color: #f2762d"
>右击目标点选择按轴平移拖动Z轴</span
>
</div>
</div>
<div class="content-edit">
@ -201,6 +205,9 @@
}
},
);
const activateEv = (e) => {
e.target.focus();
};
const takeOff = () => {
if (
data.target_latitude == null ||

@ -5,7 +5,7 @@
<div class="logo"></div>
<div class="interval"></div>
<div class="title-div">
<div class="welcome">欢迎登</div>
<div class="welcome">欢迎登</div>
<div class="title">低空数智态势感知平台</div>
</div>
</div>

Loading…
Cancel
Save