<script lang="ts" setup name="ResourceList"> import type { Ref } from 'vue' import { getCurrentInstance, nextTick, reactive, ref } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import dayjs from 'dayjs' import recordrtc from 'recordrtc' import { videoTree } from '@/api/ycjg/aqbb' import { log } from '@/utils/log' import { initPlugin, loginForCallback, logout, playBack, stop } from '@/utils/HKVideo' import { loginDH, onStartCut } from '@/utils/DHVideo' import { debounce } from 'lodash-es' // import './public/isc/h5player.min.js' const router = useRouter() const treeRef: any = ref(null) const dhVideo: any = ref(null) const hkVideo: any = ref(null) const filterText = ref('') const day: any = ref('') const startTime: any = ref('') const endTime: any = ref('') // 默认查询条件 const defaultQuery = { id: '', startTime: '', endTime: '', } const listQuery = reactive({ ...defaultQuery }) const data = ref([]) const refreshHK = ref(true) const defaultProps = ref({ children: 'children', label: 'name', isDisabled: 'disabled', }) const { proxy } = getCurrentInstance() as any const loading = ref(false) const src = ref('') const player = ref(null) let treeClickCount = 0 let recordRTC: any = null let currentCameras: any = null let isRecording = false const ishk = ref(false) const makeRange = (start: number, end: number) => { const result: number[] = [] for (let i = start; i <= end; i++) { result.push(i) } return result } const disabledHours = () => { if (startTime.value !== '') { const hour = Number(startTime.value[0] + startTime.value[1]) return makeRange(0, 23).filter(item => item !== hour) } } const disabledMinutes = (hour: number) => { if (startTime.value !== '') { const minutes = Number(startTime.value[3] + startTime.value[4]) return makeRange(0, 59).filter(item => item < minutes) } } const disabledSeconds = (hour: number, minute: number) => { if (startTime.value !== '') { const minutes = Number(startTime.value[3] + startTime.value[4]) const seconds = Number(startTime.value[6] + startTime.value[7]) if (minutes === minute) { return makeRange(0, 59).filter(item => item < seconds) } else { return [] } } } function search() { play(currentCameras) } const resize = debounce(() => { if (currentCameras?.device?.nvrManufacture === 'HIKVISION') { stop(0) refreshHK.value = true nextTick(() => { initPlugin(hkVideo.value.clientWidth, hkVideo.value.clientHeight, 1, '', false, () => { setTimeout(() => { playBack(currentCameras.device, listQuery.startTime, listQuery.endTime) }, 1000) }, () => { }) }) } }, 1000) const handleResize = () => { if (currentCameras?.device?.nvrManufacture === 'HIKVISION') { refreshHK.value = false } resize() } let cutStartTime: string | null = null // 过滤 function filterNode(value: any, data: { name: string | any[] }) { if (value === '' || value === null) { return true } return data.name.includes(value) } // 开始录制 function recordStart() { if (currentCameras === null) { ElMessage.warning('视频录制需要有正在播放的回放') return } if (isRecording) { ElMessage.warning('正在录制中,请先结束录制') return } isRecording = true const time = dayjs() const fileName = `${currentCameras.device.monitorName}_${time.format('YYYY-MM-DD HH_mm_ss')}.mp4` if (currentCameras?.device?.nvrManufacture === 'HIKVISION') { WebVideoCtrl.I_StartRecord(`${fileName}`, { bDateDir: true, success() { ElMessage.success('开始录像成功') }, error(oError: any) { ElMessage.warning('开始录像失败') }, }) } else { // cutStartTime = time.format('YYYY-MM-DD HH:mm:ss') recordRTC.startRecording() } log('开始录制视频', `${currentCameras.device.monitorName}-开始录制时间:${time.format('YYYY-MM-DD HH:mm:ss')}`) } // 结束录制 function recordStop() { if (currentCameras === null) { ElMessage.warning('视频录制需要有正在播放的回放') return } if (!isRecording) { // || cutStartTime === null) { ElMessage.warning('请先开始录制') return } isRecording = false const time = dayjs() const fileName = `${currentCameras.device.monitorName}_${time.format('YYYY-MM-DD HH_mm_ss')}.mp4` if (currentCameras?.device?.nvrManufacture === 'HIKVISION') { WebVideoCtrl.I_StopRecord({ success() { ElMessage.success('结束录像成功') }, error(oError: any) { ElMessage.warning('结束录像失败') }, }) } else { // onStartCut(currentCameras.device.nvrIp, '80', currentCameras.device.nvrUser, currentCameras.device.nvrPassword, currentCameras.device.nvrChannel, cutStartTime, time.format('YYYY-MM-DD HH:mm:ss')) recordRTC.stopRecording((videoURL: any) => { const aLink = document.createElement('a') aLink.href = videoURL aLink.download = fileName document.body.appendChild(aLink) // 模拟a标签点击事件 aLink.click() // 事件已经执行,删除本次操作创建的a标签对象 document.body.removeChild(aLink) }) } log('结束录制视频', `${currentCameras.device.monitorName}-结束录制时间:${time.format('YYYY-MM-DD HH:mm:ss')}`) } // 开始回放 async function play(data: any) { listQuery.startTime = `${day.value} ${startTime.value}` listQuery.endTime = `${day.value} ${endTime.value}` if (currentCameras?.device?.nvrManufacture === 'HIKVISION') { logout(currentCameras.device.nvrIp) WebVideoCtrl.I_Stop({ success() {}, }) } currentCameras = data if (data.device.nvrManufacture === 'HIKVISION') { ishk.value = true setTimeout(() => { initPlugin(hkVideo.value.clientWidth, hkVideo.value.clientHeight, 1, '', false, () => { loginForCallback(data.device.nvrIp, '80', data.device.nvrUser, data.device.nvrPassword, 0, true, data.device.nvrChannel, () => { setTimeout(() => { playBack(data.device, listQuery.startTime, listQuery.endTime) }, 1000) }) }, null) }, 500) } else { ishk.value = false try { loginDH(data.device.nvrIp, '80', data.device.nvrUser, data.device.nvrPassword, data.device.nvrChannel, 0, true, listQuery.startTime, listQuery.endTime) } catch (e) {} } // 提交日志 log('场景回放', `${data.name}-回放时间:${listQuery.startTime}-${listQuery.endTime}`) } // 双击树 function handleNodeClick(data: any, node: any, self: any) { const now = new Date().getTime() if (now - treeClickCount < 300) { // 双击事件的判断,300毫秒内重复点击 console.log('Double click on:', data) if (data.device === '') { // 点击父亲 return } play(data) } treeClickCount = now } const unwatch = watch(filterText, (newVal) => { treeRef.value.filter(newVal) }) // 搜索重置 function reset() { Object.assign(listQuery, defaultQuery) day.value = dayjs().format('YYYY-MM-DD') startTime.value = `${dayjs().format('HH')}:00:00` endTime.value = dayjs().format('HH:mm:ss') listQuery.startTime = `${day.value} ${startTime.value}` listQuery.endTime = `${day.value} ${endTime.value}` listQuery.id = '' } function initRecordRTC() { let stream if (dhVideo.value.captureStream) { // 适用于chrome内核 stream = dhVideo.value.captureStream() } else if (dhVideo.value.mozCaptureStream) { // 适用于firefox内核 stream = dhVideo.value.mozCaptureStream() } else { ElMessage.warning('不支持captureStream,无法录制!') return } recordRTC = new recordrtc(stream, { type: 'video' }) } onBeforeUnmount(() => { unwatch() if (currentCameras?.device?.nvrManufacture === 'HIKVISION') { logout(currentCameras.device.nvrIp) } try { WebVideoCtrl.I_DestroyPlugin() } catch (e) {} window.removeEventListener('resize', handleResize) window.location.reload() }) function solveData(data: any) { data.forEach((item: any) => { if (item.device) { item.name = `${item.name} (${item.device.deviceStatusName})` console.log('修改后的name', item.name) } if (item.children && item.children.length) { solveData(item.children) } }) return data } onMounted(() => { videoTree().then((response) => { if (response.code === 200) { data.value = response.data } }) reset() setTimeout(() => { handleResize() window.addEventListener('resize', handleResize) }, 500) initRecordRTC() }) </script> <template> <app-container style="height: calc(100vh - 110px)"> <div style="display: flex;height: 100%"> <el-card class="left"> <div style="display: flex;margin: 5px 0;margin-left: -20px;width: 280px"> <span class="title">日期:</span> <el-date-picker v-model="day" :clearable="false" format="YYYY-MM-DD" value-format="YYYY-MM-DD" type="date" style="width: 225px" placeholder="回放日期" /> </div> <div style="display: flex;margin-bottom: 5px;margin-left: -20px;width: 280px"> <span class="title">时间:</span> <el-time-picker v-model="startTime" :clearable="false" format="HH:mm:ss" value-format="HH:mm:ss" placeholder="开始时间" style="margin-right: 5px;width: 110px" /> <el-time-picker v-model="endTime" :clearable="false" format="HH:mm:ss" value-format="HH:mm:ss" :disabled-hours="disabledHours" :disabled-minutes="disabledMinutes" :disabled-seconds="disabledSeconds" style="width: 110px" placeholder="结束时间" /> </div> <el-input v-model="filterText" placeholder="设备名称过滤" style="margin-bottom: 5px;margin-left: -20px;width: 275px" /> <el-tree ref="treeRef" class="filter-tree" style="width: 100%;height: 100%" :data="data" :filter-node-method="filterNode" node-key="id" :default-expand-all="true" :props="defaultProps" @node-click="handleNodeClick" > <template #default="{ node, data }"> <span style="display: flex;align-items: center;"> <el-icon v-if="data.device.deviceStatusName === '在线'" style="margin-right: 5px"> <svg-icon name="icon-online" /> </el-icon> <el-icon v-if="data.device.deviceStatusName === '离线'" style="margin-right: 5px"> <svg-icon name="icon-offline" /> </el-icon> <el-tooltip class="box-item" effect="dark" :content="node.label" placement="right" > <template #content> <span>{{ node.label }}</span> <span v-if="data.device.deviceTypeName">({{ data.device.deviceTypeName }})</span> </template> <span v-if="data.device.deviceTypeName" :style="{ 'color': data.device.deviceStatusName === '在线' ? '#0e932e' : '#606266', 'font-weight': data.device.deviceStatusName === '在线' ? 600 : 500 }">{{ node.label }}</span> <span v-else>{{ node.label }}</span> </el-tooltip> <el-icon v-if="data.device.deviceType === '0'" style="margin-left: 5px"> <svg-icon name="icon-qiang" :color="data.device.deviceStatusName === '在线' ? '#0e932e' : '#979797'"/> </el-icon> <el-icon v-if="data.device.deviceType === '1' || data.device.deviceType === '2'" style="margin-left: 5px;"> <svg-icon name="icon-ball" :color="data.device.deviceStatusName === '在线' ? '#0e932e' : '#979797'" /> </el-icon> <el-icon v-if="data.device.deviceType === '3'" style="margin-left: 5px;"> <svg-icon name="icon-ball" :color="data.device.deviceStatusName === '在线' ? '#0e932e' : '#979797'" /> </el-icon> <el-icon v-if="data.device.deviceType === '4'" style="margin-left: 5px;"> <svg-icon name="icon-ytqiang" :color="data.device.deviceStatusName === '在线' ? '#0e932e' : '#979797'" /> </el-icon> <!-- <span v-if="data.device.deviceStatusName === '在线' || node.label.slice(node.label.indexOf('(')) === '(离线)'" :style="{ color: data.device.deviceStatusName === '在线' ? 'green' : 'red' , 'font-weight': 600 }">{{ node.label.slice(node.label.indexOf('(')) }}</span> --> </span> </template> </el-tree> </el-card> <div id="home" class="right"> <div style="display:flex;"> <div style="position: absolute;right: 20px;top: 22px"> <el-button type="primary" plain @click="recordStart()"> 录制MP4 </el-button> <el-button type="info" plain @click="recordStop"> 停止录制并保存文件 </el-button> </div> </div> <div> <video ref="dhVideo" id="dhVideo0" class="dhControl" /> <canvas id="dhCanvas0" class="canvasControl" /> <div v-if="ishk && refreshHK" id="hkPlugin" ref="hkVideo" class="plugin dhControl" /> </div> </div> </div> </app-container> </template> <style lang="scss" scoped> .title { width: 50px;padding-top: 6px;color: #4b4b4b;font-size: 15px;letter-spacing: 1px; background: rgba(86, 86, 86, 0.1); } .canvasControl { height: calc(100% - 80px); width: calc((100% - 350px)); position: absolute; top: 60px; left: 328px; } .dhControl { height: calc(100% - 80px); width: calc(100% - 350px); position: absolute; top: 60px; left: 328px; background-color: #111111; } .videoControl { height: 500px !important; width: calc(100% - 10px) !important; margin-left: 10px; margin-top: 10px; position: relative; background-color: #111111; } .left { width: 300px; height: 100%; padding: 10px; overflow-y: scroll; } .right { width: calc(100% - 310px); margin-left: 10px; background-color: white; } .video-container { display: flex; flex: 1; flex-direction: row; justify-content: center; align-items: center; margin-bottom: 5px; } video { position: relative; object-fit: fill; overflow: hidden; background: #000; } </style>