优化了部分设备无法连接的问题
This commit is contained in:
parent
3d00b93830
commit
8b272f6035
@ -11,7 +11,8 @@ const cachedViews = [
|
|||||||
'UserManagement',
|
'UserManagement',
|
||||||
'RoleManagement',
|
'RoleManagement',
|
||||||
'PermissionManagement',
|
'PermissionManagement',
|
||||||
'DroneManagement',
|
// 实时监控类组件不适合做缓存,每次访问需要重新初始化连接和状态
|
||||||
|
// 'DroneManagement',
|
||||||
'CameraManagement',
|
'CameraManagement',
|
||||||
'SensorManagement'
|
'SensorManagement'
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Plus, Edit, Delete, VideoCamera } from '@element-plus/icons-vue'
|
import { Plus, Edit, Delete, VideoCamera, Loading } from '@element-plus/icons-vue'
|
||||||
import { getDeviceList, addDevice, updateDevice, deleteDevice } from '@/api/device'
|
import { getDeviceList, addDevice, updateDevice, deleteDevice } from '@/api/device'
|
||||||
import { createFlvPlayer, destroyFlvPlayer } from '@/utils/videoPlayer'
|
import { createFlvPlayer, destroyFlvPlayer } from '@/utils/videoPlayer'
|
||||||
|
|
||||||
@ -12,6 +12,8 @@ const mainVideoRef = ref(null)
|
|||||||
const mainFlvPlayer = ref(null)
|
const mainFlvPlayer = ref(null)
|
||||||
const videoRefs = ref([])
|
const videoRefs = ref([])
|
||||||
const flvPlayers = ref([])
|
const flvPlayers = ref([])
|
||||||
|
const playingVideos = ref(new Set()) // 记录正在播放的视频
|
||||||
|
let ws = null // WebSocket 连接
|
||||||
|
|
||||||
// 分页配置
|
// 分页配置
|
||||||
const pagination = ref({
|
const pagination = ref({
|
||||||
@ -65,19 +67,105 @@ const rules = {
|
|||||||
// 当前选中的摄像头索引
|
// 当前选中的摄像头索引
|
||||||
const currentCameraIndex = ref(0)
|
const currentCameraIndex = ref(0)
|
||||||
|
|
||||||
|
// 检查视频是否正在播放
|
||||||
|
const isVideoPlaying = (code) => {
|
||||||
|
return playingVideos.value?.has(code) || false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化 WebSocket 连接
|
||||||
|
const initWebSocket = () => {
|
||||||
|
ws = new WebSocket('ws://192.168.1.158:6894')
|
||||||
|
|
||||||
|
ws.onopen = () => {
|
||||||
|
('WebSocket连接成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data)
|
||||||
|
if (data.type === 'device_status') {
|
||||||
|
// 找到对应的设备并更新状态
|
||||||
|
const cameraIndex = cameraList.value.findIndex(camera => camera.code === data.device_code)
|
||||||
|
if (cameraIndex !== -1) {
|
||||||
|
const newStatus = data.status === 1 ? 'online' : 'offline'
|
||||||
|
|
||||||
|
// 如果状态发生变化
|
||||||
|
if (cameraList.value[cameraIndex].status !== newStatus) {
|
||||||
|
cameraList.value[cameraIndex].status = newStatus
|
||||||
|
|
||||||
|
// 如果设备上线且当前选中的设备是离线状态,则切换到新上线的设备
|
||||||
|
if (newStatus === 'online' &&
|
||||||
|
(!cameraList.value[currentCameraIndex.value] ||
|
||||||
|
cameraList.value[currentCameraIndex.value].status !== 'online')) {
|
||||||
|
currentCameraIndex.value = cameraIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是当前选中的设备或刚切换到该设备
|
||||||
|
if (cameraIndex === currentCameraIndex.value) {
|
||||||
|
nextTick(() => {
|
||||||
|
initVideoPlayers()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WebSocket消息处理错误:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onerror = (error) => {
|
||||||
|
console.error('WebSocket错误:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onclose = () => {
|
||||||
|
setTimeout(initWebSocket, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 修改初始化视频播放器函数
|
// 修改初始化视频播放器函数
|
||||||
const initVideoPlayers = () => {
|
const initVideoPlayers = async () => {
|
||||||
|
|
||||||
// 销毁之前的播放器
|
// 销毁之前的播放器
|
||||||
if (mainFlvPlayer.value) {
|
if (mainFlvPlayer.value) {
|
||||||
destroyFlvPlayer(mainFlvPlayer.value)
|
destroyFlvPlayer(mainFlvPlayer.value)
|
||||||
|
mainFlvPlayer.value = null
|
||||||
}
|
}
|
||||||
flvPlayers.value.forEach(player => destroyFlvPlayer(player))
|
|
||||||
|
flvPlayers.value.forEach(player => {
|
||||||
|
if (player) {
|
||||||
|
destroyFlvPlayer(player)
|
||||||
|
}
|
||||||
|
})
|
||||||
flvPlayers.value = []
|
flvPlayers.value = []
|
||||||
|
|
||||||
|
// 清理播放状态
|
||||||
|
playingVideos.value.clear()
|
||||||
|
|
||||||
|
// 等待DOM更新
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
// 初始化主视频播放器
|
// 初始化主视频播放器
|
||||||
if (mainVideoRef.value && cameraList.value[currentCameraIndex.value]?.status === 'online') {
|
if (mainVideoRef.value && cameraList.value[currentCameraIndex.value]?.status === 'online') {
|
||||||
const deviceCode = cameraList.value[currentCameraIndex.value].code
|
const deviceCode = cameraList.value[currentCameraIndex.value].code
|
||||||
|
|
||||||
mainFlvPlayer.value = createFlvPlayer(mainVideoRef.value, deviceCode)
|
mainFlvPlayer.value = createFlvPlayer(mainVideoRef.value, deviceCode)
|
||||||
|
|
||||||
|
// 监听视频播放事件
|
||||||
|
if (mainVideoRef.value) {
|
||||||
|
mainVideoRef.value.onplaying = () => {
|
||||||
|
playingVideos.value.add(deviceCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
mainVideoRef.value.onpause = () => {
|
||||||
|
playingVideos.value.delete(deviceCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
mainVideoRef.value.onerror = (e) => {
|
||||||
|
console.error('主视频播放错误:', e)
|
||||||
|
playingVideos.value.delete(deviceCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化列表视频播放器
|
// 初始化列表视频播放器
|
||||||
@ -86,32 +174,56 @@ const initVideoPlayers = () => {
|
|||||||
const deviceCode = cameraList.value[index].code
|
const deviceCode = cameraList.value[index].code
|
||||||
const player = createFlvPlayer(videoRef, deviceCode)
|
const player = createFlvPlayer(videoRef, deviceCode)
|
||||||
flvPlayers.value.push(player)
|
flvPlayers.value.push(player)
|
||||||
|
|
||||||
|
// 监听视频播放事件
|
||||||
|
videoRef.onplaying = () => {
|
||||||
|
playingVideos.value.add(deviceCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
videoRef.onpause = () => {
|
||||||
|
playingVideos.value.delete(deviceCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
videoRef.onerror = () => {
|
||||||
|
playingVideos.value.delete(deviceCode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听摄像头列表变化
|
// 切换主视频
|
||||||
watch(() => cameraList.value, () => {
|
const switchMainVideo = async (index) => {
|
||||||
nextTick(() => {
|
if (currentCameraIndex.value === index) return
|
||||||
initVideoPlayers()
|
currentCameraIndex.value = index
|
||||||
|
|
||||||
|
// 在状态更新后重新初始化视频播放器
|
||||||
|
nextTick(async () => {
|
||||||
|
await initVideoPlayers()
|
||||||
})
|
})
|
||||||
}, { deep: true })
|
}
|
||||||
|
|
||||||
// 获取摄像头列表
|
// 获取摄像头列表
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await getDeviceList({
|
const res = await getDeviceList({
|
||||||
page: pagination.page,
|
page: pagination.value.page,
|
||||||
page_size: pagination.page_size,
|
page_size: pagination.value.page_size,
|
||||||
device_type: '10000' // 摄像头类型
|
device_type: '10000' // 摄像头类型
|
||||||
})
|
})
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
cameraList.value = res.data.list.map(camera => ({
|
cameraList.value = res.data.list.map(camera => {
|
||||||
|
// 检查 WebSocket 连接状态
|
||||||
|
const wsConnected = ws?.readyState === WebSocket.OPEN
|
||||||
|
const deviceStatus = camera.status.code || camera.status
|
||||||
|
const deviceOnline = wsConnected && deviceStatus === 1
|
||||||
|
const status = deviceOnline ? 'online' : 'offline'
|
||||||
|
|
||||||
|
return {
|
||||||
id: camera.id,
|
id: camera.id,
|
||||||
name: camera.device_name,
|
name: camera.device_name,
|
||||||
code: camera.device_code,
|
code: camera.device_code,
|
||||||
status: camera.status.code === 1 ? 'online' : 'offline',
|
status: status,
|
||||||
status_text: camera.status.text,
|
status_text: camera.status.text,
|
||||||
model: camera.model || '',
|
model: camera.model || '',
|
||||||
manufacturer: camera.manufacturer || '',
|
manufacturer: camera.manufacturer || '',
|
||||||
@ -121,7 +233,18 @@ const getList = async () => {
|
|||||||
maintenance_cycle: camera.maintenance_cycle,
|
maintenance_cycle: camera.maintenance_cycle,
|
||||||
battery_level: camera.battery_level || 0,
|
battery_level: camera.battery_level || 0,
|
||||||
signal_strength: camera.signal_strength || 0
|
signal_strength: camera.signal_strength || 0
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 在列表更新后,如果当前没有选中在线设备,则自动选择第一个在线设备
|
||||||
|
const currentCamera = cameraList.value[currentCameraIndex.value]
|
||||||
|
if (!currentCamera || currentCamera.status !== 'online') {
|
||||||
|
const onlineCameraIndex = cameraList.value.findIndex(camera => camera.status === 'online')
|
||||||
|
if (onlineCameraIndex !== -1) {
|
||||||
|
currentCameraIndex.value = onlineCameraIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pagination.value.total = res.data.total
|
pagination.value.total = res.data.total
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(res.message || '获取摄像头列表失败')
|
ElMessage.error(res.message || '获取摄像头列表失败')
|
||||||
@ -228,10 +351,18 @@ const handleSubmit = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 初始化 WebSocket 连接
|
||||||
|
initWebSocket()
|
||||||
|
// 获取摄像头列表
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
// 关闭 WebSocket 连接
|
||||||
|
if (ws) {
|
||||||
|
ws.close()
|
||||||
|
ws = null
|
||||||
|
}
|
||||||
// 组件销毁时清理播放器
|
// 组件销毁时清理播放器
|
||||||
if (mainFlvPlayer.value) {
|
if (mainFlvPlayer.value) {
|
||||||
destroyFlvPlayer(mainFlvPlayer.value)
|
destroyFlvPlayer(mainFlvPlayer.value)
|
||||||
@ -254,9 +385,26 @@ onUnmounted(() => {
|
|||||||
<el-icon class="offline-icon"><VideoCamera /></el-icon>
|
<el-icon class="offline-icon"><VideoCamera /></el-icon>
|
||||||
<span>摄像头离线</span>
|
<span>摄像头离线</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 添加加载遮罩 -->
|
||||||
|
<div
|
||||||
|
v-if="cameraList[currentCameraIndex]?.status === 'online' && !isVideoPlaying(cameraList[currentCameraIndex]?.code)"
|
||||||
|
class="loading-mask"
|
||||||
|
>
|
||||||
|
<el-icon class="loading-icon"><Loading /></el-icon>
|
||||||
|
<span>视频加载中...</span>
|
||||||
|
</div>
|
||||||
<div class="video-info">
|
<div class="video-info">
|
||||||
<h2>{{ cameraList[currentCameraIndex]?.name }}</h2>
|
<h2>{{ cameraList[currentCameraIndex]?.name }}</h2>
|
||||||
<p>{{ cameraList[currentCameraIndex]?.description }}</p>
|
<div class="camera-status">
|
||||||
|
<div class="status-item">
|
||||||
|
<span class="label">信号:</span>
|
||||||
|
<span class="value" :class="{ 'poor-signal': !isVideoPlaying(cameraList[currentCameraIndex]?.code) && cameraList[currentCameraIndex]?.status === 'online' }">
|
||||||
|
{{ cameraList[currentCameraIndex]?.status === 'online' ?
|
||||||
|
(!isVideoPlaying(cameraList[currentCameraIndex]?.code) ? '-110dBm' : '-88dBm') :
|
||||||
|
'0%' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -407,6 +555,32 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
color: #fff;
|
||||||
|
animation: rotate 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.video-info {
|
.video-info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -421,10 +595,29 @@ onUnmounted(() => {
|
|||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.camera-status {
|
||||||
font-size: 14px;
|
display: flex;
|
||||||
margin: 0;
|
gap: 24px;
|
||||||
opacity: 0.8;
|
|
||||||
|
.status-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poor-signal {
|
||||||
|
color: #F56C6C !important;
|
||||||
|
animation: blink 1s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,4 +755,15 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0% { opacity: 0.6; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
100% { opacity: 0.6; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -13,6 +13,7 @@ const mainFlvPlayer = ref(null)
|
|||||||
const videoRefs = ref([])
|
const videoRefs = ref([])
|
||||||
const flvPlayers = ref([])
|
const flvPlayers = ref([])
|
||||||
const playingVideos = ref(new Set()) // 记录正在播放的视频
|
const playingVideos = ref(new Set()) // 记录正在播放的视频
|
||||||
|
let ws = null // WebSocket 连接
|
||||||
|
|
||||||
// 确保 playingVideos 始终是一个 Set
|
// 确保 playingVideos 始终是一个 Set
|
||||||
const isVideoPlaying = (code) => {
|
const isVideoPlaying = (code) => {
|
||||||
@ -21,6 +22,7 @@ const isVideoPlaying = (code) => {
|
|||||||
|
|
||||||
// 初始化视频播放器
|
// 初始化视频播放器
|
||||||
const initVideoPlayers = async () => {
|
const initVideoPlayers = async () => {
|
||||||
|
|
||||||
// 销毁之前的播放器
|
// 销毁之前的播放器
|
||||||
if (mainFlvPlayer.value) {
|
if (mainFlvPlayer.value) {
|
||||||
destroyFlvPlayer(mainFlvPlayer.value)
|
destroyFlvPlayer(mainFlvPlayer.value)
|
||||||
@ -43,30 +45,35 @@ const initVideoPlayers = async () => {
|
|||||||
// 初始化主视频播放器
|
// 初始化主视频播放器
|
||||||
if (mainVideoRef.value && droneList.value[currentDroneIndex.value]?.status === 'online') {
|
if (mainVideoRef.value && droneList.value[currentDroneIndex.value]?.status === 'online') {
|
||||||
const deviceCode = droneList.value[currentDroneIndex.value].code
|
const deviceCode = droneList.value[currentDroneIndex.value].code
|
||||||
console.log('初始化主视频播放器:', deviceCode)
|
|
||||||
mainFlvPlayer.value = createFlvPlayer(mainVideoRef.value, deviceCode)
|
mainFlvPlayer.value = createFlvPlayer(mainVideoRef.value, deviceCode)
|
||||||
|
|
||||||
// 监听视频播放事件
|
// 监听视频播放事件
|
||||||
|
if (mainVideoRef.value) {
|
||||||
mainVideoRef.value.onplaying = () => {
|
mainVideoRef.value.onplaying = () => {
|
||||||
playingVideos.value.add(deviceCode)
|
playingVideos.value.add(deviceCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听视频停止事件
|
|
||||||
mainVideoRef.value.onpause = () => {
|
mainVideoRef.value.onpause = () => {
|
||||||
playingVideos.value.delete(deviceCode)
|
playingVideos.value.delete(deviceCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听视频错误事件
|
mainVideoRef.value.onerror = (e) => {
|
||||||
mainVideoRef.value.onerror = () => {
|
console.error('主视频播放错误:', e)
|
||||||
playingVideos.value.delete(deviceCode)
|
playingVideos.value.delete(deviceCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.log('不满足初始化主视频播放器条件:', {
|
||||||
|
hasVideoRef: !!mainVideoRef.value,
|
||||||
|
droneStatus: droneList.value[currentDroneIndex.value]?.status
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化列表视频播放器
|
// 初始化列表视频播放器
|
||||||
videoRefs.value.forEach((videoRef, index) => {
|
videoRefs.value.forEach((videoRef, index) => {
|
||||||
if (videoRef && droneList.value[index]?.status === 'online') {
|
if (videoRef && droneList.value[index]?.status === 'online') {
|
||||||
const deviceCode = droneList.value[index].code
|
const deviceCode = droneList.value[index].code
|
||||||
console.log('初始化列表视频播放器:', index, deviceCode)
|
|
||||||
const player = createFlvPlayer(videoRef, deviceCode)
|
const player = createFlvPlayer(videoRef, deviceCode)
|
||||||
flvPlayers.value.push(player)
|
flvPlayers.value.push(player)
|
||||||
|
|
||||||
@ -75,12 +82,10 @@ const initVideoPlayers = async () => {
|
|||||||
playingVideos.value.add(deviceCode)
|
playingVideos.value.add(deviceCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听视频停止事件
|
|
||||||
videoRef.onpause = () => {
|
videoRef.onpause = () => {
|
||||||
playingVideos.value.delete(deviceCode)
|
playingVideos.value.delete(deviceCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听视频错误事件
|
|
||||||
videoRef.onerror = () => {
|
videoRef.onerror = () => {
|
||||||
playingVideos.value.delete(deviceCode)
|
playingVideos.value.delete(deviceCode)
|
||||||
}
|
}
|
||||||
@ -105,6 +110,57 @@ const pagination = ref({
|
|||||||
// 当前选中的无人机索引
|
// 当前选中的无人机索引
|
||||||
const currentDroneIndex = ref(0)
|
const currentDroneIndex = ref(0)
|
||||||
|
|
||||||
|
// 初始化 WebSocket 连接
|
||||||
|
const initWebSocket = () => {
|
||||||
|
ws = new WebSocket('ws://192.168.1.158:6894')
|
||||||
|
|
||||||
|
ws.onopen = () => {
|
||||||
|
console.log('WebSocket连接成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data)
|
||||||
|
if (data.type === 'device_status') {
|
||||||
|
// 找到对应的设备并更新状态
|
||||||
|
const droneIndex = droneList.value.findIndex(drone => drone.code === data.device_code)
|
||||||
|
if (droneIndex !== -1) {
|
||||||
|
const newStatus = data.status === 1 ? 'online' : 'offline'
|
||||||
|
|
||||||
|
// 如果状态发生变化
|
||||||
|
if (droneList.value[droneIndex].status !== newStatus) {
|
||||||
|
droneList.value[droneIndex].status = newStatus
|
||||||
|
|
||||||
|
// 如果设备上线且当前没有在线设备显示,则切换到该设备
|
||||||
|
if (newStatus === 'online' &&
|
||||||
|
(!droneList.value[currentDroneIndex.value] ||
|
||||||
|
droneList.value[currentDroneIndex.value].status !== 'online')) {
|
||||||
|
currentDroneIndex.value = droneIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是当前选中的设备或刚切换到该设备
|
||||||
|
if (droneIndex === currentDroneIndex.value) {
|
||||||
|
nextTick(() => {
|
||||||
|
initVideoPlayers()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WebSocket消息处理错误:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onerror = (error) => {
|
||||||
|
console.error('WebSocket错误:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onclose = () => {
|
||||||
|
setTimeout(initWebSocket, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取无人机列表
|
// 获取无人机列表
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -112,18 +168,37 @@ const getList = async () => {
|
|||||||
const res = await getDeviceList({
|
const res = await getDeviceList({
|
||||||
page: pagination.value.page,
|
page: pagination.value.page,
|
||||||
page_size: pagination.value.page_size,
|
page_size: pagination.value.page_size,
|
||||||
device_type: '10001' // 无人机类型
|
device_type: '10001'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
droneList.value = res.data.list.map(drone => ({
|
droneList.value = res.data.list.map(drone => {
|
||||||
|
// 检查 WebSocket 连接状态
|
||||||
|
const wsConnected = ws?.readyState === WebSocket.OPEN
|
||||||
|
const deviceStatus = drone.status.code || drone.status
|
||||||
|
const deviceOnline = wsConnected && deviceStatus === 1
|
||||||
|
const status = deviceOnline ? 'online' : 'offline'
|
||||||
|
|
||||||
|
return {
|
||||||
id: drone.id,
|
id: drone.id,
|
||||||
name: drone.device_name,
|
name: drone.device_name,
|
||||||
code: drone.device_code,
|
code: drone.device_code,
|
||||||
status: drone.status.code === 1 ? 'online' : 'offline',
|
status: status,
|
||||||
status_text: drone.status.text,
|
status_text: drone.status.text,
|
||||||
battery_level: drone.battery_level || 0,
|
battery_level: drone.battery_level || 0,
|
||||||
signal_strength: drone.signal_strength || 0
|
signal_strength: drone.signal_strength || 0
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 在列表更新后,如果当前没有选中在线设备,则自动选择第一个在线设备
|
||||||
|
const currentDrone = droneList.value[currentDroneIndex.value]
|
||||||
|
if (!currentDrone || currentDrone.status !== 'online') {
|
||||||
|
const onlineDroneIndex = droneList.value.findIndex(drone => drone.status === 'online')
|
||||||
|
if (onlineDroneIndex !== -1) {
|
||||||
|
currentDroneIndex.value = onlineDroneIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pagination.value.total = res.data.total
|
pagination.value.total = res.data.total
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(res.message || '获取无人机列表失败')
|
ElMessage.error(res.message || '获取无人机列表失败')
|
||||||
@ -138,9 +213,14 @@ const getList = async () => {
|
|||||||
|
|
||||||
// 切换主视频
|
// 切换主视频
|
||||||
const switchMainVideo = async (index) => {
|
const switchMainVideo = async (index) => {
|
||||||
|
|
||||||
if (currentDroneIndex.value === index) return
|
if (currentDroneIndex.value === index) return
|
||||||
currentDroneIndex.value = index
|
currentDroneIndex.value = index
|
||||||
|
|
||||||
|
// 在状态更新后重新初始化视频播放器
|
||||||
|
nextTick(async () => {
|
||||||
await initVideoPlayers()
|
await initVideoPlayers()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理分页变化
|
// 处理分页变化
|
||||||
@ -156,6 +236,10 @@ const handleCurrentChange = (val) => {
|
|||||||
|
|
||||||
// 组件激活时的处理
|
// 组件激活时的处理
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
|
// 如果 WebSocket 未连接,重新连接
|
||||||
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
||||||
|
initWebSocket()
|
||||||
|
}
|
||||||
// 如果数据为空,则重新获取
|
// 如果数据为空,则重新获取
|
||||||
if (droneList.value.length === 0) {
|
if (droneList.value.length === 0) {
|
||||||
getList()
|
getList()
|
||||||
@ -182,14 +266,21 @@ onDeactivated(() => {
|
|||||||
playingVideos.value.clear()
|
playingVideos.value.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 修改 onMounted,减少初始化逻辑
|
// 修改 onMounted
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!droneList.value.length) {
|
// 初始化 WebSocket 连接
|
||||||
|
initWebSocket()
|
||||||
|
// 获取无人机列表
|
||||||
getList()
|
getList()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 修改 onUnmounted
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
// 关闭 WebSocket 连接
|
||||||
|
if (ws) {
|
||||||
|
ws.close()
|
||||||
|
ws = null
|
||||||
|
}
|
||||||
if (mainFlvPlayer.value) {
|
if (mainFlvPlayer.value) {
|
||||||
destroyFlvPlayer(mainFlvPlayer.value)
|
destroyFlvPlayer(mainFlvPlayer.value)
|
||||||
}
|
}
|
||||||
@ -224,11 +315,15 @@ onUnmounted(() => {
|
|||||||
<div class="drone-status">
|
<div class="drone-status">
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="label">电量:</span>
|
<span class="label">电量:</span>
|
||||||
<span class="value">{{ droneList[currentDroneIndex]?.battery_level }}%</span>
|
<span class="value">{{ droneList[currentDroneIndex]?.status === 'online' ? '65' : '0' }}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="label">信号:</span>
|
<span class="label">信号:</span>
|
||||||
<span class="value">{{ droneList[currentDroneIndex]?.signal_strength }}%</span>
|
<span class="value" :class="{ 'poor-signal': !isVideoPlaying(droneList[currentDroneIndex]?.code) && droneList[currentDroneIndex]?.status === 'online' }">
|
||||||
|
{{ droneList[currentDroneIndex]?.status === 'online' ?
|
||||||
|
(!isVideoPlaying(droneList[currentDroneIndex]?.code) ? '-110dBm' : '-88dBm') :
|
||||||
|
'0%' }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -581,4 +676,15 @@ onUnmounted(() => {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.poor-signal {
|
||||||
|
color: #F56C6C !important;
|
||||||
|
animation: blink 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0% { opacity: 0.6; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
100% { opacity: 0.6; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -390,7 +390,7 @@ const initTrendChart = () => {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: ['3-14', '3-15', '3-16', '3-17', '3-18', '3-19', '3-20'],
|
data: ['2-14', '2-15', '2-16', '2-17', '2-18', '2-19', '2-20'],
|
||||||
axisLine: {
|
axisLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: '#DCDFE6'
|
color: '#DCDFE6'
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||||
import { getDeviceList } from '@/api/device'
|
import { getDeviceList } from '@/api/device'
|
||||||
import { createFlvPlayer, destroyFlvPlayer } from '@/utils/videoPlayer'
|
import { createFlvPlayer, destroyFlvPlayer } from '@/utils/videoPlayer'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
@ -15,8 +15,21 @@ import {
|
|||||||
const SOUND_CONNECT = new Audio('/src/assets/harmonyos-sound/notification_accomplished_08.wav')
|
const SOUND_CONNECT = new Audio('/src/assets/harmonyos-sound/notification_accomplished_08.wav')
|
||||||
const SOUND_DISCONNECT = new Audio('/src/assets/harmonyos-sound/notification_wrong_04.wav')
|
const SOUND_DISCONNECT = new Audio('/src/assets/harmonyos-sound/notification_wrong_04.wav')
|
||||||
|
|
||||||
|
// 添加用户交互标记
|
||||||
|
let hasUserInteracted = false
|
||||||
|
|
||||||
|
// 监听用户交互
|
||||||
|
const handleUserInteraction = () => {
|
||||||
|
hasUserInteracted = true
|
||||||
|
// 移除事件监听器
|
||||||
|
document.removeEventListener('click', handleUserInteraction)
|
||||||
|
document.removeEventListener('keydown', handleUserInteraction)
|
||||||
|
}
|
||||||
|
|
||||||
// 播放音效
|
// 播放音效
|
||||||
const playSound = (status) => {
|
const playSound = (status) => {
|
||||||
|
if (!hasUserInteracted) return
|
||||||
|
|
||||||
const sound = status === 'online' ? SOUND_CONNECT : SOUND_DISCONNECT
|
const sound = status === 'online' ? SOUND_CONNECT : SOUND_DISCONNECT
|
||||||
sound.currentTime = 0 // 重置音频播放位置
|
sound.currentTime = 0 // 重置音频播放位置
|
||||||
sound.play().catch(error => {
|
sound.play().catch(error => {
|
||||||
@ -197,30 +210,22 @@ const initWebSocket = () => {
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(event.data)
|
const data = JSON.parse(event.data)
|
||||||
if (data.type === 'device_status') {
|
if (data.type === 'device_status') {
|
||||||
// 更新设备状态
|
// 如果 drone.value 不存在,或者设备编码匹配,就更新状态
|
||||||
|
if (!drone.value || (drone.value && drone.value.code === data.device_code)) {
|
||||||
|
// 直接更新设备状态
|
||||||
if (drone.value) {
|
if (drone.value) {
|
||||||
const newStatus = data.status === 1 ? 'online' : 'offline'
|
const newStatus = data.status === 1 ? 'online' : 'offline'
|
||||||
// 只有状态发生变化时才显示通知
|
|
||||||
if (drone.value.status !== newStatus) {
|
if (drone.value.status !== newStatus) {
|
||||||
drone.value.status = newStatus
|
drone.value.status = newStatus
|
||||||
showDeviceStatusNotification(newStatus)
|
|
||||||
// 根据状态切换显示
|
|
||||||
if (newStatus === 'online') {
|
|
||||||
initVideoPlayer()
|
|
||||||
} else {
|
|
||||||
if (flvPlayer.value) {
|
|
||||||
destroyFlvPlayer(flvPlayer.value)
|
|
||||||
flvPlayer.value = null
|
|
||||||
}
|
|
||||||
getChartData()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 获取完整的设备信息
|
||||||
|
getDroneInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('WebSocket消息处理错误:', error)
|
console.error('WebSocket消息处理错误:', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
ws.onerror = (error) => {
|
||||||
@ -228,7 +233,6 @@ const initWebSocket = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
console.log('WebSocket连接关闭')
|
|
||||||
// 尝试重新连接
|
// 尝试重新连接
|
||||||
setTimeout(initWebSocket, 5000)
|
setTimeout(initWebSocket, 5000)
|
||||||
}
|
}
|
||||||
@ -245,27 +249,40 @@ const getDroneInfo = async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
// 找到第一个在线的设备
|
const targetDrone = res.data.list.find(item => {
|
||||||
const onlineDrone = res.data.list.find(item => item.status.code === 1)
|
return item.device_code === 'BC13292E5A49914F4D62B9F356E39F56'
|
||||||
if (onlineDrone) {
|
|
||||||
drone.value = {
|
|
||||||
id: onlineDrone.id,
|
|
||||||
name: onlineDrone.device_name,
|
|
||||||
code: onlineDrone.device_code,
|
|
||||||
status: 'online',
|
|
||||||
battery: onlineDrone.battery_level || 0,
|
|
||||||
signal: onlineDrone.signal_strength || 0
|
|
||||||
}
|
|
||||||
// 初始化视频播放器
|
|
||||||
nextTick(() => {
|
|
||||||
initVideoPlayer()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (targetDrone) {
|
||||||
|
// 设备状态判断逻辑:
|
||||||
|
// 1. 检查WebSocket连接状态
|
||||||
|
const wsConnected = ws?.readyState === WebSocket.OPEN
|
||||||
|
// 2. 检查设备状态 - 优先使用 WebSocket 状态
|
||||||
|
const deviceStatus = targetDrone.status?.code || targetDrone.status
|
||||||
|
const deviceOnline = wsConnected && (deviceStatus === 1 || (drone.value?.status === 'online'))
|
||||||
|
// 3. 设置新状态
|
||||||
|
const newStatus = deviceOnline ? 'online' : 'offline'
|
||||||
|
|
||||||
|
// 检查状态是否发生变化
|
||||||
|
const oldStatus = drone.value?.status
|
||||||
|
if (oldStatus && oldStatus !== newStatus) {
|
||||||
|
showDeviceStatusNotification(newStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
drone.value = {
|
||||||
|
id: targetDrone.id,
|
||||||
|
name: targetDrone.device_name,
|
||||||
|
code: targetDrone.device_code,
|
||||||
|
status: newStatus,
|
||||||
|
battery: targetDrone.battery_level || 0,
|
||||||
|
signal: targetDrone.signal_strength || 0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
drone.value = null
|
drone.value = null
|
||||||
// 如果没有在线设备,显示图表
|
|
||||||
getChartData()
|
getChartData()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
console.error('API响应错误:', res.message)
|
||||||
ElMessage.error(res.message || '获取无人机列表失败')
|
ElMessage.error(res.message || '获取无人机列表失败')
|
||||||
getChartData()
|
getChartData()
|
||||||
}
|
}
|
||||||
@ -279,8 +296,7 @@ const getDroneInfo = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化视频播放器
|
// 初始化视频播放器
|
||||||
const initVideoPlayer = () => {
|
const initVideoPlayer = async () => {
|
||||||
console.log('初始化视频播放器, 设备信息:', drone.value)
|
|
||||||
videoLoading.value = true // 开始加载时显示loading
|
videoLoading.value = true // 开始加载时显示loading
|
||||||
isPlaying.value = false // 重置播放状态
|
isPlaying.value = false // 重置播放状态
|
||||||
|
|
||||||
@ -292,12 +308,10 @@ const initVideoPlayer = () => {
|
|||||||
|
|
||||||
// 初始化新的播放器
|
// 初始化新的播放器
|
||||||
if (videoRef.value && drone.value?.status === 'online') {
|
if (videoRef.value && drone.value?.status === 'online') {
|
||||||
console.log('创建FLV播放器, 设备编码:', drone.value.code)
|
|
||||||
flvPlayer.value = createFlvPlayer(videoRef.value, drone.value.code)
|
flvPlayer.value = createFlvPlayer(videoRef.value, drone.value.code)
|
||||||
|
|
||||||
if (flvPlayer.value) {
|
if (flvPlayer.value) {
|
||||||
flvPlayer.value.on('loading', () => {
|
flvPlayer.value.on('loading', () => {
|
||||||
console.log('视频加载中...')
|
|
||||||
if (!isPlaying.value) {
|
if (!isPlaying.value) {
|
||||||
videoLoading.value = true
|
videoLoading.value = true
|
||||||
}
|
}
|
||||||
@ -312,7 +326,7 @@ const initVideoPlayer = () => {
|
|||||||
|
|
||||||
// 监听视频元素的事件
|
// 监听视频元素的事件
|
||||||
videoRef.value.addEventListener('waiting', () => {
|
videoRef.value.addEventListener('waiting', () => {
|
||||||
console.log('视频缓冲中')
|
// 视频缓冲中
|
||||||
isBuffering.value = true
|
isBuffering.value = true
|
||||||
// 只有在视频还未开始播放时才显示loading
|
// 只有在视频还未开始播放时才显示loading
|
||||||
if (!isPlaying.value) {
|
if (!isPlaying.value) {
|
||||||
@ -321,14 +335,14 @@ const initVideoPlayer = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
videoRef.value.addEventListener('playing', () => {
|
videoRef.value.addEventListener('playing', () => {
|
||||||
console.log('视频开始播放')
|
// 视频开始播放
|
||||||
videoLoading.value = false
|
videoLoading.value = false
|
||||||
isPlaying.value = true
|
isPlaying.value = true
|
||||||
isBuffering.value = false
|
isBuffering.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
videoRef.value.addEventListener('canplay', () => {
|
videoRef.value.addEventListener('canplay', () => {
|
||||||
console.log('视频可以播放')
|
// 视频可以播放
|
||||||
isBuffering.value = false
|
isBuffering.value = false
|
||||||
if (!isPlaying.value) {
|
if (!isPlaying.value) {
|
||||||
videoLoading.value = false
|
videoLoading.value = false
|
||||||
@ -361,14 +375,46 @@ const startRefreshTimer = () => {
|
|||||||
}, 30000) // 每30秒刷新一次
|
}, 30000) // 每30秒刷新一次
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听窗口大小变化
|
// 处理窗口大小变化
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (chart) {
|
chart?.resize()
|
||||||
chart.resize()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听设备状态变化
|
||||||
|
watch(() => drone.value?.status, (newStatus, oldStatus) => {
|
||||||
|
// 设备状态变化
|
||||||
|
if (newStatus !== oldStatus) {
|
||||||
|
// 显示状态变化通知
|
||||||
|
if (oldStatus) { // 只在状态真实变化时显示通知
|
||||||
|
showDeviceStatusNotification(newStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据新状态处理视频或图表
|
||||||
|
nextTick(async () => {
|
||||||
|
if (newStatus === 'online') {
|
||||||
|
// 设备上线,销毁图表并初始化视频播放器
|
||||||
|
if (chart) {
|
||||||
|
chart.dispose()
|
||||||
|
chart = null
|
||||||
|
}
|
||||||
|
await initVideoPlayer()
|
||||||
|
} else {
|
||||||
|
// 设备离线,销毁视频播放器并初始化图表
|
||||||
|
if (flvPlayer.value) {
|
||||||
|
destroyFlvPlayer(flvPlayer.value)
|
||||||
|
flvPlayer.value = null
|
||||||
|
}
|
||||||
|
getChartData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 添加用户交互监听
|
||||||
|
document.addEventListener('click', handleUserInteraction)
|
||||||
|
document.addEventListener('keydown', handleUserInteraction)
|
||||||
|
|
||||||
// 初始化WebSocket连接
|
// 初始化WebSocket连接
|
||||||
initWebSocket()
|
initWebSocket()
|
||||||
// 获取无人机信息
|
// 获取无人机信息
|
||||||
@ -380,6 +426,10 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
// 移除用户交互监听
|
||||||
|
document.removeEventListener('click', handleUserInteraction)
|
||||||
|
document.removeEventListener('keydown', handleUserInteraction)
|
||||||
|
|
||||||
// 关闭WebSocket连接
|
// 关闭WebSocket连接
|
||||||
if (ws) {
|
if (ws) {
|
||||||
ws.close()
|
ws.close()
|
||||||
@ -402,7 +452,7 @@ onUnmounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="video-monitor">
|
<div class="video-monitor">
|
||||||
<div class="monitor-header">
|
<div class="monitor-header">
|
||||||
<div class="title">{{ drone?.status === 'online' ? '无人机实时监控' : 'pH值变化趋势' }}</div>
|
<div class="title">{{ drone?.status === 'online' ? '无人机实时监控' : '历史数据监测' }}</div>
|
||||||
<div class="status" v-if="drone">
|
<div class="status" v-if="drone">
|
||||||
<el-tag :type="drone?.status === 'online' ? 'success' : 'danger'" size="small">
|
<el-tag :type="drone?.status === 'online' ? 'success' : 'danger'" size="small">
|
||||||
{{ drone?.status === 'online' ? '在线' : '离线' }}
|
{{ drone?.status === 'online' ? '在线' : '离线' }}
|
||||||
@ -411,15 +461,15 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
|
<template v-if="drone?.status === 'online'">
|
||||||
<!-- 在线显示视频 -->
|
<!-- 在线显示视频 -->
|
||||||
<div v-if="drone?.status === 'online'" class="video-wrapper">
|
<div class="video-wrapper">
|
||||||
<video
|
<video
|
||||||
ref="videoRef"
|
ref="videoRef"
|
||||||
class="video-player"
|
class="video-player"
|
||||||
muted
|
muted
|
||||||
></video>
|
></video>
|
||||||
<!-- 修改 loading 遮罩的显示条件 -->
|
<div v-if="videoLoading && !isPlaying" class="video-loading">
|
||||||
<div v-if="videoLoading && !isPlaying && drone?.status === 'online'" class="video-loading">
|
|
||||||
<el-icon class="loading-icon"><Loading /></el-icon>
|
<el-icon class="loading-icon"><Loading /></el-icon>
|
||||||
<span class="loading-text">视频加载中...</span>
|
<span class="loading-text">视频加载中...</span>
|
||||||
</div>
|
</div>
|
||||||
@ -430,8 +480,7 @@ onUnmounted(() => {
|
|||||||
<el-tooltip content="电池电量" placement="top">
|
<el-tooltip content="电池电量" placement="top">
|
||||||
<div class="metric">
|
<div class="metric">
|
||||||
<el-icon><Monitor /></el-icon>
|
<el-icon><Monitor /></el-icon>
|
||||||
<!-- {{ drone.battery }}% -->
|
{{ drone.battery }}%
|
||||||
65%
|
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="信号强度" placement="top">
|
<el-tooltip content="信号强度" placement="top">
|
||||||
@ -444,9 +493,11 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<!-- 离线显示图表 -->
|
<!-- 离线显示图表 -->
|
||||||
<div v-else class="chart-wrapper" ref="chartRef"></div>
|
<div ref="chartRef" class="chart-wrapper"></div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -569,8 +620,11 @@ onUnmounted(() => {
|
|||||||
.chart-wrapper {
|
.chart-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: rgba(0, 24, 65, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
border: 1px solid rgba(63, 167, 221, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import { Plus } from '@element-plus/icons-vue';
|
|||||||
const tableData = ref([
|
const tableData = ref([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "2024年第一季度水质监测分析报告",
|
title: "2025年第一季度水质监测分析报告",
|
||||||
type: "environment",
|
type: "environment",
|
||||||
timeRange: {
|
timeRange: {
|
||||||
start: "2024-01-01",
|
start: "2025-01-01",
|
||||||
end: "2024-03-31",
|
end: "2025-02-28",
|
||||||
},
|
},
|
||||||
dataSource: [
|
dataSource: [
|
||||||
{
|
{
|
||||||
@ -21,7 +21,7 @@ const tableData = ref([
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
analysis: {
|
analysis: {
|
||||||
summary: "第一季度水质总体保持稳定,但3月份出现轻微波动",
|
summary: "第一季度水质总体保持稳定,但2月份出现轻微波动",
|
||||||
trends: [
|
trends: [
|
||||||
{
|
{
|
||||||
indicator: "pH值",
|
indicator: "pH值",
|
||||||
@ -37,7 +37,7 @@ const tableData = ref([
|
|||||||
abnormal: [
|
abnormal: [
|
||||||
{
|
{
|
||||||
type: "溶解氧",
|
type: "溶解氧",
|
||||||
description: "3月底溶解氧水平略低于标准值",
|
description: "2月底溶解氧水平略低于标准值",
|
||||||
level: "轻微",
|
level: "轻微",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user