<!-- Description: 人员管理-人脸拍照组件 Author: 李亚光 Date: 2023-04-23 --> <script lang="ts" setup name="photograph"> import { ElMessage } from 'element-plus' import { uploadApi } from '@/api/person' // 父组件传来的拍照状态 true 拍照 false 关闭拍照 const props = defineProps({ tackPhoto: { type: Boolean, default: false, }, }) // 父组件 传来的 关闭拍照事件 const emits = defineEmits(['off']) // 保存图片的loading const loadingbut = ref(false) // 是否展示预览图片 const preViewVisible = ref(false) // 拍照图片 const blobFile = ref<File>() const mediaStreamTrack = ref('') const canvasRef = ref() const dialogFormVisible = ref(false) // 监听接收到拍摄状态后,开始调取摄像头权限 watch(() => props.tackPhoto, (newVal) => { if (newVal) { var video = document.querySelector('video') // 兼容代码 window.URL = (window.URL || window.webkitURL || window.mozURL || window.msURL) if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {} } if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function (constraints) { var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia if (!getUserMedia) { ElMessage.warning('浏览器未开启权限,请先开启摄像头权限') return Promise.reject(new Error('getUserMedia is not implemented in this browser')) } return new Promise((resolve, reject) => { getUserMedia.call(navigator, constraints, resolve, reject) }) } } // 摄像头调用配置 var mediaOpts = { audio: false, // video: true, video: { facingMode: 'environment' }, // 或者 "user" // video: { width: 1280, height: 720 } // video: { facingMode: { exact: "environment" } }// 或者 "user" } navigator.mediaDevices.getUserMedia(mediaOpts).then((stream: any) => { mediaStreamTrack.value = stream video = document.querySelector('video') as HTMLVideoElement if ('srcObject' in video) { video.srcObject = stream } else { video.src = (window.URL && window.URL.createObjectURL(stream)) || stream } video.play() }).catch((err) => { console.log(err) }) } }, { deep: true, immediate: true, }) // blob转文件对象 const dataURLtoFile = (dataurl: string, filename: string) => { var arr = dataurl.split(',') var mime = arr[0].match(/:(.*?);/)[1] var bstr = atob(arr[1]) var n = bstr.length var u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new File([u8arr], filename, { type: mime }) } // 点击拍照截图画面 const takePhone = () => { const canvas = document.querySelector('#canvas-photo') const video = document.querySelector('video') canvas.getContext('2d').drawImage(video, 0, 0, 400, 300) // 转为本地路径 const dataurl = canvas.toDataURL('image/jpeg') // 用时间戳作为文件名 const filenname = `${Date.now().toString()}.png` // 将blob文件转为文件对象 blobFile.value = dataURLtoFile(dataurl, filenname) preViewVisible.value = true } // 关闭弹窗 const close = () => { const canvas = document.querySelector('#canvas-photo') // 获取图片base64链接 // var image = canvas.toDataURL('image/png') canvas.getContext('2d').clearRect(0, 0, 400, 300)// 清除画布 // 关闭摄像头 mediaStreamTrack.value.getVideoTracks().forEach((track) => { track.stop() }) blobFile.value = null // 告诉父组件关闭摄像头 emits('off', '') } // 保存图片 const takePhoneUpfile = () => { console.log(blobFile.value) if (!blobFile.value) { ElMessage.warning('请先拍照,再点击保存') return false } // loadingbut.value = true const formData = new FormData() formData.append('file', blobFile.value)// 图片内容 // 上传图片 uploadApi(formData).then((res) => { // 后台接口 loadingbut.value = false blobFile.value = null const canvas = document.querySelector('#canvas-photo') // 获取图片base64链接 canvas.getContext('2d').clearRect(0, 0, 400, 300)// 清除画布 // 关闭摄像头 mediaStreamTrack.value.getVideoTracks().forEach((track) => { track.stop() }) // 告诉父组件关闭摄像头,并且把返回结果传出去 emits('off', res.data) dialogFormVisible.value = false }, (error) => { loadingbut.value = false window.console.log(error) }) } const initDialog = () => { dialogFormVisible.value = true } defineExpose({ initDialog }) </script> <template> <el-dialog v-model="dialogFormVisible" title="人脸注册" append-to-body width="900px" @close="close"> <div class="camera-box" style="width: 900px;"> <el-row :gutter="20"> <el-col :span="12"> <div style="text-align: center;font-size: 14px;font-weight: bold;margin-bottom: 10px;">摄像头</div> <!-- 这里就是摄像头显示的画面 --> <video id="video-photo" width="400" height="300"></video> <div class="iCenter"> <el-button type="primary" style="margin-top: 10px;" @click="takePhone">拍照</el-button> </div> </el-col> <el-col :span="12"> <div style="text-align: center;font-size: 14px;font-weight: bold;margin-bottom: 10px;">拍摄效果</div> <!-- 这里是点击拍照显示的图片画面 --> <canvas ref="canvasRef" id='canvas-photo' width='400' height='300' style="display: block;"></canvas> <el-button :loading="loadingbut" type='primary' @click="takePhoneUpfile" style="margin-top: 10px;">保存</el-button> </el-col> </el-row> </div> </el-dialog> </template> <style> .camera-box #canvas { border: 1px solid #DCDFE6; } </style>