vue实战——登录【详解】(含自适配全屏背景,记住账号--支持多账号,显隐密码切换,登录状态保持)

效果预览

在这里插入图片描述

技术要点——自适配全屏背景

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>

配图素材

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【WP】Geek Challenge 2023 web 部分wp

EzHttp http协议基础题 unsign 简单反序列化题 n00b_Upload 很简单的文件上传&#xff0c;上传1.php&#xff0c;抓包&#xff0c;发现php内容被过滤了&#xff0c;改为<? eval($_POST[‘a’]);?>&#xff0c;上传成功&#xff0c;命令执行读取就好了 easy_php …

Docker+Jmeter+InfluxDB+Grafana优化压测报告

1、安装docker 运行Docker&#xff0c;并记录当前Docker的IP地址&#xff0c;本处IP为192.168.99.100 2、安装并配置influxDB 下载镜像 网上获取&#xff1a;docker pull tutum/influxdb 本地安装&#xff1a;docker load < influxdb.tar 安装influxDB容器 docker run…

尚硅谷大数据项目《在线教育之实时数仓》笔记008

视频地址&#xff1a;尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第10章 数仓开发之DWS层 P066 P067 P068 P069 P070 P071 P072 P073 P074 P075 P076 P077 P078 P079 P080 P081 P082 第10章 数仓开发之DWS层 P066 第10章 数仓开发之DW…

Java游戏 王者荣耀

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList…

2023年亚太杯APMCM数学建模大赛A题水果采摘机器人的图像识别

2023年亚太杯APMCM数学建模大赛 A题 水果采摘机器人的图像识别 原题再现 中国是世界上最大的苹果生产国&#xff0c;年产量约3500万吨。同时&#xff0c;中国也是世界上最大的苹果出口国&#xff0c;世界上每两个苹果中就有一个是中国出口的&#xff0c;世界上超过六分之一的…

Docker-简介、基本操作

目录 Docker理解 1、Docker本质 2、Docker与虚拟机的区别 3、Docker和JVM虚拟化的区别 4、容器、镜像的理解 5、Docker架构 Docker客户端 Docker服务器 Docker镜像 Docker容器 镜像仓库 Docker基本操作 1、Docker镜像仓库 镜像仓库分类 镜像仓库命令 docker lo…

CV计算机视觉每日开源代码Paper with code速览-2023.11.22

点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【语义分割】Mobile-Seed: Joint Semantic Segmentation and Boundary Detection for Mobile Robots 论文地址&#xff1a;https://arxiv.or…

高效视频剪辑:按指定时长批量分割视频,释放无尽创意

随着数字媒体技术的不断发展&#xff0c;视频剪辑已经成为日常生活中不可或缺的一部分。无论是制作电影、电视剧&#xff0c;还是创意生活短视频&#xff0c;视频剪辑都扮演着重要的角色。然而&#xff0c;对于许多非专业人士来说&#xff0c;视频剪辑可能是一项复杂而耗时的任…

C#,《小白学程序》第二十五课:大数乘法(BigInteger Multiply)的Karatsuba算法及源代码

1 文本格式 /// <summary> /// 《小白学程序》第二十五课&#xff1a;大数&#xff08;BigInteger&#xff09;的Karatsuba乘法 /// Multiplies two bit strings X and Y and returns result as long integer /// </summary> /// <param name"a">&…

如何在Ubuntu系统上安装Redis

Redis的下载 Redis安装包分为windows版和Linux版当前示例中介绍的是Linux版本Linux的下载地址&#xff1a;Index of /releases/ (redis.io)本次下载的压缩包为&#xff1a;redis-6.2.14.tar.gzRedis的安装 将压缩包通过ssh远程工具上传到Linux服务器中解压压缩包 tar -zxvf red…

深度学习18

卷积层 查看每个数据 使用tensorboard查看 池化层 使用数据集进行训练 创建实例&#xff0c;使用tensorboard进行显示 最大池化保留了图片信息&#xff0c;神经网络训练的数据量大大减小&#xff0c;可以加快训练 非线性激活 非线性激活为神经网络加入了一些非线性的特质…

蓝桥杯每日一题2023.11.27

题目描述 星系炸弹 - 蓝桥云课 (lanqiao.cn) 题目分析 对于此题目一一枚举即可 #include<bits/stdc.h> using namespace std; bool is_r(int n) {if((n % 4 0 && n % 100 ! 0)|| n % 400 0)return true;return false; } int mm[13] {0, 31, 28, 31, 30, 3…

【日常总结】优雅升级Swagger 2 升至 3.0, 全局设置 content-type application/json

目录 一、场景 二、问题 三、解决方案 四、延伸 上一节&#xff1a;【日常总结】Swagger-ui 导入 showdoc &#xff08;优雅升级Swagger 2 升至 3.0&#xff09;-CSDN博客 一、场景 接上一节&#xff1a;在 Swagger3Config extends WebMvcConfigurationSupport&#xff0c…

ECShop 4.x collection_listSQL注入

漏洞描述 ECShop是一款B2C独立网店系统&#xff0c;适合企业及个人快速构建个性化网上商店。系统是基于PHP语言及MYSQL数据库构架开发的跨平台开源程序 影响版本&#xff1a;ecshop4.0.7及以下 漏洞环境及利用 docker环境搭建 访问8080端口&#xff0c;数据库主机为mysql&a…

vue day2

1、指令修饰符&#xff1a;.指明一些指令后缀&#xff0c;不同后缀封装不同处理操作 按键修饰符&#xff1a;keyup.enter v-model修饰符&#xff1a; v-model.trim&#xff1a;去首位空格 v-model.number&#xff1a;转数字 事件修饰符&#xff1a; 阻止事件冒泡&#xff1…

毫米波雷达DOA角度计算-----DBF算法

DBF算法实现程序如下&#xff1a; 输入&#xff1a; parameter 是 毫米波雷达的参数设置。 antVec 是 目标点的8个虚拟天线的非相参积累数据。 function [angle,doa_abs] dbfMethod(parameter,antVec)txAntenna parameter.txAntenna; % 发射天线 [1 1]rxAntenna para…

交换技术-电路交换-报文交换-分组交换

交换技术是指主机之间、通信设备之间或主机与通信设备之间为交换信息所采用的数据格式和交换装置的方式。按交换技术可分为&#xff1a;电路交换、报文交换和分组交换。 电路交换 交换(switching)&#xff0c;就是按照某种方式动态地分配传输线路的资源。 电路交换是在源结点…

MFC、VC++操作excel后,excel程序进程无法正常退出的非暴力处理方法

先说处理方式 1、最low的方式&#xff1a;强制结束进程 //打开进程得到进程句柄 HANDLE hProcessOpenProcess(PROCESS_ALL_ACCESS,FALSE,Pid); if(hProcess!NULL) { //结束进程 if (TerminateProcess(hProcess,0)){printf("结束进程成功\n");return 0;} }这种方式…

带你用uniapp从零开发一个仿小米商场_10. 首页开发

图标菜单栏开发 轮播图开发完成后,就是图标菜单栏了 可以看出这些图标都是一样的样式,所以可以勇哥flex布局让他们每个占百分之20 代码如下,既然都是一样的那就直接用个循环嵌套一下 data数据如下 同样,为了能让这段代码能在别的地方也用到,我直接把它封装成组件 <templ…

nodejs+vue+elementui学生竞赛管理系统65o97

高校人才培养计划的重要组成部分&#xff0c;是实现人才培养目标、培养学生体育 能力与创新思维、学生竟赛管理系统检验学生综合素质与实践能力的重要手段与综合性实践教学环节。而我所在学院多采用半手工管理学生竟赛的方式&#xff0c;所以有必要开发学生竟赛管理系统来对学生…