feat(puzzleComponent): 添加加载状态并优化验证逻辑

- 在 puzzleComponent 中添加加载状态显示
- 优化验证逻辑,修复验证失败时滑块不重置的问题
- 在 login 页面中集成新的加载状态
- 调整验证码按钮的加载状态显示
This commit is contained in:
xingyy 2025-03-12 15:10:44 +08:00
parent bc30f84ccd
commit 36fab5a203
2 changed files with 81 additions and 36 deletions

View File

@ -1,20 +1,31 @@
<template> <template>
<div class="m-auto bg-white p-15px rd-10px touch-none select-none"> <div class="m-auto bg-white p-15px rd-10px touch-none select-none">
<div class="relative w-full overflow-hidden bg-#f8f8f8 rd-10px" :style="{ width: `${options?.canvasWidth}px`, height: `${options?.canvasHeight}px` }"> <div class="relative w-full overflow-hidden bg-#f8f8f8 rd-10px" :style="{ width: `${options?.canvasWidth}px`, height: `${options?.canvasHeight}px` }">
<!-- 加载状态 -->
<div v-if="loading" class="absolute inset-0 flex flex-col items-center justify-center bg-#f8f8f8">
<div class="fancy-loader">
<div class="fancy-loader-bar"></div>
<div class="fancy-loader-bar"></div>
<div class="fancy-loader-bar"></div>
<div class="fancy-loader-bar"></div>
</div>
</div>
<!-- 背景图 --> <!-- 背景图 -->
<img <img
:src="options?.canvasSrc" v-else
:src="options?.canvasSrc"
class="pointer-events-none w-full h-full" class="pointer-events-none w-full h-full"
ref="bgImage"
ref="bgImage" @load="onImageLoad"
@load="onImageLoad"
@error="onImageError" @error="onImageError"
> >
<!-- 滑块 --> <!-- 滑块 -->
<img <img
:src="options?.blockSrc" :src="options?.blockSrc"
class="absolute cursor-pointer will-change-transform transform-gpu" class="absolute cursor-pointer will-change-transform transform-gpu"
v-if="!loading"
:class="{ 'transition-all duration-300 ease-out': !isDragging }" :class="{ 'transition-all duration-300 ease-out': !isDragging }"
:style="{ :style="{
top: `${options?.blockY}px`, top: `${options?.blockY}px`,
@ -26,7 +37,7 @@
<transition name="fade-slide"> <transition name="fade-slide">
<div <div
v-if="verifyStatus.show" v-if="verifyStatus.show"
class="absolute left-0 bottom-0 w-full h-24px leading-24px text-center text-14px text-white" class="absolute left-0 bottom-0 w-full h-25px leading-25px text-center text-14px text-white"
:class="verifyStatus.type === 'success' ? 'bg-#52c41a' : 'bg-#ff4d4f'" :class="verifyStatus.type === 'success' ? 'bg-#52c41a' : 'bg-#ff4d4f'"
> >
{{ verifyStatus.message }} {{ verifyStatus.message }}
@ -64,7 +75,8 @@
// Props // Props
const props = defineProps({ const props = defineProps({
options:Object options:Object,
loading: Boolean,
}) })
const emit = defineEmits(['leave']) const emit = defineEmits(['leave'])
@ -143,9 +155,6 @@
verifyStatus.type = success ? 'success' : 'error' verifyStatus.type = success ? 'success' : 'error'
verifyStatus.message = success ? '验证成功' : '验证失败' verifyStatus.message = success ? '验证成功' : '验证失败'
isVerifying.value = false isVerifying.value = false
if (!success) moveX.value = 0
setTimeout(() => { setTimeout(() => {
verifyStatus.show = false verifyStatus.show = false
verifyStatus.message = '' verifyStatus.message = ''
@ -190,6 +199,61 @@
transform: translateY(100%); transform: translateY(100%);
opacity: 0; opacity: 0;
} }
/* 加载动画样式 */
.fancy-loader {
display: flex;
align-items: flex-end;
justify-content: center;
width: 60px;
height: 40px;
}
.fancy-loader-bar {
width: 6px;
height: 15px;
margin: 0 3px;
background-color: #1890ff;
border-radius: 3px;
animation: fancy-loading 1s ease-in-out infinite;
}
.fancy-loader-bar:nth-child(1) {
animation-delay: 0s;
}
.fancy-loader-bar:nth-child(2) {
animation-delay: 0.2s;
}
.fancy-loader-bar:nth-child(3) {
animation-delay: 0.4s;
}
.fancy-loader-bar:nth-child(4) {
animation-delay: 0.6s;
}
@keyframes fancy-loading {
0% {
transform: scaleY(0.5);
opacity: 0.5;
}
50% {
transform: scaleY(1.2);
opacity: 1;
}
100% {
transform: scaleY(0.5);
opacity: 0.5;
}
}
/* 保留原有的spin动画用于其他地方 */
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style> </style>

View File

@ -24,6 +24,7 @@ definePageMeta({
const loadingRef=ref({ const loadingRef=ref({
loading1:false, loading1:false,
loading2:false, loading2:false,
loading3:false,
}) })
const isExist=ref(false)// true const isExist=ref(false)// true
const isReal=ref(false) //isReal const isReal=ref(false) //isReal
@ -100,6 +101,7 @@ blockSrc:'',
blockY:0 blockY:0
}) })
const getCode =async () => { const getCode =async () => {
isShow.value=true
loadingRef.value.loading1=true loadingRef.value.loading1=true
const res=await userCaptcha(captcha.value) const res=await userCaptcha(captcha.value)
if (res.status===0){ if (res.status===0){
@ -107,23 +109,9 @@ loadingRef.value.loading1=true
captcha.value.blockSrc=`data:image/png;base64,${res.data.blockSrc}` captcha.value.blockSrc=`data:image/png;base64,${res.data.blockSrc}`
captcha.value.blockY=res.data.blockY captcha.value.blockY=res.data.blockY
captcha.value.nonceStr=res.data.nonceStr captcha.value.nonceStr=res.data.nonceStr
isShow.value=true
loadingRef.value.loading1=false loadingRef.value.loading1=false
} }
// loadingRef.value.loading1=true
// const res=await senCode({
// telNum:phoneNum.value,
// zone:selectedZone.value
// })
// loadingRef.value.loading1=false
// if (res.status===0){
// }
// pane.value = 1
// vanSwipeRef.value?.swipeTo(pane.value)
// startCountdown();
} }
const goBack = () => { const goBack = () => {
code.value = '' code.value = ''
@ -177,15 +165,8 @@ onUnmounted(() => {
window.removeEventListener('resize', () => {}) window.removeEventListener('resize', () => {})
}) })
const isShow=ref(false) const isShow=ref(false)
const onSuccess=()=>{
// userCaptchaValidate()
isShow.value=false
}
const onClose=()=>{
isShow.value=false
}
const onLeave =async (moveX, callback) => { const onLeave =async (moveX, callback) => {
loadingRef.value.loading1=true
const res=await senCode({ const res=await senCode({
telNum:phoneNum.value, telNum:phoneNum.value,
zone:selectedZone.value, zone:selectedZone.value,
@ -194,7 +175,6 @@ const onLeave =async (moveX, callback) => {
nonceStr:captcha.value.nonceStr nonceStr:captcha.value.nonceStr
} }
}) })
loadingRef.value.loading1=false
if (res.status===408){ if (res.status===408){
callback(false) callback(false)
getCode() getCode()
@ -249,7 +229,7 @@ const onLeave =async (moveX, callback) => {
</div> </div>
</div> </div>
<div class="mt-[55px]"> <div class="mt-[55px]">
<van-button :loading="loadingRef.loading1" v-if="phoneNum" :loading-text="$t('login.getCode')" color="#2B53AC" block style="height: 48px" @click="getCode">{{ $t('login.getCode') }}</van-button> <van-button v-if="phoneNum" :loading-text="$t('login.getCode')" color="#2B53AC" block style="height: 48px" @click="getCode">{{ $t('login.getCode') }}</van-button>
<van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('login.getCode') }}</van-button> <van-button v-else type="primary" color="#D3D3D3" block style="height: 48px">{{ $t('login.getCode') }}</van-button>
</div> </div>
</div> </div>
@ -299,6 +279,7 @@ const onLeave =async (moveX, callback) => {
<van-popup v-model:show="isShow" round style="max-width: initial" teleport="body"> <van-popup v-model:show="isShow" round style="max-width: initial" teleport="body">
<PuzzleComponent <PuzzleComponent
v-if="isShow" v-if="isShow"
:loading="loadingRef.loading1"
:options="captcha" :options="captcha"
@leave="onLeave" @leave="onLeave"
/> />