效果预览
技术要点——自适配全屏背景
https://blog.csdn.net/weixin_41192489/article/details/119992992
技术要点——密码输入框
自定义图标切换显示隐藏
https://blog.csdn.net/weixin_41192489/article/details/133940676
技术要点——记住账号(支持多账号)
核心需求和逻辑
- 勾选“记住账号”,一旦登录成功过,下次登录能在账号输入框的输入推荐建议列表中,选择该账号
- 未勾选“记住账号”,登录成功后,清除对该账号的存储
相关代码
页面加载时,获取记住的账号
mounted() { this.accountList = JSON.parse(localStorage.getItem("accountList")) || []; },
使用带输入建议的输入框
<el-autocomplete clearable class="inputStyle" v-model="formData.account" :fetch-suggestions="queryAccount" placeholder="请输入账号" @select="chooseAccount" ></el-autocomplete>
根据输入内容,从记住的账号中,过滤出最接近的已记住的账号
queryAccount(queryString, cb) { let accountList = JSON.parse(JSON.stringify(this.accountList)); accountList = accountList.map((item) => { return { value: item, }; }); var results = queryString ? accountList.filter(this.createFilter(queryString)) : accountList; cb(results); }, createFilter(queryString) { return (restaurant) => { return ( restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0 ); }; },
根据输入建议下拉选择中,选择已记住的账号时,自动勾选记住账号,并清空表单校验
chooseAccount(newAccount) { if (this.accountList.includes(newAccount.value)) { this.remember = true; } this.$nextTick(() => { this.$refs.formRef.clearValidate(); }); },
登录成功后,根据是否勾选记住账号,存入新账号或移除已记住的账号。
this.$message({ offset: 150, message: "登录成功!", type: "success", }); let account = this.formData.account; // 勾选-记住账号 if (this.remember) { // 没记住过 if (!this.accountList.includes(account)) { // 存入localStorage this.accountList.push(account); localStorage.setItem( "accountList", JSON.stringify(this.accountList) ); } } else { // 未勾选-记住账号 removeItem(this.accountList, account); localStorage.setItem( "accountList", JSON.stringify(this.accountList) ); }
用到的工具函数
// 普通数组移除指定元素 function removeItem(arr, item) { let targetIndex = arr.findIndex((itemTemp) => itemTemp === item); if (targetIndex !== -1) { arr.splice(targetIndex, 1); } }
技术要点——登录后维持登录状态
this.$store.commit("set_token", res.data.data.token); this.$store.commit("set_isLogin", true); this.$store.commit("set_userInfo", res.data.data);
完整范例代码
<template> <div class="bg loginPage"> <div> <div> <div class="hello">Hi,你好!</div> <div class="hello2">欢迎进入观光车调度系统</div> </div> <div class="logoBox"> <img class="logoBox" src="@/assets/images/login/login_logo.png" alt="" /> </div> </div> <div class="loginBox"> <div class="welcomeLogin">欢迎登录</div> <el-form ref="formRef" :model="formData" label-width="0px"> <el-form-item prop="account" :rules="{ required: true, message: '请输入账号' }" > <div class="formLabel">账号</div> <el-autocomplete clearable class="inputStyle" v-model="formData.account" :fetch-suggestions="queryAccount" placeholder="请输入账号" @select="chooseAccount" ></el-autocomplete> </el-form-item> <el-form-item prop="password" :rules="{ required: true, message: '请输入密码' }" > <div class="formLabel">密码</div> <el-input placeholder="密码" v-model="formData.password" :type="showPassword ? 'text' : 'password'" > <i slot="suffix" @click="switchPassword"> <img v-if="showPassword" class="input_icon" src="@/assets/icons/password_show.png" /> <img v-else class="input_icon" src="@/assets/icons/password_hide.png" /> </i> </el-input> </el-form-item> </el-form> <el-checkbox v-model="remember">记住账号</el-checkbox> <el-button class="loginBtn" type="primary" @click="login" >立即登录</el-button > </div> </div> </template> <script> import { api_login } from "@/APIs/login.js"; export default { data() { return { accountList: [], remember: false, // 是否显示密码 showPassword: false, formData: {}, }; }, mounted() { this.accountList = JSON.parse(localStorage.getItem("accountList")) || []; }, methods: { chooseAccount(newAccount) { if (this.accountList.includes(newAccount.value)) { this.remember = true; } this.$nextTick(() => { this.$refs.formRef.clearValidate(); }); }, queryAccount(queryString, cb) { let accountList = JSON.parse(JSON.stringify(this.accountList)); accountList = accountList.map((item) => { return { value: item, }; }); var results = queryString ? accountList.filter(this.createFilter(queryString)) : accountList; cb(results); }, createFilter(queryString) { return (restaurant) => { return ( restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0 ); }; }, switchPassword() { this.showPassword = !this.showPassword; }, gotoIndex() { this.$router.push("/"); }, login() { this.$refs.formRef.validate((valid) => { if (valid) { this.loading = this.$loading({ text: "登录中", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", lock: true, }); api_login({ ...this.formData, }) .then((res) => { if (res.data.code === 200) { this.$store.commit("set_token", res.data.data.token); this.$store.commit("set_isLogin", true); delete res.data.data["token"]; this.$store.commit("set_userInfo", res.data.data); this.$message({ offset: 150, message: "登录成功!", type: "success", }); let account = this.formData.account; // 勾选-记住账号 if (this.remember) { // 没记住过 if (!this.accountList.includes(account)) { // 存入localStorage this.accountList.push(account); localStorage.setItem( "accountList", JSON.stringify(this.accountList) ); } } else { // 未勾选-记住账号 removeItem(this.accountList, account); localStorage.setItem( "accountList", JSON.stringify(this.accountList) ); } this.gotoIndex(); } else { this.$message({ offset: 150, message: res.data.msg, type: "warning", }); } this.loading.close(); }) .catch(() => { this.loading.close(); }); } }); }, }, }; // 普通数组移除指定元素 function removeItem(arr, item) { let targetIndex = arr.findIndex((itemTemp) => itemTemp === item); if (targetIndex !== -1) { arr.splice(targetIndex, 1); } } </script> <style scoped> .input_icon { cursor: pointer; width: 24px; padding-top: 8px; padding-right: 6px; } .bg { background-image: url("~@/assets/images/login/login_bg.png"); background-size: 100% 100%; position: fixed; top: 0px; width: 100%; height: 100%; } .loginBox { width: 390px; height: 492px; background: #ffffff; padding: 40px; box-sizing: border-box; } .loginPage { display: flex; justify-content: space-around; align-items: center; padding: 0px 90px; box-sizing: border-box; } .logoBox { width: 439px; height: 341px; } .hello { height: 63px; font-size: 45px; font-family: PingFangSC, PingFang SC; font-weight: 600; color: #ffffff; line-height: 63px; } .hello2 { height: 44px; font-size: 32px; font-family: PingFangSC, PingFang SC; font-weight: 600; color: #ffffff; line-height: 44px; margin-bottom: 46px; margin-top: 12px; } .welcomeLogin { height: 25px; font-size: 18px; font-family: PingFangSC, PingFang SC; font-weight: 600; color: #2b2d31; line-height: 25px; text-align: center; margin-bottom: 26px; } .formLabel { margin-top: 20px; height: 21px; font-size: 15px; font-family: PingFangSC, PingFang SC; font-weight: 400; color: #2b2d31; line-height: 21px; margin-bottom: 10px; } .loginBtn { height: 51px; background: #3e6ff6; border-radius: 6px; width: 100%; margin-top: 50px; } .inputStyle { width: 100%; } </style>
配图素材