<template>
  <div class="pos_right" :class="posRightInTitle ? 'pos_right_in_title' : ''">
    <van-icon name="scan" size="24" @click="onH5Scan()" />
    <van-popup
      v-model="showoptions"
      :close-on-click-overlay="false"
      position="right"
      :overlay="false"
      :style="{ width: '100%', height: '100%' }"
      duration="0.3"
    >
      <div class="scan-page">
        <!-- 返回导航栏 -->
        <NavBar :is-custom="true" @go-back="close()">
          <template slot="title">扫码录入</template>
        </NavBar>
        <!-- 扫码区域 -->
        <video :id="videoId" ref="video" class="scan-video" autoplay></video>
        <!-- 提示语 -->
        <div v-show="tipShow" class="scan-tip">{{ tipMsg }}</div>
      </div>
    </van-popup>
  </div>
</template>
<script>
  import utils from '@/utils.js'
  import {
    BrowserBarcodeReader,
    BrowserQRCodeReader,
    BrowserMultiFormatReader,
  } from '@zxing/library'
  const axios = require('axios')
  const req = function (url, data = {}, option = {}) {
    const requestData = {
      url: url,
      data: data,
      method: option.method || 'GET',
      params: option.params || {},
      onUploadProgress: option.onUploadProgress || null,
      headers: option.headers || '',
      responseType: option.responseType || 'json',
    }
    return axios(requestData)
  }
  export default {
    name: 'HtBarScan',
    props: {
      scanConfig: {
        type: String,
      },
      options: {
        type: String,
      },
      // 多行文本扫码图标展示在标题栏右边
      posRightInTitle: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        showoptions: false,
        configObj: {},
        optionsObj: {},
        codeReader: null,
        tipShow: false, // 是否展示提示
        tipMsg: '', // 提示文本内容
      }
    },
    computed: {
      videoId() {
        var id = 'video'
        if (this.configObj) {
          if (this.configObj.scanQr && !this.configObj.scanBar) {
            id = 'videoQrOnly'
          } else if (!this.configObj.scanQr && this.configObj.scanBar) {
            id = 'videoBarOnly'
          }
        }
        return id
      },
    },
    watch: {},
    mounted() {
      if (this.scanConfig) {
        this.configObj = JSON.parse(Base64.decode(this.scanConfig))
      }
      if (this.options) {
        this.optionsObj = JSON.parse(Base64.decode(this.options))
      }
    },
    methods: {
      async onH5Scan() {
        // 初始化摄像头
        let ishttps = 'https:' == document.location.protocol ? true : false
        if (!ishttps) {
          this.$toast.fail('当前环境不支持该服务')
          return
        }
        this.showoptions = true
        if (this.configObj) {
          if (this.configObj.scanQr && !this.configObj.scanBar) {
            this.codeReader = await new BrowserQRCodeReader()
          } else if (!this.configObj.scanQr && this.configObj.scanBar) {
            this.codeReader = await new BrowserBarcodeReader()
          } else {
            this.codeReader = await new BrowserMultiFormatReader()
          }
        }
        this.codeReader
          .getVideoInputDevices()
          .then((videoDevices) => {
            this.tipMsg = '正在调用摄像头...'
            this.tipShow = true

            // 默认获取摄像头列表里的最后一个设备id，通过几部测试机发现一般前置摄像头位于列表里的前面几位，所以一般获取最后一个的是后置摄像头
            let firstDeviceId =
              videoDevices[videoDevices.length - 1] &&
              videoDevices[videoDevices.length - 1].deviceId
            // 一般获取了手机的摄像头列表里不止一个，有的手机摄像头高级多层，会有变焦摄像头等情况，需要做处理
            if (videoDevices && videoDevices.length > 1) {
              // 一般通过判断摄像头列表项里的 label 字段，'camera2 0, facing back' 字符串含有 'back' 和 '0'，大部分机型是这样，如果有些机型没有，那就还是默认获取最后一个
              firstDeviceId = videoDevices.find((el) => {
                return (
                  el.label.indexOf('back') > -1 && el.label.indexOf('0') > -1
                )
              })
                ? videoDevices.find((el) => {
                    return (
                      el.label.indexOf('back') > -1 &&
                      el.label.indexOf('0') > -1
                    )
                  }).deviceId
                : videoDevices[videoDevices.length - 1].deviceId
            }
            this.decodeFromInputVideoFunc(firstDeviceId)
          })
          .catch((err) => {
            this.$toast.fail('请求设备异常')
            this.tipShow = false
          })
      },
      decodeFromInputVideoFunc(firstDeviceId) {
        // 使用摄像头扫描
        this.codeReader.reset() // 重置
        this.codeReader.decodeFromInputVideoDeviceContinuously(
          firstDeviceId,
          this.videoId,
          (result, err) => {
            this.tipMsg = '正在尝试识别...'
            if (result) {
              this.$toast.success('扫码成功!', { icon: false })
              this.onmarked(1, result.text)
              // this.tipShow = false
              this.decodeFromInputVideoFunc(firstDeviceId)
            }
            if (err && !err) {
              this.tipMsg = '识别失败'
              setTimeout(() => {
                this.tipShow = false
              }, 2000)

              this.$toast.fail('识别失败')
            }
          }
        )
      },
      //  点击返回并关闭
      close() {
        //识别完停止使用摄像头
        let thisVideo = document.getElementById(this.videoId)
        // uniapp可能用到，因为uniapp会对video标签进行二次封装，如果是Vue的话直接获取video标签
        // var video = document.getElementById('video_nav_id').getElementsByTagName('video')[0]
        if (
          thisVideo &&
          thisVideo.srcObject &&
          thisVideo.srcObject.getTracks()
        ) {
          thisVideo.srcObject.getTracks()[0].stop()
        }
        if (this.codeReader) {
          // 重置
          this.codeReader.reset()
          this.codeReader = null
        }
        this.showoptions = false
      },
      onmarked(type, result) {
        let t = this
        // 扫出来的值
        var result = result.replace(/\n/g, '')
        // this.$emit('change', result)
        // if (t.configObj.fillInput) {
        //   t.$emit('input', result)
        // }
        if (t.configObj.mappingRule) {
          try {
            t.evalMappingRule(t, t.configObj.mappingRule, result)
          } catch (error) {
            t.$toast.fail('执行映射规则失败:' + error)
          }
        }
        // t.close()
      },
      evalMappingRule(t, mappingRule, result) {
        let _this = t
        let _req = req
        const runtimePInst = utils.getRuntimeTemplateOnlineForm(_this)
        if (!runtimePInst || !runtimePInst.data) {
          _this.$toast.fail('执行映射规则失败，未获取到表单对象')
          return
        }
        let pathArr = this.optionsObj.subTablePath.split('.')

        let _data = runtimePInst.data
        let index = _data[pathArr[0]][pathArr[1]].length || 0
        let _scanResult = result
        let $Formulas = this.$Formulas
        function evil() {
          let Fn = Function(
            '_req',
            '_this',
            '_data',
            '$Formulas',
            '_scanResult',
            'index',
            mappingRule
          )
          let result = Fn(_req, _this, _data, $Formulas, _scanResult, index)
          return result
        }
        evil()
      },
    },
  }
</script>
<style lang="scss" scoped>
  .pos_right {
    display: inline-block;
  }
  .pos_right_in_title {
    position: absolute;
    top: -36px;
    right: 0;
  }
  .van-icon {
    top: -2px;
    padding: 0 0 0 10px;
  }
  .scan-index-bar {
    background-image: linear-gradient(-45deg, #42a5ff, #59cfff);
    .van-nav-bar__title,
    .van-nav-bar__arrow,
    .van-nav-bar__text {
      color: #fff !important;
    }
  }
  .scan-page {
    width: 100%;
    height: 100%;
    background-color: #363636;
    overflow-y: hidden;
    .scan-video {
      height: 85%;
      width: 100%;
    }
    .scan-tip {
      width: 100%;
      text-align: center;
      color: #fff;
      font-size: 18px;
    }
  }
</style>
