vue调用腾讯人脸组件封装+接口请求后端调用

调用封装的组件

1.1 调用组件
复制代码
   <template>
     <div>
       <FaceRecognitionPanel :launch-source="launchSource" @validated="handleValidated" />
     </div>
   </template>
1.2 js里面的函数(具体的业务逻辑参考自己的实际调整)
复制代码
   <script>
   import { Toast } from "mand-mobile";
   import { apiRequest } from "@/utils/apiList";
   import FaceRecognitionPanel from "@/components/FaceRecognitionPanel/index.vue";
   import CommSteps from "@/components/CommSteps/index.vue";

   const NODE_FDD = "node6";
   const UNVS_IMG_CTLG_NO = "202412240003";
   const CERT_TYPE = "B0101";

   export default {
     name: "FaceRecognition",
     components: {
       FaceRecognitionPanel,
       CommSteps
     },
     computed: {
       launchSource() {
         const route = this.$route || {};
         return route.path || "";
       }
     },
     data() {
       return {

       };
     },
     methods: {
       handleValidated({ verifyResult }) {
         const userInfo = this.$store.state.userInfo || {};
       },
       ordfacercg(orderNo, verifyResult, node) {
         const userInfo = this.$store.state.userInfo || {};
         apiRequest(
           "ordfacercg",
           {
             ordNo: orderNo,
             nodeCode: node,
             participantType: "001",
             ossid: verifyResult,
             unvsImgCtlgNo1: UNVS_IMG_CTLG_NO,
             certNo: userInfo.faceId,
             certTyp: CERT_TYPE
           },
           {
             headers: {},
             onSuccess: () => {
               this.$router.replace("/fdd_xfd/perAuth");
               return;
             },
             onError: () => {
               Toast.info("人脸识别失败");
             }
           }
         ).catch(error => {
           if (error && error._handled === true) {
             return;
           }
         });
       }
     }
   };
   </script>

FaceRecognitionPanel 组件封装

复制代码
   <template>
     <div class="face-recognition-page">
       <div class="face-recognition-body">
         <div class="face-recognition-content">
           <TipBox :text="tipText"></TipBox>

           <div class="face-guide-container">
             <div class="face-guide-circle">
               <div class="face-pattern">
                 <img :src="facePic" alt="人脸识别" />
               </div>
             </div>
           </div>

           <div class="tips-list">
             <div v-for="(item, index) in displayTips" :key="index" class="tip-item">
               <div class="tip-icon-wrapper">
                 <img v-if="item.icon" :src="item.icon" :alt="item.text" class="tip-icon-img" />
               </div>
               <span class="tip-item-text">{{ item.text }}</span>
             </div>
           </div>
         </div>

         <div class="action-section">
           <button class="start-btn" type="button" @click="handleStartRecognition">{{ startText }}</button>
         </div>
       </div>

       <FaceRecognitionFlow ref="faceFlow" :launch-source="launchSource" @validated="onFlowValidated" />

       <ContentDialog :visible.sync="showTextDialog" title="温馨提示" :show-cancel="true" cancel-text="取消" confirm-text="去识别"
         @cancel="handleTestCancel" @confirm="handleTestConfirm">
         <input v-model="testInfo.name" type="text" placeholder="请输入姓名" class="form-input" />
         <input v-model="testInfo.id" type="text" placeholder="请输入身份证号" class="form-input" />
       </ContentDialog>
     </div>
   </template>

   <script>
   import ContentDialog from "@/components/ContentDialog/index.vue";
   import TipBox from "@/components/TipBox/index.vue";
   import FaceRecognitionFlow from "@/components/FaceRecognitionFlow/index.vue";
   import {
     isMiniProgramEnvironment
   } from "@/utils/miniProgram";
   import { Toast } from "mand-mobile";
   import { apiRequest } from "@/utils/apiList";

   const DEFAULT_TIP_TEXT = "人脸识别功能验证您的身份信息,请确保为本人操作";
   const DEFAULT_START_TEXT = "开始识别";

   export default {
     name: "FaceRecognitionPanel",
     components: {
       TipBox,
       FaceRecognitionFlow,
       ContentDialog
     },
     props: {
       tipText: {
         type: String,
         default: DEFAULT_TIP_TEXT
       },
       startText: {
         type: String,
         default: DEFAULT_START_TEXT
       },
       launchSource: {
         type: String,
         default: ""
       },
       /**
        * { icon: string (require 或 url), text: string }
        * 不传则使用 RCD 默认三图标Tips
        */
       tips: {
         type: Array,
         default: null
       },
       // 腾讯慧眼-控制配置的业务场景编号--默认通用场景 2
       ruleId: {
         type: String,
         default: "2"
       }
     },
     data() {
       return {
         showTextDialog: false, // 测试需要填写测试人员真实信息测试人脸
         testInfo: {
           name: "", // 测试人员姓名
           id: "" // 测试人员身份证
         },
         facePic: require("@/assets/face-pattern.png"),
         defaultTips: [
           { icon: require("@/assets/light.png"), text: "避免强弱光" },
           { icon: require("@/assets/face-cover.png"), text: "面部无遮挡" },
           { icon: require("@/assets/phone-stable.png"), text: "手机保持稳定" }
         ]
       };
     },
     computed: {
       displayTips() {
         return Array.isArray(this.tips) && this.tips.length ? this.tips : this.defaultTips;
       }
     },
     mounted() {
     },
     methods: {
       handleLoginTimeoutConfirm() {
         this.showForceDialog = false;
       },
       handleStartRecognition() {
         const userInfo = (this.$store && this.$store.state && this.$store.state.userInfo) || {};
         const name = userInfo.faceName || "";
         const id = userInfo.faceId || "";
         console.log(`\n`);
         console.log("-------");
         console.log("人脸识别,客户信息校验")
         console.log(`姓\u3000名:${name}`)
         console.log(`证件号:${id}`)
         console.log("-------");
         console.log(`\n`);
         //测试-开发环境和测试环境
         if (window.location.host == "testopen.shrbank.com:121" || window.location.host == "localhost:8091") { 
           this.showTextDialog = true;
         } else {
           // 生产环境,通过真实保存的数据发送接口
           const inMiniProgram = isMiniProgramEnvironment();
           if (!inMiniProgram) {
             Toast.info("请前往微信小程序打开");
             return
           }
           this.startFun(name, id);
         }
       },
       startFun(name, idCard) {
         // 兼容测试原人脸识别
         // if (!name && !idCard) {
         //   if (this.$refs.faceFlow && typeof this.$refs.faceFlow.start === "function") {
         //     this.$emit("start");
         //     this.$refs.faceFlow.start();
         //   }
         //   return
         // }
         // this.ruleId || "腾讯慧眼控制台配置的ruleId";
         // 发送接口获取bizToken
         // 必传数据 ruleId 业务编号, IdCard 身份证号, Name 姓名
         apiRequest(
           "tencentFaceAuth",
           {
             name,
             idCard,
             ruleId: this.ruleId
           },
           {
             onSuccess: res => {
               const bizToken = (res.data && res.data.bizToken) || "";
               if (bizToken) {
                 if (this.$refs.faceFlow && typeof this.$refs.faceFlow.start === "function") {
                   this.$emit("start");
                   this.$refs.faceFlow.start(bizToken, this.ruleId);
                 }
               } else {
                 Toast.info("人脸SDK初始化失败,请重新获取");
               }
             },
             onError: error => {
               console.error("人脸获取bizToken失败:", error);
               Toast.info("人脸SDK初始化失败");
             }
           }
         ).catch(error => {
           console.error("人脸获取bizToken异常:", error);
           Toast.info("人脸SDK初始化异常");
           if (error && error._handled === true) {
             return;
           }
         });
       },
       onFlowValidated(payload) {
         this.$emit("validated", payload);
       },
       // 测试弹窗 - 取消
       handleTestCancel() {
         this.showTextDialog = false;
       },
       // 测试弹窗 - 确定
       handleTestConfirm() {
         console.log(this.testInfo)
         // 通过输入的数据 发送接口 验证人脸
         this.startFun(this.testInfo.name, this.testInfo.id);
       },
     }
   };
   </script>

   <style lang="postcss" scoped>

   /** 
   .face-recognition-page {
     background-color: #fff;
     padding-top: 44px;
     height: inherit;
   }
   */

    .face-recognition-page {
     width: 100%;
     background-color: transparent;
     margin-top: 44px;
     box-sizing: border-box;
     height: calc(100vh - 44px);
     display: flex;
     flex-direction: column;
   }

   .face-recognition-body {
     width: 100%;
     margin-top: 8px;
     background: #fff;
     box-sizing: border-box;
     height: calc(100% - 8px);
     display: flex;
     flex-direction: column;
   }

   .face-recognition-content {
     padding: 0 16px;
     flex: 1;
     overflow-y: auto;
   }

   .face-guide-container {
     display: flex;
     justify-content: center;
     align-items: center;
     margin: 40px 0;
     padding: 20px 0;
   }

   .face-guide-circle {
     position: relative;
     width: 280px;
     height: 280px;
     display: flex;
     justify-content: center;
     align-items: center;
   }

   .face-pattern {
     position: relative;
     width: 240px;
     height: 246px;
     display: flex;
     justify-content: center;
     align-items: center;
     z-index: 5;
   }

   .face-pattern img {
     width: 100%;
     height: 100%;
     object-fit: contain;
   }

   .tips-list {
     display: flex;
     justify-content: space-around;
     margin: 40px 0 60px;
     padding: 0 20px;
   }

   .tip-item {
     display: flex;
     flex-direction: column;
     align-items: center;
     flex: 1;
   }

   .tip-icon-wrapper {
     width: 32px;
     height: 32px;
     display: flex;
     justify-content: center;
     align-items: center;
     margin-bottom: 8px;
   }

   .tip-icon-img {
     width: 100%;
     height: 100%;
     object-fit: contain;
   }

   .tip-item-text {
     font-size: 12px;
     color: #666;
     text-align: center;
   }

   .action-section {
     background: #FFFFFF;
     box-shadow: 0px -1px 1px 1px rgba(0,0,0,0.03);
     padding: 0 16px;
     display: flex;
     align-items: center;
     padding-top: 16px;
     padding-bottom: calc(16px + env(safe-area-inset-bottom));
     min-height: 81px;
     box-sizing: border-box;
   } 

   .start-btn {
     width: 100%;
     height: 48px;
     background: rgba(225, 183, 126, 1);
     border-radius: 24px;
     font-size: 16px;
     font-weight: 500;
     color: #fff;
     border: none;
     outline: none;
     cursor: pointer;
     -webkit-tap-highlight-color: transparent;
   }

   .start-btn:active {
     opacity: 0.8;
   }
   </style>

FaceRecognitionPanel组件里面引用的 FaceRecognitionFlow 组件封装

复制代码
   <!--
     FaceRecognitionFlow
     复用逻辑:开始小程序人脸识别 -> hash 回传 -> validateficeresult -> commit wxFace -> emit validated
   -->
   <template>
     <!-- 纯流程组件,不展示 UI -->
     <div style="display: none" />
   </template>

   <script>
   /* global wx */
   import { Toast } from "mand-mobile";
   import { apiRequest } from "@/utils/apiList";

   const HASH_FACE_SUCCESS = "face_success";
   const FACE_RECOGNIZE_ENTRY_URL = "../home/txFaceRecognize/txFaceRecognize";
   /** H5 部署 host 前缀(与 window.location.href 一致,用于解析 env) */
   const FACE_LAUNCH_HREF_HOST_PREFIX = "https://testopen.shrbank.com:121";
   /** 静态资源路径片段,env 为此前缀之后、该片段之前的路径节 */
   const FACE_LAUNCH_CCS_MIN_MARKER = "/ccs-min/";

   function getFaceMiniProgramEnvFromLocation() {
     const href = String(window.location.href || "");
     if (!href.includes(FACE_LAUNCH_HREF_HOST_PREFIX) || !href.includes(FACE_LAUNCH_CCS_MIN_MARKER)) {
       return "";
     }
     const afterHost = href.split(FACE_LAUNCH_HREF_HOST_PREFIX)[1] || "";
     return String((afterHost.split(FACE_LAUNCH_CCS_MIN_MARKER)[0] || "").trim());
   }

   export default {
     name: "FaceRecognitionFlow",
     props: {
       launchSource: {
         type: String,
         default: ""
       }
     },
     data() {
       return {
         hashChangeHandler: null,
         pending: false,
         handled: false,
         ruleId: "",
         bizToken: ""
       };
     },
     mounted() {
       this.hashChangeHandler = () => {
         this.handleHashChange();
       };
       window.addEventListener("hashchange", this.hashChangeHandler);
     },
     beforeDestroy() {
       if (this.hashChangeHandler) {
         window.removeEventListener("hashchange", this.hashChangeHandler);
       }
     },
     methods: {
       start(bizToken, ruleId) {
         console.log("测试一下接受的数据",bizToken)
         this.pending = true;
         this.handled = false;

         // 覆盖"回传参数已存在但未触发 hashchange"场景
         this.handleHashNow();

         this.ruleId = ruleId;
         this.bizToken = bizToken;
         this.gotoMini();
       },

       gotoMini() {
         // launchSource 在这里语义为"回调地址",透传给小程序用于回跳
         const callbackUrl = this.launchSource || "";
         const sourceQuery = encodeURIComponent(callbackUrl);
         const env = getFaceMiniProgramEnvFromLocation();
         const envQuery = encodeURIComponent(env);
         const userInfo = (this.$store && this.$store.state && this.$store.state.userInfo) || {};
         const name = userInfo.faceName || "";
         const id = userInfo.faceId || "";
         const BizToken = this.bizToken;
         console.log(
           "face launch",
           `${FACE_RECOGNIZE_ENTRY_URL}?bizToken=${BizToken}&name=${name}&id=${id}&callbackUrl=${sourceQuery}&env=${envQuery}`
         );
         wx.miniProgram.navigateTo({
           url: `${FACE_RECOGNIZE_ENTRY_URL}?bizToken=${BizToken}&name=${name}&id=${id}&callbackUrl=${sourceQuery}&env=${envQuery}`
         });
       },
       handleHashChange() {
         if (!this.pending || this.handled) return;
         const hash = String(window.location.hash || "");
         if (!hash.includes(HASH_FACE_SUCCESS)) return;
         this.handleHashNow();
       },

       handleHashNow() {
         if (!this.pending || this.handled) return;
         const hash = String(window.location.hash || "");
         if (!hash.includes(HASH_FACE_SUCCESS)) return;

         const href = decodeURIComponent(String(window.location.href || ""));
         const params = this.parseHashQuery(href);
         this.handled = true; // 先锁住,避免并发重复请求
         this.pending = false;
         if (this.bizToken) {
           this.validateFace(params);
         } else {
           this.validateFace1(params);
         }
       },

       parseHashQuery(href) {
         const url = decodeURIComponent(String(href || ""));
         const part = url.split("#")[1] || "";
         const queryString = part.includes("?") ? part.split("?")[1] : "";
         if (!queryString) return {};

         return queryString.split("&").reduce((acc, kv) => {
           const [k, v] = kv.split("=");
           if (k) acc[k] = v;
           return acc;
         }, {});
       },

       validateFace(params) {
         apiRequest(
           "tencentFaceCheck",
           {
             ruleId: this.ruleId,
             verifyResult: params && params.verifyResult
           },
           {
             headers: {},
             onSuccess: res => {
               const data = res && res.data ? res.data : {};
               // 复用原页面 commit 逻辑:只写 wxFace
               this.$store.commit("updateUserInfo", { wxFace: data });
               this.$emit("validated", { verifyResult: data.fileKey });
             },
             onError: () => {
               Toast.info("人脸识别失败");
             }
           }
         ).catch(err => {
           if (err && err._handled === true) return;
           Toast.info("人脸识别失败");
         });
       },

       // 废弃 人脸识别二次验证
       validateFace1(params) {
         apiRequest(
           "validateficeresult",
           {
             verifyResult: params && params.verifyResult
           },
           {
             headers: {},
             onSuccess: res => {
               const data = res && res.data ? res.data : {};
               // 复用原页面 commit 逻辑:只写 wxFace
               this.$store.commit("updateUserInfo", { wxFace: data });
               this.$emit("validated", { verifyResult: data.verifyResult });
             },
             onError: () => {
               Toast.info("人脸识别失败");
             }
           }
         ).catch(err => {
           if (err && err._handled === true) return;
           Toast.info("人脸识别失败");
         });
       }
     }
   };
   </script>

备注说明:apiRequest里面是调用的后端接口,具体调用后端接口的逻辑业务请参考实际进行
4.

接口说明

复制代码
   //传入用户信息
   tencentFaceAuth: {
       apiUrl: reqHeadUrl + "/api/tencent/face/tencentFaceAuth",
       method: "POST"
     },
   // 校验
   tencentFaceCheck: {

       apiUrl: reqHeadUrl + "/api/tencent/face/tencentFaceCheck",

       method: "POST"

     }
   // 人脸对比 验证接口
     validateficeresult: {

       apiUrl: reqHeadUrl  + "/api/wxcxc/validateficeresult",

       method: "POST"

     },

    //订单人脸识别
    ordfacercg:{
       apiUrl: reqHeadUrl + "/api/w/mcl/ord/ordaplyinf/ordfacercg",
       method: "POST"
     },

相关推荐
程序员爱钓鱼1 小时前
Rust 生命周期常见错误详解:看懂编译器报错并正确修复
前端·后端·rust
zhanghaha13141 小时前
Python进阶教程:5_XML 解析 —— 新手完全指南
java·前端·数据库
绝世唐门三哥1 小时前
CSS 虚线下划线用法指南:text-decoration 完整解析
前端·javascript·css
程序员爱钓鱼1 小时前
Go if 判断详解
前端·后端·go
程序员黑豆1 小时前
Java字符串拼接全解析:6种方式性能对比与实战指南
java·前端·ai编程
苏灿烤鱼1 小时前
14MB 模型,凭什么跟 270M 对打?
javascript·python·agent
万少6 小时前
DeepSeek 昨晚刚开源了 Harness:附万少的2 万字保姆级教程
前端·后端·架构
程序员黑豆8 小时前
Java字符串详解
java·前端·ai编程
无我Code9 小时前
开发者-2026年中总结
前端·面试·程序员