锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. 前后端分离项目实战-通用管理系统搭建(前端Vue3+ElementPlus,后端Springboot+Mysql+Redis)第四篇:找回密码功能实现(手机验证码找回和邮箱验证码找回)

前后端分离项目实战-通用管理系统搭建(前端Vue3+ElementPlus,后端Springboot+Mysql+Redis)第四篇:找回密码功能实现(手机验证码找回和邮箱验证码找回)

0
  • 软件开发
  • 发布于 2024-08-19
  • 1 次阅读
黄健
黄健

天行健,君子以自强不息;地势坤,君子以厚德载物。


每个人都有惰性,但不断学习是好好生活的根本,共勉!


文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。


燕草如碧丝,秦桑低绿枝。
------《春思》



文章目录

    • 20. 找回密码功能实现
      • 20.1 找回密码的布局开发
        • 20.1.1 ResetPwd.vue
        • 20.1.2 router/index.ts
        • 20.1.3 UsernameForm.vue
        • 20.1.4 页面效果
      • 20.2 短信验证码找回密码
        • 20.2.1 PhoneCodeSetPwdForm.vue
        • 20.2.2 ResetPwd.vue密码重置页面中引入短信验证页
        • 20.2.3 获取短信验证码页面的效果
        • 20.2.4 ResetPwdForm.vue重置密码页面的实现
        • 20.2.5 设置密码的页面效果展示
        • 20.2.6 设置密码后的上一步和下一步配置
        • 20.2.7 ResetSuccess.vue
        • 20.2.8 密码设置成功后的自动跳转效果
      • 20.3 邮箱找回密码
        • 20.3.1 引入邮箱找回密码的页面(ResetPwd.vue)
        • 20.3.2 邮箱找回密码代码创建(EmailCodeSetPwdForm.vue)
        • 20.3.3 密码设置成功页的页面优化(ResetSuccess.vue)
        • 20.3.4 邮箱找回密码功能页面效果演示
      • 20.4 项目下载地址



20. 找回密码功能实现

实现找回密码功能,此功能只在使用账号密码登录时可用,故在账号密码登录的页面中添加一个忘记密码的按钮即可

20.1 找回密码的布局开发

先对找回密码功能的页面进行布局
在src/views包中创建resetPassword包,在该包中创建ResetPwd.vue组件以及component包
以下为找回密码页面ResetPwd.vue的代码

20.1.1 ResetPwd.vue

重置密码页面
src/views/resetPassword/
ResetPwd.vue

<script setup lang="ts">

import {
    ref, onMounted } from 'vue'

import EmailCodeSetPwdForm from './components/EmailCodeSetPwdForm.vue';
import PhoneCodeSetPwdForm from './components/PhoneCodeSetPwdForm.vue';

const curTab = ref(1);

const changeTab = (tab:any)=>{
   
    curTab.value = tab;
}




// const bgColor = "linear-gradient(45deg, #5198d3, #0f9fe2, #61a2d6);";
    // 样式变量
const setPwdPagePanelWidth = '40%';
const setPwdPagePanelHeight = '30%';

</script>

<template>
    重置密码

    <div class="resetpwd-page">
        <div class="resetpwd-box">
            <div class="tabs">
                <div class="tab-item" :class="{'tab-item-selected':curTab==1}" @click="changeTab(1)">手机验证码找回密码</div>
                <div class="tab-item" :class="{'tab-item-selected':curTab!=1}" @click="changeTab(2)">邮箱验证码找回密码</div>
            </div>
            <div class="tab-content">
                <!-- <PhoneCodeSetPwdForm v-if="curTab == 1"></PhoneCodeSetPwdForm> -->
                <!-- <EmailCodeSetPwdForm v-else></EmailCodeSetPwdForm> -->
            </div>
        </div>

        <div class="resetpwd-footer">
            密码重置页面
        </div>
    </div>



</template>

<style scoped>

    .resetpwd-page{
   
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        /* background: v-bind(bgColor); */
        background: linear-gradient(45deg, #5198d3, #0f9fe2, #61a2d6);

        display: flex;
        /* text-align: center; */
        /* justify-items: center; */
        /* justify-content: center; */
        align-items: center;
    }

    .resetpwd-page .resetpwd-box{
   
        background-color: #fff;
        height: 50%;
        /* height: v-bind(setPwdPagePanelWidth); */
        width: 60%;
        /* width: v-bind(setPwdPagePanelHeight); */
        /* min-height: 800px; */
        margin: 0 auto;
        text-align: center;
        border-radius: 10px;
        /* box-shadow: var(--el-box-shadow); */
        box-shadow: 0 0 10px 10px #00000055;

        padding: 40px;

    }

    .resetpwd-page .resetpwd-box .tabs{
   
        /* height:20px; */
        height: 5%;
        /* width: 400px; */
        width: 40%;
        line-height: 40px;
        display: flex;
        margin: 0 auto;
    }

    .resetpwd-page .resetpwd-box .tabs .tab-item{
   
        /* width: 200px; */
        width: 50%;
        height: 40px;
        /* height: 180%; */
        text-align: center;
        background-color: #00000055;
        font-size: 16px;
        cursor: pointer;
        /* border-radius: 5px; */
    }

    .resetpwd-page .resetpwd-box .tabs .tab-item:first-child{
   
        border-top-left-radius: 5px;
        border-bottom-left-radius: 5px;
    }
    .resetpwd-page .resetpwd-box .tabs .tab-item:last-child{
   
        border-top-right-radius: 5px;
        border-bottom-right-radius: 5px;
    }

    .resetpwd-page .resetpwd-box .tabs .tab-item:hover,
    .resetpwd-page .resetpwd-box .tabs .tab-item-selected{
   
        color: white;
        background-color: #0ba4eb;
    }

    .resetpwd-page .resetpwd-box .tab-content{
   
        /* padding-top: 60px; */
        padding-top: 5%;
        /* display: flex=1; */
        /* justify-content: center; */
        /* padding-left: 10%; */
        /* padding: 10%; */
        /* padding: 50px; */
        /* align-items: center; */
        /* text-align: center; */
        /* height: 400px; */
        height: 100%;
        /* width: 600px; */
        width: 100%;
    }

    .resetpwd-page .resetpwd-footer{
   
        position: fixed;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 10%;
        text-align: center;
        color: white;
        font-size: 15px;
        
    }



</style>
20.1.2 router/index.ts

在路由中引入新建的页面,然后才能全局跳转

  {
   
    path: '/ResetPwd',
    component: ResetPwd
  }

完整代码如下
src/router/
index.ts


import {
    createRouter, createWebHistory } from 'vue-router'

// 自定义路由组件或从其他文件导入,这里选择从其他文件导入
import UserLogin from "../views/login/UserLogin.vue";
// import UserLogin from '@/views/login/UserLogin.vue';
import HomeIndex from '@/views/index/HomeIndex.vue';
import ResetPwd from '@/views/resetPassword/ResetPwd.vue';
// import ResetPwd from '../views/resetPassword/ResetPwd.vue';

// 定义一些路由,每个路由都需要映射到一个组件,
const routes = [
  {
   
    path: '/',
    // component: UserLogin
    redirect: "/HomeIndex"
  },
  {
   
    path: '/UserLogin',
    component: UserLogin
  },
  {
   
    path: '/HomeIndex',
    component: HomeIndex
  },
  {
   
    path: '/ResetPwd',
    component: ResetPwd
  }
]

// 创建路由实例并传递'routes'配置 你可以在这里输入更多的配置
const router = createRouter({
   
  history: createWebHistory(),
  // routes:routes可以简写成routes,不会报错
  // routes:[]
  routes
})

export default router

20.1.3 UsernameForm.vue

在账号密码登录的页面中添加忘记密码的按钮,点击可跳转到重置密码页面

                    <div class="flexItem" >
                        <!-- <el-form-item prop="savePassword"> -->
                            <router-link to="/ResetPwd">忘记密码</router-link>
                        <!-- </el-form-item> -->
                    </div>

完整代码如下
src/views/login/components/
UsernameForm.vue

<script setup lang="ts">

import {
    ref,reactive, onMounted } from 'vue'
// 引入状态存储store
import {
    useStore } from 'vuex'
// 引入路由工具router
import {
    useRouter } from 'vue-router'


// 引入工具方法
import utils from '../../../utils/utils'
import api from '../../../api/api'


    // 登录表单的实例
    // let loginFormRef = ref(null);
    let loginFormRef = ref();
    // 登录表单的数据
    const loginForm = reactive({
   
        // 用户名
        username: '',
        // 密码
        password: '',
        // 图片验证
        imgcode: '',
        // 记住用户名,默认否
        saveUsername: false,
        // 记住用户名,默认否
        savePassword: false
    });

    // 登录验证规则
    const rules = ({
   
        username:[{
   
            required: true,
            message: '请输入用户名',
            trigger: 'blur'
        }],
        password:[{
   
            required: true,
            message: '请输入密码',
            trigger: 'blur'
        }],
        imgcode:[{
   
            required: true,
            message: '请输入图片验证码',
            trigger: 'blur'
        }]
    });

    const formSize = "";

    // 图片验证码路径
    let imgCodeSrc = new URL("../../../assets/code.png", import.meta.url).href;
    // const imgCodeSrc = '../../../assets/code.png';

    
    // 刷新图片验证码
    const getImgCode = () => {
   
        // 后续改为从服务器上获取动态图片
        imgCodeSrc = new URL("../../../assets/code.png", import.meta.url).href;
    };

    // 全局状态存储
    const store = useStore();
    // 路由调用
    const router = useRouter();

    // 登录提交事件
    const onSubmit = () => {
   
        // form表单中的值,校验
        loginFormRef.value.validate((valid:string, fileds:any)=>{
   
            // 如果valid值为假,则遍历输出报错
            if(!valid){
   
                for(let key in fileds){
   
                    // 获取报错信息中的字段对应的key的索引为0的信息
                    utils.showError(fileds[key][0].message);
                }
                return;
            }
            // 登录表单的记住用户名如果被勾选
            if(loginForm.saveUsername){
   
                // 保存输入的用户名
                utils.saveData('username', loginForm.username);
                // 保存被勾选的操作
                utils.saveData('saveUsername', loginForm.saveUsername);
            }else{
   
                // 如果记住用户名的勾选取消,则移除这两个存储的内容
                utils.removeData('username');
                utils.removeData('saveUsername');
            }

            // 登录表单的记住用户名如果被勾选
            if(loginForm.savePassword){
   
                // 保存输入的用户名
                utils.saveData('password', loginForm.password);
                // 保存被勾选的操作
                utils.saveData('savePassword', loginForm.savePassword);
            }else{
   
                // 如果记住用户名的勾选取消,则移除这两个存储的内容
                utils.removeData('password');
                utils.removeData('savePassword');
            }

            // TODO 调用接口登录
            utils.showLoadding("正在加载中");
            api({
   
                method: 'get',
                url: '/login/login',
                params: {
   
                    username: loginForm.username,
                    password: loginForm.password
                }
            }).then((res)=>{
   
                utils.hideLoadding();
                if(res.status != 200 || res.data.result != 200){
   
                    utils.showError("登录失败-请求数据返回有误");
                    return;
                }

                if(res.data.login == 1){
   
                    utils.showSuccess("登录成功");
                    // 存储用户信息
                    // let userInfoLogin = res.data.login;
                    let userInfoLogin = res.data;
                    let token = res.data.token;
                    console.log("usernamelogin:", token);
                    store.commit('setUserInfo', userInfoLogin);
                    store.commit('setToken', token);
                    console.log("----------------token: ", token);
                    // 登录成功后跳转主页
                    router.push('/HomeIndex');
                }else if(res.data.login == 0){
   
                    utils.showError("登录失败-用户不存在");
                    return;
                }else if(res.data.login == 2){
   
                    utils.showError("登录失败-密码错误");
                    return;
                }
                

                // utils.showError("登录失败-返回数据错误")

            }).catch((error)=>{
   
                console.log(error);
                utils.showError("登录失败-发生异常");
            });

            // 登录成功提示
            // utils.showSuccess("登录成功");
        });
    };

    // 挂载
    onMounted(() => {
   
        // 获取记住用户名的值
        loginForm.saveUsername = utils.getData('saveUsername');
        // 如果记住用户名被勾选,则获取用户名显示
        if(loginForm.saveUsername==true){
   
            loginForm.username = utils.getData('username');
        }

        // 获取记住密码的值
        loginForm.savePassword = utils.getData('savePassword');
        // 如果记住密码被勾选,则获取密码
        if(loginForm.savePassword==true){
   
            loginForm.password = utils.getData('password');
        }
    });

</script>

<template>
    <!-- 用户密码登录 -->
    <div class="usernameLoginBox">
        <el-form 
            ref="loginFormRef"
            style="max-width: 600px"
            :model="loginForm"
            :rules="rules"
            label-width="0"
            class="loginFrom"
            :size="formSize" status-icon>
            <!-- 用户名 -->
            <el-form-item prop="username">
                <!-- 图标设置,动态绑定username,提示信息,设置输入框大小 -->
                <el-input prefix-icon="UserFilled" v-model="loginForm.username" placeholder="请输入用户名" size='large' />
            </el-form-item>
            <!-- 密码 -->
            <el-form-item prop="password">
                <!-- 密码 -->
                <div class="flexItem" >
                    <!-- show-password 属性表示是否显示切换显示密码的图标,true为显示 -->
                    <!-- <el-input prefix-icon="Lock" show-password="off" type="password" v-model="loginForm.password" placeholder="请输入密码" size='large' /> -->
                    <el-input prefix-icon="Lock" show-password type="password" v-model="loginForm.password" placeholder="请输入密码" size='large' />
                </div>
            </el-form-item>
            <!-- 图片验证 -->
            <el-form-item prop="imgcode">
                <!-- 使用两个div块来左右布局验证码输入和获取验证码按钮的实现 -->
                <div class="flex loginLine">
                    <div class="flexItem" >
                        <el-input prefix-icon="Picture" v-model="loginForm.imgcode" placeholder="请输入图片验证码" size='large' />
                        <!-- <el-input prefix-icon="Iphone" v-model="loginForm.smscode" placeholder="请输入验证码" size='large' /> -->
                    </div>
                    <div class="codeBtn" >
                        <el-image  :src="imgCodeSrc" size='large' @click="getImgCode" ></el-image>
                        <!-- <el-button type="primary" size="large" @click="getSmsCode" class="" >获取验证码</el-button> -->
                    </div>
                </div>
            </el-form-item>

            <!-- <el-form-item prop="saveUsername"> -->
            <el-form-item >
                <!-- 记住账号密码的勾选 -->
                <div class="flex loginLine" >
                    <!-- 记住用户名 -->
                    <div class="flexItem" >
                        <el-form-item prop="saveUsername">
                            <el-checkbox v-model="loginForm.saveUsername">记住用户名</el-checkbox>
                        </el-form-item>
                    </div>
                    <!-- 记住密码 -->
                    <div class="flexItem" >
                        <el-form-item prop="savePassword">
                            <el-checkbox v-model="loginForm.savePassword">记住密码</el-checkbox>
                        </el-form-item>
                    </div>
                    <div class="flexItem" >
                        <!-- <el-form-item prop="savePassword"> -->
                            <router-link to="/ResetPwd">忘记密码</router-link>
                        <!-- </el-form-item> -->
                    </div>
                </div>
            </el-form-item>
            <!-- <el-form-item prop="savePassword"> -->
            <!-- </el-form-item> -->

            <!-- 登录按钮 -->
            <el-form-item>
                <el-button class="loginBtn" type="danger" size='large' @click="onSubmit">登录</el-button>
            </el-form-item>

        </el-form>
    </div>

</template>

<style scoped>

    /* 按钮宽度设为最大 */
    .loginBtn{
   
        width: 100%;
        /* 登录按钮圆角边框 */
        border-radius: 20px;
    }

    /* 验证码按钮样式配置 */
    .codeBtn{
   
        width: 100px;
        margin-left: 10px;
    }
    /* 按钮和图片宽度100px */
    .codeBtn:deep(.el-button),
    .codeBtn:deep(img){
   
        width: 100px;
        /* height: 40px; */

    }
    /* 验证码图片高度 */
    .codeBtn:deep(img){
   
        height: 40px;
        /* 鼠标移上去会变成手型 */
        cursor: pointer;
    }

    /* 这一行宽度占满 */
    .loginLine{
   
        width: 100%
    }

</style>
20.1.4 页面效果

查看账号密码登录界面,出现忘记密码链接

点击忘记密码,跳转到密码重置界面

20.2 短信验证码找回密码

布局配置好后,进行短信验证功能的实现

20.2.1 PhoneCodeSetPwdForm.vue

使用短信验证码设置密码的页面
完整代码如下
src/views/resetPassword/components/
PhoneCodeSetPwdForm.vue

<script setup lang="ts">

// vue基础模块引入
import {
    ref, reactive, onMounted, onUnmounted } from 'vue'

// 引入状态存储工具store
import {
   useStore} from 'vuex'

// 引入工具方法
import utils from '../../../utils/utils'
import api from '../../../api/api'

// 路由引入
import {
    useRoute, useRouter } from 'vue-router';

// 引入密码重置页面组件
import ResetPwdForm from './ResetPwdForm.vue';
//import ResetSuccess from './ResetSuccess.vue';

const formSize = ""

// 步骤选择
const setStep = ref(1);

// 表单数据对象
const resetPwdForm = reactive({
   
    // 用户名
    username: '',
    // 短信验证码
    smscode: '',
    // 图片验证码
    imgcode: '',
});

// 表单验证规则
const rules = ref({
   
    username: [{
   
        required: true,
        message: '请输入用户名',
        trigger: 'blur'
    }],
    smscode: [{
   
        required: true,
        message: '请输入短信验证码',
        trigger: 'blur'
    }],
    imgcode: [{
   
        required: true,
        message: '请输入图片验证码',
        trigger: 'blur'
    }]
});

// 获取短信验证码的按钮文本值
let smsCodeBtnText = ref("获取验证码")

// 定时器
let timer:any = null;
// 短信验证码获取的间隔事件
let curTime = 0;


// 获取验证码按钮
const getSmsCode = ()=>{
   

    if(!resetPwdForm.username){
   
        utils.showError("请先输入用户名");
        return;
    }

    // 发送请求生成短信验证码
    api({
   
        method: 'post',
        url: '/login/redis/setMessageCode',
        params: {
   
            username: resetPwdForm.username
        }
    });

    // 同时进行倒计时,读秒60,结束后可重新获取
    curTime = 60
    timer = setInterval(() => {
   
        curTime--;
        // 值重新赋值
        smsCodeBtnText.value = curTime+'秒后重新获取';
        // 当及时归零时,可重新获取,并将计时器重置
        if(curTime<=0){
   
            smsCodeBtnText.value = '获取验证码'
            clearInterval(timer);
            timer = null;
        }
    },1000);

}

// 图片验证码路径
let imageCodeSrc = new URL('../../../assets/code.png',import.meta.url).href

// 刷新图片验证码
const getImgCode = () => {
   
    // 从服务器动态获取图片验证码
    imageCodeSrc = new URL('../../../assets/code.png',import.meta.url).href
}

const resetPwdFormRef = ref();

// 下一步按钮
const nextSet = () => {
   

    // 表单校验,校验所填表单中是否有值,无值则报错
    resetPwdFormRef.value.validate((valid:string, fileds:any)=>{
   
        // 如果valid值为假,则输出报错内容
        if(!valid){
   
            for(let key in fileds){
   
                utils.showError(fileds[key][0].message);
                return;
            }
            return;
        }


        // 加载效果
        utils.showLoadding("正在加载中");

        // if(resetPwdForm.smscode){
   
        //     utils.showError("验证码错误,请重新输入");
        // }

        // 校验填写的验证码是否一致
        api({
   
            method: 'get',
            url: 'login/redis/getMessageCode',
            params: {
   
                username: resetPwdForm.username
            }
        }).then((res)=>{
   
            utils.hideLoadding();
            // 如果数据返回状态码不是200或请求结果返回不是200或者请求结果返回的短信验证码为空,验证失败
            if(res.status!=200||res.data.result!=200||!res.data.msgCode){
   
                utils.showError("验证失败-请求数据返回有误");
                return;
            }
            // 如果填入的短信验证码和收到的短信验证码一致,则验证成功,跳转到修改密码界面
            if(res.data.msgCode == resetPwdForm.smscode){
   
                utils.showSuccess("验证成功");
                setStep.value = 2;
            }else{
   
                utils.showError("验证失败-验证码错误");
            }

        }).catch((error)=>{
   
            setStep.value = 2;
            utils.hideLoadding();
            console.log(error);
            utils.showError("验证失败-出现异常");
        });

    });





}

// const systemToken = ref('');

// const toPhoneCodeSetPwdForm = ()=>{
   
//     setStep.value = 1;
// }

// const toResetSuccess = ()=>{
   
//     setStep.value = 3;
// }

</script>

<template>

    <!-- <div class="stepLine"> -->
        <el-steps style="max-width: 2000px" :active="setStep" align-center>
            <el-step title="身份验证" description="请输入账号和验证码进行身份确认" />
            <el-step title="密码重置" description="填写新密码并确认" />
            <el-step title="重置成功" description="密码修改成功" />
        </el-steps>

    <!-- </div> -->

    <div v-if="setStep == 1" class="phone-reset-password-form">
        <el-form 
            ref="resetPwdFormRef"
            style="max-width: 600px;"
            :model="resetPwdForm"
            :rules="rules"
            label-width="0"
            class="resetPwdForm"
            :size="formSize"
            status-icon
        >
            <!-- 用户名 -->
            <el-form-item prop="username">
                <el-input 
                    prefix-icon="UserFilled"
                    v-model="resetPwdForm.username"
                    placeholder="用户名"
                    size="large"
                />
            </el-form-item>
            <!-- 获取验证码 -->
            <el-form-item prop="smscode">
                <div class="flex resetLine">
                    <div class="flexItem">
                        <el-input
                        prefix-icon="Iphone"
                        v-model="resetPwdForm.smscode"
                        placeholder="短信验证码"
                        size="large"
                        />
                    </div>

                    <div class="codeBtn">
                        <el-button type="primary" size="large" @click="getSmsCode" :disabled="curTime>0">
                            {
   {
    smsCodeBtnText }}
                        </el-button>
                    </div>
                </div>
            </el-form-item>
            <!-- 图片验证码 -->
            <el-form-item prop="imgcode">
                <div class="flex resetLine">
                    <div class="flexItem">
                        <el-input
                            prefix-icon="Picture"
                            v-model="resetPwdForm.imgcode"
                            placeholder="图片验证码"
                            size="large"
                        />
                    </div>
                    <div class="codeBtn">
                        <el-image :src="imageCodeSrc" size="large" @click="getImgCode"></el-image>
                    </div>
                </div>
            </el-form-item>
            <!-- 下一步按钮 -->
            <el-form-item>
                <!-- <div class="nextBtn"> -->
                    <el-button class="nextBtn" type="primary" size="large" @click="nextSet">下一步</el-button>
                <!-- </div> -->
            </el-form-item>
        </el-form>

    </div>

    <!-- <ResetPwdForm v-else-if="setStep == 2" 
        :systemToken="systemToken" 
        @pre="toPhoneCodeSetPwdForm"
        @next="toResetSuccess"
    ></ResetPwdForm> -->
    <ResetPwdForm v-else-if="setStep == 2" ></ResetPwdForm>
    <!-- <ResetSuccess v-else></ResetSuccess> -->
</template>

<style scoped>


    .phone-reset-password-form{
   
        padding: 5%;
        /* margin-top: 2%; */
        margin: 2% auto;
        width: 60%;
        height: 40%;
        /* text-align: center; */
        background-color: rgb(204, 240, 210);
        display: flex;
        justify-content: center;
        /* justify-items: center; */
        
        

    }


    .codeBtn{
   
        /* width: 100px; */
        margin-left: 10px;
        /* margin-bottom: 10px; */
    }

    /* .codeBtn:deep(.el-button),
    .codeBtn:deep(img){
        width: 100px;
    } */

    .codeBtn:deep(img){
   
        height: 40px;
        cursor: pointer;
    }

    .resetLine{
   
        width: 100%;
    }

    .nextBtn{
   
        width: 100%;
        /* display: flex; */
        /* justify-content: flex-end; */
        /* justify-items: right; */
        /* text-align: right; */
        /* float: right; */
        
    }

</style>
20.2.2 ResetPwd.vue密码重置页面中引入短信验证页

在ResetPwd.vue中引入PhoneCodeSetPwdForm.vue,在选择手机验证码重置密码时显示短信验证页面
完整代码
src/views/resetPassword/
ResetPwd.vue

<script setup lang="ts">

import {
    ref, reactive, onMounted } from 'vue'

import utils from '@/utils/utils';


    // 表单对象
    const resetPwdForm = reactive({
   
        newPassword: '',
        reNewPassword: '',
    });


    // 检测两次密码是否一致
    // const validatePassword = (rule, value, callback)=>{
   
    const validatePassword = (rule:any, value:any, callback:any)=>{
   
        if(value != resetPwdForm.newPassword){
   
            callback(new Error('两次密码不一致'));
        }else{
   
            callback();
        }
    };

    // 表单输入校验
    const rules = reactive({
   
        newPassword: [
            {
   
                required: true,
                message: '请输入新密码',
                trigger: 'blur'
            },
            {
   
                // 定义输入内容长度
                min: 6,
                max: 12,
                message: '密码长度6-12位',
                trigger: 'blur'
            }
        ],
        reNewPassword: [
            {
   
                required: true,
                message: '请再次输入新密码',
                trigger: 'blur'
            },
            {
   
                // 调用校验函数进行校验
                validator: validatePassword,
                trigger: 'blur'
            }
        ]

    });

    // 表单校验对象
    const resetPwdFormRef = ref();

    // 当前页
    let setStep = ref(2);

    // 自定义事件
    // const emits = defineEmits([
    //     'pre',
    //     'next'
    // ]);

    // 上一步
    // const preSet = () =>{
   
    //     emits('pre');
    // };


    // 外部参数获取,传递上一步获取的用于账号验证的token信息
    // const option = defineProps({
   
    //     systemToken: {
   
    //         type: String,
    //         required: true
    //     }
    // });

    // 下一步
    const nextSet = () =>{
   

        resetPwdFormRef.value.validate((valid:string, fields:any)=>{
   
            if(!valid){
   
                for(let key in fields){
   
                    utils.showError(fields[key][0].message);
                }
                return;
            }

            // 调用接口验证密码和用户,进行密码修改

            // emits('next');

            // if(resetPwdForm.newPassword == resetPwdForm.reNewPassword){
   
            //     // 调用接口进行修改
            //     utils.showSuccess("密码设置成功");
                
            //     setStep.value = 3
            // }else{
   
            //     utils.showError("密码不一致,请重新输入");
            // }

        });

        
    };


</script>

<template>

    <div v-if="setStep == 2" class="resetPwdForm">

        <el-form ref="resetPwdFormRef"
            style="max-width: 600px; "
            :model="resetPwdForm"
            :rules="rules"
            label-width="auto"
            class="resetPwdForm"
            status-icon
            >

            <!-- 新密码 -->
            <el-form-item label="新的密码:" prop="newPassword">
                <el-input 
                    prefix-icon="Key"
                    type="password"
                    show-password
                    v-model="resetPwdForm.newPassword"
                    placeholder="请输入新密码"
                    size="large"
                />
            </el-form-item>
            <!-- 确认新密码 -->
            <el-form-item label="确认密码:" prop="reNewPassword">
                <el-input
                    prefix-icon="Key"
                    type="password"
                    show-password
                    v-model="resetPwdForm.reNewPassword"
                    placeholder="请再次输入新密码"
                    size="large"
                />
            </el-form-item>
            <el-form-item>
                <div class="flex preNextLine">
                    <div class="flexItem">
                        <!-- 上一步按钮 -->
                        <el-button class="resetBtn" type="primary" size="large" @click="preSet">上一步</el-button>
                    </div>

                    <div class="middleBtn">

                    </div>
                    <div class="flexItem">
                        <!-- 下一步按钮 -->
                        <el-button class="resetBtn" type="primary" size="large" @click="nextSet">下一步</el-button>
                    </div>
                </div>
            </el-form-item>
        </el-form>
    </div>

    <!-- <PhoneCodeSetPwdForm v-else-if="setStep == 1"></PhoneCodeSetPwdForm> -->
    <!-- <ResetSuccess v-else></ResetSuccess> -->

</template>

<style scoped>

    .resetPwdForm{
   
        height: 50%;
        width: 60%;
        padding-top: 1%;
        margin: 2% auto;
        justify-content: center;
        background-color: rgb(178, 189, 185)
    }

    .resetPwdForm .preNextLine{
   
        width: 100%;
        margin-top: 10%;
    }

    .resetPwdForm .preNextLine .resetBtn{
   
        width: 80%;
    }

    .resetPwdForm .preNextLine .middleBtn{
   
        width: 30%;
    }

</style>
20.2.3 获取短信验证码页面的效果

此时页面如下,共两部分,进度提示和下方的信息验证

使用用户名和验证码验证身份,用户名是后台数据库中存储的账号名,我这里是libai,短信验证是通过点击按钮调用后台接口生成到redis中的临时六位数字,图片验证码可填任意字符,然后点击下一步

20.2.4 ResetPwdForm.vue重置密码页面的实现

该页面是在身份校验通过后跳转到的设置密码界面,输入密码后调用接口进行修改表中账号对应的密码
src/views/resetPassword/components/
ResetPwdForm.vue

<script setup lang="ts">

import {
    ref, reactive, onMounted } from 'vue'

import utils from '@/utils/utils';


    // 表单对象
    const resetPwdForm = reactive({
   
        newPassword: '',
        reNewPassword: '',
    });


    // 检测两次密码是否一致
    // const validatePassword = (rule, value, callback)=>{
   
    const validatePassword = (rule:any, value:any, callback:any)=>{
   
        if(value != resetPwdForm.newPassword){
   
            callback(new Error('两次密码不一致'));
        }else{
   
            callback();
        }
    };

    // 表单输入校验
    const rules = reactive({
   
        newPassword: [
            {
   
                required: true,
                message: '请输入新密码',
                trigger: 'blur'
            },
            {
   
                // 定义输入内容长度
                min: 6,
                max: 12,
                message: '密码长度6-12位',
                trigger: 'blur'
            }
        ],
        reNewPassword: [
            {
   
                required: true,
                message: '请再次输入新密码',
                trigger: 'blur'
            },
            {
   
                // 调用校验函数进行校验
                validator: validatePassword,
                trigger: 'blur'
            }
        ]

    });

    // 表单校验对象
    const resetPwdFormRef = ref();

    // 当前页
    let setStep = ref(2);

    // 自定义事件
    // const emits = defineEmits([
    //     'pre',
    //     'next'
    // ]);

    // 上一步
    // const preSet = () =>{
   
    //     emits('pre');
    // };


    // 外部参数获取,传递上一步获取的用于账号验证的token信息
    // const option = defineProps({
   
    //     systemToken: {
   
    //         type: String,
    //         required: true
    //     }
    // });

    // 下一步
    const nextSet = () =>{
   

        resetPwdFormRef.value.validate((valid:string, fields:any)=>{
   
            if(!valid){
   
                for(let key in fields){
   
                    utils.showError(fields[key][0].message);
                }
                return;
            }

            // 调用接口验证密码和用户,进行密码修改

            // emits('next');

            // if(resetPwdForm.newPassword == resetPwdForm.reNewPassword){
   
            //     // 调用接口进行修改
            //     utils.showSuccess("密码设置成功");
                
            //     setStep.value = 3
            // }else{
   
            //     utils.showError("密码不一致,请重新输入");
            // }

        });

        
    };


</script>

<template>

    <div v-if="setStep == 2" class="resetPwdForm">

        <el-form ref="resetPwdFormRef"
            style="max-width: 600px; "
            :model="resetPwdForm"
            :rules="rules"
            label-width="auto"
            class="resetPwdForm"
            status-icon
            >

            <!-- 新密码 -->
            <el-form-item label="新的密码:" prop="newPassword">
                <el-input 
                    prefix-icon="Key"
                    type="password"
                    show-password
                    v-model="resetPwdForm.newPassword"
                    placeholder="请输入新密码"
                    size="large"
                />
            </el-form-item>
            <!-- 确认新密码 -->
            <el-form-item label="确认密码:" prop="reNewPassword">
                <el-input
                    prefix-icon="Key"
                    type="password"
                    show-password
                    v-model="resetPwdForm.reNewPassword"
                    placeholder="请再次输入新密码"
                    size="large"
                />
            </el-form-item>
            <el-form-item>
                <div class="flex preNextLine">
                    <div class="flexItem">
                        <!-- 上一步按钮 -->
                        <el-button class="resetBtn" type="primary" size="large" @click="preSet">上一步</el-button>
                    </div>

                    <div class="middleBtn">

                    </div>
                    <div class="flexItem">
                        <!-- 下一步按钮 -->
                        <el-button class="resetBtn" type="primary" size="large" @click="nextSet">下一步</el-button>
                    </div>
                </div>
            </el-form-item>
        </el-form>
    </div>

    <!-- <PhoneCodeSetPwdForm v-else-if="setStep == 1"></PhoneCodeSetPwdForm> -->
    <!-- <ResetSuccess v-else></ResetSuccess> -->

</template>

<style scoped>

    .resetPwdForm{
   
        height: 50%;
        width: 60%;
        padding-top: 1%;
        margin: 2% auto;
        justify-content: center;
        background-color: rgb(178, 189, 185)
    }

    .resetPwdForm .preNextLine{
   
        width: 100%;
        margin-top: 10%;
    }

    .resetPwdForm .preNextLine .resetBtn{
   
        width: 80%;
    }

    .resetPwdForm .preNextLine .middleBtn{
   
        width: 30%;
    }

</style>

此刻同时先把设置成功后的界面也创建出来,
src/views/resetPassword/components/
ResetSuccess.vue

<script setup lang="ts">
</script>

<template>
重置密码成功
</template>

<style scoped>

</style>
20.2.5 设置密码的页面效果展示

身份验证通过后进入输入新密码的页面,如下

此时该页面的上一步和下一步还没生效,需要将上一步、下一步对应的页面和当前页面绑定

20.2.6 设置密码后的上一步和下一步配置

上一步和下一步需要在代码中使用defineEmits()进行事件传递,还有如果使用了token进行上一步和下一步的参数传递,会用到defineProps()

设置密码页面的代码新增如下
src/views/resetPassword/components/
ResetPwdForm.vue

<script setup lang="ts">

import {
    ref, reactive } from 'vue'

import utils from '@/utils/utils';
import api from '@/api/api';


    // 表单对象
    const resetPwdForm = reactive({
   
        newPassword: '',
        reNewPassword: '',
    });


    // 检测两次密码是否一致
    // const validatePassword = (rule, value, callback)=>{
   
    const validatePassword = (rule:any, value:any, callback:any)=>{
   
        if(value != resetPwdForm.newPassword){
   
            callback(new Error('两次密码不一致'));
        }else{
   
            callback();
        }
    };

    // 表单输入校验
    const rules = reactive({
   
        newPassword: [
            {
   
                required: true,
                message: '请输入新密码',
                trigger: 'blur'
            },
            {
   
                // 定义输入内容长度
                min: 6,
                max: 12,
                message: '密码长度6-12位',
                trigger: 'blur'
            }
        ],
        reNewPassword: [
            {
   
                required: true,
                message: '请再次输入新密码',
                trigger: 'blur'
            },
            {
   
                // 调用校验函数进行校验
                validator: validatePassword,
                trigger: 'blur'
            }
        ]

    });

    // 表单校验对象
    const resetPwdFormRef = ref();

    // 当前页
    //let setStep = ref(2);

    // 自定义事件
    const emits = defineEmits([
        'pre',
        'next'
    ]);

    // 上一步
    const preSet = () =>{
   
        emits('pre');
    };


    // 外部参数获取,传递上一步获取的用于账号验证的token信息
    const option = defineProps({
   
        // 从上一步的组件中获取参数
        usernames: String,
        systemToken: {
   
            type: String,
            required: true
        }
    });

    const username = option.usernames;



    // 下一步
    const nextSet = () =>{
   

        resetPwdFormRef.value.validate((valid:string, fields:any)=>{
   
            if(!valid){
   
                for(let key in fields){
   
                    utils.showError(fields[key][0].message);
                }
                return;
            }

            utils.showLoadding("正在加载");

            // 调用接口验证密码和用户,进行密码修改
            api({
   
                method: 'post',
                url: '/user/changePassword',
                params: {
   
                    username: username,
                    password: resetPwdForm.newPassword
                }
            }).then((res)=>{
   
                utils.hideLoadding();
                if( res.status!=200){
   
                    utils.showError("请求失败-请求返回有误");
                    return;
                }
                if(res.data.result==200){
   
                    utils.showSuccess("密码修改成功");
                    emits('next');

                }
            }).catch((error)=>{
   
                utils.hideLoadding();
                console.log(error);
                utils.showError("密码修改失败-出现异常");
            });

            // emits('next');


        });

        
    };


</script>

<template>

    <div class="resetPwdForm">

        <el-form ref="resetPwdFormRef"
            style="max-width: 600px; "
            :model="resetPwdForm"
            :rules="rules"
            label-width="auto"
            class="resetPwdForm"
            status-icon
            >

            <!-- 新密码 -->
            <el-form-item label="新的密码:" prop="newPassword">
                <el-input 
                    prefix-icon="Key"
                    type="password"
                    show-password
                    v-model="resetPwdForm.newPassword"
                    placeholder="请输入新密码"
                    size="large"
                />
            </el-form-item>
            <!-- 确认新密码 -->
            <el-form-item label="确认密码:" prop="reNewPassword">
                <el-input
                    prefix-icon="Key"
                    type="password"
                    show-password
                    v-model="resetPwdForm.reNewPassword"
                    placeholder="请再次输入新密码"
                    size="large"
                />
            </el-form-item>
            <el-form-item>
                <div class="flex preNextLine">
                    <div class="flexItem">
                        <!-- 上一步按钮 -->
                        <el-button class="resetBtn" type="primary" size="large" @click="preSet">上一步</el-button>
                    </div>

                    <div class="middleBtn">

                    </div>
                    <div class="flexItem">
                        <!-- 下一步按钮 -->
                        <el-button class="resetBtn" type="primary" size="large" @click="nextSet">下一步</el-button>
                    </div>
                </div>
            </el-form-item>
        </el-form>
    </div>

</template>

<style scoped>

    .resetPwdForm{
   
        height: 50%;
        width: 60%;
        padding-top: 1%;
        margin: 2% auto;
        justify-content: center;
        background-color: rgb(178, 189, 185)
    }

    .resetPwdForm .preNextLine{
   
        width: 100%;
        margin-top: 10%;
    }

    .resetPwdForm .preNextLine .resetBtn{
   
        width: 80%;
    }

    .resetPwdForm .preNextLine .middleBtn{
   
        width: 30%;
    }

</style>

手机验证码修改密码的页面的代码新增如下
src/views/resetPassword/components/
PhoneCodeSetPwdForm.vue

<script setup lang="ts">

// vue基础模块引入
import {
    ref, reactive, onMounted, onUnmounted } from 'vue'

// 引入状态存储工具store
// import {useStore} from 'vuex'

// 引入工具方法
import utils from '../../../utils/utils'
import api from '../../../api/api'

// 路由引入
// import { useRoute, useRouter } from 'vue-router';

// 引入密码重置页面组件
import ResetPwdForm from './ResetPwdForm.vue';
import ResetSuccess from './ResetSuccess.vue';

const formSize = ""

// 步骤选择
const setStep = ref(1);

// 表单数据对象
const resetPwdForm = reactive({
   
    // 用户名
    username: '',
    // 短信验证码
    smscode: '',
    // 图片验证码
    imgcode: '',
});

// 表单验证规则
const rules = ref({
   
    username: [{
   
        required: true,
        message: '请输入用户名',
        trigger: 'blur'
    }],
    smscode: [{
   
        required: true,
        message: '请输入短信验证码',
        trigger: 'blur'
    }],
    imgcode: [{
   
        required: true,
        message: '请输入图片验证码',
        trigger: 'blur'
    }]
});

// 获取短信验证码的按钮文本值
let smsCodeBtnText = ref("获取验证码")

// 定时器
let timer:any = null;
// 短信验证码获取的间隔事件
let curTime = 0;


// 获取验证码按钮
const getSmsCode = ()=>{
   

    if(!resetPwdForm.username){
   
        utils.showError("请先输入用户名");
        return;
    }

    // 发送请求生成短信验证码
    api({
   
        method: 'post',
        url: '/login/redis/setMessageCode',
        params: {
   
            username: resetPwdForm.username
        }
    });

    // 同时进行倒计时,读秒60,结束后可重新获取
    curTime = 60
    timer = setInterval(() => {
   
        curTime--;
        // 值重新赋值
        smsCodeBtnText.value = curTime+'秒后重新获取';
        // 当及时归零时,可重新获取,并将计时器重置
        if(curTime<=0){
   
            smsCodeBtnText.value = '获取验证码'
            clearInterval(timer);
            timer = null;
        }
    },1000);

}

// 图片验证码路径
let imageCodeSrc = new URL('../../../assets/code.png',import.meta.url).href

// 刷新图片验证码
const getImgCode = () => {
   
    // 从服务器动态获取图片验证码
    imageCodeSrc = new URL('../../../assets/code.png',import.meta.url).href
}

const resetPwdFormRef = ref();

// 下一步按钮
const nextSet = () => {
   

    // 表单校验,校验所填表单中是否有值,无值则报错
    resetPwdFormRef.value.validate((valid:string, fileds:any)=>{
   
        // 如果valid值为假,则输出报错内容
        if(!valid){
   
            for(let key in fileds){
   
                utils.showError(fileds[key][0].message);
                return;
            }
            return;
        }


        // 加载效果
        utils.showLoadding("正在加载中");

        // if(resetPwdForm.smscode){
   
        //     utils.showError("验证码错误,请重新输入");
        // }

        // 校验填写的验证码是否一致
        api({
   
            method: 'get',
            url: 'login/redis/getMessageCode',
            params: {
   
                username: resetPwdForm.username
            }
        }).then((res)=>{
   
            utils.hideLoadding();
            // 如果数据返回状态码不是200或请求结果返回不是200或者请求结果返回的短信验证码为空,验证失败
            if(res.status!=200||res.data.result!=200||!res.data.msgCode){
   
                utils.showError("验证失败-请求数据返回有误");
                return;
            }
            // 如果填入的短信验证码和收到的短信验证码一致,则验证成功,跳转到修改密码界面
            if(res.data.msgCode == resetPwdForm.smscode){
   
                utils.showSuccess("验证成功");
                setStep.value = 2;
            }else{
   
                utils.showError("验证失败-验证码错误");
            }

        }).catch((error)=>{
   
            setStep.value = 2;
            utils.hideLoadding();
            console.log(error);
            utils.showError("验证失败-出现异常");
        });

    });





}

const systemToken = ref('');
// const susername = resetPwdForm.username;

// systemToken.value = resetPwdForm.username;

const toPhoneCodeSetPwdForm = ()=>{
   
    setStep.value = 1;
}

const toResetSuccess = ()=>{
   
    setStep.value = 3;
}

</script>

<template>

    <!-- <div class="stepLine"> -->
        <el-steps style="max-width: 2000px" :active="setStep" align-center>
            <el-step title="身份验证" description="请输入账号和验证码进行身份确认" />
            <el-step title="密码重置" description="填写新密码并确认" />
            <el-step title="重置成功" description="密码修改成功" />
        </el-steps>

    <!-- </div> -->

    <div v-if="setStep == 1" class="phone-reset-password-form">
        <el-form 
            ref="resetPwdFormRef"
            style="max-width: 600px;"
            :model="resetPwdForm"
            :rules="rules"
            label-width="0"
            class="resetPwdForm"
            :size="formSize"
            status-icon
        >
            <!-- 用户名 -->
            <el-form-item prop="username">
                <el-input 
                    prefix-icon="UserFilled"
                    v-model="resetPwdForm.username"
                    placeholder="用户名"
                    size="large"
                    
                />
            </el-form-item>
            <!-- 获取验证码 -->
            <el-form-item prop="smscode">
                <div class="flex resetLine">
                    <div class="flexItem">
                        <el-input
                        prefix-icon="Iphone"
                        v-model="resetPwdForm.smscode"
                        placeholder="短信验证码"
                        size="large"
                        />
                    </div>

                    <div class="codeBtn">
                        <el-button type="primary" size="large" @click="getSmsCode" :disabled="curTime>0">
                            {
   {
    smsCodeBtnText }}
                        </el-button>
                    </div>
                </div>
            </el-form-item>
            <!-- 图片验证码 -->
            <el-form-item prop="imgcode">
                <div class="flex resetLine">
                    <div class="flexItem">
                        <el-input
                            prefix-icon="Picture"
                            v-model="resetPwdForm.imgcode"
                            placeholder="图片验证码"
                            size="large"
                        />
                    </div>
                    <div class="codeBtn">
                        <el-image :src="imageCodeSrc" size="large" @click="getImgCode"></el-image>
                    </div>
                </div>
            </el-form-item>
            <!-- 下一步按钮 -->
            <el-form-item>
                <!-- <div class="nextBtn"> -->
                    <el-button class="nextBtn" type="primary" size="large" @click="nextSet">下一步</el-button>
                <!-- </div> -->
            </el-form-item>
        </el-form>

    </div>

    <!-- usernames表示将当前数据的只绑定到usernames中,通过在别的页面中使用defineProps获取 -->
    <ResetPwdForm v-else-if="setStep == 2" 
        :systemToken="systemToken" 
        @pre="toPhoneCodeSetPwdForm"
        @next="toResetSuccess"
        :usernames = resetPwdForm.username
    ></ResetPwdForm>
    <!-- <ResetPwdForm v-else-if="setStep == 2" ></ResetPwdForm> -->
    <ResetSuccess v-else></ResetSuccess>
</template>

<style scoped>


    .phone-reset-password-form{
   
        padding: 5%;
        /* margin-top: 2%; */
        margin: 2% auto;
        width: 60%;
        height: 40%;
        background-color: rgb(204, 240, 210);
        display: flex;
        justify-content: center;
    }

    .codeBtn{
   
        margin-left: 10px;
    }

    .codeBtn:deep(img){
   
        height: 40px;
        cursor: pointer;
    }

    .resetLine{
   
        width: 100%;
    }

    .nextBtn{
   
        width: 100%;
    }

</style>

以上实现后,在设置密码页面点击上一步会跳转到身份验证界面,点击下一步会跳转到密码设置成功界面,密码设置成功的界面下面接着完善

20.2.7 ResetSuccess.vue

在设置密码后,点击下一步,跳转到密码设置成功界面
在当前成功页面中,提示五秒后自动跳转到登录页面,密码设置成功的页面代码如下
src/views/resetPassword/components/
ResetSuccess.vue

<script setup lang="ts">
import {
    onMounted } from 'vue';
import {
   useRouter} from 'vue-router'



    const router = useRouter()
    let timer:any = null;
    
    let curTime = 5;
    
    const curTimeFun = ()=>{
   
        timer = setInterval(()=>{
   
            curTime--;

            if(curTime<=0){
   
                router.push("/UserLogin")
                clearInterval(timer);
                timer = null;
            }
        },1000);

    }

    onMounted(()=>{
   
        curTimeFun();
    })


</script>

<template>
    <div class="tip">
        重置密码成功
    </div>
    

    <div class="success">
        <!-- <router-link to="/UserLogin" >点击跳转账号密码登录界面</router-link> -->
        <router-link to="/UserLogin" :curtime="curTimeFun">点击跳转账号密码登录界面,{
   {
   curTime}} 后自动跳转</router-link>
    </div>

</template>

<style scoped>

    .tip{
   
        width: 20%;
        height: 10%;
        margin: auto;
        padding: 3%;
        font-size: 20px;
        /* background-color: aqua; */
    }

    .success{
   
        width: 60%;
        height: 20%;
        margin: auto;
        padding: 4%;
        font-size: 15px;
        font-style: italic;
        /* display: flex; */
        /* justify-content: center; */
        background-color: bisque;
    }

</style>
20.2.8 密码设置成功后的自动跳转效果

当密码设置好后,下一步,密码设置成功界面,提示5秒后自动跳转到登录界面
注:这里暂时还没有实现如何将5秒倒计时实时显示,后面再想怎么解决


20.3 邮箱找回密码

根据前面手机验证码找回密码的实现,一样的方式实现邮箱找回密码的功能
这里暂时使用相同的方式模拟邮箱验证码,即使用redis,在点击获取验证码时,发送请求,生成一个六位数字,并以用户名为键,数字为值的形式存储在redis中
随后输入正确的数字,填写完整信息,点击下一步,即可通过验证

20.3.1 引入邮箱找回密码的页面(ResetPwd.vue)

在找回密码对应的页面组件中引入邮箱找回密码的组件
即在src/views/resetPassword/ResetPwd.vue中引入src/views/resetPassword/components/EmailCodeSetPwdForm.vue
此时,完整的ResetPwd.vue代码如下

<script setup lang="ts">

import {
    ref } from 'vue'

import EmailCodeSetPwdForm from './components/EmailCodeSetPwdForm.vue';
import PhoneCodeSetPwdForm from './components/PhoneCodeSetPwdForm.vue';

const curTab = ref(1);

const changeTab = (tab:any)=>{
   
    curTab.value = tab;
}




// const bgColor = "linear-gradient(45deg, #5198d3, #0f9fe2, #61a2d6);";
    // 样式变量
// const setPwdPagePanelWidth = '40%';
// const setPwdPagePanelHeight = '30%';

</script>

<template>
    重置密码

    <div class="resetpwd-page">
        <div class="resetpwd-box">
            <div class="tabs">
                <div class="tab-item" :class="{'tab-item-selected':curTab==1}" @click="changeTab(1)">手机验证码找回密码</div>
                <div class="tab-item" :class="{'tab-item-selected':curTab!=1}" @click="changeTab(2)">邮箱验证码找回密码</div>
            </div>
            <div class="tab-content">
                <PhoneCodeSetPwdForm v-if="curTab == 1"></PhoneCodeSetPwdForm>
                <EmailCodeSetPwdForm v-else></EmailCodeSetPwdForm>
            </div>
        </div>

        <div class="resetpwd-footer">
            密码重置页面
        </div>
    </div>



</template>

<style scoped>

    .resetpwd-page{
   
        min-width: 1024px;
        min-height: 900px;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        /* background: v-bind(bgColor); */
        background: linear-gradient(45deg, #5198d3, #0f9fe2, #61a2d6);

        display: flex;
        /* text-align: center; */
        /* justify-items: center; */
        /* justify-content: center; */
        align-items: center;
    }

    .resetpwd-page .resetpwd-box{
   
        background-color: #fff;
        height: 50%;
        /* height: v-bind(setPwdPagePanelWidth); */
        width: 60%;
        /* width: v-bind(setPwdPagePanelHeight); */
        /* min-height: 800px; */
        margin: 0 auto;
        text-align: center;
        border-radius: 10px;
        /* box-shadow: var(--el-box-shadow); */
        box-shadow: 0 0 10px 10px #00000055;

        padding: 40px;

    }

    .resetpwd-page .resetpwd-box .tabs{
   
        /* height:20px; */
        height: 5%;
        /* width: 400px; */
        width: 40%;
        line-height: 40px;
        display: flex;
        margin: 0 auto;
    }

    .resetpwd-page .resetpwd-box .tabs .tab-item{
   
        /* width: 200px; */
        width: 50%;
        height: 40px;
        /* height: 180%; */
        text-align: center;
        background-color: #00000055;
        font-size: 16px;
        cursor: pointer;
        /* border-radius: 5px; */
    }

    .resetpwd-page .resetpwd-box .tabs .tab-item:first-child{
   
        border-top-left-radius: 5px;
        border-bottom-left-radius: 5px;
    }
    .resetpwd-page .resetpwd-box .tabs .tab-item:last-child{
   
        border-top-right-radius: 5px;
        border-bottom-right-radius: 5px;
    }

    .resetpwd-page .resetpwd-box .tabs .tab-item:hover,
    .resetpwd-page .resetpwd-box .tabs .tab-item-selected{
   
        color: white;
        background-color: #0ba4eb;
    }

    .resetpwd-page .resetpwd-box .tab-content{
   
        /* padding-top: 60px; */
        padding-top: 5%;
        /* display: flex=1; */
        /* justify-content: center; */
        /* padding-left: 10%; */
        /* padding: 10%; */
        /* padding: 50px; */
        /* align-items: center; */
        /* text-align: center; */
        /* height: 400px; */
        height: 100%;
        /* width: 600px; */
        width: 100%;
    }

    .resetpwd-page .resetpwd-footer{
   
        position: fixed;
        bottom: 0;
        left: 0;
        width: 100%;
        height: 10%;
        text-align: center;
        color: white;
        font-size: 15px;
        
    }



</style>
20.3.2 邮箱找回密码代码创建(EmailCodeSetPwdForm.vue)

将手机验证码找回密码的代码PhoneCodeSetPwdForm.vue完整复制过来即可
然后修改一下函数和变量的名字,将短信该位邮箱就行了
完整代码如下
src/views/resetPassword/components/
EmailCodeSetPwdForm.vue

<script setup lang="ts">

// vue基础模块引入
import {
    ref, reactive } from 'vue'

// 引入状态存储工具store
// import {useStore} from 'vuex'

// 引入工具方法
import utils from '../../../utils/utils'
import api from '../../../api/api'

// 路由引入
// import { useRoute, useRouter } from 'vue-router';

// 引入密码重置页面组件
import ResetPwdForm from './ResetPwdForm.vue';
import ResetSuccess from './ResetSuccess.vue';

const formSize = ""

// 步骤选择
const setStep = ref(1);

// 表单数据对象
const resetPwdForm = reactive({
   
    // 用户名
    username: '',
    // 邮箱验证码
    emailcode: '',
    // 图片验证码
    imgcode: '',
});

// 表单验证规则
const rules = ref({
   
    username: [{
   
        required: true,
        message: '请输入用户名',
        trigger: 'blur'
    }],
    emailcode: [{
   
        required: true,
        message: '请输入邮箱验证码',
        trigger: 'blur'
    }],
    imgcode: [{
   
        required: true,
        message: '请输入图片验证码',
        trigger: 'blur'
    }]
});

// 获取短信验证码的按钮文本值
let emailcodeBtnText = ref("获取验证码")

// 定时器
let timer:any = null;
// 邮箱验证码获取的间隔事件
let curTime = 0;


// 获取验证码按钮
const getEmailCode = ()=>{
   

    if(!resetPwdForm.username){
   
        utils.showError("请先输入用户名");
        return;
    }

    // 发送请求生成邮箱验证码
    api({
   
        method: 'post',
        url: '/login/redis/setMessageCode',
        params: {
   
            username: resetPwdForm.username
        }
    });

    // 同时进行倒计时,读秒60,结束后可重新获取
    curTime = 60
    timer = setInterval(() => {
   
        curTime--;
        // 值重新赋值
        emailcodeBtnText.value = curTime+'秒后重新获取';
        // 当及时归零时,可重新获取,并将计时器重置
        if(curTime<=0){
   
            emailcodeBtnText.value = '获取验证码'
            clearInterval(timer);
            timer = null;
        }
    },1000);

}

// 图片验证码路径
let imageCodeSrc = new URL('../../../assets/code.png',import.meta.url).href

// 刷新图片验证码
const getImgCode = () => {
   
    // 从服务器动态获取图片验证码
    imageCodeSrc = new URL('../../../assets/code.png',import.meta.url).href
}

const resetPwdFormRef = ref();

// 下一步按钮
const nextSet = () => {
   

    // 表单校验,校验所填表单中是否有值,无值则报错
    resetPwdFormRef.value.validate((valid:string, fileds:any)=>{
   
        // 如果valid值为假,则输出报错内容
        if(!valid){
   
            for(let key in fileds){
   
                utils.showError(fileds[key][0].message);
                return;
            }
            return;
        }


        // 加载效果
        utils.showLoadding("正在加载中");

        // if(resetPwdForm.emailcode){
   
        //     utils.showError("验证码错误,请重新输入");
        // }

        // 校验填写的验证码是否一致
        api({
   
            method: 'get',
            url: 'login/redis/getMessageCode',
            params: {
   
                username: resetPwdForm.username
            }
        }).then((res)=>{
   
            utils.hideLoadding();
            // 如果数据返回状态码不是200或请求结果返回不是200或者请求结果返回的短信验证码为空,验证失败
            if(res.status!=200||res.data.result!=200||!res.data.msgCode){
   
                utils.showError("验证失败-请求数据返回有误");
                return;
            }
            // 如果填入的邮箱验证码和收到的短信验证码一致,则验证成功,跳转到修改密码界面
            if(res.data.msgCode == resetPwdForm.emailcode){
   
                utils.showSuccess("验证成功");
                setStep.value = 2;
            }else{
   
                utils.showError("验证失败-验证码错误");
            }

        }).catch((error)=>{
   
            setStep.value = 2;
            utils.hideLoadding();
            console.log(error);
            utils.showError("验证失败-出现异常");
        });

    });





}

const systemToken = ref('');
// const susername = resetPwdForm.username;

// systemToken.value = resetPwdForm.username;

const toPhoneCodeSetPwdForm = ()=>{
   
    setStep.value = 1;
}

const toResetSuccess = ()=>{
   
    setStep.value = 3;
}


</script>

<template>

    <!-- <div class="stepLine"> -->
        <el-steps style="max-width: 2000px" :active="setStep" align-center>
            <el-step title="身份验证" description="请输入账号和验证码进行身份确认" />
            <el-step title="密码重置" description="填写新密码并确认" />
            <el-step title="重置成功" description="密码修改成功" />
        </el-steps>

    <!-- </div> -->

    <div v-if="setStep == 1" class="phone-reset-password-form">
        <el-form 
            ref="resetPwdFormRef"
            style="max-width: 600px;"
            :model="resetPwdForm"
            :rules="rules"
            label-width="0"
            class="resetPwdForm"
            :size="formSize"
            status-icon
        >
            <!-- 用户名 -->
            <el-form-item prop="username">
                <el-input 
                    prefix-icon="UserFilled"
                    v-model="resetPwdForm.username"
                    placeholder="用户名"
                    size="large"
                    
                />
            </el-form-item>
            <!-- 获取验证码 -->
            <el-form-item prop="emailcode">
                <div class="flex resetLine">
                    <div class="flexItem">
                        <el-input
                        prefix-icon="Iphone"
                        v-model="resetPwdForm.emailcode"
                        placeholder="邮箱验证码"
                        size="large"
                        />
                    </div>

                    <div class="codeBtn">
                        <el-button type="primary" size="large" @click="getEmailCode" :disabled="curTime>0">
                            {
   {
    emailcodeBtnText }}
                        </el-button>
                    </div>
                </div>
            </el-form-item>
            <!-- 图片验证码 -->
            <el-form-item prop="imgcode">
                <div class="flex resetLine">
                    <div class="flexItem">
                        <el-input
                            prefix-icon="Picture"
                            v-model="resetPwdForm.imgcode"
                            placeholder="图片验证码"
                            size="large"
                        />
                    </div>
                    <div class="codeBtn">
                        <el-image :src="imageCodeSrc" size="large" @click="getImgCode"></el-image>
                    </div>
                </div>
            </el-form-item>
            <!-- 下一步按钮 -->
            <el-form-item>
                <!-- <div class="nextBtn"> -->
                    <el-button class="nextBtn" type="primary" size="large" @click="nextSet">下一步</el-button>
                <!-- </div> -->
            </el-form-item>
        </el-form>

    </div>

    <!-- usernames表示将当前数据的只绑定到usernames中,通过在别的页面中使用defineProps获取 -->
    <ResetPwdForm v-else-if="setStep == 2" 
        :systemToken="systemToken" 
        @pre="toPhoneCodeSetPwdForm"
        @next="toResetSuccess"
        :usernames = resetPwdForm.username
    
    ></ResetPwdForm>
    <!-- <ResetPwdForm v-else-if="setStep == 2" ></ResetPwdForm> -->
    <ResetSuccess v-else></ResetSuccess>
</template>

<style scoped>


    .phone-reset-password-form{
   
        padding: 5%;
        /* margin-top: 2%; */
        margin: 2% auto;
        width: 60%;
        height: 40%;
        background-color: rgb(204, 240, 210);
        display: flex;
        justify-content: center;
    }

    .codeBtn{
   
        margin-left: 10px;
    }

    .codeBtn:deep(img){
   
        height: 40px;
        cursor: pointer;
    }

    .resetLine{
   
        width: 100%;
    }

    .nextBtn{
   
        width: 100%;
    }

</style>
20.3.3 密码设置成功页的页面优化(ResetSuccess.vue)

对密码设置成功的页面进行优化,做了一点修改
先不自动跳转,注掉了自动跳转的代码,添加了一个图标,将点击的链接下划线剔除了
完整代码如下
src/views/resetPassword/components/
ResetSuccess.vue

<script setup lang="ts">
import {
    onMounted } from 'vue';
import {
   useRouter} from 'vue-router'



    const router = useRouter()
    let timer:any = null;
    
    let curTime = 5;
    
    const curTimeFun = ()=>{
   
        // timer = setInterval(()=>{
   
        //     curTime--;

        //     if(curTime<=0){
   
        //         router.push("/UserLogin")
        //         clearInterval(timer);
        //         timer = null;
        //     }
        // },1000);

    }

    onMounted(()=>{
   
        curTimeFun();
    })


</script>

<template>
    <div class="success-box">

        <div class="success-box-icon">
    
            <el-icon><CircleCheck /></el-icon>
    
        </div>
        
    
        <div class="success-box-link">
            <!-- <router-link to="/UserLogin" >点击跳转登录界面</router-link> -->
            <router-link to="/UserLogin" class="success-box-link-btn" :curtime="curTimeFun">点击跳转登录界面,{
   {
   curTime}} 后自动跳转</router-link>
        </div>
    </div>

</template>

<style scoped>

    /* .success-box{
        text-align: center;
    } */

    .success-box-icon{
   
        width: 20%;
        height: 10%;
        margin: auto;
        padding: 6%;
        font-size: 60px;
        color: #409eff;
    }

    /* .success-box-link{ */
        /* width: 60%; */
        /* height: 20%; */
        /* margin: auto; */
        /* padding: 1%; */
        /* font-size: 20px; */
        /* font-style: italic; */
        /* background-color: rgb(184, 230, 190); */
    /* } */

    
    .success-box-link-btn{
   
        /* width: 60%; */
        /* margin: auto; */
        padding: 1%;
        font-size: 20px;
        text-decoration: none;
        background-color: rgb(184, 230, 190);
    }

</style>
20.3.4 邮箱找回密码功能页面效果演示

忘记密码

选择邮箱找回密码

输入用户名、获取验证码,输入验证码,随便输入图片验证码,下一步

验证通过,输入新密码,下一步

密码修改成功,点击文字链接可跳转到登录界面

20.4 项目下载地址

此时的项目代码已上传,实现了目前文章中所实现的代码功能
前端项目下载地址:hslb-vue3-elementplus-admin 登录和找回密码功能实现
后端接口项目下载地址:java hslb-general-management-system 后端接口服务 登录和密码修改

注:后端接口项目需要配合mysql和数据库,可以根据需要自行实践,如已经有现成的接口可忽略这里的后端项目


感谢阅读,祝君暴富!


原文链接: https://hanshan.blog.csdn.net//article/details/141186149

标签: #ElementPlus 4 #VUE 61 #Spring Boot 173 #Mybatis 37 #redis 48
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.