当前位置: 首页 > article >正文

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

该文章实现登录界面的忘记密码功能,点击忘记密码文本,打开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:字符串类型,用于设置对话框的标题
const props =defineProps({
    showDialog:{
        type:Boolean,
        default:false
    },
    title:{
        type:String,
        default:''
    }
})

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

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

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

首先引入计算属性

import { computed, ref } from 'vue'

方法写入 

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

5、登录页面引入组件

①引入组件

import ForgetForm from '@/components/ForgetForm.vue'

②写入点击事件

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

③定义变量

//记住密码
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为数字验证码
//表单提交
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如下

// 封装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

import { post } from '@/utils/request';

// 发送短信-重置密码
export function repwdsms(data) {
    return post('/sms/repwdsms', data); 
}

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

新建接口,写入参数phone

写入期望

④验证码-验证码的发送

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

引入发送验证码方法 

import { repwdsms } from '@/api/sms'

 发送验证码

//设置验证码倒计时,默认为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秒倒计时,设置一个定时器,每秒减1
                seconds.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

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

新建期望

建立重置密码成功的期望

⑦提交-提交表单

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

//提交表单
const submitForm = async (formEl) => {
    if (!formEl) return
    await 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) return
    await formEl.validate((valid, fields) => {
        if (valid) {
            console.log('submit!');
            // 1、请求登录接口进行登录
            // 参数为ruleForm
            console.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、设置token
                    setToken(res.data.token)
                    // 4、跳转页面
                    router.push('/')
                }
                else {
                    ElMessage.error(res.msg || '登录失败')
                }
            })
        } else {
            console.log('error submit!', fields)
        }
    })
}
//重置表单
const resetForm = (formEl) => {
    if (!formEl) return
    formEl.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) return
    await 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) return
    formEl.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秒倒计时,设置一个定时器,每秒减1
                seconds.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

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

import { post } from '@/utils/request';

// 发送短信-重置密码
export function repwdsms(data) {
    return post('/sms/repwdsms', data); 
}

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

相关文章:

  • DeepSeek与人工智能:技术演进、架构解析与未来展望
  • Hive SQL 精进系列:REGEXP_COUNT 函数的用法
  • 函数指针/逗号表达式/不用if语句完成的字母输出题
  • React 和 Vue 框架设计原理对比分析
  • 乐观锁VS分布式锁实现抢单服务
  • windows安装两个或多个JDK,并实现自由切换
  • git证书文件路径错误
  • GitHub 汉化插件,GitHub 中文化界面安装全教程_最新
  • linux root丢失修改密
  • 【spring-boot-starter-data-neo4j】创建结点和查找结点操作
  • 用tiptap搭建仿google-docs工具栏
  • JavaScript基础篇:五、 流程控制语句
  • java学习笔记2
  • 告别XML模板的繁琐!Word文档导出,easy!
  • Kubernetes 单节点集群搭建
  • tcpdump剖析:入门网络流量分析实战指南
  • Ubuntu从源代码编译安装QT
  • 进程间通信--匿名管道
  • 【蓝桥杯】雪地工程核弹引爆控制器最小数量计算
  • Pytorch实现之最小二乘梯度归一化设计