徐景良 2026-03-13 09:23:12 +08:00
commit ac6c3afcac
8 changed files with 471 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -4,7 +4,7 @@
<div v-if="!beforeSave" style="padding-top: 7px;">
<div class="title">
标绘资源{{ selectPlot? `(${selectPlot})`: '' }}
<Icon v-if="selectPlot" :icon="`tabler:arrow-back`" size="24px" style="cursor: pointer;" @click="selectPlotItem('')"/>
<Icon v-if="selectPlot" :icon="`tabler:arrow-back`" size="24px" style="cursor: pointer;position: relative;top: -5px;" @click="selectPlotItem('')"/>
</div>
<div class="item-list" v-if="!selectPlot">
<div class="item" @click="selectPlotItem('二维平面类')">
@ -230,6 +230,7 @@ function addListener() {
margin-bottom: 17px;
display: flex;
justify-content: space-between;
padding-right: 40px;
}
.item-list{
display: flex;

View File

@ -1,13 +1,14 @@
<template>
<div class="resource-analysis-report-content">
<div class="lnglat-div">线索地点{{ `${clueInfo.lng}°E` }}, {{ `${clueInfo.lat}°N` }}</div>
<div class="lnglat-div" v-if="clueInfo.lng && clueInfo.lat">线{{ `${clueInfo.lng}°E` }}, {{ `${clueInfo.lat}°N` }}</div>
<div class="lnglat-div" v-else></div>
<div class="count-div">
<div class="scope-count">
<div class="scope-type">
3km
</div>
<div class="count-span-div">
<div class="count-span">{{ showData?.summary?.camp?.within3km + showData?.summary?.water?.within3km + showData?.summary?.supply?.within3km }}</div>
<div class="count-span">{{ showData?.summary?.camp?.within3km + showData?.summary?.water?.within3km + showData?.summary?.supply?.within3km || 0 }}</div>
<div class="count-unit">处资源</div>
</div>
<div class="sub-content">
@ -30,7 +31,7 @@
5km
</div>
<div class="count-span-div">
<div class="count-span">{{ showData?.summary?.camp?.within5km + showData?.summary?.water?.within5km + showData?.summary?.supply?.within5km }}</div>
<div class="count-span">{{ showData?.summary?.camp?.within5km + showData?.summary?.water?.within5km + showData?.summary?.supply?.within5km || 0 }}</div>
<div class="count-unit">处资源</div>
</div>
<div class="sub-content">
@ -53,7 +54,7 @@
10km
</div>
<div class="count-span-div">
<div class="count-span">{{ showData?.summary?.camp?.within10km + showData?.summary?.water?.within10km + showData?.summary?.supply?.within10km }}</div>
<div class="count-span">{{ showData?.summary?.camp?.within10km + showData?.summary?.water?.within10km + showData?.summary?.supply?.within10km || 0 }}</div>
<div class="count-unit">处资源</div>
</div>
<div class="sub-content">
@ -185,8 +186,8 @@
"barrack":[]
})
const clueInfo = ref({
lng:118.092787,
lat:35.386524
lng:null,
lat:null
})
const handlerGetWaterData = () => {
return axios.get("http://221.2.83.254:9007/geoserver/ksp/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=ksp%3Ananbeibushluiyuanhebing&maxFeatures=50000&outputFormat=application%2Fjson")
@ -401,8 +402,8 @@ function mergeTopResources(waterClass, supplyClass, campClass, key) {
onMounted(() => {
//
EventBus.on('responseToResourceAnalysisReport', (data) => {
console.log("data123",data.lng,data.lat);
console.log(111111111111,'资源分析报告',data);
console.log("data",data.lng,data.lat);
console.log('资源分析报告',data);
clueInfo.value.lng = Number(data.lng);
clueInfo.value.lat = Number(data.lat);
@ -415,7 +416,7 @@ function mergeTopResources(waterClass, supplyClass, campClass, key) {
let data = water.value.data;
let listTemp = geojsonPointToArray(data);
listTemp = getPointDistance([clueInfo.value.lng,clueInfo.value.lat],listTemp);
console.log(11111111111,'水源',listTemp)
console.log('水源',listTemp)
resources.value['water'] = listTemp;
}
if(wuzichubei.status === 'fulfilled'){
@ -423,7 +424,7 @@ function mergeTopResources(waterClass, supplyClass, campClass, key) {
console.log("goodsSources",data);
let listTemp = convertWKTArrayToCoordinates(data);
listTemp = getPointDistance([clueInfo.value.lng,clueInfo.value.lat],listTemp);
console.log(11111111111,'物资点',listTemp)
console.log('物资点',listTemp)
resources.value['goods'] = listTemp;
}
if(yingfang.status === 'fulfilled'){
@ -431,7 +432,7 @@ function mergeTopResources(waterClass, supplyClass, campClass, key) {
let listTemp = convertWKTArrayToCoordinates(data);
listTemp = getPointDistance([clueInfo.value.lng,clueInfo.value.lat],listTemp);
resources.value['barrack'] = listTemp;
console.log(11111111111,'营房',listTemp)
console.log('营房',listTemp)
}
const report = generateEmergencyReport(
resources.value['water'],

View File

@ -0,0 +1,18 @@
import cloneDeep from 'lodash/cloneDeep'
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig, requestSqlConfig } from '@/settings/designSetting'
import { ZhiChu_DroneLiveConfig } from './index'
export const option = {
dataset: {},
dataStyle: {},
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = ZhiChu_DroneLiveConfig.key
public attr = { ...chartInitConfig, w: 357, h: 335, zIndex: -1 }
public filter = "return res.result;"
public chartConfig = cloneDeep(ZhiChu_DroneLiveConfig)
public option = cloneDeep(option)
}

View File

@ -0,0 +1,13 @@
<template>
</template>
<script setup lang="ts">
import { PropType } from 'vue';
import { option } from './config';
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true,
},
});
</script>

View File

@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const ZhiChu_DroneLiveConfig: ConfigType = {
key: 'ZhiChu_DroneLive',
chartKey: 'VZhiChu_DroneLive',
conKey: 'VCZhiChu_DroneLive',
title: '响应中心-无人机直播',
category: ChatCategoryEnum.ZHICHU,
categoryName: ChatCategoryEnumName.ZHICHU,
package: PackagesCategoryEnum.ZHICHU,
chartFrame: ChartFrameEnum.STATIC,
image: 'ZhiChu_DroneLive.png'
}

View File

@ -0,0 +1,409 @@
<template>
<div class="zhichu_drone_live" :style="{width: `${w}px`,height: `${h}px`,}">
<div class="zhichu_drone_live_title">
无人机直播画面
<div class="zhichu_drone_live_close_button" @click="option.status.hide = true"></div>
</div>
<!-- <div class="button-div">
<div class="pre-button" @click="preButton">
<div class="pre-button-content"></div>
</div>
<div class="next-button" @click="nextButton">
<div class="next-button-content">
</div>
</div>
</div> -->
<div class="select_drone_div">
<a-select
class="drone_select"
popupClassName="drone_select_popup_select"
v-model:value="selectDrone"
style="width: 100%"
:options="uavList"
placeholder="请选择无人机"
@change="changeDrone"
></a-select>
</div>
<div class="out-live-div">
<video
id="player-zhichu-drone-live"
:width="videoWidth"
:height="videoHeight"
preload="auto"
playsinline
webkit-playsinline
>
</video>
<!-- <div class="empty-div" v-show="!showUav.isUAVLive"> -->
<!-- </div> -->
</div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, toRefs, watch, reactive, ref, onMounted } from 'vue';
import { CreateComponentType } from '@/packages/index.d';
import { icon } from '@/plugins';
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
import { useChartDataFetch } from '@/hooks';
import { EventBus } from '@/utils/eventBus';
import { replaceSqlParams } from '@/utils/sqlHandler';
import { eventHandlerHook } from '@/hooks/eventHandler.hook';
import { airPortStore } from '@/store/modules/airport';
import {
getClient,
createConnection,
clientPublish,
clientSubscribe
} from '@/utils/mqtt';
import {
errorName,
} from '@/utils/debugging/remote';
import TCPlayer from 'tcplayer.js';
import 'tcplayer.js/dist/tcplayer.min.css'; //
import { message } from 'ant-design-vue';
import { buildGUID } from '@/utils/uuid';
import { GetUavPageList } from '@/api/situation/index'
import { cameraCode } from '@/utils/debugging/remote'
const chartEditStore = useChartEditStore();
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true,
},
});
const airPortStoreVal = airPortStore();
const live_info = airPortStoreVal.getLiveInfo;
const uav = airPortStoreVal.getUAV;
const liveCode = ref('1581F8HGX254V00A0BUY');
const isUAVLive = ref(true);
let startTidList:string[] = []
const uavList = ref([])
const showIndex = ref(0)
const showUav = ref({})
let player;
const { w, h } = toRefs(props.chartConfig.attr);
const selectDrone = ref('')
const option = reactive({
status: props.chartConfig.status,
});
const videoWidth = computed(() => {
return w.value - 12
})
const videoHeight = computed(() => {
return h.value - 130
})
onMounted(() => {
//
EventBus.on(props.chartConfig.id + 'click', (data) => {
isUAVLive.value = false;
});
EventBus.on(props.chartConfig.id + 'dataupdate', (data) => {
isUAVLive.value = false;
});
EventBus.on('controlToDroneLive', (gateway) => {
uavList.value.forEach(item => {
if(item.gateWay == gateway){
item.isUAVLive = true
if(showUav.value.gateWay == gateway){
player.src(live_info.url + showUav.value.sn + '.flv');
player.play();
showUav.value.isUAVLive = true
}
}
})
})
if (!getClient() || !getClient().connected) {
createConnection();
}
setTimeout(() => {
GetUavPageList().then(resData => {
console.log('resData',resData)
uavList.value = resData.items.map(item => {
let startTid = buildGUID();
startTidList.push(startTid)
return {
...item,
label: item.name,
value: item.id,
startTid: startTid,
flighttask_step_code: null,
isUAVLive: false,
}
});
uavList.value.forEach((item, index) => {
let topicUrl = `thing/product/${item.psn}/osd`;
clientSubscribe(topicUrl);
startLiveFun(item)
if(index == 0){
showUav.value = item
selectDrone.value = item.id
playVideo(item)
}
})
})
getClient().on('message', (topic, mes) => {
const rs = JSON.parse(mes);
if (topic.endsWith("osd") && rs.data.flighttask_step_code) {
let sn = rs.gateway
for(let i = 0; i < uavList.value.length; i++){
if(uavList.value[i].psn == sn){
uavList.value[i] = {...uavList.value[i], flighttask_step_code: rs.data.flighttask_step_code}
}
}
}
if (rs.method == 'live_start_push' && startTidList.includes(rs.tid)) {
if (rs.data.result == 0) {
// message.success('');
uavList.value.forEach(item => {
if(rs.tid == item.startTid){
item.isUAVLive = true
if(showUav.value.gateWay == item.gateWay){
showUav.value.isUAVLive = true
}
}
})
} else if (rs.data.result == 513003) {
// message.success('');
uavList.value.forEach(item => {
if(rs.tid == item.startTid){
item.isUAVLive = true
if(showUav.value.gateWay == item.gateWay){
showUav.value.isUAVLive = true
}
}
})
} else {
// message.error('' + errorName(rs.data.result));
uavList.value.forEach(item => {
if(rs.tid == item.startTid){
item.isUAVLive = false
}
})
}
}
})
}, 1000)
});
const playVideo = (item) => {
player = TCPlayer('player-zhichu-drone-live', {
sources: [
{
src: live_info.url + item.sn + '.flv', //
},
],
licenseUrl: live_info.url + item.sn + '.flv', // license license licenseUrl
});
};
const startLiveFun = (item) => {
const querys = {
bid: buildGUID(),
method: 'live_start_push',
tid: item.startTid,
timestamp: new Date().getTime(),
data: {
url_type: 1, // 0 = RTMP 1GB28181 3WebRTC 4
url: live_info.rtmp + item.sn,
video_id: `${item.sn}/${cameraCode(item.typeId)}/normal-0`,
video_quality: 3, // 0=1=2=3=4=
},
};
console.log('query',querys)
clientPublish('thing/product/' + item.gateWay + '/services', querys);
clientSubscribe('thing/product/' + item.gateWay + '/services_reply');
};
const changeDrone = (key,record) => {
showUav.value = record
player.src(live_info.url + showUav.value.sn + '.flv');
player.play();
}
addListener();
// -
let isDragging = false;
let initialMouseX;
let initialMouseY;
let initialDocumentX;
let initialDocumentY;
//
function addListener() {
chartEditStore.getComponentList.forEach((element) => {
console.log('element',element)
if (element.key == 'ZhiChu_DroneLive') {
setTimeout(() => {
const dragDocument = document.getElementById(element.id);
if (dragDocument) {
dragDocument.addEventListener('mousedown', function (event) {
isDragging = true;
initialMouseX = event.clientX;
initialMouseY = event.clientY;
initialDocumentX = dragDocument.offsetLeft;
initialDocumentY = dragDocument.offsetTop;
dragDocument.style.cursor = 'grabbing';
});
dragDocument.addEventListener('mousemove', function (event) {
if (isDragging) {
const deltaX = event.clientX - initialMouseX;
const deltaY = event.clientY - initialMouseY;
dragDocument.style.left = initialDocumentX + deltaX + 'px';
dragDocument.style.top = initialDocumentY + deltaY + 'px';
}
});
dragDocument.addEventListener('mouseup', function (event) {
isDragging = false;
dragDocument.style.cursor = 'default';
});
}
}, 1000)
}
})
}
</script>
<style lang="scss" scoped>
.zhichu_drone_live{
background: #0c2411;
border: 1px solid #00601a;
padding: 10px;
border-radius: 3px;
user-select: none;
.zhichu_drone_live_title{
display: flex;
justify-content: space-between;
margin-bottom: 15px;
font-family: PingFangSC-Medium;
color: #fff;
font-size: 19px;
align-items: center;
.zhichu_drone_live_close_button{
width: 30px;
height: 30px;
background-image: url('@/assets/images/chart/uav/close.png');
background-size: 100% 100%;
cursor: pointer;
}
}
.select_drone_div{
margin-bottom: 15px;
.drone_select{
:deep(.ant-select-selector){
color: #fff;
height: 35px;
background: #05231A;
border-radius: 3px;
border: 1px solid #00611A !important;
.ant-select-selection-item{
color: #fff;
}
.ant-select-selection-placeholder{
color: rgba(255, 255, 255, 0.5);
}
}
}
}
.button-div{
height: 30px;
display: flex;
justify-content: end;
margin-bottom: 9px;
.pre-button{
width: 30px;
height: 30px;
background-image: url('/public/ZhiGan_DroneLive/pre_button.png');
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 9px;
cursor: pointer;
.pre-button-content{
width: 13px;
height: 9px;
background-image: url('/public/ZhiGan_DroneLive/pre_button_content.png');
background-size: 100% 100%;
}
}
.next-button{
width: 30px;
height: 30px;
background-image: url('/public/ZhiGan_DroneLive/next_button.png');
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.next-button-content{
width: 13px;
height: 9px;
background-image: url('/public/ZhiGan_DroneLive/next_button_content.png');
background-size: 100% 100%;
}
}
}
.out-live-div{
position: relative;
width: 100%;
height: calc(100% - 108px);
border: 1px solid #457453;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
.empty-div{
position: absolute;
width: 100%;
height: 100%;
background: url('/public/ZhiGan_DroneLive/empty_background.gif');
background-size: 100% 100%;
}
}
.drone-title{
height: 59px;
background-image: url('/public/ZhiGan_DroneLive/drone_title.png');
background-size: 100% 100%;
display: flex;
align-items: center;
padding-left: 18px;
.drone-icon{
width: 40px;
height: 40px;
background-image: url('/public/ZhiGan_DroneLive/drone_icon.png');
background-size: 100% 100%;
margin-right: 18px;
}
.drone-title-content{
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #FFFFFF;
line-height: 22px;
text-shadow: 0px 0px 13px rgba(0,207,44,0.5);
}
}
}
</style>
<style lang="scss">
.drone_select_popup_select{
background: #05231A;
border: 1px solid #00611A;
.ant-select-item{
background: #05231A;
color: #FFF;
}
.ant-select-item:hover{
background: #00611A;
}
.ant-select-item-option-selected{
color: #FFF !important;
background-color: #00611A !important;
}
}
</style>

View File

@ -5,6 +5,7 @@ import { ZhiChu_TuLiConfig } from "./ZhiChu_TuLi/index";
import { ZhiChu_LuXianGuHuaConfig } from "./ZhiChu_LuXianGuHua/index";
import { ZhiChu_ResourceConfig } from "./ZhiChu_Resource/index";
import { ResourceAnalysisReportConfig } from "./ResourceAnalysisReport/index";
import { ZhiChu_DroneLiveConfig } from "./ZhiChu_DroneLive/index";
export default [
ZhiChu_ModalFrameConfig,
@ -13,5 +14,6 @@ export default [
ZhiChu_TuLiConfig,
ZhiChu_LuXianGuHuaConfig,
ZhiChu_ResourceConfig,
ResourceAnalysisReportConfig
ResourceAnalysisReportConfig,
ZhiChu_DroneLiveConfig
];