Pārlūkot izejas kodu

登录验证码

gr 3 mēneši atpakaļ
vecāks
revīzija
e2574fa0c0
3 mainītis faili ar 173 papildinājumiem un 12 dzēšanām
  1. 1 2
      src/assets/styles/index.scss
  2. 158 0
      src/components/VerifyCode.vue
  3. 14 10
      src/views/Login.vue

+ 1 - 2
src/assets/styles/index.scss

@@ -46,9 +46,8 @@ ul {
 }
 
 .el-message {
-  top: 15vh !important;
+  top: 10vh !important;
   min-width: 10vw;
-  height: 2.5vh;
   background-color: rgba($color: #021434, $alpha: 0.5) !important;
   border: 1px solid #5c94d2 !important;
   border-radius: 10px !important;

+ 158 - 0
src/components/VerifyCode.vue

@@ -0,0 +1,158 @@
+<template>
+  <div class="s-canvas" @click="drawPic">
+    <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
+  </div>
+</template>
+<script>
+export default {
+  name: "SIdentify",
+  props: {
+    fontSizeMin: {
+      // 字体最小值
+      type: Number,
+      default: 30,
+    },
+    fontSizeMax: {
+      // 字体最大值
+      type: Number,
+      default: 40,
+    },
+    backgroundColorMin: {
+      // 验证码图片背景色最小值
+      type: Number,
+      default: 200,
+    },
+    backgroundColorMax: {
+      // 验证码图片背景色最大值
+      type: Number,
+      default: 220,
+    },
+    dotColorMin: {
+      // 背景干扰点最小值
+      type: Number,
+      default: 60,
+    },
+    dotColorMax: {
+      // 背景干扰点最大值
+      type: Number,
+      default: 120,
+    },
+  },
+  data() {
+    return {
+      contentWidth: 0,
+      contentHeight: 0,
+      identifyCode: '',
+    }
+  },
+  methods: {
+    // 生成一个随机数
+    randomNum(min, max) {
+      return Math.floor(Math.random() * (max - min) + min);
+    },
+    //获取验证码的值
+    makeCode(len) {
+      this.identifyCode = ''
+      const dict = "234567890abcdefghjkmnopqrstuvwxyz"
+      for (let i = 0; i < len; i++) {
+        //通过循环获取字符串内随机几位
+        this.identifyCode += dict[this.randomNum(0, dict.length)];
+      }
+    },
+    // 生成一个随机的颜色
+    randomColor(min, max) {
+      let r = this.randomNum(min, max);
+      let g = this.randomNum(min, max);
+      let b = this.randomNum(min, max);
+      return "rgb(" + r + "," + g + "," + b + ")";
+    },
+    //画图
+    drawPic() {
+      this.makeCode(4)
+      let canvas = document.getElementById("s-canvas");
+      //创建一个2D对象作为上下文。
+      let ctx = canvas.getContext("2d");
+      ctx.textBaseline = "bottom";
+      // 绘制背景
+      ctx.fillStyle = "#e6ecfd";
+      ctx.fillRect(0, 0, this.contentWidth, this.contentHeight);
+      // 绘制文字
+      for (let i = 0; i < this.identifyCode.length; i++) {
+        this.drawText(ctx, this.identifyCode[i], i);
+      }
+      this.drawLine(ctx);
+      this.drawDot(ctx);
+    },
+    //在画布上显示数据
+    drawText(ctx, txt, i) {
+      ctx.fillStyle = this.randomColor(50, 160); // 随机生成字体颜色
+      ctx.font =
+        this.randomNum(this.fontSizeMin, this.fontSizeMax) + "px SimHei"; // 随机生成字体大小
+      let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1));
+      let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5);
+      var deg = this.randomNum(-30, 30);
+      // 修改坐标原点和旋转角度
+      ctx.translate(x, y);
+      ctx.rotate((deg * Math.PI) / 180);
+      ctx.fillText(txt, 0, 0);
+      // 恢复坐标原点和旋转角度
+      ctx.rotate((-deg * Math.PI) / 180);
+      ctx.translate(-x, -y);
+    },
+
+    // 绘制干扰线
+    drawLine(ctx) {
+      for (let i = 0; i < 4; i++) {
+        ctx.strokeStyle = this.randomColor(100, 200);
+        ctx.beginPath();
+        ctx.moveTo(
+          this.randomNum(0, this.contentWidth),
+          this.randomNum(0, this.contentHeight)
+        );
+        ctx.lineTo(
+          this.randomNum(0, this.contentWidth),
+          this.randomNum(0, this.contentHeight)
+        );
+        ctx.stroke();
+      }
+    },
+
+    // 绘制干扰点
+    drawDot(ctx) {
+      for (let i = 0; i < 30; i++) {
+        ctx.fillStyle = this.randomColor(0, 255);
+        ctx.beginPath();
+        ctx.arc(
+          this.randomNum(0, this.contentWidth),
+          this.randomNum(0, this.contentHeight),
+          1,
+          0,
+          2 * Math.PI
+        );
+        ctx.fill();
+      }
+    },
+
+    getContainerSize() {
+      const ele = document.getElementsByClassName('s-canvas')[0]
+      this.contentWidth = ele.clientWidth
+      this.contentHeight = ele.clientHeight
+    }
+  },
+  mounted() {
+    this.getContainerSize()
+    this.$nextTick(() => {
+      this.drawPic();
+    })
+  },
+  
+};
+</script>
+
+<style lang="scss" scoped>
+.s-canvas {
+  width: 100%;
+  height: 100%;
+  cursor: pointer;
+}
+</style>

+ 14 - 10
src/views/Login.vue

@@ -24,8 +24,8 @@
             auto-complete="off">
             <template #prefix><i class="input-prefix icon-vcode"></i></template>
           </el-input>
-          <div class="login-code" v-loading="captchaLoading">
-            <!-- <img :src="codeUrl" @click="getCode" class="login-code-img" /> -->
+          <div class="login-code">
+             <verify-code ref="verifyCode" />
           </div>
         </el-form-item>
       </el-form>
@@ -37,6 +37,7 @@
 <script setup>
 import { useRouter } from 'vue-router'
 import { ref, getCurrentInstance } from 'vue'
+import VerifyCode from '@/components/VerifyCode.vue';
 
 const router = useRouter();
 const { proxy } = getCurrentInstance();
@@ -55,12 +56,20 @@ const loginRules = ref({
 
 const loading = ref(false)
 
-const captchaLoading = ref(true)
-
 function handleLogin() {
+  loading.value = true;
   proxy.$refs.loginRef.validate(valid => {
     if (valid) {
-      loading.value = true;
+      const code = proxy.$refs.verifyCode.identifyCode
+      if(code!==loginForm.value.code) {
+        loading.value = false
+        ElMessage({
+          type: 'warning',
+          message: '验证码错误,请重试'
+        })
+        proxy.$refs.verifyCode.drawPic()
+        return
+      }
       setTimeout(() => {
         loading.value = false
         router.push({ name: 'Navigation' })
@@ -180,11 +189,6 @@ function handleLogin() {
             cursor: pointer;
             vertical-align: middle;
           }
-
-          .login-code-img {
-            height: 40px;
-            padding-left: 12px;
-          }
         }
       }