Newer
Older
safe_production_front / src / views / ycjg / ssjk / control.vue
<!-- gm706 -->
<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 { controlCamera, init, login, logout, sipVideoFn, unInit, unSipVideo, videoInit } from '@/utils/Dispatch706'
const videoLib: any = ['']
const callId: any = ['']
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 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(() => {
  nextTick(() => {
    setTimeout(() => {
      // handleResize()
      init('ssjk/page', () => {
        videoLib[0] = videoInit(0)
        login()
        // 退出上一个callId
        if (callId[0] !== '' && callId[0] !== null) {
          unSipVideo(callId[0], videoLib[0]) // 挂断
        }
        callId[0] = sipVideoFn(videoLib[0], deviceData.value.cameraIndexCode, 6) // 拉取视频
      })
      window.addEventListener('resize', handleResize)
    }, 200)
  })
}, 1000)

function handleResize() {
  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')}`
  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() {
  window.player.play()
}
function stopPlay() {
  window.player.pause()
}

// 放大/缩小、调近\调远
function controlFocus(command: number, action: number) {
  controlCamera(deviceData.value.cameraIndexCode, command, action, 30)
}

const cameraParam = {
  up: {
    command: 1,
    direction: 0,
  },
  down: {
    command: 1,
    direction: 1,
  },
  left: {
    command: 2,
    direction: 0,
  },
  right: {
    command: 2,
    direction: 1,
  },
} as any

// 上下左右
function controlWithSpeed(event: any, type: string) {
  controlCamera(deviceData.value.cameraIndexCode, cameraParam.type.command, cameraParam.type.direction, 30)
}

// 停止
function stopControlCamera(event: any, type: string) {
  controlCamera(deviceData.value.cameraIndexCode, 0)
}

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

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(() => {
        handleResize()
        init('ssjk/page', () => {
          videoLib[0] = videoInit(0)
          if (callId[0] !== '' && callId[0] !== null) {
            unSipVideo(callId[0], videoLib[0]) // 挂断
          }
          callId[0] = sipVideoFn(videoLib[0], deviceData.value.cameraIndexCode, 6) // 拉取视频
          login()
        })
        window.addEventListener('resize', handleResize)
      }, 200)
    })
    fetchData()
  }
})

onBeforeUnmount(() => {
  unwatch()
  logout()
  unInit()
  window.removeEventListener('resize', handleResize)
})
</script>

<template>
  <div class="video-wrap">
    <div id="video0" class="dhControl" style="height: calc(100% - 165px);width: calc(100% - 20px);margin-left: 10px;margin-top: 10px;" />
    <div class="right-block" />
    <!-- <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 0px;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="stopControlCamera" />
          <div class="round-btn" style="top: 90px;left: 95px" @mousedown="controlWithSpeed($event, 'down')" @mouseup="stopControlCamera" />
          <div class="round-btn" style="top: 48px;left: 53px" @mousedown="controlWithSpeed($event, 'left')" @mouseup="stopControlCamera" />
          <div class="round-btn" style="top: 48px;left: 135px" @mousedown="controlWithSpeed($event, 'right')" @mouseup="stopControlCamera" />
        </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="controlFocus(4, 0)" @mouseup="stopControlCamera">
          <el-icon style="margin-right: 5px">
            <svg-icon name="icon-zoomin" />
          </el-icon>放 大
        </el-button>
        <el-button type="primary" class="btn" @mousedown="controlFocus(4, 1)" @mouseup="stopControlCamera">
          <el-icon style="margin-right: 5px">
            <svg-icon name="icon-zoomout" />
          </el-icon>缩 小
        </el-button>
        <el-button type="primary" class="btn" @mousedown="controlFocus(5, 0)" @mouseup="stopControlCamera">
          <el-icon style="margin-right: 5px">
            <svg-icon name="icon-focusIn" />
          </el-icon>调 近
        </el-button>
        <el-button type="primary" class="btn" @mousedown="controlFocus(5, 1)" @mouseup="stopControlCamera">
          <el-icon style="margin-right: 5px">
            <svg-icon name="icon-focusOut" />
          </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 {
  width: 100%;
  height: 100%;
  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;
}

.dhControl {
  // height: calc(50% - 10px) !important;
  // width: calc(50% - 195px) !important;
  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;
}

.right-block {
  height: calc(100% - 165px);
  width: 10px;
  background-color: transparent;
}

.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;
  }

  // .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>