Browse Source

提交扫描市监二维码开发

弹出企业监督码功能开发
v3
chenrui 2 years ago
parent
commit
1bf557dca7
  1. 9
      src/router/enforceHome.js
  2. 548
      src/views/enforce/qrCodeLoading.vue
  3. 135
      src/views/enforce/scanPageTest.vue
  4. 56
      src/views/enforce/scanQrCode.vue
  5. 18
      src/views/enforceHome.vue

9
src/router/enforceHome.js

@ -128,6 +128,15 @@ const routes = [
isKeepAlive: false
},
},
//企业监督码中间页面
{
path:"/qrCodeLoading",
name:"qrCodeLoading",
component: () => import("@/views/enforce/qrCodeLoading.vue"),
meta: {
isKeepAlive: false
},
},
]
export {routes}

548
src/views/enforce/qrCodeLoading.vue

@ -0,0 +1,548 @@
<!-- 跳转企业二维码load页面 -->
<template>
<div class="home app_content">
<!-- 中间 -->
<div class="warp_body">
<div class="white-body">
<div class="boutique_box">
<div class="box-qr-title">执法监督码</div>
<div class="boutique_body">
<div class="box-qr-code">
<img class="box-qr-code-img" :src="data.qrCodeImg" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup name="Home">
import Vconsole from "vconsole";
import {showFailToast,showToast} from 'vant';
import {checkIsState,getEnterpriseInfoBySj} from '@/api/enforce';
import {szyptGetToken} from "@/api/Home";
import { baseUrl } from "../../config/env";
import wx from "weixin-js-sdk";
const {useCommon} = $globalStore;
const router = useRouter();
//路由参数
const route = useRoute();
const intervalId = ref(null);
const data = reactive({
showNoScanPicker: ref(false),
qrCodeImg: baseUrl + "/profile/upload/2024/11/29/03043b8531b8e1aad5a0f72d2fc605f0.jpg",
});
const indexSens = ref(3);
const butText = ref("点击跳转企业画像....,"+indexSens.value+"秒后自动跳转");
const openScan = () => {
let fullUrl = window.location.href;
let code = fullUrl.substring(fullUrl.indexOf('code=') + 5, fullUrl.indexOf('&state='));
let paramUrl = window.location.href.split("#")[0];
let param = {code: code, url: paramUrl};
console.log(paramUrl);
szyptGetToken(param, success => {
let resMsg = success.data;
console.log(resMsg);
wx.config({
// 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
debug: false,
// 必填,公众号的唯一标识
appId: resMsg.corpid,
// 必填,生成签名的时间戳
timestamp: resMsg.timestamp,
// 必填,生成签名的随机串
nonceStr: resMsg.nonceStr,
// 必填,签名,见附录1
signature: resMsg.signature,
// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
jsApiList: ["scanQRCode"],
});
wx.ready(() => {
//微信扫一扫请求操作
ClickScan()
});
wx.error((res) => {
console.log("获取失败");
});
}, error => {
});
};
const ClickScan = () => {
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
success: (res) => {
// 页面跳转
console.log("=========== 扫码返回 ===========")
console.log(res)
//TODO 判断是否是市监
if(isHttpLink(res.resultStr)){
//判断地址是否存在cid统一信用代码
getEnterpriseInfoBySj({"url":res.resultStr},success =>{
if (success.success) {
var element = success.data;
//TODO 添加延时弹出监督码
showToast({
duration: 3000,
message:"企业监督码",
icon: baseUrl + element.qrCodeGenerateHistory.qrCodeUrl,
iconSize:"100px",
onClose: openEnterpriseView(element.enterpriseNumber)
})
} else {
showFailToast(success.message);
router.push({
path: "/enforce/scanQrCode",
})
}
},error =>{})
}else{
const scanObj = JSON.parse(res.resultStr);
// 判断二维码是否过期, 如果已过期则跳转提示页
checkIsState(scanObj.salt, scanObj.codeId, success => {
console.log(success)
if (success.data) {
router.push({
path: "/enterpriseScanPreview",
query: {
enterpriseNumber: scanObj.salt,
isNoScan: false
}
})
} else {
showFailToast(success.message);
router.push({
path: "/enforce/scanQrCode",
})
}
}, error => {
showFailToast("该二维码不正确");
})
}
},
error: function(res) {
console.log("错误回调",res)
if (res.errMsg.indexOf('function_not_exist') > 0) {
showFailToast('版本过低请升级')
}
},
cancel: (res) => {
console.log('err', res);
},
});
};
const toScanPageView = async () => {
openScan();
};
onMounted(() => {
// new Vconsole();
startTimer(route?.query?.enterpriseNumber);
console.log("企业统一信用代码:",route?.query?.enterpriseNumber)
console.log("企业二维码地址:",route?.query?.qrCodeUrl)
})
//定时器
const startTimer = (enterpriseNumber) => {
// 假设我们每隔1秒钟执行一些操作
intervalId.value = setInterval(() => {
console.log('定时器执行中...');
// 执行一些定时器相关的操作
indexSens.value--;
if(indexSens.value >= 0){
console.log("加减",indexSens.value)
butText.value = "点击跳转企业画像....,"+indexSens.value+"秒后自动跳转";
}else{
console.log("清除定时器")
console.log("清除定时器",enterpriseNumber)
clearInterval(intervalId.value);
router.push({
path: "/enterpriseScanPreview",
query: {
enterpriseNumber: enterpriseNumber,
isNoScan: false
}
})
}
}, 1000);
};
// 返回
const goBack = () => {
router.go(-1);
};
const isHttpLink = (url) =>{
return url.indexOf("http://") === 0 || url.indexOf("https://") === 0;
}
const openEnterpriseView = (enterpriseNumber) =>{
console.log("外层获取企业统一信用代码",enterpriseNumber);
setTimeout(() =>{
console.log("定时内获取企业统一信用代码",enterpriseNumber);
router.push({
path: "/enterpriseScanPreview",
query: {
enterpriseNumber: enterpriseNumber,
isNoScan: false
}
})
}, 3000);
}
onUnmounted(() => {
// 清除定时器
if (intervalId.value) {
clearInterval(intervalId.value);
}
});
</script>
<script>
//不扫码企业筛选弹窗
import Popup from '@/views/enforce/noScanFilter.vue';
import { baseUrl } from "../../config/env";
export default {
components: {
Popup
},
data() {
return {
popupVisible: false,
popupContent: ''
}
},
methods: {
showPopup() {
this.popupVisible = true;
},
closePopup() {
this.popupVisible = false;
}
}
}
</script>
<style lang="less" scoped>
.home {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
background-color: #ebf3fb;
.warp_body {
position: relative;
background-image: linear-gradient(to bottom, #ebf3fb, transparent);
.carousel_box {
position: relative;
top: 2%;
height: 4rem;
width: 100%;
background: url("/src/assets/images/scan-mean.jpg") no-repeat center center;
background-size: 60%;
opacity: 0.75;
.box-scan-one, .box-scan-two {
position: absolute;
top: 30%;
left: 15%;
height: 2.67rem;
width: 30%;
font-size: 0.4rem;
color: #999;
.box-scan-text {
margin-top: 70%;
display: block;
width: 100%;
text-align: center
}
}
.box-scan-two {
left: auto;
right: 12.5%;
}
}
}
.boutique_box {
position: relative;
top: 5%;
z-index: 100;
width: 100%;
.boutique_body {
padding: 0 0 0.27rem 0;
display: flex;
justify-content: center;
}
;
.box-qr-code {
width: 80%;
text-align: right;
height: 7rem;
//background-color: var(--van-primary-color);
// background: url("../../assets/images/qrCode.jpg") no-repeat center center;
background-size: 100%;
// border-radius: 0.27rem;
// background-size: cover;
// border-radius: 50%;
// background-image: url("@/assets/images/scan.png");
.box-qr-code-text {
margin-right: 25%;
margin-top: 70%;
display: block;
font-size: 0.45rem;
}
.box-qr-code-img{
width: 100%;
height: 100%;
}
// img {
// position: absolute;
// top: 0%;
// left: 34%;
// }
}
}
}
.white-body {
position: relative;
margin: 0 3%;
width: 94%;
height: 99%;
background-color: #fff;
border-radius: 0.27rem;
}
.box-qr-title{
margin: 3% 0 0;
width: 90%;
font-size: 0.42rem;
text-align: center;
transform: translate(5%, 5%);
line-height: 3;
}
.nocode {
margin: 3% 0 0;
width: 90%;
font-size: 0.42rem;
text-align: center;
line-height: 3;
background-color: #619cd9;
transform: translate(5%, 5%);
border-radius: 6px;
}
.scan-note {
width: 100%;
font-size: 0.45rem;
text-align: center;
line-height: 2;
margin-top: 2%;
}
.popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.35);
}
.popup-content {
position: fixed;
z-index: 999;
bottom: 0;
left: 0;
padding: 0 3%;
width: 94%;
height: 35vh;
background: #fff;
overflow-y: auto;
font-size: 0.37rem;
}
.subtitle {
margin: 5% 2% 2%;
width: 96%;
/*font-size: 0.4rem;
font-weight: 600;*/
border-bottom: 1px solid #f4f4f4;
line-height: 2;
}
.item-btn {
float: left;
margin: 0.3rem 0.2rem;
padding: 0.2rem 0.3rem;
background-color: #f2f3f4;
color: #999;
border-radius: 1rem;
font-size: 0.35rem;
}
.item-btn.active {
background-color: #e9f2fb;
color: #1367fe;
}
.btnbox {
display: flex;
justify-content: center;
position: absolute;
bottom: 1rem;
left: 5%;
width: 90%;
height: 1rem;
}
.btn-reset {
display: inline-block;
margin: 0 2%;
padding: 0;
width: 60%;
color: #fff;
font-size: 0.35rem;
border-radius: 1rem;
background: #1367fe;
border: 1px solid #1367fe;
text-align: center;
}
.btn-search {
display: inline-block;
margin: 3% 7%;
padding: 0;
width: 70%;
color: #fff;
font-size: 0.35rem;
border-radius: 1rem;
background: #1367fe;
border: 1px solid #1367fe;
text-align: center;
height: 90%;
}
.btn-reset {
width: 35%;
background-color: #fff;
border-color: rgba(19, 103, 254, .7);
color: #1367fe;
}
.close-button {
position: absolute;
top: 0.15rem;
right: 0.1rem;
width: 0.8rem;
height: 0.8rem;
background-color: rgba(255, 255, 255, .5);
border: 0.05rem solid rgba(255, 255, 255, .7);
border-radius: 50%;
transform: rotate(45deg);
}
.close-button:before,
.close-button:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
background: #333;
border-radius: 50%;
transform: translate(-50%, -50%);
height: 0.4rem;
width: 0.02rem;
}
.close-button:after {
height: 0.02rem;
width: 0.4rem;
}
.nav-div {
display: flex;
margin: 15% 2% 0;
width: 96%;
color: #287eef;
font-size: 0.4rem;
line-height: 2;
text-align: center;
}
.nav-div > div {
margin: 2% 3%;
width: 45%;
}
.nav-div div span{
display: block;
}
.nav-div div i {
margin: 0.35rem;
width: 1.5rem;
height: 1.5rem;
font-size: 0.8rem;
background: #e6eff7;
border-radius: 50%;
text-align: center;
line-height: 1.85;
}
.nav-div .act-note {
margin: 10% 0 0;
width: 100%;
font-size: 0.3rem;
color: #d3d0d0;
line-height: 1.5;
text-align: left;
}
.addinfo {
position: absolute;
bottom: 2rem;
right: 2%;
z-index: 999;
width: 1.5rem;
height: 1.5rem;
background-color: #e0e9f9;
border-radius: 50%;
box-shadow: 0 0 0.27rem #90b6f9;
color: #0240f7;
font-size: 0.4rem;
text-align: center;
vertical-align: middle;
padding-top: 0.45rem;
box-sizing: border-box;
}
.record {
position: absolute;
bottom: 2rem;
color: #bbbaba;
font-size: 0.35rem;
width: 100%;
text-align: center;
white-space: nowrap;
}
.record a {
color: #287eef;
}
</style>

135
src/views/enforce/scanPageTest.vue

@ -1,135 +0,0 @@
<style scoped>
.scan-area {
width: 100%;
height: 18.15rem;
}
</style>
<template>
<div class="scan-area">
<qrcode-stream @click="handleClick" :paused="scanPause" :track="paintOutLineAndCenterText" @error="handleError" @camera-on="onCameraOn">
<div v-if="!cameraReady">
loading...
</div>
</qrcode-stream>
<!-- <audio ref="audioRef" src="/src/assets/Di.mp3"></audio> -->
</div>
</template>
<script setup lang="ts">
// import Vconsole from "vconsole";
import { QrcodeStream } from "vue-qrcode-reader";
import {ref} from 'vue'
const audioRef = ref(null);
const errorInfo = ref('');
const cameraReady = ref(false);
const scanPause = ref(false);
const reactAngles = ref<any>([]);
const onCameraOn = function () {
cameraReady.value = true;
// new Vconsole();
}
const handleError = function (err) {
errorInfo.value = `[${err.name}]: `
if (err.name === 'NotAllowedError') {
errorInfo.value += '您需要授予相机访问权限'
} else if (err.name === 'NotFoundError') {
errorInfo.value = '此设备上没有检测到摄像头';
} else if (err.name === 'NotReadableError') {
errorInfo.value = '相机被占用'
} else if (err.name === 'NotSupportedError') {
errorInfo.value = 'secure context required (HTTPS, localhost)'
} else if (err.name === 'OverconstrainedError') {
errorInfo.value += ''
} else if (err.name === 'StreamApiNotSupportedError') {
errorInfo.value += 'Stream API is not supported in this browser'
} else if (err.name === 'InsecureContextError') {
errorInfo.value += 'Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.'
} else {
errorInfo.value += err.message
}
console.log(errorInfo.value);
errorInfo.value = '';
}
const paintOutLineAndCenterText = (detectedCodes, ctx) => {
// 暂停页面
scanPause.value = true;
// 播放语音
// audioRef.value.play();
// 画边框和文本
reactAngles.value.splice();
for (const detectedCode of detectedCodes) {
// 画边框
ctx.lineWidth = 1;
const [firstPoint, ...otherPoints] = detectedCode.cornerPoints
ctx.strokeStyle = 'red'
ctx.beginPath()
ctx.moveTo(firstPoint.x, firstPoint.y)
for (const { x, y } of otherPoints) {
ctx.lineTo(x, y)
}
ctx.lineTo(firstPoint.x, firstPoint.y)
ctx.closePath();
ctx.stroke();
// 画文字
const { boundingBox, rawValue } = detectedCode
const centerX = boundingBox.x + boundingBox.width / 2
const centerY = boundingBox.y + boundingBox.height / 2
const fontSize = Math.max(12, (50 * boundingBox.width) / ctx.canvas.width)
console.log(boundingBox.width, ctx.canvas.width)
ctx.lineWidth = 2;
ctx.font = `${fontSize}px sans-serif`
ctx.textAlign = 'center'
ctx.strokeStyle = '#35e5a7'
ctx.strokeText('>', centerX, centerY)
// 配置矩形内容
if (detectedCode.cornerPoints.length==4) {
reactAngles.value.push({
leftTop: {x: firstPoint.x, y:firstPoint.y},
rightTop: {x: otherPoints[0].x, y:otherPoints[0].y},
rightBottom: {x: otherPoints[1].x, y:otherPoints[1].y},
leftBottom: {x: otherPoints[2].x, y:otherPoints[2].y},
text: detectedCode.rawValue
});
}
}
};
// 获取事件的发生位置
const getEventPosition = (ev) => {
var x, y;
if (ev.layerX || ev.layerX == 0) {
x = ev.layerX;
y = ev.layerY;
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX;
y = ev.offsetY;
}
return {x: x, y: y};
};
const handleClick = function (event) {
// 1. 获取点击点的位置
var eventPosition = getEventPosition(event);
// 2. 判断在不在已有的矩形内部,在的话获取二维码解析的结果,并进行业务处理
var resolveText = handleClickRectAngle(eventPosition);
if (resolveText != undefined) {
alert(resolveText);
}
// 3. 业务处理完,如果决定跳转就跳转,不决定跳转的话就关闭暂停
scanPause.value = false;
}
// 判断一个点在不在某个矩形内部的算法,后面可以采取凸包算法,优化判断的实现,不过这里已经能实现了,没必要深究
const handleClickRectAngle = function (eventPosition) {
for (let i = 0; i < reactAngles.value.length; i++) {
let reactAngle = reactAngles.value[i];
if ( ((eventPosition.x >= Math.min(reactAngle.leftTop.x, reactAngle.leftBottom.x)) && (eventPosition.x <= Math.max(reactAngle.rightTop.x, reactAngle.rightBottom.x)))
&& ((eventPosition.y >= Math.min(reactAngle.leftTop.y, reactAngle.rightTop.y)) && (eventPosition.y <= Math.max(reactAngle.leftBottom.y, reactAngle.rightBottom.y)))
) {
return reactAngle.text;
}
}
return undefined;
}
</script>

56
src/views/enforce/scanQrCode.vue

@ -52,6 +52,7 @@
<div class="record">如遇网络异常、设备故障无法登录系统的情况,<a @click="toSign">补录 >></a></div>
<div class="record"><a @click="toQrView">二维码 >></a></div>
</div>
<!-- <div class="recommend_box">
<van-cell :border="false" title="推荐" />
@ -78,11 +79,12 @@
<script setup name="Home">
import Vconsole from "vconsole";
import {showFailToast} from 'vant';
import {showFailToast,showToast} from 'vant';
import {getEnterpriseMerchantAuthorization} from '@/api/Home';
import {checkIsExists} from '@/api/enterprise';
import {checkIsState,getEnterpriseInfoBySj} from '@/api/enforce';
import {szyptGetToken} from "@/api/Home";
import { baseUrl } from "../../config/env";
import wx from "weixin-js-sdk";
import noScan from '@/views/enforce/noScan.vue';
@ -104,7 +106,9 @@
columnNum: 4,
itemClass: "boutique_item",
goodsCradclass: "boutique-crad",
}); // 商品卡片展示
});
//定时器
const timerId = ref(null);
//获取轮播
const getCarousel = async () => {
@ -168,13 +172,19 @@
//判断地址是否存在cid统一信用代码
getEnterpriseInfoBySj({"url":res.resultStr},success =>{
if (success.success) {
router.push({
path: "/enterpriseScanPreview",
query: {
enterpriseNumber: success.data.enterpriseNumber,
isNoScan: false
}
})
var element = success.data;
//TODO 添加延时弹出监督码
// showToast({
// duration: 3000,
// message:"企业监督码",
// icon: baseUrl + element.qrCodeGenerateHistory.qrCodeUrl,
// iconSize:"100px",
// onClose: openEnterpriseView(element.enterpriseNumber)
// })
//跳转企业二维码中间页面
// toQrView(element.qrCodeGenerateHistory.qrCodeUrl,element.enterpriseNumber)
//测试
toQrView("/profile/upload/2024/11/29/03043b8531b8e1aad5a0f72d2fc605f0.jpg","91440300769156389U");
} else {
showFailToast(success.message);
router.push({
@ -254,7 +264,7 @@
};
onMounted(() => {
// new Vconsole();
new Vconsole();
})
// 返回
@ -275,13 +285,39 @@
}
});
};
const toQrView = (enterpriseNumber,qrCodeUrl) => {
// qrCodeUrl = "/profile/upload/2024/11/29/03043b8531b8e1aad5a0f72d2fc605f0.jpg";
// enterpriseNumber = "91440300769156389U";
router.push({
path: "/qrCodeLoading",
query: {
enterpriseNumber: enterpriseNumber,
qrCodeUrl: qrCodeUrl,
}
});
};
const isHttpLink = (url) =>{
return url.indexOf("http://") === 0 || url.indexOf("https://") === 0;
}
const openEnterpriseView = (enterpriseNumber) =>{
console.log("外层获取企业统一信用代码",enterpriseNumber);
setTimeout(() =>{
console.log("定时内获取企业统一信用代码",enterpriseNumber);
router.push({
path: "/enterpriseScanPreview",
query: {
enterpriseNumber: enterpriseNumber,
isNoScan: false
}
})
}, 3000);
}
</script>
<script>
//不扫码企业筛选弹窗
import Popup from '@/views/enforce/noScanFilter.vue';
import { baseUrl } from "../../config/env";
export default {
components: {

18
src/views/enforceHome.vue

@ -1,7 +1,7 @@
<template>
<Loading class="loading-div" type="spinner" v-show="isLoading" color="#1989fa"/>
<div class="home app_content" v-if="!isLoading">
<!-- <div class="home app_content">-->
<!-- <div class="home app_content" v-if="!isLoading"> -->
<div class="home app_content">
<!-- 导航栏 -->
<!-- <div class="header_body">
<header-nav :leftArrow="false" titelText="执法监督码"></header-nav>
@ -51,7 +51,7 @@
<script setup name="Home">
import Vconsolefrom from 'vconsole';
import {Loading, showFailToast} from 'vant';
import {Loading, showFailToast,showToast} from 'vant';
import {getEnterpriseMerchantAuthorization, getSzyptLoginAuth} from '../api/Home';
import {queryEnforcementInspectionProgramListNoPermissionNoPaging} from "@/api/enforce";
@ -239,7 +239,7 @@
};
onMounted(() => {
// new Vconsolefrom();
new Vconsolefrom();
let fullUrl = window.location.href;
let code = fullUrl.substring(fullUrl.indexOf('code=') + 5, fullUrl.indexOf('&state='));
console.log(useCommon.firstLoad)
@ -266,14 +266,7 @@
}, error => {
queryList();
})
// getEnterpriseMerchantAuthorization({merchantTel: useCommon.phonenumber}, success => {
// if (!(success.data == null && success.data.length == 0)) {
// useCommon.SET_USER_ENTERPRISE_NUMBER(success.data[0].enterpriseNumber);
// }
// }, error => {
// })
// queryList();
setInterval(showNotice, 3000)
setInterval(showNotice, 3000);
});
const setNotificationMove = () => {
// 清除定时器
@ -330,6 +323,7 @@
}, 500)
}
</script>
<style lang="less" scoped>

Loading…
Cancel
Save