From 9a6cd220224fd3a9a6c84b5bb37c6410a470969f Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期二, 17 三月 2026 17:53:21 +0800
Subject: [PATCH] 考点核验

---
 src/views/h5/faceAuth/components/auditDialog.vue |   14 
 package-lock.json                                |   11 
 src/views/h5/login/bind.vue                      |  140 +++++++++
 src/views/main/components/UploadIdCard.vue       |    2 
 src/views/h5/login/index.vue                     |   21 
 src/utils/wxjssdk.js                             |  263 ----------------
 src/router/index.js                              |    5 
 src/views/h5/verify/form.vue                     |    5 
 src/views/h5/login/redirect.vue                  |  232 +++++++++++++++
 src/views/h5/verify/index.vue                    |   46 +-
 src/views/h5/signup/index.vue                    |   48 ++-
 src/views/h5/index.vue                           |   27 
 src/main.js                                      |    6 
 src/views/h5/faceAuth/index.vue                  |   29 -
 package.json                                     |    1 
 vite.config.js                                   |    1 
 src/router/h5/router.js                          |   20 +
 src/App.vue                                      |   21 +
 src/views/h5/faceAuth/components/camera.vue      |    2 
 19 files changed, 530 insertions(+), 364 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 106b5fd..80f3b71 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
 {
-  "name": "app-web-examination-platform",
+  "name": "app-web-examination-user",
   "version": "0.0.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "app-web-examination-platform",
+      "name": "app-web-examination-user",
       "version": "0.0.0",
       "dependencies": {
         "@element-plus/icons-vue": "^2.3.2",
@@ -22,6 +22,7 @@
         "vconsole": "^3.15.1",
         "vue": "^3.5.22",
         "vue-router": "^4.6.3",
+        "weixin-js-sdk": "^1.6.5",
         "xlsx": "^0.18.5",
         "xlsx-js-style": "^1.2.0"
       },
@@ -5712,6 +5713,12 @@
       "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
       "license": "MIT"
     },
+    "node_modules/weixin-js-sdk": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmmirror.com/weixin-js-sdk/-/weixin-js-sdk-1.6.5.tgz",
+      "integrity": "sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==",
+      "license": "MIT"
+    },
     "node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 757c2a8..9ef02f3 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
     "vconsole": "^3.15.1",
     "vue": "^3.5.22",
     "vue-router": "^4.6.3",
+    "weixin-js-sdk": "^1.6.5",
     "xlsx": "^0.18.5",
     "xlsx-js-style": "^1.2.0"
   },
diff --git a/src/App.vue b/src/App.vue
index b2e0d57..a4fb8fd 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,10 +4,23 @@
   </div>
 </template>
 
-<script setup>
-// import { useWindowSize } from '@/utils/hook.js'
-
-// const { width, height } = useWindowSize()
+<script>
+import { isWeixin } from '@/utils/UA.js'
+import { getWxSignature } from '@/utils/wxjssdk.js'
+export default {
+  data() {
+    return {
+      
+    }
+  },
+  watch: {
+    '$route.path': function(){
+      if (isWeixin) {
+        getWxSignature(this.$route)
+      }
+    },
+  }
+}
 </script>
 
 <style scoped>
diff --git a/src/main.js b/src/main.js
index 13eec40..9f4cb15 100644
--- a/src/main.js
+++ b/src/main.js
@@ -29,9 +29,9 @@
   app.component(key, component)
 }
 
-// import('vconsole').then((module) => {
-//   new module.default()
-// })
+import('vconsole').then((module) => {
+  new module.default()
+})
 
 app.config.globalProperties.$rules = ruleGenerator
 app.config.globalProperties.$property = property
diff --git a/src/router/h5/router.js b/src/router/h5/router.js
index c8ebaa8..5c23388 100644
--- a/src/router/h5/router.js
+++ b/src/router/h5/router.js
@@ -20,11 +20,6 @@
         component: () => import('@/views/h5/verify/noAccess.vue'),
       },
       {
-        path: 'login',
-        name: '韬唤楠岃瘉鐧诲綍',
-        component: () => import('@/views/h5/login/index.vue'),
-      },
-      {
         path: 'signup',
         name: '绛惧埌',
         component: () => import('@/views/h5/signup/index.vue'),
@@ -36,5 +31,20 @@
       },
     ],
   },
+  {
+    path: '/h5/login',
+    name: '韬唤楠岃瘉鐧诲綍',
+    component: () => import('@/views/h5/login/index.vue'),
+  },
+  {
+    path: '/h5/redirect',
+    name: '閲嶅畾鍚�',
+    component: () => import('@/views/h5/login/redirect.vue'),
+  },
+  {
+    path: '/h5/bind',
+    name: '缁戝畾鎵嬫満鍙�',
+    component: () => import('@/views/h5/login/bind.vue'),
+  },
 ]
 export default router
diff --git a/src/router/index.js b/src/router/index.js
index 3b466f4..899675b 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -3,7 +3,6 @@
 import errorPage from '@/router/error/index.js'
 import mainPage from '@/router/main/index.js'
 import h5 from '@/router/h5/router.js'
-import { useLoginStore } from '@/stores/login.js'
 
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
@@ -11,7 +10,6 @@
 })
 
 router.beforeEach((to, from, next) => {
-  const { setLastRouteInfo } = useLoginStore()
   if (!to.matched.length) {
     if (to.path === '/') {
       next({ path: '/main/home' })
@@ -19,9 +17,6 @@
       next({ path: '/error/404', query: { errorUrl: to.path } })
     }
   } else {
-    if (from.name) {
-      setLastRouteInfo(from)
-    }
     next()
   }
 })
diff --git a/src/utils/wxjssdk.js b/src/utils/wxjssdk.js
index 083dcda..7312be2 100644
--- a/src/utils/wxjssdk.js
+++ b/src/utils/wxjssdk.js
@@ -1,16 +1,14 @@
 import wx from 'weixin-js-sdk'
 import axios from './axios'
-import store from '../store.js'
 import $qxueyou from '@/config/qxueyou.js'
-import utilsUA from '@/plugins/utilsUA'
-import { getUUID, qxyResImg } from '@/plugins/utils'
+import { isWeixin, isMobile } from '@/utils/UA.js'
 
 let newFeature = false
 let oldShare = ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone']
 let newShare = ['updateTimelineShareData', 'updateAppMessageShareData']
 
-let weixinFlag = utilsUA.isWeixin
-let mobileFlag = utilsUA.isMobile
+let weixinFlag = isWeixin
+let mobileFlag = isMobile
 let channel = weixinFlag && mobileFlag ? 'wx_pub' : 'wx_pub_qr'
 let isWxpub = 'wx_pub'.includes(channel)
 
@@ -21,7 +19,7 @@
 function getWxSignature(toRoute) { 
   if (!weixinFlag) { return false }
 
-  axios.get('/wx/js/signature', {
+  axios.get('/system/wx/js/signature', {
     params: { url: location.href } 
   }).then(res => {
     if (!res || !res.data) {
@@ -36,193 +34,19 @@
       signature: result.signature,
       jsApiList: [
         ...(newFeature ? newShare : oldShare),
-        'chooseWXPay',
+        // 'chooseWXPay',
         'chooseImage',
         'getLocalImgData'
       ],
       openTagList: ['wx-open-launch-app']
     })
-    wx.ready(function () {
-      initShareOption(toRoute)
-    })
+    // wx.ready(function () {
+    //   initShareOption(toRoute)
+    // })
     wx.error(function (res) {
       console.log(res)
     })
   })
-}
-
-/**
- * 鍒濆鍖栧垎浜暟鎹�
- * @param {*} toRoute 
- */
-function initShareOption(toRoute) {
-  if (!weixinFlag) { return false }
-
-  let uuid = getUUID()
-  let shareOptions = getShareOptions(uuid,toRoute)
-
-  shareOptions.success = function () {
-    shareSuccess(uuid,toRoute)
-  }
-  shareOptions.cancel = function () {
-    console.log('鍙栨秷鍒嗕韩')
-  }
-  shareOptions.trigger = function () {
-    console.log('鐢ㄦ埛鐐瑰嚮鍙戦�佺粰鏈嬪弸')
-  }
-
-  // 鍚戜笅鍏煎鏃х増鍒嗕韩鎺ュ彛
-  if (!newFeature) {
-    wx.onMenuShareTimeline(shareOptions)
-    wx.onMenuShareAppMessage(shareOptions)
-  } else {
-    wx.updateAppMessageShareData(shareOptions)
-    wx.updateTimelineShareData(shareOptions)
-  }
-}
-
-/**
- * 鑾峰彇鍒嗕韩鏁版嵁
- * @param {*} list 
- */
-function getShareOptions(uuid,toRoute) {
-  let result = {}
-  let customShare = store.state.share.custom
-  if (customShare.title) {
-    result.title = customShare.title
-    result.desc = customShare.desc
-  } else {
-    result.title = toRoute.name
-    result.desc = toRoute.name
-  }
-  if (customShare.imgUrl) {
-    if (customShare.imgUrl.includes('http')) {
-      result.imgUrl = customShare.imgUrl
-    } else {
-      result.imgUrl = qxyResImg(customShare.imgUrl)
-    }
-  }
-
-  if (customShare.uuidLink) {
-    let pageUrl = customShare.pageUrl || toRoute.fullPath
-    let encode = encodeURI(`${uuid},${$qxueyou.htmlRoot + pageUrl}`)
-    result.link = customShare.uuidLink + btoa(encode)
-    // result.link = customShare.uuidLink + uuid
-  } else if (customShare.link) {
-    result.link = customShare.link
-  } else {
-    result.link = location.href
-  }
-
-  return result
-}
-
-/**
- * 鍒嗕韩鎴愬姛鍥炶皟
- */
-function shareSuccess(uuid,toRoute) {
-  let customShare = store.state.share.custom
-  if (customShare.targetId) {
-    let pageUrl = customShare.pageUrl || toRoute.fullPath
-    axios.post('/wx/share/callback', {
-      urlId: uuid,
-      pageUrl: $qxueyou.htmlRoot + pageUrl,
-      targetId: customShare.targetId,
-      planIds: customShare.planIds
-    }).then(() => {
-      initShareOption(toRoute)
-    })
-  }
-
-  // 褰撳垎浜湁鏂规Id锛岃Е鍙戞槸鍚﹀叧娉ㄥ叕浼楀彿
-  if (customShare.planIds) {
-    store.commit('wxh5/subscribe', '鍙婃椂鑾峰彇濂栧姳鎻愰啋')
-  }
-
-  let mask = store.state.share.mask
-  if (mask.show) {
-    let text = mask.type === 'plan' ? '鍒嗕韩鎴愬姛锛岀户缁姪鍔�' : '閭�璇锋垚鍔燂紝缁х画閭�璇�'
-    let newMask = { show: true, type: mask.type, text: text, codeText: mask.codeText }
-    store.commit("share/maskText", newMask)
-  }
-}
-
-/**
- * 缁熶竴鏀粯鎺ュ彛澶勭悊
- * @param {*} orderId
- * @param {*} successCallback // 鏀粯鎴愬姛鍥炶皟
- * @param {*} showCodeCallback // 鏄剧ず浜岀淮鐮佸洖璋�
- */
-function unipayPay(orderId, successCallback, showCodeCallback) {
-  axios.post('/wx/pay/createOrder', {
-    orderId: orderId,
-    channel: channel,
-    redirectUrl: weixinFlag ? store.state.order.paySuccessUrl : undefined,
-  }).then(res => {
-    if (!res.data.data) { return false }
-    
-    let params = res.data.data.param
-
-    if (isWxpub) { // 鍏紬鍙锋敮浠�
-      chooseWXPay(params, successCallback)
-    } else { // 鎵爜鏀粯
-      store.commit('timer/paying', true)
-      showCodeCallback && showCodeCallback(params.codeUrl)
-      checkIsPay(orderId, successCallback)
-    }
-  })
-}
-
-function chooseWXPay(result, successCallback) {
-  //璋冪敤寰俊鏀粯鎺ュ彛
-  wx.chooseWXPay({
-    timestamp: result.timeStamp, // 鏀粯绛惧悕鏃堕棿鎴筹紝娉ㄦ剰寰俊jssdk涓殑鎵�鏈変娇鐢╰imestamp瀛楁鍧囦负灏忓啓銆備絾鏈�鏂扮増鐨勬敮浠樺悗鍙扮敓鎴愮鍚嶄娇鐢ㄧ殑timeStamp瀛楁鍚嶉渶澶у啓鍏朵腑鐨凷瀛楃
-    nonceStr: result.nonceStr, // 鏀粯绛惧悕闅忔満涓诧紝涓嶉暱浜� 32 浣�
-    package: result.packageValue, // 缁熶竴鏀粯鎺ュ彛杩斿洖鐨刾repay_id鍙傛暟鍊硷紝鎻愪氦鏍煎紡濡傦細prepay_id=\*\*\*锛�
-    signType: result.signType, // 绛惧悕鏂瑰紡锛岄粯璁や负'SHA1'锛屼娇鐢ㄦ柊鐗堟敮浠橀渶浼犲叆'MD5'
-    paySign: result.paySign, // 鏀粯绛惧悕
-    success: () => {
-      successCallback && successCallback()
-    },
-    fail: (e) => {
-      store.commit('snack/error', '璇疯仈绯绘妧鏈鏈嶈В鍐�' + e.errMsg)
-    }
-  })
-}
-
-/**
- * 鎵爜鏀粯鎴愬姛鐨勫洖璋�
- * @param {} orderId
- * @returns {} 
- */
-function checkIsPay(orderId, successCallback) {
-  if (!store.state.timer.paying) {
-    return false
-  }
-  setTimeout(function () {
-    axios.get('/transact/order/payResult', {
-      params: { orderId: orderId }
-    }).then(res => {
-      if (res.data.success) {
-        successCallback && successCallback()
-        store.commit('timer/paying', false)
-      } else {
-        checkIsPay(orderId, successCallback)
-      }
-    })
-  }, 2000)
-}
-
-/**
- * 鑾峰彇瀹氫綅
- * @param {*} locationCallback 
- */
-function getPosition(locationCallback){
-  wx.getLocation({
-    success: function (res) {
-      locationCallback(res)
-    }
-  })  
 }
 
 function chooseImage(){
@@ -246,6 +70,7 @@
     })
   })
 }
+
 function getLocalImgData(localId){
   return new Promise((resolve) => {
     wx.getLocalImgData({
@@ -260,75 +85,7 @@
   })
 }
 
-
-/**
- * 棰勮鍥剧墖
- * @param {*} url 
- * @param {*} urlList 
- */
-function previewImage(current, urls){
-  wx.previewImage({
-    current: current, // 褰撳墠鏄剧ず鍥剧墖鐨刪ttp閾炬帴
-    urls: urls ? urls : [current] // 闇�瑕侀瑙堢殑鍥剧墖http閾炬帴鍒楄〃
-  })
-}
-
-/**
- * 杩斿洖灏忕▼搴忛〉闈�
- * @param {*} mpRouter // 灏忕▼搴忕殑璺敱
- * @param {*} otherCallback // 鍏朵粬鍥炶皟鎿嶄綔
- */
-function redirectToMp(mpRouter) {
-  return new Promise(resolve => {
-    // 闈炲井淇� 鎴� 浜虹ぞ灞�娲诲姩鍏ュ彛
-    if (!weixinFlag || store.state.course.isRsjActivity) {
-      resolve(true)
-      return false
-    }
-    wx.miniProgram.getEnv(function(res) {
-      if (res.miniprogram) { // 灏忕▼搴�
-        wx.miniProgram.redirectTo({
-          url: mpRouter,
-          success: function(){
-            console.log('璺宠浆鎴愬姛')
-            setTimeout(() => { // 澶勭悊鍏朵粬鏈烘瀯鐨勫皬绋嬪簭璺宠浆鍦烘櫙
-              resolve(true);
-            }, 3000)
-          },
-          fail: function(){
-            resolve(true)
-          }
-        })
-      } else { // 闈炲皬绋嬪簭
-        resolve(true)
-      }
-    })
-  })
-}
-
-/**
- * 鍚戝皬绋嬪簭鍙戦�佹秷鎭�
- */
-function postMessage(data) {
-  // 闈炲井淇�
-  if (!weixinFlag) { return false }
-
-  wx.miniProgram.getEnv(function(res) {
-    if (res.miniprogram) { // 灏忕▼搴�
-      wx.miniProgram.postMessage({
-        data: data
-      })
-    }
-  })
-}
-
 export {
   getWxSignature,
-  initShareOption,
-  unipayPay,
-  getPosition,
-  previewImage,
-  chooseImage,
-  redirectToMp,
-  postMessage
+  chooseImage
 }
diff --git a/src/views/h5/faceAuth/components/auditDialog.vue b/src/views/h5/faceAuth/components/auditDialog.vue
index 15b15e8..e7495eb 100644
--- a/src/views/h5/faceAuth/components/auditDialog.vue
+++ b/src/views/h5/faceAuth/components/auditDialog.vue
@@ -66,7 +66,8 @@
   data() {
     return {
       dialogFlag: false,
-      status: '' 
+      status: '',
+      url: ''
     }
   },
   props: {
@@ -99,16 +100,15 @@
   methods: {
     async submitAudit() {
       this.status = 'auditing'
-      const url = await uploadByBase64(this.base64 ,'浜鸿劯鐓х墖')
-      if (!url) {
+      this.url = await uploadByBase64(this.base64 ,'浜鸿劯鐓х墖')
+      if (!this.url) {
         this.status = 'fail'
         return
       }
-      const params = { faceImgPath: url  }
+      const params = { faceImgPath: this.url  }
       this.$axios.get('/system/auth/staff/checkin/face-match', { params }).then(res => {
         if (res.data.code == 0) {
-          // this.status = res.data.data ? 'success' : 'fail'
-          this.status = 'success'
+          this.status = res.data.data ? 'success' : 'fail'
         } else {
           this.status = 'fail'
           this.$message.error(res.data.msg || "浜鸿劯姣斿澶辫触")
@@ -119,7 +119,7 @@
     },
     handlerSuccess() {
       this.dialogFlag = false
-      this.$emit('handlerSuccess')
+      this.$emit('handlerSuccess', this.url)
     }
   }
 }
diff --git a/src/views/h5/faceAuth/components/camera.vue b/src/views/h5/faceAuth/components/camera.vue
index 530cbe0..21bec1c 100644
--- a/src/views/h5/faceAuth/components/camera.vue
+++ b/src/views/h5/faceAuth/components/camera.vue
@@ -15,7 +15,6 @@
 </template>
 
 <script>
-import { uploadByBase64 } from '@/utils/tool.js'
 export default {
   data () {
     return {
@@ -197,7 +196,6 @@
     },
     async uploadBase64(){ // 涓婁紶鍥剧墖
       let base64 = this.$refs.canvasEl.toDataURL("image/png", 1);
-      // const url = await uploadByBase64(base64, '鏍搁獙鐓х墖')
       if (base64) {
         this.$emit('handlerSuccess', base64)
         this.closeCamera()
diff --git a/src/views/h5/faceAuth/index.vue b/src/views/h5/faceAuth/index.vue
index c7635b9..ef9b33a 100644
--- a/src/views/h5/faceAuth/index.vue
+++ b/src/views/h5/faceAuth/index.vue
@@ -51,6 +51,7 @@
 import auditDialog from '@/views/h5/faceAuth/components/auditDialog.vue';
 import { useSessionStore } from '@/stores/session.js'
 import { storeToRefs } from 'pinia';
+import { chooseImage } from '@/utils/wxjssdk.js'
 export default {
   components: {
     camera,
@@ -63,10 +64,10 @@
   data() {
     return {
       tipItems: [
-        { label: '鏍囧噯鎷嶆憚', isCheck: true },
-        { label: '鏍囧噯鎷嶆憚', isCheck: true },
-        { label: '鏍囧噯鎷嶆憚', isCheck: true },
-        { label: '鏍囧噯鎷嶆憚', isCheck: true },
+        { label: '鏍囧噯鎷嶆憚' },
+        { label: '閬尅鑴搁儴' },
+        { label: '鎷嶆憚涓嶅叏' },
+        { label: '鍏夌嚎涓嶈冻' },
       ],
       openCameraFlag: false,
       base64: '',
@@ -86,6 +87,9 @@
           'border-color': '#f8f8f8'
         }
       }
+    },
+    appId() {
+      return this.$route.query.appId
     }
   },
   async mounted() {
@@ -95,9 +99,10 @@
     getUserPositionStatus(evt) {
       this.userPositionStatus = evt
     },
-    startCapture() {
+    async startCapture() {
       if (isWeixin) {
-        console.log('')
+        const photo = await chooseImage()
+        this.shootSuccess('data:image/jpg;base64,' + photo)
       } else {
         this.openCameraFlag = true
       }
@@ -108,17 +113,9 @@
         this.auditDialogFlag = true
       }
     },
-    auditSuccess() {
-      localStorage.setItem('isFace', true)
-      if (!this.getIsSignup()) {
-        this.$router.replace({ path: '/h5/signup', query: { appId: this.appId } })
-      } else {
-        this.$router.replace({ path: '/h5/verForm', query: { appId: this.appId }})
-      }
+    auditSuccess(evt) {
+      this.$router.replace({ path: '/h5/signup', query: { appId: this.appId, url: evt } })
     },
-    getIsSignup() {
-      return Boolean(localStorage.getItem('isSignup'))
-    }
   }
 }
 </script>
diff --git a/src/views/h5/index.vue b/src/views/h5/index.vue
index a4c46f9..fd2c39d 100644
--- a/src/views/h5/index.vue
+++ b/src/views/h5/index.vue
@@ -1,33 +1,32 @@
 <template>
-  <div>
+  <div v-if="userInfo.id">
     <router-view></router-view>
   </div>
 </template>
 <script>
 import { useSessionStore } from '@/stores/session.js'
+import { storeToRefs } from 'pinia';
 export default {
   setup() {
     const { setUserInfo } = useSessionStore()
-    return { setUserInfo }
+    const { userInfo } = storeToRefs(useSessionStore())
+    return { setUserInfo, userInfo }
   },
   data() {
     return {}
   },
-  async created() {
-    await this.getUserInfo()
+  created() {
+    if (this.$route.path == '/h5/verify' && this.$route.query.appId) {
+      localStorage.setItem('verify_url', this.$route.fullPath)
+    }
+    this.getUserInfo()
   },
   methods: {
     getUserInfo() {
-      return new Promise((resolve) => {
-        this.$axios.get('/system/auth/staff/profile').then(res => {
-          if (res.data.code == 0) {
-            this.setUserInfo(res.data.data || {})
-          } else {
-            this.$message.error(res.data.msg || '鑾峰彇鐢ㄦ埛淇℃伅澶辫触')
-          }
-        }).finally(() => {
-          resolve()
-        })
+      this.$axios.get('/system/auth/staff/profile').then(res => {
+        if (res.data.code == 0) {
+          this.setUserInfo(res.data.data || {})
+        }
       })
     },
   }
diff --git a/src/views/h5/login/bind.vue b/src/views/h5/login/bind.vue
new file mode 100644
index 0000000..cf7d47a
--- /dev/null
+++ b/src/views/h5/login/bind.vue
@@ -0,0 +1,140 @@
+<template>
+  <div class="login">
+    <el-form ref="form" :model="form">
+      <el-form-item :rules="[$rules.required('璇疯緭鍏ョ粦瀹氭墜鏈哄彿') , $rules.phone()]" prop="mobile">
+        <el-input v-model="form.mobile" placeholder="璇疯緭鍏ョ粦瀹氭墜鏈哄彿" style="width: 100%" size="large" />
+      </el-form-item>
+      <el-form-item prop="code" :rules="[$rules.required('璇疯緭鍏ラ獙璇佺爜'), $rules.code()]">
+        <el-input 
+          v-model="form.code" 
+          placeholder="璇疯緭鍏ラ獙璇佺爜" 
+          style="width: 100%" size="large"
+        >
+          <template #append>
+            <el-row style="width: 70px;justify-content: center;">
+              <el-button 
+                v-if="countdown == 180"
+                style="color: var(--el-color-primary);"
+                class="cursor-p" 
+                :loading="sendCodeLoading"
+                @click="sendCode()"
+              >
+                鑾峰彇楠岃瘉鐮�
+              </el-button>
+              <el-text v-else>{{ countdown }}s</el-text>
+            </el-row>
+          </template>
+        </el-input>
+      </el-form-item>
+    </el-form>
+    <el-button @click="login()" :loading="loginLoading" type="primary" size="large" class="mt-2" style="width: 100%">缁戝畾骞剁櫥褰�</el-button>
+  </div>
+</template>
+<script>
+import { tokenUtils } from '@/utils/axios.js';
+import { useLoginStore } from '@/stores/login.js'
+import { storeToRefs } from 'pinia';
+export default {
+  setup() {
+    const { lastRouteInfo } = storeToRefs(useLoginStore())
+    return { lastRouteInfo }
+  },
+  data() {
+    return {
+      form: {
+        mobile: '',
+        code: '',
+      },
+      countdown: 180,
+      sendCodeLoading: false,
+      loginLoading: false,
+      countdownInterval: null
+    }
+  },
+  created() {
+  },
+  computed: {
+    state() {
+      return this.$route.query.state
+    },
+    openid() {
+      return this.$route.query.openid
+    },
+    wxCode() {
+      return this.$route.query.code
+    }
+  },
+  mounted() {
+    document.title = this.$route.name
+  },
+  methods: {
+    startCountdownInterval() {
+      this.clearCountdownInterval()
+      this.countdown--
+      this.countdownInterval = setInterval(() => {
+        if (this.countdown > 0) {
+          this.countdown--
+        } else {
+          this.countdown = 180
+          this.clearCountdownInterval()
+        }
+      }, 1000)
+    },
+    clearCountdownInterval() {
+      clearInterval(this.countdownInterval)
+      this.countdownInterval = null
+    },
+    async sendCode() {
+      const validate = await this.$refs.form.validateField('mobile')
+      if (validate) {
+        const data = {
+          captchaVerification: '',
+          mobile: this.form.mobile,
+          scene: 21,
+        }
+        this.sendCodeLoading = true
+        this.$axios.post('/system/auth/send-sms-code', data).then(res => {
+          if (res.data.code == 0) {
+            this.startCountdownInterval()
+            this.$message.success('宸插彂閫侀獙璇佺爜锛岃娉ㄦ剰鏌ユ敹')
+          } else {
+            this.$message.error(res.data.msg || '鑾峰彇楠岃瘉鐮佸け璐�')
+          }
+        }).finally(() => {
+          this.sendCodeLoading = false
+        })
+      }
+    },
+    login() {
+      const data = {
+        mobile: this.form.mobile,
+        code: this.form.code,
+        state: this.state,
+        openid: this.openid,
+        wxCode: this.wxCode
+      }
+      this.loginLoading = true
+      this.$axios.post('/system/auth/staff/checkin/bind', data).then(async res => {
+        if (res.data.code == 0) {
+          const resData = res.data.data
+          tokenUtils.setTokens(resData.accessToken, resData.refreshToken)
+          this.$message.success('缁戝畾鎴愬姛')
+          const path = localStorage.getItem('verify_url')
+          if (path) {
+            this.$router.replace(path)
+          }
+        } else {
+          this.$message.error(res.data.msg || '鐧诲綍澶辫触')
+        }
+      }).finally(() => {
+        this.loginLoading = false
+      })
+    },
+  }
+}
+</script>
+<style scoped>
+.login {
+  padding: 40px 20px 20px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/h5/login/index.vue b/src/views/h5/login/index.vue
index b9cd759..f434649 100644
--- a/src/views/h5/login/index.vue
+++ b/src/views/h5/login/index.vue
@@ -33,15 +33,16 @@
 <script>
 import { tokenUtils } from '@/utils/axios.js';
 import { useLoginStore } from '@/stores/login.js'
+import { storeToRefs } from 'pinia';
 import { isWeixin } from '@/utils/UA.js'
 export default {
   setup() {
-    const { lastRouteInfo } = useLoginStore()
+    const { lastRouteInfo } = storeToRefs(useLoginStore())
     return { lastRouteInfo }
   },
   data() {
     return {
-      loginType: '', //mobile銆亀eixin
+      loginType: '', //mobilePhone銆亀eixin
       form: {
         mobile: '',
         code: '',
@@ -55,7 +56,10 @@
   created() {
     tokenUtils.clearTokens()
     this.loginType = isWeixin ? 'weixin' : 'mobilePhone'
-    this.loginType = 'mobile'
+    if (isWeixin) {
+      this.loginType = 'weixin'
+      this.$router.replace({ path: '/h5/redirect' })
+    }
   },
   computed: {
     appId() {
@@ -114,8 +118,9 @@
           const resData = res.data.data
           tokenUtils.setTokens(resData.accessToken, resData.refreshToken)
           this.$message.success('鐧诲綍鎴愬姛')
-          if (this.lastRouteInfo.name) {
-            this.$router.replace(this.lastRouteInfo)
+          const path = localStorage.getItem('verify_url')
+          if (path) {
+            this.$router.replace(path)
           }
         } else {
           this.$message.error(res.data.msg || '鐧诲綍澶辫触')
@@ -124,12 +129,6 @@
         this.loginLoading = false
       })
     },
-    verify() {
-      this.$router.push('/h5/verify')
-    },
-    signup() {
-      this.$router.push('/h5/signup')
-    }
   }
 }
 </script>
diff --git a/src/views/h5/login/redirect.vue b/src/views/h5/login/redirect.vue
new file mode 100644
index 0000000..07fa156
--- /dev/null
+++ b/src/views/h5/login/redirect.vue
@@ -0,0 +1,232 @@
+<template>
+  <div class="wx-login-container">
+    <div class="loading-wrapper">
+      <div class="wechat-icon">
+        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
+          <path d="M717.5 595.8c-15.7 8.3-32.6 13.9-50.4 15.7-12.6 1.3-25.3-0.4-37.6-2.9-19-3.8-36.9-10.8-53.5-20.7-3.6-2.2-7.2-4.4-10.7-6.8-19.5-13.6-36.5-29.9-50.8-49-6.5-8.6-12.1-17.9-16.9-27.5-4.8-9.7-8.3-19.8-10.6-30.3-2.2-10.1-3.2-20.3-3-30.6 0.1-5.3 0.4-10.5 1.1-15.8 1.7-12.6 5.1-24.7 10.1-36.3 6.1-14.1 14.2-27 24.4-38.5 13.9-15.6 30.4-28.1 49.3-37.2 12.9-6.3 26.5-10.6 40.6-12.8 14.1-2.2 28.4-2.2 42.6 0.1 14.2 2.3 27.8 6.7 40.7 13.1 18.9 9.2 35.4 21.7 49.3 37.3 10.2 11.5 18.3 24.5 24.4 38.5 5 11.6 8.4 23.8 10.1 36.3 0.7 5.3 1 10.5 1.1 15.8 0.2 10.3-0.8 20.5-3 30.6-2.3 10.5-5.8 20.6-10.6 30.3-4.8 9.6-10.4 18.9-16.9 27.5-14.3 19.1-31.3 35.4-50.8 49-3.5 2.4-7.1 4.6-10.7 6.8-16.6 9.9-34.5 16.9-53.5 20.7-12.3 2.5-25 4.2-37.6 2.9z" fill="#07C160"/>
+          <path d="M395.6 380.4c0 54.7 26.5 103.5 67.6 134.4-3.6 9.8-7.9 19.2-12.8 28.1-4.9 8.9-10.4 17.4-16.6 25.4-6.2 8-13.1 15.4-20.6 22.2-7.5 6.8-15.7 12.8-24.4 17.9-8.7 5.1-18 9.3-27.6 12.5-9.6 3.2-19.6 5.3-29.8 6.3-10.2 1-20.5 0.9-30.6-0.4-10.1-1.3-20-3.8-29.4-7.4-9.4-3.6-18.3-8.3-26.6-14-8.3-5.7-15.9-12.2-22.7-19.6-6.8-7.4-12.7-15.5-17.6-24.3-4.9-8.8-8.8-18.2-11.5-28-2.7-9.8-4.2-19.9-4.5-30.2-0.3-10.3 0.8-20.5 3.2-30.5 2.4-10 6.1-19.6 10.9-28.7 4.8-9.1 10.7-17.5 17.5-25.2 6.8-7.7 14.5-14.5 22.9-20.4 8.4-5.9 17.5-10.8 27.1-14.6 9.6-3.8 19.7-6.4 30-7.9 10.3-1.5 20.8-1.8 31.2-0.7 10.4 1.1 20.6 3.6 30.3 7.4 9.7 3.8 18.8 8.8 27.1 15 8.3 6.2 15.8 13.4 22.4 21.5 6.6 8.1 12.2 16.9 16.6 26.3 4.4 9.4 7.6 19.3 9.5 29.6z" fill="#07C160"/>
+        </svg>
+      </div>
+      <div class="sub-text">
+        寰俊鎺堟潈涓�
+        <span class="dots">
+          <span class="dot"></span>
+          <span class="dot"></span>
+          <span class="dot"></span>
+        </span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { tokenUtils } from '@/utils/axios.js';
+import { useLoginStore } from '@/stores/login.js'
+import { storeToRefs } from 'pinia';
+export default {
+  setup() {
+    const { lastRouteInfo } = storeToRefs(useLoginStore())
+    return { lastRouteInfo }
+  },
+  data() {
+    return {
+
+    }
+  },
+  watch() {
+
+  },
+  computed: {
+    code() {
+      return this.$route.query.code
+    },
+    state() {
+      return this.$route.query.state
+    }
+  },
+  created() {
+    if (this.code && this.state) {
+      this.authSuccess()
+    } else {
+      this.redirectUrl()
+    }
+  },
+  methods: {
+    redirectUrl() {
+      const params = {
+        targetId: '',
+        redirectUrl: encodeURIComponent(window.location.href),
+      }
+      this.$axios.get('/system/auth/staff/checkin/wx-auth-redirect', { params }).then(res => {
+        if (res.data.code == 0) {
+          window.location.replace(res.data.data)
+        } else {
+          this.$message.error(res.data.msg)
+        }
+      })
+    },
+    authSuccess() {
+      const params = {
+        targetId: '',
+        code: this.code,
+        state: this.state
+      }
+      this.$axios.get('/system/auth/staff/checkin/auth-success/null', { params }).then(res => {
+        if (res.data.code == 0) {
+          const resData = res.data.data || {}
+          if (resData.userId) {
+            tokenUtils.setTokens(resData.accessToken, resData.refreshToken)
+            const path = localStorage.getItem('verify_url')
+            if (path) {
+              this.$router.replace(path)
+            }
+          } else {
+            this.$router.replace({ 
+              path: '/h5/bind', 
+              query: { 
+                state: resData.state, 
+                openid: resData.openid,
+                code: resData.code
+              } 
+            })
+          }
+        } else {
+          this.$message.error(res.data.msg)
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.wx-login-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 100vh;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+}
+
+.loading-wrapper {
+  text-align: center;
+  padding: 60px 40px;
+}
+
+.wechat-icon {
+  width: 100px;
+  height: 100px;
+  margin: 0 auto 30px;
+  animation: iconPulse 2s ease-in-out infinite;
+}
+
+.wechat-icon svg {
+  width: 100%;
+  height: 100%;
+  filter: drop-shadow(0 8px 16px rgba(7, 193, 96, 0.3));
+}
+
+@keyframes iconPulse {
+  0%, 100% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(1.1);
+  }
+}
+
+.loading-text {
+  font-size: 28px;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 16px;
+  display: inline-block;
+}
+
+.text-item {
+  display: inline-block;
+  opacity: 0;
+  animation: textFadeIn 0.5s ease-out forwards;
+}
+
+.text-item:nth-child(1) {
+  animation-delay: 0.2s;
+}
+
+.text-item:nth-child(2) {
+  animation-delay: 0.4s;
+}
+
+.text-item:nth-child(3) {
+  animation-delay: 0.6s;
+}
+
+.text-item:nth-child(4) {
+  animation-delay: 0.8s;
+}
+
+@keyframes textFadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.dots {
+  display: inline-block;
+  margin-left: 8px;
+  vertical-align: middle;
+}
+
+.dot {
+  display: inline-block;
+  width: 6px;
+  height: 6px;
+  background: #07C160;
+  border-radius: 50%;
+  margin: 0 3px;
+  animation: dotBounce 1.4s ease-in-out infinite both;
+}
+
+.dot:nth-child(1) {
+  animation-delay: -0.32s;
+}
+
+.dot:nth-child(2) {
+  animation-delay: -0.16s;
+}
+
+.dot:nth-child(3) {
+  animation-delay: 0;
+}
+
+@keyframes dotBounce {
+  0%, 80%, 100% {
+    transform: scale(0);
+    opacity: 0.5;
+  }
+  40% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+.sub-text {
+  font-size: 18px;
+  color: #999;
+  animation: fadeInOut 2s ease-in-out infinite;
+}
+
+@keyframes fadeInOut {
+  0%, 100% {
+    opacity: 0.5;
+  }
+  50% {
+    opacity: 1;
+  }
+}
+</style>
diff --git a/src/views/h5/signup/index.vue b/src/views/h5/signup/index.vue
index 7a6f83d..99098ca 100644
--- a/src/views/h5/signup/index.vue
+++ b/src/views/h5/signup/index.vue
@@ -77,11 +77,11 @@
       distance: null,
       positionError: false,
       positionName: '',
-      positionAddress: '鍗楀北鍖烘墦鐭充簩璺崡118鍙�',
+      positionAddress: '',
       currentTimeText: '',
       centerPoint: {
-        lat: 22.580372,
-        lng: 113.946530
+        lat: 23.135618,
+        lng: 113.27077
       }
     }
   },
@@ -104,6 +104,9 @@
     },
     appId() {
       return this.$route.query.appId
+    },
+    url() {
+      return this.$route.query.url
     }
   },
   created() {
@@ -118,10 +121,12 @@
       this.$axios.get('/exam/verify-record/get-by-application-id', { params }).then(res => {
         if (res.data.code == 0) {
           const resData = res.data.data || {}
-          // this.centerPoint = {
-          //   lat: resData.examSite?.locationLat,
-          //   lng: resData.examSite?.locationLng
-          // }
+          if (resData.examSite?.locationLat && resData.examSite?.locationLng) {
+            this.centerPoint = {
+              lat: resData.examSite?.locationLat,
+              lng: resData.examSite?.locationLng 
+            }
+          }
           this.positionAddress = resData.examSite?.address
         } else {
           this.$message.error(res.data.msg)
@@ -140,22 +145,29 @@
       }
     },
     signinConfirm() {
-      if (!this.canSignup) {
+      if (!this.canSignup || this.confirmLoading) {
         return
       }
-      this.$message.success('绛惧埌鎴愬姛')
-      localStorage.setItem('isSignup', true)
-      setTimeout(() => {
-        if (this.getIsFace()) {
-          this.$router.replace({ path: '/h5/face', query: { appId: this.appId }})
+      const data = {
+        targetId: this.appId,
+        targetType: 2,
+        url: this.url,
+        type: 0
+      }
+      this.confirmLoading = true
+      this.$axios.post('/exam/staff/checkin', data).then(res => {
+        if (res.data.code == 0) {
+          this.$message.success('绛惧埌鎴愬姛')
+          setTimeout(() => {
+            this.$router.replace({ path: '/h5/verForm', query: { appId: this.appId }})
+          }, 500)
         } else {
-          this.$router.replace({ path: '/h5/verForm', query: { appId: this.appId }})
+          this.$message.error(res.data.msg)
         }
-      }, 500)
+      }).finally(() => {
+        this.confirmLoading = false
+      })
     },
-    getIsFace() {
-      return Boolean(localStorage.getItem('isFace'))
-    }
   }
 }
 </script>
diff --git a/src/views/h5/verify/form.vue b/src/views/h5/verify/form.vue
index 8409462..8fc725e 100644
--- a/src/views/h5/verify/form.vue
+++ b/src/views/h5/verify/form.vue
@@ -1,5 +1,5 @@
 <template>
-  <div v-if="pdfUrl">
+  <div>
     <el-row class="p-3 m-0" justify="space-between" align="middle">
       <el-col :span="4"></el-col>
       <el-col :span="16">
@@ -28,6 +28,7 @@
       <div v-if="pdfUrl" :style="{width: '100%', height: `${mainHeight - 100}px`}">
         <PdfPreview v-if="pdfUrl" :url="pdfUrl"></PdfPreview>
       </div>
+      <el-text class="ml-2 text-info">鑰冪偣鐢虫姤鏂囦欢鍔犺浇澶辫触...</el-text>
       <div class="p-2 my-4">
         <el-form ref="verifyForm" :model="form">
           <el-form-item label="*浠ヤ笂鐢虫姤鍐呭鏄惁灞炲疄" prop="isVerified">
@@ -125,7 +126,7 @@
       this.$axios.get('/exam/verify-record/get-by-application-id', { params }).then(res => {
         if (res.data.code == 0) {
           const resData = res.data.data || {}
-          this.pdfUrl = this.$qxueyou.qxyRes + resData.examSiteVerifyFile
+          this.pdfUrl = resData.examSiteVerifyFile ? this.$qxueyou.qxyRes + resData.examSiteVerifyFile : ''
           this.title = resData.organizationName + '-' + resData.examSite.siteName + '鑰冪偣鏍搁獙'
           if (resData.id) {
             this.form.isContentTrue  = resData.isContentTrue
diff --git a/src/views/h5/verify/index.vue b/src/views/h5/verify/index.vue
index 02e2d30..0befee6 100644
--- a/src/views/h5/verify/index.vue
+++ b/src/views/h5/verify/index.vue
@@ -2,33 +2,27 @@
   <div></div>
 </template>
 <script>
-import { tokenUtils } from '@/utils/axios.js';
-
 export default {
   components: {},
   data() {
     return {}
   },
   computed: {
-    query() {
-      return this.$route.query
-    },
     appId() {
-      return this.query.appId
+      return this.$route.query.appId
     }
   },
   async created() {
     const canVerify = await this.getCanVerify()
-    if (canVerify) {
-      if (!this.getIsFace()) {
-        this.$router.replace({ path: '/h5/face', query: { appId: this.appId }})
-      } else if (!this.getIsSignup()) {
-        this.$router.replace({ path: '/h5/signup', query: { appId: this.appId } })
-      } else {
-        this.$router.replace({ path: '/h5/verForm', query: { appId: this.appId }})
-      }
-    } else {
+    if (!canVerify) {
       this.$router.replace('/h5/noVerAccess')
+      return
+    }
+    const checkinExist = await this.getCheckinExist()
+    if (checkinExist) {
+      this.$router.replace({ path: '/h5/verForm', query: { appId: this.appId }})
+    } else {
+      this.$router.replace({ path: '/h5/face', query: { appId: this.appId }})
     }
   },
   mounted() {
@@ -38,7 +32,7 @@
     getCanVerify() {
       return new Promise((resolve) => {
         const params = {
-          applicationId: this.$route.query.appId
+          applicationId: this.appId
         }
         this.$axios.get('/exam/verify-record/can-verify', { params }).then(res => {
           if (res.data.code == 0) {
@@ -51,12 +45,22 @@
         })
       })
     },
-    getIsFace() {
-      return Boolean(localStorage.getItem('isFace'))
+    getCheckinExist() {
+      return new Promise((resolve) => {
+        const params = {
+          targetId: this.appId
+        }
+        this.$axios.get('/exam/staff/checkin/exist', { params }).then(res => {
+          if (res.data.code == 0) {
+            resolve(res.data.data)
+          } else {
+            resolve(false)
+          }
+        }, () => {
+          resolve(false)
+        })
+      })
     },
-    getIsSignup() {
-      return Boolean(localStorage.getItem('isSignup'))
-    }
   }
 }
 </script>
\ No newline at end of file
diff --git a/src/views/main/components/UploadIdCard.vue b/src/views/main/components/UploadIdCard.vue
index 924684c..11d73ef 100644
--- a/src/views/main/components/UploadIdCard.vue
+++ b/src/views/main/components/UploadIdCard.vue
@@ -151,7 +151,7 @@
         file: UploadRequestOptions.file,
         directory: ''
       }
-      this.$axios.post('/infra/file/upload', data, { 
+      this.$axios.post('/infra/file/exam/upload', data, { 
         headers: { 'Content-Type': "multipart/form-data" }
       }).then(res => {
         let index = this.list.findIndex(ele => ele.uid == data.file.uid)
diff --git a/vite.config.js b/vite.config.js
index 1837faf..8dad771 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -14,6 +14,7 @@
     },
   },
   server: {
+    allowedHosts: ['dev.qxueyou.com'],
     host: '0.0.0.0',
     proxy: {
       '/app-api': {

--
Gitblit v1.8.0