hldy_vue/src/views/system/loginmini/huakuai.vue

170 lines
4.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 滑动验证码容器 -->
<div class="slider-container" ref="containerRef">
<!-- 滑动遮罩层显示滑动进度 -->
<div class="slider-mask" :style="{ width: sliderMaskWidth }" />
<!-- 滑块按钮 -->
<div
class="slider"
ref="sliderRef"
@mousedown="handleDragStart"
:class="{ 'slider-success': verified }"
:style="{ left: sliderLeft }"
>
<!-- 滑块图标 -->
<Icon icon="ant-design:double-right-outlined" :size="20" />
</div>
<!-- 提示文本 -->
<div class="slider-text" :class="{ 'slider-text-success': verified }">
{{ verified ? "验证通过" : "将滑块拖动至右侧完成验证" }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineEmits, onUnmounted } from "vue"
const emit = defineEmits(["verifySuccess"])
// DOM 引用
const containerRef = ref<HTMLElement | null>(null) // 容器元素引用
const sliderRef = ref<HTMLElement | null>(null) // 滑块元素引用
// 状态变量
const sliderLeft = ref("0px") // 滑块左侧位置
const sliderMaskWidth = ref("0px") // 遮罩层宽度
const verified = ref(false) // 验证状态
// 拖动相关变量
let startX = 0 // 开始拖动时的 X 坐标
let sliderLeft_temp = 0 // 临时存储滑块位置
/**
* 开始拖动处理
* @param e 鼠标事件对象
*/
const handleDragStart = (e: MouseEvent) => {
if (verified.value) return // 如果已验证通过,不再处理
startX = e.clientX
sliderLeft_temp = parseInt(sliderLeft.value)
// 添加鼠标移动和松开事件监听
document.addEventListener("mousemove", handleDragMove)
document.addEventListener("mouseup", handleDragEnd)
}
/**
* 拖动过程处理
* @param e 鼠标事件对象
*/
const handleDragMove = (e: MouseEvent) => {
if (verified.value) return
const container = containerRef.value
if (!container) return
// 计算拖动距离
const diff = e.clientX - startX
const containerWidth = container.offsetWidth
const sliderWidth = sliderRef.value?.offsetWidth || 0
const maxLeft = containerWidth - sliderWidth
// 计算新位置并限制范围
let newLeft = sliderLeft_temp + diff
if (newLeft < 0) newLeft = 0
if (newLeft > maxLeft) newLeft = maxLeft
// 更新滑块和遮罩层位置
sliderLeft.value = newLeft + "px"
sliderMaskWidth.value = newLeft + sliderWidth / 2 + "px"
// 检查是否验证成功
if (newLeft === maxLeft) {
verified.value = true
emit("verifySuccess", true) // 触发验证成功
handleDragEnd()
}
}
/**
* 结束拖动处理
*/
const handleDragEnd = () => {
if (!verified.value) {
// 如果未验证成功,重置位置
sliderLeft.value = "0px"
sliderMaskWidth.value = "0px"
}
// 移除事件监听
document.removeEventListener("mousemove", handleDragMove)
document.removeEventListener("mouseup", handleDragEnd)
}
// 组件卸载时清理事件监听
onUnmounted(() => {
document.removeEventListener("mousemove", handleDragMove)
document.removeEventListener("mouseup", handleDragEnd)
})
</script>
<style scoped>
/* 滑动验证码容器样式 */
.slider-container {
position: relative;
width: 100%;
height: 42px;
background-color: #f5f5f5;
border: 1px solid #e4e7ed;
border-radius: 8px;
margin-top: 20px;
}
/* 滑动遮罩层样式 */
.slider-mask {
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: #d1e9fc;
border-radius: 8px;
transition: width 0.1s linear;
}
/* 滑块按钮样式 */
.slider {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 60px;
height: 42px;
background: #e1dede;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
/* 验证成功时滑块样式 */
.slider-success {
background-color: #1ea0fa;
}
/* 提示文本样式 */
.slider-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
user-select: none;
font-weight: 400;
font-size: 12px;
color: #9cb3c5;
width: auto;
}
/* 验证成功时文本样式 */
.slider-text-success {
color: #1ea0fa;
}
</style>