<template>
  <el-dialog v-loading.fullscreen.lock="fullscreenLoading" :visible.sync="showDialog" :title="title" destroy-on-close width="750px" top="10vh" @close="closeDialog">
    <div v-if="sign" class="device-top">
      <el-button class="btn-clear" size="small" @click="openSign">{{ signList.length === 0 ? '签名' : '重签' }}</el-button>
    </div>
    <div class="preview-list">
      <div v-for="(item, i) of photoList" :key="i" class="preview-item" >
        <img :src="item.accessUrl" @click="previewPhoto(i)" >
        <i class="el-icon-delete" @click="removePhoto(i)"/>
      </div>
    </div>

    <div v-loading="loading" class="take-photo">
      <canvas v-show="stepIndex === 0 && !sign" ref="photoCanvas" :width="videoParams.video_frame_w" :height="videoParams.video_frame_h" class="canvas-phone"/>
      <div v-if="sign" :width="iWidth" :height="iHeight" class="canvas-sign">
        <div v-for="sign in signList" :key="sign.accessUrl" class="compose-area">
          <img :src="sign.agreement" crossorigin="anonymous" class="agreement-image">
          <img :src="sign.accessUrl" class="sign-image">
          <img :src="attachmentUrl" class="agreement-image">
        </div>
        <div v-if="signList.length === 0" class="tip">点击“签名”按钮后，请在高拍仪上签名</div>
      </div>
    </div>

    <div slot="footer" class="dialog-footer">
      <el-button size="small" @click="closeDialog">取消</el-button>

      <template v-if="sign">
        <el-button :disabled="signList.length === 0" :loading="showLoading" size="small" type="primary" @click="handleComposeSign">完成</el-button>
      </template>

      <template v-else>
        <el-button :disabled="photoList.length >= limit" size="small" class="btn-camera" @click="takePhoto()">拍摄</el-button>
        <el-button :disabled="photoList.length === 0" size="small" type="primary" @click="uploadList">完成</el-button>
      </template>
    </div>
    <image-viewer v-if="showViewer" :z-index="zIndex" :initial-index="imageIndex" :on-close="closeViewer" :url-list="urlList" />
  </el-dialog>
</template>
<script>
import deviceService from '@/components/Device/service'
import ImageViewer from '@/components/Uploader/image-viewer'
import { postVinUpload, cuscImageUpload } from '@/api/realname-enterprise'
import { getToken } from '@/utils/auth'
import request from '@/utils/request'
import photoStep from './components/photoStep'
import wzh from './wzh'
import { imageViewer } from '@/utils/upload'
import { jsPDF as JsPdf } from 'jspdf'
import html2canvas from 'html2canvas'
import { queryPotocolManage } from '@/api/management'

export default {
  name: 'DeviceHandle',
  components: {
    ImageViewer,
    photoStep
  },
  props: {
    // 展示弹窗
    show: {
      type: Boolean,
      default: false
    },
    // 是否签字
    sign: {
      type: Boolean,
      default: false
    },
    // 弹窗类型, photo: 图片, video: 视频
    type: {
      type: String,
      default: 'photo'
    },
    // 图片个数
    limit: {
      type: Number,
      default: 3
    },
    // 弹窗标题
    title: {
      type: String,
      default: '拍照'
    },
    // 图片列表
    data: {
      type: Array,
      default: () => { [] }
    },
    // 是否加密上传
    security: {
      type: Boolean,
      default: false
    },
    // 需要签名的字段
    signKey: {
      type: String,
      default: ''
    },
    // 附件
    attachmentUrl: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      fullscreenLoading: false,
      loading: false,
      showDialog: false,
      stepIndex: 0,
      stepData: ['拍摄', '签字'],

      openStatus: false, // 视频打开状态
      cutStatus: 0, // 0代表裁切，1代表不裁切
      changeStatus: false,
      // 设备宽高
      deviceWidth: 0,
      deviceHeight: 0,
      // 画板宽高
      iWidth: 800,
      iHeight: 490,
      // 签字状态
      startSign: false,
      isFirst: false,
      points: [],
      beginPoint: null,
      videoParams: {
        camera_type: 0, // 0代表主摄像头, 1代表副摄像头
        video_frame_w: 0,
        video_frame_h: 0
      },

      // 上传参数
      uploadConf: {
        url: this.security ? cuscImageUpload : postVinUpload,
        headers: {
          'X-Requested-With': 'XMLHttpRequest',
          'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
          'Authorization': `bearer ${getToken()}`
        },
        data: {
          rootPath: '',
          path: ''
        }
      },

      // 预览参数
      photoList: [],
      signList: [],
      showViewer: false,
      zIndex: 2000,
      imageIndex: 0,
      prevOverflow: '',
      showLoading: false
    }
  },
  computed: {
    urlList() {
      if (this.stepIndex === 0) {
        return this.photoList.map(item => {
          return item.accessUrl
        })
      } else {
        return this.signList.map(item => {
          return item.accessUrl
        })
      }
    }
  },
  watch: {
    show(val) {
      this.showDialog = val
      if (val) {
        // 如果当前需要签名，则直接打开签名页
        if (!this.sign) {
          this.initList(this.data)
          this.$nextTick(() => {
            this.openPhoto()
          })
        }
      } else {
        this.closePhoto()
        if (this.stepIndex === 1) {
          this.closeSign()
        }
        this.stepIndex = 0
        this.photoList = []
        this.signList = []
        this.startSign = false
      }
    }
  },
  methods: {
    /**
    * 初始化图片列表及签名列表数据
    * @time 2022-05-12 21:03:29
    */
    initList(list) {
      list.forEach(item => {
        if (item.type === 'photo' || !item.type) {
          this.photoList.push(item)
        } else if (item.type === 'sign') {
          this.signList.push(item)
        }
      })
    },

    /**
    * 打开拍照
    * @time 2022-05-10 15:51:55
    */
    openPhoto() {
      this.openStatus = false
      this.videoParams = {
        camera_type: 0,
        video_frame_w: 0,
        video_frame_h: 0
      }
      this.loading = true

      // 获取设备信息
      deviceService.getVideoInfo().then(data => {
        if (data.length === 2) {
          // 主摄像头
          const masterCamera = data.find(item => {
            return item.camera_type === this.videoParams.camera_type
          })
          const resolutionInfo = masterCamera.resolution_info || []
          const pixelIndex = parseInt(resolutionInfo.length / 2)

          if (this.videoParams.video_frame_w === 0 || this.videoParams.video_frame_w === resolutionInfo[pixelIndex].width) {
            this.videoParams.video_frame_w = resolutionInfo[pixelIndex].width
            this.videoParams.video_frame_h = resolutionInfo[pixelIndex].height
          }
          const ctx = this.$refs.photoCanvas.getContext('2d')
          const image = new Image()
          ctx.clearRect(0, 0, this.videoParams.video_frame_w, this.videoParams.video_frame_h)
          // 打开主视频
          deviceService.masterOpenVideo(this.videoParams, this.cutStatus, json => {
            if (!this.changeStatus) {
              this.openStatus = true
            }
            if (json.err_code === 0) {
              this.loading = false
              image.src = 'data:image/jpeg;base64,' + json.data
              image.onload = function() {
                ctx.drawImage(image, 0, 0, image.width, image.height)
              }
            } else {
              this.$message.error(json.err_msg)
              this.loading = false
            }
          })
        }
      }).catch(err => {
        this.$message.error(err)
      })
    },

    /**
    * 关闭拍照
    * @time 2022-05-10 16:21:38
    */
    closePhoto() {
      deviceService.closeVideo().then(() => {
        console.log('拍照关闭成功')
      }).catch(err => {
        console.log(err)
      })
    },

    /**
    * 主摄像头拍照
    * @time 2022-05-10 15:54:58
    */
    takePhoto() {
      if (!this.openStatus) {
        this.$message.warning('摄像头正在加载中，请稍候')
        return
      }
      deviceService.masterTakePhoto().then(data => {
        if (data) {
          const content = 'data:image/png;base64,' + data
          const blob = this.base64ToBlob(content)
          const blobUrl = URL.createObjectURL(blob)
          this.photoList.push({
            type: 'photo',
            blob,
            accessUrl: blobUrl
          })
        } else {
          this.closeDialog()
        }
      }).catch(err => {
        this.$message.error(err)
      })
    },

    /**
    * 关闭弹窗
    * @time 2022-05-10 16:21:17
    */
    closeDialog() {
      this.$emit('update:show', false)
    },

    /**
    * 下一步
    * @time 2022-05-10 16:09:45
    */
    onNextStep() {
      this.loading = true
      this.stepIndex = 1

      this.closePhoto()

      this.initSignCanvas()
      if (this.signList.length === 0) {
        this.openSign()
      }
    },

    /**
    * 上一步
    * @time 2022-05-10 16:09:36
    */
    onPrevStep() {
      this.stepIndex = 0
      this.startSign = false
      this.signList = []
      this.initSignCanvas()

      this.closeSign()
      this.$nextTick(() => {
        this.openPhoto()
      })
    },

    /**
    * 初始化签名画布
    * @time 2022-05-10 16:00:49
    */
    initSignCanvas() {
      this.loading = false
      this.ctxSign = this.$refs.signCanvas.getContext('2d')
      this.ctxSign.fillStyle = '#f9f9f9'
      this.ctxSign.fillRect(0, 0, this.iWidth, this.iHeight)
      this.ctxSign.strokeStyle = '#000' // 线条颜色
      this.ctxSign.lineWidth = 3
      this.ctxSign.lineCap = 'round' // 线条末端添加圆形线帽，减少线条的生硬感
      this.ctxSign.lineJoin = 'round' // 线条交汇时为原型边角
      // 利用阴影，消除锯齿
      this.ctxSign.shadowBlur = 1 // 线条阴影大小
      this.ctxSign.shadowColor = '#000' // 线条阴影颜色
    },

    /**
    * 页面点击重签
    * @time 2022-05-10 16:49:47
    */
    againSign() {
      if (this.signList.length > 0) {
        this.signList = []
        this.startSign = true
        this.initSignCanvas()
        this.openSign()
        return
      }
      if (!this.startSign) {
        return
      }

      this.againSignCallback()
    },

    /**
    * 外部设备重签
    * @time 2022-05-10 16:09:57
    */
    againSignCallback() {
      this.startSign = false
      this.initSignCanvas()
    },

    /**
    * 关闭签字
    * @time 2022-05-10 16:10:07
    */
    closeSign() {
      deviceService.closeSign().then(data => {
        console.log('关闭签字成功')
      }).catch(err => {
        console.log(err)
      })
    },

    /**
    * 开始签名
    * @time 2022-05-10 16:10:39
    */
    async openSign() {
      // 请求接口
      const agreements = await queryPotocolManage({ orgId: this.$store.getters.organId })
      // 源url
      const originUrlLink = agreements[this.signKey + 'Vertical']
      // 源文件路径
      const originUrl = originUrlLink ? originUrlLink.fileUrl : ''
      // 协议图片
      const agreementImage = new Image()
      // 协议图片加载完成
      agreementImage.onload = async() => {
        // 创建canvas
        const cvs = document.createElement('canvas')
        // 获取绘图上下文
        const ctx = cvs.getContext('2d')
        // 创建canvas的大小
        cvs.width = agreementImage.width
        cvs.height = agreementImage.height
        // 开始绘制图片
        ctx.drawImage(agreementImage, 0, 0, agreementImage.width, agreementImage.height)
        // 获取base64地址
        const base64 = cvs.toDataURL('image/jpeg', 0.8)
        // 创建pdf对象
        const pdf = new JsPdf('', 'pt', 'a4')
        // 开始添加图片
        pdf.addImage(base64, 'JPEG', 0, 0, 595.28, (595.28 / agreementImage.width) * agreementImage.height)
        // pdf的base64编码
        const pdfBase64 = pdf.output('dataurlstring')
        // 开始预览签名
        const respData = await wzh.previewSignT(pdfBase64.slice(pdfBase64.indexOf('base64,') + 7))
        // 将签名图片合
        this.signList = [
          { accessUrl: `data:image/png;base64,${respData.signImage}`, agreement: originUrl }
        ]
      }
      // 添加跨域
      agreementImage.setAttribute('crossOrigin', 'anonymous')
      // 开始加载图片
      agreementImage.src = originUrl
    },

    /**
     * 将签名照片和协议照片组合在一起
     */
    async handleComposeSign() {
      // 确定按钮展示loading
      this.showLoading = true
      try {
        // 将html抓换成canvas
        const canvas = await html2canvas(document.querySelector('.compose-area'), { useCORS: true })
        // 导出图片文件
        canvas.toBlob(async blob => {
          try {
            // 将生成的图片传递给父组件
            const file = await this.uploadPhoto(blob)
            // 关闭弹框
            this.closeDialog()
            // 开始通知父组件
            this.$emit('getPhoto', [file])
          } catch (error) {
            console.error(error)
          }
          // 关闭loading状态
          this.showLoading = false
        })
      } catch (error) {
        console.error(error)
        this.showLoading = false
      }
    },

    /**
    * 签名获取坐标
    * @time 2022-05-10 16:10:47
    */
    getCoordinateCallback(res) {
      if (res.err_code === 0) {
        const signStatus = res.data.status
        const xInit = res.data.x
        const yInit = res.data.y
        const xSign = this.iWidth * xInit / this.deviceWidth
        const ySign = this.iHeight * yInit / this.deviceHeight

        switch (signStatus) {
          case 161:
            this.startSign = true
            if (this.isFirst) {
              this.points.push({ xSign: xSign, ySign: ySign })

              if (this.points.length > 3) {
                const lastTwoPoints = this.points.slice(-2)
                const controlPoint = lastTwoPoints[0]
                const endPoint = {
                  xSign: (lastTwoPoints[0].xSign + lastTwoPoints[1].xSign) / 2,
                  ySign: (lastTwoPoints[0].ySign + lastTwoPoints[1].ySign) / 2
                }
                this.drawSign(this.beginPoint, controlPoint, endPoint)
                this.beginPoint = endPoint
              }
            } else {
              // 开始绘制
              this.isFirst = true
              this.points.push({ xSign: xSign, ySign: ySign })
              this.beginPoint = { xSign, ySign }
            }
            break
          case 128:
            this.points.push({ xSign: xSign, ySign: ySign })
            if (this.points.length > 3) {
              var lastTwoPoints = this.points.slice(-2)
              var controlPoint = lastTwoPoints[0]
              var endPoint = lastTwoPoints[1]
              this.drawSign(this.beginPoint, controlPoint, endPoint)
            }
            this.isFirst = false
            this.beginPoint = null
            this.points = []
            // 结束绘制
            break
        }
      } else {
        this.closeDialog()
      }
    },

    /**
    * 签名方法
    * @time 2022-05-10 16:11:01
    */
    drawSign(beginPoint, controlPoint, endPoint) {
      this.ctxSign.beginPath()
      this.ctxSign.moveTo(beginPoint.xSign, beginPoint.ySign)
      this.ctxSign.quadraticCurveTo(controlPoint.xSign, controlPoint.ySign, endPoint.xSign, endPoint.ySign)
      this.ctxSign.stroke()
      this.ctxSign.closePath()
    },

    /**
    * 下载文件
    * @time 2022-05-10 15:56:04
    */
    downloadFile(content) {
      const fileName = 'photo_' + new Date().getTime()
      const link = document.createElement('a')
      const blob = this.base64ToBlob(content)
      link.download = fileName
      link.href = URL.createObjectURL(blob)
      link.click()
    },

    /**
    * base64转blob
    * @param  {str} base64code base64编码
    * @time 2022-05-10 15:56:11
    */
    base64ToBlob(base64code) {
      const parts = base64code.split(';base64,')
      const contentType = parts[0].split(':')[1]
      const raw = window.atob(parts[1])
      const rawLength = raw.length
      const uInt8Array = new Uint8Array(rawLength)
      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i)
      }
      return new Blob([uInt8Array], {
        type: contentType
      })
    },

    /**
    * 关闭图片预览
    * @time 2022-05-10 15:57:48
    */
    closeViewer() {
      document.body.style.overflow = this.prevOverflow
      this.showViewer = false
    },

    /**
    * 图片预览
    * @param  {num} index 图片索引
    * @time 2022-05-10 15:57:17
    */
    previewPhoto(index) {
      this.imageIndex = index
      this.prevOverflow = document.body.style.overflow
      document.body.style.overflow = 'hidden'
      this.showViewer = true
    },

    /**
    * 移除图片
    * @param  {num} index 图片索引
    * @time 2022-05-10 15:58:17
    */
    removePhoto(index) {
      if (this.stepIndex === 0) {
        this.photoList.splice(index, 1)
      } else {
        this.signList.splice(index, 1)
      }
    },

    /**
    * 上传列表
    * @time 2022-05-10 18:34:04
    */
    uploadList() {
      this.fullscreenLoading = true
      const blobList = []
      const httpList = []
      this.photoList.forEach(item => {
        if (item.accessUrl.indexOf('blob:') === 0) {
          blobList.push(item)
        } else {
          httpList.push(item)
        }
      })
      this.signList.forEach(item => {
        if (item.accessUrl.indexOf('blob:') === 0) {
          blobList.push(item)
        } else {
          httpList.push(item)
        }
      })
      if (blobList.length === 0) {
        this.closeDialog()
        this.$emit('getPhoto', httpList)
        this.fullscreenLoading = false
        return
      }
      const photoList = []
      this.runQueue(blobList, async(item, next) => {
        try {
          const data = await this.uploadPhoto(item.blob)
          photoList.push({
            type: item.type,
            uuid: data.uuid,
            accessUrl: data.accessUrl
          })
          next()
        } catch (e) {
          this.closeDialog()
          this.$emit('getPhoto', [...httpList])
          this.fullscreenLoading = false
          console.log(e)
        }
      }, () => {
        this.closeDialog()
        this.$emit('getPhoto', [...httpList, ...photoList])
        this.fullscreenLoading = false
      })
    },

    /**
    * promise阵列
    * @time 2022-05-11 23:03:00
    */
    runQueue(queue, fn, cb) {
      const step = function(index) {
        if (index >= queue.length) {
          cb()
        } else {
          if (queue[index]) {
            fn(queue[index], function() {
              step(index + 1)
            })
          } else {
            step(index + 1)
          }
        }
      }
      step(0)
    },

    /**
    * 上传图片
    * @param  {str} blob 图片blob地址
    * @time 2022-05-10 15:58:45
    */
    uploadPhoto(blob) {
      return new Promise((resolve, reject) => {
        const data = new FormData()
        const file = new File([blob], `${new Date().getTime()}.png`, { type: 'image/png' })
        const opts = { ...this.uploadConf }
        data.append('file', file)
        data.append('path', opts.data.path)
        data.append('rootPath', opts.data.rootPath)
        opts.data = data
        return request(opts).then(async data => {
          // 如果当前需要加密
          if (this.security) {
            data.accessUrl = await imageViewer(data.uuid)
          }
          resolve(data)
        }).catch(err => {
          reject(err)
        })
      })
    }
  }
}
</script>
<style lang="scss" scoped>
.device-top {
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
}
.btn-camera,
.btn-camera:hover {
  color: #2A68FF;
  border: 1px solid #B9CFFF !important;
  padding-left: 30px;
  background:#F5F7FF url(./components/photoStep/icon_camera.svg) no-repeat 10px center / 16px 17px;
}
.btn-clear,
.btn-clear:hover {
  color: #2A68FF;
  border: 1px solid #B9CFFF !important;
  padding-left: 30px;
  background:#F5F7FF url(./components/photoStep/icon_clear.svg) no-repeat 10px center / 16px 16px;
}
.take-photo {
  height: 400px;
  .canvas-phone,
  .canvas-sign {
    width: 100%;
    height: 100%;
    display: block;
    border: 1px dashed #ddd;
    border-radius: 4px;
    overflow: auto;
    .compose-area {
      width: 600px;
      position: relative;
      margin: 0 auto;
      .agreement-image {
        width: 100%;
        height: auto;
      }
      .sign-image {
        display: block;
        width: auto;
        height: 80px;
        margin: -80px 0 0 52%;
      }
    }
    .tip {
      color: #666666;
      font-size: 24px;
      text-align: center;
      line-height: 380px;
    }
  }
}
.preview-list {
  display: flex;
  .preview-item {
    position: relative;
    margin: 0 8px 10px 0;
    height: 68px;
    width: 68px;
    border-radius: 3px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    background: #f2f2f2;
    &:hover {
      .el-icon-delete {
        opacity: 1;
      }
    }
    .el-icon-delete {
      width: 68px;
      height: 20px;
      line-height: 20px;
      background: rgba(0, 0, 0, 0.5);
      color: #ccc;
      text-align: center;
      cursor: pointer;
      position: absolute;
      bottom: 0;
      left: 0;
      font-size: 12px;
      opacity: 0;
      transition: opacity .2s ease;
    }
  }
  img {
    width: 100%;
    position: relative;
    cursor: pointer;
    &:last-child {
      margin-right: 0;
    }
  }
}
.dialog-footer {
  text-align: right;
}
</style>
