Vue 邮箱登录界面

news2024/12/27 11:45:26

功能

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

技术介绍

主要介绍绘制图形人机验证乃个
使用的是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();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1898503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

哏号分治,CF103D - Time to Raid Cowavans

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 103D - Time to Raid Cowavans 二、解题报告 1、思路分析 想了半天数据结构最终选择根号分治 我们考虑 大于 550 的公差直接暴力 小于550 的公差的所有询问&#xff0c;我们直接计算该公差后缀和&#xf…

Ubuntu 22.04.4 LTS 安装 php apache LAMP 环境nginx

1 安装php-fpm apt update apt-get install php-fpm #配置php-fpm服务启动 systemctl enable php8.1-fpm systemctl start php8.1-fpm #查看服务 systemctl status php8.1-fpm #查看版本 rootiZbp1g7fmjea77vsqc5hmmZ:~# php -v PHP 8.1.2-1ubuntu2.18 (cli) (built: J…

植物学(书籍学习资料)

包含观赏植物学、植物学、植物学百科图鉴等多本植物学方面的书籍学习资料。 图2、3为观赏植物学截图&#xff1b; 图4、5为植物学百科图鉴截图&#xff1b; 图6、7为植物学学习指南截图。

【网络安全】第4讲 身份认证技术(笔记)

一、身份认证技术概述 1、身份认证 是网络安全的第一道防线。是最基本的安全服务&#xff0c;其他的安全服务都依赖于它。在物联网应用系统中&#xff0c;身份认证也是整个物联网应用层信息安全体系的基础。 2、基本身份认证技术 &#xff08;1&#xff09;双方认证 是一种双…

数据结构——队列练习题

在C语言中&#xff0c;.和->运算符用于访问结构体的成员变量。它们之间的区别在于&#xff1a;.运算符用于访问结构体变量的成员。->运算符用于访问结构体指针变量的成员 1a&#xff08;rear指向队尾元素后一位&#xff0c;判空判满时牺牲一个存储单元&#xff09; 首先…

Linux系统安装青龙面板结合内网穿透实现使用公网地址远程访问

文章目录 前言一、前期准备本教程环境为&#xff1a;Centos7&#xff0c;可以跑Docker的系统都可以使用。本教程使用Docker部署青龙&#xff0c;如何安装Docker详见&#xff1a; 二、安装青龙面板三、映射本地部署的青龙面板至公网四、使用固定公网地址访问本地部署的青龙面板 …

ASP.NET Core 使用Log4net

1. Nuget安装log4net&#xff0c;图里的两个 2.项目根目录下添加log4net.config.添加下面的代码: <?xml version"1.0" encoding"utf-8"?> <configuration><!-- This section contains the log4net configuration settings --><log…

若依 Vue 前端分离 3.8.8 版中生成的前端代码中关于下拉框只有下拉箭头的问题

生成代码修改前 <el-form-item label"课程学科" prop"subject"><el-select v-model"queryParams.subject" placeholder"请选择课程学科" clearable><el-optionv-for"dict in course_subject":key"dict…

学会python——用python制作一个绘图板(python实例十九)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.制作一个绘图板 3.1 代码构思 3.2 代码实例 3.3 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可…

并发、多线程和HTTP连接之间有什么关系?

一、并发的概念 并发是系统同时处理多个任务或事件的能力。在计算中&#xff0c;这意味着系统能够在同一时间段内处理多个任务&#xff0c;而不是严格按照顺序一个接一个地执行它们。并发提高了系统的效率和资源利用率&#xff0c;从而更好地满足用户的需求。在现代应用程序中&…

2-3 图像分类数据集

MNIST数据集是图像分类任务中广泛使用的数据集之一&#xff0c;但作为基准数据集过于简单&#xff0c;我们将使用类似但更复杂的Fashion-MNIST数据集。 %matplotlib inline import torch import torchvision # pytorch模型关于计算机视觉模型实现的一个库 from torch.utils i…

Ubuntu24.04LTS基础软件下载

librewolf: deb文件link 作用&#xff1a;访问github&#xff0c;无痕浏览&#xff0c;这个速度&#xff0c;不指望了 vscodium: 从deb安装&#xff0c;ubuntu sudo dpkg -i xxx.debpaste-image 插件替代 markdown wps: libreoffice: 替换USTC源 sudo nano /etc/apt/sourc…

昇思25天学习打卡营第8天|ResNet50迁移学习

一、迁移学习定义 迁移学习&#xff08;Transfer Learning&#xff09;&#xff1a;在一个任务上训练得到的模型包含的知识可以部分或全部地转移到另一个任务上。允许模型将从一个任务中学到的知识应用到另一个相关的任务中。适用于数据稀缺的情况&#xff0c;可减少对大量标记…

【hive】数据采样

参考https://hadoopsters.com/how-random-sampling-in-hive-works-and-how-to-use-it-7cdb975aa8e2&#xff0c;可以直接查看原文&#xff0c;下面只是对原文进行概括和实际性能测试。 1.distribute by sort by2.测试3.map端数据过滤优化采样 在说数据采样之前&#xff0c;需要…

聚集索引与非聚集索引的区别

1.从文件存储方式来区别 聚集索引是指表的索引和数据存储在一个文件中&#xff08;innodb&#xff09; 非聚集索引指表数据与表索引存储在两个文件中&#xff08;MyISAM&#xff09; 2.从文件的检索方式来区别 聚集索引的data存在叶子节点 非聚集索引叶子节点存储的事data的…

三叶青图像识别研究简概

三叶青图像识别研究总概 文章目录 前言一、整体目录介绍二、前期安排三、构建图像分类数据集四、模型训练准备五、迁移学习模型六、在测试集上评估模型精度七、可解释性分析、显著性分析八、图像分类部署九、树莓派部署十、相关补充总结 前言 本系列文章为近期所做项目研究而作…

Java并发编程知识整理笔记

目录 ​1. 什么是线程和进程&#xff1f; 线程与进程有什么区别&#xff1f; 那什么是上下文切换&#xff1f; 进程间怎么通信&#xff1f; 什么是用户线程和守护线程&#xff1f; 2. 并行和并发的区别&#xff1f; 3. 创建线程的几种方式&#xff1f; Runnable接口和C…

基于aardio web.view2库和python playwright包的内嵌浏览器自动化操作

通过cdp协议可以实现playwright操控webview。 新建Python窗口工程 修改pip.aardio 修改pip.aardio&#xff0c;并执行&#xff0c;安装playwright。 //安装模块 import process.python.pip; //process.python.path "python.exe";/* 安装模块。 参数可以用一个字…

深度学习每周学习总结N3(文本分类实战:基本分类(熟悉流程)、textCNN分类(通用模型)、Bert分类(模型进阶))

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结&#xff1a;1. 前期准备环境安装 2. 文本分类基本流程a. 加载数据b.构建词典c.生成数据批次和迭代器d.定义模型及实例e. 定义…

C++ 仿QT信号槽二

// 实现原理 // 每个signal映射到bitset位&#xff0c;全集 // 每个slot做为signal的bitset子集 // signal全集触发&#xff0c;标志位有效 // flip将触发事件队列前置 // slot检测智能指针全集触发的标志位&#xff0c;主动运行子集绑定的函数 // 下一帧对bitset全集进行触发清…