You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
433 lines
12 KiB
433 lines
12 KiB
4 months ago
|
<template>
|
||
|
<view>
|
||
|
<view class="cu-modal" :class="show?'show':''" @click.stop="close()">
|
||
|
<view @click.stop @touchmove.stop="'diyname'" class="cu-dialog captchaDialog bg-white text-left"
|
||
|
style="border-radius: 40rpx">
|
||
|
<view class="padding-xs margin-top text-center">
|
||
|
<text class="text-grey text-df">人机验证</text>
|
||
|
</view>
|
||
|
<view class="margin-top-xs text-center">
|
||
|
<text class="text-lg text-black">{{title}}</text>
|
||
|
</view>
|
||
|
<view class="margin-top-lg flex justify-center text-sl">
|
||
|
<view class="captchaImage flex-center bg-white">
|
||
|
<view v-if="captchaSrc!=''" class="captchaData" :style="{ transform: `rotate(${imageTransform}deg)`, backgroundImage: `url(${captchaSrc})` }"></view>
|
||
|
<text v-if="pageLoading || captchaSrcLoading" class="captchaIcon wlIcon-jiazai2 text-orange wlIconfont-spin"></text>
|
||
|
<text v-if="captchaError" name="close" class="captchaIcon wlIcon-31guanbi text-white"></text>
|
||
|
<template v-if="isDrag">
|
||
|
<!-- 辅助虚线 -->
|
||
|
<view class="verticalArrow"></view>
|
||
|
<view class="transverseArrow"></view>
|
||
|
</template>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="capFoot margin-top-lg margin-bottom-sm padding-lr-sm">
|
||
|
<view class="slider solid" id="slider">
|
||
|
<view class="sliderItem clickActive-gray text-xxl" :class="captchaError?'shake':''" id="sliderItem"
|
||
|
@touchstart="dragStart" @touchmove.prevent="dragMove" @touchend="dragEnd"
|
||
|
@mousedown="dragStart" @mousemove.prevent="dragMove" @mouseup="dragEnd"
|
||
|
@dragstart="dragStart" @dragend="dragEnd" @dragover.stop @drop="dragMove" :style="{
|
||
|
left: `${sliderItemLeft}px`,
|
||
|
backgroundColor: captchaError?'red':(isDrag?'#fe6600':'#fff')
|
||
|
}">
|
||
|
<text v-if="dragLoading || pageLoading || captchaSrcLoading" class="wlIcon-jiazai2 wlIconfont-spin"></text>
|
||
|
<text v-else :class="dragIcon" class="wlIcon-shoushouzhang"></text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="text-center text-sm margin-bottom">
|
||
|
<view class="text-xs text-gray">
|
||
|
<text>由</text>
|
||
|
<text class="padding-lr-xs text-red">Wanl</text>
|
||
|
<text>提供技术支持</text>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="captchaCloseView">
|
||
|
<text @click="close" name="close" class="wlIcon-31guanbi text-white text-sl"></text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
|
||
|
</view>
|
||
|
</template>
|
||
|
<script>
|
||
|
let pmsSuccess = null; //验证成功事件
|
||
|
let pmsError = null; //弹窗关闭事件
|
||
|
export default {
|
||
|
data() {
|
||
|
return {
|
||
|
title: '',
|
||
|
show: false, //控制弹窗是否展示
|
||
|
captchaSrc: "", //验证码图片 base64
|
||
|
|
||
|
isDrag: false, //是否拖动中
|
||
|
dragStartTime: 0, //拖动开始时间
|
||
|
dragUseTime: 0, //拖动用时
|
||
|
mouseTrackList: [], //鼠标轨迹列表
|
||
|
|
||
|
sliderItemInitX: 0, //滑动按钮初始X坐标
|
||
|
sliderItemLeftMax: 0, //滑块允许距离左边最大长度
|
||
|
sliderItemLeft: 0,
|
||
|
|
||
|
captchaSrcLoading: false, //请求图片验证码中
|
||
|
pageLoading: false, //页面加载中,验证码图片加载中
|
||
|
dragLoading: false, //拖动按钮loading状态
|
||
|
captchaError: false, //是否验证失败
|
||
|
append: {} //附加信息
|
||
|
}
|
||
|
},
|
||
|
computed: {
|
||
|
/**
|
||
|
* 图片旋转角度
|
||
|
*/
|
||
|
imageTransform() {
|
||
|
if (this.sliderItemLeft == 0) return 0;
|
||
|
return Math.ceil((360 / this.sliderItemLeftMax) * this.sliderItemLeft * 100) / 100;
|
||
|
},
|
||
|
/**
|
||
|
* 拖动条上的图标
|
||
|
*/
|
||
|
dragIcon() {
|
||
|
let white = 'text-white';
|
||
|
let black = 'text-black';
|
||
|
if (this.captchaError) return white;
|
||
|
if (this.isDrag) return white;
|
||
|
return black;
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
/**
|
||
|
* 外部调用
|
||
|
*/
|
||
|
init(append) {
|
||
|
return new Promise((yes, err) => {
|
||
|
pmsSuccess = yes;
|
||
|
pmsError = err;
|
||
|
this.append = append;
|
||
|
this.captchaSrc = false;
|
||
|
this.captchaError = false;
|
||
|
this.isDrag = false;
|
||
|
this.captchaSrcLoading = false;
|
||
|
this.pageLoading = false;
|
||
|
this.dragLoading = false;
|
||
|
this.show = true;
|
||
|
if (this.sliderItemLeftMax == 0) {
|
||
|
this.pageLoading = true;
|
||
|
setTimeout(() => {
|
||
|
this.$nextTick(() => {
|
||
|
this.getRect('#slider').then(res => {
|
||
|
let sliderWidth = res.width;
|
||
|
this.getRect('#sliderItem').then(res => {
|
||
|
this.sliderItemInitX = res.left;
|
||
|
this.sliderItemLeftMax = sliderWidth - res.width;
|
||
|
this.pageLoading = false;
|
||
|
this.getCaptchaSrc()
|
||
|
}).catch((err) => {
|
||
|
console.log("获取节点信息失败 2 -> ", err);
|
||
|
})
|
||
|
}).catch((err) => {
|
||
|
console.log("获取节点信息失败 1 -> ", err);
|
||
|
})
|
||
|
})
|
||
|
}, 500)
|
||
|
} else {
|
||
|
this.getCaptchaSrc()
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
/**
|
||
|
* 手动关闭弹窗
|
||
|
*/
|
||
|
close() {
|
||
|
this.show = false;
|
||
|
pmsError(); //执行关闭回调事件
|
||
|
},
|
||
|
/**
|
||
|
* 拖动开始事件
|
||
|
*/
|
||
|
dragStart(e) {
|
||
|
let that = this;
|
||
|
if (that.dragLoading) return false;
|
||
|
if (that.pageLoading) return false;
|
||
|
if (that.captchaSrcLoading) return false;
|
||
|
that.mouseTrackList = []; //初始化轨迹列表
|
||
|
that.dragStartTime = new Date().getTime(); //记录开始时间
|
||
|
that.isDrag = true;
|
||
|
that.addMouseTrack(e);
|
||
|
},
|
||
|
/**
|
||
|
* 手指移动事件
|
||
|
*/
|
||
|
dragMove(e) {
|
||
|
let that = this;
|
||
|
if (that.dragLoading) return false;
|
||
|
if (that.pageLoading) return false;
|
||
|
if (that.captchaSrcLoading) return false;
|
||
|
if (!that.isDrag) return false;
|
||
|
let maringLeft = (e.clientX ? e.clientX : e.changedTouches[0].clientX) - that.sliderItemInitX - 30;
|
||
|
if (maringLeft < 0) maringLeft = 0;
|
||
|
if (maringLeft > that.sliderItemLeftMax) maringLeft = that.sliderItemLeftMax;
|
||
|
that.sliderItemLeft = maringLeft;
|
||
|
that.addMouseTrack(e);
|
||
|
},
|
||
|
/**
|
||
|
* 拖动结束事件
|
||
|
*/
|
||
|
dragEnd(e) {
|
||
|
let that = this;
|
||
|
if (that.dragLoading) return false;
|
||
|
if (that.pageLoading) return false;
|
||
|
if (that.captchaSrcLoading) return false;
|
||
|
that.addMouseTrack(e);
|
||
|
that.isDrag = false;
|
||
|
that.dragLoading = true; //开启loading准备ajax
|
||
|
that.dragUseTime = new Date().getTime() - that.dragStartTime; //计算拖动时间
|
||
|
that.checkCaptcha();
|
||
|
},
|
||
|
/**
|
||
|
* 加载验证码
|
||
|
*/
|
||
|
getCaptchaSrc() {
|
||
|
this.captchaSrcLoading = true;
|
||
|
this.sliderItemLeft = 0;
|
||
|
this.captchaSrc = '';
|
||
|
this.title = '拖动滑块使图片角度为正';
|
||
|
uni.request({
|
||
|
url: '/wanlshop/sms/captcha',
|
||
|
method: 'POST',
|
||
|
withCredentials: true,
|
||
|
complete: res => {
|
||
|
this.captchaSrcLoading = false;
|
||
|
if(res.res.code == 1){
|
||
|
this.dragLoading = false;
|
||
|
this.captchaSrc = `data:image/png;base64,${res.res.data.captchaSrc}`
|
||
|
// // #ifdef MP
|
||
|
// uni.removeStorageSync('wanlshop:cookie');
|
||
|
// uni.setStorageSync('wanlshop:cookie', res.header['Set-Cookie'].split(";")[0]);
|
||
|
// // #endif
|
||
|
// #ifdef MP || H5
|
||
|
uni.removeStorageSync('wanlshop:cookie');
|
||
|
uni.setStorageSync('wanlshop:cookie', res.res.data.captchaCookie);
|
||
|
// #endif
|
||
|
|
||
|
}else{
|
||
|
this.$wanlshop.msg(res.res.msg);
|
||
|
setTimeout(()=>{ // 防止闪烁,延迟一秒
|
||
|
this.close(); //关闭验证码
|
||
|
},1600)
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
/**
|
||
|
* 提交效验
|
||
|
*/
|
||
|
checkCaptcha() {
|
||
|
uni.request({
|
||
|
url: '/wanlshop/sms/check',
|
||
|
method: 'POST',
|
||
|
withCredentials: true,
|
||
|
// #ifdef MP || H5
|
||
|
header:{
|
||
|
'Cookie': uni.getStorageSync('wanlshop:cookie')
|
||
|
},
|
||
|
// #endif
|
||
|
data: {
|
||
|
append: JSON.stringify(this.append),
|
||
|
rotationAngle: this.imageTransform, //旋转角度,为'坏人'增加繁琐的工作
|
||
|
mouseTrackList: JSON.stringify(this.mouseTrackList), //滑动轨迹
|
||
|
dragUseTime: this.dragUseTime, //拖动用时
|
||
|
dragStartTime: this.dragStartTime, //拖动开始时间
|
||
|
keyO: Math.ceil(new Date().getTime() * Math.random() * 678951),
|
||
|
keyA: Math.ceil(new Date().getTime() * Math.random() * 123456),
|
||
|
keyD: Math.ceil(new Date().getTime() * Math.random() * 781545),
|
||
|
hash: Math.ceil(new Date().getTime() * Math.random() * 456789),
|
||
|
md5Key: Math.ceil(new Date().getTime() * Math.random() * 7891),
|
||
|
},
|
||
|
complete: res => {
|
||
|
if(res.res.code == 1){
|
||
|
this.title = '恭喜你,验证成功!';
|
||
|
this.show = false;
|
||
|
pmsSuccess(); //回调
|
||
|
}else{
|
||
|
this.title = res.res.msg;
|
||
|
this.dragLoading = false;
|
||
|
this.captchaError = true;
|
||
|
setTimeout(() => {
|
||
|
this.captchaError = false;
|
||
|
if(res.res.code == 0){
|
||
|
this.getCaptchaSrc();
|
||
|
}else{
|
||
|
if(res.res.data.getNewCaptcha){
|
||
|
this.getCaptchaSrc();
|
||
|
}else{
|
||
|
this.captchaSrcLoading = false;
|
||
|
this.sliderItemLeft = 0;
|
||
|
}
|
||
|
}
|
||
|
}, 1000)
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
/**
|
||
|
* 添加滑动数据进鼠标轨迹列表
|
||
|
*/
|
||
|
addMouseTrack() {
|
||
|
let lastTime = this.mouseTrackList == '' ? this.dragStartTime : this.mouseTrackList[this.mouseTrackList
|
||
|
.length - 1].t;
|
||
|
let now = new Date().getTime();
|
||
|
if (lastTime + 200 <= now) {
|
||
|
this.mouseTrackList.push({
|
||
|
r: Math.ceil(this.imageTransform / 360 * 10000) / 100, //当前角度
|
||
|
t: now, //当前时间戳
|
||
|
})
|
||
|
}
|
||
|
},
|
||
|
getRect(selector, all) {
|
||
|
return new Promise(resolve => {
|
||
|
let query = null;
|
||
|
// 支付宝小程序不能加后面的.in(this),是它自身的限制
|
||
|
// #ifndef MP-ALIPAY
|
||
|
query = uni.createSelectorQuery().in(this)
|
||
|
// #endif
|
||
|
// #ifdef MP-ALIPAY
|
||
|
query = uni.createSelectorQuery()
|
||
|
// #endif
|
||
|
query[all ? 'selectAll' : 'select'](selector)
|
||
|
.boundingClientRect(rect => {
|
||
|
if (all && Array.isArray(rect) && rect.length) {
|
||
|
resolve(rect)
|
||
|
}
|
||
|
if (!all && rect) {
|
||
|
resolve(rect)
|
||
|
}
|
||
|
})
|
||
|
.exec()
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
<style lang="scss" scoped>
|
||
|
.captchaImage {
|
||
|
height: 320rpx;
|
||
|
width: 320rpx;
|
||
|
border-radius: 50%;
|
||
|
overflow: hidden;
|
||
|
position: relative;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
.captchaData{
|
||
|
height: 320rpx;
|
||
|
width: 320rpx;
|
||
|
border-radius: 50%;
|
||
|
overflow: hidden;
|
||
|
background-color: #f5f5f5;
|
||
|
background-position: center center;
|
||
|
background-size: cover;
|
||
|
background-repeat: no-repeat;
|
||
|
}
|
||
|
|
||
|
.captchaIcon {
|
||
|
position: absolute;
|
||
|
}
|
||
|
|
||
|
.verticalArrow {
|
||
|
position: absolute;
|
||
|
height: 320rpx;
|
||
|
border-left: 1px dashed #fff;
|
||
|
margin-left: 1px;
|
||
|
width: 1px;
|
||
|
}
|
||
|
|
||
|
.transverseArrow {
|
||
|
position: absolute;
|
||
|
height: 1px;
|
||
|
width: 320rpx;
|
||
|
border-top: 1px dashed #fff;
|
||
|
margin-top: 1px;
|
||
|
overflow: visible;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.capFoot {
|
||
|
height: 60px;
|
||
|
display: flex;
|
||
|
flex-direction: row;
|
||
|
justify-content: center;
|
||
|
|
||
|
.slider {
|
||
|
width: 580rpx;
|
||
|
height: 90rpx;
|
||
|
background-color: #f5f5f5;
|
||
|
border-radius: 900rpx;
|
||
|
overflow: hidden;
|
||
|
position: relative;
|
||
|
|
||
|
.sliderItem {
|
||
|
height: 88rpx;
|
||
|
width: 110rpx;
|
||
|
border-radius: 900rpx;
|
||
|
background-color: white;
|
||
|
position: absolute;
|
||
|
left: 0;
|
||
|
top: 1rpx;
|
||
|
cursor: pointer;
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
-webkit-box-shadow: 0 21px 52px 0 rgba(82, 82, 82, .2);
|
||
|
box-shadow: 0 21px 52px 0 rgba(82, 82, 82, .2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.shake {
|
||
|
animation: shake 800ms ease-in-out;
|
||
|
}
|
||
|
|
||
|
@keyframes shake {
|
||
|
|
||
|
/* 水平抖动,核心代码 */
|
||
|
10%,
|
||
|
90% {
|
||
|
transform: translate3d(-1px, 0, 0);
|
||
|
}
|
||
|
|
||
|
20%,
|
||
|
80% {
|
||
|
transform: translate3d(+2px, 0, 0);
|
||
|
}
|
||
|
|
||
|
30%,
|
||
|
70% {
|
||
|
transform: translate3d(-3px, 0, 0);
|
||
|
}
|
||
|
|
||
|
40%,
|
||
|
60% {
|
||
|
transform: translate3d(+3px, 0, 0);
|
||
|
}
|
||
|
|
||
|
50% {
|
||
|
transform: translate3d(-3px, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.captchaDialog {
|
||
|
position: relative;
|
||
|
overflow: visible;
|
||
|
}
|
||
|
|
||
|
.captchaCloseView {
|
||
|
position: absolute;
|
||
|
bottom: -150rpx;
|
||
|
width: 100%;
|
||
|
height: 100rpx;
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: center;
|
||
|
}
|
||
|
</style>
|