Compare commits

...

4 Commits

Author SHA1 Message Date
a7f2ed7c8a merge 2025-12-26 00:37:01 +08:00
90187fcccf merge 2025-12-26 00:36:13 +08:00
0447e8691f merge 2025-12-26 00:21:42 +08:00
624c55c4b1 无人机接入 2025-12-26 00:19:22 +08:00
8 changed files with 308 additions and 28 deletions

View File

@@ -7,7 +7,7 @@ VITE_APP_BASE_URL = 'http://198.16.74.211:7284/api'
# 智能体访问地址
VITE_APP_MODEL_URL = 'http://198.16.74.211:7284/zhinengti'
# 海康播放插件配置
VITE_APP_HAIKANG_SECRET = 'pvQSichVMqtLGh4Ltedo'
VITE_APP_HAIKANG_APPKEY = '28273161'
VITE_APP_HAIKANG_IP = '39.175.75.233'
VITE_APP_HAIKANG_PORT = 10443
VITE_APP_HAIKANG_SECRET = 'DL6W0UBiH5iLrqCn6330'
VITE_APP_HAIKANG_APPKEY = '28091035'
VITE_APP_HAIKANG_IP = '198.16.74.206'
VITE_APP_HAIKANG_PORT = 443

View File

@@ -10,9 +10,26 @@ export const videoCameraFindPage = (data) => {
return request({
url: '/videoCamera/findPage',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
/**
* 分页查询监控设备列表
* 查询
* @param data
* @returns {*}
*/
export const findVideoLevelList = (data) => {
return request({
url: '/fishingPort/dsVideo/findVideoLevelList',
method: 'post',
data: data
})
}
/**
* 原有监控点位
* 查询
@@ -23,10 +40,8 @@ export const dsVideoList = (data) => {
return request({
url: '/fishingPort/dsVideo/getList',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
data: data
})
}
/**
@@ -39,7 +54,10 @@ export const findUavPage = (data) => {
return request({
url: '/videoCamera/findUavPage',
method: 'post',
data: data
data: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
/**

View File

@@ -189,7 +189,9 @@ const addUAVToMap = () => {
if (circleGeometry) {
circleGeometry.show()
}
if(item.sourceType) {
mapStore.updateWindowInfo({ visible: true, type: '_UAV', data: { ...item } })
}
})
}
})

View File

@@ -1,27 +1,259 @@
<template>
<div class="content-wrapper">
<div class="content-wrapper" v-if="data.sourceType == '2'">
<div class="left-wrapper">
<div class="uav-button" @click="handleAlgorithm">{{algorithmStatus ? '关闭' : '开启'}}算法</div>
<div class="Form">
<el-checkbox-group v-model="algorithmValue">
<el-checkbox
v-for="item in algorithms"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-checkbox-group>
</div>
<div class="flv-container"><FlvPlayerComponent v-if="dialog.url" id="uav" :url="dialog.url"/></div>
</div>
<div class="resize-handle" @mousedown="startResize"></div>
<div class="right-wrapper">
<CockpitCom v-if="dialog.visible"/>
</div>
</div>
<div class="content-wrapper" v-else-if="data.sourceType === '1'">
<iframe :src="url" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
</div>
</template>
<script setup>
import { computed } from 'vue'
import { computed, onUnmounted, reactive, ref, watch } from 'vue'
import useMapStore from '@/store/modules/map'
import FlvPlayerComponent from '@/components/FlvPlayer/index.vue'
import CockpitCom from '@/views/business/drone/cockpit.vue'
import { getVideoStream, doStartOrStopUavAlgorithm } from '@/api/uav'
import { ElMessage } from 'element-plus'
const mapStore = useMapStore()
const data = computed(() => mapStore.windowInfo.data)
const url = computed(() => 'https://lbs.baidu.com/maptool/getpoint')
console.log(data.value.droneSn, 'data.value.droneSn')
const url = computed(() => `http://198.16.74.210:3456/embed?projectId=4bd996b8-5201-4e5d-82b1-6879be360c20&authInfoId=eyJhbGciOiJIUzUxMiIsImNyaXQiOlsidHlwIiwiYWxnIiwia2lkIl0sImtpZCI6IjU3YmQyNmEwLTYyMDktNGE5My1hNjg4LWY4NzUyYmU1ZDE5MSIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiIiwiZXhwIjoyMDc1OTQ3NzIyLCJuYmYiOjE3NjA0MTQ5MjIsIm9yZ2FuaXphdGlvbl91dWlkIjoiOWRmMjlmYTgtNGI5OS00MThlLWJhMmQtMGY5ZWY5ZWVlMzkyIiwicHJvamVjdF91dWlkIjoiIiwic3ViIjoiZmgyIiwidXNlcl9pZCI6IjE3NjA0MTQxMDkzNTcwMDI0MjkifQ.DC_aS37W2fkqOjCtfvysDfhTn-4XVn3_IrXBnPD9rICGyrIBKBG3oPldeW_pqele5H_gCn1EgM0KXcbDgvq-dw&
deviceSn=${data.value.droneSn}&deviceType=drone&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE3NjcxNzA1NTIsImlhdCI6MTc2NjU2NTc1Mn0._YhukLexaErvTc3QDIAV5MuOa6cqglYUfsixNCit3us`)
// 算法开启关闭状态
const algorithmStatus = ref(false)
const algorithms = [
{ value: 120000, label: 'AIS挂牌/船牌识别' },
{ value: 140000, label: '船型识别' },
{ value: 139000, label: '未穿救生衣' },
{ value: 140009, label: '未悬挂国旗' },
{ value: 140008, label: '未封舱' }
]
const algorithmValue = ref([])
// 添加分割线相关的响应式数据
const isResizing = ref(false)
const leftWidth = ref('462px')
const rightWidth = ref('1391px')
const containerWidth = ref(0)
let containerBoundsLeft = 0 // 容器左边界
const dialog = reactive({
visible: false,
url: ''
})
const toggleFly = () => {
dialog.visible = true
dialog.url = ''
setTimeout(() => {
const params = {
status: 'stop',
sn: data.value.droneSn,
enable_orc: false
}
getVideoStream(params).then(res => {
if(res.success) {
setTimeout(() => {
dialog.url = res.result.httpUrl
}, 10000)
}
})
}, 20000)
}
// 开启/关闭算法
const handleAlgorithm = () => {
if(algorithmStatus.value) {
closeAlgorithm()
} else {
let enable_orc = false
const valueArray = [ ...algorithmValue.value ]
const index = valueArray.indexOf(120000)
if (index > -1) {
valueArray.splice(index, 1)
enable_orc = true
}
const params = {
class_codes: valueArray.join(','),
enable_orc,
status: 'start',
sn: data.value.droneSn
}
doStartOrStopUavAlgorithm(params).then(res => {
if(res.success) {
algorithmStatus.value = true
ElMessage.success('算法已开启')
}else {
ElMessage.error(res.result)
}
})
}
}
// 算法关闭
const closeAlgorithm = () => {
const params = {
status: 'stop',
sn: data.value.droneSn
}
doStartOrStopUavAlgorithm(params).then(res => {
if(res.success) {
algorithmStatus.value = false
algorithmValue.value = []
ElMessage.success(res.result)
}else {
ElMessage.error(res.result)
}
})
}
// 添加分割线控制相关方法
const startResize = (e) => {
isResizing.value = true
const containerRect = e.target.parentElement.getBoundingClientRect()
containerWidth.value = containerRect.width
// 保存容器的左边界位置
containerBoundsLeft = containerRect.left
document.addEventListener('mousemove', resize)
document.addEventListener('mouseup', stopResize)
}
const resize = (e) => {
if (isResizing.value) {
// 使用预先保存的容器左边界
const offsetX = e.clientX - containerBoundsLeft
// 计算左侧宽度百分比
let leftPercent = offsetX / containerWidth.value * 100
// 限制左右两侧的最小宽度
const minRightWidth = 1391
const minLeftWidth = 200
// 计算右侧最小百分比
const minRightPercent = minRightWidth / containerWidth.value * 100
const minLeftPercent = minLeftWidth / containerWidth.value * 100
// 限制拖动范围
if (leftPercent < minLeftPercent) {
leftPercent = minLeftPercent
} else if (leftPercent > 100 - minRightPercent) {
leftPercent = 100 - minRightPercent
}
leftWidth.value = `${leftPercent}%`
rightWidth.value = `${100 - leftPercent}%`
}
}
const stopResize = () => {
isResizing.value = false
document.removeEventListener('mousemove', resize)
document.removeEventListener('mouseup', stopResize)
}
watch(() => data.value.sourceType, (val) => {
if(val === '2') {
toggleFly()
}
}, { immediate: true })
onUnmounted(() => {
if(algorithmStatus.value && data.value.sourceType === '2') {
closeAlgorithm()
}
})
</script>
<style lang="scss" scoped>
// 无人机弹窗
.content-wrapper{
display: flex;
position: relative;
height: 850px;
iframe{
width: 100%;
height: 100%;
}
// //播放按钮
video::-webkit-media-controls-play-button{display: none;}
//进度条
video::-webkit-media-controls-timeline{display: none;}
//观看的当前时间
video::-webkit-media-controls-current-time-display{display: none;}
//剩余时间
video::-webkit-media-controls-time-remaining-display{display: none;}
//音量按钮
video::-webkit-media-controls-mute-button{display: none;}
video::-webkit-media-controls-toggle-closed-captions-buttonf{display: none;}
//1音量的控制条
video::-webkit-media-controls-volume-slider{display: none;}
video::-webkit-media-controls-enclosuret{display: none;}
.left-wrapper{
// width: 60%;
width: v-bind(leftWidth);
height: 700px;
display: flex;
flex-direction: column;
gap: 10px;
.el-checkbox-group{
gap: 4px !important;
.el-checkbox{
width: 150px !important;
padding-left: 5px !important;
}
}
.flv-container{
flex: 1; // 占据剩余空间
overflow: hidden; // 隐藏溢出内容
}
}
.resize-handle {
width: 8px;
height: 100%;
background: transparent;
cursor: col-resize;
position: relative;
user-select: none;
z-index: 10;
&::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2px;
// height: 40px;
background: #fff;
}
&:hover {
background: transparent;
}
}
.right-wrapper{
// width: 40%;
width: v-bind(rightWidth);
height: 700px;
// position: absolute;
// right: 0;
flex: 1;
}
.temporary{
position: absolute;
right: 0'';
right: 0;
top: 48px;
z-index: 999;
width: 1069px;
height: 429px;
}
}
</style>

View File

@@ -70,6 +70,8 @@ const resize = (e) => {
}
.tab{
display: flex;
position: relative;
z-index: 10;
.tab-item{
background: linear-gradient( 180deg, rgba(46,164,240,0.26) 0%, #2EA4F0 100%);
box-shadow: inset 0px -1px 2px 0px rgba(83,203,255,0.22);

View File

@@ -20,13 +20,14 @@
<div style="width: 100%;height: 100%;" :class="[`video-window${item}`]">
<!-- 视频预览组件 -->
<HikPlayerComponent
v-if="haikang[item-1]?.codes"
v-if="deviceType === 'CCTV' && haikang[item-1]?.codes"
ref="videoPreview"
:id="item"
:cameraIndexCode="haikang[item-1]?.codes"
:layout="haikang[item-1]?.layout"
@close="closeVideo">
</HikPlayerComponent>
<FlvPlayerComponent v-if="deviceType === 'UAV' && haikang[item-1]?.codes" :id="'uav'+item" :url="haikang[item-1]?.codes"/>
</div>
</div>
</div>
@@ -37,6 +38,8 @@
<script setup>
import { nextTick, onUnmounted, ref } from 'vue'
import HikPlayerComponent from '@/components/Player/HikPlayer.vue'
import FlvPlayerComponent from '@/components/FlvPlayer/index.vue'
import { getVideoStream, doStartOrStopUavAlgorithm } from '@/api/uav'
const control = ref([
{ label: '1X1', value: 1 },
@@ -47,6 +50,12 @@ const control = ref([
const active = ref(4)
const haikang = ref([])
const videoPreview = ref(null)
const props = defineProps({
deviceType: {
type: String,
default: () => ''
}
})
// 根据选中的分屏数计算样式
const getStyle = () => {
@@ -82,13 +91,33 @@ const closeVideo = (data, item) => {
}
// 点击视频预览
const handleNodeClick = (node) => {
if(node.videoCode) {
if(props.deviceType === 'CCTV' && node.videoCode) {
let index = haikang.value.findIndex(i => !i)
console.log(index, haikang.value, 'value')
if(index !== -1) {
haikang.value[index] = { codes: node.videoCode }
}
}
if(props.deviceType === 'UAV' && node.droneSn) {
toggleFly(node)
}
}
const toggleFly = (data) => {
setTimeout(() => {
const params = {
status: 'stop',
sn: data.droneSn,
enable_orc: false
}
getVideoStream(params).then(res => {
if(res.success) {
let index = haikang.value.findIndex(i => !i)
if(index !== -1) {
haikang.value[index] = { codes: res.result.httpUrl }
}
}
})
}, 20000)
}
changeGridType(4)
defineExpose({

View File

@@ -3,7 +3,7 @@
<DialogComponent @mousedown="drag" class="wall-dialog" :style="{ resize: 'both', overflow: 'auto' }" :title="title" width="1800" :draggable="true" :modal="false" @close="close">
<div class="wall-container">
<TreeCom ref="Tree" @handle="handle"/>
<GridCom ref="Grid"/>
<GridCom ref="Grid" :videoType="type"/>
</div>
</DialogComponent>
</template>

View File

@@ -8,7 +8,7 @@
@node-click="handleNodeClick">
<template v-slot="{ node, data }">
<!-- <img src="@/assets/images/livePreview/icon-monitor.png" alt=""> -->
<span :class="(data.status === 'offline' && treeType === 'UAV') ? 'offline': ''">{{ node.label }}</span>
<span :class="(!data.sourceType && treeType === 'UAV') ? 'offline': ''">{{ node.label }}</span>
</template>
</el-tree>
</div>
@@ -17,7 +17,7 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { videoCameraFindPage, findUavPage, findEnvPage, dsVideoList } from '@/api/device.js'
import { findVideoLevelList, findUavPage, findEnvPage, dsVideoList } from '@/api/device.js'
const emit = defineEmits([ 'handle' ])
@@ -27,11 +27,8 @@ const treeType = ref('')
const refresh = (type) => {
treeType.value = type
if(type === 'CCTV') {
const params = {
pageNo: 1,
pageSize: 9999
}
videoCameraFindPage(params).then(res => {
const params = {}
findVideoLevelList(params).then(res => {
if (res.success) {
const data = res.result.records
const groupedData = {}
@@ -93,14 +90,14 @@ const refresh = (type) => {
}
})
tableData.value = result
tableData.value = res.result.records
}
})
}
}
const handleNodeClick = (data, node) => {
if(data.status && data.status === 'offline') {
if(!data.sourceType) {
return false
}
if(!data.children || data.children.length === 0) {