Newer
Older
safe_production_front / src / views / ycjg / sjhf / index.vue
dutingting on 31 Oct 14 KB 网关集成完成
<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>