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();
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cookie的默认存储路径以及后端如何设置

问题场景 最近在写一个前后端分离的项目&#xff0c;需要跨域&#xff0c;前端开发同学遇到一个问题一直报错&#xff0c;本质上就是后端返回的cookie中的sessionID在前端发送http请求时无法被请求自动携带&#xff0c;每次htttpRequest都被后端识别为一个新的session&#xf…

Java传引用问题

本文将介绍 Java 中的引用传递&#xff0c;包括其定义、实现方式、通过引用修改原来指向的内容和通过引用修改当前引用的指向的区别 目录 1、引用传递的概念 2、引用传递的实现方式 3、传引用会发生的两种情况&#xff1a; 通过引用修改当前引用的指向 通过引用修改原来指…

IDEA 编译单个Java文件

文章目录 一、class文件的生成位置二、编译单个文件编译项目报错Error:java: 无效的源发行版: 8 一、class文件的生成位置 file->project structure->Modules 二、编译单个文件 选中文件&#xff0c;点击recompile 编译项目报错 Error:java: 无效的源发行版: 8 Fi…

谈大语言模型动态思维流程编排

尽管大语言模型已经呈现出了强大的威力&#xff0c;但是如何让它完美地完成一个大的问题&#xff0c;仍然是一个巨大的挑战。 需要精心地给予大模型许多的提示&#xff08;Prompt&#xff09;。对于一个复杂的应用场景&#xff0c;编写一套完整的&#xff0c;准确无误的提示&am…

【技术支持】console控制台输出美化(腾讯文档)

function style(color, size 12){return display:inline-block;background-color:${color};color:#fff;padding:2px 4px;font-size:${size}px; } const dataVersion 3.0.0 const codeVersion 3.0.28657969 const branchVersion release-20240617-f98487dc //注意此处%c后面…

Amesim应用篇-信号传递

前言 在Amesim中常见的信号传递是通过信号线连接&#xff0c;针对简单的模型通过信号线连接还可以是信号线清晰规整&#xff0c;方便查看。如果模型较复杂&#xff0c;传递信号的元件较多时&#xff0c;此时再继续使用信号线进行信号传递&#xff0c;可能会使草图界面看起来杂…

vxe-table合并行数据;element-plus的el-table动态合并行

文章目录 一、vxe-table合并行数据1.代码 二、使用element-plus的el-table动态合并行2.代码 注意&#xff1a;const fields 是要合并的字段 一、vxe-table合并行数据 1.代码 <vxe-tableborderresizableheight"500":scroll-y"{enabled: false}":span-m…

ASUS/华硕枪神4 G532L G732L系列 原厂win10系统 工厂文件 带F12 ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;Windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…

【HALCON】如何实现hw窗口自适应相机拍照成像的大小

前言 在开发一个喷码检测软件的时候碰到相机成像和hw窗体的大小不一致&#xff0c;hw太小显示不完全成像的图片&#xff0c;这使得成像不均匀&#xff0c;现场辨别起来比较不直观&#xff0c;因此需要对其进行一个调整。 解决 省略掉读取图片的环节&#xff0c;我们只需要将…

BSI 第七届万物互联智慧高峰论坛:主题:拥抱AI时代,标准赋能组织实现可持续发展

BSI 第七届万物互联智慧高峰论坛&#xff1a;主题&#xff1a;拥抱AI时代&#xff0c;标准赋能组织实现可持续发展 主要收到 BSI 温女士的邀请参加的本次论坛。还是学到的很多 。 在科技日新月异的时代背景下&#xff0c;BSI 第七届万物互联智慧高峰论坛于[时间&#xff1a;6…

mac安装docker

1、首先打开docker官网 https://docs.docker.com/engine/install/ 2、下载好后安装到app应用 3、安装好环境变量 #docker echo export PATH"/usr/local/Cellar/docker/20.10.11/bin:$PATH" >> .bash_profile

百度云智能媒体内容分析一体机(MCA)建设

导读 &#xff1a;本文主要介绍了百度智能云MCA产品的概念和应用。 媒体信息海量且复杂&#xff0c;采用人工的方式对视频进行分析处理&#xff0c;面临着效率低、成本高的困难。于是&#xff0c;MCA应运而生。它基于百度自研的视觉AI、ASR、NLP技术&#xff0c;为用户提供音视…

不错的用户需求访谈方法

不错的用户需求访谈方法&#xff0c;可以用如下的矩阵&#xff0c;用来引导用户访谈&#xff1a;

vue实现搜索文章关键字,滑到指定位置并且高亮

1、输入搜索条件&#xff0c;点击搜索按钮 2、滑到定位到指定的搜索条件。 <template><div><div class"search_form"><el-inputv-model"searchVal"placeholder"请输入关键字查询"clearablesize"small"style&quo…

Go源码--channel源码解读

简介 channel顾名思义就是channel的意思&#xff0c;主要用来在协程之间传递数据&#xff0c;所以是并发安全的。其实现原理&#xff0c;其实就是一个共享内存再加上锁&#xff0c;底层阻塞机制使用的是GMP模型。可见 GMP模型就是那个道&#xff0c;道生一,一生二,二生三,三生…

2024.8月28号杭州电商博览会,在杭州国博举办

2024杭州电商新渠道博览会暨集脉电商节 时间&#xff1a;2024年08月28-30日 地点&#xff1a;杭州国际博览中心&#xff08;G20&#xff09; 主办单位&#xff1a;浙江集脉展览有限公司、杭州华维展览有限公司 承办单位&#xff1a;浙江集脉展览有限公司 报名参展&#xf…

Navicat和MySQL的安装

1、下载 Navicat Navicat 官网&#xff1a;www.navicat.com.cn/ 在产品中可以看到很多的产品&#xff0c;点击免费试用 Navicat Premium 即可&#xff0c;是一套多连数据库开发工具&#xff0c;其他的只能连接单一类型数据库 点击试用 选择系统直接下载 二、安装 Navicat 安…

03:EDA的进阶使用

使用EDA设计一个38译码器电路和245放大电路 1、38译码器1.1、查看74HC138芯片数据1.2、电路设计 2、245放大电路2.1、查看数据手册2.2、设计电路 3、绘制PCB3.1、导入3.2、放置3.3、飞线3.4、特殊方式连接GND3.5、泪滴3.6、配置丝印和划分区域3.7、添加typc接口供电 1、38译码器…

‘艾’公益——微笑行动「广安站」为艾祝福,让笑起舞

艾多美“微笑行动”广安站拉开帷幕 此次爱心帮助7名唇腭裂患儿 重新绽放微笑 艾多美“微笑行动”广安站拉开帷幕 此次爱心帮助7名唇腭裂患儿 重新绽放微笑 不让笑容留有缺憾 每个孩子都有微笑的权利 艾多美向唇腭裂儿童伸出援手 绽放笑容&#xff0c;拥抱全新的未来 2…

通信安全员考试精选练习题库,2024年备考必刷题!

16.设计单位必须在设计文件中&#xff08;&#xff09;计列安全生产费。 A.全额 B.部分 C.按建设单位要求 D.按工程建设需要 答案&#xff1a;A 17.日最高气温达到&#xff08;&#xff09;℃以上&#xff0c;应当停止当日室外露天作业。 A.38 B.36 C.35 D.40 答案&…