This commit is contained in:
@jyuhsin
2026-03-03 16:10:29 +08:00
parent d36d909851
commit 6a3393b26f
12 changed files with 718 additions and 48 deletions
@@ -13,7 +13,9 @@ import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.ActionContext;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
@@ -0,0 +1,100 @@
package com.budwk.app.zhgh.activity.trainSignUp.controller.mobile;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import com.aspose.slides.internal.og.add;
import com.budwk.app.base.result.Result;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpCourse;
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUser;
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUserCourse;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @ClassName TrainScanCodeController
* @Author JyuHsin
* @Date 2026/3/3 13:53
* @Version 1.0
* @Description TODO
*/
@IocBean
@Ok("json:full")
@At("/platform/mobile/train/scanCode")
public class TrainScanCodeController {
@Inject
private Dao dao;
@At("")
@Ok("beetl:/platform/zhghh5/activity/trainSignUp/scanCode/index.html")
@SaCheckPermission("h5.trainSignUp.sign")
public void scanCode() {
}
@At
@SaCheckPermission("h5.trainSignUp.sign")
public Result getSignData(@Param("courseId") String courseId) {
TrainSignUpCourse course = dao.fetch(TrainSignUpCourse.class, courseId);
int count = dao.count(TrainSignUpUserCourse.class,
Cnd.where("activityId", "=", course.getActivityId())
.and("userId", "=", SecurityUtil.getUserId())
.and("isAttend", "=", true)
);
Map<String, Integer> resultMap = Map.of("signCount", count);
// 查询用户是有报名记录
TrainSignUpUserCourse userCourse = dao.fetch(TrainSignUpUserCourse.class, Cnd.where("courseId", "=", courseId).and("userId", "=", SecurityUtil.getUserId()));
if (userCourse == null) {
return Result.error(99, "您没有权限签到").addData(resultMap);
}
if (userCourse.isAttend()) {
return Result.error(99, "此点位,您已经签到").addData(resultMap);
}
userCourse.setAttend(true);
userCourse.setAttendTime(DateUtil.date());
dao.update(userCourse);
count = count + 1;
return Result.success("签到成功").addData(Map.of("signCount", count));
}
@At
@SaCheckPermission("h5.trainSignUp.sign")
public Result saveAddress(@Param("courseId") String courseId,
@Param("name") String name,
@Param("tel") String tel,
@Param("province") String province,
@Param("city") String city,
@Param("county") String county,
@Param("addressDetail") String addressDetail) {
TrainSignUpUser signUpUser = dao.fetch(TrainSignUpUser.class, Cnd.where("courseId", "=", courseId).and("userId", "=", SecurityUtil.getUserId()));
List<NutMap> mobileColumnsValue = new ArrayList<>();
Map<String, Object> nameMap = Map.of("columnCode", "name", "columnName", "姓名", "columnValue", name, "columnFormType", "INPUT");
mobileColumnsValue.add(NutMap.WRAP(nameMap));
Map<String, Object> telMap = Map.of("columnCode", "tel", "columnName", "电话", "columnValue", tel, "columnFormType", "INPUT");
mobileColumnsValue.add(NutMap.WRAP(telMap));
String address = province + city + county + addressDetail;
Map<String, Object> addressMap = Map.of("columnCode", "address", "columnName", "收货地址", "columnValue", address, "columnFormType", "INPUT");
mobileColumnsValue.add(NutMap.WRAP(addressMap));
signUpUser.setMobileColumnsValue(mobileColumnsValue);
dao.update(signUpUser);
return Result.success("保存成功");
}
}
@@ -0,0 +1,52 @@
package com.budwk.app.zhgh.activity.trainSignUp.scanHandle;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import com.budwk.app.zhgh.activity.culture.models.ActivityTissue;
import com.budwk.app.zhgh.activity.culture.models.ActivityTissuePerson;
import com.budwk.app.zhgh.activity.trainSignUp.models.TrainSignUpUserCourse;
import com.budwk.app.zhgh.scan.core.ScanHandler;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.ActionContext;
import org.nutz.mvc.view.ForwardView;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @ClassName TrainScanHandle
* @Author JyuHsin
* @Date 2025/12/30 15:43
* @Version 1.0
* @Description TODO
*/
@IocBean
public class TrainScanHandle implements ScanHandler {
@Inject
private Dao dao;
@Override
public String getBizType() {
return "train";
}
@Override
public Object handle(Map<String, Object> params) {
try {
// 获取参数
String courseId = params.get("courseId").toString();
return Result.success().addData("/platform/mobile/train/scanCode?courseId=" + courseId);
} catch (Exception e) {
return Result.error("请使用智慧工会系统身份二维码");
}
}
}
@@ -4,17 +4,23 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.web.commons.base.Globals;
import com.budwk.app.zhgh.scan.core.ScanDispatcher;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.mvc.ActionContext;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.POST;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.view.ForwardView;
import org.nutz.mvc.view.ServerRedirectView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
@@ -37,16 +43,28 @@ public class ScanController {
@At("/h5")
@SaCheckPermission("h5.scan")
@Ok("beetl:/platform/zhghh5/scan/index.html")
public void h5Index() {}
public void h5Index() {
}
@At("/handle")
@Ok("json")
@SaCheckLogin
public Object handle(@Param("bizType") String bizType, @Param("params") String params) {
if(StrUtil.isBlank(bizType)) {
public Object handle(@Param("biz") String biz,
@Param("params") String params,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (StrUtil.isBlank(biz)) {
return Result.error("请使用智慧工会系统身份二维码");
}
Map<String, Object> paramsMap = Json.fromJson(Map.class, params);
return dispatcher.dispatch(bizType, paramsMap);
String requestType = request.getHeader("request-type");
Object dispatch = dispatcher.dispatch(biz, paramsMap);
if ("inner".equals(requestType)) {
return dispatch;
} else {
Result result = (Result) dispatch;
new ServerRedirectView(Globals.AppDomain + result.getData().toString()).render(request, response, null);
return null;
}
}
}
@@ -1,9 +1,12 @@
package com.budwk.app.zhgh.scan.core;
import org.checkerframework.checker.units.qual.C;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.ActionContext;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -1,5 +1,8 @@
package com.budwk.app.zhgh.scan.core;
import org.nutz.mvc.ActionContext;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
@@ -72,6 +72,8 @@
<script src="${base!}/assets/platform/plugins/form-create/form-create.min.js"></script>
<script src="${base!}/assets/platform/plugins/form-create/index.umd.js"></script>
<script src="${base!}/assets/platform/plugins/jr-qrcode/jr-qrcode.js"></script>
<script src="${base!}/components/plugins/sysDict/DictData.js"></script>
<script src="${base!}/assets/platform/js/util/commonUtil.js"></script>
<script src="${base!}/assets/platform/js/main.js"></script>
@@ -121,7 +121,7 @@ layout("/layouts/platform.html"){
</template>
</guava>
<el-dialog :close-on-click-modal="false" :visible.sync="courseDialog" :title="trainType + '列表'" top="50px">
<el-dialog :close-on-click-modal="false" :visible.sync="courseDialog" :title="trainType + '列表'" top="50px" append-to-body>
<el-table :data="clickRow.courseList">
<el-table-column :label="trainType + '名称'" prop="courseName"></el-table-column>
<el-table-column label="校区" prop="campus">
@@ -222,11 +222,14 @@ layout("/layouts/platform.html"){
this.courseDialog = true
},
makeCourseCode(row) {
const o = {
const params = {
courseId: row.id
}
const encodedParams = encodeURIComponent(JSON.stringify(params));
const codeAddress = "${AppDomain!}" + '/platform/scan/handle?biz=train&params=' + encodedParams
console.log(codeAddress)
// 生成二维码的 base64 编码
const content = jrQrcode.getQrBase64(JSON.stringify(o))
const content = jrQrcode.getQrBase64(codeAddress)
let image = new Image()
image.src = content
let viewer = new Viewer(image)
@@ -0,0 +1,118 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
body {
background: white;
}
.weui-msg {
background: white;
height: auto;
min-height: auto;
}
.weui-msg__title {
font-weight: normal;
}
.weui-msg__msg {
font-weight: bolder;
}
.success_tooltip {
width: 90%;
margin: 10px auto 0;
text-align: center;
border-radius: 6px;
background: #F5F7FA;
line-height: 40px;
font-size: 13px;
color: #666666;
}
.result-dialog .van-dialog__footer {
display: none !important;
}
</style>
<div id="app" v-cloak v-if="Object.keys(result).length > 0">
<van-notice-bar
left-icon="volume-o"
:text="'温馨提醒:您已经累计签到' + (result?.data?.signCount || 0) + '个点位。'"
></van-notice-bar>
<div class="weui-msg">
<div class="weui-msg__icon-area">
<i v-if="result?.code === 0" class="weui-icon-success weui-icon_msg"></i>
<i v-else class="weui-icon-warn weui-icon_msg"></i>
</div>
<div class="weui-msg__text-area">
<div class="weui-msg__title">温馨提醒</div>
<div class="weui-msg__msg" v-html="result?.msg"></div>
</div>
</div>
<template v-if="result?.data?.signCount >= 3">
<van-divider></van-divider>
<div class="success_tooltip">恭喜您,点位均签到成功,请设置您的收货地址</div>
<van-address-edit
:address-info="addressInfo"
:area-columns-placeholder="['请选择', '请选择', '请选择']"
:area-list="areaList"
:show-delete="false"
:show-set-default="false"
@save="onSave"
ref="address"
tel-maxlengtfals
></van-address-edit>
</template>
</div>
<script>
<!--#include("/platform/zhghh5/welfare/include/areaList.js"){}#-->
new Vue({
el: "#app",
data() {
return {
courseId: '',
result: {},
addressInfo: {},
areaList: areaList,
}
},
methods: {
onSave(row) {
console.log(row)
this.saveAddress(row)
},
saveAddress(row) {
this.$axios.post('/platform/mobile/train/scanCode/saveAddress', {
courseId: this.courseId,
...row
})
.then(res => {
this.$toast(res.msg)
if(res.code === 0) {
// 关闭父页面弹窗
window.parent.postMessage({
type: 'confirm',
data: {}
}, window.parent.location.origin);
}
})
.catch(err => {
this.$toast(res.msg)
})
},
async init() {
this.result = await this.$axios.post('/platform/mobile/train/scanCode/getSignData', { courseId: this.courseId })
},
},
async created() {
this.courseId = GetQueryString('courseId');
await this.init();
}
})
</script>
<!--#
}
#-->
@@ -95,6 +95,9 @@ layout("/layouts/platform_h5.html"){
.weui-msg__icon-area {
margin-bottom: 20px;
}
/*.result-dialog .van-dialog__footer {
display: none !important;
}*/
</style>
<div id="app" v-cloak>
@@ -118,22 +121,33 @@ layout("/layouts/platform_h5.html"){
<van-button v-else @click="initCamera" block color="#006DB9">点击扫描</van-button>
</div>
<van-dialog v-model="resultVisible"
@confirm="resultConfirm"
@cancel="isProcessing = false; initCamera()"
:confirm-button-text="result?.code === 0 ? '确认入场' : '确认'"
:show-cancel-button="result?.code === 0"
cancel-button-text="取消入场"
<van-dialog
v-if="resultVisible"
v-model="resultVisible"
title="扫码结果"
width="90%"
:close-on-click-overlay="false"
:before-close="handleDialogClose"
confirm-button-text="关闭"
@open="resizeIframe"
class="result-dialog"
@confirm="resultConfirm"
>
<div class="weui-msg">
<div class="weui-msg__icon-area">
<i v-if="result?.code === 0" class="weui-icon-success weui-icon_msg"></i>
<i v-else class="weui-icon-warn weui-icon_msg"></i>
</div>
<div class="weui-msg__text-area">
<div class="weui-msg__title">查询结果</div>
<div class="weui-msg__msg" v-html="result?.msg"></div>
<!-- iframe容器:解决尺寸、加载状态问题 -->
<div style="width: 100%; min-height: 400px; position: relative; margin-top: 20px">
<!-- 加载中状态 -->
<div v-if="iframeLoading" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: #f7f8fa; z-index: 1;">
<van-loading type="spinner" color="#006DB9"></van-loading>
</div>
<!-- iframe核心:拼接完整URL,解决相对路径问题 -->
<iframe
:src="fullJumpUrl"
style="width: 100%; height: 400px; border: none;"
frameborder="0"
scrolling="auto"
@load="iframeLoading = false"
@error="handleIframeError"
></iframe>
</div>
</van-dialog>
@@ -148,14 +162,30 @@ layout("/layouts/platform_h5.html"){
scanStatus: false,
html5QrCode: null,
isProcessing: false, // 防止重复处理
decodedText: null,
resultVisible: false,
result: null,
jumpUrl: '/platform/mobile/train/scanCode',
decodedText: null,
iframeLoading: true, // iframe加载状态
iframeError: false // iframe加载失败
}
},
computed: {
// 自动拼接完整URL,解决相对路径问题
fullJumpUrl() {
if (!this.jumpUrl) return '';
// 拼接当前页面的域名(如https://你的域名)
return this.jumpUrl.startsWith('http')
? this.jumpUrl
: window.location.origin + this.jumpUrl;
}
},
methods: {
resultConfirm() {
this.isProcessing = false
this.resultVisible = false
},
async initCamera() {
try {
const devices = await Html5Qrcode.getCameras()
@@ -251,48 +281,79 @@ layout("/layouts/platform_h5.html"){
this.isProcessing = true;
await this.closeScan()
const { bizType, userId } = JSON.parse(decodedText)
this.result = await this.$axios.post('/platform/scan/handle', {
bizType: bizType,
params: JSON.stringify({ userId: userId, handle: false }),
const res = await this.$axios.post(decodedText, {}, {
headers: { 'request-type': 'inner' } // 标记为内部请求
})
} catch (e) {
this.result = {
code: 99,
msg: '请使用智慧工会系统身份二维码'
.catch(e => {
this.$toast('请扫描智慧工会系统二维码')
})
if(res && res.code === 0) { // 增加res判空,避免报错
this.jumpUrl = res.data;
this.iframeLoading = true; // 重置加载状态
this.iframeError = false;
this.resultVisible = true; // 显示弹窗(自动加载iframe)
}
} catch (e) {
this.$toast('请扫描智慧工会系统二维码')
} finally {
this.resultVisible = true
}
},
async resultConfirm() {
try {
const {bizType, userId} = JSON.parse(this.decodedText)
const res = await this.$axios.post('/platform/scan/handle', {
bizType: bizType,
params: JSON.stringify({userId: userId, handle: true}),
})
if(res.code === 0) {
this.$toast.success(res.msg)
resizeIframe(height) {
this.$nextTick(() => {
const iframe = document.querySelector('iframe');
if (iframe) {
// 如果传入了高度,用子页面的实际高度;否则用默认高度
iframe.style.height = height || '400px';
// 移除固定高度,改用动态值
iframe.height = ''; // 清空原生height属性,优先用style
}
} catch (e) {
});
},
} finally {
this.isProcessing = false;
this.initCamera()
// iframe加载失败处理
handleIframeError() {
this.iframeLoading = false;
this.iframeError = true;
this.$toast('页面加载失败,请重试');
},
// 弹窗关闭时清理iframe
handleDialogClose() {
this.jumpUrl = '';
this.iframeLoading = true;
this.iframeError = false;
// 清空iframe内容,避免缓存
const iframe = document.querySelector('iframe');
if (iframe) iframe.src = 'about:blank';
},
handleIframeMessage(event) {
const allowedOrigins = [window.location.origin, 'null']; // 允许about:blank
if (!allowedOrigins.includes(event.origin)) {
console.warn('拒绝接收非信任域名的消息:', event.origin);
return;
}
const { type, data } = event.data;
if (type === 'confirm') {
this.isProcessing = false
this.resultVisible = false
}
console.log('收到子页面消息:', type, data);
}
},
async created() {
await this.initCamera()
//this.resultVisible = true
window.addEventListener('message', this.handleIframeMessage);
},
beforeDestroy() {
if (this.scanStatus) {
this.closeScan();
}
window.removeEventListener('message', this.handleIframeMessage);
}
});
</script>
@@ -0,0 +1,301 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.2.0/weui.min.css">
<style>
#app {
min-height: 100vh;
background: #f7f8fa;
}
.scan-container {
width: 100%;
height: 65vh;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
/* 扫描框 - 更大、圆角、柔和边框 */
.scan-box {
position: absolute;
width: 260px;
height: 260px;
border: 2px solid #1989fa;
border-radius: 16px;
z-index: 2;
}
/* 扫描线 - 渐变色 + 柔和动画 */
.scan-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(to right, transparent, #1989fa, transparent);
animation: scan 2.5s ease-in-out infinite;
z-index: 3;
}
/*@keyframes scan {
0% { top: 0; opacity: 0.8; }
50% { opacity: 1; }
100% { top: 257px; opacity: 0.8; }
}*/
.footer-container {
position: fixed;
bottom: 16px;
width: 100%;
}
.footer-container .van-button--block {
width: 90%;
margin: 0 auto 6px;
height: 50px;
border-radius: 12px;
}
#reader {
width: 90%;
height: 60vh;
margin: 10px auto 40px;
text-align: center;
position: relative;
overflow: hidden;
}
#reader video {
width: 100% !important;
height: 100% !important;
object-fit: contain !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
}
.overlay {
position: absolute;
top: 0; left: 0;
width: 100%; height: 65vh;
pointer-events: none;
z-index: 10;
}
.weui-msg__title {
font-weight: normal;
}
.weui-msg__msg {
font-weight: bolder;
}
.weui-msg {
padding-top: 20px;
}
.weui-msg__icon-area {
margin-bottom: 20px;
}
</style>
<div id="app" v-cloak>
<van-sticky>
<van-nav-bar title="扫一扫" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
</van-sticky>
<div v-if="!scanStatus" class="scan-container overlay">
<!-- 扫描框 -->
<div class="scan-box">
<!--<div class="scan-line"></div>-->
</div>
<!-- 初始图标(仅在未启动时显示) -->
<van-icon v-if="!scanStatus" name="scan" size="56" color="#1989fa" style="z-index: 2;"></van-icon>
</div>
<div id="reader"></div>
<div class="footer-container">
<van-button v-if="scanStatus" @click="closeScan" block color="#006DB9" :loading="html5QrCode?.stateManagerProxy?.stateManager?.state !== 2">关闭扫描</van-button>
<van-button v-else @click="initCamera" block color="#006DB9">点击扫描</van-button>
</div>
<van-dialog v-model="resultVisible"
@confirm="resultConfirm"
@cancel="isProcessing = false; initCamera()"
:confirm-button-text="result?.code === 0 ? '确认入场' : '确认'"
:show-cancel-button="result?.code === 0"
cancel-button-text="取消入场"
>
<div class="weui-msg">
<div class="weui-msg__icon-area">
<i v-if="result?.code === 0" class="weui-icon-success weui-icon_msg"></i>
<i v-else class="weui-icon-warn weui-icon_msg"></i>
</div>
<div class="weui-msg__text-area">
<div class="weui-msg__title">查询结果</div>
<div class="weui-msg__msg" v-html="result?.msg"></div>
</div>
</div>
</van-dialog>
</div>
<script src="${base!}/assets/platform/plugins/html5-qrcode/html5-qrcode.min.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
scanStatus: false,
html5QrCode: null,
isProcessing: false, // 防止重复处理
resultVisible: false,
result: null,
decodedText: null,
}
},
methods: {
async initCamera() {
try {
const devices = await Html5Qrcode.getCameras()
if (!devices || devices.length === 0) {
vant.Dialog.alert({
title: '提示',
message: '未检测到摄像头,请检查设备权限或尝试其他浏览器'
}).then(() => {})
return;
}
let cameraId
if (devices.length > 1) {
cameraId = devices[1].id;
} else {
cameraId = devices[0].id;
}
let isHuawei = navigator.userAgent.toLowerCase().match(/huawei/i) == 'huawei';
const backCamera = devices.find(d => /back|rear/i.test(d.label));
if (isHuawei && backCamera) cameraId = backCamera.id;
await this.startScanning(cameraId);
} catch (err) {
console.error('摄像头初始化失败:', err);
let msg = '请允许访问摄像头权限';
if (err.name !== 'NotAllowedError') {
msg = '摄像头启动失败,请重试或更换设备';
}
vant.Dialog.alert({
title: '权限提示',
message: msg
}).then(() => {})
}
},
async startScanning(cameraId) {
this.html5QrCode = new Html5Qrcode("reader");
this.scanStatus = true;
try {
const container = document.getElementById('reader');
const size = Math.min(container.clientWidth * 0.8, 300); // 最大 300px
await this.html5QrCode.start(
cameraId,
{
fps: 10,
qrbox: size,
videoConstraints: {
facingMode: "environment" // 强制后置(部分浏览器支持)
},
disableFlip: true, // 防止镜像
formatsToSupport: [Html5QrcodeSupportedFormats.QR_CODE] // 只扫 QR,提升速度
},
(decodedText) => {
this.decodedText = decodedText
this.onHandle(decodedText)
},
(errorMessage) => {}
);
} catch (err) {
console.error('扫描启动失败:', err);
vant.Dialog.alert({
title: '扫描失败',
message: '无法启动扫码功能,请刷新页面重试'
}).then(() => {})
}
},
async closeScan() {
if (!this.html5QrCode || !this.scanStatus) return;
try {
const videoEl = this.html5QrCode?.getState()?.camera?.stream?.getVideoTracks();
if (videoEl && videoEl.length) {
videoEl.forEach(track => track.stop());
}
await this.html5QrCode.stop();
} catch (err) {
console.error('关闭失败:', err);
}finally {
this.scanStatus = false;
this.html5QrCode = null;
const reader = document.getElementById('reader');
if (reader) reader.innerHTML = '';
}
},
async onHandle(decodedText) {
try {
// 防重:如果正在处理,直接忽略
if (this.isProcessing) return;
// 立即设置为处理中,并停止扫描
this.isProcessing = true;
await this.closeScan()
const { bizType, userId } = JSON.parse(decodedText)
this.result = await this.$axios.post('/platform/scan/handle', {
bizType: bizType,
params: JSON.stringify({ userId: userId, handle: false }),
})
} catch (e) {
this.result = {
code: 99,
msg: '请使用智慧工会系统身份二维码'
}
} finally {
this.resultVisible = true
}
},
async resultConfirm() {
try {
const {bizType, userId} = JSON.parse(this.decodedText)
const res = await this.$axios.post('/platform/scan/handle', {
bizType: bizType,
params: JSON.stringify({userId: userId, handle: true}),
})
if(res.code === 0) {
this.$toast.success(res.msg)
}
} catch (e) {
} finally {
this.isProcessing = false;
this.initCamera()
}
}
},
async created() {
await this.initCamera()
},
beforeDestroy() {
if (this.scanStatus) {
this.closeScan();
}
}
});
</script>
<!--#
}
#-->
@@ -134,6 +134,7 @@ const mine = {
})
},
codeValid() {
return true
if(this.$store.state.user.member === true || ['年薪制', '境外聘用', '劳务派遣一类', '劳务派遣二类'].includes(this.$store.state.user.preparedBy)) {
return true
}
@@ -141,7 +142,13 @@ const mine = {
},
qrCode() {
this.codeVisible = true
this.codeAddress = JSON.stringify({userId: this.$store.state.user.id, bizType: 'culture'})
//this.codeAddress = JSON.stringify({userId: this.$store.state.user.id, bizType: 'culture'})
const params = {
loginName: this.$store.state.user.loginname,
}
const encodedParams = encodeURIComponent(JSON.stringify(params));
this.codeAddress = "${AppDomain!}" + '/platform/scan/handle?biz=culture&params=' + encodedParams
console.log(this.codeAddress)
},
},
style() {