前后端分离项目实战-通用管理系统搭建(前端Vue3+ElementPlus,后端Springboot+Mysql+Redis)第三篇:登录功能优化


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


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


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


仰天大笑出门去,我辈岂是蓬蒿人。
——《南陵别儿童入京》


文章目录

    • 19. 登录优化(记住用户名和密码)
      • 19.1 api/api.ts
      • 19.2 store/index.ts
      • 19.3 index/HomeIndex.vue
      • 19.4 PhoneCodeForm.vue
      • 19.5 QcodeForm.vue
      • 19.6 UsernameForm.vue
      • 19.7 App.vue
      • 19.8 页面效果展示
        • 19.8.1 使用短信验证码登录的展示
        • 19.8.2 使用账号密码登录的展示
        • 19.8.3 使用二维码登录的展示



先进行上一篇最后一部分登录问题进行优化
优化内容如下:
1.部分警告,虽然不影响项目正常使用,但控制台输出的警告看着还是比较难受的
2.取消勾选记住用户名和密码操作,用户名密码依旧显示,该问题是因为当记住用户名密码对应的值移除后,其值变为undefined,而代码中的判断在其为undefined时依旧满足条件执行,需要将其值与ture进行比较

19. 登录优化(记住用户名和密码)

部分其他代码也进行了优化,以下代码基于上一篇最后下载的项目进行改动
改动如下

19.1 api/api.ts

src/api/api.ts 注掉了一部分无用代码

// 引入axios
import axios from 'axios'
// 引入store
import store from '@/store';
import { useRouter } from 'vue-router';// 数据请求自定义配置(实例)
// console.log(import.meta.url,"------------");
const api = axios.create({// baseURL: 'https://mo_sss.blog.csdn.net.cn',// baseURL: import.meta.BaseURL,// baseURL: 'https://hanshanlibai.gms.com',baseURL: 'http://127.0.0.1:8888/',timeout: 1000,headers: {// 'X-Custom-Header': 'foobar''Content-Type': 'application/json;charset=UTF-8'}// withCredentials 表示跨域请求时是否需要使用凭证,默认是true// withCredentials: true,// responseType 表示浏览器将要响应的数据类型,包括arraybuffer、document、json、text、stream// 浏览器专属类型: blob// 默认值就是json// responseType: 'json',// responseEncoding 表示用于解码响应的编码(Node.js专属),注意,忽略responseType值为stream或者客户端请求// 默认值为utf-8// responseEncoding: 'utf-8'
});// const router = useRouter();// 添加请求拦截器
api.interceptors.request.use(function (config) {console.log("request------------------->")// 在发送请求之前做些什么// 获取缓存中的tokenconst token = store.getters.getToken;if(token){// 如果token为真值,则将其赋值赋给请求头config.headers.token = token;}return config;}, function (error) {// 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器
api.interceptors.response.use(function (response) {console.log("response-------------------> code",response.status)// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么// store.commit("logout");// router.push('/UserLogin')return response;}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么if(error.response){console.log(error.response.status)switch(error.response.status){case 401:// case 200:store.commit("logout");// router.replace({//     path: '/UserLogin',//     query: {//         redirect: router.currentRoute.value.fullPath//     }// })break;default:store.commit("logout");// router.replace({//     path: "/UserLogin",//     query: {//         redirect: router.currentRoute.value.fullPath//     }// })}}return Promise.reject(error);});export default api;

19.2 store/index.ts

src/store/index.ts 新增了登出时清除缓存数据的代码,此处为缓存校验时使用

// 引入, 用于存储全局的状态数据,可供其他地方调用
import { createStore } from "vuex";
// 引入工具方法
import utils from "@/utils/utils";// 创建一个新的store实例
const store = createStore({state() {return{// count: 0// 当前登录的用户信息userInfo: {},// 当前登录的标识tokentoken: null,}},getters: {getUserInfo(state:any){return state.userInfo;},getToken(state:any){return state.token;}},mutations: {// 登出,清除缓存中的数据logout: function(state:any){console.log("---111---")state.userInfo = null;utils.removeData("userInfo");utils.removeData("token");// utils.removeData("username");// utils.saveData("username","");// utils.removeData("saveUsername");// utils.removeData("password");// utils.removeData("savePassword");},// 存储用户信息setUserInfo: function(state:any, userInfo:any){state.userInfo = userInfo;utils.saveData('userInfo', userInfo);},// 存储tokensetToken: function(state:any, token:any){state.toekn = token;utils.saveData('token', token);}}})export default store;

19.3 index/HomeIndex.vue

src/views/index/HomeIndex.vue 未做改变,但也列出来看一下

<script setup lang="ts">import utils from '@/utils/utils';
import { onMounted } from 'vue';</script><template>后台主页
</template><style scoped>
</style>

19.4 PhoneCodeForm.vue

src/views/login/components/PhoneCodeForm.vue 对登录缓存校验进行了优化

<script setup lang="ts">
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';// 登录表单的实例
// let loginFormRef = ref(null);
let loginFormRef = ref()
// 登录表单的数据
const loginForm = reactive({// 用户名username: '',// 手机验证码smscode: '',// 图片验证imgcode: '',// 记住用户名,默认否saveUsername: false
})// 登录验证规则
const rules = {username: [{required: true,message: '请输入用户名',trigger: 'blur'}],smscode: [{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
}// 定时器
let timer: any = null
// 获取短信验证码的间隔时间
let curTime = 0
// 获取短信验证码按钮的文本显示内容
let smsCodeBtnText = ref('获取验证码')// 获取短信验证码
const getSmsCode = () => {// 当点击获取短信验证码时,如果其他信息没填则提示输入if (!loginForm.username) {utils.showError('请输入用户名')return}// if(!loginForm.smscode){//     utils.showError('请输入短信验证码');//     return;// }// TODO 从后台获取短信验证码// 调用接口生成短信验证码// 1 直接使用axios请求后端完整地址请求
//   axios({
//     method: 'post',
//     url: 'http://127.0.0.1:8888/login/redis/setMessageCode',
//     // url: 'login/redis/setMessageCode',
//     // 这里需要注意,不管请求方式是什么,这里是根据后端传参方式来定的,如果后端使用@RequestParam则这里使用params作为key
//     params: {
//       username: loginForm.username
//     }
//   });// 2 使用axios实例传参请求后端接口地址的用法  api({method: 'post',url: '/login/redis/setMessageCode',params: {username: loginForm.username}})curTime = 60timer = setInterval(() => {curTime--;smsCodeBtnText.value = curTime + '秒后重新获取';if (curTime <= 0) {smsCodeBtnText.value = '获取验证码'clearInterval(timer)// 清除时,值为空,防止重复点击触发多次timer = null}}, 1000)
}// 状态存储的store
const store = useStore();
// 路由,转到指定页面,使用push
// const route = useRoute();
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==true) {console.log("短信验证登录1:",loginForm.saveUsername);// 保存输入的用户名utils.saveData('username', loginForm.username)// 保存被勾选的操作utils.saveData('saveUsername', loginForm.saveUsername)} else {console.log("短信验证登录2:",loginForm.saveUsername);// 如果记住用户名的勾选取消,则移除这两个存储的内容utils.removeData('username')utils.removeData('saveUsername')}// TODO 调用接口登录// 因为太快了,所以可能看不到效果,可以将下方的hideLoading方法注掉,可以看到效果utils.showLoadding('正在加载中')api({method: 'get',url: '/login/redis/getMessageCode',params: {username: loginForm.username,smscode: loginForm.smscode// imgcode: loginForm.imgcode}}).then((res) => {utils.hideLoadding()console.log(res)// console.log(res.status)// if (!res || res.status != 200 || !res.data || res.data.result != 200 || !res.data.data) {if (res.status != 200 || res.data.result != 200 || !res.data.msgCode) {utils.showError('登录失败-请求数据返回有误');return;}// console.log(res.data.data, loginForm.smscode);if(res.data.msgCode == loginForm.smscode){utils.showSuccess('登陆成功')// 存储用户token信息并转到主页// let userInfo = res.data.datalet userInfo = res.datalet token = res.data.token// 状态数据存储store.commit('setUserInfo', userInfo);store.commit('setToken', token);// 登录成功后将页面转到主页router.push('/HomeIndex')}else if(res.data.msgCode != loginForm.smscode){utils.showError('登录失败-验证码错误');return;}// utils.showError('登录失败')}).catch((error) => {// utils.hideLoadding();console.log(error);utils.showError('登录失败-出现异常')})// api.post("/api/login/code",{//     username: loginForm.username,//     smscode: loginForm.smscode,//     imgcode: loginForm.imgcode// }).then((res)=>{//     utils.hideLoadding();//     console.log(res);//     console.log(res.status);//     if(!res || res.status != 200 || !res.data || res.data.code != 8888 || !res.data.data){//         if(res.data.message){//             utils.showError(res.data.message);//             return;//         }//         utils.showError('登录失败');//         return;//     }//     // 存储用户token信息并转到主页//     let userInfo = res.data.data;//     let token = res.data.token;//     utils.showSuccess('登陆成功');// }).catch((error)=>{//     // utils.hideLoadding();//     utils.showError('登录失败');// });// 登录成功信息提示// utils.showSuccess("登录成功");})
}// 挂载
onMounted(() => {// 获取记住用户名的值loginForm.saveUsername = utils.getData('saveUsername')// 如果记住用户名被勾选,则获取用户名显示(saveUsername可能会是undefined,当为undefined时也是真,故这里不能直接使用saveUsername,而是要判断是否为true)if (loginForm.saveUsername==true) {loginForm.username = utils.getData('username')}
})// 清空定时器
onUnmounted(() => {timer && clearInterval(timer)
})
</script><template><!-- 手机验证码登录 --><div class="phoneCodeLoginBox"><el-formref="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-inputprefix-icon="UserFilled"v-model="loginForm.username"placeholder="请输入用户名"size="large"/></el-form-item><!-- 短信验证 --><el-form-item prop="smscode"><!-- 使用两个div块来左右布局验证码输入和获取验证码按钮的实现 --><div class="flex loginLine"><div class="flexItem"><el-inputprefix-icon="Iphone"v-model="loginForm.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块来左右布局验证码输入和获取验证码按钮的实现 --><div class="flex loginLine"><div class="flexItem"><el-inputprefix-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-checkbox v-model="loginForm.saveUsername">记住用户名</el-checkbox></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>

19.5 QcodeForm.vue

src/views/login/components/QcodeForm.vue 对二维码生成部分进行了优化

<script setup lang="ts">import { ref,reactive, onMounted, onUnmounted } from 'vue'// 引入工具方法
import utils from '../../../utils/utils'
import api from '../../../api/api'// 引入store
import {useStore} from 'vuex'
// 引入router
import {useRouter} from 'vue-router'const store = useStore();const router = useRouter();// 二维码// let qcodePath:any = null;// 二维码对应的token, 用于判断当前二维码是否已经被扫码登录let qrToken:string = "";// 第一次获取验证码// api({//     method: 'post',//     url: 'login/qr/generateQrCodeAsFile'// }).then((res)=>{// if(res.data.result != 200){// utils.showError("登录失败");// }// utils.showSuccess("登录成功");//     qcodePath = res.data.data//     qrToken = res.data.token// });// qcodePath = 'E:\\WORKPROJECTS\\MySelfPro\\hslb-general-management-system\\src\\main\resources\\login_qr_pngs\\QRCode.png';// 二维码let qcodeSrc = new URL("../../../assets/hslb-qcode.png", import.meta.url).href;// let qcodeSrc = new URL(qcodePath, import.meta.url).href;// let qcodeSrc = qcodePath;const qcodeToken = ref('');// 当前定时器事件const curTime = ref(0);let timer:any = null;let username:string = utils.getData("username");const qrString = "100100100222";// 后台更新获取二维码const loadQcode = () => {// 后续改为从服务器上获取动态图片// const qrString = "100100100222";console.log("9999999====== "+qrString);// let username:string = utils.getData("username");api({method: 'post',url: 'login/qr/generateQrCodeAsFile',params: {username: username,qrContent: qrString}}).then((res)=>{// if(res.data.result != 200){// utils.showError("登录失败");// }// utils.showSuccess("登录成功");// qcodePath = res.data.dataqrToken = res.data.token});qcodeSrc = new URL("../../../assets/hslb-qcode.png", import.meta.url).href;// qcodeSrc = new URL(qcodePath, import.meta.url).href;// qcodeSrc = qcodePath;// 初始化token的值qcodeToken.value = qrToken;// 设定定时时间// curTime.value = 60;// 为了让二维码失效的效果及时,这里暂时设置10秒,后续改回60秒即可curTime.value = 10;// 定义定时器,倒计时timer = setInterval(() => {curTime.value--;// 这里获取toekn,校验是否已经被登陆过checkLogin();if(curTime.value<=0){// 事件为0则清空定时器clearInterval(timer);timer = null;}}, 1000);};// 登录提交事件// const onSubmit = () => {// };// 挂载onMounted(() => {// 获取二维码loadQcode();});// 清空计时器onUnmounted(()=>{timer && clearInterval(timer);});// 使用qcodeToken判断当前二维码是否已经被扫码登录const checkLogin = () => {// TODOapi({method: 'post',url: 'login/qr/generateQrCodeAsFile',params: {username: username,qrContent: qrString}}).then((res)=>{if(res.data.token){utils.showSuccess("登录成功");store.commit('setUserInfo',res.data);store.commit('setToken',res.data.token);router.push('/HomeIndex');}// res.data.token;}).catch((error)=>{console.log(error);// utils.showError("登录失败")});}</script><template><!-- 扫码登录 --><div class="qcodeLoginBox"><div class="qcodeBox" ><img class="qcodeImg" :class="{'endImg':curTime<=0}" :src="qcodeSrc" alt="无法获取二维码,请联系客服解决"><div v-if="curTime<=0" class="endBox" @click="loadQcode" >当前二维码失效,点击重新加载{{ curTime }}</div></div><div class="tipInfo" >使用微信或移动端扫码登录 此二维码将在{{ curTime }}秒后刷新</div></div></template><style scoped>/* 二维码窗口样式 */.qcodeBox{width: 80%;height: 80%;position: relative;/* 边框自动 */margin: 0 auto;   }/* 二维码图片样式 */.qcodeBox .qcodeImg{width: 100%;height: 100%;}.qcodeBox .endBox{width: 100%;height: 100%;/* 悬浮显示 */position: absolute;/* 靠左 *//* left: 0%; *//* 靠上 */top: 0;/* 居中 *//* text-align: center; *//* 字体大小 */font-size: 14px;/* 字体颜色 */color: red;display: flex;/* 上下居中 */align-items: center;/* justify-items: center; *//* 左右居中 */justify-content: center;/* 背景色为灰色 */background-color: #00000055;}/* .endImg{filter: brightness(10%);} *//* 提示信息样式 */.tipInfo{/* 行高 */line-height: 30px;/* 字体大小 */font-size: 14px;/* 居中 */text-align: center;/* 颜色 */color: var(--el-text-color-placeholder);}</style>

19.6 UsernameForm.vue

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

19.7 App.vue

src/App.vue 对登录缓存校验进行了优化

<script setup lang="ts">
import { onMounted } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import utils from './utils/utils';
import api from './api/api';// // // 引入暗黑主题的动态切换
// import { useDark, useToggle } from '@vueuse/core'// const isDark = useDark()
// // // 切换主题函数
// const toggleDark = useToggle(isDark)// 状态存储
let store = useStore();// 路由使用
const router = useRouter();onMounted(()=>{// let tt = localStorage.getItem("token");// console.log("tt: ",tt);console.log("=== ===");let token = "";// 由于token可能返回undefined报错,需要进行报错处理try {token = utils.getData("token");} catch (error) {error;}console.log("store-token",token);let userInfo = utils.getData('userInfo');console.log("userInfoL: "+userInfo)console.log("userInfoL: "+token&&userInfo)if(token && userInfo){console.log("token userInfo :",token," -- ", userInfo);// 登录成功,验证utils.showLoadding("正在加载")const username = utils.getData('username');if(!username){// 登录失败,跳转到登录页if(username===undefined){utils.saveData("username","");}// token验证失败utils.showError("用户名过期-请重新登录");router.push('/UserLogin');utils.hideLoadding();}else{console.log("username-", username);api.get('/login/tokenCheck',{params:{username}}).then((res)=>{console.log("res.data.token",res.data);utils.hideLoadding();if(res.data.token==token){// 登陆成功// store.commit('setUserInfo', userInfo);// store.commit('setToken', token);router.push('/HomeIndex');utils.showSuccess("登录成功");}else{// if(username===undefined){//   utils.saveData("username","");// }// 登录失败utils.showError("Token已过期,请重新登录");// 登录失败,跳转到登录页router.push('/UserLogin');}});utils.hideLoadding();}}else{// 登录失败,跳转到登录页utils.showError("用户登录缓存过期,请重新登录");router.push('/UserLogin');utils.hideLoadding();}});</script><template><!-- 暗黑主题动态切换按钮实现 --><!-- <button @click="toggleDark()"><i inline-block align-middle i="dark:carbon-moon carbon-sun"/><span class="ml-2">{{ isDark ? 'Dark' : 'Light' }}</span></button> --><RouterView></RouterView></template><style scoped></style>

19.8 页面效果展示

19.8.1 使用短信验证码登录的展示

登录前
在这里插入图片描述

不勾选记住用户名登录

输入用户名,点击获取验证码按钮,获取验证码后填入(redis中查看),并填写图片验证码(此项随机填写即可)不勾选记住用户名
在这里插入图片描述
在这里插入图片描述

登录成功界面
在这里插入图片描述
60秒内刷新页面如下
在这里插入图片描述

等待60秒后刷新界面(token设定的是60秒过期,故过期后需要重新登录)
在这里插入图片描述

勾选记住用户名登录

输入用户名,点击获取验证码按钮,获取验证码后填入(redis中查看),并填写图片验证码(此项随机填写即可),勾选记住用户名
在这里插入图片描述
在这里插入图片描述

登录成功界面
在这里插入图片描述
60秒内刷新页面如下
在这里插入图片描述

60秒后刷新界面
在这里插入图片描述

19.8.2 使用账号密码登录的展示

登录前
不勾选记住用户名密码登录
在这里插入图片描述

登录成功后
在这里插入图片描述
60秒后刷新页面
在这里插入图片描述

勾选记住用户名密码登录
在这里插入图片描述
登录成功
在这里插入图片描述
60秒后刷新页面
在这里插入图片描述
在这里插入图片描述

19.8.3 使用二维码登录的展示

这里没有做任何逻辑进行用户的登录,只是生成了一个token,并且当前用户名是另外两种登录方式存储的缓存,此时直接验证通过
在这里插入图片描述
登录成功
在这里插入图片描述
60秒后刷新页面会跳转到短信登录


感谢阅读,祝君暴富!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/406461.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

钓鱼的常见几种方式

钓鱼的多种方式 office钓鱼攻击 宏与宏病毒 # 宏 宏是office自带的一种高级脚本特性&#xff0c;通过VBA代码&#xff0c;可以在office中去完成某项特定的任务&#xff0c;而不必再重复相同的动作&#xff0c;目的是让用户文档中一些任务自动化# 宏病毒 宏病毒是一种寄存在文…

linux笔记1

命令格式 命令行界面的提示符解析&#xff1a; [rootlocalhost ~]# root位置&#xff1a; 登录用户名 &#xff1a; 连接符号 localhost位置: 本机的主机名 ~位置: 当前的所在位置 #位置&#xff1a; 表示是超级管理员还是普通用户 超级管…

RPA自动化流程机器人助力企业财务数字化转型

在数字经济时代&#xff0c;企业需要快速响应市场变化&#xff0c;而财务数字化转型是企业适应现代商业环境、提升竞争力的必要步骤。财务数字化转型不仅涉及企业财务能力的提升&#xff0c;推动了财务管理与决策模式的转变。RPA自动化流程机器人因其能通过自动化技术帮助企业实…

超声波水表是什么?量程比又是什么?

一、超声波水表概述 1.定义&#xff1a; 超声波水表是一种利用超声波技术来测量水流速度&#xff0c;进而计算出流经管道的水体积流量的计量设备。它通过发送和接收超声波信号的时间差来确定水流的速度&#xff0c;从而精确地计量水的流量。 2.工作原理&#xff1a; 超声波…

Android 架构模式之 MVC

目录 架构设计的目的对 MVC 的理解Android 中 MVC 的问题试吃个小李子ModelViewController 大家好&#xff01; 作为 Android 程序猿&#xff0c;MVC 应该是我们第一个接触的架构吧&#xff0c;从开始接触 Android 那一刻起&#xff0c;我们就开始接触它&#xff0c;可还记得我…

企业入驻西安国际数字媒体产业园的十大好处

在当今数字化飞速发展的时代&#xff0c;企业的发展需要依托创新的平台和资源的整合。西安国际数字影像产业园&#xff0c;作为数字产业的引领者&#xff0c;为入驻企业提供了众多独特的优势和机遇。 好处一&#xff1a;产业集聚效应。西安国际数字影像产业园汇聚了众多数字媒体…

【Unity】通用GM QA工具 运行时数值修改 命令行 测试工具

GM工具使用: GM工具通常用于游戏运行时修改数值(加钱/血量)、解锁关卡等&#xff0c;用于快速无死角测试游戏。一个通用型GM工具对于游戏项目是非常实用且必要的&#xff0c;但通用不能向易用妥协&#xff0c;纯命令行GM门槛太高&#xff0c;对QA不友好。 这类运行时命令行工具…

【蓝桥杯冲刺省一,省一看这些就够了-C++版本】蓝桥杯STL模板及相关练习题

蓝桥杯历年省赛真题 点击链接免费加入题单 STL map及其函数 map<key,value> 提供一对一的数据处理能力&#xff0c;由于这个特性&#xff0c;它完成有可能在我们处理一对一数据的时候&#xff0c;在编程上提供快速通道。map 中的第一个值称为关键字(key)&#xff0c;…

以前嗤之以鼻,现在逐字学习!缠论量化代码大公开!|邢不行

这是邢不行第 113 期量化小讲堂的分享 作者 | 邢不行、密斯锌硒 一千个人眼中有一千个哈姆雷特&#xff0c;我们只是尽可能的去量化我们理解的部分缠论的思路。 我们过往在文章中多次聊过技术指标&#xff0c;如MACD、KDJ等等&#xff0c;也聊过一些K线形态&#xff0c;如跳…

C语言 | Leetcode C语言题解之第354题俄罗斯套娃信封问题

题目&#xff1a; 题解&#xff1a; int cmp(int** a, int** b) {return (*a)[0] (*b)[0] ? (*b)[1] - (*a)[1] : (*a)[0] - (*b)[0]; }int maxEnvelopes(int** envelopes, int envelopesSize, int* envelopesColSize) {if (envelopesSize 0) {return 0;}qsort(envelopes, …

简历系统

TOC springboot0745简历系统 第1章 绪论 1.1背景及意义 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们生活水平的不断提高&#xff0c;日常生活中人们对简历系统方面的要求也在不断提高&#xff0c;需要工作的人数更是不断增加&#xff0c;使得简历系统…

论文解读:LONGWRITER: UNLEASHING 10,000+ WORD GENERATION FROM LONG CONTEXT LLMS

摘要 现象&#xff1a;当前的大预言模型可以接受超过100,000个tokens的输入&#xff0c;但是却难以生成超过2000个token的输出。 原因&#xff1a;监督微调过程(SFT)中看到的样本没有足够长的样本。 解决方法&#xff1a; Agent Write&#xff0c;可以将长任务分解为子任务&a…

Java CompletableFuture:你真的了解它吗?

文章目录 1 什么是 CompletableFuture&#xff1f;2 如何正确使用 CompletableFuture 对象&#xff1f;3 如何结合回调函数处理异步任务结果&#xff1f;4 如何组合并处理多个 CompletableFuture&#xff1f; 1 什么是 CompletableFuture&#xff1f; CompletableFuture 是 Ja…

Coze插件发布!PDF转Markdown功能便捷集成,打造你的专属智能体

近日&#xff0c;TextIn开发的PDF转Markdown插件正式上架Coze。 在扣子搜索“pdf转markdown”&#xff0c;或在Coze搜索“pdf2markdown” 即可找到插件&#xff0c;在你的专属智能体中便捷使用文档解析功能。 如果想测试解析插件在你需要的场景下表现如何&#xff0c;可以直接…

后端开发刷题 | 合并k个已排序的链表

描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围&#xff1a;节点总数 0≤n≤5000&#xff0c;每个节点的val满足 ∣val∣<1000 要求&#xff1a;时间复杂度 O(nlogn) 示例1 输入&#xff1a; [{1,2,3},{4,5,6,7}] 返回值&#xff1a; …

【数据结构】二叉树的深度理解

&#x1f36c;个人主页&#xff1a;Yanni.— &#x1f308;数据结构&#xff1a;Data Structure.​​​​​​ &#x1f382;C语言笔记&#xff1a;C Language Notes 前言 在之前学习了二叉树的基本概念&#xff0c;但二叉树有着更深入知识理解&#xff0c;这篇文章可以帮助大…

使用Obsidian实现Anki快速制卡

文章目录 前言准备双双启用遇到问题查看是什么问题解决问题 开始使用使用前的一些设置快速制卡 前言 我现在使用 Anki 的同时也使用 Obsidian&#xff0c;正好可以通过插件来让这两个十分好用的软件实现联动。 在 Obsidian 中实现 Anki 的快速制卡。 准备 首先要在这两个软…

原型制作 | 歌词与进度条的位置呼应

在之前的案例里面咱们做过了歌词滚动的效果&#xff0c;具体效果是这样的&#xff0c;点击播放按钮&#xff0c;歌词开始滚动&#xff1b;点击暂停按钮&#xff0c;歌词停止滚动&#xff0c;再次点击播放&#xff0c;歌词会继续滚动&#xff1b;一直播放直到结束&#xff0c;歌…

java常见面试题汇总

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明一、封装 继承 多态1.封装2.继承3.多态 二、什么是重载…

【前端】VUE 在线运行 模拟器 通过字符串动态渲染页面 可以灵活使用

【前端】VUE2 在线运行 模拟器 通过字符串动态渲染页面 可以灵活使用 <template><div><!-- 这里是动态组件--><component :is"component"></component><!-- 这里是动态组件--><br /><br /><br />可…