Vue 邮箱登录界面

功能

模拟了纯前端的邮箱登录逻辑
还没有连接后端的发送邮件的服务
后续计划,再做一个邮箱、密码登录的界面
然后把这两个一块连接上后端

技术介绍

主要介绍绘制图形人机验证乃个
使用的是canvas,在源码里就有

界面控制主要就是用 表格、表单(其实表单都没怎么用到)、v-if、文本输入框和按钮。

效果图

在这里插入图片描述

在这里插入图片描述

代码

Auth.vue

<template>
  <div>
    <form @submit.prevent="handleSubmit">
      <table>
        <!-- 邮箱输入框 -->
        <tr v-show="!showEmailVerification">
          <td><label for="email">邮箱</label></td>
          <td colspan="2"><input type="email" id="email" v-model="formData.email" placeholder="请输入邮箱" required></td>
        </tr>
        <!-- 人机校验 -->
        <tr v-show="!showEmailVerification">
          <td><button type="button" @click="refreshCaptcha"><canvas ref="myCanvas" width="90" height="25"></canvas></button></td>
          <td><input type="text" id="captcha" v-model="userInputCaptchaText" placeholder="请输入验证码" required></td>
        </tr>
        <tr v-show="!showEmailVerification">
          <td></td>
          <td><button type="button" @click="sendEmail">人机验证</button></td>
        </tr>
        <!-- 邮箱认证 -->
        <tr v-show="showEmailVerification">
          <td><label for="emailVerificationCode">验证码</label></td>
          <td colspan="2"><input type="text" id="emailVerificationCode" v-model="formData.emailVerificationCode" placeholder="请输入邮箱验证码" required></td>
        </tr>
        <tr v-show="showEmailVerification">
          <td></td>
          <td colspan="2"><button type="button" @click="verifyEmailVerificationCode">提交验证码</button></td>
        </tr>
        <!-- 消息通知栏 -->
        <tr>
          <td colspan="2">{{ message }}</td>
        </tr>
      </table>
    </form>
  </div>
</template>

<script setup>
import { onMounted } from 'vue';
import useFormValidation from './formValidation';

const {
  formData,
  userInputCaptchaText,
  showEmailVerification,
  message,
  refreshCaptcha,
  sendEmail,
  verifyEmailVerificationCode,
} = useFormValidation();

// Initialize captcha on component mount
onMounted(refreshCaptcha);

function handleSubmit() {
  // 可以在这里添加表单提交逻辑
}
</script>

<style scoped>
table, th, td {
  border: 1px solid black;
  border-collapse: collapse;
}
</style>


captcha.js

export function drawCaptcha(ctx, width, height) {
    // 填充背景色
    ctx.fillStyle = '#f2f2f2';
    ctx.fillRect(0, 0, width, height);

    // 设置字符集
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
    const charCount = 4; // 验证码字符数
    let captchaText = '';

    // 随机生成验证码文本
    for (let i = 0; i < charCount; i++) {
        captchaText += chars.charAt(Math.floor(Math.random() * chars.length));
    }

    // 设置字体样式
    ctx.font = '24px Arial';
    ctx.fillStyle = '#333';
    ctx.textBaseline = 'middle';

    // 绘制字符
    for (let i = 0; i < captchaText.length; i++) {
        const x =  i * 24;
        const y = height / 2;
        const angle = (Math.random() - 0.5) * 0.6; // 随机旋转角度
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(angle);
        ctx.fillText(captchaText.charAt(i), 0, 0);
        ctx.restore();
    }

    // 添加干扰线条
    for (let i = 0; i < 10; i++) {
        ctx.strokeStyle = getRandomColor();
        ctx.beginPath();
        ctx.moveTo(Math.random() * width, Math.random() * height);
        ctx.lineTo(Math.random() * width, Math.random() * height);
        ctx.stroke();
    }

    // 添加噪点
    for (let i = 0; i < 50; i++) {
        ctx.fillStyle = getRandomColor();
        ctx.beginPath();
        ctx.arc(Math.random() * width, Math.random() * height, 1, 0, Math.PI * 2);
        ctx.fill();
    }
    return captchaText;
}

function getRandomColor() {
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    return `rgb(${r},${g},${b})`;
}


formValidation.js

import { ref } from 'vue';
import { drawCaptcha } from './captcha';
import axios from 'axios';

export default function useFormValidation() {
    const formData = ref({
        email: '',
        emailVerificationCode: ''
    });

    const userInputCaptchaText = ref('');
    let captchaText = '';

    const isValid = ref(false);
    const showEmailVerification = ref(false);
    const loginSuccess = ref(false);
    const message = ref('');

    function refreshCaptcha() {
        const canvas = document.querySelector('canvas');
        const ctx = canvas.getContext('2d');
        captchaText = drawCaptcha(ctx, canvas.width, canvas.height);
    }

    function sendEmail() {
        if (!isValidEmail(formData.value.email)) {
            message.value = '请输入有效的邮箱地址';
            return;
        }
        if (isValidCaptcha(userInputCaptchaText.value, captchaText)) {
            isValid.value = true;
            showEmailVerification.value = true;
            message.value = '请查收邮箱验证码';
            // 发送邮件验证码的逻辑可以放在这里
            sendVerificationCode(formData.value.email);
        } else {
            message.value = '验证码错误,请重新输入';
            refreshCaptcha();
            userInputCaptchaText.value = '';
            isValid.value = false;
            showEmailVerification.value = false;
        }
    }

    const sendVerificationCode = async (email) => {
        try {
            const response = await axios.post('http://localhost:8080/api/register', { email }, {
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            console.log('Verification code sent successfully:', response.data);
        } catch (error) {
            console.error('Error sending verification code:', error);
        }
    };

    // 校验邮箱验证码
    function verifyEmailVerificationCode() {
        if (isValidEmailVerificationCode(formData.value.emailVerificationCode)) {
            loginSuccess.value = true;
            message.value = '邮箱验证码校验通过,登录成功!';
        } else {
            message.value = '邮箱验证码错误,请重新输入';
        }
    }

    const isValidEmailVerificationCode = async (code) => {
        console.log(code);
        try {
            const response = await axios.post('http://localhost:8080/api/checkEmail', { code }, {
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            console.log('Verification code check result:', response.data);

            return response.data;
        } catch (error) {
            console.error('Error checking verification code:', error);
            message.value = '校验邮箱验证码时发生错误';
            return false;
        }
    }

    return {
        formData,
        userInputCaptchaText,
        isValid,
        showEmailVerification,
        loginSuccess,
        message,
        refreshCaptcha,
        sendEmail,
        verifyEmailVerificationCode
    };
}

function isValidEmail(email) {
    const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
    return emailRegex.test(email);
}

function isValidCaptcha(inputCaptcha, generatedCaptcha) {
    return inputCaptcha.toLowerCase() === generatedCaptcha.toLowerCase();
}

相关推荐

  1. seleniumui自动化实例-邮箱登录

    2024-07-09 16:54:12       47 阅读
  2. py注册登录界面

    2024-07-09 16:54:12       56 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-09 16:54:12       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-09 16:54:12       106 阅读
  3. 在Django里面运行非项目文件

    2024-07-09 16:54:12       87 阅读
  4. Python语言-面向对象

    2024-07-09 16:54:12       97 阅读

热门阅读

  1. RISC-V的历史与设计理念

    2024-07-09 16:54:12       29 阅读
  2. mysql面试

    2024-07-09 16:54:12       36 阅读
  3. linux程序安装-编译-rpm-yum

    2024-07-09 16:54:12       26 阅读
  4. Haproxy搭建Web群集

    2024-07-09 16:54:12       34 阅读
  5. DNS隧道

    DNS隧道

    2024-07-09 16:54:12      36 阅读
  6. 利用node连接mongodb实现一个小型后端服务系统demo

    2024-07-09 16:54:12       29 阅读
  7. pdfplumber vs PyMuPDF:PDF文本、图像和表格识别的比较

    2024-07-09 16:54:12       36 阅读
  8. 编写简单的Ansible Playbook

    2024-07-09 16:54:12       33 阅读
  9. VSCode + 阿里云OSS + 图床插件Picgo

    2024-07-09 16:54:12       38 阅读