From e1b028d486713eaf55aaf35fbf334aa568059c0d Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期二, 14 四月 2026 15:46:54 +0800
Subject: [PATCH] 项目复制

---
 src/views/h5/faceAuth/components/camera.vue |  263 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 263 insertions(+), 0 deletions(-)

diff --git a/src/views/h5/faceAuth/components/camera.vue b/src/views/h5/faceAuth/components/camera.vue
new file mode 100644
index 0000000..21bec1c
--- /dev/null
+++ b/src/views/h5/faceAuth/components/camera.vue
@@ -0,0 +1,263 @@
+<template>
+  <div ref="cameraBox" class="camera_box" :style="cameraStyle" v-show="isCameraOpening">
+    <video ref="videoEl" :width="videoWidth" :height="videoHeight" autoplay></video>
+    <canvas ref="canvasEl" :width="videoWidth" :height="videoHeight" style="display:none;"></canvas>
+    
+    <el-row justify="center" class="btn_box">
+      <el-button plain text @click="closeCamera()">
+        <Icon icon="material-symbols:cancel" width="28" height="28"  style="color: #FA5252" />
+      </el-button>
+      <el-button plain text @click="startRecordImage()">
+        <Icon icon="ant-design:camera-filled" width="28" height="28"  style="color: #FA5252" />
+      </el-button>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  data () {
+    return {
+      recorderOptions: { // recorder 閰嶇疆椤�
+        mimeType: 'video/webm;codecs=vp8,opus'
+      },
+      isCameraOpening: false,
+    }
+  },
+  computed: {
+    videoWidth: function () {
+      return 400
+    },
+    videoHeight: function(){
+      return (this.videoWidth * 3) / 4
+    },
+    cameraStyle: function(){
+      return {
+        width: `${this.videoWidth}px`,
+        height: `${this.videoHeight}px`
+      }
+    },
+  },
+  beforeUnmount(){
+    this.closeCamera()
+  },
+  mounted () {
+    this.initNavigatorMedia()
+    this.openCamera()
+  },
+  methods: {
+    initNavigatorMedia: function(){
+      // 鑾峰彇濯掍綋灞炴�э紝鏃х増鏈祻瑙堝櫒鍙兘涓嶆敮鎸乵ediaDevices锛岃缃竴涓┖瀵硅薄
+      if (navigator.mediaDevices === undefined) {
+        navigator.mediaDevices = {};
+      }
+      // 浣跨敤getUserMedia锛屽洜涓哄畠浼氳鐩栫幇鏈夌殑灞炴�с��
+      // 杩欓噷锛屽鏋滅己灏慻etUserMedia灞炴�э紝灏辨坊鍔犲畠銆�
+      if (navigator.mediaDevices.getUserMedia === undefined) {
+        this.initGetUserMedia()
+      }
+    },
+    initGetUserMedia: function(){
+      navigator.mediaDevices.getUserMedia = (constraints) => {
+        // 棣栧厛鑾峰彇鐜板瓨鐨刧etUserMedia(濡傛灉瀛樺湪)
+        let getUserMedia =
+          navigator.webkitGetUserMedia ||
+          navigator.mozGetUserMedia ||
+          navigator.getUserMedia;
+        // 鏈変簺娴忚鍣ㄤ笉鏀寔锛屼細杩斿洖閿欒淇℃伅
+        // 淇濇寔鎺ュ彛涓�鑷�
+        if (!getUserMedia) {//涓嶅瓨鍦ㄥ垯鎶ラ敊
+          return Promise.reject(
+            new Error("getUserMedia is not implemented in this browser")
+          );
+        }
+        // 鍚﹀垯锛屼娇鐢≒romise灏嗚皟鐢ㄥ寘瑁呭埌鏃х殑navigator.getUserMedia
+        return new Promise((resolve, reject) => {
+          getUserMedia.call(navigator, constraints, resolve, reject);
+        });
+      };
+    },
+    openCamera: function(){ // 鎵撳紑鎽勫儚澶�
+      navigator.mediaDevices.getUserMedia({
+        audio: false,
+        video: {
+          width: this.videoWidth,
+          height: this.videoHeight,
+        }
+      }).then((stream) => {
+        this.isCameraOpening = true
+        this.mediaStream = stream
+        this.initVideoSrcObject()
+      }).catch(err => {
+        console.log(`${err.name}锛�${err.message}`);
+        this.closeCamera()
+        this.mediaErrorHandler(err)
+      });
+    },
+    closeCamera() { // 鍏抽棴鎽勫儚澶�
+      if (!this.isCameraOpening) return false
+
+      this.isCameraOpening = false
+
+      if (this.mediaStream) {
+        let tracks = this.mediaStream.getTracks()
+        tracks.forEach(track => track.stop());
+      }
+
+      if (this.mediaRecorder) this.mediaRecorder.stop();
+      this.$emit('close')
+    },
+    initVideoSrcObject: function(){ // 鍒濆鍖� 瑙嗛褰曞埗 鐨� video srcObject
+      // 鏃х殑娴忚鍣ㄥ彲鑳芥病鏈塻rcObject
+      if ("srcObject" in this.$refs.videoEl) {
+        this.$refs.videoEl.srcObject = this.mediaStream;
+        this.initVideoMoveListener()
+      } else {
+        console.log('娴忚鍣ㄤ笉鏀寔')
+      }
+    },
+    initVideoMoveListener: function(){ // 鍒濆鍖� 瑙嗛绐楀彛 鐨� 绉诲姩浜嬩欢
+      let eventState = {}
+      let startMoving = (e) => { // 寮�濮嬬Щ鍔ㄧ殑鍥炶皟
+        e.preventDefault()
+        e.stopPropagation()
+        eventState = {
+          left: this.$refs.cameraBox.offsetLeft,
+          top: this.$refs.cameraBox.offsetTop,
+          x: e.clientX,
+          y: e.clientY
+        }
+        document.addEventListener('mousemove', moving)
+        document.addEventListener('mouseup', endMoving)
+      }
+      let moving = (e) => { // 绉诲姩鐨勫洖璋�
+        e.preventDefault()
+        e.stopPropagation()
+
+        let margin = 10
+        let left = e.clientX - (eventState.x - eventState.left)
+        let top = e.clientY - (eventState.y - eventState.top)
+        let maxLeft = document.documentElement.clientWidth - this.videoWidth - margin
+        let maxTop = document.documentElement.clientHeight - this.videoHeight - margin
+
+        // 闄愬埗绉诲姩鎴浘鍖哄煙涓嶈兘绉诲埌鍙鍖哄煙澶�
+        if (left < 10) left = margin
+        if (top < 10) top = margin
+        if (left > maxLeft) left = maxLeft
+        if (top > maxTop) top = maxTop
+
+        this.$refs.cameraBox.style.left = `${left}px`
+        this.$refs.cameraBox.style.top = `${top}px`
+      }
+      let endMoving = (e) => { // 缁撴潫绉诲姩鐨勫洖璋�
+        e.preventDefault()
+        document.removeEventListener('mousemove', moving)
+        document.removeEventListener('mouseup', endMoving)
+      }
+      this.$refs.cameraBox.addEventListener('mousedown', startMoving)
+    },
+    initUploadChunk: async function(blobs){ // 鍒濆鍖栦笂浼犱换鍔�
+      let tmpBlob = new Blob(blobs, { 'type': this.recorderOptions.mimeType });
+      // let file = new File(blobs, fileName, { type: 'video/webm' });
+      let file = null
+      try {
+        // 瑙e喅 Webm 瑙嗛 duration 闂
+        file = await fixWebmMetaInfo(tmpBlob)
+      } catch (error) {
+        file = tmpBlob
+        console.log(error)
+      }
+
+      let path = await uploadChunk(file, '瀛︿範瑙嗛', 'webm')
+      if (!path) return false
+
+      if (this.recordingFlag || this.recordCameraFlag) {
+        this.$store.commit('authCamera/recordVideoUrl', path)
+        this.$store.commit('authCamera/faceCaptureAll', { flag: false, imgList: this.faceCaptureAllImgList })
+      } else {
+        this.$store.commit('authCamera/biopsyVideoUrl', path)
+      }
+    },
+    startRecordImage() { // 寮�濮嬪綍鍒跺浘鍍忥紙鎷嶇収锛�
+      if (!this.$refs.canvasEl) return false
+
+      this.drawImage()
+      // this.closeCamera()
+    },
+    drawImage: function(){ // 缁樺埗鍥剧墖
+      this.$refs.canvasEl.getContext('2d').drawImage(
+        this.$refs.videoEl,
+        0,
+        0,
+        this.videoWidth,
+        this.videoHeight
+      );
+      this.uploadBase64()
+    },
+    async uploadBase64(){ // 涓婁紶鍥剧墖
+      let base64 = this.$refs.canvasEl.toDataURL("image/png", 1);
+      if (base64) {
+        this.$emit('handlerSuccess', base64)
+        this.closeCamera()
+      } else {
+        this.$message.error('鎷嶆憚澶辫触')
+      }
+    },
+    uploadErrorHandler: function (evt) { // 涓婁紶澶辫触鍥炶皟
+      console.log(JSON.stringify(evt.target))
+    },
+    mediaErrorHandler: function(){ // 寮�鍚憚鍍忓ご鎴栧綍鍒跺睆骞曞紓甯稿鐞�
+      this.$message.error('褰撳墠娴忚鍣ㄤ笉鏀寔锛岃鏇存崲娴忚鍣�')
+    },
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.camera_box{
+  position: fixed;
+  bottom: 10px;
+  right: 10px;
+  z-index: 300;
+}
+.status_box{
+  position: absolute;
+  left: 0;
+  right: 0;
+  padding: 6px;
+  margin-top: -38px;
+  font-size: 0.8rem;
+  color: white;
+  background: rgba(0,0,0,0.4);
+  .normal > span,
+  .abnormal > span{
+    width: 6px;
+    height: 6px;
+    border-radius: 28px;
+    display: inline-block;
+    margin-right: 5px;
+  }
+  .normal > span{
+    background-color: #00E63C;
+  }
+  .abnormal > span{
+    background-color: #FF3232;
+  }
+}
+.btn_box{
+  background: rgba(0,0,0,0.4);
+  margin-top: -63px;
+  padding-top: 10px;
+  padding-bottom: 7px;
+  position: absolute;
+  left: 0;
+  right: 0;
+}
+.duration{
+  position: absolute;
+  top: 0;
+  right: 0;
+  color: white;
+  text-shadow: 1px 1px 1px black, 1px -1px 1px black, -1px -1px 1px black, -1px 1px 1px black;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.8.0