|
|
|
|
@ -59,9 +59,9 @@
|
|
|
|
|
<div class="focus-controls">
|
|
|
|
|
<div class="control-row">
|
|
|
|
|
<el-button-group>
|
|
|
|
|
<el-button icon="ZoomOut" @click="focusing(-1)" />
|
|
|
|
|
<el-button icon="ZoomOut" @mousedown="focusing(-1)" @mouseup="focusing(0)" />
|
|
|
|
|
<el-button disabled class="control-label">调焦</el-button>
|
|
|
|
|
<el-button icon="ZoomIn" @click="focusing(1)" />
|
|
|
|
|
<el-button icon="ZoomIn" @mousedown="focusing(1)" @mouseup="focusing(0)" />
|
|
|
|
|
</el-button-group>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -72,9 +72,9 @@
|
|
|
|
|
<div class="focus-controls">
|
|
|
|
|
<div class="control-row">
|
|
|
|
|
<el-button-group>
|
|
|
|
|
<el-button icon="ZoomOut" @click="focus(-1)" />
|
|
|
|
|
<el-button icon="ZoomOut" @mousedown="focus(-1)" @mouseup="focus(0)" />
|
|
|
|
|
<el-button disabled class="control-label">聚焦</el-button>
|
|
|
|
|
<el-button icon="ZoomIn" @click="focus(1)" />
|
|
|
|
|
<el-button icon="ZoomIn" @mousedown="focus(1)" @mouseup="focus(0)" />
|
|
|
|
|
</el-button-group>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -85,9 +85,9 @@
|
|
|
|
|
<div class="aperture-controls">
|
|
|
|
|
<div class="control-row">
|
|
|
|
|
<el-button-group>
|
|
|
|
|
<el-button icon="ZoomOut" @click="aperture(-1)" />
|
|
|
|
|
<el-button icon="ZoomOut" @mousedown="aperture(-1)" @mouseup="aperture(0)" />
|
|
|
|
|
<el-button disabled class="control-label">光圈</el-button>
|
|
|
|
|
<el-button icon="ZoomIn" @click="aperture(1)" />
|
|
|
|
|
<el-button icon="ZoomIn" @mousedown="aperture(1)" @mouseup="aperture(0)" />
|
|
|
|
|
</el-button-group>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -135,24 +135,46 @@
|
|
|
|
|
>
|
|
|
|
|
{{ recordingStates[index] ? '停止' : '录屏' }}
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button size="small" icon="FullScreen">放大</el-button>
|
|
|
|
|
<el-button size="small" icon="FullScreen" @click="openEnlarge(index)">放大</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 放大弹窗 -->
|
|
|
|
|
<el-dialog
|
|
|
|
|
v-model="enlargeVisible"
|
|
|
|
|
:title="enlargeTitle"
|
|
|
|
|
fullscreen
|
|
|
|
|
destroy-on-close
|
|
|
|
|
:close-on-press-escape="true"
|
|
|
|
|
@closed="handleEnlargeClosed"
|
|
|
|
|
>
|
|
|
|
|
<div class="enlarge-video-body">
|
|
|
|
|
<WebRtcPlayer
|
|
|
|
|
v-if="enlargeVisible && deviceInfos.cameras && enlargedIndex !== null"
|
|
|
|
|
:key="'enlarge-' + enlargedIndex"
|
|
|
|
|
:src="`${deviceInfos.cameras[enlargedIndex]?.videoStreaming}/whep`"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, onMounted, onBeforeUnmount, markRaw } from 'vue'
|
|
|
|
|
import { ref, onMounted, onBeforeUnmount, markRaw, computed } from 'vue'
|
|
|
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import sha256 from 'js-sha256'
|
|
|
|
|
import { getPodInfos } from '@/api/pod_detail'
|
|
|
|
|
import { newPodApi } from '@/api/user'
|
|
|
|
|
import { TopLeft, Top, TopRight, Back, Refresh, Right, BottomLeft, Bottom, BottomRight } from '@element-plus/icons-vue'
|
|
|
|
|
import fileHttp from '@/utils/fileHttp'
|
|
|
|
|
import WebRtcPlayer from '@/components/WebRtcPlayer.vue'
|
|
|
|
|
import { useKeepAlive } from '@/composables/useKeepAlive'
|
|
|
|
|
const { startKeepAlive } = useKeepAlive()
|
|
|
|
|
const { stopKeepAlive } = useKeepAlive()
|
|
|
|
|
// 方向按钮配置:3行3列
|
|
|
|
|
const directionRows = [
|
|
|
|
|
[
|
|
|
|
|
@ -192,19 +214,12 @@ const podInfo = async () => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 控制设备
|
|
|
|
|
const newPodSession = localStorage.getItem('newPodSession')
|
|
|
|
|
const params = {
|
|
|
|
|
"session": Number(newPodSession),
|
|
|
|
|
"id": 2,
|
|
|
|
|
"call": {
|
|
|
|
|
"service": "ptz",
|
|
|
|
|
"method": "setPTZCmd"
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
const newPodSession = ref(0)
|
|
|
|
|
const params = ref({})
|
|
|
|
|
// 方向控制
|
|
|
|
|
const controlPod = (x,y) => {
|
|
|
|
|
const reqParams = {
|
|
|
|
|
...params,
|
|
|
|
|
...params.value,
|
|
|
|
|
"params": {
|
|
|
|
|
"channel": 0,
|
|
|
|
|
"continuousPanTiltSpace": {
|
|
|
|
|
@ -213,14 +228,14 @@ const controlPod = (x,y) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
podApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
// 自转
|
|
|
|
|
const isRotating = ref(false)
|
|
|
|
|
const podRotation = ()=>{
|
|
|
|
|
isRotating.value = !isRotating.value
|
|
|
|
|
const reqParams = {
|
|
|
|
|
...params,
|
|
|
|
|
...params.value,
|
|
|
|
|
"params": {
|
|
|
|
|
"channel": 0,
|
|
|
|
|
"autoPanCtrl": {
|
|
|
|
|
@ -228,12 +243,12 @@ const podRotation = ()=>{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
podApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
// 调焦
|
|
|
|
|
const focusing = async(val) => {
|
|
|
|
|
const focusing = (val) => {
|
|
|
|
|
const reqParams = {
|
|
|
|
|
...params,
|
|
|
|
|
...params.value,
|
|
|
|
|
"params": {
|
|
|
|
|
"channel": 0,
|
|
|
|
|
"continuousZoomSpace": {
|
|
|
|
|
@ -241,14 +256,12 @@ const focusing = async(val) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await newPodApi(reqParams)
|
|
|
|
|
reqParams.params.continuousZoomSpace.z = 0
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
podApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
// 聚焦
|
|
|
|
|
const focus = async(val) => {
|
|
|
|
|
const focus = (val) => {
|
|
|
|
|
const reqParams = {
|
|
|
|
|
...params,
|
|
|
|
|
...params.value,
|
|
|
|
|
"params": {
|
|
|
|
|
"channel": 0,
|
|
|
|
|
"focusCtrl": {
|
|
|
|
|
@ -256,14 +269,12 @@ const focus = async(val) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await newPodApi(reqParams)
|
|
|
|
|
reqParams.params.focusCtrl.focus = 0
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
podApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
// 光圈
|
|
|
|
|
const aperture = async(val) => {
|
|
|
|
|
const aperture = (val) => {
|
|
|
|
|
const reqParams = {
|
|
|
|
|
...params,
|
|
|
|
|
...params.value,
|
|
|
|
|
"params": {
|
|
|
|
|
"channel": 0,
|
|
|
|
|
"irisCtrl": {
|
|
|
|
|
@ -271,15 +282,13 @@ const aperture = async(val) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await newPodApi(reqParams)
|
|
|
|
|
reqParams.params.irisCtrl.iris = 0
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
podApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
// 雨刷
|
|
|
|
|
const wiperCtrl = ref(false)
|
|
|
|
|
const wiper = () => {
|
|
|
|
|
const reqParams = {
|
|
|
|
|
"session": Number(newPodSession),
|
|
|
|
|
"session": Number(newPodSession.value),
|
|
|
|
|
"id": 2,
|
|
|
|
|
"call": {
|
|
|
|
|
"service": "ptz",
|
|
|
|
|
@ -289,14 +298,14 @@ const wiper = () => {
|
|
|
|
|
"wiper": wiperCtrl.value ? "on" : "off"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
podApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
// 镜头初始化
|
|
|
|
|
const initialization = ref(false)
|
|
|
|
|
const lensInitialization = async() => {
|
|
|
|
|
initialization.value = true
|
|
|
|
|
const reqParams = {
|
|
|
|
|
"session": Number(newPodSession),
|
|
|
|
|
"session": Number(newPodSession.value),
|
|
|
|
|
"id": 2,
|
|
|
|
|
"call": {
|
|
|
|
|
"service": "ptz",
|
|
|
|
|
@ -306,13 +315,37 @@ const lensInitialization = async() => {
|
|
|
|
|
"channel": 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await newPodApi(reqParams)
|
|
|
|
|
await podApi(reqParams)
|
|
|
|
|
initialization.value = false
|
|
|
|
|
}
|
|
|
|
|
const podApi = async (reqParams) => {
|
|
|
|
|
const res = await newPodApi(reqParams)
|
|
|
|
|
if(res.error && !res.result){
|
|
|
|
|
console.error('设备session失效,即将刷新请求')
|
|
|
|
|
await podLogout()
|
|
|
|
|
await loadPod()
|
|
|
|
|
newPodApi(reqParams)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 返回首页
|
|
|
|
|
const goBack = () => {
|
|
|
|
|
const goBack = async() => {
|
|
|
|
|
await podLogout()
|
|
|
|
|
router.push('/home')
|
|
|
|
|
}
|
|
|
|
|
const podLogout = async () => {
|
|
|
|
|
const params = {
|
|
|
|
|
"session": Number(newPodSession.value),
|
|
|
|
|
"id": 2,
|
|
|
|
|
"call": {
|
|
|
|
|
"service": "rpc",
|
|
|
|
|
"method": "logout"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 退出后台吊舱登录
|
|
|
|
|
await newPodApi(params)
|
|
|
|
|
localStorage.removeItem('newPodSession')
|
|
|
|
|
stopKeepAlive()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const playerRefs = ref([])
|
|
|
|
|
const recordingStates = ref({})
|
|
|
|
|
@ -380,6 +413,23 @@ const handleRecordStatus = (index, status) => {
|
|
|
|
|
recordingStates.value[index] = status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 放大弹窗
|
|
|
|
|
const enlargedIndex = ref(null)
|
|
|
|
|
const enlargeVisible = ref(false)
|
|
|
|
|
const enlargeTitle = computed(() => {
|
|
|
|
|
if (enlargedIndex.value !== null && deviceInfos.value.cameras) {
|
|
|
|
|
return deviceInfos.value.cameras[enlargedIndex.value]?.cameraLocation || '视频放大'
|
|
|
|
|
}
|
|
|
|
|
return '视频放大'
|
|
|
|
|
})
|
|
|
|
|
const openEnlarge = (index) => {
|
|
|
|
|
enlargedIndex.value = index
|
|
|
|
|
enlargeVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
const handleEnlargeClosed = () => {
|
|
|
|
|
enlargedIndex.value = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【示例】上传图片到后端
|
|
|
|
|
const uploadImageToServer = async (blob, index) => {
|
|
|
|
|
const formData = new FormData()
|
|
|
|
|
@ -394,12 +444,58 @@ const uploadImageToServer = async (blob, index) => {
|
|
|
|
|
ElMessage.error('截图上传失败')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const generateRandomString = (length = 6) => {
|
|
|
|
|
// 定义字符集:数字 + 大写字母 (去除了易混淆的 I, O 等,可根据需求调整)
|
|
|
|
|
const chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
|
|
|
|
|
|
|
|
|
|
let result = '';
|
|
|
|
|
const charsLength = chars.length;
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
|
|
|
const randomIndex = Math.floor(Math.random() * charsLength);
|
|
|
|
|
result += chars[randomIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
const loadPod = async() => {
|
|
|
|
|
// 登录后台吊舱管理
|
|
|
|
|
const random = generateRandomString()
|
|
|
|
|
const podLogin = await newPodApi({
|
|
|
|
|
"session": 0,
|
|
|
|
|
"id": 2,
|
|
|
|
|
"call": {
|
|
|
|
|
"service": "rpc",
|
|
|
|
|
"method": "login"
|
|
|
|
|
},
|
|
|
|
|
"params": {
|
|
|
|
|
"userName": "admin",
|
|
|
|
|
"password": sha256(sha256('abcd1234') + random),
|
|
|
|
|
"random": random,
|
|
|
|
|
"ip": "127.0.0.1",
|
|
|
|
|
"port": 80,
|
|
|
|
|
"encryptType": 1
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
newPodSession.value = podLogin.params.session
|
|
|
|
|
params.value = {
|
|
|
|
|
"session": Number(newPodSession.value),
|
|
|
|
|
"id": 2,
|
|
|
|
|
"call": {
|
|
|
|
|
"service": "ptz",
|
|
|
|
|
"method": "setPTZCmd"
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
localStorage.setItem('newPodSession', podLogin.params.session)
|
|
|
|
|
startKeepAlive()
|
|
|
|
|
}
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
loadPod()
|
|
|
|
|
podInfo()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
podLogout()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
@ -640,4 +736,21 @@ onBeforeUnmount(() => {
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.el-dialog.is-fullscreen) {
|
|
|
|
|
.el-dialog__body {
|
|
|
|
|
padding: 0;
|
|
|
|
|
height: calc(100% - 56px);
|
|
|
|
|
background: #000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.enlarge-video-body {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background: #000;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|