前端加入旋转图像验证组件
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import ServerSDK from '@/assets/ServerSDK';
|
/**
|
||||||
|
* 旋转图像验证码组件
|
||||||
|
* @event success 验证成功
|
||||||
|
* @event fail 验证失败
|
||||||
|
* @requires ServerSDK
|
||||||
|
* @since 1.0.1
|
||||||
|
*/
|
||||||
|
import { request } from '@/lib/request';
|
||||||
import { onBeforeMount, onMounted, ref } from 'vue';
|
import { onBeforeMount, onMounted, ref } from 'vue';
|
||||||
let imageBase64 = ref('');
|
let imageBase64 = ref('');
|
||||||
const emit = defineEmits(['fail', 'success'])
|
const emit = defineEmits(['fail', 'success'])
|
||||||
@@ -7,10 +14,11 @@ let onVerifying = ref(false);
|
|||||||
let onVerifyFail = ref(false);
|
let onVerifyFail = ref(false);
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
try {
|
try {
|
||||||
let res = await ServerSDK.GetRotationVerification();
|
let res = await request.get('captcha')
|
||||||
imageBase64.value = res;
|
localStorage.setItem('captcha-session', res.data.session)
|
||||||
|
imageBase64.value = res.data.imgPreStr + res.data.img;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("获取图像验证码失败:"+error);
|
console.log("获取图像验证码失败:" + error);
|
||||||
emit('fail');
|
emit('fail');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -21,17 +29,16 @@ onMounted(() => {
|
|||||||
let silderMoveable = true;// 是否可以开始移动标识位
|
let silderMoveable = true;// 是否可以开始移动标识位
|
||||||
let startX;
|
let startX;
|
||||||
let deltaX;// 拖动距离
|
let deltaX;// 拖动距离
|
||||||
slider.addEventListener('mousedown', function(e) {
|
slider.addEventListener('mousedown', function (e) {
|
||||||
if(silderMoveable)
|
if (silderMoveable) {
|
||||||
{
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
startX = e.clientX;
|
startX = e.clientX;
|
||||||
onVerifyFail.value = false;
|
onVerifyFail.value = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
slider.addEventListener('touchstart', function(e) {
|
slider.addEventListener('touchstart', function (e) {
|
||||||
if(silderMoveable) {
|
if (silderMoveable) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
startX = e.touches[0].clientX;
|
startX = e.touches[0].clientX;
|
||||||
@@ -39,52 +46,53 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
|
|
||||||
document.addEventListener('mousemove', function(e) {
|
document.addEventListener('mousemove', function (e) {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
silderMoveable = false;// 产生了位移,则需等待验证后才可再次移动
|
silderMoveable = false;// 产生了位移,则需等待验证后才可再次移动
|
||||||
deltaX = e.clientX - startX;
|
deltaX = e.clientX - startX;
|
||||||
// 对位移距离限幅
|
// 对位移距离限幅
|
||||||
if(deltaX < 0)
|
if (deltaX < 0)
|
||||||
deltaX = 0;
|
deltaX = 0;
|
||||||
if(deltaX > 200)
|
if (deltaX > 200)
|
||||||
deltaX = 200;
|
deltaX = 200;
|
||||||
// 调整滑块条位置样式
|
// 调整滑块条位置样式
|
||||||
slider.style.transform = ` translateX(${deltaX}px)`;
|
slider.style.transform = ` translateX(${deltaX}px)`;
|
||||||
// 调整图片旋转样式 (200 -> 360)映射 y = 9/5x
|
// 调整图片旋转样式 (200 -> 360)映射 y = 9/5x
|
||||||
imageEle.style.transform = `rotate(${deltaX*9/5}deg)`
|
imageEle.style.transform = `rotate(${deltaX * 9 / 5}deg)`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('touchmove', function(e) {
|
document.addEventListener('touchmove', function (e) {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
silderMoveable = false;
|
silderMoveable = false;
|
||||||
deltaX = e.touches[0].clientX - startX;
|
deltaX = e.touches[0].clientX - startX;
|
||||||
if(deltaX < 0) deltaX = 0;
|
if (deltaX < 0) deltaX = 0;
|
||||||
if(deltaX > 200) deltaX = 200;
|
if (deltaX > 200) deltaX = 200;
|
||||||
slider.style.transform = `translateX(${deltaX}px)`;
|
slider.style.transform = `translateX(${deltaX}px)`;
|
||||||
imageEle.style.transform = `rotate(${deltaX*9/5}deg)`;
|
imageEle.style.transform = `rotate(${deltaX * 9 / 5}deg)`;
|
||||||
}
|
}
|
||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('mouseup', async () => {
|
document.addEventListener('mouseup', async () => {
|
||||||
if(isDragging)
|
if (isDragging) {
|
||||||
{
|
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
if(!deltaX)
|
if (!deltaX) {
|
||||||
{
|
|
||||||
silderMoveable = true;// 位移距离为0,无需验证可以继续移动
|
silderMoveable = true;// 位移距离为0,无需验证可以继续移动
|
||||||
}else{
|
} else {
|
||||||
try {
|
try {
|
||||||
onVerifying.value = true;
|
onVerifying.value = true;
|
||||||
let res = await ServerSDK.CheckRotationVerification(deltaX * 9 / 5);
|
let res = await request.post('checkCaptcha', {
|
||||||
switch (res) {
|
session: localStorage.getItem('captcha-session'),
|
||||||
case 1:
|
rotateDeg: deltaX * 9 / 5
|
||||||
|
})
|
||||||
|
switch (res.code) {
|
||||||
|
case 0:
|
||||||
// 验证成功
|
// 验证成功
|
||||||
emit('success');
|
emit('success');
|
||||||
break;
|
break;
|
||||||
case -2:
|
case -5002:
|
||||||
// 可以再试一次
|
// 可以再试一次
|
||||||
onVerifyFail.value = true;
|
onVerifyFail.value = true;
|
||||||
slider.style.transition = "transform 0.6s";
|
slider.style.transition = "transform 0.6s";
|
||||||
@@ -98,20 +106,15 @@ onMounted(() => {
|
|||||||
imageEle.style.transition = "transform 0s";
|
imageEle.style.transition = "transform 0s";
|
||||||
}, 600);
|
}, 600);
|
||||||
break;
|
break;
|
||||||
case -1:
|
|
||||||
console.log('验证次数过多,请重试')
|
|
||||||
emit('fail');
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// 大概率是0,过期和不存在
|
console.log('验证session过期、不存在、服务器错误')
|
||||||
console.log('验证session过期或不存在')
|
|
||||||
emit('fail');
|
emit('fail');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("图像验证码错误:"+error);
|
console.log("图像验证码错误:" + error);
|
||||||
emit('fail');
|
emit('fail');
|
||||||
}finally{
|
} finally {
|
||||||
onVerifying.value = false;
|
onVerifying.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,22 +122,23 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('touchend', async () => {
|
document.addEventListener('touchend', async () => {
|
||||||
if(isDragging)
|
if (isDragging) {
|
||||||
{
|
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
if(!deltaX)
|
if (!deltaX) {
|
||||||
{
|
|
||||||
silderMoveable = true;// 位移距离为0,无需验证可以继续移动
|
silderMoveable = true;// 位移距离为0,无需验证可以继续移动
|
||||||
}else{
|
} else {
|
||||||
try {
|
try {
|
||||||
onVerifying.value = true;
|
onVerifying.value = true;
|
||||||
let res = await ServerSDK.CheckRotationVerification(deltaX * 9 / 5);
|
let res = await request.post('checkCaptcha', {
|
||||||
switch (res) {
|
session: localStorage.getItem('captcha-session'),
|
||||||
case 1:
|
rotateDeg: deltaX * 9 / 5
|
||||||
|
})
|
||||||
|
switch (res.code) {
|
||||||
|
case 0:
|
||||||
// 验证成功
|
// 验证成功
|
||||||
emit('success');
|
emit('success');
|
||||||
break;
|
break;
|
||||||
case -2:
|
case -5002:
|
||||||
// 可以再试一次
|
// 可以再试一次
|
||||||
onVerifyFail.value = true;
|
onVerifyFail.value = true;
|
||||||
slider.style.transition = "transform 0.6s";
|
slider.style.transition = "transform 0.6s";
|
||||||
@@ -148,20 +152,15 @@ onMounted(() => {
|
|||||||
imageEle.style.transition = "transform 0s";
|
imageEle.style.transition = "transform 0s";
|
||||||
}, 600);
|
}, 600);
|
||||||
break;
|
break;
|
||||||
case -1:
|
|
||||||
console.log('验证次数过多,请重试')
|
|
||||||
emit('fail');
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// 大概率是0,过期和不存在
|
console.log('验证session过期、不存在、服务器错误')
|
||||||
console.log('验证session过期或不存在')
|
|
||||||
emit('fail');
|
emit('fail');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("图像验证码错误:"+error);
|
console.log("图像验证码错误:" + error);
|
||||||
emit('fail');
|
emit('fail');
|
||||||
}finally{
|
} finally {
|
||||||
onVerifying.value = false;
|
onVerifying.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,25 +170,32 @@ onMounted(() => {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<transition name="el-fade-in-linear">
|
<transition name="el-fade-in-linear">
|
||||||
<div class="verification-bcc" @click="$emit('fail')">
|
<div class="verification-bcc" @click="$emit('fail')">
|
||||||
<div class="verification-container" onclick="event.stopPropagation()">
|
<div class="verification-container" onclick="event.stopPropagation()">
|
||||||
<div class="title">安全验证</div>
|
<div class="title">安全验证</div>
|
||||||
<div class="subtitle">{{ onVerifying ? "正在验证,请稍后..." : (onVerifyFail?"验证失败,请再试一次":"拖动滑块,使图片角度为水平") }}</div>
|
<div class="subtitle">{{ onVerifying ? "正在验证,请稍后..." : (onVerifyFail ? "验证失败,请再试一次" : "拖动滑块,使图片角度为水平")
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
<div class="image-container">
|
<div class="image-container">
|
||||||
<img :src="imageBase64" alt="" id="RV-image">
|
<img :src="imageBase64" alt="" id="RV-image">
|
||||||
</div>
|
</div>
|
||||||
<div class="slide-container">
|
<div class="slide-container">
|
||||||
<div class="slider" id="RV-slider">
|
<div class="slider" id="RV-slider">
|
||||||
<svg t="1706696449802" class="icon" viewBox="0 0 1024 1024" fill="#666" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1924" width="18" height="18"><path d="M567.32505 547.18536c20.970614-21.479197 20.970614-56.307424 0-77.790714L185.251168 77.115332c-20.971637-21.47715-54.975079-21.47715-75.948763 0-20.973684 21.484314-20.973684 56.30947 0 77.793784l344.188016 353.383446-344.188016 353.384469c-20.973684 21.484314-20.973684 56.311517 0 77.79276 20.971637 21.482267 54.975079 21.482267 75.948763 0l382.072858-392.280337 0.001024-0.004094zM440.60802 154.908092l344.18597 353.383446-344.18597 353.385493c-20.973684 21.484314-20.973684 56.311517 0 77.79276 20.972661 21.482267 54.975079 21.482267 75.949786 0l382.074905-392.281361c20.966521-21.478174 20.966521-56.307424 0-77.790714L516.555759 77.115332c-20.972661-21.47715-54.975079-21.47715-75.949786 0-20.971637 21.48329-20.971637 56.30947 0.002047 77.79276z" p-id="1925"></path></svg>
|
<svg t="1706696449802" class="icon" viewBox="0 0 1024 1024" fill="#666" version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" p-id="1924" width="18" height="18">
|
||||||
|
<path
|
||||||
|
d="M567.32505 547.18536c20.970614-21.479197 20.970614-56.307424 0-77.790714L185.251168 77.115332c-20.971637-21.47715-54.975079-21.47715-75.948763 0-20.973684 21.484314-20.973684 56.30947 0 77.793784l344.188016 353.383446-344.188016 353.384469c-20.973684 21.484314-20.973684 56.311517 0 77.79276 20.971637 21.482267 54.975079 21.482267 75.948763 0l382.072858-392.280337 0.001024-0.004094zM440.60802 154.908092l344.18597 353.383446-344.18597 353.385493c-20.973684 21.484314-20.973684 56.311517 0 77.79276 20.972661 21.482267 54.975079 21.482267 75.949786 0l382.074905-392.281361c20.966521-21.478174 20.966521-56.307424 0-77.790714L516.555759 77.115332c-20.972661-21.47715-54.975079-21.47715-75.949786 0-20.971637 21.48329-20.971637 56.30947 0.002047 77.79276z"
|
||||||
|
p-id="1925"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.verification-bcc{
|
.verification-bcc {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -201,7 +207,8 @@ onMounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.verification-container{
|
|
||||||
|
.verification-container {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@@ -210,14 +217,17 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
}
|
}
|
||||||
.verification-container>.title{
|
|
||||||
|
.verification-container>.title {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
.verification-container>.subtitle{
|
|
||||||
|
.verification-container>.subtitle {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
.verification-container>.image-container{
|
|
||||||
|
.verification-container>.image-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
width: 160px;
|
width: 160px;
|
||||||
@@ -228,13 +238,15 @@ onMounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.verification-container>.image-container>img{
|
|
||||||
|
.verification-container>.image-container>img {
|
||||||
width: 226px;
|
width: 226px;
|
||||||
height: 226px;
|
height: 226px;
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
background-color: gray;
|
background-color: gray;
|
||||||
}
|
}
|
||||||
.verification-container>.slide-container{
|
|
||||||
|
.verification-container>.slide-container {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
@@ -243,7 +255,8 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.verification-container>.slide-container>.slider{
|
|
||||||
|
.verification-container>.slide-container>.slider {
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@@ -255,5 +268,4 @@ onMounted(() => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user