<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)// 滑块 refconst slider = ref()// 滑块容器 refconst sliderWrap = ref()// 登陆模块 refconst 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>