Newer
Older
safe_production_front / src / views / ycjg / ssjk / control-noPlugin.vue
<!-- SDK -->
<script lang="ts" setup name="videoControl">
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import html2canvas from 'html2canvas'
import type { Ref } from 'vue'
import { getCurrentInstance, nextTick, reactive, ref } from 'vue'
import dayjs from 'dayjs'
import { debounce } from 'lodash-es'
import useWebsocketStore from '@/store/modules/websocket'
import { getDevInfo, ptzControl, restartDev } from '@/api/ptz/dev'
import controlImg from '@/assets/images/control.png'
import {
  devControl,
  devControlWithSpeed,
  devToPosition,
} from '@/api/ptz/control'
import { getPicListPage, pictureAdd } from '@/api/ycjg/ssjk'
import { base64ToBlob, exportFile } from '@/utils/exportUtils'
import { log } from '@/utils/log'
import { getPreviewUrl } from '@/utils/hik'
import { initPlugin, login, logout, preview, stop } from '@/utils/HKVideo'
import { loginDH } from '@/utils/DHVideo'
const websocket = useWebsocketStore()
const $route = useRoute()
const deviceData: Ref<any> = ref({})
const instance = getCurrentInstance()
const videoControl = ref('videoControl')
const loading = ref(false)
const total = ref(0)
const list = ref([])
const refreshHK = ref(true)
const hkPlugin: any = ref(null)
const dhVideo: any = 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.map((item: any) => {
      item.url = `${window.localStorage.getItem('baseurl-safe')}/picture/download/${item.url}?token=${window.localStorage.getItem('token')}`
      return item
    })
    total.value = res.data.total
    loading.value = false
  }).catch(() => {
    loading.value = false
  })
}

// 调整大小
const resize = debounce(() => {
  if (deviceData.value.nvrManufacture === 'HIKVISION') {
    stop(0)
    refreshHK.value = true
    nextTick(() => {
      initPlugin(hkPlugin.value.clientWidth, hkPlugin.value.clientHeight, 1, '', false, () => {
        setTimeout(() => {
          preview(deviceData.value.nvrIp, '80', deviceData.value.nvrUser, deviceData.value.nvrPassword, 0, true, deviceData.value.nvrChannel)
        }, 1000)
      }, () => {
      })
    })
  }
}, 1000)

const handleResize = () => {
  if (deviceData.value.nvrManufacture === 'HIKVISION') {
    refreshHK.value = false
  }
  resize()
}

function dataURLtoBlob(dataurl: any) {
  const arr = dataurl.split(',')
  const mime = arr[0].match(/:(.*?);/)[1]
  const bstr = atob(arr[1])
  let n = bstr.length
  const u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new Blob([u8arr], { type: mime })
}

// 抓拍
async function takePhoto() {
  const time = dayjs()
  log('视频抓拍', `${deviceData.value.monitorName}_抓拍时间:${time.format('YYYY-MM-DD HH:mm:ss')}`)
  const name = `${deviceData.value.monitorName}_${time.format('YYYY-MM-DD HH_mm_ss')}`
  if (deviceData.value.nvrManufacture === 'HIKVISION') {
    WebVideoCtrl.I2_CapturePic(`${name}.jpeg`, {
      bDateDir: true,
    }).then((e: any) => {
      var blob = new Blob([e], { type: 'image/jpeg' })
      var reader = new FileReader()
      reader.readAsDataURL(blob)
      reader.onload = function (base64: any) {
        pictureAdd({
          devId: $route.query.id,
          picture: base64.target.result,
        }).then((res: any) => {
          ElMessage.success('抓图成功')
          search()
        })
      }
    }, () => {})
  }
  else {
    window.player.capture(`${name}.png`)
    pictureAdd({
      devId: $route.query.id,
      picture: window.player.getCapture(),
    }).then((res: any) => {
      ElMessage.success('抓图成功')
      search()
    })
    // domToPic(dhVideo.value, time)
  }
}
function domToPic(dom: any, time: any) {
  html2canvas(dom).then((res) => {
    // 下载到本地
    const imgUrl = res.toDataURL('image/png')
    const aLink = document.createElement('a')
    aLink.id = 'qrcodeimg'
    aLink.href = imgUrl
    aLink.download = `${deviceData.value.monitorName}_${time.format('YYYY-MM-DD HH_mm_ss')}.png`	// 导出文件名
    document.body.appendChild(aLink)
    // 模拟a标签点击事件
    aLink.click()
    // 事件已经执行,删除本次操作创建的a标签对象
    document.body.removeChild(aLink)

    // 上传到服务器
    const jpg = res.toDataURL('image/jpeg')
    pictureAdd({
      devId: $route.query.id,
      picture: jpg,
    }).then((res: any) => {
      ElMessage.success('抓图成功')
      search()
    })
  })
}
/* 预览&对讲 */
async function realplay() {
  if (deviceData.value.nvrManufacture === 'HIKVISION') {
    const canvas = document.getElementById('canvas1')
    canvas!.id = 'canvas0'
  }
  else {
    window.player.play()
  }
}
function stopPlay() {
  if (deviceData.value.nvrManufacture === 'HIKVISION') {
    const canvas = document.getElementById('canvas0')
    canvas!.id = 'canvas1'
  }
  else {
    window.player.pause()
  }
}

// 放大
function zoomIn(action: string) {
  const params = {
    command: action,
    horizonSpeed: '30',
    verticalSpeed: '30',
    zoomSpeed: '30',
  }
  ptzControl(deviceData.value.nvrIndexCode, deviceData.value.cameraIndexCode, params).then((res) => {})
}

// 上下左右
function controlWithSpeed(event: any, type: any) {
  if (event.detail === 1) {
    const params = {
      command: type,
      zoomSpeed: '30',
      horizonSpeed: '30',
      verticalSpeed: '30',
    }
    ptzControl(deviceData.value.nvrIndexCode, deviceData.value.cameraIndexCode, params).then((res) => {})
  }
}

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)
}

function loginDevice() {
  login(
    deviceData.value.nvrIp,
    80,
    deviceData.value.nvrUser,
    deviceData.value.nvrPassword,
    0,
    true,
    deviceData.value.nvrChannel)
}

const disabledPastDateStart = (date: any) => {
  if (listQuery.endTime !== '') {
    return date.getTime() > new Date(`${listQuery.endTime} 23:59:59`).getTime()
  }
  else {
    return null
  }
}

const disabledPastDateEnd = (date: any) => {
  if (listQuery.startTime) {
    return date.getTime() < new Date(`${listQuery.startTime} 00:00:00`).getTime()
  }
  else {
    return null
  }
}

onMounted(() => {
  if ($route.query && $route.query.id) {
    listQuery.devId = $route.query.id!.toString()
    getDevInfo(listQuery.devId).then((response) => {
      deviceData.value = response.data
      setTimeout(() => {
        if (response.data.nvrManufacture === 'HIKVISION') {
          initPlugin(hkPlugin.value.clientWidth, hkPlugin.value.clientHeight, 1, '', false, loginDevice, null)
        }
        else {
          nextTick(() => {
            loginDH(deviceData.value.nvrIp, '80', deviceData.value.nvrUser, deviceData.value.nvrPassword, deviceData.value.nvrChannel, 0)
          })
        }
        handleResize()
        window.addEventListener('resize', handleResize)
      }, 200)
    })
    fetchData()
  }
})

onBeforeUnmount(() => {
  try {
    window.play = null
  }
  catch (e) {}
  unwatch()
  try {
    logout(deviceData.value.nvrIp)
    stop(0)
  }
  catch (e) {}
  window.removeEventListener('resize', handleResize)
})
</script>

<template>
  <div class="video-wrap">
    <video id="dhVideo0" ref="dhVideo" class="videoControl" />
    <canvas id="dhCanvas0" class="dhCanvas" />
    <div v-if="deviceData?.nvrManufacture === 'HIKVISION' && refreshHK" id="hkPlugin" ref="hkPlugin" class="plugin 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="历史截图">
          <span style="color: #2f3742;font-size: 15px;margin-top: 2px;">开始时间:</span>
          <el-date-picker
            v-model="listQuery.startTime" type="date" clearable size="small"
            format="YYYY-MM-DD" value-format="YYYY-MM-DD"
            placeholder="请选择"
            :disabled-date="disabledPastDateStart"
            style="width: 160px;z-index: 1111111111;"
          />
          <div>
            <span style="color: #2f3742;font-size: 15px;margin-top: 2px;">结束时间:</span>
            <el-date-picker
              v-model="listQuery.endTime" type="date" clearable size="small"
              format="YYYY-MM-DD" value-format="YYYY-MM-DD"
              placeholder="请选择"
              :disabled-date="disabledPastDateEnd"
              style="margin: 5px 20px 10px 0;width: 160px;z-index: 1111111111;"
            />
            <el-button type="primary" style="width: 60px;margin-top: -5px;" size="small" @click="fetchData">
              查 询
            </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')" @mouseup="controlWithSpeed($event, 'stop')" />
          <div class="round-btn" style="top: 90px;left: 95px;" @mousedown="controlWithSpeed($event, 'down')" @mouseup="controlWithSpeed($event, 'stop')" />
          <div class="round-btn" style="top: 48px;left: 53px;" @mousedown="controlWithSpeed($event, 'left')" @mouseup="controlWithSpeed($event, 'stop')" />
          <div class="round-btn" style="top: 48px;left: 135px;" @mousedown="controlWithSpeed($event, 'right')" @mouseup="controlWithSpeed($event, 'stop')" />
        </div>
      </div>
      <div style="display: flex;max-width: 250px;flex-wrap: wrap;margin: 0 20px;">
        <el-button type="primary" class="btn" @click="stopPlay">
          <el-icon style="margin-right: 5px;">
            <svg-icon name="icon-stop" />
          </el-icon>暂 停
        </el-button>
        <el-button type="primary" class="btn" @click="realplay">
          <el-icon style="margin-right: 5px;">
            <svg-icon name="icon-play" />
          </el-icon>恢 复
        </el-button>
        <el-button type="primary" class="btn" @mousedown="zoomIn('zoomin')" @mouseup="zoomIn('stop')">
          <el-icon style="margin-right: 5px;">
            <svg-icon name="icon-zoomin" />
          </el-icon>放 大
        </el-button>
        <el-button type="primary" class="btn" @mousedown="zoomIn('zoomout')" @mouseup="zoomIn('stop')">
          <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: rgb(255 255 255 / 65%);border-radius: 10px;">
        <el-tabs v-if="deviceData !== {}" tab-position="left" style="height: 100%;width: 100%;">
          <el-tab-pane label="设备信息">
            <el-descriptions style="flex: 1;" class="margin-top" :column="3" border>
              <el-descriptions-item label="设备名称">
                {{ deviceData.monitorName }}
              </el-descriptions-item>
              <el-descriptions-item label="设备类型">
                {{ deviceData.deviceTypeName }}
              </el-descriptions-item>
              <el-descriptions-item label="所属单位">
                {{ deviceData.deptName }}
              </el-descriptions-item>
              <el-descriptions-item label="所属区域">
                {{ deviceData.areaName }}
              </el-descriptions-item>
              <el-descriptions-item label="经度">
                {{ deviceData.longitude }}
              </el-descriptions-item>
              <el-descriptions-item label="纬度">
                {{ deviceData.latitude }}
              </el-descriptions-item>
              <el-descriptions-item label="位置">
                {{ deviceData.location }}
              </el-descriptions-item>
              <el-descriptions-item label="备注">
                {{ deviceData.description }}
              </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(100% - 165px);
  width: calc(100% - 390px);
  margin-left: 10px;
  margin-top: 10px;
  position: absolute;
  background-color: #111;
}

.dhCanvas {
  height: calc(100% - 165px);
  width: calc(100% - 390px);
  margin-left: 10px;
  margin-top: 10px;
  position: absolute;
}

.right {
  height: calc(100% - 165px);
  width: 360px;
  background-color: white;
  border-radius: 10px;
  padding: 10px;
  position: absolute;
  top: 10px;
  right: 10px;
}

.bottom {
  width: calc(100% - 10px);
  height: 140px;
  position: absolute;
  left: 0;
  bottom: 5px;
  display: flex;
  border-radius: 10px;
  margin: 5px;
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  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: rgb(61 125 254 / 53%);
}

.icon-button {
  background-color: #d6e5fc;
  border-color: #d6e5fc;
  padding: 6px;
  border-radius: 6px;
  color: #4384ff;

  :deep(.el-icon) {
    width: 18px;
    height: 18px;
  }

  &:hover {
    background-color: #c3dafd;
    color: #fff;
  }
}

.add-line {
  width: 250px;
  display: flex;
  margin-left: 10px;
  z-index: 1111111;
}
</style>