vue3:八、登录界面实现-忘记密码

devtools/2025/3/22 8:35:37/

该文章实现登录界面的忘记密码功能,点击忘记密码文本,打开dialog对话框

一、页面效果

加入忘记密码,在记住密码的同一行中,实现flex-between

二、对话框实现

1、新建组件页面

2、引入dialog组件到组件页面

参考路径

Dialog 对话框 | Element Plus

找到对话框基本用法,复制相关代码

粘贴到新建的组件页面ForgetForm.vue

3、整理引入的代码

<template><el-dialog v-model="dialogVisible" title="Tips" width="30%"><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="dialogVisible = false">提交</el-button></span></template></el-dialog>
</template><script setup>
import { ref } from 'vue'const dialogVisible = ref(false)</script>
<style scoped>
.dialog-footer button:first-child {margin-right: 10px;
}
</style>

4、建立子组件父组件的关联

在dialog组件中,需要定义一个参数props用于接收父组件传值

  • defineProps:声明组件可以接收的属性
  • showDialog:布尔类型,控制对话框的显示与隐藏,默认为隐藏
  • title:字符串类型,用于设置对话框的标题
javascript">const props =defineProps({showDialog:{type:Boolean,default:false},title:{type:String,default:''}
})

 定义emit,用于触发自定义事件

  • defineEmits:定义组件可以触发的自定义事件
  • ['update:showDialog']:数组,列出了组件可以触发的事件名称。这里是update:showDialog事件,表示组件可以触发update:showDialog事件
javascript">//定义emit,触发自定义事件
const emit = defineEmits(['update:showDialog']);

根据父组件传递的参数,控制组件的显示与隐藏

首先引入计算属性

javascript">import { computed, ref } from 'vue'

方法写入 

javascript">//定义一个计算属性,根据接收的父组件的传值值,控制dialog的显示与隐藏
const dialogVisible = computed({get(){return props.showDialog},set(val){emit('update:showDialog',val)}
})

5、登录页面引入组件

①引入组件

javascript">import ForgetForm from '@/components/ForgetForm.vue'

②写入点击事件

<span class="forget" @click="forgetDialog = true">忘记密码</span>

③定义变量

javascript">//记住密码
const forgetDialog = ref(false);

④组件写入

  •  title="找回密码"子传递的是字符串,所以直接title就行,传递对话框的名称
  • :showDialog="forgetDialog"将子组件的显示状态绑定到forgetDialog变量上,也就是forgetDialog的值为啥就会传递给父组件的showDialog(布尔值)(这里是传递的是变量,所以用了:)
  • @update:showDialog="(v) => (forgetDialog = v)",使用子组件的方法update:showDialog监听子组件的显示状态,当子组件的显示状态改变时,将子组件的显示状态绑定到forgetDialog变量上,函数控制子组件是否显示
<ForgetForm title="找回密码" :showDialog="forgetDialog" @update:showDialog="(v)=>(forgetDialog = v)"></ForgetForm>

⑤对话框正常显示确认

三、找回密码功能实现

主要实现密码、二次输入密码,手机号输入,验证码填入功能。

实现密码、手机号、验证码的规范;密码与二次输入的密码是否保持一致;验证码接收的倒计时效果等

1、视图层

①页面效果

②代码

ForgetForm.vue

  • 在dialog中使用el表单,增加密码框(密码加密显示用 show-password属性 )、确认密码框、手机号输入框(使用number框),验证码输入框(使用number框)
  • 在验证码获取时:需要获取按钮+倒计时显示
  • 底部按钮:提交按钮+重置按钮+取消按钮
<template><el-dialog v-model="dialogVisible" :title="title" width="30%"><el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"class="demo-ruleForm" :size="formSize" status-icon><el-form-item label="新密码" prop="password"><el-input show-password v-model="ruleForm.password" /></el-form-item><el-form-item label="确认密码" prop="re_password"><el-input show-password v-model="ruleForm.re_password" /></el-form-item><el-form-item label="手机号" prop="phone"><el-input v-model.number="ruleForm.phone" /></el-form-item><el-form-item label="验证码" prop="code" class="flex flex-between"><el-input style="width:75%" v-model.number="ruleForm.code" /><el-button type="primary" @click="sendSms" style="width:22%" :disabled="seconds > 0"><span v-if="seconds > 0">{{ seconds }}</span><span v-else>发送验证码</span></el-button></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button @click="resetForm(ruleFormRef)">重置</el-button><el-button type="primary" @click="submitForm(ruleFormRef)">提交</el-button></div></template></el-dialog>
</template>

2、样式层

对按钮曾进行了一个右侧的外边距设置

<style scoped>.dialog-footer button:first-child {margin-right: 10px;
}
</style>

3、逻辑层

①基本表单数据

  • 首先定义四个表单数据密码:password,确认密码:re_password,手机号:phone,验证码:code
  • 验证规则的写法:
    • 密码的写法可以和登录页面一致(由于确认密码的验证规则和密码的规则一致,所以可以定义一个数组专门存入统一规则)
    • 确认密码:首先引入写入的规则,然后还需要加入是否和密码输入的一致
    • 手机号:通过pattern写入正则,按照手机号的正确格式
    • 验证码:通过pattern写入正则,输入6为数字验证码
javascript">//表单提交
const formSize = ref('default')
const ruleFormRef = ref()
const ruleForm = reactive({password: '',re_password: '',phone: '',code: '',
})
const psdrule = [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 20, message: '长度请在6-20之间', trigger: 'blur' },{ pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/, message: '密码必须包含大、小写字母、数字和特殊字符', trigger: 'blur' }
];
const rules = reactive({password: psdrule,re_password: [...psdrule,{validator: (rule, value, callback) => {if (value != ruleForm.password) {callback(new Error("两次输入密码不一致"))}else {callback();}}, trigger: 'blur'}],phone: [{ required: true, message: '请输入手机号', trigger: 'blur' },//1[3-9]\d{9}:手机号为1 + 3-9取一位 + 9位{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }],code: [{ required: true, message: '请输入验证码', trigger: 'blur' },// \d{6} 表示6位数字{ pattern: /^\d{6}$/, message: '验证码格式不正确', trigger: 'blur' }]
})

②验证码-定义发送验证码的api

新建一个关于发送验证码的api页面,这里是src/api/sms.js

(其实可以参见之前的文章)可以统一一下请求和方法:

vue3:request.js中请求方法,api封装请求,方法请求-CSDN博客

这里使用的请求方法采用的是request.js如下

javascript">// 封装GET请求
export const get = (url, params = {}) => {return request.get(url, { params });
};
// 封装POST请求
export const post = (url, data = {}) => {return request.post(url, data);
};
// 导出request实例
export default request;

 src/api/sms.js

javascript">import { post } from '@/utils/request';// 发送短信-重置密码
export function repwdsms(data) {return post('/sms/repwdsms', data); 
}

③验证码-手机验证码发送apifox

新建接口,写入参数phone

写入期望

④验证码-验证码的发送

  • 设置验证码变量,定义默认值为0
  • 定义一个定时器,默认为空
  • 写入验证码方法
    • 首先写入手机号的规则,如果当手机号满足规则时,才执行方法,否则报错
    • 手机号正确:发送请求,如果请求成功,进行提示,并且使用setInterval开始倒计时

引入发送验证码方法 

javascript">import { repwdsms } from '@/api/sms'

 发送验证码

javascript">//设置验证码倒计时,默认为0秒
const seconds = ref(0);
//设置一个为空的定时器
let timer = null;
//发送验证码
const sendSms = () => {//手机号规则const phoneRule = /^1[3-9]\d{9}$/;console.log(phoneRule.test(ruleForm.phone))if (!phoneRule.test(ruleForm.phone)) {ElMessage.error("手机号格式不正确");return}else {//发送请求repwdsms({phone: ruleForm.phone}).then(res => {if (res.code == 1) {//设置验证码为60秒倒计时,设置一个定时器,每秒减1seconds.value = 60;//如果timer存在就清除这个定时器,然后在执行定时器操作timer && clearInterval(timer);timer = setInterval(() => {seconds.value--;if (seconds.value == 0) {clearInterval(timer);}}, 1000);ElMessage.success(res.msg || "验证码发送成功");}else {ElMessage.error(res.msg || "验证码发送失败");}})}
}

⑤ 提交-定义提交表单的api

 新建一个关于发送验证码的api页面,这里是src/api/sms.js

⑥提交-提交表单apifox

新建接口,写入参数密码,确认密码,手机号,验证码四个参数

新建期望

建立重置密码成功的期望

⑦提交-提交表单

请求成功,返回提示信息,并且隐藏对话框;如果失败就返回提示信息

javascript">//提交表单
const submitForm = async (formEl) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {repwd(ruleForm).then(res => {if(res.code ==1){ElMessage.success(res.msg || '修改成功')dialogVisible.value = false;//隐藏对话框}else{ElMessage.error(res.msg || '修改失败')}})} else {console.log('error submit!', fields)}})
}

四、完整代码

1、登录页面

路径:src/views/LoginView.vue

<template><div class="page_all flex flex-center"><div class="login_all flex flex-between"><div class="login_left flex flex-center"><img src="/public/img/login.png"></div><div class="login_right flex flex-center flex-column"><div class="form flex flex-center flex-column"><div class="title flex flex-center">CMS管理系统</div><el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="el-form demo-ruleForm":size="formSize" status-icon><el-form-item prop="username"><el-input v-model="ruleForm.username" placeholder="请输入账号" /></el-form-item><el-form-item prop="password"><el-input v-model="ruleForm.password" show-password placeholder="请输入密码" /></el-form-item><el-form-item class="checkbox flex flex-between"><el-checkbox label="记住密码" v-model="remember" /><span class="forget" @click="forgetDialog = true">忘记密码</span></el-form-item><el-form-item class="btn-group"><el-button type="primary" @click="submitForm(ruleFormRef)">登录</el-button><el-button @click="resetForm(ruleFormRef)">重置</el-button></el-form-item></el-form></div></div></div></div><ForgetForm title="找回密码" :showDialog="forgetDialog" @update:showDialog="(v)=>(forgetDialog = v)"></ForgetForm>
</template>
<script setup>
//引入方法
import { reactive, ref } from 'vue'
import { login } from '@/api/user'
import { setToken } from '@/utils/token'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getLoginInfo, setLoginInfo, removeLoginInfo } from '@/utils/logininfo'
import ForgetForm from '@/components/ForgetForm.vue'const formSize = ref('default')
const ruleFormRef = ref()
const ruleForm = reactive({username: '',password: '',
})//记住密码
const forgetDialog = ref(false);//设置记住密码,默认为未选中
const remember = ref(false)
const loginInfo = getLoginInfo();
if(loginInfo){ruleForm.username = loginInfo.username;ruleForm.password = loginInfo.password;remember.value = true;
}
//设置路由
const router = useRouter();
//设置验证规则
const rules = reactive({username: [{ required: true, message: '请输入账号', trigger: 'blur' },{ min: 3, max: 10, message: '长度请在3-10之间', trigger: 'blur' },],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 20, message: '长度请在6-20之间', trigger: 'blur' },{validator: (rule, value, callback) => {const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/;if (!passwordPattern.test(value)) {callback(new Error('密码必须包含大写字母、小写字母、数字和特殊字符'));} else {callback();}}, trigger: 'blur'}],
})
//表单提交
const submitForm = async (formEl) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {console.log('submit!');// 1、请求登录接口进行登录// 参数为ruleFormconsole.log(ruleForm)// 2、请求登录接口进行登录login(ruleForm).then(res => {if (res.code == 1) {// 2、提示成功信息ElMessage.success(res.msg || '登录成功')//记住密码设置console.log('记住密码?:', remember.value);if(remember.value){setLoginInfo({username: ruleForm.username,password: ruleForm.password})}else{removeLoginInfo();}// return// 3、设置tokensetToken(res.data.token)// 4、跳转页面router.push('/')}else {ElMessage.error(res.msg || '登录失败')}})} else {console.log('error submit!', fields)}})
}
//重置表单
const resetForm = (formEl) => {if (!formEl) returnformEl.resetFields()
}</script>
<style>
.page_all {width: 100%;height: 100vh;background-color: #808cdd;
}.login_all {width: 50%;height: 60%;background-color: white;
}.login_left {width: 50%;height: 98%;
}.login_left img {width: 95%;height: 80%;object-fit: contain;
}.login_right {width: 50%;height: 100%;
}.title {font-size: 25px;color: #646cff;letter-spacing: 3px;height: 20%;
}.form {flex: 1;width: 90%;
}.el-form {width: 60%;
}.checkbox{margin-top: 20px;
}
.btn-group {width: 100%;
}.btn-group button {width: 45%;
}.el-form-item__content {justify-content: space-between;
}
.forget{color: #646cff;font-size:90%;text-decoration: underline;cursor: default;
}
</style>

2、忘记密码对话框

路径:src/components/ForgetForm.vue

<template><el-dialog v-model="dialogVisible" :title="title" width="30%"><el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"class="demo-ruleForm" :size="formSize" status-icon><el-form-item label="新密码" prop="password"><el-input show-password v-model="ruleForm.password" /></el-form-item><el-form-item label="确认密码" prop="re_password"><el-input show-password v-model="ruleForm.re_password" /></el-form-item><el-form-item label="手机号" prop="phone"><el-input v-model.number="ruleForm.phone" /></el-form-item><el-form-item label="验证码" prop="code" class="flex flex-between"><el-input style="width:75%" v-model.number="ruleForm.code" /><el-button type="primary" @click="sendSms" style="width:22%" :disabled="seconds > 0"><span v-if="seconds > 0">{{ seconds }}</span><span v-else>发送验证码</span></el-button></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button @click="resetForm(ruleFormRef)">重置</el-button><el-button type="primary" @click="submitForm(ruleFormRef)">提交</el-button></div></template></el-dialog>
</template><script setup>
import { computed, ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { repwdsms } from '@/api/sms'
import { repwd } from '@/api/user'
// 定义props,用于接收父组件传值
const props = defineProps({showDialog: {type: Boolean,default: false},title: {type: String,default: ''}
})
//定义emit,触发自定义事件
const emit = defineEmits(['update:showDialog']);
//定义一个计算属性,根据接收的父组件的传值值,控制dialog的显示与隐藏
const dialogVisible = computed({get() {return props.showDialog},set(val) {emit('update:showDialog', val)}
})//表单提交
const formSize = ref('default')
const ruleFormRef = ref()
const ruleForm = reactive({password: '',re_password: '',phone: '',code: '',
})
const psdrule = [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 20, message: '长度请在6-20之间', trigger: 'blur' },{ pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/, message: '密码必须包含大、小写字母、数字和特殊字符', trigger: 'blur' }
];
const rules = reactive({password: psdrule,re_password: [...psdrule,{validator: (rule, value, callback) => {if (value != ruleForm.password) {callback(new Error("两次输入密码不一致"))}else {callback();}}, trigger: 'blur'}],phone: [{ required: true, message: '请输入手机号', trigger: 'blur' },//1[3-9]\d{9}:手机号为1 + 3-9取一位 + 9位{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }],code: [{ required: true, message: '请输入验证码', trigger: 'blur' },// \d{6} 表示6位数字{ pattern: /^\d{6}$/, message: '验证码格式不正确', trigger: 'blur' }]
})
//提交表单
const submitForm = async (formEl) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {repwd(ruleForm).then(res => {if(res.code ==1){ElMessage.success(res.msg || '修改成功')dialogVisible.value = false;//隐藏对话框}else{ElMessage.error(res.msg || '修改失败')}})} else {console.log('error submit!', fields)}})
}
//重置表单
const resetForm = (formEl) => {if (!formEl) returnformEl.resetFields()
}
//设置验证码倒计时,默认为0秒
const seconds = ref(0);
//设置一个为空的定时器
let timer = null;
//发送验证码
const sendSms = () => {//手机号规则const phoneRule = /^1[3-9]\d{9}$/;console.log(phoneRule.test(ruleForm.phone))if (!phoneRule.test(ruleForm.phone)) {ElMessage.error("手机号格式不正确");return}else {//发送请求repwdsms({phone: ruleForm.phone}).then(res => {if (res.code == 1) {//设置验证码为60秒倒计时,设置一个定时器,每秒减1seconds.value = 60;//如果timer存在就清除这个定时器,然后在执行定时器操作timer && clearInterval(timer);timer = setInterval(() => {seconds.value--;if (seconds.value == 0) {clearInterval(timer);}}, 1000);ElMessage.success(res.msg || "验证码发送成功");}else {ElMessage.error(res.msg || "验证码发送失败");}})}
}
</script>
<style scoped>.dialog-footer button:first-child {margin-right: 10px;
}
</style>

3、关于用户的api

路径:src/api/user.js

javascript">import { post } from '@/utils/request';// 登录
export function login(data) {return post('/user/login', data); 
}
//重置密码-通过手机号
export function repwd(data) {return post('/user/repassword', data); 
}

4、关于验证码的api

路径:src/api/sms.js

javascript">import { post } from '@/utils/request';// 发送短信-重置密码
export function repwdsms(data) {return post('/sms/repwdsms', data); 
}


http://www.ppmy.cn/devtools/169114.html

相关文章

<el-form >ref数据监测不到的原因

<template><el-form ref"container"><el-form-item><el-input v-model"inputValue" placeholder"请输入内容"></el-input></el-form-item></el-form> </template><script setup> import …

GLB文件介绍

GLB文件是由支持glTF&#xff08;GL Transmission Format&#xff09;标准的软件或工具生成的。glTF是一种开放的3D模型传输格式&#xff0c;而GLB是其二进制版本&#xff0c;通常用于嵌入纹理和模型数据。以下是常见的生成GLB文件的软件和工具&#xff1a; 1. 3D建模软件 • …

OpenCV图像处理基础2

接着上一篇OpenCV图像处理基础1继续说。 图像阈值处理 1、简单阈值处理 ret, thresholded_image = cv2.threshold(image, thresh, maxval, cv2.THRESH_BINARY)thresh 是阈值,maxval 是最大值。 2、自适应阈值处理 thresholded_image = cv2.adaptiveThreshold(image, maxv…

Python与区块链隐私保护技术:如何在去中心化世界中保障数据安全

Python与区块链隐私保护技术:如何在去中心化世界中保障数据安全 在区块链世界里,透明性和不可篡改性是两大核心优势,但这也带来了一个悖论——如何在公开账本的同时保障用户隐私?如果你的交易记录对所有人可见,如何防止敏感信息泄露? Python 作为区块链开发中最受欢迎的…

算法1--两束求和

题目描述 解题思路 先说一种很容易想到的暴力解法 暴力解法的思路很简单&#xff0c;就是遍历数组&#xff0c;对于每一个元素&#xff0c;都去遍历数组中剩下的元素&#xff0c;判断是否有两个元素的和等于目标值&#xff0c;如果有&#xff0c;就返回这两个元素的下标。 c…

【架构】单体架构 vs 微服务架构:如何选择最适合你的技术方案?

文章目录 ⭐前言⭐一、架构设计的本质差异&#x1f31f;1、代码与数据结构的对比&#x1f31f;2、技术栈的灵活性 ⭐二、开发与维护的成本博弈&#x1f31f;1、开发效率的阶段性差异&#x1f31f;2、维护成本的隐形陷阱 ⭐三、部署与扩展的实战策略&#x1f31f;1、部署模式的本…

微服务分层架构详解:表示层、应用层与基础设施层的协同工作

微服务分层架构详解&#xff1a;表示层、应用层与基础设施层的协同工作 文章目录 微服务分层架构详解&#xff1a;表示层、应用层与基础设施层的协同工作1. 表示层&#xff08;Presentation Layer&#xff09;1.1 表示层的作用1.2 技术选型1.3 表示层的挑战 2. 应用层&#xff…

python打乱列表顺序

在 Python 中&#xff0c;有多种方法可以打乱列表的顺序。最常用的方法是使用 random 模块中的 shuffle 函数。这个函数会直接在原列表上进行操作&#xff0c;将列表中的元素顺序随机打乱。 以下是一个简单的示例&#xff1a; import random# 创建一个示例列表 my_list [1, …