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