From 4e6f18dfa08e2f2f4f02aaa1b8e8e51852b7a9a1 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期五, 13 三月 2026 17:46:03 +0800
Subject: [PATCH] 考点核验
---
src/views/main/components/Signature.vue | 29
src/views/h5/signup/BaiduMap.vue | 3
package-lock.json | 82 +++
src/utils/UA.js | 57 ++
src/utils/tool.js | 15
src/assets/images/h5/face_tip_1.png | 0
src/utils/wxjssdk.js | 334 ++++++++++++
src/stores/login.js | 8
src/views/h5/verify/form.vue | 154 ++++-
src/views/h5/verify/index.vue | 35
src/views/h5/signup/index.vue | 80 ++
src/assets/images/h5/face_tip_4.png | 0
src/views/h5/faceAuth/index.vue | 199 +++++++
src/utils/axios.js | 6
vite.config.js | 1
src/router/h5/router.js | 58 +
src/views/h5/faceAuth/components/camera.vue | 265 ++++++++++
src/assets/images/h5/face_tip_3.png | 0
src/views/h5/faceAuth/components/auditDialog.vue | 156 ++++++
src/assets/images/h5/face_success.png | 0
src/views/h5/login/index.vue | 18
src/assets/images/map/position-marker-green.png | 0
src/router/index.js | 5
src/views/h5/index.vue | 35 +
src/assets/images/h5/face_tip_2.png | 0
src/main.js | 10
src/assets/images/map/position-marker-red.png | 0
package.json | 4
src/views/main/components/UploadBtn.vue | 4
src/config/qxueyou.js | 2
src/assets/images/h5/face_default.png | 0
src/assets/images/h5/face_fail.png | 0
32 files changed, 1,439 insertions(+), 121 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 546230b..106b5fd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,8 +16,10 @@
"file-saver": "^2.0.5",
"pdfh5": "^3.0.0",
"pinia": "^3.0.3",
+ "pinia-plugin-persistedstate": "^4.7.1",
"qs": "^6.14.1",
"sass-embedded": "^1.98.0",
+ "vconsole": "^3.15.1",
"vue": "^3.5.22",
"vue-router": "^4.6.3",
"xlsx": "^0.18.5",
@@ -473,6 +475,15 @@
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.6.tgz",
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
@@ -2779,6 +2790,29 @@
"url": "https://github.com/sponsors/mesqueeb"
}
},
+ "node_modules/copy-text-to-clipboard": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmmirror.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.2.tgz",
+ "integrity": "sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "3.48.0",
+ "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.48.0.tgz",
+ "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
@@ -2898,6 +2932,12 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/defu": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz",
+ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
+ "license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
@@ -4051,6 +4091,11 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/mutation-observer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/mutation-observer/-/mutation-observer-1.0.3.tgz",
+ "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA=="
+ },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
@@ -4374,6 +4419,31 @@
},
"peerDependenciesMeta": {
"typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pinia-plugin-persistedstate": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.7.1.tgz",
+ "integrity": "sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "defu": "^6.1.4"
+ },
+ "peerDependencies": {
+ "@nuxt/kit": ">=3.0.0",
+ "@pinia/nuxt": ">=0.10.0",
+ "pinia": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@nuxt/kit": {
+ "optional": true
+ },
+ "@pinia/nuxt": {
+ "optional": true
+ },
+ "pinia": {
"optional": true
}
}
@@ -5344,6 +5414,18 @@
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
"license": "MIT"
},
+ "node_modules/vconsole": {
+ "version": "3.15.1",
+ "resolved": "https://registry.npmmirror.com/vconsole/-/vconsole-3.15.1.tgz",
+ "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.17.2",
+ "copy-text-to-clipboard": "^3.0.1",
+ "core-js": "^3.11.0",
+ "mutation-observer": "^1.0.3"
+ }
+ },
"node_modules/vite": {
"version": "7.3.1",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.3.1.tgz",
diff --git a/package.json b/package.json
index 792cc97..757c2a8 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "app-web-examination-platform",
+ "name": "app-web-examination-user",
"version": "0.0.0",
"private": true,
"type": "module",
@@ -22,8 +22,10 @@
"file-saver": "^2.0.5",
"pdfh5": "^3.0.0",
"pinia": "^3.0.3",
+ "pinia-plugin-persistedstate": "^4.7.1",
"qs": "^6.14.1",
"sass-embedded": "^1.98.0",
+ "vconsole": "^3.15.1",
"vue": "^3.5.22",
"vue-router": "^4.6.3",
"xlsx": "^0.18.5",
diff --git a/src/assets/images/h5/face_default.png b/src/assets/images/h5/face_default.png
new file mode 100644
index 0000000..95ddd7a
--- /dev/null
+++ b/src/assets/images/h5/face_default.png
Binary files differ
diff --git a/src/assets/images/h5/face_fail.png b/src/assets/images/h5/face_fail.png
new file mode 100644
index 0000000..8803597
--- /dev/null
+++ b/src/assets/images/h5/face_fail.png
Binary files differ
diff --git a/src/assets/images/h5/face_success.png b/src/assets/images/h5/face_success.png
new file mode 100644
index 0000000..227529f
--- /dev/null
+++ b/src/assets/images/h5/face_success.png
Binary files differ
diff --git a/src/assets/images/h5/face_tip_1.png b/src/assets/images/h5/face_tip_1.png
new file mode 100644
index 0000000..600ca60
--- /dev/null
+++ b/src/assets/images/h5/face_tip_1.png
Binary files differ
diff --git a/src/assets/images/h5/face_tip_2.png b/src/assets/images/h5/face_tip_2.png
new file mode 100644
index 0000000..5f057af
--- /dev/null
+++ b/src/assets/images/h5/face_tip_2.png
Binary files differ
diff --git a/src/assets/images/h5/face_tip_3.png b/src/assets/images/h5/face_tip_3.png
new file mode 100644
index 0000000..a0b2385
--- /dev/null
+++ b/src/assets/images/h5/face_tip_3.png
Binary files differ
diff --git a/src/assets/images/h5/face_tip_4.png b/src/assets/images/h5/face_tip_4.png
new file mode 100644
index 0000000..a673e2d
--- /dev/null
+++ b/src/assets/images/h5/face_tip_4.png
Binary files differ
diff --git a/src/assets/images/map/position-marker-green.png b/src/assets/images/map/position-marker-green.png
new file mode 100644
index 0000000..f1587ef
--- /dev/null
+++ b/src/assets/images/map/position-marker-green.png
Binary files differ
diff --git a/src/assets/images/map/position-marker-red.png b/src/assets/images/map/position-marker-red.png
new file mode 100644
index 0000000..0b5a042
--- /dev/null
+++ b/src/assets/images/map/position-marker-red.png
Binary files differ
diff --git a/src/config/qxueyou.js b/src/config/qxueyou.js
index e57c416..2aef9f1 100644
--- a/src/config/qxueyou.js
+++ b/src/config/qxueyou.js
@@ -9,7 +9,7 @@
baseUrl: serverContext,
htmlRoot: baseDomain + htmlContext,
serverRoot: baseDomain + serverContext,
- upload: `${serverContext}/base/file/upload`,
+ upload: `${serverContext}/infra/file/exam/upload`,
ACCESS_TOKEN_KEY: 'qxy-user-accessToken',
REFRESH_TOKEN_KEY: 'qxy-user-refreshToken'
}
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 5707d68..13eec40 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,5 +1,6 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import './assets/styles/global.css'
@@ -27,6 +28,11 @@
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
+
+// import('vconsole').then((module) => {
+// new module.default()
+// })
+
app.config.globalProperties.$rules = ruleGenerator
app.config.globalProperties.$property = property
app.config.globalProperties.$qxueyou = qxueyou
@@ -47,7 +53,9 @@
app.use(ElementPlus, {
locale: zhCn
})
-app.use(createPinia())
+const pinia = createPinia()
+pinia.use(piniaPluginPersistedstate)
+app.use(pinia)
app.use(router)
app.mount('#app')
diff --git a/src/router/h5/router.js b/src/router/h5/router.js
index 80f06e5..c8ebaa8 100644
--- a/src/router/h5/router.js
+++ b/src/router/h5/router.js
@@ -1,28 +1,40 @@
const router = [
{
- path: '/h5/verify',
- name: '鑰冪偣鏍搁獙',
- component: () => import('@/views/h5/verify/index.vue'),
- },
- {
- path: '/h5/verForm/:id',
- name: '鎻愪氦鑰冪偣鏍搁獙',
- component: () => import('@/views/h5/verify/form.vue'),
- },
- {
- path: '/h5/noVerAccess',
- name: '鏍搁獙鏃犳潈闄�',
- component: () => import('@/views/h5/verify/noAccess.vue'),
- },
- {
- path: '/h5/login',
- name: '韬唤楠岃瘉鐧诲綍',
- component: () => import('@/views/h5/login/index.vue'),
- },
- {
- path: '/h5/signup',
- name: '绛惧埌',
- component: () => import('@/views/h5/signup/index.vue'),
+ path: '/h5',
+ name: 'h5椤甸潰',
+ component: () => import('@/views/h5/index.vue'),
+ children: [
+ {
+ path: 'verify',
+ name: '鑰冪偣鏍搁獙',
+ component: () => import('@/views/h5/verify/index.vue'),
+ },
+ {
+ path: 'verForm',
+ name: '鎻愪氦鑰冪偣鏍搁獙',
+ component: () => import('@/views/h5/verify/form.vue'),
+ },
+ {
+ path: 'noVerAccess',
+ name: '鏍搁獙鏃犳潈闄�',
+ 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'),
+ },
+ {
+ path: 'face',
+ name: '浜鸿劯楠岃瘉',
+ component: () => import('@/views/h5/faceAuth/index.vue'),
+ },
+ ],
},
]
export default router
diff --git a/src/router/index.js b/src/router/index.js
index 899675b..3b466f4 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -3,6 +3,7 @@
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),
@@ -10,6 +11,7 @@
})
router.beforeEach((to, from, next) => {
+ const { setLastRouteInfo } = useLoginStore()
if (!to.matched.length) {
if (to.path === '/') {
next({ path: '/main/home' })
@@ -17,6 +19,9 @@
next({ path: '/error/404', query: { errorUrl: to.path } })
}
} else {
+ if (from.name) {
+ setLastRouteInfo(from)
+ }
next()
}
})
diff --git a/src/stores/login.js b/src/stores/login.js
index 362488f..b6c6cd3 100644
--- a/src/stores/login.js
+++ b/src/stores/login.js
@@ -2,10 +2,14 @@
import { defineStore } from 'pinia'
export const useLoginStore = defineStore('login', () => {
const loginDialogVisible = ref(false)
+ const lastRouteInfo = ref({})
function setLoginDialogVisible(visible) {
loginDialogVisible.value = visible
}
+ function setLastRouteInfo(info) {
+ lastRouteInfo.value = info
+ }
- return { loginDialogVisible, setLoginDialogVisible }
-})
+ return { loginDialogVisible, setLoginDialogVisible, lastRouteInfo, setLastRouteInfo }
+}, { persist: true })
diff --git a/src/utils/UA.js b/src/utils/UA.js
new file mode 100644
index 0000000..d27de0f
--- /dev/null
+++ b/src/utils/UA.js
@@ -0,0 +1,57 @@
+const ua = window.navigator.userAgent
+const mobileAgents = ['Android', 'iPhone', 'qxyiOSApp', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod', 'OpenHarmony']
+
+const isWeixin = ua.match(/MicroMessenger/i) == 'MicroMessenger'
+const isWeixinWork = ua.match(/wxwork/i) == 'wxwork'
+const isTBSX5 = ua.match(/MQQBrowser/i) == 'MQQBrowser' || ua.match(/TBS/i) == 'TBS'
+
+const isHarmony = ua.indexOf('OpenHarmony') > -1
+const isHarmonyApp = ua.indexOf('qxyHarmony') > -1
+const isAndroid = ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1
+const isAndroidApp = ua.indexOf('qxyAndroidApp') > -1
+const isiOSApp = ua.indexOf('qxyiOSApp') > -1
+const isiOS = !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) || isiOSApp
+const isApp = isAndroidApp || isiOSApp
+const isOldVerApp = (isApp) && ua.indexOf('qxyEcpt') < 0
+
+let isMobile = false
+for (let v = 0; v < mobileAgents.length; v++) {
+ if (ua.includes(mobileAgents[v])) {
+ isMobile = true
+ break
+ }
+}
+
+let iosInputBlur = function () { // 鍏煎ios杈撳叆妗�
+ if(isiOS) { // 鍒ゆ柇鏄惁涓篒OS绯荤粺
+ setTimeout(() => {
+ const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0
+ window.scrollTo(0, Math.max(scrollHeight - 1, 0))
+ }, 100)
+ }
+}
+
+let wxWorkIosScrollToTop = function() {
+ if (isWeixinWork && isiOS) {
+ setTimeout(() => {
+ window.scrollTo(1, 0)
+ }, 1000)
+ }
+}
+
+export {
+ isWeixin,
+ isWeixinWork,
+ isMobile,
+ isTBSX5,
+ isiOS,
+ isiOSApp,
+ isAndroid,
+ isAndroidApp,
+ isApp,
+ isOldVerApp,
+ iosInputBlur,
+ wxWorkIosScrollToTop,
+ isHarmony,
+ isHarmonyApp
+}
\ No newline at end of file
diff --git a/src/utils/axios.js b/src/utils/axios.js
index 293396f..1341863 100644
--- a/src/utils/axios.js
+++ b/src/utils/axios.js
@@ -138,11 +138,17 @@
});
refreshQueue = [];
tokenUtils.clearTokens();
+ if (router.currentRoute._value.path.includes('/h5/')) {
+ router.push({ path: '/h5/login', query: router.currentRoute._value.query })
+ }
} finally {
isRefreshing = false;
}
} else {
tokenUtils.clearTokens();
+ if (router.currentRoute._value.path.includes('/h5/')) {
+ router.push({ path: '/h5/login', query: router.currentRoute._value.query })
+ }
}
}
diff --git a/src/utils/tool.js b/src/utils/tool.js
index 02f842c..6b559d7 100644
--- a/src/utils/tool.js
+++ b/src/utils/tool.js
@@ -1,5 +1,7 @@
import { useOptionItemsStore } from '@/stores/optionItems.js';
import $qxueyou from '@/config/qxueyou.js'
+import { tokenUtils } from '@/utils/axios.js'
+import $axios from '@/utils/axios.js'
/**
* 鑾峰彇 assets/images 鐩綍涓嬬殑鍥剧墖URL
* @param {string} imageName - 鍥剧墖鏂囦欢鍚嶏紙鍖呭惈鎵╁睍鍚嶏級
@@ -159,20 +161,25 @@
let uploadRequest = function(blob, fileName, fileType){
return new Promise((resolve) => {
+ const file = new File([blob], fileName, {
+ type: blob.type || 'application/octet-stream',
+ lastModified: Date.now()
+ });
let fd = new FormData()
let xhr = new XMLHttpRequest()
- fd.append('image', blob, `${fileName}.${fileType}`)
+ fd.append('file', file)
xhr.open('POST', $qxueyou.upload, true)
+ xhr.setRequestHeader('Authorization', localStorage.getItem($qxueyou.ACCESS_TOKEN_KEY));
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200 && xhr.responseText) {
- let file = JSON.parse(xhr.responseText)[0] // 杩斿洖缁撴灉
- resolve(file.path)
+ let file = JSON.parse(xhr.responseText) // 杩斿洖缁撴灉
+ resolve(file.data)
}
}
xhr.onerror = (evt) => { // 涓婁紶澶辫触鍥炶皟
store.commit("snack/error", "涓婁紶澶辫触锛�")
console.log(JSON.stringify(evt.target))
- resolve()
+ resolve(false)
}
xhr.send(fd);
})
diff --git a/src/utils/wxjssdk.js b/src/utils/wxjssdk.js
new file mode 100644
index 0000000..083dcda
--- /dev/null
+++ b/src/utils/wxjssdk.js
@@ -0,0 +1,334 @@
+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'
+
+let newFeature = false
+let oldShare = ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone']
+let newShare = ['updateTimelineShareData', 'updateAppMessageShareData']
+
+let weixinFlag = utilsUA.isWeixin
+let mobileFlag = utilsUA.isMobile
+let channel = weixinFlag && mobileFlag ? 'wx_pub' : 'wx_pub_qr'
+let isWxpub = 'wx_pub'.includes(channel)
+
+/**
+ * 寰俊鑾峰彇绛惧悕
+ * @param {*} toRoute 鐩爣璺敱
+ */
+function getWxSignature(toRoute) {
+ if (!weixinFlag) { return false }
+
+ axios.get('/wx/js/signature', {
+ params: { url: location.href }
+ }).then(res => {
+ if (!res || !res.data) {
+ return false
+ }
+ let result = res.data.data || {}
+ wx.config({ // 寰俊閰嶇疆
+ debug: false,
+ appId: result.appId,
+ timestamp: result.timestamp,
+ nonceStr: result.nonceStr,
+ signature: result.signature,
+ jsApiList: [
+ ...(newFeature ? newShare : oldShare),
+ 'chooseWXPay',
+ 'chooseImage',
+ 'getLocalImgData'
+ ],
+ openTagList: ['wx-open-launch-app']
+ })
+ 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(){
+ return new Promise((resolve) => {
+ wx.chooseImage({
+ count: 1,
+ sizeType: ['compressed'],
+ sourceType: ['camera'],
+ success: (res) => {
+ if (res && res.localIds) {
+ getLocalImgData(res.localIds[0]).then((localData) => {
+ resolve(localData)
+ })
+ } else {
+ store.commit('snack/error', `寰俊涓婁紶鍥剧墖澶辫触锛�${res}`)
+ }
+ },
+ fail:function(e) {
+ store.commit('snack/error', `寰俊涓婁紶鍥剧墖寮傚父锛�${JSON.stringify(e)}`)
+ }
+ })
+ })
+}
+function getLocalImgData(localId){
+ return new Promise((resolve) => {
+ wx.getLocalImgData({
+ localId: localId,
+ success: function (res) {
+ resolve(res.localData)
+ },
+ fail:function(e) {
+ store.commit('snack/error', `寰俊鑾峰彇鍥剧墖寮傚父锛�${JSON.stringify(e)}`)
+ }
+ })
+ })
+}
+
+
+/**
+ * 棰勮鍥剧墖
+ * @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
+}
diff --git a/src/views/h5/faceAuth/components/auditDialog.vue b/src/views/h5/faceAuth/components/auditDialog.vue
new file mode 100644
index 0000000..15b15e8
--- /dev/null
+++ b/src/views/h5/faceAuth/components/auditDialog.vue
@@ -0,0 +1,156 @@
+<template>
+ <el-dialog
+ v-model="dialogFlag"
+ width="80%"
+ style="max-width: 500px;"
+ align-center
+ :show-close="status!='auditing'"
+ :close-on-click-modal="false"
+ :close-on-press-escape="false"
+ >
+ <div class="p-7 pt-0" style="display: flex; flex-direction: column; align-items: center;">
+ <div class="image_box" v-if="['unStart', 'auditing'].includes(status)">
+ <el-image :src="base64"></el-image>
+ <div v-if="status=='auditing'" class="scan-line"></div>
+ </div>
+ <el-row justify="center" v-else-if="['success', 'fail'].includes(status)">
+ <el-image :src="$getImageUrl(`/h5/face_${status}.png`)"></el-image>
+ <el-row>
+ <el-text class="text-lg">浜鸿劯楠岃瘉{{ status=='success'?'':'涓�' }}閫氳繃</el-text>
+ </el-row>
+ <el-row class="mt-2">
+ <el-text v-if="status=='success'">绯荤粺宸叉垚鍔熷鏍告偍鐨勮韩浠�</el-text>
+ <el-text v-else-if="status=='fail'">璇烽噸鏂伴獙璇佹偍鐨勮韩浠�</el-text>
+ </el-row>
+ </el-row>
+ <el-row justify="center" class="mt-5" v-if="status=='auditing'">
+ <el-text>姝e湪瀹℃牳涓紝璇疯�愬績绛夊緟...</el-text>
+ </el-row>
+ <el-button
+ v-if="status=='unStart'"
+ @click="submitAudit"
+ :loading="status=='auditing'"
+ type="primary"
+ size="large" class="mt-5"
+ style="width: 100%;"
+ >
+ 鎻愪氦瀹℃牳
+ </el-button>
+ <el-button
+ v-else-if="status=='success'"
+ @click="handlerSuccess"
+ type="primary"
+ size="large" class="mt-5"
+ style="width: 100%;"
+ >
+ 瀹屾垚楠岃瘉
+ </el-button>
+ <el-button
+ v-else-if="status=='fail'"
+ @click="dialogFlag=false"
+ type="primary"
+ size="large" class="mt-5"
+ style="width: 100%;"
+ >
+ 纭畾
+ </el-button>
+ </div>
+ </el-dialog>
+</template>
+
+<script>
+import { uploadByBase64 } from '@/utils/tool.js';
+
+export default {
+ components: {},
+ data() {
+ return {
+ dialogFlag: false,
+ status: ''
+ }
+ },
+ props: {
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ base64: {
+ type: String,
+ default: ''
+ }
+ },
+ computed: {
+
+ },
+ created() {
+
+ },
+ watch: {
+ modelValue(val) {
+ this.dialogFlag = val
+ if (val) {
+ this.status = 'unStart'
+ }
+ },
+ dialogFlag(val) {
+ this.$emit('update:modelValue', val)
+ }
+ },
+ methods: {
+ async submitAudit() {
+ this.status = 'auditing'
+ const url = await uploadByBase64(this.base64 ,'浜鸿劯鐓х墖')
+ if (!url) {
+ this.status = 'fail'
+ return
+ }
+ const params = { faceImgPath: 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'
+ } else {
+ this.status = 'fail'
+ this.$message.error(res.data.msg || "浜鸿劯姣斿澶辫触")
+ }
+ }).catch(() => {
+ this.status = 'fail'
+ })
+ },
+ handlerSuccess() {
+ this.dialogFlag = false
+ this.$emit('handlerSuccess')
+ }
+ }
+}
+</script>
+<style scoped lang="scss">
+.image_box {
+ position: relative;
+ width: 90%;
+ max-width: 300px;
+ aspect-ratio: 1/1;
+ border-radius: 50% 50%;
+ overflow: hidden;
+ display: flex;
+ justify-content: center;
+ border: 5px solid #5693f4;
+}
+/* 鎵弿绾� */
+.scan-line {
+ position: absolute;
+ left: 0px;
+ right: 0px;
+ height: 50px;
+ background: linear-gradient(0deg, #5693f4, transparent);
+ animation: scan 2s ease-in-out infinite;
+ /* 鍒濆浣嶇疆璁惧湪椤堕儴 */
+ top: -50px;
+}
+
+@keyframes scan {
+ 0% { top: -50px; }
+ 100% { top: 100%; } /* 绉诲姩鍒板簳閮� */
+}
+
+</style>
\ No newline at end of file
diff --git a/src/views/h5/faceAuth/components/camera.vue b/src/views/h5/faceAuth/components/camera.vue
new file mode 100644
index 0000000..530cbe0
--- /dev/null
+++ b/src/views/h5/faceAuth/components/camera.vue
@@ -0,0 +1,265 @@
+<template>
+ <div ref="cameraBox" class="camera_box" :style="cameraStyle" v-show="isCameraOpening">
+ <video ref="videoEl" :width="videoWidth" :height="videoHeight" autoplay></video>
+ <canvas ref="canvasEl" :width="videoWidth" :height="videoHeight" style="display:none;"></canvas>
+
+ <el-row justify="center" class="btn_box">
+ <el-button plain text @click="closeCamera()">
+ <Icon icon="material-symbols:cancel" width="28" height="28" style="color: #FA5252" />
+ </el-button>
+ <el-button plain text @click="startRecordImage()">
+ <Icon icon="ant-design:camera-filled" width="28" height="28" style="color: #FA5252" />
+ </el-button>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import { uploadByBase64 } from '@/utils/tool.js'
+export default {
+ data () {
+ return {
+ recorderOptions: { // recorder 閰嶇疆椤�
+ mimeType: 'video/webm;codecs=vp8,opus'
+ },
+ isCameraOpening: false,
+ }
+ },
+ computed: {
+ videoWidth: function () {
+ return 400
+ },
+ videoHeight: function(){
+ return (this.videoWidth * 3) / 4
+ },
+ cameraStyle: function(){
+ return {
+ width: `${this.videoWidth}px`,
+ height: `${this.videoHeight}px`
+ }
+ },
+ },
+ beforeUnmount(){
+ this.closeCamera()
+ },
+ mounted () {
+ this.initNavigatorMedia()
+ this.openCamera()
+ },
+ methods: {
+ initNavigatorMedia: function(){
+ // 鑾峰彇濯掍綋灞炴�э紝鏃х増鏈祻瑙堝櫒鍙兘涓嶆敮鎸乵ediaDevices锛岃缃竴涓┖瀵硅薄
+ if (navigator.mediaDevices === undefined) {
+ navigator.mediaDevices = {};
+ }
+ // 浣跨敤getUserMedia锛屽洜涓哄畠浼氳鐩栫幇鏈夌殑灞炴�с��
+ // 杩欓噷锛屽鏋滅己灏慻etUserMedia灞炴�э紝灏辨坊鍔犲畠銆�
+ if (navigator.mediaDevices.getUserMedia === undefined) {
+ this.initGetUserMedia()
+ }
+ },
+ initGetUserMedia: function(){
+ navigator.mediaDevices.getUserMedia = (constraints) => {
+ // 棣栧厛鑾峰彇鐜板瓨鐨刧etUserMedia(濡傛灉瀛樺湪)
+ let getUserMedia =
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.getUserMedia;
+ // 鏈変簺娴忚鍣ㄤ笉鏀寔锛屼細杩斿洖閿欒淇℃伅
+ // 淇濇寔鎺ュ彛涓�鑷�
+ if (!getUserMedia) {//涓嶅瓨鍦ㄥ垯鎶ラ敊
+ return Promise.reject(
+ new Error("getUserMedia is not implemented in this browser")
+ );
+ }
+ // 鍚﹀垯锛屼娇鐢≒romise灏嗚皟鐢ㄥ寘瑁呭埌鏃х殑navigator.getUserMedia
+ return new Promise((resolve, reject) => {
+ getUserMedia.call(navigator, constraints, resolve, reject);
+ });
+ };
+ },
+ openCamera: function(){ // 鎵撳紑鎽勫儚澶�
+ navigator.mediaDevices.getUserMedia({
+ audio: false,
+ video: {
+ width: this.videoWidth,
+ height: this.videoHeight,
+ }
+ }).then((stream) => {
+ this.isCameraOpening = true
+ this.mediaStream = stream
+ this.initVideoSrcObject()
+ }).catch(err => {
+ console.log(`${err.name}锛�${err.message}`);
+ this.closeCamera()
+ this.mediaErrorHandler(err)
+ });
+ },
+ closeCamera() { // 鍏抽棴鎽勫儚澶�
+ if (!this.isCameraOpening) return false
+
+ this.isCameraOpening = false
+
+ if (this.mediaStream) {
+ let tracks = this.mediaStream.getTracks()
+ tracks.forEach(track => track.stop());
+ }
+
+ if (this.mediaRecorder) this.mediaRecorder.stop();
+ this.$emit('close')
+ },
+ initVideoSrcObject: function(){ // 鍒濆鍖� 瑙嗛褰曞埗 鐨� video srcObject
+ // 鏃х殑娴忚鍣ㄥ彲鑳芥病鏈塻rcObject
+ if ("srcObject" in this.$refs.videoEl) {
+ this.$refs.videoEl.srcObject = this.mediaStream;
+ this.initVideoMoveListener()
+ } else {
+ console.log('娴忚鍣ㄤ笉鏀寔')
+ }
+ },
+ initVideoMoveListener: function(){ // 鍒濆鍖� 瑙嗛绐楀彛 鐨� 绉诲姩浜嬩欢
+ let eventState = {}
+ let startMoving = (e) => { // 寮�濮嬬Щ鍔ㄧ殑鍥炶皟
+ e.preventDefault()
+ e.stopPropagation()
+ eventState = {
+ left: this.$refs.cameraBox.offsetLeft,
+ top: this.$refs.cameraBox.offsetTop,
+ x: e.clientX,
+ y: e.clientY
+ }
+ document.addEventListener('mousemove', moving)
+ document.addEventListener('mouseup', endMoving)
+ }
+ let moving = (e) => { // 绉诲姩鐨勫洖璋�
+ e.preventDefault()
+ e.stopPropagation()
+
+ let margin = 10
+ let left = e.clientX - (eventState.x - eventState.left)
+ let top = e.clientY - (eventState.y - eventState.top)
+ let maxLeft = document.documentElement.clientWidth - this.videoWidth - margin
+ let maxTop = document.documentElement.clientHeight - this.videoHeight - margin
+
+ // 闄愬埗绉诲姩鎴浘鍖哄煙涓嶈兘绉诲埌鍙鍖哄煙澶�
+ if (left < 10) left = margin
+ if (top < 10) top = margin
+ if (left > maxLeft) left = maxLeft
+ if (top > maxTop) top = maxTop
+
+ this.$refs.cameraBox.style.left = `${left}px`
+ this.$refs.cameraBox.style.top = `${top}px`
+ }
+ let endMoving = (e) => { // 缁撴潫绉诲姩鐨勫洖璋�
+ e.preventDefault()
+ document.removeEventListener('mousemove', moving)
+ document.removeEventListener('mouseup', endMoving)
+ }
+ this.$refs.cameraBox.addEventListener('mousedown', startMoving)
+ },
+ initUploadChunk: async function(blobs){ // 鍒濆鍖栦笂浼犱换鍔�
+ let tmpBlob = new Blob(blobs, { 'type': this.recorderOptions.mimeType });
+ // let file = new File(blobs, fileName, { type: 'video/webm' });
+ let file = null
+ try {
+ // 瑙e喅 Webm 瑙嗛 duration 闂
+ file = await fixWebmMetaInfo(tmpBlob)
+ } catch (error) {
+ file = tmpBlob
+ console.log(error)
+ }
+
+ let path = await uploadChunk(file, '瀛︿範瑙嗛', 'webm')
+ if (!path) return false
+
+ if (this.recordingFlag || this.recordCameraFlag) {
+ this.$store.commit('authCamera/recordVideoUrl', path)
+ this.$store.commit('authCamera/faceCaptureAll', { flag: false, imgList: this.faceCaptureAllImgList })
+ } else {
+ this.$store.commit('authCamera/biopsyVideoUrl', path)
+ }
+ },
+ startRecordImage() { // 寮�濮嬪綍鍒跺浘鍍忥紙鎷嶇収锛�
+ if (!this.$refs.canvasEl) return false
+
+ this.drawImage()
+ // this.closeCamera()
+ },
+ drawImage: function(){ // 缁樺埗鍥剧墖
+ this.$refs.canvasEl.getContext('2d').drawImage(
+ this.$refs.videoEl,
+ 0,
+ 0,
+ this.videoWidth,
+ this.videoHeight
+ );
+ this.uploadBase64()
+ },
+ async uploadBase64(){ // 涓婁紶鍥剧墖
+ let base64 = this.$refs.canvasEl.toDataURL("image/png", 1);
+ // const url = await uploadByBase64(base64, '鏍搁獙鐓х墖')
+ if (base64) {
+ this.$emit('handlerSuccess', base64)
+ this.closeCamera()
+ } else {
+ this.$message.error('鎷嶆憚澶辫触')
+ }
+ },
+ uploadErrorHandler: function (evt) { // 涓婁紶澶辫触鍥炶皟
+ console.log(JSON.stringify(evt.target))
+ },
+ mediaErrorHandler: function(){ // 寮�鍚憚鍍忓ご鎴栧綍鍒跺睆骞曞紓甯稿鐞�
+ this.$message.error('褰撳墠娴忚鍣ㄤ笉鏀寔锛岃鏇存崲娴忚鍣�')
+ },
+ }
+};
+</script>
+
+<style lang="scss" scoped>
+.camera_box{
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ z-index: 300;
+}
+.status_box{
+ position: absolute;
+ left: 0;
+ right: 0;
+ padding: 6px;
+ margin-top: -38px;
+ font-size: 0.8rem;
+ color: white;
+ background: rgba(0,0,0,0.4);
+ .normal > span,
+ .abnormal > span{
+ width: 6px;
+ height: 6px;
+ border-radius: 28px;
+ display: inline-block;
+ margin-right: 5px;
+ }
+ .normal > span{
+ background-color: #00E63C;
+ }
+ .abnormal > span{
+ background-color: #FF3232;
+ }
+}
+.btn_box{
+ background: rgba(0,0,0,0.4);
+ margin-top: -63px;
+ padding-top: 10px;
+ padding-bottom: 7px;
+ position: absolute;
+ left: 0;
+ right: 0;
+}
+.duration{
+ position: absolute;
+ top: 0;
+ right: 0;
+ color: white;
+ text-shadow: 1px 1px 1px black, 1px -1px 1px black, -1px -1px 1px black, -1px 1px 1px black;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/h5/faceAuth/index.vue b/src/views/h5/faceAuth/index.vue
new file mode 100644
index 0000000..c7635b9
--- /dev/null
+++ b/src/views/h5/faceAuth/index.vue
@@ -0,0 +1,199 @@
+<template>
+ <div class="p-7 py-4 face">
+ <div>
+ <el-row class="px-7" justify="space-between">
+ <el-text class="text-lg">濮撳悕锛�<span class="font-bold">{{ userInfo.name }}</span></el-text>
+ <el-text class="text-lg">韬唤璇佸熬鍙凤細<span class="font-bold">{{ userInfo.idCard?.slice(-4) }}</span></el-text>
+ </el-row>
+ <el-row justify="center">
+ <el-image :src="$getImageUrl(`/h5/face_default.png`)" style="width: 300px;"></el-image>
+ </el-row>
+ <!-- <video id="video" width="400" height="300" autoplay></video> -->
+ <el-row justify="center">
+ <el-text class="text-xl">璇锋媿鎽勭収鐗囧畬鎴愪汉鑴歌璇�</el-text>
+ </el-row>
+ <el-row class="my-7">
+ <el-text class="text-info text-center">
+ 鎻愮ず锛氫汉鑴搁獙璇佸皢浼氳繘琛岀郴缁熷鏍革紝涓虹‘淇濇偍鑳藉揩閫熼�氳繃楠岃瘉锛岃鍕胯。鐫�鏆撮湶锛岄厤鍚堢郴缁熸寚绀哄畬鎴愰獙璇併��
+ </el-text>
+ </el-row>
+ <el-row class="pt-7">
+ <el-text class="text-xl">鎷嶆憚椤荤煡</el-text>
+ </el-row>
+ <el-row justify="space-between" class="mt-3">
+ <div v-for="(tip,index) in tipItems" :key="`tip${index}`">
+ <el-image :src="$getImageUrl(`/h5/face_tip_${index+1}.png`)" style="width: 70px;"></el-image>
+ </div>
+ </el-row>
+ </div>
+ <el-row justify="center" class="mb-7">
+ <el-button @click="startCapture" type="primary" style="width: 100%;" size="large">寮�濮嬫媿鎽�</el-button>
+ </el-row>
+
+ <camera
+ v-if="openCameraFlag"
+ @close="openCameraFlag=false"
+ @handlerSuccess="shootSuccess"
+ ></camera>
+
+ <auditDialog
+ v-model="auditDialogFlag"
+ :base64="base64"
+ @handlerSuccess="auditSuccess"
+ >
+ </auditDialog>
+ </div>
+</template>
+
+<script>
+import camera from '@/views/h5/faceAuth/components/camera.vue';
+import {isWeixin} from '@/utils/UA.js'
+import auditDialog from '@/views/h5/faceAuth/components/auditDialog.vue';
+import { useSessionStore } from '@/stores/session.js'
+import { storeToRefs } from 'pinia';
+export default {
+ components: {
+ camera,
+ auditDialog
+ },
+ setup() {
+ const { userInfo } = storeToRefs(useSessionStore())
+ return { userInfo }
+ },
+ data() {
+ return {
+ tipItems: [
+ { label: '鏍囧噯鎷嶆憚', isCheck: true },
+ { label: '鏍囧噯鎷嶆憚', isCheck: true },
+ { label: '鏍囧噯鎷嶆憚', isCheck: true },
+ { label: '鏍囧噯鎷嶆憚', isCheck: true },
+ ],
+ openCameraFlag: false,
+ base64: '',
+ auditDialogFlag: false
+ }
+ },
+ computed: {
+ getSigninButtonStyle() {
+ if (this.positionStatus == 'success') {
+ return {
+ 'background': '#66d06c',
+ 'border-color': '#e7f7eb'
+ }
+ } else {
+ return {
+ 'background': '#e1e1e1',
+ 'border-color': '#f8f8f8'
+ }
+ }
+ }
+ },
+ async mounted() {
+ this.currentTimeText = this.$dayjs().format('HH:mm')
+ },
+ methods: {
+ getUserPositionStatus(evt) {
+ this.userPositionStatus = evt
+ },
+ startCapture() {
+ if (isWeixin) {
+ console.log('')
+ } else {
+ this.openCameraFlag = true
+ }
+ },
+ shootSuccess(evt) {
+ this.base64 = evt
+ if (this.base64) {
+ 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 }})
+ }
+ },
+ getIsSignup() {
+ return Boolean(localStorage.getItem('isSignup'))
+ }
+ }
+}
+</script>
+<style lang="scss" scoped>
+.face {
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ max-width: 700px;
+ margin: 0 auto;
+ overflow: auto;
+}
+.mapBox {
+ width: 300px;
+ height: 300px;
+ border-radius: 150px;
+}
+.mask {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 300px;
+ height: 340px;
+ background: radial-gradient(circle at center, transparent 0px, white 160px);
+ z-index: 12; /* 浣嶄簬绾㈣壊鐩掑瓙涓婃柟 */
+}
+.center-sign {
+ z-index: 13;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+.text-sign {
+ z-index: 13;
+ position: absolute;
+ left: 50%;
+ top: 66%;
+ white-space: nowrap;
+ transform: translate(-50%, -50%);
+}
+.signin-button {
+ width: 140px;
+ height: 140px;
+ border-radius: 70px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ border: 16px solid
+}
+.ripple {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ background-color: rgba(255, 255, 255, 0.5);
+ transform: scale(0);
+ animation: ripple 2s ease-out infinite;
+ pointer-events: none; /* 闃叉娉㈢汗閬尅鎸夐挳鐐瑰嚮 */
+ z-index: 14;
+}
+/* 纭繚鎸夐挳鏂囧瓧鏄剧ず鍦ㄦ尝绾逛笂鏂� */
+.signin-button .el-row {
+ position: relative;
+ z-index: 2;
+}
+@keyframes ripple {
+ to {
+ transform: scale(2);
+ opacity: 0;
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/h5/index.vue b/src/views/h5/index.vue
new file mode 100644
index 0000000..a4c46f9
--- /dev/null
+++ b/src/views/h5/index.vue
@@ -0,0 +1,35 @@
+<template>
+ <div>
+ <router-view></router-view>
+ </div>
+</template>
+<script>
+import { useSessionStore } from '@/stores/session.js'
+export default {
+ setup() {
+ const { setUserInfo } = useSessionStore()
+ return { setUserInfo }
+ },
+ data() {
+ return {}
+ },
+ async created() {
+ await 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()
+ })
+ })
+ },
+ }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/h5/login/index.vue b/src/views/h5/login/index.vue
index befa9ae..b9cd759 100644
--- a/src/views/h5/login/index.vue
+++ b/src/views/h5/login/index.vue
@@ -1,5 +1,5 @@
<template>
- <div class="login">
+ <div class="login" v-if="loginType == 'mobilePhone'">
<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" />
@@ -32,9 +32,16 @@
</template>
<script>
import { tokenUtils } from '@/utils/axios.js';
+import { useLoginStore } from '@/stores/login.js'
+import { isWeixin } from '@/utils/UA.js'
export default {
+ setup() {
+ const { lastRouteInfo } = useLoginStore()
+ return { lastRouteInfo }
+ },
data() {
return {
+ loginType: '', //mobile銆亀eixin
form: {
mobile: '',
code: '',
@@ -47,6 +54,8 @@
},
created() {
tokenUtils.clearTokens()
+ this.loginType = isWeixin ? 'weixin' : 'mobilePhone'
+ this.loginType = 'mobile'
},
computed: {
appId() {
@@ -104,9 +113,12 @@
if (res.data.code == 0) {
const resData = res.data.data
tokenUtils.setTokens(resData.accessToken, resData.refreshToken)
- this.$router.replace({ path: '/h5/verify', query: { appId: this.appId } })
+ this.$message.success('鐧诲綍鎴愬姛')
+ if (this.lastRouteInfo.name) {
+ this.$router.replace(this.lastRouteInfo)
+ }
} else {
- this.$message.error(res.data.msg)
+ this.$message.error(res.data.msg || '鐧诲綍澶辫触')
}
}).finally(() => {
this.loginLoading = false
diff --git a/src/views/h5/signup/BaiduMap.vue b/src/views/h5/signup/BaiduMap.vue
index e1930ac..bc3b59f 100644
--- a/src/views/h5/signup/BaiduMap.vue
+++ b/src/views/h5/signup/BaiduMap.vue
@@ -32,8 +32,6 @@
});
return baiduMapPromise;
}
-import { getCurrentPosition } from '@/utils/tool.js'
-
export default {
name: 'BaiduMap',
props: {
@@ -78,6 +76,7 @@
map.enableScrollWheelZoom();
this.map = map;
this.$emit('ready', map);
+ this.$emit('getMapStatus', 'success')
this.getUserPosition()
},
async getUserPosition() {
diff --git a/src/views/h5/signup/index.vue b/src/views/h5/signup/index.vue
index 3d549eb..7a6f83d 100644
--- a/src/views/h5/signup/index.vue
+++ b/src/views/h5/signup/index.vue
@@ -2,8 +2,8 @@
<div class="p-4 signin">
<div>
<el-row class="px-7" justify="space-between">
- <el-text class="text-lg">濮撳悕锛�<span class="font-bold">寮犱笁</span></el-text>
- <el-text class="text-lg">韬唤璇佸熬鍙凤細<span class="font-bold">8888</span></el-text>
+ <el-text class="text-lg">濮撳悕锛�<span class="font-bold">{{ userInfo.name }}</span></el-text>
+ <el-text class="text-lg">韬唤璇佸熬鍙凤細<span class="font-bold">{{ userInfo.idCard?.slice(-4) }}</span></el-text>
</el-row>
<el-row justify="center" class="m-4">
<div style="position: relative;">
@@ -13,7 +13,7 @@
:center="centerPoint"
@getUserPositionStatus="(evt) => userPositionStatus = evt"
@getMapStatus="(evt) => mapStatus = evt"
- @getDistance="(evt) => distance = evt"
+ @getDistance="getDistance"
/>
</div>
<div class="center-sign">
@@ -37,18 +37,18 @@
</div>
</div>
</el-row>
- <el-row justify="center">
+ <!-- <el-row justify="center">
<el-text class="text-lg font-bold">{{ positionName }}</el-text>
- </el-row>
+ </el-row> -->
<el-row justify="center" class="mt-1">
<el-text class="text-lg text-info">{{ positionAddress }}</el-text>
</el-row>
</div>
<el-row justify="center" class="mt-7">
- <div class="signin-button" :style="getSigninButtonStyle">
+ <div class="signin-button" :style="getSigninButtonStyle" @click="signinConfirm()">
<span class="ripple" v-if="!positionError"></span>
<el-row justify="center">
- <el-text class="text-lg text-white">绛惧埌</el-text>
+ <el-text class="text-lg text-white">绛惧埌</el-text>
</el-row>
<el-row justify="center" class="mt-1">
<el-text class="text-lg text-white">{{ currentTimeText }}</el-text>
@@ -60,29 +60,37 @@
<script>
import BaiduMap from '@/views/h5/signup/BaiduMap.vue'
+import { useSessionStore } from '@/stores/session.js'
+import { storeToRefs } from 'pinia';
export default {
components: {
BaiduMap
+ },
+ setup() {
+ const { userInfo } = storeToRefs(useSessionStore())
+ return { userInfo }
},
data() {
return {
userPositionStatus: 'loading', // loading銆乫ail銆乻uccess
mapStatus: 'loading', //loading銆乫ail銆乻uccess
- distance: 0,
+ distance: null,
positionError: false,
- failMsg: '瓒呭嚭绛惧埌鑼冨洿锛屼笉鍙鍒�',
- positionName: '涓囩浜戝煄',
+ positionName: '',
positionAddress: '鍗楀北鍖烘墦鐭充簩璺崡118鍙�',
currentTimeText: '',
centerPoint: {
- lat:22.580372,
+ lat: 22.580372,
lng: 113.946530
}
}
},
computed: {
+ canSignup() {
+ return this.mapStatus == 'success' && this.userPositionStatus == 'success' && !this.positionError
+ },
getSigninButtonStyle() {
- if (this.positionStatus == 'success') {
+ if (this.canSignup) {
return {
'background': '#66d06c',
'border-color': '#e7f7eb'
@@ -93,21 +101,67 @@
'border-color': '#f8f8f8'
}
}
+ },
+ appId() {
+ return this.$route.query.appId
}
+ },
+ created() {
+ this.getSignupAddress()
},
async mounted() {
this.currentTimeText = this.$dayjs().format('HH:mm')
},
methods: {
+ getSignupAddress() {
+ const params = { applicationId: this.appId }
+ 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
+ // }
+ this.positionAddress = resData.examSite?.address
+ } else {
+ this.$message.error(res.data.msg)
+ }
+ })
+ },
getUserPositionStatus(evt) {
this.userPositionStatus = evt
+ },
+ getDistance(evt) {
+ this.distance = evt
+ if (this.distance && this.distance <= 500) {
+ this.positionError = false
+ } else {
+ this.positionError = true
+ }
+ },
+ signinConfirm() {
+ if (!this.canSignup) {
+ return
+ }
+ this.$message.success('绛惧埌鎴愬姛')
+ localStorage.setItem('isSignup', true)
+ setTimeout(() => {
+ if (this.getIsFace()) {
+ this.$router.replace({ path: '/h5/face', query: { appId: this.appId }})
+ } else {
+ this.$router.replace({ path: '/h5/verForm', query: { appId: this.appId }})
+ }
+ }, 500)
+ },
+ getIsFace() {
+ return Boolean(localStorage.getItem('isFace'))
}
}
}
</script>
<style lang="scss" scoped>
.signin {
- height: 100%;
+ height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
diff --git a/src/views/h5/verify/form.vue b/src/views/h5/verify/form.vue
index 8e2491d..8409462 100644
--- a/src/views/h5/verify/form.vue
+++ b/src/views/h5/verify/form.vue
@@ -1,34 +1,45 @@
<template>
- <div>
+ <div v-if="pdfUrl">
<el-row class="p-3 m-0" justify="space-between" align="middle">
- <el-col :span="3"></el-col>
- <el-col :span="18">
- <el-text class="text-lg font-bold text-center">
- {{ title }}
- </el-text>
+ <el-col :span="4"></el-col>
+ <el-col :span="16">
+ <el-row justify="center">
+ <el-text class="text-lg font-bold text-center">
+ {{ title }}
+ </el-text>
+ </el-row>
</el-col>
- <el-col :span="3">
- <el-button text style="color: var(--el-color-primary);" :loading="saveLoading" @click="tempSave()">鏆傚瓨</el-button>
+ <el-col :span="4">
+ <el-row justify="center">
+ <el-button
+ v-if="!isVerified"
+ text style="color: var(--el-color-primary);"
+ :loading="saveLoading" @click="tempSave()"
+ class="mx-4"
+ >
+ 鏆傚瓨
+ </el-button>
+ </el-row>
</el-col>
</el-row>
<el-divider class="m-0" style="flex-shrink: 0;"></el-divider>
- <el-scrollbar :height="`${mainHeight}px`" class="p-2 m-0 mt-1" min-size="none">
+ <el-scrollbar :height="`${mainHeight}px`" class="p-2 m-0 mt-1" >
<div v-if="pdfUrl" :style="{width: '100%', height: `${mainHeight - 100}px`}">
<PdfPreview v-if="pdfUrl" :url="pdfUrl"></PdfPreview>
</div>
<div class="p-2 my-4">
<el-form ref="verifyForm" :model="form">
- <el-form-item label="浠ヤ笂鐢虫姤鍐呭鏄惁灞炲疄" prop="isVerified">
- <el-radio-group v-model="form.isVerified">
- <el-radio :value="true">鏄�</el-radio>
- <el-radio :value="false">鍚�</el-radio>
+ <el-form-item label="*浠ヤ笂鐢虫姤鍐呭鏄惁灞炲疄" prop="isVerified">
+ <el-radio-group v-model="form.isContentTrue" :disabled="isVerified">
+ <el-radio :value="1">鏄�</el-radio>
+ <el-radio :value="0">鍚�</el-radio>
</el-radio-group>
</el-form-item>
- <el-form-item label="璇ヨ�冪偣鏍搁獙鏄惁閫氳繃" prop="isPass">
- <el-radio-group v-model="form.isPass">
- <el-radio :value="true">鏄�</el-radio>
- <el-radio :value="false">鍚�</el-radio>
+ <el-form-item label="*璇ヨ�冪偣鏍搁獙鏄惁閫氳繃" prop="isPass">
+ <el-radio-group v-model="form.isSitePass" :disabled="isVerified">
+ <el-radio :value="1">鏄�</el-radio>
+ <el-radio :value="0">鍚�</el-radio>
</el-radio-group>
</el-form-item>
<el-row><el-text>涓撳璇勪及鎰忚</el-text></el-row>
@@ -38,16 +49,23 @@
:rows="3"
type="textarea"
placeholder="璇峰~鍐欒瘎浼版剰瑙�"
+ :disabled="isVerified"
/>
</el-form-item>
- <el-row><el-text>鐜板満宸ヤ綔鐓х墖</el-text></el-row>
+ <el-row><el-text>*鐜板満宸ヤ綔鐓х墖</el-text></el-row>
<el-row>
- <UploadBtn v-model="form.image" :accept="['pdf', 'jpg']" :limitFileCount="10" listType="picture-card"></UploadBtn>
+ <UploadBtn v-model="form.image" :disabled="isVerified" :accept="['pdf', 'jpg']" :limitFileCount="10" listType="picture-card"></UploadBtn>
</el-row>
- <Signature v-model="form.signature"></Signature>
+ <Signature v-model="form.signatureUrl" :disabled="isVerified" :isRequire="true"></Signature>
- <el-button type="primary" size="large" class="my-7" style="width: 100%;">鎻愪氦鏍搁獙缁撴灉</el-button>
+ <el-button
+ v-if="!isVerified"
+ @click="submitVerify"
+ type="primary" size="large"
+ class="my-7" style="width: 100%;"
+ :loading="submitLoading"
+ >鎻愪氦鏍搁獙缁撴灉</el-button>
</el-form>
</div>
</el-scrollbar>
@@ -59,7 +77,6 @@
import Signature from '@/views/main/components/Signature.vue';
import { useSessionStore } from '@/stores/session.js'
import { storeToRefs } from 'pinia';
-import { tokenUtils } from '@/utils/axios.js';
export default {
components: {
@@ -77,13 +94,15 @@
pdfUrl: '',
form: {
id: '',
- isVerified: false,
- isPass: false,
+ isContentTrue: 0,
+ isSitePass: 0,
suggestion: '',
image: [],
- signature: ''
+ signatureUrl: ''
},
- saveLoading: false
+ isVerified: false,
+ saveLoading: false,
+ submitLoading: false
}
},
computed: {
@@ -91,7 +110,7 @@
return this.pageHeight - 80
},
appId() {
- return this.$route.params.id
+ return this.$route.query.appId
}
},
async created() {
@@ -99,18 +118,24 @@
},
mounted() {
document.title = '鑰冪偣鏍搁獙'
- this.pdfUrl = this.$qxueyou.qxyRes + '20260304/鍖犲績瀛﹂櫌鑱屼笟鎶�鑳借瘎浠疯�冪偣鏍搁獙鐢宠琛╛1772591122177.pdf'
},
methods: {
getVerifyDetail() {
- const params = { id: this.appId }
- this.$axios.get('/exam/verify-record/get', { params }).then(res => {
+ const params = { applicationId: this.appId }
+ 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.title = resData.organizationName + '-' + resData.examSite.siteName + '鑰冪偣鏍搁獙'
if (resData.id) {
- this.form = {
- ...resData
- }
+ this.form.isContentTrue = resData.isContentTrue
+ this.form.isSitePass = resData.isSitePass
+ this.form.suggestion = resData.evaluationOpinion
+ resData.sitePhotos?.forEach(ele => {
+ this.form.image.push({ name: '', url: ele })
+ })
+ this.form.signatureUrl = resData.signatureUrl
+ this.isVerified = resData.isVerified
}
} else {
this.$message.error('鑾峰彇鏍搁獙淇℃伅澶辫触')
@@ -119,25 +144,62 @@
},
tempSave() {
const data = {
+ applicationId: this.appId,
id: this.form.id,
- userId: 0,
- name: "",
- gender: "",
- mobile: "",
- age: 0,
- idNumber: "",
- isContentTrue: 0,
- isSitePass: 0,
- evaluationOpinion: "",
- sitePhotos: [],
- status: 0
+ userId: this.userInfo.id,
+ name: this.userInfo.name,
+ mobile: this.userInfo.mobile,
+ idNumber: this.userInfo.idCard,
+ isContentTrue: this.form.isContentTrue,
+ isSitePass: this.form.isSitePass,
+ evaluationOpinion: this.form.suggestion,
+ sitePhotos: this.form.image.map(ele => ele.url),
+ signatureUrl: this.form.signatureUrl,
}
- this.$axios.post('/exam/verify-record/create', data).then(res => {
+ this.saveLoading = true
+ this.$axios.put(`/exam/verify-record/save`, data).then(res => {
if (res.data.code == 0) {
- console.log(res.data.data)
+ this.$message.success('淇濆瓨鎴愬姛')
+ this.getVerifyDetail()
} else {
this.$message.error(res.data.msg)
}
+ }).finally(() => {
+ this.saveLoading = false
+ })
+ },
+ submitVerify() {
+ if (this.form.image.length==0) {
+ this.$message.error('璇蜂笂浼犵幇鍦哄伐浣滅収鐗�')
+ return
+ }
+ if (!this.form.signatureUrl) {
+ this.$message.error('璇峰~鍐欑鍚�')
+ return
+ }
+ this.$messageBox.confirm('鎻愪氦涔嬪悗涓嶅彲鍐嶇紪杈戞牳楠岋紝纭鎻愪氦鍚�', '鎻愮ず',
+ { confirmButtonText: '纭畾', cancelButtonText: '鍙栨秷', type: 'tip' }).then(res => {
+ if (res == 'confirm') {
+ this.submitLoading = true
+ const data = {
+ applicationId: this.appId,
+ isContentTrue: this.form.isContentTrue,
+ isSitePass: this.form.isSitePass,
+ evaluationOpinion: this.form.suggestion,
+ sitePhotos: this.form.image.map(ele => ele.url),
+ signatureUrl: this.form.signatureUrl,
+ }
+ this.$axios.post('/exam/verify-record/verify', data).then(res => {
+ if (res.data.code == 0) {
+ this.$message.success('鎻愪氦鏍搁獙鎴愬姛')
+ this.isVerified = true
+ } else {
+ this.$message.error(res.data.msg || '鎻愪氦鏍搁獙澶辫触')
+ }
+ }).finally(() => {
+ this.submitLoading = false
+ })
+ }
})
},
onPagesLoaded(msg) {
diff --git a/src/views/h5/verify/index.vue b/src/views/h5/verify/index.vue
index 7d67685..02e2d30 100644
--- a/src/views/h5/verify/index.vue
+++ b/src/views/h5/verify/index.vue
@@ -1,6 +1,5 @@
<template>
- <div>
- </div>
+ <div></div>
</template>
<script>
import { tokenUtils } from '@/utils/axios.js';
@@ -8,9 +7,7 @@
export default {
components: {},
data() {
- return {
-
- }
+ return {}
},
computed: {
query() {
@@ -21,17 +18,17 @@
}
},
async created() {
- if (tokenUtils.getAccessToken()) {
- const canVerify = await this.getCanVerify()
- if (canVerify) {
- this.$router.replace(`/h5/verForm/${this.appId}`)
- }
- // else {
- // this.$router.replace('/h5/noVerAccess')
- // }
- } else {
- this.$message.error('璇峰厛鐧诲綍')
- this.$router.replace({ path: '/h5/login', query: { appId: this.appId } })
+ 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 {
+ this.$router.replace('/h5/noVerAccess')
}
},
mounted() {
@@ -54,6 +51,12 @@
})
})
},
+ getIsFace() {
+ return Boolean(localStorage.getItem('isFace'))
+ },
+ getIsSignup() {
+ return Boolean(localStorage.getItem('isSignup'))
+ }
}
}
</script>
\ No newline at end of file
diff --git a/src/views/main/components/Signature.vue b/src/views/main/components/Signature.vue
index 7d1bf71..cf900d7 100644
--- a/src/views/main/components/Signature.vue
+++ b/src/views/main/components/Signature.vue
@@ -1,11 +1,17 @@
<template>
<div class="signature">
<el-row justify="space-between">
- <el-text>绛惧悕</el-text>
- <el-text v-if="imageUrl" @click="imageUrl=''">娓呴櫎绛惧悕</el-text>
- <el-text v-else @click="signatureDialog=true">鐐瑰嚮绛惧悕</el-text>
+ <el-text>{{isRequire?'*':''}}绛惧悕</el-text>
+ <template v-if="!disabled">
+ <el-text v-if="imageUrl" @click="imageUrl=''">娓呴櫎绛惧悕</el-text>
+ <el-text v-else @click="signatureDialog=true">鐐瑰嚮绛惧悕</el-text>
+ </template>
</el-row>
- <el-image v-if="imageUrl" :src="imageUrl"></el-image>
+ <el-image
+ v-if="imageUrl"
+ style="width: 100%;"
+ :src="imageUrl.includes('http') ? imageUrl : $qxueyou.qxyRes + imageUrl">
+ </el-image>
<div v-else class="image-slot"></div>
<el-dialog
@@ -68,6 +74,14 @@
modelValue: {
type: String,
default: ''
+ },
+ isRequire: {
+ type: Boolean,
+ default: false
+ },
+ disabled: {
+ type: Boolean,
+ default: false
}
},
computed: {
@@ -193,9 +207,10 @@
}
let base64 = this.editCanvas.toDataURL('image/png', 1)
let smallBase64 = await this.resizedataURL(base64, 240, 80)
- // let url = await uploadByBase64(smallBase64, '绛惧悕')
- // if (!url) return false
- this.imageUrl = smallBase64
+ let url = await uploadByBase64(smallBase64, '绛惧悕')
+ if (!url) return false
+ this.imageUrl = url
+ this.$emit('update:modelValue', url)
this.signatureDialog = false
},
resizedataURL: function(base64, wantedWidth, wantedHeight){
diff --git a/src/views/main/components/UploadBtn.vue b/src/views/main/components/UploadBtn.vue
index 0df0c52..7e92fdd 100644
--- a/src/views/main/components/UploadBtn.vue
+++ b/src/views/main/components/UploadBtn.vue
@@ -38,7 +38,7 @@
<template v-if="listType=='picture-card'">
<el-image
ref="previewImg"
- :src="file.url"
+ :src="file.url.includes('http') ? file.url : $qxueyou.qxyRes + file.url"
:initial-index="initialPreviewImgIndex"
:preview-src-list="filterPreviewImgList"
>
@@ -177,7 +177,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 d18ddc1..1837faf 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -14,6 +14,7 @@
},
},
server: {
+ host: '0.0.0.0',
proxy: {
'/app-api': {
target: 'http://101.43.143.75:48180', // dev
--
Gitblit v1.8.0