前端加入旋转图像验证组件

This commit is contained in:
2024-08-30 21:33:43 +08:00
parent 39cd4bc66b
commit 41b7a38669

View File

@@ -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 class="image-container"> }}
<img :src="imageBase64" alt="" id="RV-image"> </div>
</div> <div class="image-container">
<div class="slide-container"> <img :src="imageBase64" alt="" id="RV-image">
<div class="slider" id="RV-slider"> </div>
<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 class="slide-container">
<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>
</div>
</div>
</div> </div>
</div> </div>
</div> </transition>
</div>
</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>