云台联调
This commit is contained in:
parent
117aa3c224
commit
6f2f047ede
|
|
@ -44,6 +44,7 @@
|
||||||
"echarts": "5.4.0",
|
"echarts": "5.4.0",
|
||||||
"element-ui": "2.15.14",
|
"element-ui": "2.15.14",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
|
"flv.js": "^1.6.2",
|
||||||
"fuse.js": "6.4.3",
|
"fuse.js": "6.4.3",
|
||||||
"highlight.js": "9.18.5",
|
"highlight.js": "9.18.5",
|
||||||
"js-beautify": "1.13.0",
|
"js-beautify": "1.13.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
printWidth: 120,
|
printWidth: 120,
|
||||||
tabWidth: 4,
|
tabWidth: 2,
|
||||||
semi: false,
|
semi: false,
|
||||||
vueIndentScriptAndStyle: false,
|
vueIndentScriptAndStyle: false,
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
|
|
|
||||||
|
|
@ -63,4 +63,19 @@ export const delPlatformConfiguration = (id) => {
|
||||||
// 修改流媒体平台配置状态
|
// 修改流媒体平台配置状态
|
||||||
export const changeStatus = (data) => {
|
export const changeStatus = (data) => {
|
||||||
return request.post('/smart-site/platform_configuration/changeStatus', data);
|
return request.post('/smart-site/platform_configuration/changeStatus', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取清新token
|
||||||
|
export const getQxToken = (params) => {
|
||||||
|
return request.get('/smart-site/video_equipment/getQxToken', { params });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取清新视频设备列表
|
||||||
|
export const getVideoEquipment = (params) => {
|
||||||
|
return request.get('/smart-site/video_equipment/getVideoEquipment', { params });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新水印
|
||||||
|
export const updateWaterMark = (data) => {
|
||||||
|
return request.post('/smart-site/video_equipment/updateWaterMark', data);
|
||||||
};
|
};
|
||||||
|
|
@ -230,7 +230,7 @@ export default {
|
||||||
wsUrl: [{ required: true, message: '请输入视频连接地址-公网', trigger: 'blur' }],
|
wsUrl: [{ required: true, message: '请输入视频连接地址-公网', trigger: 'blur' }],
|
||||||
epid: [
|
epid: [
|
||||||
{ required: true, message: '请输入epid', trigger: 'blur' },
|
{ required: true, message: '请输入epid', trigger: 'blur' },
|
||||||
{ pattern: /^[0-9]*$/, message: '只能输入正整数', trigger: 'blur' },
|
{ pattern: /^[A-Za-z0-9]+$/, message: '只能输入数字和字母', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
bfix: [{ required: true, message: '请输入bfix', trigger: 'blur' }],
|
bfix: [{ required: true, message: '请输入bfix', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog title="调试" :visible.sync="videoDebug" width="70%" height="80%">
|
<el-dialog title="调试" :visible.sync="videoDebug" width="70%" height="80%">
|
||||||
<MonitoringAndDebug />
|
<MonitoringAndDebug v-if="videoDebug" :row="this.row"/>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -249,6 +249,7 @@ export default {
|
||||||
qrData: {
|
qrData: {
|
||||||
devName: '',
|
devName: '',
|
||||||
},
|
},
|
||||||
|
row: {}, // 当前行
|
||||||
dialogTitle: '新增',
|
dialogTitle: '新增',
|
||||||
isAdd: true,
|
isAdd: true,
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
|
|
@ -418,6 +419,7 @@ export default {
|
||||||
// 监控调试
|
// 监控调试
|
||||||
handleMonitoringAndDebug(row) {
|
handleMonitoringAndDebug(row) {
|
||||||
console.log('监控调试', row)
|
console.log('监控调试', row)
|
||||||
|
this.row = row
|
||||||
this.videoDebug = true
|
this.videoDebug = true
|
||||||
},
|
},
|
||||||
// 查看配置
|
// 查看配置
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@
|
||||||
<div class="monitor-page">
|
<div class="monitor-page">
|
||||||
<!-- 左侧视频监控区 -->
|
<!-- 左侧视频监控区 -->
|
||||||
<div class="video-section">
|
<div class="video-section">
|
||||||
<div class="video-title">视频播放</div>
|
<div class="video-title">
|
||||||
|
视频播放
|
||||||
|
<el-select v-model="ballIndex" placeholder="请选择" style="width: 80px">
|
||||||
|
<el-option v-for="item in ballIndexOpts" :key="item.value" :label="item.label" :value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
<div class="video-container">
|
<div class="video-container">
|
||||||
<div class="video-overlay">
|
<video class="cell-player-1" ref="videosmallone" preload="auto" muted type="rtmp/flv" :id="videoId">
|
||||||
<div class="timestamp">{{ currentTime }}</div>
|
<source src="" />
|
||||||
<div class="coordinates">
|
</video>
|
||||||
<div>{{ coordinates.line1 }}</div>
|
<canvas ref="canvas" style="display: none"></canvas>
|
||||||
<div>{{ coordinates.line2 }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="signal-info">电池: {{ batteryInfo }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -23,34 +25,30 @@
|
||||||
<!-- 方向控制 -->
|
<!-- 方向控制 -->
|
||||||
<div class="direction-control">
|
<div class="direction-control">
|
||||||
<div class="direction-pad">
|
<div class="direction-pad">
|
||||||
<el-button icon="el-icon-arrow-up" circle @click="control('up')">
|
<el-button icon="el-icon-arrow-up" circle @click="control('up')"> </el-button>
|
||||||
</el-button>
|
|
||||||
<div class="horizontal-buttons">
|
<div class="horizontal-buttons">
|
||||||
<el-button icon="el-icon-arrow-left" circle @click="control('left')">
|
<el-button icon="el-icon-arrow-left" circle @click="control('left')"> </el-button>
|
||||||
</el-button>
|
<el-button icon="el-icon-arrow-right" circle @click="control('right')"> </el-button>
|
||||||
<el-button icon="el-icon-arrow-right" circle @click="control('right')">
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
<el-button icon="el-icon-arrow-down" circle @click="control('down')">
|
<el-button icon="el-icon-arrow-down" circle @click="control('down')"> </el-button>
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 缩放控制 -->
|
<!-- 缩放控制 -->
|
||||||
<div class="zoom-controls">
|
<div class="zoom-controls">
|
||||||
<div class="zoom-column">
|
<div class="zoom-column">
|
||||||
<el-button size="small" @click="control('zoomIn')">
|
<el-button size="small" @click="control('focusIn')">
|
||||||
<i class="el-icon-plus"></i>
|
<i class="el-icon-plus"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button size="small" @click="control('zoomOut')">
|
<el-button size="small" @click="control('focusOut')">
|
||||||
<i class="el-icon-minus"></i>
|
<i class="el-icon-minus"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<div class="column-label">聚焦</div>
|
<div class="column-label">聚焦</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="zoom-column">
|
<div class="zoom-column">
|
||||||
<el-button size="small" @click="control('focusIn')">
|
<el-button size="small" @click="control('zoomIn')">
|
||||||
<i class="el-icon-plus"></i>
|
<i class="el-icon-plus"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button size="small" @click="control('focusOut')">
|
<el-button size="small" @click="control('zoomOut')">
|
||||||
<i class="el-icon-minus"></i>
|
<i class="el-icon-minus"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<div class="column-label">缩放</div>
|
<div class="column-label">缩放</div>
|
||||||
|
|
@ -72,9 +70,10 @@
|
||||||
<div class="view-row">
|
<div class="view-row">
|
||||||
<div class="view-item">
|
<div class="view-item">
|
||||||
<i class="el-icon-video-camera"></i>
|
<i class="el-icon-video-camera"></i>
|
||||||
<span>本地录像</span>
|
<span v-if="!isRecording" @click="startRecording">本地录像</span>
|
||||||
|
<span v-else @click="stopRecording">停止录像</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-item">
|
<div class="view-item" @click="takeSnapshot">
|
||||||
<i class="el-icon-picture"></i>
|
<i class="el-icon-picture"></i>
|
||||||
<span>本地抓拍</span>
|
<span>本地抓拍</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -104,15 +103,9 @@
|
||||||
|
|
||||||
<!-- 水印设置 -->
|
<!-- 水印设置 -->
|
||||||
<div class="watermark-section">
|
<div class="watermark-section">
|
||||||
<div class="watermark-title">应用水印<i class="el-icon-s-operation"></i></div>
|
<div class="watermark-title" @click="updateWaterMark">应用水印<i class="el-icon-s-operation"></i></div>
|
||||||
<el-input
|
<el-input v-model="watermark.oneText" placeholder="设置第一行视频水印文字"></el-input>
|
||||||
v-model="watermark.line1"
|
<el-input v-model="watermark.twoText" placeholder="设置第二行视频水印文字"></el-input>
|
||||||
placeholder="设置第一行视频水印文字"
|
|
||||||
></el-input>
|
|
||||||
<el-input
|
|
||||||
v-model="watermark.line2"
|
|
||||||
placeholder="设置第二行视频水印文字"
|
|
||||||
></el-input>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-title">快捷键设置</div>
|
<div class="control-title">快捷键设置</div>
|
||||||
|
|
@ -122,13 +115,13 @@
|
||||||
<div class="shortcut-grid">
|
<div class="shortcut-grid">
|
||||||
<div class="shortcut-row" v-for="(row, rowIndex) in shortcutSettings" :key="rowIndex">
|
<div class="shortcut-row" v-for="(row, rowIndex) in shortcutSettings" :key="rowIndex">
|
||||||
<div class="shortcut-item" v-for="(item, colIndex) in row" :key="colIndex">
|
<div class="shortcut-item" v-for="(item, colIndex) in row" :key="colIndex">
|
||||||
<span>{{item.label}}</span>
|
<span>{{ item.label }}</span>
|
||||||
<input
|
<input
|
||||||
v-model="item.key"
|
v-model="item.key"
|
||||||
@keydown="handleKeyPress($event, rowIndex, colIndex)"
|
@keydown="handleKeyPress($event, rowIndex, colIndex)"
|
||||||
:maxlength="1"
|
:maxlength="1"
|
||||||
class="key"
|
class="key"
|
||||||
></input>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -138,66 +131,123 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import flvjs from 'flv.js'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { getQxToken, getVideoEquipment, updateWaterMark } from '@/api/deviceManagement'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
props: {
|
||||||
|
row: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
name: 'VideoMonitor',
|
name: 'VideoMonitor',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentTime: '2023-01-07 13:19:38',
|
qxToken: '',
|
||||||
coordinates: {
|
qxInfo: {},
|
||||||
line1: '116.13263L 000KPH',
|
videoId: '',
|
||||||
line2: '033.50462N 000D/G'
|
player: null,
|
||||||
},
|
ballIndex: 0,
|
||||||
batteryInfo: '64% 4G1:96% 4G2:96%',
|
ballIndexOpts: [],
|
||||||
|
isDown: false,
|
||||||
watermark: {
|
watermark: {
|
||||||
line1: '',
|
oneText: '',
|
||||||
line2: ''
|
twoText: '',
|
||||||
},
|
},
|
||||||
// 合并后的快捷键设置数据结构
|
// 合并后的快捷键设置数据结构
|
||||||
shortcutSettings: [
|
shortcutSettings: [
|
||||||
[
|
[
|
||||||
{ label: '上', key: 'W', action: 'up' },
|
{ label: '上', key: 'W', action: 'up' },
|
||||||
{ label: '放大', key: 'Q', action: 'zoomIn' }
|
{ label: '放大', key: 'Q', action: 'zoomIn' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ label: '下', key: 'S', action: 'down' },
|
{ label: '下', key: 'S', action: 'down' },
|
||||||
{ label: '缩小', key: 'E', action: 'zoomOut' }
|
{ label: '缩小', key: 'E', action: 'zoomOut' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ label: '左', key: 'A', action: 'left' },
|
{ label: '左', key: 'A', action: 'left' },
|
||||||
{ label: '远焦', key: 'R', action: 'focusIn' }
|
{ label: '远焦', key: 'R', action: 'focusIn' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ label: '右', key: 'D', action: 'right' },
|
{ label: '右', key: 'D', action: 'right' },
|
||||||
{ label: '近焦', key: 'T', action: 'focusOut' }
|
{ label: '近焦', key: 'T', action: 'focusOut' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ label: '抓拍', key: 'F', action: 'capture' },
|
{ label: '抓拍', key: 'F', action: 'capture' },
|
||||||
{ label: '录像', key: 'G', action: 'record' }
|
{ label: '录像', key: 'G', action: 'record' },
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
|
mediaRecorder: null,
|
||||||
|
recordedChunks: [],
|
||||||
|
isRecording: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
control(action) {
|
control(action) {
|
||||||
console.log('Control action:', action)
|
console.log('Control action:', action)
|
||||||
|
this.isDown = true
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'up':
|
case 'up':
|
||||||
console.log('Up')
|
console.log('Up')
|
||||||
|
this.requestPost(
|
||||||
|
'PTZ/C_PTZ_Turn?token=' + this.qxToken,
|
||||||
|
{
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
motion: 'up',
|
||||||
|
},
|
||||||
|
async (rv) => {},
|
||||||
|
)
|
||||||
|
this.handleStop()
|
||||||
break
|
break
|
||||||
case 'down':
|
case 'down':
|
||||||
console.log('Down')
|
console.log('Down')
|
||||||
|
this.requestPost(
|
||||||
|
'PTZ/C_PTZ_Turn?token=' + this.qxToken,
|
||||||
|
{
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
motion: 'down',
|
||||||
|
},
|
||||||
|
async (rv) => {},
|
||||||
|
)
|
||||||
|
this.handleStop()
|
||||||
break
|
break
|
||||||
case 'left':
|
case 'left':
|
||||||
console.log('Left')
|
console.log('Left')
|
||||||
|
this.requestPost(
|
||||||
|
'PTZ/C_PTZ_Turn?token=' + this.qxToken,
|
||||||
|
{
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
motion: 'left',
|
||||||
|
},
|
||||||
|
async (rv) => {},
|
||||||
|
)
|
||||||
|
this.handleStop()
|
||||||
break
|
break
|
||||||
case 'right':
|
case 'right':
|
||||||
console.log('Right')
|
console.log('Right')
|
||||||
|
this.requestPost(
|
||||||
|
'PTZ/C_PTZ_Turn?token=' + this.qxToken,
|
||||||
|
{
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
motion: 'right',
|
||||||
|
},
|
||||||
|
async (rv) => {},
|
||||||
|
)
|
||||||
|
this.handleStop()
|
||||||
break
|
break
|
||||||
case 'zoomIn':
|
case 'zoomIn':
|
||||||
console.log('Zoom In')
|
console.log('Zoom In')
|
||||||
|
this.handleZoomIn()
|
||||||
break
|
break
|
||||||
case 'zoomOut':
|
case 'zoomOut':
|
||||||
console.log('Zoom Out')
|
console.log('Zoom Out')
|
||||||
|
this.handleZoomOut()
|
||||||
break
|
break
|
||||||
case 'focusIn':
|
case 'focusIn':
|
||||||
console.log('Focus In')
|
console.log('Focus In')
|
||||||
|
|
@ -219,7 +269,6 @@ export default {
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.log('Unknown action:', action)
|
console.log('Unknown action:', action)
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleCloud() {
|
toggleCloud() {
|
||||||
|
|
@ -231,51 +280,250 @@ export default {
|
||||||
|
|
||||||
// 处理键盘按键事件
|
// 处理键盘按键事件
|
||||||
handleKeyPress(event, rowIndex, colIndex) {
|
handleKeyPress(event, rowIndex, colIndex) {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
const key = event.key.toUpperCase();
|
const key = event.key.toUpperCase()
|
||||||
// 只允许字母键
|
// 只允许字母键
|
||||||
if (/^[A-Z]$/.test(key)) {
|
if (/^[A-Z]$/.test(key)) {
|
||||||
// 检查是否已经存在相同的键位
|
// 检查是否已经存在相同的键位
|
||||||
const isDuplicate = this.shortcutSettings.some(row =>
|
const isDuplicate = this.shortcutSettings.some((row) =>
|
||||||
row.some(item => item.key === key && !(item.key === this.shortcutSettings[rowIndex][colIndex].key))
|
row.some((item) => item.key === key && !(item.key === this.shortcutSettings[rowIndex][colIndex].key)),
|
||||||
);
|
)
|
||||||
|
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
this.$message.error('该键位已被使用,请选择其他键位');
|
this.$message.error('该键位已被使用,请选择其他键位')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前的键位
|
// 获取当前的键位
|
||||||
const oldKey = this.shortcutSettings[rowIndex][colIndex].key;
|
const oldKey = this.shortcutSettings[rowIndex][colIndex].key
|
||||||
|
|
||||||
// 更新快捷键设置
|
// 更新快捷键设置
|
||||||
this.shortcutSettings[rowIndex][colIndex].key = key;
|
this.shortcutSettings[rowIndex][colIndex].key = key
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
// 获取清新token
|
||||||
|
async getQxToken() {
|
||||||
|
try {
|
||||||
|
const res = await getQxToken()
|
||||||
|
console.log('qxToken:', res)
|
||||||
|
this.qxToken = res.msg
|
||||||
|
} catch (error) {
|
||||||
|
console.log('getQxToken error:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取清新设备信息
|
||||||
|
async getQxInfo() {
|
||||||
|
try {
|
||||||
|
const res = await getVideoEquipment()
|
||||||
|
console.log('qxInfo:', res)
|
||||||
|
this.qxInfo = res.data
|
||||||
|
} catch (error) {
|
||||||
|
console.log('getQxInfo error:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
setTimeout(() => {
|
||||||
|
//使用定时器是因为,在mounted声明周期里调用,可能会出现DOM没加载出来的原因
|
||||||
|
var videoElement = this.$refs.videosmallone // 获取到html中的video标签
|
||||||
|
if (videoElement && flvjs.isSupported()) {
|
||||||
|
//因为我这个是复用组件,进来先判断 player是否存在,如果存在,销毁掉它,不然会占用TCP名额
|
||||||
|
if (this.player !== null) {
|
||||||
|
this.player.pause()
|
||||||
|
this.player.unload()
|
||||||
|
this.player.detachMediaElement()
|
||||||
|
this.player.destroy()
|
||||||
|
this.player = null
|
||||||
|
}
|
||||||
|
this.player = flvjs.createPlayer(
|
||||||
|
//创建直播流,加载到DOM中去
|
||||||
|
{
|
||||||
|
type: 'flv',
|
||||||
|
url:
|
||||||
|
this.qxInfo.videoUrl +
|
||||||
|
'stream.flv?puid=' +
|
||||||
|
this.row.puId +
|
||||||
|
'&idx=' +
|
||||||
|
this.ballIndex +
|
||||||
|
'&stream=0&token=' +
|
||||||
|
this.qxToken, //你的url地址
|
||||||
|
isLive: true, //数据源是否为直播流
|
||||||
|
hasAudio: false, //数据源是否包含有音频
|
||||||
|
hasVideo: true, //数据源是否包含有视频
|
||||||
|
enableStashBuffer: true, //是否启用缓存区
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enableWorker: false, //不启用分离线程
|
||||||
|
enableStashBuffer: false, //关闭IO隐藏缓冲区
|
||||||
|
autoCleanupSourceBuffer: true, //自动清除缓存
|
||||||
|
lazyLoad: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
this.player.attachMediaElement(videoElement) //放到dom中去
|
||||||
|
this.player.load() //准备完成
|
||||||
|
//!!!!!!这里需要注意,有的时候load加载完成不一定可以播放,要是播放不成功,用settimeout 给下面的this.player.play() 延时几百毫秒再播放
|
||||||
|
setTimeout(() => {
|
||||||
|
this.player.play()
|
||||||
|
}, 500) // Delay to ensure the player is ready
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
requestPost(url, data, callback) {
|
||||||
|
console.log('requestPost----------------', JSON.stringify(data))
|
||||||
|
url = this.qxInfo.videoUrl + url
|
||||||
|
axios.post(url, data).then((rv) => {
|
||||||
|
// console.log(rv)
|
||||||
|
// console.log(JSON.stringify(rv))
|
||||||
|
callback(rv)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleStop() {
|
||||||
|
console.log('handleStop')
|
||||||
|
setTimeout(() => {
|
||||||
|
this.requestPost(
|
||||||
|
'PTZ/C_PTZ_Turn?token=' + this.qxToken,
|
||||||
|
{
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
motion: 'stop',
|
||||||
|
},
|
||||||
|
async (rv) => {},
|
||||||
|
)
|
||||||
|
this.isDown = false
|
||||||
|
}, 700)
|
||||||
|
},
|
||||||
|
// 停止缩放
|
||||||
|
handleStopZoom() {
|
||||||
|
const params = {
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.requestPost('PTZ/C_PTZ_StopPictureZoom?token=' + this.qxToken, params, (rv) => {})
|
||||||
|
}, 700)
|
||||||
|
},
|
||||||
|
// 放大
|
||||||
|
handleZoomIn() {
|
||||||
|
const params = {
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
}
|
||||||
|
this.requestPost('PTZ/C_PTZ_ZoomInPicture?token=' + this.qxToken, params, (rv) => {})
|
||||||
|
this.handleStopZoom()
|
||||||
|
},
|
||||||
|
// 缩小
|
||||||
|
handleZoomOut() {
|
||||||
|
const params = {
|
||||||
|
puid: this.row.puId,
|
||||||
|
idx: this.ballIndex,
|
||||||
|
}
|
||||||
|
this.requestPost('PTZ/C_PTZ_ZoomOutPicture?token=' + this.qxToken, params, (rv) => {})
|
||||||
|
this.handleStopZoom()
|
||||||
|
},
|
||||||
|
// 更新水印
|
||||||
|
async updateWaterMark() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
puId: this.row.puId,
|
||||||
|
oneText: this.watermark.oneText,
|
||||||
|
twoText: this.watermark.twoText,
|
||||||
|
videoPath: this.qxInfo.videoUrl,
|
||||||
|
}
|
||||||
|
console.log('更新水印 params:', params)
|
||||||
|
const res = await updateWaterMark(params)
|
||||||
|
console.log('updateWaterMark:', res)
|
||||||
|
// 提示
|
||||||
|
this.$message.success('操作成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.log('updateWaterMark error:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 开始录像
|
||||||
|
startRecording() {
|
||||||
|
this.isRecording = true;
|
||||||
|
const video = this.$refs.videosmallone;
|
||||||
|
const stream = video.captureStream();
|
||||||
|
this.mediaRecorder = new MediaRecorder(stream);
|
||||||
|
this.mediaRecorder.ondataavailable = (event) => {
|
||||||
|
if (event.data.size > 0) {
|
||||||
|
this.recordedChunks.push(event.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.mediaRecorder.start();
|
||||||
|
},
|
||||||
|
// 停止录像
|
||||||
|
stopRecording() {
|
||||||
|
this.isRecording = false
|
||||||
|
this.mediaRecorder.stop()
|
||||||
|
this.mediaRecorder.onstop = () => {
|
||||||
|
const blob = new Blob(this.recordedChunks, { type: 'video/webm' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = `本地录像_${new Date().getTime()}.mp4`
|
||||||
|
a.click()
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
this.recordedChunks = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 抓拍
|
||||||
|
takeSnapshot() {
|
||||||
|
console.log('Take snapshot')
|
||||||
|
const video = this.$refs.videosmallone
|
||||||
|
const canvas = this.$refs.canvas
|
||||||
|
canvas.width = video.videoWidth
|
||||||
|
canvas.height = video.videoHeight
|
||||||
|
const context = canvas.getContext('2d')
|
||||||
|
context.drawImage(video, 0, 0, canvas.width, canvas.height)
|
||||||
|
const dataURL = canvas.toDataURL('image/png')
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = dataURL
|
||||||
|
a.download = 'snapshot.png'
|
||||||
|
a.click()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
keyMap() {
|
keyMap() {
|
||||||
return this.shortcutSettings.reduce((map, row) => {
|
return this.shortcutSettings.reduce((map, row) => {
|
||||||
row.forEach(item => {
|
row.forEach((item) => {
|
||||||
map[item.key] = item.action;
|
map[item.key] = item.action
|
||||||
});
|
})
|
||||||
return map;
|
return map
|
||||||
}, {});
|
}, {})
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// 键盘事件监听
|
// 键盘事件监听
|
||||||
window.addEventListener('keydown', (e) => {
|
window.addEventListener('keydown', (e) => {
|
||||||
const key = e.key.toUpperCase();
|
// 检查事件目标是否是输入框
|
||||||
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
||||||
|
console.log('Input focused')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.isDown) return
|
||||||
|
const key = e.key.toUpperCase()
|
||||||
if (this.keyMap[key]) {
|
if (this.keyMap[key]) {
|
||||||
this.control(this.keyMap[key])
|
this.control(this.keyMap[key])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
this.ballIndexOpts = this.row.videoIndex.split(',').map((item) => {
|
||||||
|
return {
|
||||||
|
value: item,
|
||||||
|
label: item,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.getQxToken()
|
||||||
|
this.getQxInfo()
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
|
.cell-player-1 {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
.monitor-page {
|
.monitor-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
@ -386,7 +634,7 @@ export default {
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.watermark-title{
|
.watermark-title {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -427,7 +675,7 @@ export default {
|
||||||
.key {
|
.key {
|
||||||
width: 21px;
|
width: 21px;
|
||||||
height: 21px;
|
height: 21px;
|
||||||
border: 0px solid #DCDFE6;
|
border: 0px solid #dcdfe6;
|
||||||
background: #f5f7fa;
|
background: #f5f7fa;
|
||||||
color: rgb(25, 190, 107);
|
color: rgb(25, 190, 107);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
@ -436,7 +684,7 @@ export default {
|
||||||
|
|
||||||
.control-title {
|
.control-title {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: "微软雅黑 Bold", "微软雅黑 Regular", 微软雅黑, sans-serif;
|
font-family: '微软雅黑 Bold', '微软雅黑 Regular', 微软雅黑, sans-serif;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
color: rgb(102, 177, 255);
|
color: rgb(102, 177, 255);
|
||||||
|
|
@ -446,11 +694,11 @@ export default {
|
||||||
padding: 8px 9px;
|
padding: 8px 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-button [class^="el-icon-"] {
|
.el-button [class^='el-icon-'] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-label{
|
.column-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue