| | |
| | | { |
| | | "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", |
| | |
| | | "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" |
| | | }, |
| | |
| | | "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", |
| | |
| | | "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" |
| | | }, |
| | |
| | | </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> |
| | |
| | | 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 |
| | |
| | | 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: '/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 |
| | |
| | | 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), |
| | |
| | | }) |
| | | |
| | | router.beforeEach((to, from, next) => { |
| | | const { setLastRouteInfo } = useLoginStore() |
| | | if (!to.matched.length) { |
| | | if (to.path === '/') { |
| | | next({ path: '/main/home' }) |
| | |
| | | next({ path: '/error/404', query: { errorUrl: to.path } }) |
| | | } |
| | | } else { |
| | | if (from.name) { |
| | | setLastRouteInfo(from) |
| | | } |
| | | next() |
| | | } |
| | | }) |
| | |
| | | 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) |
| | | |
| | |
| | | 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) { |
| | |
| | | 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中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 |
| | | nonceStr: result.nonceStr, // 支付签名随机串,不长于 32 位 |
| | | package: result.packageValue, // 统一支付接口返回的prepay_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(){ |
| | |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | function getLocalImgData(localId){ |
| | | return new Promise((resolve) => { |
| | | wx.getLocalImgData({ |
| | |
| | | }) |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 预览图片 |
| | | * @param {*} url |
| | | * @param {*} urlList |
| | | */ |
| | | function previewImage(current, urls){ |
| | | wx.previewImage({ |
| | | current: current, // 当前显示图片的http链接 |
| | | 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 |
| | | } |
| | |
| | | data() { |
| | | return { |
| | | dialogFlag: false, |
| | | status: '' |
| | | status: '', |
| | | url: '' |
| | | } |
| | | }, |
| | | props: { |
| | |
| | | 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 || "人脸比对失败") |
| | |
| | | }, |
| | | handlerSuccess() { |
| | | this.dialogFlag = false |
| | | this.$emit('handlerSuccess') |
| | | this.$emit('handlerSuccess', this.url) |
| | | } |
| | | } |
| | | } |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { uploadByBase64 } from '@/utils/tool.js' |
| | | export default { |
| | | data () { |
| | | return { |
| | |
| | | }, |
| | | async uploadBase64(){ // 上传图片 |
| | | let base64 = this.$refs.canvasEl.toDataURL("image/png", 1); |
| | | // const url = await uploadByBase64(base64, '核验照片') |
| | | if (base64) { |
| | | this.$emit('handlerSuccess', base64) |
| | | this.closeCamera() |
| | |
| | | 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, |
| | |
| | | data() { |
| | | return { |
| | | tipItems: [ |
| | | { label: '标准拍摄', isCheck: true }, |
| | | { label: '标准拍摄', isCheck: true }, |
| | | { label: '标准拍摄', isCheck: true }, |
| | | { label: '标准拍摄', isCheck: true }, |
| | | { label: '标准拍摄' }, |
| | | { label: '遮挡脸部' }, |
| | | { label: '拍摄不全' }, |
| | | { label: '光线不足' }, |
| | | ], |
| | | openCameraFlag: false, |
| | | base64: '', |
| | |
| | | 'border-color': '#f8f8f8' |
| | | } |
| | | } |
| | | }, |
| | | appId() { |
| | | return this.$route.query.appId |
| | | } |
| | | }, |
| | | async mounted() { |
| | |
| | | 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 |
| | | } |
| | |
| | | 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> |
| | |
| | | <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 || {}) |
| | | } |
| | | }) |
| | | }, |
| | | } |
| New file |
| | |
| | | <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> |
| | |
| | | <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、weixin |
| | | loginType: '', //mobilePhone、weixin |
| | | form: { |
| | | mobile: '', |
| | | code: '', |
| | |
| | | created() { |
| | | tokenUtils.clearTokens() |
| | | this.loginType = isWeixin ? 'weixin' : 'mobilePhone' |
| | | this.loginType = 'mobile' |
| | | if (isWeixin) { |
| | | this.loginType = 'weixin' |
| | | this.$router.replace({ path: '/h5/redirect' }) |
| | | } |
| | | }, |
| | | computed: { |
| | | appId() { |
| | |
| | | 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 || '登录失败') |
| | |
| | | this.loginLoading = false |
| | | }) |
| | | }, |
| | | verify() { |
| | | this.$router.push('/h5/verify') |
| | | }, |
| | | signup() { |
| | | this.$router.push('/h5/signup') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| New file |
| | |
| | | <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> |
| | |
| | | distance: null, |
| | | positionError: false, |
| | | positionName: '', |
| | | positionAddress: '南山区打石二路南118号', |
| | | positionAddress: '', |
| | | currentTimeText: '', |
| | | centerPoint: { |
| | | lat: 22.580372, |
| | | lng: 113.946530 |
| | | lat: 23.135618, |
| | | lng: 113.27077 |
| | | } |
| | | } |
| | | }, |
| | |
| | | }, |
| | | appId() { |
| | | return this.$route.query.appId |
| | | }, |
| | | url() { |
| | | return this.$route.query.url |
| | | } |
| | | }, |
| | | created() { |
| | |
| | | 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) |
| | |
| | | } |
| | | }, |
| | | 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> |
| | |
| | | <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"> |
| | |
| | | <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"> |
| | |
| | | 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 |
| | |
| | | <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() { |
| | |
| | | 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) { |
| | |
| | | }) |
| | | }) |
| | | }, |
| | | 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> |
| | |
| | | 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) |
| | |
| | | }, |
| | | }, |
| | | server: { |
| | | allowedHosts: ['dev.qxueyou.com'], |
| | | host: '0.0.0.0', |
| | | proxy: { |
| | | '/app-api': { |