该文章实现登录界面的忘记密码功能,点击忘记密码文本,打开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);
}