feat:重构模块
This commit is contained in:
182
src/components/Login/LoginAuth.vue
Normal file
182
src/components/Login/LoginAuth.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="login-auth">
|
||||
<div class="slider-container"
|
||||
@mousedown="startDrag" @mousemove="handleDrag"
|
||||
@mouseup="endDrag" @mouseleave="endDrag"
|
||||
@touchstart="startDrag" @touchmove="handleDrag" @touchend="endDrag">
|
||||
<div ref="draggableRef" class="draggable"
|
||||
:style="{ left: `${loginAuth.dragLeft}px` }">
|
||||
<i v-if="authStore.isVerified" class="fas fa-check"></i>
|
||||
<i v-else class="fas fa-arrow-right"></i>
|
||||
</div>
|
||||
<div :style="{ width: loginAuth.progressWidth + '%' }" class="slider-progress"></div>
|
||||
<div class="slider-message" :style="{color: authStore.isVerified ? '#FFFFFF' : '#696969'}">
|
||||
<span v-if="authStore.isVerified">验证成功!</span>
|
||||
<span v-else-if="loginAuth.isDragging">拖动中...</span>
|
||||
<span v-else>向右拖动滑块 完成验证</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
|
||||
const draggableRef = ref(null)
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const loginAuth = reactive({
|
||||
isDragging: false,
|
||||
initialX: 0,
|
||||
offsetX: 0,
|
||||
dragLeft: 0,
|
||||
progressWidth: 0,
|
||||
targetWidth: 100,
|
||||
// 记录拖动的开始时间
|
||||
startTime: 0,
|
||||
// 记录拖动过程中的位置
|
||||
positions: [],
|
||||
// 记录拖动过程中的时间戳
|
||||
timestamps: []
|
||||
})
|
||||
|
||||
const startDrag = (event) => {
|
||||
// 验证成功后禁止拖动
|
||||
if (authStore.isVerified) return
|
||||
loginAuth.isDragging = true
|
||||
|
||||
const touch = event.changedTouches ? event.changedTouches[0] : event
|
||||
loginAuth.initialX = touch.clientX
|
||||
loginAuth.offsetX = draggableRef.value?.offsetLeft
|
||||
|
||||
loginAuth.startTime = Date.now()
|
||||
loginAuth.positions = []
|
||||
loginAuth.timestamps = []
|
||||
loginAuth.positions.push(loginAuth.initialX)
|
||||
loginAuth.timestamps.push(loginAuth.startTime)
|
||||
}
|
||||
|
||||
const handleDrag = (event) => {
|
||||
if (loginAuth.isDragging && !authStore.isVerified) {
|
||||
const touch = event.changedTouches ? event.changedTouches[0] : event
|
||||
const dx = touch.clientX - loginAuth.initialX
|
||||
const newLeft = loginAuth.offsetX + dx
|
||||
const sliderWidth = draggableRef.value?.parentNode.offsetWidth
|
||||
const handleWidth = draggableRef.value?.offsetWidth
|
||||
|
||||
if (newLeft < 0) {
|
||||
loginAuth.dragLeft = 0
|
||||
loginAuth.progressWidth = 0
|
||||
} else if (newLeft > sliderWidth - handleWidth) {
|
||||
loginAuth.dragLeft = sliderWidth - handleWidth
|
||||
loginAuth.progressWidth = loginAuth.targetWidth
|
||||
} else {
|
||||
loginAuth.dragLeft = newLeft
|
||||
loginAuth.progressWidth = (newLeft / (sliderWidth - handleWidth)) * loginAuth.targetWidth
|
||||
}
|
||||
|
||||
loginAuth.positions.push(touch.clientX)
|
||||
loginAuth.timestamps.push(Date.now())
|
||||
}
|
||||
}
|
||||
|
||||
const endDrag = () => {
|
||||
loginAuth.isDragging = false
|
||||
if (loginAuth.progressWidth === loginAuth.targetWidth && !checkIsHuman()) {
|
||||
authStore.isVerified = true
|
||||
} else {
|
||||
loginAuth.dragLeft = 0
|
||||
loginAuth.progressWidth = 0
|
||||
}
|
||||
}
|
||||
|
||||
const checkIsHuman = () => {
|
||||
const endTime = Date.now()
|
||||
const dragDuration = endTime - loginAuth.startTime
|
||||
|
||||
const speedChanges = []
|
||||
for (let i = 1; i < loginAuth.positions.length; i++) {
|
||||
const distance = loginAuth.positions[i] - loginAuth.positions[i - 1]
|
||||
const time = loginAuth.timestamps[i] - loginAuth.timestamps[i - 1]
|
||||
const speed = time > 0 ? distance / time : 0
|
||||
speedChanges.push(speed)
|
||||
}
|
||||
|
||||
// 计算速度变化的标准差
|
||||
const meanSpeed = speedChanges.reduce((sum, speed) => sum + speed, 0) / speedChanges.length
|
||||
const variance = speedChanges.reduce((sum, speed) => sum + Math.pow(speed - meanSpeed, 2), 0) / speedChanges.length
|
||||
const stdDevSpeed = Math.sqrt(variance)
|
||||
|
||||
// 检查是否有停顿
|
||||
let hasPause = false
|
||||
for (let i = 1; i < loginAuth.timestamps.length; i++) {
|
||||
if (loginAuth.timestamps[i] - loginAuth.timestamps[i - 1] > 200) {
|
||||
hasPause = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 综合判断是否为机器操作
|
||||
return dragDuration >= 100 && stdDevSpeed >= 0.1 && hasPause
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.login-auth {
|
||||
width: 100%;
|
||||
|
||||
.slider-container {
|
||||
position: relative;
|
||||
height: 32px;
|
||||
background-color: #E7E9E8;
|
||||
border: 1px solid #E7E9E8;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
|
||||
.draggable {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 35px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--el-color-primary);
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
z-index: 3;
|
||||
border-left: 1px solid #E7E9E8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.slider-progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background-color: var(--el-color-primary);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.slider-message {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
transition: opacity 0.3s ease;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user