fbw vor 2 Wochen
Ursprung
Commit
756a0bb52c

BIN
public/imgs/personLK.png


+ 1 - 1
public/index.html

@@ -5,7 +5,7 @@
   <meta charset="utf-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width,initial-scale=1.0" />
-  <link rel="icon" href="<static/setting/favicon.ico" />
+  <link rel="icon" href="static/setting/favicon.ico" />
   <script src="js/config.js?"></script>
   <script src="js/station.js"></script>
 </head>

+ 8 - 0
src/API/custom.js

@@ -102,6 +102,14 @@ export const savePerson = data =>
     data,
     notShowLoading: true,
   })
+//检索人员信息
+export const GetPersonList = data =>
+  $http({
+    method: 'post',
+    url: '/System/Person/GetPageListJson',
+    data,
+    notShowLoading: true,
+  })
 //人员绑定标签信息
 export const bindingCard = data =>
   $http({

+ 18 - 0
src/API/report.js

@@ -7,4 +7,22 @@ export const getMileageList = data =>
     url: 'customPage/page/getList',
     data,
     notShowLoading: true,
+  })
+
+  // 获取通用报表信息
+export const getDataList = data =>
+  $http({
+    method: 'POST',
+    url: 'Report/Data/GetList',
+    data,
+    notShowLoading: true,
+  })
+
+  // 更新通用报表信息
+export const updateDataList = data =>
+  $http({
+    method: 'POST',
+    url: 'customPage/Page/Update',
+    data,
+    notShowLoading: true,
   })

+ 5 - 0
src/router/index.js

@@ -38,6 +38,11 @@ const routes = [
     name: 'leave',
     component: () => import('@/views/leave/index.vue'),
   },
+  {
+    path: '/test',
+    name: 'test',
+    component: () => import('@/views/test/index.vue'),
+  },
 ]
 
 const router = new VueRouter({

+ 12 - 0
src/utils/request.js

@@ -66,6 +66,18 @@ $http.interceptors.response.use(
     // if (res.code === 200) return res;
     if ((res.code + "").startsWith("2")) {
       return res;
+    } else if (res.code === 40000) {
+      // console.log("ERROR:", res, localStorage.getItem("token"));
+      Message({
+        message: "登录超时,请重新登录",
+        type: "error",
+        duration: 800,
+      });
+      setTimeout(() => {
+        localStorage.clear();
+        window.location.reload();
+      }, 500);
+      // this.$router.push("/login");
     } else {
       // Message({
       //   message: res?.msg || "连接错误",

+ 7 - 7
src/views/components/baseHeader/left.vue

@@ -112,7 +112,12 @@ export default {
           path: "/basePage",
         },
         {
-          id: "1",
+          id: "2",
+          name: "今日进站",
+          path: "/leave",
+        },
+        {
+          id: "3",
           name: "退出登记",
           path: "/basePage",
         },
@@ -127,11 +132,6 @@ export default {
         //   name: "绑卡记录",
         //   path: "/Page1?path=custompage%2Fpage?pageId=839895523707068416",
         // },
-        // {
-        //   id: "3",
-        //   name: "还卡记录",
-        //   path: "/leave",
-        // },
       ],
       activeSubNav: "",
       datetimeData: {
@@ -167,11 +167,11 @@ export default {
     },
   },
   created() {
+    this.refreshTime();
     // this.getMenuList();
   },
   mounted() {
     this.title = SYSTEM_TITLE;
-    this.refreshTime();
     this.$EventBus.$on("alarmCount", (data) => {
       this.alarmCount = +data;
     });

+ 10 - 5
src/views/components/dialog/RiskTipDialog.vue

@@ -379,7 +379,8 @@ export default {
           this.person = { cardNo: 0 };
         }
       } catch (err) {
-        console.log(err, 123);
+        this.$message.error(err.msg);
+        console.log(err, "RFID查询");
       }
     },
     async savePersonInfo(person) {
@@ -396,6 +397,7 @@ export default {
         }
       } catch (err) {
         console.log(err);
+        this.$message.error(err.msg);
         console.log("绑定失败");
         this.enable = true;
         this.info = err.msg;
@@ -420,7 +422,8 @@ export default {
         const res = await bindAndUnbindrecord(record);
         console.log("绑定记录反馈信息:", res);
       } catch (err) {
-        console.log(err);
+        this.$message.error(err.msg);
+        console.log(err, "基础信息绑定错误");
       }
     },
     async unbindRecord(person) {
@@ -440,7 +443,8 @@ export default {
           this.unbindNumber = "";
         }, 500);
       } catch (err) {
-        console.log(err);
+        this.$message.error(err.msg);
+        console.log(err, "基础信息解绑错误");
       }
     },
     checkrecognizedNumberBind() {
@@ -540,8 +544,9 @@ export default {
             this.info = "刷卡识别中。。。请刷卡";
           }, 1000);
         }
-      } catch (error) {
-        console.log(error);
+      } catch (err) {
+        this.$message.error(err.msg);
+        console.log(er, "绑卡操作");
       }
     },
     setZIndex(val) {

+ 203 - 51
src/views/leave/index.vue

@@ -2,51 +2,103 @@
   <div class="recognize-main">
     <template>
       <!-- 退卡人员信息 -->
-      <div class="infomation_content">
+      <div>
         <div>
-          <div class="infomation_prompt">
-            人员信息(请依次,点击退卡-再刷卡-确认退卡。):
-          </div>
-          <div class="infomation_data_line"></div>
-          <div class="infomation_data">
-            <div
-              class="infomation_data_content"
-              v-for="(item, index) in stepProcessInfomation"
-              :key="item.step"
-            >
-              <span class="infomation_data_prop">{{ item.propName }}</span>
-              <span>
-                <el-input
-                  ref="dealContent"
-                  v-model="infomation[item.prop]"
-                  rows="1"
-                  type="textarea"
-                  style="
-                    font-size: 40px;
-                    font-family: 'Microsoft YaHei';
-                    width: 380px;
-                    height: 68px;
-                  "
-                />
-              </span>
-            </div>
-          </div>
-          <div class="infomation_button">
-            <el-button
-              style="width: 160px; height: 80px; font-size: xx-large"
-              type="primary "
-              size="medium "
-              @click="handleClick(infomation, 1)"
-              >退卡</el-button
-            >
-            <el-button
-              style="width: 160px; height: 80px; font-size: xx-large"
-              type="primary "
-              size="medium "
-              @click="handleClick(infomation, 2)"
-              >确认</el-button
-            >
-          </div>
+          <el-row>
+            <el-col class="infomation_content" :span="18">
+              <div class="infomation_prompt">
+                今日进站信息概览:登记信息共计{{ companyCount }}条,人数共计{{
+                  personsCount
+                }}人次
+              </div>
+              <div class="infomation_data_line"></div>
+              <div>
+                <el-table
+                  height="70vh"
+                  :data="personComingList"
+                  style="width: 100%"
+                  :key="refreKey"
+                  :default-sort="{ prop: 'field001', order: 'descending' }"
+                >
+                  <el-table-column align="center" prop="field003" label="单位">
+                  </el-table-column>
+                  <el-table-column
+                    align="center"
+                    prop="field006"
+                    label="进站目的"
+                  >
+                  </el-table-column>
+                  <el-table-column align="center" prop="field002" label="领队">
+                  </el-table-column>
+                  <el-table-column
+                    align="center"
+                    prop="field005"
+                    label="联系电话"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    sortable
+                    align="center"
+                    prop="field001"
+                    label="进站时间"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    sortable
+                    align="center"
+                    prop="field013"
+                    label="离站时间"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    align="center"
+                    prop="field007"
+                    label="总人数"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    align="center"
+                    prop="field008"
+                    label="同行人员"
+                  >
+                  </el-table-column>
+                  <el-table-column align="center" label="操作">
+                    <template slot-scope="scope">
+                      <el-button
+                        v-if="!scope.row.field013"
+                        type="primary"
+                        size="medium "
+                        @click="handleClick(scope.row, 3)"
+                        >离站</el-button
+                      >
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </div>
+            </el-col>
+            <el-col class="infomation_content" :span="6">
+              <div class="infomation_prompt">
+                今日发出未收回卡{{ inList.length }}张
+              </div>
+              <div class="infomation_data_line"></div>
+              <div>
+                <el-table
+                  height="70vh"
+                  :data="inList"
+                  style="width: 100%"
+                  :key="refreKey"
+                >
+                  <el-table-column align="center" prop="field001" label="姓名">
+                  </el-table-column>
+                  <el-table-column align="center" label="在站">
+                    <template slot-scope="scope">
+                      {{ scope.row ? "未还卡" : "还卡" }}
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </div>
+            </el-col>
+          </el-row>
         </div>
       </div>
     </template>
@@ -55,6 +107,8 @@
 
 <script>
 import BaseTableList from "@/views/components/base/BaseTableList";
+import { getDataList, updateDataList } from "@/API/report";
+import Dayjs from "dayjs";
 
 export default {
   name: "recognize",
@@ -107,6 +161,13 @@ export default {
       cameraInfo: {
         id: "1",
       },
+      personComingList: [{}],
+      personBindList: [{}],
+      personUnbindList: [{}],
+      inList: [{}],
+      refreKey: 1,
+      companyCount: 0,
+      personsCount: 0,
     };
   },
   watch: {
@@ -125,8 +186,88 @@ export default {
       immediate: true,
     },
   },
-  mounted() {},
+  mounted() {
+    this.getComingDataList();
+    this.getBindList().then(() => {
+      this.getUnbindList();
+    });
+  },
   methods: {
+    async getComingDataList() {
+      try {
+        const params = {
+          sql: "select CAST(id as char) as id,field001,field002,field003,field004,field005,field006,field007,field008,field009,field010,field011,field012,field013 from custom_table_person_coming_records where TO_DAYS(field001) = TO_DAYS(NOW())", //登记时间
+        };
+        const res = await getDataList(params);
+        this.personComingList = res.data.content;
+        this.companyCount = this.personComingList.length;
+        this.personsCount = 0;
+        this.personComingList.forEach((e) => {
+          this.personsCount += Number(e.field007);
+        });
+        console.log("入场登记报表sql", params, res);
+        this.refreKey++;
+      } catch (err) {
+        console.log(err);
+      }
+    },
+    async getBindList() {
+      try {
+        const params = {
+          sql: "select CAST(id as char) as id,field001,field002,field003,field004,field005,field006,field007 from custom_table_bind_card_records where TO_DAYS(BaseCreateTime) = TO_DAYS(NOW())", //登记时间
+        };
+        const res = await getDataList(params);
+        this.personBindList = res.data.content;
+        // this.companyCount = this.personComingList.length;
+        // this.personsCount = 0;
+        // this.personComingList.forEach((e) => {
+        //   this.personsCount += Number(e.field007);
+        // });
+        console.log("今日绑卡", params, res);
+        this.refreKey++;
+      } catch (err) {
+        console.log(err);
+      }
+    },
+    async getUnbindList() {
+      try {
+        const params = {
+          sql: "select CAST(id as char) as id,field001,field002,field003,field004,field005 from custom_table_unbind_card_records where TO_DAYS(BaseCreateTime) = TO_DAYS(NOW())", //登记时间
+        };
+        const res = await getDataList(params);
+        this.personUnbindList = res.data.content;
+        // 过滤标记
+        for (let i = 0; i < this.personUnbindList.length; i++) {
+          const e = this.personUnbindList[i];
+          for (let j = 0; j < this.personBindList.length; j++) {
+            const bind = this.personBindList[j];
+            if (bind.field001 === e.field001 && bind.field004 === e.field004) {
+              this.personBindList[j].in = true;
+            }
+          }
+        }
+        //过滤显示
+        this.inList = this.personBindList.filter((e) => !e.in);
+        // console.log(
+        //   "在站",
+        //   this.personBindList,
+        //   this.personUnbindList,
+        //   this.inList
+        // );
+        this.refreKey++;
+      } catch (err) {
+        console.log(err);
+      }
+    },
+    async saveComingRecord(data) {
+      try {
+        const res = await updateDataList(data);
+        console.log("入场登记报表保存离站时间", data, res);
+        this.refreKey++;
+      } catch (err) {
+        console.log(err);
+      }
+    },
     handleClick(data, type) {
       console.log("退卡了哦!", data);
       if (type) {
@@ -149,7 +290,12 @@ export default {
             console.log("确认退卡", data);
             this.$store.dispatch("dialog/addRiskDialog", dialogConfig);
             break;
-
+          case 3:
+            data.pageId = "842348058862690304";
+            data.field013 = Dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss");
+            console.log("确认离站", data);
+            this.saveComingRecord(data);
+            break;
           default:
             break;
         }
@@ -172,13 +318,15 @@ export default {
   color: #fff;
   .infomation_content {
     color: #fff;
+    padding: 6px;
+    height: 100%;
     .infomation_prompt {
-      font-size: 60px;
-      margin-top: 2%;
-      margin-left: 2%;
+      font-size: 40px;
+      margin-top: 40px;
+      margin-left: 20px;
     }
     .infomation_prop {
-      margin-top: 14%;
+      margin-top: 100px;
       width: 100%;
       height: 100px;
       display: flex;
@@ -187,7 +335,11 @@ export default {
       font-size: 60px;
     }
     .infomation_data_line {
-      margin-top: 5%;
+      margin-top: 40px;
+    }
+    .infomation_table {
+      height: 195px;
+      overflow-y: scroll;
     }
     .infomation_data {
       width: 100%;

+ 2 - 1
src/views/recognize/components/VideoLocalWindow.vue

@@ -105,7 +105,8 @@ export default {
           // 新增定时截图功能
           this.captureInterval = setInterval(() => {
             this.captureImage();
-          }, 3000);
+            console.log("截图上传");
+          }, 2500);
         }
       } catch (err) {
         console.error("摄像头访问错误:", err);

+ 371 - 0
src/views/recognize/components/VideoLocalWindowBack.vue

@@ -0,0 +1,371 @@
+<template>
+  <div class="videoContainer">
+    <img id="background-image" :src="backgroundImageSrc" alt="背景图片" />
+    <video ref="video" autoplay muted playsinline></video>
+    <div id="timestamp" v-show="false">
+      {{ datetimeData.date + " " + datetimeData.time }}
+    </div>
+    <!-- <el-button @click="startCamera">开启摄像头</el-button>
+    <el-button @click="stopCamera" :disabled="!isCameraActive"
+      >关闭摄像头</el-button
+    > -->
+  </div>
+</template>
+
+
+<script>
+import { GetPersonByFace, GetPersonByFaceBase64 } from "@/API/custom";
+import Dayjs from "dayjs";
+
+export default {
+  props: {
+    processStep: {
+      type: String,
+      require: true,
+    },
+    isUpload: {
+      type: String,
+      require: false,
+    },
+    cameraData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  watch: {
+    processStep: {
+      handler(step) {
+        if (step == 0 || step == 9) {
+          this.startCamera();
+        } else {
+          this.stopCamera();
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
+    isUpload: {
+      handler(val) {
+        console.log("上传子级开关", val);
+        if (val) {
+          this.uploadImage();
+        } else {
+          this.isUpload = false;
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
+  },
+  data() {
+    return {
+      backgroundImageSrc: "/imgs/personLK.png",
+      isCameraActive: false,
+      stream: null,
+      captureInterval: null, // 新增定时器变量
+      datetimeData: {
+        date: Dayjs(new Date()).format("YYYY-MM-DD"),
+        time: Dayjs(new Date()).format("HH:mm:ss"),
+        week: Dayjs(new Date()).format("dddd"),
+      },
+    };
+  },
+  methods: {
+    refreshTime() {
+      let speed = 1000;
+      let timer = null;
+      let theNowTime = () => {
+        // this.datePersonData.time = Dayjs(new Date()).format('HH:mm:ss')
+        this.datetimeData.time = Dayjs(new Date()).format("HH:mm:ss");
+        this.datetimeData.date = Dayjs(new Date()).format("YYYY-MM-DD");
+        this.datetimeData.week = Dayjs(new Date()).format("dddd");
+      };
+      timer = setInterval(theNowTime, speed);
+      this.$once("hook:beforeDestroy", () => {
+        clearInterval(timer);
+        timer = null;
+      });
+    },
+    async startCamera() {
+      try {
+        const stream = await navigator.mediaDevices.getUserMedia({
+          video: {
+            width: { ideal: 1280 },
+            height: { ideal: 800 },
+            facingMode: "user",
+          },
+        });
+        this.stream = stream;
+        this.isCameraActive = true;
+        this.$refs.video.srcObject = stream;
+
+        if (this.isUpload) {
+          setTimeout(() => {
+            this.uploadImage();
+          }, 1000);
+        } else {
+          // 新增定时截图功能
+          this.captureInterval = setInterval(() => {
+            this.captureImage();
+            console.log("截图上传");
+          }, 2500);
+        }
+      } catch (err) {
+        console.error("摄像头访问错误:", err);
+        alert(`摄像头访问失败: ${err.message}`);
+      }
+    },
+    stopCamera() {
+      if (this.stream) {
+        this.stream.getTracks().forEach((track) => track.stop());
+        this.isCameraActive = false;
+        this.$refs.video.srcObject = null;
+        this.stream = null;
+
+        // 停止定时器
+        if (this.captureInterval) {
+          clearInterval(this.captureInterval);
+          this.captureInterval = null;
+        }
+      }
+    },
+    // 新增截图方法
+    // async captureImage() {
+    //   const video = this.$refs.video;
+    //   video.currentTime = 1;
+    //   const canvas = document.createElement("canvas");
+    //   canvas.width = video.videoWidth;
+    //   canvas.height = video.videoHeight;
+    //   canvas
+    //     .getContext("2d")
+    //     .drawImage(video, 0, 0, canvas.width, canvas.height);
+    //   // 下载到本地
+    //   // const link = document.createElement("a");
+    //   // link.download = `capture_${new Date()
+    //   //   .toISOString()
+    //   //   .replace(/[:.]/g, "-")}.png`;
+    //   // link.href = canvas.toDataURL("image/png");
+    //   // link.click();
+
+    //   // 将canvas转为Blob对象
+    //   canvas.toBlob(async (blob) => {
+    //     if (blob) {
+    //       console.log("blob数据", blob);
+    //       const formData = new FormData();
+    //       formData.append("timestamp", new Date().toISOString());
+    //       formData.append("file", blob, "screenshot.png");
+    //       console.log("上传数据", formData);
+    //       try {
+    //         // const res = await GetPersonByFace(formData);
+    //         // console.log("上传反馈", res);
+    //         // const response = await fetch("System/Person/GetPersonByFace", {
+    //         //   method: "POST",
+    //         //   body: formData,
+    //         // });
+    //       } catch (err) {
+    //         console.error("上传过程中出错:", err);
+    //       }
+    //     }
+    //   }, "image/png");
+    // },
+
+    async captureImage() {
+      const video = this.$refs.video;
+      if (video.paused) return;
+      const canvas = document.createElement("canvas");
+
+      const cropWidth = video.videoWidth * 0.5;
+      const cropHeight = video.videoHeight * 0.98;
+
+      // 设置canvas尺寸为选择区域大小
+      canvas.width = cropWidth;
+      canvas.height = cropHeight;
+
+      const x = (video.videoWidth - cropWidth) / 2;
+      const y = (video.videoHeight - cropHeight) / 2;
+
+      const ctx = canvas.getContext("2d");
+      //中心60%区域
+      ctx.drawImage(
+        video,
+        x,
+        y,
+        cropWidth,
+        cropHeight,
+        0,
+        0,
+        cropWidth,
+        cropHeight
+      );
+      // 获取背景图片元素
+      const backgroundImage = document.getElementById("background-image");
+      console.log("背景图片", backgroundImage.complete);
+      // 等待背景图片加载完成
+      if (!backgroundImage.complete) {
+        backgroundImage.onload = () => {
+          // 绘制背景图片
+          ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
+        };
+      } else {
+        // 绘制背景图片
+        ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
+      }
+      // canvas.width = video.videoWidth;
+      // canvas.height = video.videoHeight;
+      // const ctx = canvas.getContext("2d");
+      // ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+      try {
+        const base64Data = canvas.toDataURL("image/png");
+        // console.log("上传数据:Base64", base64Data);
+        // 替换YOUR_SERVER_URL为你的实际服务器地址
+        const response = await fetch(
+          "/yapi/System/Person/GetPersonByFaceBase64",
+          {
+            method: "POST",
+            headers: {
+              token: localStorage.getItem("token"),
+              "Content-Type": "application/json",
+            },
+            body: JSON.stringify({
+              data: base64Data,
+              fileModule: 2,
+            }),
+          }
+        );
+        const result = await response.json();
+        // console.log("上传反馈", response, result);
+        if (result.code === 20000) {
+          // console.log("服务器返回数据:", result);
+          this.$emit("output", result.data);
+          this.uploadImage();
+          // setTimeout(() => {
+          //   this.stopCamera();
+          // }, 200);
+        }
+      } catch (err) {
+        console.error("上传过程中出错:", err);
+      }
+    },
+    async uploadImage() {
+      const video = this.$refs.video;
+      const canvas = document.createElement("canvas");
+
+      const cropWidth = video.videoWidth * 0.5;
+      const cropHeight = video.videoHeight * 0.98;
+
+      // 设置canvas尺寸为选择区域大小
+      canvas.width = cropWidth;
+      canvas.height = cropHeight;
+
+      const x = (video.videoWidth - cropWidth) / 2;
+      const y = (video.videoHeight - cropHeight) / 2;
+
+      const ctx = canvas.getContext("2d");
+      //中心60%区域
+      ctx.drawImage(
+        video,
+        x,
+        y,
+        cropWidth,
+        cropHeight,
+        0,
+        0,
+        cropWidth,
+        cropHeight
+      );
+
+      // 获取背景图片元素
+      const backgroundImage = document.getElementById("background-image");
+      console.log("背景图片", backgroundImage.complete);
+      // 等待背景图片加载完成
+      if (!backgroundImage.complete) {
+        backgroundImage.onload = () => {
+          // 绘制背景图片
+          ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
+        };
+      } else {
+        // 绘制背景图片
+        ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
+      }
+      // ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+      const timestampDiv = document.getElementById("timestamp");
+      // 在canvas上绘制时间戳
+      ctx.font = "24px Arial";
+      ctx.fillStyle = "white";
+      ctx.fillText(timestampDiv.textContent, 20, canvas.height - 20);
+      try {
+        const base64Data = canvas.toDataURL("image/png");
+        // console.log("上传数据:Base64", base64Data);
+        // 替换YOUR_SERVER_URL为你的实际服务器地址
+        const response = await fetch("/yapi/File/UploadBase64", {
+          method: "POST",
+          headers: {
+            token: localStorage.getItem("token"),
+            "Content-Type": "application/json",
+          },
+          body: JSON.stringify({
+            data: base64Data,
+            fileModule: 1,
+          }),
+        });
+        const result = await response.json();
+        // console.log("上传反馈", response, result);
+        if (result.code === 20000) {
+          console.log("服务器上传返回数据:", result);
+          this.$emit("outputPath", result.data);
+          setTimeout(() => {
+            this.stopCamera();
+          }, 200);
+        }
+        this.isUpload = false;
+      } catch (err) {
+        console.error("上传过程中出错:", err);
+      }
+    },
+  },
+  created() {
+    this.startCamera();
+    this.refreshTime();
+  },
+  beforeDestroy() {
+    this.stopCamera();
+  },
+};
+</script>
+
+<style scoped>
+.videoContainer {
+  position: relative;
+  width: 50%; /* 容器宽度设为50% */
+  height: 100%; /* 容器高度设为100% */
+  margin: 0 auto; /* 居中显示 */
+  overflow: hidden; /* 隐藏溢出部分 */
+}
+
+#background-image {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1;
+}
+video {
+  height: 800px;
+  width: 1280px;
+  transform: translate(-25%, 0%);
+  /* width: 100%;
+  max-width: 500px; */
+  background: #000;
+  z-index: 0;
+}
+#timestamp {
+  position: absolute;
+  bottom: 20px;
+  left: 20px;
+  color: white;
+  font-size: 24px;
+  background-color: rgba(0, 0, 0, 0.5);
+  padding: 5px;
+}
+</style>

+ 332 - 0
src/views/recognize/components/VideoLocalWindowTimestamp.vue

@@ -0,0 +1,332 @@
+<template>
+  <div class="videoContainer">
+    <video ref="video" autoplay muted playsinline></video>
+    <div id="timestamp" v-show="false">
+      {{ datetimeData.date + " " + datetimeData.time }}
+    </div>
+    <!-- <el-button @click="startCamera">开启摄像头</el-button>
+    <el-button @click="stopCamera" :disabled="!isCameraActive"
+      >关闭摄像头</el-button
+    > -->
+  </div>
+</template>
+
+
+<script>
+import { GetPersonByFace, GetPersonByFaceBase64 } from "@/API/custom";
+import Dayjs from "dayjs";
+
+export default {
+  props: {
+    processStep: {
+      type: String,
+      require: true,
+    },
+    isUpload: {
+      type: String,
+      require: false,
+    },
+    cameraData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  watch: {
+    processStep: {
+      handler(step) {
+        if (step == 0 || step == 9) {
+          this.startCamera();
+        } else {
+          this.stopCamera();
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
+    isUpload: {
+      handler(val) {
+        console.log("上传子级开关", val);
+        if (val) {
+          this.uploadImage();
+        } else {
+          this.isUpload = false;
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
+  },
+  data() {
+    return {
+      isCameraActive: false,
+      stream: null,
+      captureInterval: null, // 新增定时器变量
+      datetimeData: {
+        date: Dayjs(new Date()).format("YYYY-MM-DD"),
+        time: Dayjs(new Date()).format("HH:mm:ss"),
+        week: Dayjs(new Date()).format("dddd"),
+      },
+    };
+  },
+  methods: {
+    refreshTime() {
+      let speed = 1000;
+      let timer = null;
+      let theNowTime = () => {
+        // this.datePersonData.time = Dayjs(new Date()).format('HH:mm:ss')
+        this.datetimeData.time = Dayjs(new Date()).format("HH:mm:ss");
+        this.datetimeData.date = Dayjs(new Date()).format("YYYY-MM-DD");
+        this.datetimeData.week = Dayjs(new Date()).format("dddd");
+      };
+      timer = setInterval(theNowTime, speed);
+      this.$once("hook:beforeDestroy", () => {
+        clearInterval(timer);
+        timer = null;
+      });
+    },
+    async startCamera() {
+      try {
+        const stream = await navigator.mediaDevices.getUserMedia({
+          video: {
+            width: { ideal: 1280 },
+            height: { ideal: 800 },
+            facingMode: "user",
+          },
+        });
+        this.stream = stream;
+        this.isCameraActive = true;
+        this.$refs.video.srcObject = stream;
+
+        if (this.isUpload) {
+          setTimeout(() => {
+            this.uploadImage();
+          }, 1000);
+        } else {
+          // 新增定时截图功能
+          this.captureInterval = setInterval(() => {
+            this.captureImage();
+            console.log("截图上传");
+          }, 2500);
+        }
+      } catch (err) {
+        console.error("摄像头访问错误:", err);
+        alert(`摄像头访问失败: ${err.message}`);
+      }
+    },
+    stopCamera() {
+      if (this.stream) {
+        this.stream.getTracks().forEach((track) => track.stop());
+        this.isCameraActive = false;
+        this.$refs.video.srcObject = null;
+        this.stream = null;
+
+        // 停止定时器
+        if (this.captureInterval) {
+          clearInterval(this.captureInterval);
+          this.captureInterval = null;
+        }
+      }
+    },
+    // 新增截图方法
+    // async captureImage() {
+    //   const video = this.$refs.video;
+    //   video.currentTime = 1;
+    //   const canvas = document.createElement("canvas");
+    //   canvas.width = video.videoWidth;
+    //   canvas.height = video.videoHeight;
+    //   canvas
+    //     .getContext("2d")
+    //     .drawImage(video, 0, 0, canvas.width, canvas.height);
+    //   // 下载到本地
+    //   // const link = document.createElement("a");
+    //   // link.download = `capture_${new Date()
+    //   //   .toISOString()
+    //   //   .replace(/[:.]/g, "-")}.png`;
+    //   // link.href = canvas.toDataURL("image/png");
+    //   // link.click();
+
+    //   // 将canvas转为Blob对象
+    //   canvas.toBlob(async (blob) => {
+    //     if (blob) {
+    //       console.log("blob数据", blob);
+    //       const formData = new FormData();
+    //       formData.append("timestamp", new Date().toISOString());
+    //       formData.append("file", blob, "screenshot.png");
+    //       console.log("上传数据", formData);
+    //       try {
+    //         // const res = await GetPersonByFace(formData);
+    //         // console.log("上传反馈", res);
+    //         // const response = await fetch("System/Person/GetPersonByFace", {
+    //         //   method: "POST",
+    //         //   body: formData,
+    //         // });
+    //       } catch (err) {
+    //         console.error("上传过程中出错:", err);
+    //       }
+    //     }
+    //   }, "image/png");
+    // },
+
+    async captureImage() {
+      const video = this.$refs.video;
+      if (video.paused) return;
+      const canvas = document.createElement("canvas");
+
+      const cropWidth = video.videoWidth * 0.5;
+      const cropHeight = video.videoHeight * 0.98;
+
+      // 设置canvas尺寸为选择区域大小
+      canvas.width = cropWidth;
+      canvas.height = cropHeight;
+
+      const x = (video.videoWidth - cropWidth) / 2;
+      const y = (video.videoHeight - cropHeight) / 2;
+
+      const ctx = canvas.getContext("2d");
+      //中心60%区域
+      ctx.drawImage(
+        video,
+        x,
+        y,
+        cropWidth,
+        cropHeight,
+        0,
+        0,
+        cropWidth,
+        cropHeight
+      );
+      // canvas.width = video.videoWidth;
+      // canvas.height = video.videoHeight;
+      // const ctx = canvas.getContext("2d");
+      // ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+      try {
+        const base64Data = canvas.toDataURL("image/png");
+        // console.log("上传数据:Base64", base64Data);
+        // 替换YOUR_SERVER_URL为你的实际服务器地址
+        const response = await fetch(
+          "/yapi/System/Person/GetPersonByFaceBase64",
+          {
+            method: "POST",
+            headers: {
+              token: localStorage.getItem("token"),
+              "Content-Type": "application/json",
+            },
+            body: JSON.stringify({
+              data: base64Data,
+              fileModule: 2,
+            }),
+          }
+        );
+        const result = await response.json();
+        // console.log("上传反馈", response, result);
+        if (result.code === 20000) {
+          // console.log("服务器返回数据:", result);
+          this.$emit("output", result.data);
+          this.uploadImage();
+          // setTimeout(() => {
+          //   this.stopCamera();
+          // }, 200);
+        }
+      } catch (err) {
+        console.error("上传过程中出错:", err);
+      }
+    },
+    async uploadImage() {
+      const video = this.$refs.video;
+      const canvas = document.createElement("canvas");
+
+      const cropWidth = video.videoWidth * 0.5;
+      const cropHeight = video.videoHeight * 0.98;
+
+      // 设置canvas尺寸为选择区域大小
+      canvas.width = cropWidth;
+      canvas.height = cropHeight;
+
+      const x = (video.videoWidth - cropWidth) / 2;
+      const y = (video.videoHeight - cropHeight) / 2;
+
+      const ctx = canvas.getContext("2d");
+      //中心60%区域
+      ctx.drawImage(
+        video,
+        x,
+        y,
+        cropWidth,
+        cropHeight,
+        0,
+        0,
+        cropWidth,
+        cropHeight
+      );
+      // ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+      const timestampDiv = document.getElementById("timestamp");
+      // 在canvas上绘制时间戳
+      ctx.font = "24px Arial";
+      ctx.fillStyle = "white";
+      ctx.fillText(timestampDiv.textContent, 20, canvas.height - 20);
+      try {
+        const base64Data = canvas.toDataURL("image/png");
+        // console.log("上传数据:Base64", base64Data);
+        // 替换YOUR_SERVER_URL为你的实际服务器地址
+        const response = await fetch("/yapi/File/UploadBase64", {
+          method: "POST",
+          headers: {
+            token: localStorage.getItem("token"),
+            "Content-Type": "application/json",
+          },
+          body: JSON.stringify({
+            data: base64Data,
+            fileModule: 1,
+          }),
+        });
+        const result = await response.json();
+        // console.log("上传反馈", response, result);
+        if (result.code === 20000) {
+          console.log("服务器上传返回数据:", result);
+          this.$emit("outputPath", result.data);
+          setTimeout(() => {
+            this.stopCamera();
+          }, 200);
+        }
+        this.isUpload = false;
+      } catch (err) {
+        console.error("上传过程中出错:", err);
+      }
+    },
+  },
+  created() {
+    this.startCamera();
+    this.refreshTime();
+  },
+  beforeDestroy() {
+    this.stopCamera();
+  },
+};
+</script>
+
+<style scoped>
+.videoContainer {
+  position: relative;
+  width: 50%; /* 容器宽度设为50% */
+  height: 100%; /* 容器高度设为100% */
+  margin: 0 auto; /* 居中显示 */
+  overflow: hidden; /* 隐藏溢出部分 */
+}
+video {
+  height: 800px;
+  width: 1280px;
+  transform: translate(-25%, 0%);
+  /* width: 100%;
+  max-width: 500px; */
+  background: #000;
+}
+#timestamp {
+  position: absolute;
+  bottom: 20px;
+  left: 20px;
+  color: white;
+  font-size: 24px;
+  background-color: rgba(0, 0, 0, 0.5);
+  padding: 5px;
+}
+</style>

+ 57 - 4
src/views/recognize/index.vue

@@ -576,6 +576,7 @@ import {
   personComingTagUpdate,
   RefreshDriver,
   bindAndUnbindrecord,
+  GetPersonList,
 } from "@/API/custom";
 import Dayjs from "dayjs";
 
@@ -595,6 +596,9 @@ export default {
       idEnable: false, //身份证刷卡
       uploadPic: false, //上传录入
       recognizeSuccess: false, //识别成功
+      finallySet: false, //最终修改
+      nameEnbale: false, //最终修改
+      tempList: [],
       fullLoading: null,
       rfid: "",
       idCardData: {},
@@ -679,10 +683,10 @@ export default {
       ],
       infomation: {
         id: "",
-        name: "",
-        company: "",
+        name: "112",
+        company: "333",
         idNumber: "",
-        telephone: "",
+        telephone: "444",
         cause: "",
         label: "",
         areasEnable: [2],
@@ -872,6 +876,26 @@ export default {
       deep: true,
       immediate: true,
     },
+    nameEnbale: {
+      handler(newVal) {
+        console.log("重名检测", newVal);
+        if (newVal) {
+          this.$alert("系统有同名人员,请加区分前缀、后缀修改名称", "提示", {
+            confirmButtonText: "返回修改",
+            callback: () => {
+              if (this.processStep.num == 2 || this.processStep.num == 11) {
+                this.processStep.num--;
+              } else if (this.processStep.num == 14) {
+                this.processStep.num = this.processStep.num - 4;
+                this.infomation.persons.pop();
+              }
+            },
+          });
+        }
+      },
+      deep: true,
+      immediate: true,
+    },
   },
   mounted() {
     this.init();
@@ -957,6 +981,28 @@ export default {
         console.log(err);
       }
     },
+    async getPersonList(person) {
+      try {
+        const params = {
+          // pageSize: 10000,
+          // pageIndex: 1,
+          name: person.name, //登记时间
+        };
+        this.nameEnbale = false;
+        const res = await GetPersonList(params);
+        this.tempList = res.data.content;
+        console.log("名称信息", params);
+        for (let i = 0; i < this.tempList.length; i++) {
+          const p = this.tempList[i];
+          if (p.name === params.name) {
+            this.nameEnbale = true;
+          }
+        }
+        // console.log("名称检索数据表", this.tempList);
+      } catch (err) {
+        console.log(err);
+      }
+    },
     openFullScreen() {
       const loading = this.$loading({
         lock: true,
@@ -996,6 +1042,9 @@ export default {
             break;
           case 1:
             console.log("人员登记信息流程");
+            if (this.processStep.num === 1 && this.infomation.id === "") {
+              this.getPersonList({ name: this.infomation.name });
+            }
             if (this.personType == 1 && this.processStep.num === 2) {
               this.processStep.num += 3;
             } else {
@@ -1039,13 +1088,15 @@ export default {
             break;
           case 9:
             if (this.processStep.num === 10) {
+              if (this.person.id === "") {
+                this.getPersonList({ name: this.person.name }); //人员重名检测
+              }
               this.person.department = this.infomation.department;
               this.processStep.num += 4;
               this.infomation.persons.push(this.person);
             }
             console.log("同行人员信息");
             break;
-
           default:
             break;
         }
@@ -1073,6 +1124,7 @@ export default {
         this.infomation.persons = this.infomation.persons.filter((e) => {
           return e.name !== data.name;
         });
+        this.person.id = "";
         this.tableConfig.tableData = this.infomation.persons;
         console.log("删除", data, this.infomation.persons);
         this.refresh();
@@ -1088,6 +1140,7 @@ export default {
       this.infomation.personEnable = 1;
       this.person = {
         //初始化
+        id: "",
         name: "",
         company: this.infomation.company,
         idNumber: "",

+ 149 - 0
src/views/test/index.vue

@@ -0,0 +1,149 @@
+<template>
+  <div>
+    <div v-if="loading">正在加载摄像头,请授予访问权限...</div>
+    <div v-else-if="error">{{ error }}</div>
+    <div v-else id="video-container">
+      <video id="video" :width="640" :height="480" autoplay></video>
+      <canvas
+        id="blurred-canvas"
+        :width="640"
+        :height="480"
+        v-if="showBlurred"
+      ></canvas>
+    </div>
+    <div>
+      <button id="capture-btn" @click="capture" :disabled="!video">截图</button>
+      <!-- 新增虚化背景按钮 -->
+      <button id="blur-btn" @click="toggleBlurred" :disabled="!video">
+        {{ showBlurred ? "隐藏虚化" : "显示虚化" }}
+      </button>
+    </div>
+    <div id="snapshot-container">
+      <canvas id="canvas" :width="640" :height="480"></canvas>
+    </div>
+  </div>
+</template>
+
+<script>
+/* eslint-disable */
+import * as tf from "@tensorflow/tfjs";
+import * as bodyPix from "@tensorflow-models/body-pix";
+
+export default {
+  name: "CameraCapture",
+  data() {
+    return {
+      video: null,
+      canvas: null,
+      captureBtn: null,
+      blurredCanvas: null,
+      ctx: null,
+      net: null,
+      // 新增状态,用于控制虚化画面的显示
+      showBlurred: false,
+      // 新增加载状态
+      loading: true,
+      // 新增错误状态
+      error: null,
+      stream: null
+    };
+  },
+  mounted() {
+    this.initializeCamera();
+  },
+  beforeDestroy() {
+    // 组件卸载时释放摄像头资源
+    if (this.stream) {
+      this.stream.getTracks().forEach(track => track.stop());
+    }
+  },
+  methods: {
+    async initializeCamera() {
+      try {
+        this.stream = await navigator.mediaDevices.getUserMedia({ video: true });
+        this.video = document.getElementById("video");
+        this.video.srcObject = this.stream;
+        this.loading = false;
+        // 加载 BodyPix 模型
+        this.loadBodyPix();
+      } catch (err) {
+        console.error("访问摄像头失败:", err);
+        if (err.name === 'NotAllowedError') {
+          this.error = "您拒绝了摄像头访问权限,请刷新页面并允许访问。";
+        } else if (err.name === 'NotFoundError') {
+          this.error = "未检测到摄像头设备,请检查设备连接。";
+        } else {
+          this.error = "无法访问摄像头,请检查权限或设备连接。";
+        }
+        this.loading = false;
+      }
+    },
+    async loadBodyPix() {
+      this.net = await bodyPix.load();
+      this.processVideoFrame();
+    },
+    async processVideoFrame() {
+      if (this.showBlurred) {
+        const segmentation = await this.net.segmentPerson(this.video);
+        const backgroundBlurAmount = 5;
+        const edgeBlurAmount = 3;
+        const flipHorizontal = false;
+
+        bodyPix.drawBokehEffect(
+          this.blurredCanvas,
+          this.video,
+          segmentation,
+          backgroundBlurAmount,
+          edgeBlurAmount,
+          flipHorizontal
+        );
+      }
+
+      requestAnimationFrame(() => this.processVideoFrame());
+    },
+    capture() {
+      const ctx = this.canvas.getContext("2d");
+      ctx.drawImage(
+        this.showBlurred ? this.blurredCanvas : this.video,
+        0,
+        0,
+        this.canvas.width,
+        this.canvas.height
+      );
+
+      // 保存截图
+      const link = document.createElement("a");
+      link.download = "snapshot.png";
+      link.href = this.canvas.toDataURL();
+      link.click();
+    },
+    // 新增方法,用于切换虚化画面的显示状态
+    toggleBlurred() {
+      this.showBlurred = !this.showBlurred;
+    },
+  },
+};
+</script>
+
+<style scoped>
+#video-container {
+  margin-bottom: 20px;
+}
+#snapshot-container {
+  margin-top: 20px;
+}
+#capture-btn,
+#blur-btn {
+  /* 按钮背景虚化 */
+  backdrop-filter: blur(8px);
+  background-color: rgba(255, 255, 255, 0.5);
+  border: none;
+  padding: 10px 20px;
+  border-radius: 5px;
+  cursor: pointer;
+  margin-right: 10px;
+}
+#blurred-video {
+  display: none;
+}
+</style>

+ 4 - 4
vue.config.js

@@ -17,9 +17,9 @@ module.exports = {
     },
     proxy: {
       '/yapi': {
-        target: 'https://10.0.0.201:8080/prod-api/', // 托克托现场
+        // target: 'https://10.0.0.201:8080/prod-api/', // 托克托现场
         // target: 'http://172.168.0.62:8080/prod-api/', // 托克托现场
-        // target: 'http://192.168.195.136:8080/prod-api/', // 托克托本地虚拟机
+        target: 'https://192.168.195.136:8080/prod-api/', // 托克托本地虚拟机
         // target: "http://localhost:5002", // 远程服务器-赵哥
         changeOrigin: true,
         pathRewrite: {
@@ -27,9 +27,9 @@ module.exports = {
         },
       },
       '/model': {
-        target: 'https://10.0.0.201:8081/model/', //托克托模型现场服务器
+        // target: 'https://10.0.0.201:8081/model/', //托克托模型现场服务器
         // target: 'http://172.168.0.62:8081/model/', //托克托模型现场服务器
-        // target: 'http://192.168.195.136:8081/model/', //托克托模型虚拟机
+        target: 'https://192.168.195.136:8081/model/', //托克托模型虚拟机
         changeOrigin: true,
         pathRewrite: {
           '^/model': '/',