<script lang="ts" setup name="videoControl"> import type { FormInstance, FormRules } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus' import type { Ref } from 'vue' import { getCurrentInstance, reactive, ref } from 'vue' import dayjs from 'dayjs' import useWebsocketStore from '@/store/modules/websocket' import { initPlugin, login, logout, onlyLogin, preview } from '@/utils/HKVideo' import { getDevInfo, restartDev } from '@/api/ptz/dev' import controlImg from '@/assets/images/control.png' import { devControl, devControlWithSpeed, devToPosition, setPfetchData, } from '@/api/ptz/control' import {getPicListPage, pictureAdd} from '@/api/ycjg/ssjk' import {exportFile} from "@/utils/exportUtils"; import {Search} from "@element-plus/icons-vue"; const { proxy } = getCurrentInstance() const websocket = useWebsocketStore() const $route = useRoute() const deviceData: Ref<any> = ref({ id: '', monitorName: '***********', deviceIp: '192.168.1.101', devicePort: '', deviceUser: '', devicePassword: '', nvrIp: '', nvrPort: '', nvrUser: '', nvrPassword: '', nvrChannel: '', deviceType: '', deviceTypeName: '', longitude: '', latitude: '', description: '', location: '**************************', deviceStatus: '', deviceStatusName: '', createTime: '2022-01-08', dept: '', deptName: '北京计量测试研究所', modelName: 'HA-84282', }) const realh = ref('****') const realv = ref('****') const instance = getCurrentInstance() const videoControl = ref('videoControl') const controlSpeed = ref(7) const loading = ref(false) const total = ref(1000) const list = ref([{}, {}, {}, {}, {}, {}]) const divPlugin = ref(null) const lineList: Ref<any[]> = ref([]) const timeRange = ref<[any, any]>(['', '']) const defaultQuery = { devId: '', startTime: '', endTime: '', offset: 1, limit: 6, } const listQuery = reactive({ ...defaultQuery }) // socket更新数据 const unwatch = watch(websocket, (newVal) => { }) // 搜索重置 function fetchData() { search() } function search(isNowPage = false) { if (timeRange.value) { listQuery.startTime = timeRange.value[0] as string || '' listQuery.endTime = timeRange.value[1] as string || '' } if (!isNowPage) { // 是否显示当前页,否则跳转第一页 listQuery.offset = 1 } loading.value = true getPicListPage(listQuery).then((res: any) => { list.value = res.data.rows total.value = res.data.total loading.value = false }).catch(() => { loading.value = false }) } // 调整大小 const resize = () => { WebVideoCtrl.I_Resize(divPlugin.value.clientWidth, divPlugin.value.clientHeight) } // 抓拍 async function takePhoto() { const oWndInfo = WebVideoCtrl.I_GetWindowStatus(0) if (oWndInfo != null) { const oLocalConfig = await WebVideoCtrl.I_GetLocalCfg() let szCaptureFileFormat = '0' if (oLocalConfig) { szCaptureFileFormat = oLocalConfig.captureFileFormat } const szPicName = `${deviceData.value.monitorName}_${new Date().getTime()}${szCaptureFileFormat === '0' ? '.jpg' : '.bmp'}` // plan A WebVideoCtrl.I_CapturePicData().then( (data: any) => { const blob = new Blob([data]) // 上传 const fileObj = new File([blob], szPicName, { lastModified: new Date().getTime(), type: 'image/png', }) pictureAdd({ devId: $route.query.id, file: fileObj, }).then((res: any) => { // 本地 exportFile(blob, `${deviceData.value.monitorName}_${new Date().getTime()}${szCaptureFileFormat === '0' ? '.jpg' : '.bmp'}`) ElMessage.success('抓图成功') }) }, (oError: any) => { ElMessage.error('抓图失败!') }) // // plan B // oLocalConfig.capturePath = 'C:\\capture' // oLocalConfig.playbackPicPath = 'C:\\capture' // await WebVideoCtrl.I_SetLocalCfg(oLocalConfig).then(() => {}, (oError) => {}) // WebVideoCtrl.I_CapturePic(szPicName, { // bDateDir: true, // 是否生成日期文件 // }).then( // () => { // ElMessage.success('抓图成功,文件地址:C:\\capture') // }, // (oError) => { // ElMessage.error('抓图失败!') // }) } } // 放大 function zoomIn(isStop: string) { console.log(isStop) const params = { deviceIp: deviceData.value.deviceIp, command: 'zoomIn', isStop, } devControl(params).then(() => { // ElMessage.success('放大成功') }) } // 缩小 function zoomOut(isStop: string) { const params = { deviceIp: deviceData.value.deviceIp, command: 'zoomOut', isStop, } devControl(params).then(() => { // ElMessage.success('缩小成功') }) } // 上下左右 function controlWithSpeed(event, type, isStop) { // console.log(event.detail, type, isStop) if (event.detail === 1) { const params = { deviceIp: deviceData.value.deviceIp, command: type, isStop, speed: controlSpeed.value.toString(), } devControlWithSpeed(params).then(() => {}) } else if (event.detail === 2) { // 小角度转动 let horizontalAngle = Number(realh.value) let verticalAngle = Number(realv.value) switch (type) { case 'up': verticalAngle += deviceData.value.deviceType === '1' ? -1 : 1 break case 'down': verticalAngle += deviceData.value.deviceType === '1' ? 1 : -1 break case 'left': horizontalAngle += -1 break case 'right': horizontalAngle += 1 break } if (horizontalAngle < 0 || horizontalAngle > 360) { return } if (verticalAngle < -90 || verticalAngle > 90) { return } const params = { deviceIp: deviceData.value.deviceIp, horizontalAngle, verticalAngle, } devToPosition(params).then(() => {}) } } const handleDoubleClick = (index, fullSceen, event) => { console.log(event.point) const x = (event.point[0] - 0.5) * 59.8 const y = (event.point[1] - 0.5) * 30 // (29.9 * divPlugin.value.clientHeight / divPlugin.value.clientWidth) console.log(x, y) const params = { deviceIp: deviceData.value.deviceIp, horizontalAngle: Number(realh.value) + x, verticalAngle: Number(realv.value) - y, } devToPosition(params).then(() => { // ElMessage.success('定位成功') }) } // 登录设备 function loginDevice() { login( deviceData.value.deviceIp, '80', deviceData.value.deviceUser, deviceData.value.devicePassword, 0) if (proxy.NVR) { login( deviceData.value.nvrIp, deviceData.value.nvrPort, deviceData.value.nvrUser, deviceData.value.nvrPassword, 0, true, deviceData.value.nvrChannel) // onlyLogin( // deviceData.value.nvrIp, // deviceData.value.nvrPort, // deviceData.value.nvrUser, // deviceData.value.nvrPassword, // 0, // true, // deviceData.value.nvrChannel) } } function handleSizeChange(val: number) { // emit('change', { size: val }) listQuery.limit = val search(true) } // 改变当前页 function handleCurrentChange(val: number) { // emit('change', { page: val }) listQuery.offset = val search(true) } onMounted(() => { console.log($route.query) // 从路由中获取页面类型参数 if ($route.query && $route.query.id) { getDevInfo($route.query.id.toString()).then((response) => { deviceData.value = response.data setTimeout(() => { initPlugin(1, '', false, loginDevice, handleDoubleClick) divPlugin.value = document.getElementById('divPlugin') window.addEventListener('resize', resize) }, 200) }) listQuery.devId = $route.query.id!.toString() fetchData() } }) onBeforeUnmount(() => { // WebVideoCtrl.JS_HideWnd() WebVideoCtrl.I_Resize(0, 0) unwatch() if (!proxy.NVR) { logout(deviceData.value.deviceIp) } else { try { logout(deviceData.value.nvrIp) } catch (e) {} } window.location.reload() window.removeEventListener('resize', resize) }) </script> <template> <div class="video-wrap"> <div id="divPlugin" class="plugin" :class="videoControl" /> <div class="right"> <el-tabs tab-position="top" style="height: calc(100% - 30px);width: 100%;margin-bottom: 10px;margin-top: -10px"> <el-tab-pane label="历史截图"> <div style="display: flex;margin-bottom: 10px"> <el-date-picker v-model="timeRange" type="daterange" clearable size="small" format="YYYY-MM-DD" value-format="YYYY-MM-DD" start-placeholder="开始时间" end-placeholder="结束时间" range-separator="到" style="margin-right: 20px"/> <el-button type="primary" @click="fetchData" style="width: 60px" size="small">查 询</el-button> </div> <div style="background-color: white;overflow-y: hidden;overflow-x: scroll;width: 100%;display: flex;flex-wrap: wrap;"> <div v-for="(item, index) in list" :key="index" style="height: 100px;width: 160px;margin: 5px"> <el-image style="width: 160px; height: 100px;" :src="item.url" fit="fill" /> </div> </div> </el-tab-pane> </el-tabs> <div style="z-index: 99999999;margin: 10px 10px 0;"> <el-pagination small :current-page="listQuery.offset" :page-sizes="[6]" :page-size="listQuery.limit" :total="total" layout="total,prev,pager,next" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </div> <div class="bottom"> <div class="control-title">设备控制</div> <div name="four" style="width: 150px; height: 130px;"> <el-image style="width: 130px; height: 130px;position: absolute;top: 5px;left: 50px" :src="controlImg" fit="fill" /> <div> <div class="round-btn" style="top: 8px;left: 95px" @mousedown="controlWithSpeed($event, 'up', '0')" @mouseup="controlWithSpeed($event, 'up', '1')" /> <div class="round-btn" style="top: 90px;left: 95px" @mousedown="controlWithSpeed($event, 'down', '0')" @mouseup="controlWithSpeed($event, 'down', '1')" /> <div class="round-btn" style="top: 48px;left: 53px" @mousedown="controlWithSpeed($event, 'left', '0')" @mouseup="controlWithSpeed($event, 'left', '1')" /> <div class="round-btn" style="top: 48px;left: 135px" @mousedown="controlWithSpeed($event, 'right', '0')" @mouseup="controlWithSpeed($event, 'right', '1')" /> </div> </div> <div style="display: flex;max-width: 250px;flex-wrap: wrap;margin: 0 20px"> <el-button type="primary" class="btn" @click=""> <el-icon style="margin-right: 5px"><svg-icon name="icon-stop" /></el-icon>暂 停 </el-button> <el-button type="primary" class="btn" @click=""> <el-icon style="margin-right: 5px"><svg-icon name="icon-play" /></el-icon>恢 复 </el-button> <el-button type="primary" class="btn" @mousedown="zoomIn('0')" @mouseup="zoomIn('1')"> <el-icon style="margin-right: 5px"><svg-icon name="icon-zoomin" /></el-icon>放 大 </el-button> <el-button type="primary" class="btn" @mousedown="zoomOut('0')" @mouseup="zoomOut('1')"> <el-icon style="margin-right: 5px"><svg-icon name="icon-zoomout" /></el-icon>缩 小 </el-button> <el-button type="primary" class="btn" @click="takePhoto"> <el-icon style="margin-right: 5px"><svg-icon name="icon-camera" /></el-icon>抓 拍</el-button> </div> <div style="width: calc(100% - 350px);height: 100%;display: flex;background-color: rgba(255,255,255,0.65);border-radius: 10px"> <el-tabs tab-position="left" style="height: 100%;width: 100%"> <el-tab-pane label="设备信息"> <el-descriptions style="flex: 1" class="margin-top" :column="2" border> <el-descriptions-item label="设备名称">{{deviceData.monitorName}}</el-descriptions-item> <el-descriptions-item label="所属单位">{{deviceData.deptName}}</el-descriptions-item> <el-descriptions-item label="型号"><el-tag size="small">{{deviceData.modelName}}</el-tag></el-descriptions-item> <el-descriptions-item label="位置">{{deviceData.location}}</el-descriptions-item> <el-descriptions-item label="IP">{{deviceData.deviceIp}}</el-descriptions-item> <el-descriptions-item label="安装时间">{{deviceData.createTime}}</el-descriptions-item> </el-descriptions> </el-tab-pane> </el-tabs> </div> </div> </div> </template> <style lang="scss" scoped> .video-wrap { display: flex; flex-wrap: wrap; } .videoControl { height: calc(80vh - 90px); width: calc(100% - 390px); margin-left: 10px; margin-top: 10px; position: relative; background-color: #111111; } .right { height: calc(80vh - 90px); width: 360px; margin-left: 10px; margin-top: 10px; background-color: white; border-radius: 10px; padding: 10px; } .bottom { width: calc(100% - 10px); height: calc(20vh); position: absolute; left: 0px; bottom: 5px; display: flex; overflow: hidden; border-radius: 10px; margin: 5px; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); overflow: hidden; padding: 5px; justify-content: space-between; } .control-title { color: white; background-color: #ff9f11; border-bottom-right-radius: 10px; border-top-right-radius: 10px; line-height: 30px; font-weight: bold; text-align: center; letter-spacing: 1px; font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,SimSun,sans-serif; writing-mode: vertical-rl; text-orientation: upright; width: 30px; height: 100px; } .b2 { height: 100%; width: 20%; text-align: center; overflow: hidden; } .btn { margin: 3px auto; width: 100px; } .control-row { display: flex; position: absolute; left: 260px; width: 60%; overflow: hidden; justify-content: flex-start; align-items: center; } .real-text { margin: 6px 10px; color: #6e6e6e; } .ppm-line { padding: 0; height: 100%; flex: 1; } .el-message-box { position: absolute !important; top: calc(50% - 68px) !important; left: 100px !important; } .ptz-control { width: 400px; position: absolute; right: 18%; top: 5px; display: flex; justify-content: center; align-items: flex-start; flex-wrap: wrap; } .round-btn { position: absolute; top: 14px; left: 129px; width: 42px; height: 42px; z-index: 111111; border-radius: 21px; cursor: pointer; } .round-btn:hover { background-color: rgba(61, 125, 254, 0.53); } .icon-button { background-color: #d6e5fc; border-color: #d6e5fc; padding: 6px; border-radius: 6px; color: #4384ff; :deep(.el-icon) { width: 18px; height: 18px; } //.icon-button-icon { // width: 16px; // height: 16px; //} &:hover { background-color: #c3dafd; color: #fff; } } .add-line { width: 250px; display: flex; margin-left: 10px; z-index: 1111111; } </style>