From 10298137c4d5e646a6e297c4fa1c250c70432ca0 Mon Sep 17 00:00:00 2001 From: pengwei <2071057782@qq.com> Date: 星期二, 08 四月 2025 17:31:37 +0800 Subject: [PATCH] 最新代码 --- 项目代码/client/src/views/Login.vue | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 316 insertions(+), 20 deletions(-) diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/client/src/views/Login.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/client/src/views/Login.vue" index 3f75935..bc1662c 100644 --- "a/\351\241\271\347\233\256\344\273\243\347\240\201/client/src/views/Login.vue" +++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/client/src/views/Login.vue" @@ -105,34 +105,91 @@ cursor: pointer; font-size: 1rem; " - @click="show = false" + @click="recognition" >浜鸿劯璇嗗埆鐧诲綍</span > </div> </el-form-item> </el-form> <div class="face-login" v-else> - <span style="font-size: 0.88rem; font-weight: bold; color: #333333" + <span + style=" + text-align: center; + font-size: 0.88rem; + font-weight: bold; + color: #333333; + " >璇峰皢鑴搁儴姝e钃濊壊鏄剧ず妗嗗唴锛屽苟淇濇寔鍏夌嚎鍏呰冻</span > - <div - style=" - display: flex; - justify-content: center; - align-items: center; - border: 1px solid #4386ff; - border-radius: 50%; - width: 18.75rem; - height: 18.75rem; - margin: 2.06rem 0; - background-color: #f1fcff; - " - > - <img src="@/assets/login/face.png" alt="" /> + <div style="width: 100%; display: flex; justify-content: center"> + <div + style=" + display: flex; + justify-content: center; + align-items: center; + border: 1px solid #4386ff; + border-radius: 50%; + width: 18.75rem; + height: 18.75rem; + margin: 2.06rem 0; + background-color: #f1fcff; + " + > + <img v-if="!face" src="@/assets/login/face.png" alt="" /> + <!-- <canvas v-else ref="canvasDom" /> --> + <div v-else> + <!-- 鎾斁鍣紝鐢ㄦ潵鎾斁鎷嶆憚鐨勮棰� --> + <video class="camera_video" ref="videoDom" /> + <!-- 鏄剧ず鐓х墖 --> + <!-- <img + style="border-radius: 50%; width: 18.75rem; height: 18.75rem" + v-else + :src="imgurl" + /> --> + </div> + </div> </div> - <el-button type="primary" size="small" style="width: 100%" + <!-- <el-button + v-if="!reBtn" + type="primary" + size="small" + style="width: 100%" + @click="takePhoto" >寮�濮嬭瘑鍒�</el-button > + <el-button + v-else + type="primary" + size="small" + style="width: 100%" + @click="REtakePhoto" + >閲嶆柊璇嗗埆</el-button + > --> + <div style="width: 100%; margin-top: 1rem; display: flex"> + <el-button + type="primary" + size="small" + style="width: 100%" + @click="recognition" + >閲嶆柊鑾峰彇鎽勫儚澶�</el-button + > + <el-select + v-if="videoArr.length > 1" + v-model="constraints" + placeholder="璇烽�夋嫨鎽勫儚澶�" + size="samll" + @change="changeconstraints" + style="height: 2rem" + :disabled="!videoArr.length > 1" + > + <el-option + v-for="item in videoArr" + :key="item.id" + :label="item.label" + :value="item.id" + /> + </el-select> + </div> <div style=" display: flex; @@ -150,7 +207,7 @@ cursor: pointer; font-size: 1rem; " - @click="show = true" + @click="accountlogin" >璐﹀彿鐧诲綍</span > </div> @@ -161,11 +218,13 @@ </template> <script setup> -import { getCodeImg, login } from "@/api/login"; +import { getCodeImg, login, CleanUnusedImages } from "@/api/login"; +import { UploadImg } from "@/api/user"; import { useRouter, useRoute } from "vue-router"; import { getCurrentInstance, ref, nextTick, onMounted } from "vue"; import { ElMessage } from "element-plus"; import store from "@/store"; +import axios from "axios"; const router = useRouter(); const route = useRoute(); @@ -176,7 +235,14 @@ const codeUrl = ref(""); // 楠岃瘉鐮� const loading = ref(false); // 鐧诲綍鍔犺浇鐘舵�� - +const face = ref(false); // 浜鸿劯璇嗗埆鐧诲綍 +// 鐓х墖璺緞 +const imgurl = ref(null); +// canvas鎺т欢瀵硅薄 +const canvasDom = ref(null); +// video 鎺т欢瀵硅薄 +const videoDom = ref(null); +const reBtn = ref(false); const loginForm = ref({ userName: "", password: "", @@ -236,9 +302,229 @@ nextTick(() => { userNameRef.value.focus(); }); +const videoArr = ref([]); +//閫夋嫨鐨勬憚鍍忓ご鐨刬d +const constraints = ref(""); +const videoConstraints = ref({}); +//浜鸿劯璇嗗埆鐧诲綍 +const recognition = () => { + show.value = false; + videoArr.value = []; + setTimeout(async () => { + face.value = true; + navigator.mediaDevices + .enumerateDevices() + .then((devices) => { + devices.forEach(function (device) { + if (device.kind == "videoinput") { + videoArr.value.push({ + label: device.label, + id: device.deviceId, + }); + constraints.value = videoArr.value[0].id; + } + }); + openCamera(); + }) + .catch(function (err) { + layer.msg(err.name + ": " + err.message); + }); + }, 1000); + ElMessage({ + message: "姝e湪璋冪敤鎽勫儚澶达紝璇风◢绛�...", + type: "warning", + plain: true, + duration: 1000, + }); + // 浜鸿劯璇嗗埆鐧诲綍閫昏緫 +}; + +//璐﹀彿鐧诲綍 +function accountlogin() { + stop(); + ElMessage({ + message: "姝e湪鍏抽棴鎽勫儚澶达紝璇风◢绛�...", + type: "warning", + plain: true, + duration: 2000, + }); + setTimeout(() => { + ElMessage({ + message: "鍏抽棴鎽勫儚澶存垚鍔�", + type: "success", + plain: true, + }); + }, 3000); + setTimeout(() => { + show.value = true; + face.value = false; + }, 2000); +} +//鎵撳紑鎽勫儚澶� +const openCamera = async () => { + videoConstraints.value.deviceId = { exact: constraints.value }; + + // 妫�娴嬫祻瑙堝櫒鏄惁鏀寔mediaDevices + if (navigator.mediaDevices) { + navigator.mediaDevices + // 寮�鍚棰戯紝鍏抽棴闊抽 + .getUserMedia({ + audio: false, + video: { + width: 300, // 璁剧疆瑙嗛瀹藉害 + height: 300, // 璁剧疆瑙嗛楂樺害 + facingMode: "user", // 浣跨敤鍓嶇疆鎽勫儚澶� + deviceId: videoConstraints.value.deviceId, + }, + }) + .then((stream) => { + // 灏嗚棰戞祦浼犲叆viedo鎺т欢 + videoDom.value.srcObject = stream; + // 鎾斁 + videoDom.value.play(); + Facerecognition(); + ElMessage({ + message: "鎽勫儚澶磋皟鐢ㄦ垚鍔�", + type: "success", + plain: true, + }); + }) + .catch((err) => { + console.log(err); + }); + } else { + window.alert("璇ユ祻瑙堝櫒涓嶆敮鎸佸紑鍚憚鍍忓ご锛岃鏇存崲鏈�鏂扮増娴忚鍣�"); + } +}; +const changeconstraints = (res) => { + openCamera(); +}; + +// 寮�濮嬭瘑鍒� +const takePhoto = async () => { + // 濡傛灉宸茬粡鎷嶇収浜嗗氨閲嶆柊鍚姩鎽勫儚澶� + // if (imgurl.value) { + // imgurl.value = null; + // openCamera(); + // return; + // } + + console.log(videoDom.value.videoWidth, videoDom.value.videoHeight); + // 鍒涘缓涓�涓敾甯冨厓绱狅紝璁剧疆鐢诲竷灏哄涓鸿棰戞祦鐨勫昂瀵� + const canvas = document.createElement("canvas"); + // 璁剧疆鐢诲竷澶у皬涓庢憚鍍忓ぇ灏忎竴鑷� + canvas.width = videoDom.value.videoWidth; + canvas.height = videoDom.value.videoHeight; + // 鑾峰彇鐢诲竷涓婁笅鏂囧璞� + const ctx = canvas.getContext("2d"); + // 缁樺埗褰撳墠瑙嗛甯у埌鐢诲竷涓� + ctx.drawImage(videoDom.value, 0, 0, canvas.width, canvas.height); + // 灏嗙敾甯冨唴瀹硅浆涓� Base64 鏁版嵁 + const imageDataUrl = canvas.toDataURL("image/png"); + // 瀛樺偍鍥剧墖璺緞 + imgurl.value = imageDataUrl; + + // // 鍒涘缓涓�涓浘鐗囧厓绱� + // const imageElement = new Image(); + // // 灏� Base64 鏁版嵁璁剧疆涓哄浘鐗囩殑 src 灞炴�� + // imageElement.src = imageDataUrl; + // console.log(imageElement, imageDataUrl, "鍥剧墖璺緞"); + // return; + // canvasDom.value.width = videoDom.value.videoWidth; + // canvasDom.value.height = videoDom.value.videoHeight; + // // 鎵ц鐢荤殑鎿嶄綔 + // canvasDom.value.getContext("2d").drawImage(videoDom.value, 0, 0); + // // 灏嗙粨鏋滆浆鎹负鍙睍绀虹殑鏍煎紡 + // imgurl.value = canvasDom.value.toDataURL("image/webp"); + // 鍏抽棴鎽勫儚澶� + let files = dataURLtoFile(imgurl.value, new Date().getTime() + ".png"); + const formdata = new FormData(); + formdata.append("files", files); + let response = await axios.post("/api/User/SaveFiles", formdata, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + ElMessage({ + message: "寮�濮嬭瘑鍒腑锛岃绋嶇瓑...", + type: "warning", + plain: true, + duration: 2000, + }); + setTimeout(() => { + login({ + userName: "", + password: "", + path: response.data.data, + }) + .then((res) => { + if (res.status) { + store.commit("setUserInfo", { ...res.data }); + ElMessage({ + message: "璇嗗埆鎴愬姛锛屽紑濮嬬櫥褰�", + type: "success", + plain: true, + duration: 2000, + }); + CleanUnusedImages(); + stop(); + setTimeout(() => { + router.push({ path: "/" }); + }, 1000); + } + }) + .catch((err) => { + loading.value = false; + return proxy.$message.error(err.message); + }); + setTimeout(() => { + loading.value = false; + }, 1000); + }, 1000); + reBtn.value = true; +}; +//閲嶆柊璇嗗埆 +// const REtakePhoto = () => { +// reBtn.value = false; +// takePhoto(); +// }; +// 灏� base64 杞崲涓� Blob +const dataURLtoFile = (dataurl, filename) => { + let arr = dataurl.split(","), + mime = arr[0].match(/:(.*?);/)[1], + bstr = atob(arr[1]), + n = bstr.length, + u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new File([u8arr], filename, { + type: mime, + }); +}; +// 鍏抽棴鎽勫儚澶� +const stop = () => { + let stream = videoDom.value.srcObject; + if (!stream) return; + let tracks = stream.getTracks(); + tracks.forEach((x) => { + x.stop(); + }); + clearInterval(timer.value); +}; //鑾峰彇楠岃瘉鐮� getCode(); +//瀹氭椂浜鸿劯璇嗗埆 +const timer = ref(null); +const Facerecognition = () => { + clearInterval(timer.value); + if (!show.value) { + timer.value = setInterval(() => { + takePhoto(); + }, 3000); + } +}; </script> <style lang="less" scoped> @@ -301,9 +587,19 @@ } } .face-login { + width: 30rem; display: flex; flex-direction: column; + justify-content: center; + align-content: center; + text-align: center; } } } +.camera_video { + width: 18.75rem; + height: 18.75rem; + border: 1px black solid; + border-radius: 50% 50%; +} </style> -- Gitblit v1.9.3