选片 网站 建设,福州高端建站,单页面网站怎么做优化排名,网站开发时间表环境搭建
在node安装好的情况下#xff08;一般vue环境有的node也有 没有可以使用winr回车输入node -v 有版本号则已经安装好 找一个空文件夹作为此项目文件夹 点击上面的地址栏输入cmd回车 输入npm init -y 再输入npm install nodemailer安装发送邮件的插件 环境配置
使用v…环境搭建
在node安装好的情况下一般vue环境有的node也有 没有可以使用winr回车输入node -v 有版本号则已经安装好 找一个空文件夹作为此项目文件夹 点击上面的地址栏输入cmd回车 输入npm init -y 再输入npm install nodemailer安装发送邮件的插件 环境配置
使用vscode打开这个程序 在vscode中的终端输入npm install 命令完成后项目会多个json文件里面是插件还有一些基础环境的版本配置 在文件夹根目录新建servers.js
const express require(express);
const nodemailer require(nodemailer);const app express();
const port 3000;
const content 123456;// 创建发送邮件的接口
app.get(/send-email, (req, res) {console.log(req)console.log(bodyParser)console.log(123123);// 创建邮件传输对象let transporter nodemailer.createTransport({service: 163, // 运营商选择 qq 网易port: 465,secure: true, // true for 465, false for other portsauth: {user: 123123163.com, // 发送方的邮箱pass: ABCDSD // pop3 授权码 **百度如何获取吧**}});// 生成六位随机数作为验证码const code Math.floor(100000 Math.random() * 900000);let mailOptions {from: 发送人 123123163.com, // 发送方邮箱to: 123123qq.com, // list of receiverssubject: 欢迎注册√, // Subject linetext: ${content}, // plain text bodyhtml: 欢迎注册xxx系统,验证码为:h1 stylebackground-color:white;${code}h1,有效期为五分钟. // html body }// 发送邮件transporter.sendMail(mailOptions, (error, info) {if (error) {console.log(error);res.status(500).send({success: false,data: {}});} else {console.log(mailOptions)console.log(Email sent: info.response);res.send({//返回值success: true,data: {code: 123123}});}});
});// 启动服务器
app.listen(port, () {console.log(Server running on port ${port});
});
是的我把后面的代码也贴上了 其实具体只需要添加启动服务器 端口设置使用一个测试接口即可 设置完之后先配置自己的邮箱
配置邮箱
打开自己的网易邮箱或者是qq邮箱 点击设置 打开这个 在里面打开这两个设置 通过验证之后保存好自己的pop3授权码只显示一次 找不到了自行百度解决
配置好之后将授权码、自己的邮箱 、要发送的邮箱都进行配置
配置好之后启动
启动
终端中输入 node server.js 可以看到终端显示 {//返回值 success: true, data: { code: code } }); 这样的这就是返回值。 这里面的code就是生成的六位数随机码通过接口传给前端 尝试使用浏览器 里边输入http://localhost:3000/send-email回车能看到同样的有返回值 并且发现收到了验证码
前端配置
前端如果使用ajax直接键入接口api即可即http://localhost:3000/send-email
思路
实现邮箱验证码是从你的数据库获得id对应的邮箱并填充不建议或者用户输入邮箱之后先进行校验 校验结束之后用户可以点击发送验证码 点击按钮开始60秒倒计时 倒计时结束之后才能再次点击这是最简单的防抖动 点击之后调用接口发送邮箱号然后咱们刚刚写好的接口对这个邮箱号进行获取获取之后配置好邮箱 随机生成六位验证码其实就是生成一个100000到999999的数而已 配置到邮箱之后发送邮箱接口返回一个验证码给前端 前端获取验证码之后放入pinia中然后和用户键入的六位验证码对比如果用户输入的不是六位验证码或者是其他数字则提醒输入六位数字
待解决问题
1、接口返回的验证码很容易被截取也就是数据安全收到威胁 现在还没有更好的办法 2、用户输入id的时候是直接获取其邮箱i地址还是由用户直接输入存在争议因为邮箱和账号并没有绑定如果绑定了必然要在注册页面对邮箱进行绑定绑定的时候又要发送验证码。算是我之前考虑不周
前端配置60秒倒数 设置验证码和邮箱校验
/** 6位数字验证码正则 */ export const REGEXP_SIX /^\d{6}KaTeX parse error: Undefined control sequence: \w at position 40: …nst EMAIL /^[\̲w̲-](\.[\w-])*…/;
/** 忘记密码校验 */ const updateRules reactive({
verifyCode: [ { validator: (rule, value, callback) { if (value “”) { callback(new Error(“请输入验证码”)); } else if (!REGEXP_SIX.test(value)) { callback(new Error(“请输入六位数字”)); } else { callback(); } }, trigger: “blur” } ], email: [ { validator: (rule, value, callback) { if (value “”) { callback(new Error(“请输入邮箱”)); } else if (!EMAIL.test(value)) { callback(new Error(“不正确的邮箱格式”)); } else { callback(); } }, trigger: “blur” } ], password: [ { validator: (rule, value, callback) { if (value “”) { callback(new Error(“请输入密码”)); } else if (!REGEXP_PWD.test(value)) { callback(new Error(“密码格式应为8-18位数字、字母、符号的任意两种组合”)); } else { callback(); } }, trigger: “blur” } ] });
整个ts文件代码
import { reactive } from vue;
import type { FormRules } from element-plus;import { useUserStoreHook } from /store/modules/user;
/** 密码正则密码格式应为8-18位数字、字母、符号的任意两种组合 */
export const REGEXP_PWD /^(?![0-9]$)(?![a-z]$)(?![A-Z]$)(?!([^(0-9a-zA-Z)]|[()])$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
/** 6位数字验证码正则 */
export const REGEXP_SIX /^\d{6}$/;
/** 邮箱正则 */
export const EMAIL /^[\w-](\.[\w-])*([\w-]\.)[a-zA-Z]{2,7}$/;/** 登录校验 */
const loginRules reactiveFormRules({password: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入密码));}// else if (!REGEXP_PWD.test(value)) {// callback(new Error( 密码格式应为8-18位数字、字母、符号的任意两种组合));// }else {callback();}},trigger: blur}],email: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入邮箱));} else if (!EMAIL.test(value)) {callback(new Error(不正确的邮箱格式));} else {callback();}},trigger: blur}],verifyCode: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入验证码));} else if (useUserStoreHook().verifyCode ! value) {callback(new Error(请输入正确的验证码));} else {callback();}},trigger: blur}]
});/** 忘记密码校验 */
const updateRules reactiveFormRules({verifyCode: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入验证码));} else if (!REGEXP_SIX.test(value)) {callback(new Error(请输入六位数字));} else {callback();}},trigger: blur}],email: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入邮箱));} else if (!EMAIL.test(value)) {callback(new Error(不正确的邮箱格式));} else {callback();}},trigger: blur}],password: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入密码));} else if (!REGEXP_PWD.test(value)) {callback(new Error(密码格式应为8-18位数字、字母、符号的任意两种组合));} else {callback();}},trigger: blur}]
});// else if (REGEXP_PWD.test(value)) {//!REGEXP_PWD.test(value)
// callback(
// new Error(密码格式应为8-18位数字、字母、符号的任意两种组合)
// );
export { loginRules, updateRules };整个vue文件
script setup langts
import { ref, reactive, watch, onMounted } from vue;
import Motion from ../utils/motion;
import { message } from /utils/message;
import { updateRules } from ../utils/rule;
import type { FormInstance } from element-plus;
import { useVerifyCode } from ../utils/verifyCode;
import { useUserStoreHook } from /store/modules/user;
import { useRenderIcon } from /components/ReIcon/src/hooks;
import Lock from iconify-icons/ri/lock-fill;
import Iphone from iconify-icons/ep/iphone;
import email from iconify-icons/ep/message;
import User from iconify-icons/ri/user-3-fill;
import { ReImageVerify } from /components/ReImageVerify;
import sendMail from ../utils/send;// sendMail(mail, 123)
// .then(() {
// console.log(邮件发送成功);
// })
// .catch(() {
// console.log(邮件发送失败);
// });
import nodemailer from nodemailer
const store useUserStoreHook();const checked ref(false);
const loading ref(false);
const ruleForm reactive({username: ,email: ,verifyCode: ,password: ,repeatPassword:
});
const ruleFormRef refFormInstance();
const { isDisabled, text } useVerifyCode();
const repeatPasswordRule [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入确认密码));} else if (ruleForm.password ! value) {callback(new Error(两次密码不一致!));} else {callback();}},trigger: blur}
];const onUpdate async (formEl: FormInstance | undefined) {loading.value true;if (!formEl) return;await formEl.validate((valid, fields) {if (valid) {if (checked.value) {// 模拟请求需根据实际开发进行修改setTimeout(() {message(密码修改成功即将返回登录界面, {type: success});loading.value false;}, 2000);setTimeout(() {useUserStoreHook().SET_CURRENTPAGE(0);}, 3000);}} else {loading.value false;return fields;}});
};function onBack() {useVerifyCode().end();useUserStoreHook().SET_CURRENTPAGE(0);}const imgCode ref();
watch(imgCode, value {useUserStoreHook().SET_VERIFYCODE(value);
});onMounted(() {});
/scripttemplateel-form refruleFormRef :modelruleForm :rulesupdateRules sizelargeMotionel-form-item :rules[{required: true,message: 请输入账号,trigger: blur}] propusernameel-input clearable v-modelruleForm.username :placeholder账号 :prefix-iconuseRenderIcon(User) //el-form-item/MotionMotionel-form-item propemailel-inputclearablev-modelruleForm.email:placeholder邮箱:prefix-iconuseRenderIcon(email)//el-form-item/MotionMotion :delay100el-form-item propverifyCodediv classw-full flex justify-between styledisplay: flex; justify-content: space-between;el-inputclearablev-modelruleForm.verifyCode:placeholder邮箱验证码stylewidth: 19vw; margin-right: 10px;:prefix-iconuseRenderIcon(ri:shield-keyhole-line)/el-button:disabledisDisabledclassml-2 align-rightclickuseVerifyCode().start(ruleFormRef, email){{text.length 0? text 秒后重新获取: 获取验证码}}/el-button/div/el-form-item
/MotionMotion :delay200el-form-item proppasswordel-input clearable show-password v-modelruleForm.password :placeholder新密码:prefix-iconuseRenderIcon(Lock) //el-form-item/MotionMotion :delay250el-form-item :rulesrepeatPasswordRule proprepeatPasswordel-input clearable show-password v-modelruleForm.repeatPassword :placeholder确认密码:prefix-iconuseRenderIcon(Lock) //el-form-item/MotionMotion :delay350el-form-itemdiv styledisplay: flex; justify-content: space-between;el-button classw-full sizedefault typeprimary :loadingloading clickonUpdate(ruleFormRef)确定/el-buttonel-button classw-full sizedefault clickonBack返回/el-button/div/el-form-item/Motion/el-form
/template
style
.scroll-container {/* width: 300px; *//* 设置容器的宽度 *//* height: 200px; *//* 设置容器的高度 */overflow: auto;/* 开启滚动功能 */
}.scroll-container::-webkit-scrollbar {width: 0;/* 隐藏滚动条 */
}.align-right {margin-left: auto;
}
/style无关的引入报错了就删掉就行
配置发送按钮的方法
点击发送先校验邮箱 通过之后则传入接口
按钮点击校验邮箱 el-button:disabledisDisabledclassml-2 align-rightclickuseVerifyCode().start(ruleFormRef, email),getcode(){{text.length 0? text 秒后重新获取: 获取验证码}}/el-button添加一个getcode方法 function getcode() { const email ruleForm.verifyCode.trim(); // 去除输入的空格
if (ruleForm.verifyCode “”) { ElMessage.error(‘请输入邮箱地址’); return; }
const emailRegex /1(.[\w-])*([\w-].)[a-zA-Z]{2,7}$/; if (!emailRegex.test(ruleForm.verifyCode)) { ElMessage.error(‘请输入正确的邮箱格式’); return; }
} 还是先校验一下 这次用弹窗的方式提醒用户 弹窗引入的插件是这个 import { ElMessage, formContextKey } from ‘element-plus’;
注意使用了trim方法 去除空格
配置接口
我之前尝试的直接在地址栏传入信息 成功了 但是我觉得还是不太妥 const toemail req.query.toemail; // 获取name参数的值 地址栏输入的样子 http://localhost:3000/send-email?toemail123123qq.com 这样能获取到但是我还是想使用接口里面的请求体发送请求数据 最后还是没有找到放置的地方 只能放在地址栏传输了 没关系 邮箱又不是什么私密的东西 我使用的是pure-admin的框架 里面配置的api基本地址 在utils.ts中加一行 export const baseUrlMyApi (url: string) /myapi/${url} 在vite.config.ts中加上 “^/myapi/.*”: { target: “http://localhost:3000”, changeOrigin: true, rewrite: path path.replace(/^/myapi/, “”) } 这里面的target就是配置的后端接口 ‘
前端接口编写
import { getToken } from /utils/auth;
import { http } from /utils/http;
import { baseUrlMyApi } from ./utils;import { useUserStore } from /store/modules/user;
import { useselectedStore } from /store/modules/selected;//发送邮箱验证码
export const postemail (myemail) {const data myemailreturn http.requestany(//baseUrlApi(/Analysis/ResultTempCurrent),post,baseUrlMyApi(send-email?toemail data),);
};
这个api接口需要一个参数 参数传的就是用户输入的邮箱地址
校验
在rule.ts中添加校验 /** 忘记密码校验 */
const updateRules reactiveFormRules({emailCode: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入验证码));} else if (!REGEXP_SIX.test(value)) {callback(new Error(请输入六位数字));} else if (useUserStoreHook().emailcode ! value) {console.log(useUserStoreHook().emailcode)console.log(value)callback(new Error(请输入正确的验证码));}else {callback();}},trigger: blur}],email: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入邮箱));} else if (!EMAIL.test(value)) {callback(new Error(不正确的邮箱格式));} else {callback();}},trigger: blur}],password: [{validator: (rule, value, callback) {if (value ) {callback(new Error(请输入密码));} else if (!REGEXP_PWD.test(value)) {callback(new Error(密码格式应为8-18位数字、字母、符号的任意两种组合));} else {callback();}},trigger: blur}]
});如果没有这个文件可以就按照我之前写的校验类型编写
测试接口是否能使用
import {postemail} from /api/emailpostemail(email).then((response) {
console.log(response)code.value response.data.code useUserStoreHook().SET_EMAILCODE(code.value);})然后可以看到我使用response获取到了接口返回来的code useUserStoreHook().SET_EMAILCODE(code.value);是我将code赋值给pinia里面的emailcode便于在另一个页面校验
也就是如果鼠标失去这个焦点的时候就开始校验 然后在注册按钮可以再次校验一次 如果和用户输入的不一样就报错 \w- ↩︎