业务配置页面+接口联调,弹窗修改路由跳转

This commit is contained in:
2026-01-09 15:45:57 +08:00
parent ecf0e4add9
commit 3a264bc1ab
34 changed files with 803 additions and 617 deletions

View File

@@ -3,11 +3,8 @@ NODE_ENV = 'development'
# 渔船点位
VITE_WS_BASE_URL ='ws://220.185.188.222:8055/api/gisWs'
# VITE_APP_BASE_URL = 'http://125.124.131.105:6811/api'
# 成彬本地
VITE_APP_BASE_URL = 'http://100.95.100.2:6061/api'
# VITE_APP_BASE_URL = 'http://100.95.236.218:6061/api'
# VITE_APP_BASE_URL = 'http://119.167.138.11:6061/video-service'
VITE_APP_BASE_URL = 'http://100.95.196.8:6061/api'
# 智能体访问地址
# 宋凯忠本地

View File

@@ -6,14 +6,50 @@ import request from '@/utils/request'
* @param data
* @returns {*}
*/
export const videoCameraFindPage = (data) => {
export const dsVideoPage = (data) => {
return request({
url: '/videoCamera/findPage',
url: '/fishingPort/dsVideo/page',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
data: data
})
}
/**
* 分页查询监控设备列表
* 新增
* @param data
* @returns {*}
*/
export const dsVideoSave = (data) => {
return request({
url: '/fishingPort/dsVideo/save',
method: 'post',
data: data
})
}
/**
* 分页查询监控设备列表
* 修改
* @param data
* @returns {*}
*/
export const dsVideoUpdate = (data) => {
return request({
url: '/fishingPort/dsVideo/update',
method: 'post',
data: data
})
}
/**
* 分页查询监控设备列表
* 删除
* @param data
* @returns {*}
*/
export const dsVideoDelete = (data) => {
return request({
url: '/fishingPort/dsVideo/delete',
method: 'post',
data: data
})
}
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -11,7 +11,7 @@
v-model="model[item.prop]"
:placeholder="item.placeholder ? item.placeholder : '请输入'"
:clearable="item.clearable !== false"
style="width: 110px">
:style="{width: item.width || '110px'}">
</el-input>
<el-select
v-if="item.type === 'select'"
@@ -23,7 +23,7 @@
:collapse-tags="item.collapseTags || false"
:collapse-tags-tooltip="item.collapseTagsTooltip || false"
@change="(val) => handle(item.event,val)"
style="width: 90px"
:style="{width: item.width || '90px'}"
poper-class="filter-select"
:empty-values="item.emptyValues || [ '', undefined]"
:value-on-clear="item.valueOnClear || ''">

View File

@@ -68,7 +68,6 @@ const initPlayer = () => {
timeoutId = setTimeout(() => {
timeoutId = null
console.log(retryCount, retryDelay, '重连次数......')
if (retryCount <= MAX_RETRIES) {
initPlayer() // 重新初始化播放器
} else {
@@ -76,7 +75,6 @@ const initPlayer = () => {
if (player) {
player = null
}
console.error('重试超过最大次数')
}
}, retryDelay)
})

View File

@@ -69,11 +69,11 @@
]
const navRight = [
// {
// label: '识别记录',
// path: '/screen/identification',
// prop: 'identification'
// },
{
label: '业务配置',
path: '/setting',
prop: 'identification'
},
{
label: '插件下载',
path: '',
@@ -101,6 +101,8 @@
const toggle = (nav) => {
if(nav.prop === 'monitor') {
window.open('/plugin/VideoWebPlugin.exe', '_blank')
}else{
router.push(nav.path)
}
}
// 右上角时间
@@ -250,7 +252,7 @@
}
.datetime{
display: flex;
gap: 14px;
gap: 12px;
color: #FFFFFF;
text-align: left;
height: 29px;
@@ -284,7 +286,7 @@
margin-left: 61px;
}
.right{
gap:20px;
gap:15px;
margin-right: 40px;
align-items: center;
}

View File

@@ -1,130 +0,0 @@
<template>
<div :class="[{ 'expend-only': expend }, 'screen-map-server',toolBarStore.expand ? 'expand' : '']" @mouseenter="moveEnter" @mouseleave="moveLeave">
<img alt="ICON_MAP" class="icon-cover" :src="getAssetsFile(`icon-${active}-active.png`)">
<div v-for="(item, index) in list" class="screen-map-server-item" :key="index" :style="getStyle(item, index)"
@click="toggle(item, index)">
<img v-show="(!expend && !index) || expend" alt="ICON_MAP" class="icon-map"
:src="getAssetsFile(`icon-${item.prop}${item.prop == active ? '-active' : ''}.png`)">
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { getAssetsFile } from '@/utils/common'
import useToolBarStore from '@/store/modules/toolbar'
const toolBarStore = useToolBarStore()
const emit = defineEmits([ 'toggle' ])
const active = ref('satellite')
const expend = ref(false)
const list = [
{
label: '遥感',
prop: 'satellite'
},
{
label: '海图',
prop: 'sea'
},
{
label: '浅色',
prop: 'light'
}
]
/**
* 动态改变当前图例的定位
* @param e 当前图例信息
* @param index 当前图例下标
*/
const getStyle = (e, index) => ({
'background-color': !expend.value && e.color ? e.color : 'transparent', // 未展开样式
'border-radius': expend.value ? '0px' : '5px', // 未展开样式
right: expend.value ? `${index * 40 + (index + 1) * 4}px` : `${(index + 1) * 4}px`,
'transition-duration': expend.value ? `${index * 0.2}s` : '0s',
'z-index': e.index
})
const moveEnter = () => {
expend.value = true
}
const moveLeave = () => {
expend.value = false
}
/**
* 底图切换
* @param e 当前图例信息
*/
const toggle = (e) => {
active.value = e.prop
emit('toggle', 'toggle-base', e.prop)
}
</script>
<style scoped lang="scss">
.screen-map-server {
position: absolute;
right: 30px;
bottom: 112px;
width: 48px;
height: 48px;
padding: 4px;
border-radius: 5px;
box-sizing: border-box;
background-color: transparent;
transition-duration: .3s;
cursor: pointer;
pointer-events: auto;
&.expend-only {
height: 48px;
width: 135px;
}
&:hover {
background-color: rgba(77, 151, 255, 0.5);
.icon-cover {
display: none;
}
}
/* COVER */
.icon-cover {
display: block;
position: absolute;
right: 4px;
top: 4px;
z-index: 9;
}
/* ITEM */
.screen-map-server-item {
border-radius: 0;
height: 40px;
position: absolute;
top: 4px;
width: 40px;
z-index: 1;
.icon-map {
height: 100%;
width: 100%;
}
}
}
.expand{
right: 390px;
}
</style>

View File

@@ -23,14 +23,11 @@ import { monitors, uavs, stations, environmentals, fences, detailFences } from '
import InfoWindowComponent from '@/components/Map/window/index.vue'
import TrawlerInfoWindowComponent from '@/components/Map/window/trawler.vue'
import { dsVideoList, findUavPage, getVideoInfo } from '@/api/device.js'
import { useRouter } from 'vue-router'
const router = useRouter()
const mapStore = useMapStore()
const UAV = computed(() => mapStore.legend.UAV)
const monitor = computed(() => mapStore.legend.monitor)
const ais_station = computed(() => mapStore.legend.ais_station)
const environmental = computed(() => mapStore.legend.environmental)
const fence = computed(() => mapStore.legend.fence)
const sector = computed(() => mapStore.sector)
const legend = computed(() => mapStore.legend)
const locateData = computed(() => mapStore.locate.data)
let globalMap = null
let vector = {}
@@ -108,7 +105,7 @@ const addMonitorToMap = () => {
},
{
id: item.id,
symbol: $configs.getDevicePointSymbol('_monitor', { ...item, name: item.videoName }),
symbol: $configs.getDevicePointSymbol('_monitor' + (item.beBayonet === 1 ? '_bayonet' : ''), { ...item, name: item.videoName }), // beBayonet 是否卡口1是 0否
properties: item,
zIndex: 2
}
@@ -118,21 +115,8 @@ const addMonitorToMap = () => {
const pvalue = item.ptzcfg?.pValue || 0
drawSector('_monitor', [ item.longitude, item.latitude ], visionDistance, item.id, (90 - pvalue) % 360)
marker.on('click', (evt) => {
// 先隐藏所有扇形
changeSectorsInLayer('sectors_monitor', false)
// 确保扇形图层是可见的
if (!vector.sectors_monitor.isVisible()) {
vector.sectors_monitor.show()
}
// 显示当前点击项的所有相关扇形(圆、椭圆、线)
const baseId = `sector__monitor${item.id}`
vector.sectors_monitor.getGeometryById(baseId + '_circle')?.show()
vector.sectors_monitor.getGeometryById(baseId + '_ellipse')?.show()
vector.sectors_monitor.getGeometryById(baseId + '_line')?.show()
mapStore.updateWindowInfo({ visible: true, type: '_monitor', data: { ...item } })
mapStore.updateLocate({ ...item }) // 视频定位
mapStore.updateWindowInfo({ visible: true, type: '_monitor', data: { ...item } }) // 打开监控视频弹窗
})
}
})
@@ -140,34 +124,17 @@ const addMonitorToMap = () => {
}
// 要素/视频定位
const locateMoitor = (data) => {
const name = 'locate-monitor'
let layer = vector[name]
if (!layer) {
initLayerToMap('locate-monitor')
layer = vector['locate-monitor']
} else {
layer.clear()
}
initLayerToMap('locate-monitor')
const monitors = vector.monitor
const marker = monitors.getGeometryById(data.id)
if (marker) {
const coordinates = marker.getCoordinates()
geography.monitor.forEach((item) => {
if (item.id === data.id) {
// globalMap.map.animateTo(
// {
// center: [ coordinates.x, coordinates.y ]
// },
// {
// duration: 1000 * 0.5,
// easing: 'out'
// }
// )
// 先隐藏所有扇形
changeSectorsInLayer('sectors_monitor', false)
getVideoInfo({ id: item.id }).then(res => {
const visionDistance = item.visionDistance * 1000 || 5 * 1000 // 视角距离
const pvalue = res.data.ptzcfg.pValue || 0 // 旋转角度
const pvalue = res.data.ptzcfg?.pValue || 0 // 旋转角度
drawSector('_monitor', [ item.longitude, item.latitude ], visionDistance, item.id, (90 - pvalue) % 360)
// 确保扇形图层是可见的
@@ -369,7 +336,6 @@ const initUAV = () => {
addUAVToMap()
}
})
}
const initMonitor = () => {
const params = {}
@@ -394,83 +360,60 @@ const initFence = () => {
addFenceToMap('detailFence')
}
const toModel = () => {
mapStore.updateDialog({ visible: true, type: 'largeModel' })
router.push('/largeModel')
}
onMounted(() => {
initMap()
initFence()
initUAV()
initMonitor()
initAisStation()
initEnvironmental()
// 监控设备图层初始化
Object.values(layerMehods).forEach(initFunc => initFunc())
// 渔船链接
BoatUtil.init(mapStore)
BoatUtil.getShip()
// 轨迹图层
initLayerToMap('track')
})
watch(() => UAV.value, () => {
if(UAV.value) {
initUAV()
}else{
vector.UAV?.hide()
changeSectorsInLayer('sectors_UAV', false)
}
})
watch(() => monitor.value, () => {
if(monitor.value) {
initMonitor()
}else{
vector.monitor?.hide()
changeSectorsInLayer('sectors_monitor', false)
const layerMehods = {
UAV: initUAV,
monitor: initMonitor,
ais_station: initAisStation,
environmental: initEnvironmental,
fence: initFence
}
watch(() => Object.keys(layerMehods).reduce((obj, key) => {
obj[key] = legend.value[key]
return obj
}, {}), (newVal, oldVal) => {
Object.keys(layerMehods).forEach(key => {
// 监听值变化进行更改
if (newVal[key] !== oldVal[key]) {
if (newVal[key]) {
layerMehods[key]()
} else {
vector[key]?.hide()
if (key === 'UAV' || key === 'monitor') { // 无人机和监控可视区域隐藏
changeSectorsInLayer('sectors_' + key, false)
} else if (key === 'fence') {
vector.detailFence?.hide()
}
}
}
})
}, { deep: true })
const watchKeys = [ 'sectors_monitor', 'sectors_UAV' ]
watch(() => ({ sectors_UAV: legend.value.sectors_UAV, sectors_monitor: legend.value.sectors_monitor }), (newVal, oldVal) => {
watchKeys.forEach(key => {
// 监听值变化进行更改
if (newVal[key] !== oldVal[key]) {
if (newVal[key]) {
changeSectorsInLayer(key, true)
} else {
changeSectorsInLayer(key, false)
}
}
})
watch(() => ais_station.value, () => {
if(ais_station.value) {
initAisStation()
}else{
vector.ais_station?.hide()
}
})
watch(() => environmental.value, () => {
if(environmental.value) {
initEnvironmental()
}else{
vector.environmental?.hide()
}
})
watch(() => fence.value, () => {
if(fence.value) {
initFence()
}else{
vector.fence?.hide()
vector.detailFence?.hide()
}
})
watch(() => sector.value.monitor, (newVal) => {
if(vector.sectors_monitor) {
if(newVal) {
// vector.sectors_monitor.show()
changeSectorsInLayer('sectors_monitor', true)
} else {
// vector.sectors_monitor.hide()
changeSectorsInLayer('sectors_monitor', false)
}
}
})
watch(() => sector.value.UAV, (newVal) => {
if(vector.sectors_UAV) {
if(newVal) {
// vector.sectors_UAV.show()
changeSectorsInLayer('sectors_UAV', true)
} else {
// vector.sectors_UAV.hide()
changeSectorsInLayer('sectors_UAV', false)
}
}
})
})
}, { deep: true })
// 定位数据变化
watch(() => locateData.value.videoCode, (newVal) => {
watch(() => locateData.value.videoCode, () => {
nextTick(() => {
locateMoitor(locateData.value)
})

View File

@@ -184,7 +184,6 @@ const cleanupExpiredBoats = () => {
const getShip = () => {
findByCurrent().then(res => {
console.log('初始获取')
addBoats(res.result)
})
// ----------船只数据------------------
@@ -192,7 +191,6 @@ const getShip = () => {
dynamicBoatInfoWebSocket.onopen(event => {
})
dynamicBoatInfoWebSocket.onmessage(event => {
console.log('接收数据:', JSON.parse(event.data))
if(Array.isArray(JSON.parse(event.data))) {
addBoats(JSON.parse(event.data))

View File

@@ -75,7 +75,7 @@
return
}
item.sector = !item.sector
mapStore.updateSector(item.prop, item.sector)
mapStore.updateLegend('sectors_' + item.prop, item.sector)
}
const toggleExpand = () => {
isFold.value = !isFold.value
@@ -86,7 +86,7 @@
mapStore.updateLegend(item.prop, item.checked)
if(!item.checked && (item.prop === 'monitor' || item.prop === 'UAV')) {
item.sector = false
mapStore.updateSector(item.prop, item.sector)
mapStore.updateLegend('sectors_' + item.prop, item.sector)
}
}
</script>

View File

@@ -182,7 +182,7 @@ onUnmounted(() => {
.content-wrapper{
display: flex;
position: relative;
height: 100%;
height: 800px;
iframe{
width: 100%;
height: 100%;

View File

@@ -283,8 +283,6 @@ export default {
});
},
uninit() {
console.log(999);
let that = this;
if (that.oWebControl != null) {
that.oWebControl.JS_RequestInterface({

View File

@@ -14,9 +14,7 @@ const routes = [
auth: false,
title: '首页'
},
component: () => import('@/views/business/index.vue'),
children: [
]
component: () => import('@/views/business/index.vue')
},
{
path: '/login',
@@ -71,7 +69,7 @@ router.beforeEach((to, from, next) => {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
next()
} else {
// next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
next('/login') // 否则全部重定向到登录页

View File

@@ -6,29 +6,22 @@ const useMapStore = defineStore(
state: () => ({
legend: {
UAV: true,
sectors_UAV: false, // 无人机可视域开关
monitor: true,
sectors_monitor: false, // 监控可视域开关
ais_station: true,
environmental: true,
fence: true
},
windowInfo: {
visible: false,
type: '',
type: '', // 弹窗类型 _monitor/_UAV/_trawler_dynamic
data: {}
},
sector: {
'UAV': false, // 无人机可视域开关
'monitor': false // 监控可视域开关
},
hik: { // 海康插件设备层级
level: 1,
data: {}
},
dialog: {
visible: false,
type: '', // 弹窗类型 alarm/CCTV/UAV/largeModel
data: {}
},
// 无人机信息
uavs: {
data: {}
@@ -48,20 +41,11 @@ const useMapStore = defineStore(
this.windowInfo.type = type
this.windowInfo.data = data
},
updateSector(type, checked) {
this.sector[type] = checked
},
updateHik(option) {
const { level = 1, data = {} } = option
this.hik.level = level
this.hik.data = data
},
updateDialog(option) {
const { visible = false, type = '', data } = option
this.dialog.visible = visible
this.dialog.type = type
this.dialog.data = data
},
updateUavData(option) {
this.uavs.data = option
},

View File

@@ -1,22 +0,0 @@
import { defineStore } from 'pinia'
const useToolBarStore = defineStore(
'toolbar',
{
state: () => ({
expand: false, //地图图例位置
trawler: {
visible: false // 渔船动态详情收缩框显示
}
}),
actions: {
toogleExpand(expand) {
this.expand = expand
},
toggleTrawlerVisible(visible) {
this.trawler.visible = visible
}
}
})
export default useToolBarStore

View File

@@ -5,11 +5,11 @@
</div>
</template>
<script setup>
import useMapStore from '@/store/modules/map'
import { useRouter } from 'vue-router'
const mapStore = useMapStore()
const router = useRouter()
const handle = (type) => {
mapStore.updateDialog({ visible: true, type })
router.push({ path: '/wall', query: { type } })
}
</script>
<style scoped lang="scss">

View File

@@ -61,12 +61,12 @@
</template>
<script setup>
import SubtitleComponent from '@/components/SubtItle/index.vue'
import { reactive, ref } from 'vue'
import useMapStore from '@/store/modules/map'
import { ref } from 'vue'
import { videoIdentificationPage } from '@/api/identification.js'
import { useRouter } from 'vue-router'
const router = useRouter()
const emit = defineEmits([ 'toggle-fold' ])
const mapStore = useMapStore()
const tabs = [
{
label: '无船号',
@@ -120,7 +120,7 @@ const toggle = (index) => {
const handle = (type, item) => {
switch (type) {
case 'more':
mapStore.updateDialog({ visible: true, type: 'alarm', data: { isHasBoatName: current.value == 1 ? 1 : 2 } })
router.push({ path: '/identification', query: { isHasBoatName: current.value == 1 ? 1 : 2 } })
break
default:
break

View File

@@ -28,6 +28,7 @@ const lock = ref(false)
let timer = null
let monitorIndex = 0
const videoUrl = ref('')
const time = ref(10000)
const handle = (index) => {
if(index === 0) {
@@ -57,33 +58,40 @@ const init = () => {
const params = {}
videoUrl.value = ''
dsVideoList(params).then(res => {
data.value = res.data.filter(i => i.videoUrl).sort((a, b) => a.playIndex - b.playIndex)
videoUrl.value = data.value[monitorIndex].videoUrl
mapStore.updateLocate(data.value[monitorIndex])
clearTimer()
handlePolling(true) // 开始轮询
timer = setInterval(() => {
if(lock.value) {
return false
}
videoUrl.value = ''
nextTick(() => {
if(monitorIndex >= data.value.length - 1) {
handlePolling(false) // 轮询结束
monitorIndex = 0
handlePolling(true) // 开始轮询
}else{
monitorIndex++
}
videoUrl.value = data.value[monitorIndex].videoUrl
})
mapStore.updateLocate(data.value[monitorIndex])
}, 60 * 1000 * 0.5)
data.value = res.data.filter(i => i.videoUrl && i.bePlay === 1).sort((a, b) => a.playIndex - b.playIndex)
// 启动轮播定时器
startPolling()
})
}
const handlePolling = (flag) => {
console.log(flag + '---轮询')
// 启动轮播
const startPolling = () => {
clearTimer()
const runPolling = () => {
if (!lock.value) {
switchToNextVideo()
}
timer = setTimeout(runPolling, time.value)
}
runPolling()
}
// 切换到下一个视频
const switchToNextVideo = () => {
videoUrl.value = ''
time.value = data.value[monitorIndex].playTime * 1000 || 10 * 1000
mapStore.updateLocate(data.value[monitorIndex])
nextTick(() => {
videoUrl.value = data.value[monitorIndex].videoUrl
// 更新播放时间
if (monitorIndex >= data.value.length - 1) {
monitorIndex = 0 // 回到第一个
} else {
monitorIndex++
}
})
}
/**
* 拖拽事件
* @param e
@@ -96,8 +104,7 @@ const drag = (e) => {
document.querySelector('.monitor-container').style.width = i.width + 'px'
})
}else{
dragEvent(e, 'monitor-container', () => {
})
dragEvent(e, 'monitor-container', () => { /* empty */ })
}
}
onMounted(() => {

View File

@@ -45,154 +45,8 @@ onMounted(() => {
drone_sn: data.value.droneSn // 飞机 sn按实际传入
})
}
// // 使用轮询方式等待内容加载完成后再滚动
// waitForContentAndScroll()
})
const waitForContentAndScroll = () => {
let attempts = 0
let previousScrollWidth = 0
let stableCount = 0
const maxAttempts = 30
const checkAndScroll = () => {
attempts++
if (Detail.value) {
// 检查目标容器是否已加载内容
const mapContainer = document.getElementById('map-app-global')
// 判断内容是否加载完成
const isContentLoaded = mapContainer && mapContainer.children.length > 0
// console.log('Content loaded:', isContentLoaded, mapContainer)
// 获取当前尺寸
const currentScrollWidth = Detail.value.scrollWidth
const clientWidth = Detail.value.clientWidth
// console.log('Size check:', {
// attempt: attempts,
// scrollWidth: currentScrollWidth,
// clientWidth: clientWidth,
// isContentLoaded: isContentLoaded
// })
// 检查尺寸是否稳定(连续几次尺寸相同)
if (currentScrollWidth === previousScrollWidth && currentScrollWidth > clientWidth) {
stableCount++
} else {
stableCount = 0
}
previousScrollWidth = currentScrollWidth
// 当内容加载完成且尺寸稳定,或者达到最大尝试次数时执行滚动
if (isContentLoaded && stableCount >= 2 && currentScrollWidth > clientWidth ||
attempts >= maxAttempts) {
// 强制重新计算布局
forceLayoutRecalculation(() => {
executeScroll()
})
} else {
// 内容未加载完成或尺寸不稳定,继续等待
setTimeout(checkAndScroll, 300)
}
}
}
// 开始检查
setTimeout(checkAndScroll, 300)
}
const forceLayoutRecalculation = (callback) => {
if (!Detail.value) {
if (callback) {
callback()
}
return
}
// 方法1: 强制重排
Detail.value.style.display = 'none'
void Detail.value.offsetHeight // 强制重排
Detail.value.style.display = 'flex'
// 方法2: 临时修改overflow触发重新计算
const originalOverflow = Detail.value.style.overflow
Detail.value.style.overflow = 'hidden'
void Detail.value.offsetWidth
Detail.value.style.overflow = originalOverflow
setTimeout(() => {
if (callback) {
callback()
}
}, 100)
}
const executeScroll = () => {
if (!Detail.value) {
return
}
// 重新获取准确的尺寸
const scrollWidth = Detail.value.scrollWidth
const clientWidth = Detail.value.clientWidth
const maxScroll = scrollWidth - clientWidth
// console.log('Final size calculation:', {
// scrollWidth,
// clientWidth,
// maxScroll
// })
if (maxScroll > 0) {
const targetPosition = 1388
const scrollPosition = targetPosition > maxScroll ? maxScroll : targetPosition
// 修复:正确处理 scrollPosition 为 0 的情况
const finalPosition = typeof scrollPosition === 'number' ? scrollPosition : 645
// 激活滚动机制后再执行滚动
activateScrollMechanism(() => {
Detail.value.scrollTo({
left: finalPosition,
behavior: 'smooth'
})
// console.log('Scrolled to position:', finalPosition,
// 'Max scroll:', maxScroll,
// 'Container width:', clientWidth,
// 'Scroll width:', scrollWidth)
})
} else {
// console.log('No scrollable content, but content is loaded')
// 即使没有滚动内容,也尝试激活滚动机制
activateScrollMechanism(() => {
Detail.value.scrollTo({
left: 645,
behavior: 'smooth'
})
})
}
}
const activateScrollMechanism = (callback) => {
if (!Detail.value) {
if (callback) {
callback()
}
return
}
// 预激活滚动机制
Detail.value.scrollTo({ left: 1, behavior: 'instant' })
setTimeout(() => {
Detail.value.scrollTo({ left: 0, behavior: 'instant' })
setTimeout(() => {
if (callback) {
callback()
}
}, 50)
}, 20)
}
</script>
<style lang="scss">
.fh2-container {

View File

@@ -203,7 +203,6 @@ const rules = {
model[key] = props.data[key]
if(key === 'illegalType') {
model[key] = props.data[key].map(i => i.label)
console.log(model[key])
}
})
}

View File

@@ -5,14 +5,6 @@
<FilterCom ref="Filter" :filter-buttons="filterButtons" :filter-items="items" :filter-model="model" @handle="handle"/>
<!-- 多选框筛选 -->
<div class="Form">
<!-- 暂时只支持单个类型的筛选 -->
<!-- <el-checkbox-group v-model="illegalType" :max="1">
<el-checkbox
v-for="item in illegalTypes"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-checkbox-group> -->
<el-radio-group v-model="illegalType" @change="changeRadio">
<el-radio v-for="item in illegalTypes"
:key="item.value"
@@ -98,9 +90,6 @@ import { useRoute } from 'vue-router'
import DialogComponent from '@/components/Dialog/screen.vue'
import DetailComponent from './detail.vue'
import useMapStore from '@/store/modules/map'
const mapStore = useMapStore()
const route = useRoute()
const illegalTypes = [
{ value: '未封舱预警', label: '未封舱预警', prop: 'warning' },
@@ -258,7 +247,6 @@ const columns = ref([
])
const config = {
label: '操作',
// width: 60
width: 150
}
const operate = [
@@ -344,7 +332,6 @@ const initData = () => {
const params = new FormData()
const obj = {
...model,
// illegalType: illegalType.value.join(','),
illegalType: illegalType.value,
beginTime: model.time && model.time[0] ? model.time[0] : '',
endTime: model.time && model.time[1] ? model.time[1] : '',
@@ -352,9 +339,6 @@ const initData = () => {
pageSize: pagination.size
}
delete obj.time
// Object.keys(obj).forEach((key) => {
// params.append(key, obj[key])
// })
videoIdentificationPage(obj).then(res => {
if (res.success) {
tableData.value = res.result.records.map(i => {
@@ -378,63 +362,6 @@ const initData = () => {
}).finally(() => {
loading.value = false
})
// tableData.value = [
// {
// 'aisStatus': '未开启',
// 'belongPort': '',
// 'boatCodePath': '',
// 'boatName': '浙周田货0998',
// 'boatNameEn': 'WUCHUANHAO',
// 'cog': '0',
// 'createAt': '2025-12-01 11:04:44',
// 'createBy': '',
// 'crossLineTime': null,
// 'delFlag': 0,
// 'distance': null,
// 'draftMarks': null,
// 'entryOut': '',
// 'height': 0.00000000,
// 'heightRange': '',
// 'hkResult': '',
// 'id': 76168,
// 'identificationType': '',
// 'illegalType': '未穿救生衣预警,未封舱预警,未悬挂国旗',
// 'isCloseDoor': '否',
// 'isHasAis': '否',
// 'jacketStatus': '',
// 'latitude': null,
// 'length': 0.00000000,
// 'longitude': null,
// 'mmsi': '413823183',
// 'shipType': '集装箱船',
// 'sourcePicPath': 'http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_1_20251201_110559_644.jpg',
// 'speed': null,
// 'streetName': '',
// 'sysShipName': '',
// 'sysUpdateName': '',
// 'systemResult': '',
// 'takeTime': '2025-12-23 11:06:08',
// 'takeType': '卡口',
// 'trackerPicPath': 'http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_1_20251201_110559_644.jpg,http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_2_20251201_110603_161.jpg,http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_3_20251201_110605_535.jpg,http://198.16.74.209:6060/pic/2025-12-01/198.16.74.187/20251201_110559/ship_4_20251201_110608_298.jpg',
// 'updateAt': '2025-12-01 11:04:44',
// 'updateBy': '',
// 'videoCode': 'fd3b45e1429a4e47bba873af602a9bed',
// 'videoName': '卧旗--雷云一体机',
// 'videoUrl': '',
// 'width': 0.00000000
// }, {}
// ].map(i => {
// return {
// ...i,
// illegalType: i.illegalType?.split(',').map(j => {
// return {
// value: illegalTypes.find(type => type.value === j)?.prop,
// label: j
// }
// })
// }
// })
// handle('check', tableData.value.length > 0 ? tableData.value[0] : {})
}
const changeRadio = () => {
nextTick(() => {
@@ -526,17 +453,7 @@ const closeDetail = () => {
initData()
}
onMounted(() => {
model.isHasBoatName = mapStore.dialog.data.isHasBoatName
console.log(model)
if(Object.keys(route.query).length > 0 && route.query.type === 'alarm') {
model.takeType = route.query.takeType
illegalType.value = route.query.illegalType
nextTick(() => {
if (Filter.value && Filter.value.model) {
Filter.value.model.takeType = route.query.takeType
}
})
}
model.isHasBoatName = route.query.isHasBoatName
initData()
})

View File

@@ -1,22 +1,18 @@
<template>
<!-- 识别记录页面 -->
<DialogComponent :style="{ resize: 'both', overflow: 'auto' }" v-if="type === 'alarm'" title="识别记录" width="1800" :draggable="true" :modal="false" @close="close">
<DialogComponent :style="{ resize: 'both', overflow: 'auto' }" title="识别记录" width="1800" :draggable="true" :modal="false" @close="close">
<AlarmCom/>
</DialogComponent>
</template>
<script setup>
import { computed } from 'vue'
import DialogComponent from '@/components/Dialog/screen.vue'
import AlarmCom from './alarm/index.vue'
import { useRouter } from 'vue-router'
import useMapStore from '@/store/modules/map'
const mapStore = useMapStore()
const router = useRouter()
const type = computed(() => mapStore.dialog.type)
const close = () => {
mapStore.updateDialog(false)
router.push('/')
}
</script>

View File

@@ -6,24 +6,13 @@
<router-view></router-view>
</div>
<Alarm/>
<LargeModelCom v-if="visible && type === 'largeModel'"/>
<IdentificationCom v-if="visible && type === 'alarm'"/>
<WallCom v-if="visible && (type==='CCTV'|| type==='UAV')"/>
</div>
</template>
<script setup>
import HeaderMenuComponent from '@/components/HeaderMenu/index.vue'
import MapComponent from '@/components/Map/index.vue'
import Alarm from '@/views/business/alarm/index.vue'
import LargeModelCom from '@/views/business/largeModel/index.vue'
import IdentificationCom from '@/views/business/identification/index.vue'
import WallCom from '@/views/business/wall/index.vue'
import useMapStore from '@/store/modules/map'
import { computed } from 'vue'
const mapStore = useMapStore()
const visible = computed(() => mapStore.dialog.visible)
const type = computed(() => mapStore.dialog.type)
window.name = 'business_window'
</script>
@@ -202,7 +191,7 @@ window.name = 'business_window'
position: absolute;
right: 7px;
i:not(.el-select__clear) {
// background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
background-size: 8px 6px;
width: 8px;
height: 6px;
@@ -230,7 +219,7 @@ window.name = 'business_window'
right: 7px;
top: 13px;
.el-input__suffix-inner{
// background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
background: url("@/assets/images/common/icon_suffix.png") center center no-repeat;
background-size: 8px 6px;
width: 8px;
height: 6px;

View File

@@ -256,13 +256,11 @@ const send = () => {
additionalContent += '\n' + data.mp4_url.join(' ') + '\n'
}
if(data.rtsp_url && Array.isArray(data.rtsp_url)) {
console.log('data.rtsp_url', data.rtsp_url)
data.rtsp_url.forEach(item => {
additionalContent += '\n' + JSON.stringify(item) + '\n'
})
}
if(data.rtsp_data && Array.isArray(data.rtsp_data)) {
console.log('data.rtsp_data', data.rtsp_data)
data.rtsp_data.forEach(item => {
additionalContent += '\n' + JSON.stringify(item) + '\n'
})

View File

@@ -54,7 +54,9 @@ import { dragEvent } from '@/utils/common'
import { getVideoStream, doStartOrStopUavAlgorithm } from '@/api/uav'
import { ElMessage } from 'element-plus'
import useMapStore from '@/store/modules/map'
import { useRouter } from 'vue-router'
const router = useRouter()
const mapStore = useMapStore()
const uavData = computed(() => mapStore.uavs.data)
const ModuleLeft = ref(null)
@@ -105,7 +107,7 @@ const initList = () => {
History.value.initList()
}
const close = () => {
mapStore.updateDialog({ visible: false, type: 'largeModel' })
router.push('/')
}
const openVideo = (data) => {
visible.value = data.visible

View File

@@ -70,7 +70,6 @@ const renderMedia = (url, text) => {
if (!url) {
return ''
}
console.log(url, 'url')
// 监控
if (url.startsWith('{') && url.includes('rtsp_url')) {
const videoNameMatch = JSON.parse(url).video_name
@@ -193,7 +192,29 @@ return ''
// 监控视频查看
const openVideoHandler = (index) => {
const data = videoArr.value[index]
codes.value = [ data.videoCode ]
// 雷云一体机一个视频
if(data.videoName.indexOf('雷云') !== -1) {
const videos = videoArr.value.filter(i => i.videoName.indexOf('雷云') !== -1).sort((a, b) => {
const index1 = a.videoName[a.videoName.length - 1]
const index2 = b.videoName[b.videoName.length - 1]
return index1 - index2
})
codes.value = [ videos[0].videoCode ]
}else{
if(data.videoName.indexOf('球机') !== -1) {
const videos = videoArr.value.filter(i => i.videoName.indexOf('球机') !== -1).sort((a, b) => {
const index1 = a.videoName[a.videoName.length - 1]
const index2 = b.videoName[b.videoName.length - 1]
return index1 - index2
})
codes.value = videos.map((i, index) => {
return i.previewUrl
})
}else{
codes.value = [ data.videoCode ]
}
}
nextTick(() => {
setTimeout(() => {
visible.value = true

View File

@@ -0,0 +1,85 @@
<template>
<!-- 业务配置页面 -->
<DialogComponent :style="{ resize: 'both', overflow: 'auto' }" title="业务配置" width="1800" :draggable="true" :modal="false" @close="close">
<div class="tabs-container">
<el-tabs v-model="type" tab-position="left" class="tabs">
<el-tab-pane v-for="item in tabs"
:key="item.value"
:label="item.label"
:name="item.value">
<template #label>
<span class="custom-tabs-label">
<span>{{item.label}}</span>
<el-icon v-if="type == item.value"><CaretRight /></el-icon>
</span>
</template>
</el-tab-pane>
</el-tabs>
<CCTVComponent v-if="type === 'cctv'"/>
</div>
</DialogComponent>
</template>
<script setup>
import { CaretRight } from '@element-plus/icons-vue'
import DialogComponent from '@/components/Dialog/screen.vue'
import CCTVComponent from './monitor/index.vue'
import { useRouter } from 'vue-router'
import { ref } from 'vue'
const router = useRouter()
const tabs = ref([
{
value: 'cctv',
label: 'CCTV'
},
{
value: 'UAV',
label: '无人机'
}
])
const type = ref('cctv')
const close = () => {
router.push('/')
}
</script>
<style scoped lang="scss">
.tabs-container{
display: flex;
width: 100%;
}
.el-tabs {
box-sizing: border-box;
:deep(.el-tabs__header){
&.is-left{
margin-right: 20px !important;
}
.el-tabs__nav-wrap::after {
background-color: rgba(0, 192, 255, 0.2) !important;
}
.el-tabs__item {
width: 200px;
color: rgba(68, 165, 255, 1);
justify-content: center;
&.is-active{
color: #00C0FF;
background: rgba(10, 169, 255, 0.15);
.el-icon{
position: absolute;
right: 0;
}
}
}
.el-tabs__item.is-disabled {
cursor: not-allowed;
}
.el-tabs__active-bar {
background-color: #00C0FF;
}
}
}
.content{
width: calc(100% - 220px);
}
</style>

View File

@@ -0,0 +1,262 @@
<template>
<el-form class="detail-form" ref="form" :model="model" label-width="auto" :rules="rules" label-suffix="">
<el-form-item v-for="(item,index) in items" :key="index" :label="item.label" :prop="item.prop" :style="`grid-column-start: span ${item.width || '1'};`">
<el-input v-if="item.type === 'input'" v-model="model[item.prop]" :readonly="type==='check'"/>
<el-input v-if="item.type === 'textarea'" autosize v-model="model[item.prop]" :rows="item.rows || 2" type="textarea" :readonly="type==='check'"/>
<el-input-number v-if="item.type === 'number'" v-model="model[item.prop]" :min="item.min || 0" style="width: 100%;" :readonly="type==='check'"/>
<el-select v-if="item.type === 'select'" v-model="model[item.prop]" :clearable="item.clearable" :disabled="type==='check'">
<el-option v-for="(opt, index) in item.options" :key="index" :label="opt.label" :value="opt.value">
</el-option>
</el-select>
<el-date-picker v-if="item.type === 'datetime'" v-model="model[item.prop]" type="datetime"
:placeholder="item.placeholder ? item.placeholder : '请选择时间'" value-format="YYYY-MM-DD HH:mm:ss"
:clearable="item.clearable" style="width:100%" :readonly="type==='check'">
</el-date-picker>
</el-form-item>
</el-form>
<div class="button-wrapper" v-if="type !== 'check'">
<el-button type="primary" @click="validate">提交</el-button>
<el-button type="primary" @click="$emit('close')">取消</el-button>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { dsVideoSave, dsVideoUpdate } from '@/api/device'
const emit = defineEmits([ 'close' ])
const props = defineProps({
data: {
default() {
return {}
},
required: false,
type: Object
},
type: {
default: '',
required: false,
type: String
}
})
const form = ref(null)
const model = reactive({
videoCode: '',
videoName: '',
longitude: '',
latitude: '',
height: 0,
visionDistance: 5,
videoIp: '',
videoPort: '',
videoAccount: '',
videoPassword: '',
channelNo: '0',
videoType: '云台',
belongUnit: '',
runStatus: '正常',
beBayonet: 0,
bePlay: 0,
playIndex: 1,
playTime: 5
})
const items = ref([
{
label: '监控编号',
prop: 'videoCode',
type: 'input'
},
{
label: '监控名称',
prop: 'videoName',
type: 'input'
},
{
label: '经度',
prop: 'longitude',
type: 'input'
},
{
label: '纬度',
prop: 'latitude',
type: 'input'
},
{
label: '监控安装高度(米)',
prop: 'height',
type: 'number'
},
{
label: '可视距离(公里)',
prop: 'visionDistance',
type: 'number'
},
{
label: '设备ip',
prop: 'videoIp',
type: 'input'
},
{
label: '设备端口号',
prop: 'videoPort',
type: 'input'
},
{
label: '设备账号',
prop: 'videoAccount',
type: 'input'
},
{
label: '设备密码',
prop: 'videoPassword',
type: 'input'
},
{
label: '监控类型',
prop: 'videoType',
type: 'select',
options: [
{ value: '球机', label: '球机' },
{ value: '云台', label: '云台' }
]
},
{
label: '运行状态',
prop: 'runStatus',
type: 'select',
options: [
{ value: '正常', label: '正常' },
{ value: '故障', label: '故障' }
]
},
{
label: '是否卡口',
prop: 'beBayonet',
type: 'select',
options: [
{ value: 1, label: '是' },
{ value: 0, label: '否' }
]
},
{
label: '是否轮询',
prop: 'bePlay',
type: 'select',
options: [
{
value: 1,
label: '是'
},
{
value: 0,
label: '否'
}
]
},
{
label: '轮询顺序',
prop: 'playIndex',
type: 'number'
},
{
label: '轮询时间(s)',
prop: 'playTime',
type: 'number'
},
{
label: '通道号',
prop: 'channelNo',
type: 'input'
},
{
label: '监管所属单位',
prop: 'belongUnit',
type: 'input'
}
])
const rules = {
videoCode: [ { required: true, message: '监控编号 未填写', trigger: 'blur' } ],
videoName: [ { required: true, message: '监控名称 未填写', trigger: 'blur' } ],
longitude: [ { required: true, message: '经度 未填写', trigger: 'blur' } ],
latitude: [ { required: true, message: '纬度 未填写', trigger: 'blur' } ],
height: [ { required: true, message: '监控安装高度 未填写', trigger: 'blur' } ],
visionDistance: [ { required: true, message: '可视距离 未填写', trigger: 'blur' } ],
videoIp: [ { required: true, message: '设备ip 未填写', trigger: 'blur' } ],
videoPort: [ { required: true, message: '设备端口号 未填写', trigger: 'blur' } ],
videoAccount: [ { required: true, message: '设备账号 未填写', trigger: 'blur' } ],
videoPassword: [ { required: true, message: '设备密码 未填写', trigger: 'blur' } ],
videoType: [ { required: true, message: '监控类型 未填写', trigger: 'blur' } ],
runStatus: [ { required: true, message: '运行状态 未填写', trigger: 'blur' } ],
beBayonet: [ { required: true, message: '是否卡口 未填写', trigger: 'change' } ],
bePlay: [ { required: true, message: '是否轮询 未填写', trigger: 'change' } ]
}
/**
* 初始化赋值
*/
const initData = () => {
if (props.type !== 'add') {
Object.keys(model).forEach((key) => {
model[key] = props.data[key]
})
}
}
/**
* 表单校验
*/
const validate = () => {
form.value.validate().then((valid) => {
if (valid) {
submit()
}
})
}
/**
* 表单校验
*/
const submit = () => {
const params = {
...model
}
if(props.type === 'add') {
dsVideoSave(params).then(() => {
ElMessage.success('提交成功!')
emit('close', true)
})
}else{
dsVideoUpdate({ ...params, id: props.data.id }).then(() => {
ElMessage.success('提交成功!')
emit('close', true)
})
}
}
initData()
</script>
<style lang="scss" scoped>
.detail-form{
display: grid;
box-sizing: border-box;
grid-template-columns: 1fr 1fr;
column-gap: 20px;
}
.button-wrapper{
width: 100%;
margin-top: 20px;
display: flex;
justify-content: center;
gap: 20px;
}
</style>

View File

@@ -0,0 +1,263 @@
<template>
<div class="content">
<FilterComponent :filter-buttons="filterButtons" :filter-items="items" :filter-model="model" @handle="handle"/>
<TableComponent style="height: 650px;" :loading="loading" :data="tableData" :columns="columns" :config="config" :operate="operate" @handle="handle"/>
<el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size"
:total="pagination.total" :page-sizes="[10, 15, 20, 50]" layout="total, sizes, prev, pager, next, jumper"
@size-change="initData" @current-change="initData"
style="margin-top: 10px; justify-content: center;" />
<DialogComponent v-if="detail.visible" :title="detail.title[detail.type]" :modal="true" width="800"
@close="closeDetail">
<DetailComponent :data="detail.data" :type="detail.type" @close="closeDetail" />
</DialogComponent>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import FilterComponent from '@/components/Filter/index.vue'
import TableComponent from '@/components/Table/index2.vue'
import DialogComponent from '@/components/Dialog/screen.vue'
import DetailComponent from './detail.vue'
import { dsVideoPage, dsVideoDelete } from '@/api/device'
const items = [
{
label: '监控名称',
prop: 'videoName',
width: '180px',
type: 'input'
},
{
label: '监控编号',
prop: 'videoCode',
width: '180px',
type: 'input'
},
{
label: '是否卡口',
prop: 'beBayonet',
type: 'select',
width: '180px',
options: [
{ value: 1, label: '是' },
{ value: 0, label: '否' }
]
},
{
label: '是否轮询',
prop: 'bePlay',
type: 'select',
width: '180px',
options: [
{ value: 1, label: '是' },
{ value: 0, label: '否' }
]
}
]
const filterButtons = ref([
{
name: '检索',
prop: 'query',
theme: 'primary',
type: 'button'
},
{
name: '新增',
prop: 'add',
theme: 'primary',
type: 'button'
}
])
let model = {
videoName: '',
videoCode: '',
beBayonet: '',
bePlay: ''
}
const loading = ref(false)
const tableData = ref([])
const columns = [
{
label: '监控编号',
prop: 'videoCode',
width: 290
},
{
label: '监控名称',
prop: 'videoName',
width: 250
},
{
label: '经度',
prop: 'longitude',
width: 100
},
{
label: '纬度',
prop: 'latitude',
width: 100
},
{
label: '设备ip',
prop: 'videoIp',
width: 120
},
{
label: '设备端口号',
prop: 'videoPort',
width: 90
},
{
label: '通道号',
prop: 'channelNo',
width: 60
},
{
label: '监控类型',
prop: 'videoType',
width: 80
},
{
label: '运行状态',
prop: 'runStatus',
width: 80
},
{
label: '是否卡口',
prop: 'beBayonetF',
width: 80
},
{
label: '是否轮询',
prop: 'bePlayF',
width: 80
}
]
const config = {
type: 'index',
width: 110,
align: 'center'
}
const operate = [
{
label: '编辑',
prop: 'edit',
theme: 'success'
},
{
label: '删除',
prop: 'remove',
theme: 'danger'
}
]
const pagination = {
current: 1,
size: 15,
total: 0
}
const detail = reactive({
visible: false,
title: {
add: '监控信息新增',
check: '监控信息查看',
edit: '监控信息编辑'
},
type: 'add',
data: {}
})
const initData = () => {
loading.value = true
const data = {
...model,
pageNum: pagination.current,
pageSize: pagination.size
}
dsVideoPage(data).then(res => {
tableData.value = res.rows.map(i => {
return {
...i,
beBayonetF: i.beBayonet === 1 ? '是' : '否',
bePlayF: i.bePlay === 1 ? '是' : '否'
}
})
pagination.total = res.total
}).finally(() => {
loading.value = false
})
}
/**
* 按钮操作
* @param type 操作类型
* @param data 数据
*/
const handle = (type, data) => {
switch (type) {
case 'query':
model = { ...data }
pagination.current = 1
initData()
break
case 'add':
detail.visible = true
detail.type = 'add'
detail.data = {}
break
case 'check':
case 'edit':
detail.visible = true
detail.type = type
detail.data = { ...data }
break
case 'remove': {
ElMessageBox.confirm(
'是否移除该条数据?',
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
const params = {
id: data.id
}
dsVideoDelete(params).then(() => {
ElMessage.success('移除成功!')
initData()
})
}).catch(() => { /* empty */ })
break
}
default:
break
}
}
/**
* 关闭详情弹窗
* @param refresh 是否刷新数据
*/
const closeDetail = (refresh) => {
detail.visible = false
if (refresh) {
initData()
}
}
initData()
</script>
<style lang="scss" scoped>
</style>

View File

@@ -39,7 +39,7 @@
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'
import { getVideoStream } from '@/api/uav'
const control = ref([
{ label: '1X1', value: 1 },
@@ -93,7 +93,6 @@ const closeVideo = (data, item) => {
const handleNodeClick = (node) => {
if(props.videoType === '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 }
}

View File

@@ -12,13 +12,12 @@ import { computed, nextTick, ref, watch } from 'vue'
import DialogComponent from '@/components/Dialog/screen.vue'
import TreeCom from './tree.vue'
import GridCom from './grid.vue'
import { useRouter } from 'vue-router'
import useMapStore from '@/store/modules/map'
import { useRoute, useRouter } from 'vue-router'
import { dragEvent, resizeEvent } from '@/utils/common'
const mapStore = useMapStore()
const route = useRoute()
const router = useRouter()
const type = computed(() => mapStore.dialog.type)
const type = computed(() => route.query.type)
const Tree = ref(null)
const Grid = ref(null)
@@ -32,7 +31,7 @@ const handle = (node) => {
Grid.value.handleNodeClick(node)
}
const close = () => {
mapStore.updateDialog(false)
router.push('/')
}
watch(() => type.value, () => {
if(type.value) {
@@ -48,8 +47,8 @@ watch(() => type.value, () => {
*
*/
const drag = (e) => {
if(e.target.className.includes('el-dialog-title')) {
dragEvent(e, 'el-dialog-title', () => {
if(e.target.className.includes('el-dialog-title') || e.target.className.includes('el-dialog__title')) {
dragEvent(e, 'el-dialog', () => {
if(Grid.value.videoPreview) {
Grid.value.videoPreview.forEach(i => {
i.initResize(false)

View File

@@ -16,8 +16,8 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { findVideoLevelList, findUavPage, findEnvPage, dsVideoList } from '@/api/device.js'
import { ref } from 'vue'
import { findVideoLevelList, findUavPage } from '@/api/device.js'
const emit = defineEmits([ 'handle' ])

View File

@@ -12,7 +12,6 @@
</el-breadcrumb>
<div class="title">千帆智瞰</div>
<div class="user">
<!-- <el-button :icon="Monitor" @click="goBusiness">业务系统</el-button> -->
<el-button :icon="Monitor" @click="goHome">返回门户</el-button>
<el-avatar :icon="UserFilled" :size="28"/>
<el-dropdown @command="handle">
@@ -150,12 +149,6 @@ const validate = () => {
}
})
}
/**
* 跳转到系统设置
*/
const goBusiness = () => {
window.open('/', 'business_window')
}
const goHome = () => {
window.open('/', 'business_window')
}