Vue基于ElementUI组件实现滑块登录验证组件

Posted 北溟溟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue基于ElementUI组件实现滑块登录验证组件相关的知识,希望对你有一定的参考价值。

引言

在实际项目应用开发中,为了防止用户频繁发起登录请求,导致后端登录访问压力瞬时过大,我们可以设计一些验证规则防刷,最常见的方式是通过输入验证码的方式降低刷新频率,后台通过返回不同的验证码从而降低用户的频繁请求,这里我们提供一种前端的方式,降低防刷的频率,那就是滑块验证码验证。话不多说,先上效果图。

 本组件的集成及案例教程依然在我们的ATP应用测试平台中,源码地址:https://gitee.com/northcangap/atp.git,仅供参考。滑块组件使用vue+element实现,希望你对vue中的常用功能有所了解,关于父子组件传值,watch、computed等监控的了解。

正文

  • 创建滑块验证组件slider-verify-code.vue
<template>
  <div class="drag" :style="style">
    <div class="background"/>
    <div class="text shadow" onselectstart="return false" :style="{ color: textColor }">
      <slot name="content">
        {{ content }}
      </slot>
    </div>
    <div class="slider" :style="{height,width:sliderWidth}">
      <slot v-if="icon" name="icon">
        <i :class="icon"></i>
      </slot>
      <slot v-else name="icon">
        >>
      </slot>
    </div>
  </div>
</template>

<script>
const debounce = (function () {
  let timer = 0
  return function (callback, ms) {
    clearTimeout(timer)
    timer = setTimeout(callback, ms)
  }
})();

export default {
  name: 'slider-verify-code',
  model: {
    event: 'change',
    prop: 'isLock'
  },
  props: {
    isLock: { //解锁状态
      type: [String, Boolean, Number, Object],
      required: true,
      default: false
    },
    icon: { //滑块图标
      type: [String],
      default: "el-icon-d-arrow-right"
    },
    activeValue: { //滑块解锁后的值
      type: [String, Boolean, Number, Object],
      default: true
    },
    inactiveValue: { //滑块解锁前的值
      type: [String, Boolean, Number, Object],
      default: false
    },
    content: { //滑块的文字
      type: [String],
      default: "请向右拖动滑块"
    },
    height: { //高度
      type: [String],
      default: "40px"
    },
    sliderWidth: { //滑块宽度
      type: [String],
      default: "40px"
    },
    background: { //高度
      type: [String],
      default: "#e8e8e8"
    },
    textColor: { //滑块的文字颜色
      type: [String],
      default: "#777"
    }
  },
  watch: {
    isLock(data) { //重置样式
      !data && this.init();
    },
  },
  computed: {
    style() {
      const {height, background} = this;
      return {height, 'line-height': height, background};
    },
    resize() {
      return document.body.clientWidth;
    },
  },
  mounted() {
    this.init();
    window.onresize = () => {
      debounce(() => {
        this.init();
      }, 120);
    };
  },
  methods: {
    /**
     * 定义一个获取DOM元素的方法-选择器
     */
    selector(selector) {
      return document.querySelector(selector);
    },
    /**
     * 初始化
     */
    init() {
      const box = this.selector('.drag'); //容器
      const background = this.selector('.background'); //背景
      const text = this.selector('.text'); //文字
      const slider = this.selector('.slider');//滑块
      const distance = box.offsetWidth - slider.offsetWidth;//滑动成功的宽度(距离)
      let success = this.inactiveValue;//是否通过验证的标志
      // 初始化的时候 清除所有属性
      slider.style.transition = null;
      background.style.transition = null;
      slider.style.left = 0 + 'px';
      background.style.width = 0 + 'px';
      text.innerhtml = this.content;
      slider.innerHTML = '<i class="el-icon-d-arrow-right"></i>';
      slider.style.color = '#777';
      //二、给滑块注册鼠标按下事件
      slider.onmousedown = (event) => {
        //1.鼠标按下之前必须清除掉后面设置的过渡属性
        slider.style.transition = null;
        background.style.transition = null;
        //说明:clientX 事件属性会返回当事件被触发时,鼠标指针向对于浏览器页面(或客户区)的水平坐标。
        //2.当滑块位于初始位置时,得到鼠标按下时的水平位置
        const ev = event || window.event;
        const downX = ev.clientX;
        //三、给文档注册鼠标移动事件
        document.onmousemove = (e) => {
          const evt = e || window.event;//是为了更好的兼容IE浏览器和非ie浏览器。在ie浏览器中,window.event是全局变量,在非ie中,就需要自己传入一个参数来获取event啦,所以就有了var e = e||window.event
          //1.获取鼠标移动后的水平位置
          const moveX = evt.clientX;
          //2.得到鼠标水平位置的偏移量(鼠标移动时的位置 - 鼠标按下时的位置)
          let offsetX = moveX - downX;
          //3.在这里判断一下:鼠标水平移动的距离 与 滑动成功的距离 之间的关系
          if (offsetX > distance) {
            offsetX = distance;//如果滑过了终点,就将它停留在终点位置
          } else if (offsetX < 0) {
            offsetX = 0;//如果滑到了起点的左侧,就将它重置为起点位置
          }
          //4.根据鼠标移动的距离来动态设置滑块的偏移量和背景颜色的宽度
          slider.style.left = offsetX + 'px';
          background.style.width = offsetX + 'px';
          //如果鼠标的水平移动距离 = 滑动成功的宽度
          if (offsetX == distance) {
            //1.设置滑动成功后的样式
            text.innerHTML = '验证成功';
            text.style.color = '#fff';
            slider.innerHTML = '<i class="el-icon-success"></i>';
            slider.style.color = '#53C300';
            //2.设置滑动成功后的状态
            success = this.activeValue;
            //成功后,清除掉鼠标按下事件和移动事件(因为移动时并不会涉及到鼠标松开事件)
            slider.onmousedown = null;
            document.onmousemove = null;
            //3.成功解锁后的回调函数
            setTimeout(() => {
              this.$emit('change', this.activeValue);
            }, 100);
          }
        };
        //四、给文档注册鼠标松开事件
        document.onmouseup = () => {
          //如果鼠标松开时,滑到了终点,则验证通过
          if (success == this.activeValue) return true;
          //反之,则将滑块复位(设置了1s的属性过渡效果)
          slider.style.left = 0;
          background.style.width = 0;
          slider.style.transition = 'left 1s ease';
          background.style.transition = 'width 1s ease';
          //只要鼠标松开了,说明此时不需要拖动滑块了,那么就清除鼠标移动和松开事件。
          document.onmousemove = null;
          document.onmouseup = null;
        };
      };
      /* 移动端 */
      //二、给滑块注册鼠标按下事件
      slider.ontouchstart = (event) => {
        const touch = event.changedTouches[0];
        //1.鼠标按下之前必须清除掉后面设置的过渡属性
        slider.style.transition = null;
        background.style.transition = null;
        //说明:clientX 事件属性会返回当事件被触发时,鼠标指针向对于浏览器页面(或客户区)的水平坐标。
        //2.当滑块位于初始位置时,得到鼠标按下时的水平位置
        const downX = touch.pageX;
        //三、给文档注册鼠标移动事件
        document.ontouchmove = (e) => {
          const tev = e.changedTouches[0];
          //1.获取鼠标移动后的水平位置
          const moveX = tev.pageX;
          //2.得到鼠标水平位置的偏移量(鼠标移动时的位置 - 鼠标按下时的位置)
          let offsetX = moveX - downX;
          //3.在这里判断一下:鼠标水平移动的距离 与 滑动成功的距离 之间的关系
          if (offsetX > distance) {
            offsetX = distance;//如果滑过了终点,就将它停留在终点位置
          } else if (offsetX < 0) {
            offsetX = 0;//如果滑到了起点的左侧,就将它重置为起点位置
          }
          //4.根据鼠标移动的距离来动态设置滑块的偏移量和背景颜色的宽度
          slider.style.left = offsetX + 'px';
          background.style.width = offsetX + 'px';
          //如果鼠标的水平移动距离 = 滑动成功的宽度
          if (offsetX == distance) {
            //1.设置滑动成功后的样式
            text.innerHTML = '验证成功';
            text.style.color = '#fff';
            slider.innerHTML = '&radic;';
            slider.style.color = '#53C300';
            //2.设置滑动成功后的状态
            success = this.activeValue;
            //成功后,清除掉鼠标按下事件和移动事件(因为移动时并不会涉及到鼠标松开事件)
            slider.ontouchstart = null;
            document.ontouchmove = null;
            //3.成功解锁后的回调函数
            setTimeout(() => {
              this.$emit('change', this.activeValue);
              // console.log('解锁成功');
            }, 100);
          }
        };
        //四、给文档注册鼠标松开事件
        document.ontouchend = () => {
          //如果鼠标松开时,滑到了终点,则验证通过
          if (success == this.activeValue) return true;
          //反之,则将滑块复位(设置了1s的属性过渡效果)
          slider.style.left = 0;
          background.style.width = 0;
          slider.style.transition = 'left 1s ease';
          background.style.transition = 'width 1s ease';
          //只要鼠标松开了,说明此时不需要拖动滑块了,那么就清除鼠标移动和松开事件。
          document.ontouchmove = null;
          document.ontouchend = null;
        };
      };
    }
  }
};

</script>
<style scoped lang="scss">
* {
  margin: 0px;
  padding: 0px;
  font-family: "微软雅黑";
  box-sizing: border-box;
}

.drag {
  height: 2.5rem;
  line-height: 2.5rem;
  background-color: #e8e8e8;
  position: relative;
  margin: 0 auto;
  border-radius: 3px;
}

.background {
  width: 2.5rem;
  height: 100%;
  position: absolute;
  background-color: #53C300;
  border-radius: 3px 0 0 3px;
}

.text {
  position: absolute;
  width: 100%;
  height: 100%;
  text-align: center;
  user-select: none;
}

.slider {
  width: 2.5rem;
  height: 2.375rem;
  position: absolute;
  border: 1px solid #ccc;
  cursor: move;
  font-family: "宋体";
  text-align: center;
  background-color: #fff;
  user-select: none;
  color: #666;
}

.shadow {
  text-align: center;
  background: -webkit-gradient(linear, left top, right top, color-stop(0, #4d4d4d), color-stop(.2, #5d5d5d),
      color-stop(.4, #6d6d6d), color-stop(.5, white), color-stop(.6, #6d6d6d), color-stop(.8, #5d5d5d), color-stop(1, #4d4d4d));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  -webkit-animation: animate 3s infinite;
}

@-webkit-keyframes animate {
  from {
    background-position: -80px;
  }
  to {
    background-position: 80px;
  }
}

@keyframes animate {
  from {
    background-position: -80px;
  }
  to {
    background-position: 80px;
  }
}
</style>

  •  在登录页面集成登录滑块组件功能login.vue
<template>
  <div class="container">
    <el-form ref="form" :model="form" :rules="rules" label-width="70px" class="login">
      <h3>ATP应用测试平台</h3>
      <el-form-item label="用户名" prop="name">
        <el-input v-model="form.name"></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="pass">
        <el-input v-model="form.pass" type="password" show-password></el-input>
      </el-form-item>
      <el-form-item label="验证" prop="isLock">
        <slider-verify-code v-model="form.isLock" @change="handlerLock"></slider-verify-code>
      </el-form-item>
      <el-button type="primary" @click="login" style="width: 100%;margin: 0;">立即登录</el-button>
    </el-form>
  </div>
</template>

<script>
import sliderVerifyCode from '@/components/slider-verify-code.vue';

export default {
  name: "Login",
  data() {
    const checkStatus = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("请拖动滑块完成验证"));
      } else {
        if (this.form.name == '' || this.form.pass == ''
            || !this.form.name || !this.form.pass) {
          setTimeout(() => {
            this.form.isLock = false;
            this.$refs.form.validateField('name');
            this.$refs.form.validateField('pass');
            return callback(new Error("验证未通过"));
          }, 1);
        }
        callback();
      }
    };
    return {
      form: {},
      rules: {
        name: [
          {required: true, message: '用户名称不得为空!', trigger: 'blur'},
          {min: 6, max: 18, message: '长度在 6 到 18 个字符', trigger: 'blur'}
        ],
        pass: [
          {required: true, message: '密码不得为空!', trigger: 'blur'},
          {min: 6, max: 18, message: '长度在 6 到 18 个字符', trigger: 'blur'}
        ],
        isLock: [
          {validator: checkStatus, trigger: 'blur'},
        ],
      },
    }
  },
  components: {
    'slider-verify-code': sliderVerifyCode
  },
  methods: {
    //登录
    login() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          this.$http.post('/sys/user/login', this.$qs.stringify(this.form)).then(res => {
            if (res.data.code === 1) {
              this.$router.push('/home')
            } else {
              this.$refs.form.resetFields();
              this.$message.warning(res.data.msg);
            }
          }).catch(error => {
            this.$message.error(error);
          });
        } else {
          return false;
        }
      });
    },

    handlerLock(data) {
      if (data) {
        this.$refs.form.validateField('isLock');
      }
    },
  }
}
</script>

<style scoped lang="scss">
.container {
  width: 100%;
  height: 100%;
  overflow: auto;
  background: rgb(84, 92, 100);

  .login {
    text-align: center;
    padding: 20px 30px 30px 30px;
    margin: 10% auto;
    width: 320px;
    background: white;
    border-radius: 10px;

    h3 {
      margin: 30px 0px;
    }

    .el-form-item {
      margin-bottom: 35px;
    }
  }
}
</style>

  •  验证

 结语

ok,关于Vue基于ElementUI组件实现滑块登录验证组件的介绍就到这里了,我们下期见。。。

以上是关于Vue基于ElementUI组件实现滑块登录验证组件的主要内容,如果未能解决你的问题,请参考以下文章

vue+elementui的导航滑块组件

vue+elementUI+node实现登录模块--验证用户名是否正确

Vue登录模块中基于canvas实现验证码组件

Vue登录模块中基于canvas实现验证码组件

vue3+element-plus表单验证之登录

基于Vue的ElementUI项目实现父子组件信息传递