liveh5-nuxt/app/components/slider-verify/slider-verify/index.vue
xingyy e9896d86d6 feat(login): 实现滑动验证码功能
- 新增滑动验证码
2025-03-12 10:18:30 +08:00

418 lines
9.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="slider-verify-box" v-if="isShow">
<view :style="`background-image: url('../../static/image/slider-verify/${
$i18n.locale
}/${isError ? 'bg_error' : 'bg'}.png');`" class="verifyBox">
<!-- <view class="slider-title">{{ $t('authentication.title') }}</view> -->
<image class="slider-verify-box-close" src="/static/image/icon/close.png" @click.stop="hideSliderBox">
</image>
<view class="slide-content">
<view class="slider-pintu">
<!-- <u-icon
name="reload"
size="32"
color="#fff"
class="reload"
@tap="refreshVerify"
v-if="!isLoading"
></u-icon> -->
<image src="../../static/image/slider-verify/reload.png" mode="widthFix" style="width: 38rpx"
class="reload" @tap="refreshVerify" v-if="!isLoading"></image>
<view class="load" v-if="isLoading">
<van-loading type="spinner" />
<!-- <u-loading-icon text="" textSize="16" :vertical="true"></u-loading-icon> -->
</view>
<template v-else>
<image id="pintuImg" :src="canvasSrc" class="pintu"></image>
<view class="pintukuai" :style="{ top: '0px', left: oldx + 'px' }">
<image :src="blockSrc" :style="{
top: blockY + 'px',
left: oldx + 'px',
width: blockWidth + 'px',
height: blockHeight + 'px'
}"></image>
</view>
<view class="mark" v-if="isMess">
<image :src="`../../static/image/slider-verify/${$i18n.locale}/error.png`" mode="widthFix"
v-if="isError"></image>
<image :src="`../../static/image/slider-verify/${$i18n.locale}/success.png`" mode="widthFix"
v-else></image>
</view>
</template>
</view>
<view class="slider-movearea" @touchend="endTouchMove">
<movable-area :animation="true">
<movable-view :class="
isLoading
? 'movable-view btn_info'
: isError
? 'movable-view btn_error'
: 'movable-view btn_success'
" :x="x" direction="horizontal" @change="startMove"></movable-view>
</movable-area>
<view class="huadao">{{
$t('authentication.content')
}}</view>
<view :class="
isError ? 'huadao_done error_bg' : 'huadao_done'
" :style="'width:' + doneWindth + 'px;'"></view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
postDataByParams
} from '../../utils/api.js'
export default {
name: 'slider-verify',
props: {
isShow: {
type: Boolean,
default: true
}
},
data() {
return {
x: 0, //初始距离
oldx: 0, //移动的距离
top: 0, //拼图的top距离
canvasSrc: '',
blockSrc: '',
blockY: '',
nonceStr: '',
blockWidth: 50, //块图像的宽度(blockWidth大于14)
blockHeight: 50,
isLoading: true,
doneWindth: 0,
isError: false,
isMess: false
}
},
watch: {
// 每次打开重新刷新拼图
isShow(newValue, oldValue) {
if (newValue) {
this.refreshVerify() //刷新
}
}
},
mounted() {
var that = this
// that.refreshVerify();
that.getCaptcha()
// console.log(this.$i18n.locale, 'this.$i18n.locale')
},
methods: {
async getCaptcha() {
this.isLoading = true
var that = this
let url = 'generate/captcha'
let params = {
canvasWidth: 320, //画布的宽度(canvasWidth大于41并且canvasWidth-10/2 - 1> blockWidth)
canvasHeight: 190, //画布的高度(canvasHeight大于26并且 canvasHeight - blockHeight > 11
blockWidth: this.blockWidth, //块图像的宽度(blockWidth大于14)
blockHeight: this.blockHeight, //块图像的高度(blockHeight大于14)
// blockRadius: 9, //块图像的圆角半径
place: 0 //图像来源标识0表示URL下载1表示本地文件 一般用0
}
postDataByParams(url, params)
.then((res) => {
if (res.status === 0) {
that.canvasSrc = 'data:image/jpg;base64,' + res.data.canvasSrc
that.blockSrc = 'data:image/jpg;base64,' + res.data.blockSrc
that.blockY = res.data.blockY
that.nonceStr = res.data.nonceStr
that.isLoading = false
} else {
this.$emit('sliderError', encodeURIComponent(JSON.stringify(res)))
}
})
.catch((err) => {
this.$emit('sliderError', encodeURIComponent(JSON.stringify(err)), true)
});
},
//刷新验证
refreshVerify() {
this.x = 1
this.oldx = 1
setTimeout(() => {
this.x = 0
this.oldx = 0
this.doneWindth = 0
}, 300)
this.getCaptcha()
this.isError = false
this.isMess = false
},
/* 滑动中 */
startMove(e) {
// console.log(e.detail.x)
this.oldx = e.detail.x / 2 - 5
if (e.detail.x > 1) {
this.doneWindth = e.detail.x + 30
} else {
this.doneWindth = 0
}
},
/* 滑动结束 */
async endTouchMove() {
var that = this
// console.log('滑块结束')
let url = 'validate/captcha'
let params = {
nonceStr: this.nonceStr,
blockX: this.oldx * 2 + 5
}
postDataByParams(url, params)
.then((res) => {
this.isMess = true
if (res.status == 0) {
setTimeout(() => {
that.$emit('touchSliderResult', res.data.nonceStr)
}, 1000)
} else {
this.isError = true
this.$emit('sliderError', encodeURIComponent(JSON.stringify(res)))
}
})
.catch((err) => {
this.$emit('sliderError', encodeURIComponent(JSON.stringify(err)), true)
});
},
/* 重置阴影位置 */
/* resetMove() {
this.x = 1;
this.oldx = 1;
setTimeout(() => {
this.x = 0;
this.oldx = 0;
}, 300);
}, */
// 关闭
closeSlider() {
this.$emit('touchSliderResult', false)
},
//隐藏滑块验证弹窗
hideSliderBox() {
this.$emit('hideSliderBox')
}
}
}
</script>
<style lang="scss">
.slider-verify-box {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
}
.error_box {
// background: url('../../static/image/slider-verify/bg_error.png') no-repeat !important;
background-size: 100% 100% !important;
}
.verifyBox {
// width: 588rpx;
// height: 662rpx;
padding: 218rpx 45rpx 30rpx 45rpx;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// width: 85%;
// background-color: #fff;
background-repeat: no-repeat;
// background: url('../../static/image/slider-verify/bg.png') no-repeat;
background-size: 100% 100%;
border-radius: 20upx;
// box-shadow: 0 0 5upx rgba(0, 0, 0, 1);
.slider-verify-box-close {
width: 40rpx;
height: 40rpx;
position: absolute;
top: 0;
right: 0;
}
.slider-title {
font-size: 36upx;
text-align: center;
padding: 12rpx 0;
color: #000000;
}
.slide-content {
// width: 560rpx;
// padding: 0 ;
// margin: 0 auto;
.slide-tips {
font-size: 28rpx;
color: rgba(2, 20, 33, 0.45);
padding: 0.5em 0;
}
.slider-pintu {
position: relative;
width: 100%;
border-radius: 10rpx;
overflow: hidden;
.reload {
position: absolute;
right: 10rpx;
top: 10rpx;
z-index: 110;
}
.load {
width: 320px;
height: 190px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
background: #000000;
}
.pintu {
width: 320px;
height: 190px;
display: block;
margin: 0 auto;
}
.pintukuai {
position: absolute;
/* top: 0;
left: 0; */
z-index: 100;
box-shadow: 0 0 5upx rgba(0, 0, 0, 0.3);
image {
display: block;
position: absolute;
top: 0;
left: 0;
/* width: 560rpx;
height: 315rpx; */
}
}
.mark {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #000000;
z-index: 101;
display: flex;
align-items: center;
justify-content: center;
image {
width: 60%;
height: 50rpx;
}
}
}
.yinying {
position: absolute;
width: 120rpx;
height: 120rpx;
background-color: rgba(0, 0, 0, 0.5);
}
}
}
.slider-movearea {
position: relative;
height: 80upx;
width: 320px;
margin-top: 25rpx;
movable-area {
width: 100%;
height: 64rpx;
.movable-view {
width: 100upx;
height: 74rpx;
border-radius: 40upx;
// background-color: #699A70;
background-image: url(../../static/image/slider-verify/icon-button-normal.png);
background-repeat: no-repeat;
background-size: auto 30upx;
background-position: center;
position: relative;
z-index: 100;
border: 1px solid transparent;
}
}
}
.btn_info {
background-color: #878787 !important;
}
.btn_error {
background-color: #fd343c !important;
}
.btn_success {
background-color: #699a70 !important;
}
.error_bg {
background-color: #ffb7ba !important;
}
.success_bg {
background-color: #aad0b0 !important;
}
.huadao {
width: 100%;
// width: 320px;
height: 66upx;
line-height: 66upx;
background: #ededed;
// box-shadow: inset 0 0 5upx #EDEDED;
border-radius: 40rpx;
color: #bcbcbc;
text-align: center;
box-sizing: border-box;
position: absolute;
top: 7rpx;
left: 0;
font-size: 28rpx;
z-index: 99;
}
.huadao_done {
height: 66upx;
line-height: 66upx;
background: #aad0b0;
border-radius: 40rpx;
text-align: center;
box-sizing: border-box;
position: absolute;
top: 7rpx;
left: 0;
font-size: 28rpx;
z-index: 99;
}
</style>