update @智能防刷验证 二次兑换弹窗验证

This commit is contained in:
huangzhen 2024-09-23 11:11:16 +08:00
parent 99aacf4b33
commit 2118efffd6
15 changed files with 145 additions and 23 deletions

View File

@ -5,17 +5,20 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,viewport-fit=cover" /> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,viewport-fit=cover" />
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<!-- <script src="https://g.alicdn.com/AWSC/AWSC/awsc.js"></script> --> <script type="text/javascript" src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"></script>
</head> </head>
<body> <body>
<noscript> <noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<!-- 智能验证防刷 -->
<div id="captcha-element"></div>
<div id="captcha-button"></div>
<!-- 仅开发/测试使用 --> <!-- 仅开发/测试使用 -->
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script> <!-- <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script> <script>
var vConsole = new window.VConsole(); var vConsole = new window.VConsole();
</script> </script> -->
</body> </body>
</html> </html>

View File

@ -144,11 +144,6 @@ html {
} }
} }
// 阿里防刷 待加
.alsc {
margin: 0 !important;
}
// PC 模版展示 // PC 模版展示
.comWrapperPC { .comWrapperPC {
min-height: 667px !important; min-height: 667px !important;

View File

@ -3,6 +3,7 @@ import {
createWebHashHistory, createWebHashHistory,
createWebHistory, createWebHistory,
} from "vue-router"; } from "vue-router";
import Cap from "@/utils/aliyunCaptcha";
// 公共路由 // 公共路由
const routePublic = [ const routePublic = [
@ -82,6 +83,12 @@ const router = createRouter({
}); });
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
// 默认全局 智能验证防刷
if (!window.CapCom && !window.CapTime) {
znInit();
}
// 首页 /XXX 获取 key
const rouList = routes.filter( const rouList = routes.filter(
(item) => item.path !== "/" && item.path === to.path (item) => item.path !== "/" && item.path === to.path
); );
@ -98,4 +105,21 @@ router.beforeEach(async (to, from, next) => {
} }
}); });
function znInit() {
window.CapCom = new Cap({});
const CapCom = window.CapCom;
setTimeout(() => {
CapCom.popup('start'); // 首页触发之后,二次触发才唤起验证 todo: 无痕验证(首次验证 不会渲染图形验证)
}, 3 * 1000);
window.CapTime = setInterval(() => {
if (CapCom) {
CapCom.popup('click');
} else {
znInit();
}
}, 5 * 60 * 1000);
}
export default router; export default router;

6
src/shims-vue.d.ts vendored
View File

@ -4,3 +4,9 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any> const component: DefineComponent<{}, {}, any>
export default component export default component
} }
interface Window {
CapCom: any;
CapTime: any;
initAliyunCaptcha: any;
}

View File

@ -0,0 +1,64 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let captcha = null;
let captchaButton: any = null;
let popType: any = '';
class Cap {
constructor (opt = {}) {
this.clear();
this.init(opt).then();
}
clear () {
// 初始化-重置
captcha = null;
captchaButton = null;
popType = '';
// 必须删除相关元素,否则再次 init 多次调用 initAliyunCaptcha 会导致多次回调 captchaVerifyCallback
document.getElementById('aliyunCaptcha-mask')?.remove();
document.getElementById('aliyunCaptcha-window-popup')?.remove();
}
init = async function (opt: any) {
captchaButton = document.getElementById('captcha-button');
window.initAliyunCaptcha({
SceneId: '1mjp887e', // 场景ID。根据步骤二新建验证场景后您可以在验证码场景列表获取该场景的场景ID
prefix: '1mpccr', // 身份标。开通阿里云验证码2.0后,您可以在控制台概览页面的实例基本信息卡片区域,获取身份标
mode: 'popup', // 验证码模式。popup表示要集成的验证码模式为弹出式。无需修改
element: '#captcha-element', // 页面上预留的渲染验证码的元素,与原代码中预留的页面元素保持一致。
button: '#captcha-button', // 触发验证码弹窗的元素。button表示单击登录按钮后触发captchaVerifyCallback函数。您可以根据实际使用的元素修改element的值
captchaVerifyCallback: this.captchaVerifyCallback, // 业务请求(带验证码校验)回调函数,无需修改
onBizResultCallback: this.onBizResultCallback, // 业务请求结果回调函数,无需修改
getInstance: this.getInstance, // 绑定验证码实例函数,无需修改
slideStyle: {
width: 280,
height: 40,
}, // 滑块验证码样式支持自定义宽度和高度单位为px。其中width最小值为320 px
language: 'cn', // 验证码语言类型支持简体中文cn、繁体中文tw、英文en
});
}
getInstance(instance: any) {
captcha = instance;
}
async captchaVerifyCallback(captchaVerifyParam: any) {
// console.log('---业务请求(带验证码校验)回调函数---', captchaVerifyParam);
return {
captchaResult: true,
bizResult: true,
}
}
// 验证通过后调用
onBizResultCallback() {
// console.log('---业务请求结果回调函数---');
if (popType === 'click') {
// showToast('验证成功');
}
}
// 弹出验证框
popup(val: any) {
popType = val; // 'start' 初始化;'click':唤起验证框
captchaButton.click();
}
}
export default Cap;

View File

@ -1,4 +1,5 @@
import { isObject } from "@/utils/index"; import { isObject } from "@/utils/index";
import { showConfirmDialog } from "vant";
/** /**
* *
@ -73,6 +74,25 @@ export function urlToLocal(query: any) {
}); });
} }
/**
* local
* @returns {*}
*/
export async function receiveAgain() {
const confirmRes: any = await new Promise<void>((resolve: any, reject) => {
showConfirmDialog({
message: '是否确认兑换该商品?',
})
.then(() => {
resolve(true)
})
.catch(() => {
resolve(false)
})
});
return confirmRes;
}
/** /**
* *
*/ */

View File

@ -43,12 +43,14 @@ service.interceptors.request.use((config: AxiosRequestConfig) => {
service.interceptors.response.use( service.interceptors.response.use(
(response: AxiosResponse) => { (response: AxiosResponse) => {
const data = response.data; const data = response.data;
if ([401, 4001].includes(data.code)) {
location.replace(`${location.origin}/yxh5/home?loginBack=${location.href}`);
}
return data || {}; return data || {};
}, },
(err) => { (err) => {
// todo 待验证接口 token 失效情况
console.log('---err---', err);
if (err?.response?.status === 401 || err?.response?.code === 4001) {
location.replace(`${location.origin}/yxh5/home?loginBack=${location.href}`);
}
return {}; return {};
} }
); );

View File

@ -1,6 +1,6 @@
<template> <template>
<van-loading v-if="loadingStatus" class="pageNoneLoading_Vant" size="24px" color="#0094ff" vertical>加载中...</van-loading> <van-loading v-if="loadingStatus" class="pageNoneLoading_Vant" size="24px" color="#0094ff" vertical>加载中...</van-loading>
<div v-else class="pageBody"> <div v-else class="comProductBody">
<ul class="typeBox"> <ul class="typeBox">
<template v-for="(item) in showData" :key="item"> <template v-for="(item) in showData" :key="item">
<li <li
@ -556,7 +556,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.pageBody { .comProductBody {
width: 375px; width: 375px;
min-height: 100vh; min-height: 100vh;
background-color: #ffffff; background-color: #ffffff;

View File

@ -1,6 +1,6 @@
<template> <template>
<van-loading v-if="loadingStatus" class="pageNoneLoading_Vant" size="24px" color="#0094ff" vertical>加载中...</van-loading> <van-loading v-if="loadingStatus" class="pageNoneLoading_Vant" size="24px" color="#0094ff" vertical>加载中...</van-loading>
<div v-else class="pageBody"> <div v-else class="orderListBody">
<van-pull-refresh class="refreshBody" v-model="refreshLoading" @refresh="onRefresh"> <van-pull-refresh class="refreshBody" v-model="refreshLoading" @refresh="onRefresh">
<ul class="listBox"> <ul class="listBox">
<li @click="toDetail(item)" v-for="(item, index) in listData" :key="index"> <li @click="toDetail(item)" v-for="(item, index) in listData" :key="index">
@ -79,7 +79,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.pageBody { .orderListBody {
width: 375px; width: 375px;
min-height: 100vh; min-height: 100vh;
background-color: #f9fafb; background-color: #f9fafb;

View File

@ -78,7 +78,7 @@ import "./index.scss";
import { DETAIL_QUERY, EXCHANGE, ORDER_QUERY } from "@/router/api"; import { DETAIL_QUERY, EXCHANGE, ORDER_QUERY } from "@/router/api";
import { btnMap, btnHighLight, btnPoll, pollTimeNum } from "@/utils/datas"; import { btnMap, btnHighLight, btnPoll, pollTimeNum } from "@/utils/datas";
import { channelJudge } from "@/utils/empower"; import { channelJudge } from "@/utils/empower";
import { phoneRegex, startsWith, urlToLocal } from "@/utils/methods"; import { phoneRegex, receiveAgain, startsWith, urlToLocal } from "@/utils/methods";
export default { export default {
name: "cmsCash", name: "cmsCash",
@ -226,6 +226,7 @@ export default {
} }
// -/ // -/
if (!account) return channelJudge(this.cash.channel, this.cash.receive_mode, { id: this.goodId }); if (!account) return channelJudge(this.cash.channel, this.cash.receive_mode, { id: this.goodId });
if (!await receiveAgain()) return;
this.overlayLoad = true; this.overlayLoad = true;
const res = await EXCHANGE({ const res = await EXCHANGE({
key: this.key, key: this.key,

View File

@ -173,4 +173,7 @@
color: #45464b !important; color: #45464b !important;
word-wrap: break-word; /* 强制换行 */ word-wrap: break-word; /* 强制换行 */
} }
}
.van-overflow-hidden {
overflow: visible !important;
} }

View File

@ -73,7 +73,7 @@
import "./index.scss"; import "./index.scss";
import config from "./config"; import config from "./config";
import { DETAIL_QUERY, GET_THEME, EXCHANGE, ORDER_QUERY, GET_SMS } from "@/router/api"; import { DETAIL_QUERY, GET_THEME, EXCHANGE, ORDER_QUERY, GET_SMS } from "@/router/api";
import { phoneRegex } from "@/utils/methods"; import { phoneRegex, receiveAgain } from "@/utils/methods";
import { btnMap, btnHighLight, btnPoll, pollTimeNum } from "@/utils/datas"; import { btnMap, btnHighLight, btnPoll, pollTimeNum } from "@/utils/datas";
import { startsWith } from "@/utils/methods"; import { startsWith } from "@/utils/methods";
@ -273,6 +273,7 @@ export default {
} }
account = this.inputVal; account = this.inputVal;
} }
if (!await receiveAgain()) return;
this.overlayLoad = true; this.overlayLoad = true;
const res = await EXCHANGE({ const res = await EXCHANGE({
key: this.key, key: this.key,

View File

@ -71,7 +71,7 @@ import "./index.scss";
import config from "./config"; import config from "./config";
import { GET_THEME, DETAIL_QUERY, ORDER_QUERY, EXCHANGE } from "@/router/api"; import { GET_THEME, DETAIL_QUERY, ORDER_QUERY, EXCHANGE } from "@/router/api";
import { btnHighLight, btnMap, btnPoll, pollTimeNum } from "@/utils/datas"; import { btnHighLight, btnMap, btnPoll, pollTimeNum } from "@/utils/datas";
import { phoneRegex, startsWith } from "@/utils/methods"; import { phoneRegex, receiveAgain, startsWith } from "@/utils/methods";
import dayjs from "dayjs"; import dayjs from "dayjs";
// //
@ -290,6 +290,7 @@ export default {
} else { } else {
return showToast('仅支持支付宝-账号领取或云闪付'); return showToast('仅支持支付宝-账号领取或云闪付');
} }
if (!await receiveAgain()) return;
this.overlayLoad = true; this.overlayLoad = true;
const res = await EXCHANGE({ const res = await EXCHANGE({
key: this.key, key: this.key,

View File

@ -9,7 +9,7 @@
v-else v-else
:slides-per-view="3" :slides-per-view="3"
:space-between="8" :space-between="8"
:loop="true" :loop="pageData.length >=3"
:loopAdditionalSlides="2" :loopAdditionalSlides="2"
:centered-slides="true" :centered-slides="true"
:initialSlide="initialSlide" :initialSlide="initialSlide"
@ -110,7 +110,7 @@ export default {
data() { data() {
return { return {
// modules: [ Pagination, Autoplay ], // modules: [ Pagination, Autoplay ],
initialSlide: 1, initialSlide: 0,
key: this.$route.query.key || localStorage.getItem('key'), key: this.$route.query.key || localStorage.getItem('key'),
// jumpPath: sessionStorage.getItem('jumpPath'), // jumpPath: sessionStorage.getItem('jumpPath'),
loadingStatus: false, loadingStatus: false,
@ -235,8 +235,9 @@ export default {
...this.cssData, ...this.cssData,
...JSON.parse(res.data.config) ...JSON.parse(res.data.config)
}; };
this.pageData = this.cssData.contentType === 1 ? [ ...changeData, ...changeData ] : [ ...changeData ]; // const lbBool = ((this.cssData.contentType === 1) && (changeData.length >= 3));
this.initialSlide = changeData.length; this.pageData = lbBool ? [ ...changeData, ...changeData ] : [ ...changeData ]; //
this.initialSlide = lbBool ? changeData.length : 0;
this.loadingStatus = false; this.loadingStatus = false;
} else { } else {
showToast(res?.message || '请求失败'); showToast(res?.message || '请求失败');

View File

@ -57,7 +57,7 @@ import "./index.scss";
import { DETAIL_QUERY, EXCHANGE, ORDER_QUERY } from "@/router/api"; import { DETAIL_QUERY, EXCHANGE, ORDER_QUERY } from "@/router/api";
import { btnMap, btnHighLight, btnPoll, pollTimeNum } from "@/utils/datas"; import { btnMap, btnHighLight, btnPoll, pollTimeNum } from "@/utils/datas";
import { channelJudge } from "@/utils/empower"; import { channelJudge } from "@/utils/empower";
import { startsWith, urlToLocal } from "@/utils/methods"; import { startsWith, urlToLocal, receiveAgain } from "@/utils/methods";
export default { export default {
name: "cmsVoucher", name: "cmsVoucher",
@ -154,6 +154,7 @@ export default {
} }
// -/ // -/
if (!account) return channelJudge(this.info.channel, this.info.receive_mode, { id: this.goodId }); if (!account) return channelJudge(this.info.channel, this.info.receive_mode, { id: this.goodId });
if (!await receiveAgain()) return;
this.overlayLoad = true; this.overlayLoad = true;
const res = await EXCHANGE({ const res = await EXCHANGE({
key: this.key, key: this.key,