视频管理
This commit is contained in:
parent
5879a4d054
commit
cad34775f7
|
|
@ -1,11 +1,11 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 设备管理->视频监控->保存画线数据
|
||||
export function getLineDetailAPI(data) {
|
||||
export function getLineDetailAPI(params) {
|
||||
return request({
|
||||
url: '/smartCar/data/line/getLineDetail',
|
||||
method: 'POST',
|
||||
data
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
<el-card class="top-bar-card" shadow="never">
|
||||
<div class="top-bar">
|
||||
<div class="status-bar">
|
||||
<span class="status-text">博诺思车辆道路计数仪</span>
|
||||
<span class="status-indicator" :class="deviceStatus">
|
||||
<span class="status-text">{{ lineInfo.equipmentName }}</span>
|
||||
<span class="status-indicator">
|
||||
{{ statusText }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -16,31 +16,17 @@
|
|||
</div>
|
||||
<!-- 画线按钮组 -->
|
||||
<div class="draw-line-buttons">
|
||||
<el-button
|
||||
v-if="!isDrawingMode"
|
||||
type="primary"
|
||||
class="draw-line-btn"
|
||||
@click="handleDrawLine"
|
||||
icon="el-icon-edit"
|
||||
>
|
||||
<el-button v-if="!isDrawingMode" type="primary" class="draw-line-btn" @click="handleDrawLine"
|
||||
icon="el-icon-edit">
|
||||
画线
|
||||
</el-button>
|
||||
<template v-else>
|
||||
<el-button
|
||||
type="success"
|
||||
class="save-line-btn"
|
||||
@click="handleSaveLines"
|
||||
icon="el-icon-check"
|
||||
:loading="saving"
|
||||
>
|
||||
<el-button type="success" class="save-line-btn" @click="handleSaveLines"
|
||||
icon="el-icon-check" :loading="saving">
|
||||
保存
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
class="cancel-line-btn"
|
||||
@click="handleCancelLines"
|
||||
icon="el-icon-close"
|
||||
>
|
||||
<el-button type="info" class="cancel-line-btn" @click="handleCancelLines"
|
||||
icon="el-icon-close">
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
|
|
@ -53,54 +39,17 @@
|
|||
<el-card class="video-card" shadow="never" :body-style="{ padding: '0', height: '100%' }">
|
||||
<div class="video-wrapper">
|
||||
<div class="video-container" ref="videoContainer">
|
||||
<video
|
||||
ref="videoPlayer"
|
||||
class="video-player"
|
||||
:src="videoUrl"
|
||||
autoplay
|
||||
muted
|
||||
controls
|
||||
@loadedmetadata="handleVideoLoaded"
|
||||
@error="handleVideoError"
|
||||
>
|
||||
<video ref="videoPlayer" class="video-player" :src="videoUrl" autoplay muted controls
|
||||
@loadedmetadata="handleVideoLoaded" @error="handleVideoError">
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
|
||||
<!-- 画线画布覆盖层 -->
|
||||
<canvas
|
||||
ref="drawCanvas"
|
||||
class="draw-canvas"
|
||||
:class="{ active: isDrawingMode }"
|
||||
@mousedown="startDrawing"
|
||||
@mousemove="draw"
|
||||
@mouseup="stopDrawing"
|
||||
@mouseleave="stopDrawing"
|
||||
></canvas>
|
||||
<canvas ref="drawCanvas" class="draw-canvas" :class="{ active: isDrawingMode }"
|
||||
@mousedown="startDrawing" @mousemove="draw" @mouseup="stopDrawing"
|
||||
@mouseleave="stopDrawing"></canvas>
|
||||
<!-- 已保存的画线显示层(只读) -->
|
||||
<canvas
|
||||
ref="savedLineCanvas"
|
||||
class="saved-line-canvas"
|
||||
></canvas>
|
||||
|
||||
<!-- 车辆检测框覆盖层 -->
|
||||
<div class="detection-overlay">
|
||||
<div
|
||||
v-for="(vehicle, index) in detectedVehicles"
|
||||
:key="index"
|
||||
class="vehicle-box"
|
||||
:style="{
|
||||
left: vehicle.x + '%',
|
||||
top: vehicle.y + '%',
|
||||
width: vehicle.width + '%',
|
||||
height: vehicle.height + '%'
|
||||
}"
|
||||
>
|
||||
<div class="box-corner corner-tl"></div>
|
||||
<div class="box-corner corner-tr"></div>
|
||||
<div class="box-corner corner-bl"></div>
|
||||
<div class="box-corner corner-br"></div>
|
||||
</div>
|
||||
</div>
|
||||
<canvas ref="savedLineCanvas" class="saved-line-canvas"></canvas>
|
||||
|
||||
<!-- 视频加载提示 -->
|
||||
<div v-if="!videoUrl" class="video-placeholder">
|
||||
|
|
@ -120,7 +69,6 @@ export default {
|
|||
name: 'VideoMonitor',
|
||||
data() {
|
||||
return {
|
||||
deviceStatus: 'online', // online, offline, standby, upgrading
|
||||
currentTime: '',
|
||||
videoUrl: '', // 视频流地址,可以从接口获取
|
||||
isDrawingMode: false, // 是否开启画线模式
|
||||
|
|
@ -136,17 +84,25 @@ export default {
|
|||
detectedVehicles: [], // 检测到的车辆数据
|
||||
timer: null,
|
||||
saving: false, // 保存中状态
|
||||
lineInfo: {
|
||||
equipmentName: '',
|
||||
equipmentStatus: '',
|
||||
startX: '',
|
||||
startY: '',
|
||||
endX: '',
|
||||
endY: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusText() {
|
||||
const statusMap = {
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
standby: '待机',
|
||||
upgrading: '升级中'
|
||||
'1': '在线',
|
||||
'0': '离线',
|
||||
'2': '待机',
|
||||
'3': '升级中'
|
||||
}
|
||||
return statusMap[this.deviceStatus] || '未知'
|
||||
return statusMap[this.lineInfo.equipmentStatus] || '未知'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -205,31 +161,38 @@ export default {
|
|||
},
|
||||
|
||||
/** 初始化默认线 */
|
||||
initInitialLine() {
|
||||
// 可以从接口获取初始线数据
|
||||
// 这里使用示例数据:从左上角到右下角的一条线
|
||||
const container = this.$refs.videoContainer
|
||||
if (container) {
|
||||
async initInitialLine() {
|
||||
try {
|
||||
const container = this.$refs.videoContainer
|
||||
if (!container) return
|
||||
|
||||
const containerWidth = container.offsetWidth || 1920
|
||||
const containerHeight = container.offsetHeight || 1080
|
||||
|
||||
// 示例:从左上角20%位置到右下角80%位置
|
||||
this.initialLine = {
|
||||
startX: containerWidth * 0.2,
|
||||
startY: containerHeight * 0.2,
|
||||
endX: containerWidth * 0.8,
|
||||
endY: containerHeight * 0.8
|
||||
}
|
||||
|
||||
// 如果有初始线,设置为已保存的线并显示
|
||||
if (this.initialLine) {
|
||||
this.savedLine = { ...this.initialLine }
|
||||
this.redrawCanvas()
|
||||
|
||||
// 调用接口获取画线详情
|
||||
const res = await getLineDetailAPI({})
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
const data = res.data
|
||||
this.lineInfo = data
|
||||
|
||||
// 如果有画线数据,将百分比坐标转换为像素坐标
|
||||
if (data.startX && data.startY && data.endX && data.endY) {
|
||||
this.initialLine = {
|
||||
startX: (parseFloat(data.startX) / 100) * containerWidth,
|
||||
startY: (parseFloat(data.startY) / 100) * containerHeight,
|
||||
endX: (parseFloat(data.endX) / 100) * containerWidth,
|
||||
endY: (parseFloat(data.endY) / 100) * containerHeight
|
||||
}
|
||||
|
||||
// 设置为已保存的线并显示
|
||||
this.savedLine = { ...this.initialLine }
|
||||
this.redrawCanvas()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取画线详情失败:', error)
|
||||
}
|
||||
|
||||
// 真实场景:从接口获取初始线
|
||||
// this.loadInitialLine()
|
||||
},
|
||||
|
||||
/** 处理画线按钮点击 */
|
||||
|
|
@ -250,7 +213,7 @@ export default {
|
|||
// 如果有已保存的线,可以继续保存(更新保存)
|
||||
// 如果没有新画的线,但有已保存的线,也可以保存(更新保存)
|
||||
const lineToSave = this.line || this.savedLine
|
||||
|
||||
|
||||
if (!lineToSave) {
|
||||
this.$message.warning('请先画线后再保存')
|
||||
return
|
||||
|
|
@ -263,29 +226,16 @@ export default {
|
|||
const containerWidth = container ? container.offsetWidth : 1920
|
||||
const containerHeight = container ? container.offsetHeight : 1080
|
||||
|
||||
// 准备保存的数据,将像素坐标转换为相对坐标(百分比)
|
||||
const lineData = {
|
||||
startX: (lineToSave.startX / containerWidth) * 100,
|
||||
startY: (lineToSave.startY / containerHeight) * 100,
|
||||
endX: (lineToSave.endX / containerWidth) * 100,
|
||||
endY: (lineToSave.endY / containerHeight) * 100,
|
||||
}
|
||||
|
||||
// 准备保存的数据,将像素坐标转换为相对坐标(百分比),并转换为字符串
|
||||
const params = {
|
||||
line: lineData,
|
||||
videoUrl: this.videoUrl,
|
||||
deviceId: this.$route.query.deviceId || '', // 如果有设备ID
|
||||
startX: String(((lineToSave.startX / containerWidth) * 100).toFixed(2)),
|
||||
startY: String(((lineToSave.startY / containerHeight) * 100).toFixed(2)),
|
||||
endX: String(((lineToSave.endX / containerWidth) * 100).toFixed(2)),
|
||||
endY: String(((lineToSave.endY / containerHeight) * 100).toFixed(2))
|
||||
}
|
||||
|
||||
// 模拟保存成功(延迟500ms模拟API调用)
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
// 模拟API返回成功
|
||||
const res = { code: 200, msg: '保存成功' }
|
||||
|
||||
// 真实API调用(注释掉,使用模拟数据)
|
||||
// const res = await saveVideoLineAPI(params)
|
||||
|
||||
const res = await updateLineAPI(params)
|
||||
|
||||
if (res.code === 200) {
|
||||
this.$message.success('画线数据保存成功')
|
||||
// 保存成功后,将当前线保存到已保存的线,同时更新初始线
|
||||
|
|
@ -427,7 +377,7 @@ export default {
|
|||
stopDrawing() {
|
||||
if (this.isDrawing && this.currentLine) {
|
||||
// 只有当起点和终点不同时才保存
|
||||
if (this.currentLine.startX !== this.currentLine.endX ||
|
||||
if (this.currentLine.startX !== this.currentLine.endX ||
|
||||
this.currentLine.startY !== this.currentLine.endY) {
|
||||
// 如果已经存在已保存的线,清除它(使用最新的线)
|
||||
if (this.savedLine) {
|
||||
|
|
@ -439,7 +389,7 @@ export default {
|
|||
ctx.clearRect(0, 0, savedCanvas.width, savedCanvas.height)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 保存当前画的线(只保存一条线)
|
||||
this.line = {
|
||||
startX: this.currentLine.startX,
|
||||
|
|
@ -447,7 +397,7 @@ export default {
|
|||
endX: this.currentLine.endX,
|
||||
endY: this.currentLine.endY
|
||||
}
|
||||
|
||||
|
||||
// 重绘画布,显示当前画的线
|
||||
this.redrawCanvas()
|
||||
}
|
||||
|
|
@ -466,10 +416,10 @@ export default {
|
|||
savedCanvas.width = container.offsetWidth
|
||||
savedCanvas.height = container.offsetHeight
|
||||
}
|
||||
|
||||
|
||||
const ctx = savedCanvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, savedCanvas.width, savedCanvas.height)
|
||||
|
||||
|
||||
// 绘制已保存的线(优先显示已保存的线,如果没有则显示当前线,最后显示初始线)
|
||||
const lineToDraw = this.savedLine || this.line || this.initialLine
|
||||
if (lineToDraw) {
|
||||
|
|
@ -504,7 +454,6 @@ export default {
|
|||
|
||||
/** 加载视频流 */
|
||||
async loadVideoStream() {
|
||||
// 这里应该调用API获取视频流地址
|
||||
const res = await getVideoStreamAPI()
|
||||
if (res.code === 200) {
|
||||
this.videoUrl = res.data.streamUrl
|
||||
|
|
@ -808,9 +757,12 @@ export default {
|
|||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue