commit
This commit is contained in:
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
+100
@@ -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¶ms=' + 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¶ms=' + encodedParams
|
||||
console.log(this.codeAddress)
|
||||
},
|
||||
},
|
||||
style() {
|
||||
|
||||
Reference in New Issue
Block a user