提交 dcb2c886 作者: 廖在望

feat: 优化登录和注册页面逻辑。

上级 2a2d7e73
<script setup lang="ts">
/**
* 滑动验证组件
* 用法:<SlideVerify v-model:show="showSlide" @success="onVerifySuccess" />
*/
const props = defineProps<{
show: boolean
}>()
const emit = defineEmits<{
(e: 'update:show', val: boolean): void
(e: 'success'): void
(e: 'close'): void
}>()
const TRACK_WIDTH = 560 // rpx,滑道宽度(与样式保持一致)
const BTN_WIDTH = 96 // rpx,滑块宽度
const maxRpx = TRACK_WIDTH - BTN_WIDTH // 最大可拖动距离(rpx)
const state = reactive({
translateX: 0, // 当前滑块位移(px,运行时换算)
isDragging: false,
startX: 0,
maxPx: 0, // 运行时根据屏幕宽度换算
verified: false,
text: '按住滑块,拖动到最右侧',
})
// 将 rpx 换算为 px
function rpxToPx(rpx: number): number {
const screenWidth = uni.getSystemInfoSync().windowWidth
return (rpx / 750) * screenWidth
}
watch(() => props.show, (val) => {
if (val) {
reset()
state.maxPx = rpxToPx(maxRpx)
}
})
function reset() {
state.translateX = 0
state.isDragging = false
state.verified = false
state.text = '按住滑块,拖动到最右侧'
}
function onTouchStart(e: any) {
if (state.verified) return
state.isDragging = true
state.startX = e.touches[0].clientX
}
function onTouchMove(e: any) {
if (!state.isDragging || state.verified) return
const dx = e.touches[0].clientX - state.startX
state.translateX = Math.max(0, Math.min(dx, state.maxPx))
}
function onTouchEnd() {
if (!state.isDragging) return
state.isDragging = false
// 距终点 10px 以内视为验证通过
if (state.translateX >= state.maxPx - rpxToPx(10)) {
state.translateX = state.maxPx
state.verified = true
state.text = '验证通过'
setTimeout(() => {
emit('success')
close()
}, 600)
} else {
// 未拖到底,回弹
state.translateX = 0
}
}
function close() {
emit('update:show', false)
emit('close')
reset()
}
</script>
<template>
<view class="sv-mask" v-if="show" @click.self="close">
<view class="sv-panel">
<view class="sv-title">安全验证</view>
<view class="sv-desc">{{ state.text }}</view>
<!-- 滑道 -->
<view class="sv-track">
<!-- 已划过的高亮区 -->
<view class="sv-fill" :style="{ width: state.translateX + 'px' }" />
<!-- 滑块 -->
<view
class="sv-btn"
:class="{ success: state.verified }"
:style="{ transform: `translateX(${state.translateX}px)` }"
@touchstart.prevent="onTouchStart"
@touchmove.prevent="onTouchMove"
@touchend.prevent="onTouchEnd"
>
<text class="sv-arrow" v-if="!state.verified">&#8594;</text>
<text class="sv-check" v-else">&#10003;</text>
</view>
</view>
<view class="sv-close" @click="close">
<text>取消</text>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.sv-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.sv-panel {
width: 640rpx;
background: #fff;
border-radius: 24rpx;
padding: 48rpx 40rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.16);
}
.sv-title {
font-size: 34rpx;
font-weight: 700;
color: #222;
margin-bottom: 12rpx;
}
.sv-desc {
font-size: 26rpx;
color: #888;
margin-bottom: 40rpx;
}
.sv-track {
position: relative;
width: 560rpx;
height: 96rpx;
background: #f0f0f0;
border-radius: 48rpx;
overflow: hidden;
}
.sv-fill {
position: absolute;
left: 0;
top: 0;
height: 100%;
background: linear-gradient(90deg, #b8e8c0, #5db66f);
border-radius: 48rpx 0 0 48rpx;
transition: width 0.05s;
}
.sv-btn {
position: absolute;
left: 0;
top: 0;
width: 96rpx;
height: 96rpx;
background: #fff;
border-radius: 48rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.18);
display: flex;
align-items: center;
justify-content: center;
transition: background 0.3s;
z-index: 2;
&.success {
background: #5db66f;
}
}
.sv-arrow {
font-size: 40rpx;
color: #5db66f;
font-weight: 700;
}
.sv-check {
font-size: 40rpx;
color: #fff;
font-weight: 700;
}
.sv-close {
margin-top: 32rpx;
text {
font-size: 28rpx;
color: #aaa;
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论