vue3+ts 登陆页面,前端实现滑块验证

<template>
    <div class="login" @keyup.enter="clickLogin">
        <div class="loginModule" ref="formWrap">
            <div class="logo">
                <img src="@/assets/images/logo.png" alt="" />
            div>
            <div class="form">
                <el-form ref="loginFormRef" :rules="loginRules" :model="loginForm">
                    <el-form-item prop="username">
                        <el-input
                            clearable
                            placeholder="用户名"
                            v-model="loginForm.username"
                        >
                        el-input>
                    el-form-item>
                    <el-form-item prop="password">
                        <el-input
                            clearable
                            show-password
                            placeholder="密码"
                            v-model="loginForm.password"
                        >
                        el-input>
                    el-form-item>
                    <div class="sliderValidate">
                        <div
                            class="validateWrap"
                            ref="sliderWrap"
                            :class="{
                                validatePass: validPass
                            }"
                        >
                            <div class="block" ref="slider">
                                <img
                                    v-show="validPass"
                                    src="@/assets/images/Login/slider-icon.png"
                                    alt="滑块"
                                />
                                <div class="sliderIcon" v-show="!validPass">
                                    <DArrowRight style="width: 1em; height: 1em; " />
                                div>
                            div>
                            <span v-show="validPass" class="validatePass">验证通过span>
                            <span v-show="!validPass" class="noValidate">拖动滑块校验span>
                        div>
                    div>
                    <div class="showResetPassword">
                        <p>
                            <span class="sliderValid" v-if="showSliderWarn">请拖动滑块完成校验span>
                        p>
                    div>
                el-form>
                <a-button class="loginButton" type="primary" @click="clickLogin">登录a-button>
            div>
        div>
    div>
template>
<script lang="ts" setup>
// ref:用于声明基础类型响应式数据, reactive:用于声明复杂类型响应式数据。unref:如果参数是一个 ref 则返回它的 value,否则返回参数本身
import { reactive, ref, unref, onMounted, inject, getCurrentInstance } from "vue";
// 接口
import { login } from "@/api/User";
// 节流
import throttle from "@/utils/common"
// 提示信息
const Mes: any = inject('$message')
// 登陆密码加密
import { submitEncrypt } from '@/utils/jsencrypt';

// 对用户和密码进行类型限制
interface loginData {
    username: string;
    password: string;
}

//登录数据
let loginForm = reactive<loginData>({
    username: "",
    password: ""
});

// 检查用户名是否存在
const usernameCheck = async (rule: any, value: string, cb: any) => {
    if (!/^\w{5,20}$/g.test(value)) {
        return cb("用户名不合法");
    }
  
};
// 登录数据校验
let loginRules = reactive({
    username: [ { required: true, message: "请输入用户名", trigger: "blur" }, { validator: usernameCheck, trigger: 'blur' } ],
    password: [ { required: true, message: "请输入密码", trigger: "blur" } ]
});
// 表单校验结果
const loginFormRef = ref();

// 标示滑块校验是否通过
const validPass = ref(false)
// 显示滑块校验文字提示
const showSliderWarn = ref(false)
// 滑块 ref
const slider = ref()
// 滑块容器 ref
const sliderWrap = ref()
// 登陆模块 ref
const formWrap = ref()

/**
 * 为滑块添加事件
 */
function sliderEvent() {
    if (!slider.value || !sliderWrap.value) return null;
    // 滑块容器的宽度
    const sliderBoxWidth = sliderWrap.value.clientWidth;
    // 滑块宽度
    const sliderWidth = slider.value.clientWidth;
    // 最大可拖动距离
    const maxSlideX = sliderBoxWidth - sliderWidth;
    // 滑块容器距离屏幕左边的距离(通过父级元素的offsetLeft计算)
    const formClientX = formWrap.value.offsetLeft + 43;
    // 滑块划过的过程
    slider.value.onmousedown = function(e: any) {
        const offsetX = e.offsetX
        // 滑块校验已通过
        if (validPass.value) {
            return
        }
        // 鼠标在页面上进行移动
        document.onmousemove = function(e: any) {
            if (validPass.value) {
                document.onmousemove = null;
                document.onmouseup = null;
                return;
            }
            const x = e.clientX - formClientX - offsetX;
            if (x > 0 && x < maxSlideX) {
                slider.value.style.left = x + "px";
            }
            if (x < 0) {
                slider.value.style.left = "0px";
            }
            if (x > maxSlideX) {
                slider.value.style.left = maxSlideX + "px";
                validPass.value = true;
                showSliderWarn.value = false;
            }
            if (x === maxSlideX) {
                validPass.value = true;
                showSliderWarn.value = false;
            }
        }
        //鼠标按键被松开
        document.onmouseup = function(e: any) {
            const centerX = sliderBoxWidth / 2 - 23;
            let x = e.clientX - formClientX - offsetX;
            
            let timer: any = null;
            
            // 恢复到起始点
            if (x <= centerX && x > 0) {
                timer = setInterval(() => {
                    x -= 2;
                    if (x <= 0) {
                        clearInterval(timer);
                        slider.value.style.left = "0px";
                    }
                    
                    slider.value.style.left = x + "px";
                }, 1);
            } else if (x > centerX && x < maxSlideX) { // 终点
                timer = setInterval(() => {
                    x += 2;
                    if (x >= maxSlideX) {
                        clearInterval(timer);
                        slider.value.style.left = maxSlideX + "px";
                        validPass.value = true;
                        showSliderWarn.value = false;
                    }
                    slider.value.style.left = x + "px";
                }, 1);
            }
            document.onmousemove = null;
            document.onmouseup = null;
        }
    }
}

// 节流
const throttles = throttle(2000);
/**
 * 登陆
 */

const clickLogin = () => {
    throttles(async () => {
        const form: any = unref(loginFormRef)
        await form.validate()
        if (validPass.value) {
            showSliderWarn.value = false;
            // 登陆
            await isLogin();
        } else {
            showSliderWarn.value = true;
        }
    })
}



/**
 * 执行登陆
 */
async function isLogin() {
    try {
        const { data } = await login({
            username: loginForm.username,
            password: submitEncrypt(loginForm.password)
        })
        if (data.code) {
        	//登陆成功
        }else{
        	// 失败
        }
        
   
    } catch (err) {
        console.log('e', err)
        Mes.error('用户名或密码错误,请重试')
    }
}

onMounted(() => {
    // ref页面组件还没有挂载完成,需要在挂载完成之后才能使用refs
    sliderEvent()
});
script>

<style lang="scss" scoped src="./Login.scss">style>

你可能感兴趣的:(前端,vue.js,typescript)