【Vue框架】基本的login登录

news2024/11/23 15:08:38

前言

最近事情比较多,只能抽时间看了,放几天就把之前弄的都忘了,现在只挑着核心的部分看。现在铺垫了这么久,终于可以看前端最基本的登录了😂。

1、views\login\index.vue

由于代码比较长,这里将vue和js代码分开展示。

1.1、login的vue代码

<template>
  <div class="login-container">
    <!-- 使用`loginForm`作为表单的数据模型,
    `loginRules`作为表单字段的验证规则,
    `ref`属性为`loginForm`用于在代码中引用该表单组件,
    `class`属性添加了`login-form`样式类名,
    `auto-complete`设置为`on`表示浏览器会自动完成输入,
    `label-position`设置为`left`表示标签位于输入框左侧-->
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
      <!--标题-->
      <div class="title-container">
        <h3 class="title">Login Form</h3>
      </div>
      <!--一个表单项组件,用于包裹一个字段。`prop`属性设置为"username",用于表单验证。-->
      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <!--一个Element UI的输入框组件
          `ref="username"`:给输入框组件添加一个引用,用于在代码中引用该输入框组件
          `v-model="loginForm.username"`:利用双向绑定,将输入框的值与`loginForm`对象中的`username`属性进行绑定
          `placeholder="输入框的占位文字Username"`:设置输入框的占位文字"
          `type="text"`:设置输入框的类型为文本输入
          `tabindex="1"`:设置输入框的tab索引为1,用于设置键盘焦点的顺序
          `auto-complete="on"`:设置浏览器自动完成输入-->
        <el-input
          ref="username"
          v-model="loginForm.username"
          placeholder="输入框的占位文字Username"
          name="username"
          type="text"
          tabindex="1"
          auto-complete="on"
        />
      </el-form-item>
      <!--一个Element UI的工具提示组件,用于显示大写锁定键(Caps lock)的状态提示信息-->
      <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
        <el-form-item prop="password">
          <span class="svg-container">
            <svg-icon icon-class="password" />
          </span>
          <!--`@keyup.native="checkCapslock"`:当键盘按键弹起时触发`checkCapslock`方法,用于检查大写锁定键的状态
           `@blur="capsTooltip = false"`:当输入框失去焦点时,隐藏大写锁定键提示
           `@keyup.enter.native="handleLogin"`:当按下Enter键时触发`handleLogin`方法,完成登录操作-->
          <el-input
            :key="passwordType"
            ref="password"
            v-model="loginForm.password"
            :type="passwordType"
            placeholder="输入框的占位文字Password"
            name="password"
            tabindex="2"
            auto-complete="on"
            @keyup.native="checkCapslock"
            @blur="capsTooltip = false"
            @keyup.enter.native="handleLogin"
          />
          <span class="show-pwd" @click="showPwd">
            <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
          </span>
        </el-form-item>
      </el-tooltip>
      <!--`@click`是Vue的事件绑定语法,
      `.native`修饰符用于监听组件根元素的原生(非自定义)事件。
      `.prevent`修饰符用于阻止事件的默认行为,例如阻止表单的提交行为-->
      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>

      <div style="position:relative">
        <div class="tips">
          <span>Username : admin</span>
          <span>Password : any</span>
        </div>
        <div class="tips">
          <span style="margin-right:18px;">Username : editor</span>
          <span>Password : any</span>
        </div>

        <el-button class="thirdparty-button" type="primary" @click="showDialog=true">
          Or connect with
        </el-button>
      </div>
    </el-form>

    <el-dialog title="Or connect with" :visible.sync="showDialog">
      Can not be simulated on local, so please combine you own business simulation! ! !
      <br>
      <br>
      <br>
      <social-sign />
    </el-dialog>
  </div>
</template>

1.2、login的js代码

<script>
import { validUsername } from '@/utils/validate'
import SocialSign from './components/SocialSignin'

export default {
  name: 'Login',
  // 这个引入`SocialSign`中export default中所有的东西;SocialSign是第三方登录的内容
  components: { SocialSign },
  data() {
    // callback:函数变量,传入函数名称,可以复用,动态传入不同的函数使用
    // 定义验证方式
    const validateUsername = (rule, value, callback) => {
      // 验证用户名是否符合规则
      if (!validUsername(value)) {
        callback(new Error('Please enter the correct user name'))
      } else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) => {
      if (value.length < 6) {
        callback(new Error('The password can not be less than 6 digits'))
      } else {
        callback()
      }
    }
    // 一般变量的定义,都是在data()下的return中写
    return {
      // 用来记录前端交互的用户名和密码
      loginForm: {
        username: 'admin',
        password: '111111'
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
      },
      passwordType: 'password',
      capsTooltip: false,
      loading: false,
      showDialog: false,
      redirect: undefined,
      otherQuery: {}
    }
  },
  watch: {
    $route: {
      handler: function(route) {
        const query = route.query
        if (query) {
          this.redirect = query.redirect
          this.otherQuery = this.getOtherQuery(query)
        }
      },
      immediate: true
    }
  },
  created() {
    // window.addEventListener('storage', this.afterQRScan)
  },
  // `mounted()`,在组件挂载后被调用
  mounted() {
    // `this.$refs`来访问已经挂载的`username`输入框组件,并调用其`focus()`方法来设置该输入框组件获取焦点
    if (this.loginForm.username === '') {
      this.$refs.username.focus()
    } else if (this.loginForm.password === '') {
      this.$refs.password.focus()
    }
  },
  destroyed() {
    // window.removeEventListener('storage', this.afterQRScan)
  },
  methods: {
    // 检查大写锁定键的方法
    checkCapslock({ shiftKey, key } = {}) {
      if (key && key.length === 1) {
        if (shiftKey && (key >= 'a' && key <= 'z') || !shiftKey && (key >= 'A' && key <= 'Z')) {
          this.capsTooltip = true
        } else {
          this.capsTooltip = false
        }
      }
      if (key === 'CapsLock' && this.capsTooltip === true) {
        this.capsTooltip = false
      }
    },
    // 是否显示具体密码
    showPwd() {
      if (this.passwordType === 'password') {
        this.passwordType = ''
      } else {
        this.passwordType = 'password'
      }
      this.$nextTick(() => {
        this.$refs.password.focus()
      })
    },
    // 实际登录的方法
    handleLogin() {
      // `validate`是`this.$refs.loginForm`访问到的登录表单组件的方法,
      // 这个方法会根据组件的表单验证规则进行验证,并将验证结果以参数的形式传递给回调函数
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          // 访问store/modules/user.js中action下的方法login
          this.$store.dispatch('user/login', this.loginForm)
            .then(() => {
              // `this.$router`全局路由,通过`push({path:'路径',query:{id:参数}})`进行路由跳转
              this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
              this.loading = false
            })
            .catch(() => {
              this.loading = false
            })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },
    getOtherQuery(query) {
      return Object.keys(query).reduce((acc, cur) => {
        if (cur !== 'redirect') {
          acc[cur] = query[cur]
        }
        return acc
      }, {})
    }
    // afterQRScan() {
    //   if (e.key === 'x-admin-oauth-code') {
    //     const code = getQueryObject(e.newValue)
    //     const codeMap = {
    //       wechat: 'code',
    //       tencent: 'code'
    //     }
    //     const type = codeMap[this.auth_type]
    //     const codeName = code[type]
    //     if (codeName) {
    //       this.$store.dispatch('LoginByThirdparty', codeName).then(() => {
    //         this.$router.push({ path: this.redirect || '/' })
    //       })
    //     } else {
    //       alert('第三方登录失败')
    //     }
    //   }
    // }
  }
}
</script>

上面的内容涉及到callbackthis.$router的内容,具体可以参考以下链接了解:

  1. callback的使用
  2. vue this.$router.push 实现路由跳转

2、前端登录的执行流程图

根据views\login\index.vue的代码和【Vue框架】用户和请求的内容,整理得到他们的执行流程图,如下:
在这里插入图片描述
PS:前后端response内容关系不清楚的,可以看【Vue框架】用户和请求中的第5节。

3、src\permission.js

3.1 代码

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar 用于显示进度条,NProgress显示页面加载或异步操作进度的JavaScript库
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title' // 用于获取页面标题

// showSpinner: false不会显示旋转的加载图标,只会显示进度的条形动画
NProgress.configure({ showSpinner: false }) // NProgress Configuration

// 定义了一些不需要进行路由重定向的白名单路径,例如`/login`和`/auth-redirect`。这些路径将被认为是无需进行重定向或权限检查的安全路径
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist

/**
 * 在 Vue Router 中,全局前置守卫(Global before Guards)是一种用于拦截导航的机制,
 * 允许在路由切换之前对导航进行一些操作,比如进行身份验证、权限检查等。
 * 全局前置守卫是在 Vue Router 实例中注册的一组函数(constantRoutes和asyncRoutes),会在每次导航发生之前按照顺序执行。
 * 过调用 `router.beforeEach` 方法来注册全局前置守卫
 * 1. `to`:即将进入的路由。
 * 2. `from`:当前导航正要离开的路由。
 * 3. `next`:一个函数,用于控制导航的行为。需要在回调函数最后调用 `next` 方法来确认导航是否继续。
 */
router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page 如果用户已经登录并且正在访问登录页面
      // 【用户从新打开页面,应该默认访问首页,然后这里来判断用户是否登录,如果登录就可以直接访问首页】
      // 重定向到主页
      next({ path: '/' })
      // 结束进度条
      NProgress.done()
    } else {
      // 如果用户已经登录,但不是在访问登录页面
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        // 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
        next()
      } else {
        try {
          // get user info
          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
          // store是Vuex的实例,dispatch访问到里面action定义的方法('user/getInfo')
          const { roles } = await store.dispatch('user/getInfo')

          // generate accessible routes map based on roles
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)

          // dynamically add accessible routes
          // `addRoutes`方法只能在实例化Vue Router之后才能调用,且只能调用一次。否则会导致路由异常
          router.addRoutes(accessRoutes)

          // hack method to ensure that addRoutes is complete
          // set the replace: true, so the navigation will not leave a history record
          next({ ...to, replace: true })
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`) // 重定向到登录页面,并附带返回地址
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/

    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

3.2 什么是路由守卫(导航守卫)

  • 个人理解:
    作用:从一个Vue文件(路由)跳转到另一个Vue文件(另一个路由),就会通过路由守卫来判断是否有权限正常跳转。

在这里插入图片描述
补充:
this.$router全局路由,通过push({path:'路径',query:{id:参数}})进行路由跳转,如下代码:

 this.$router.push({ path: this.redirect || '/', query: this.otherQuery })

个人理解:
push这里应该像是把route(路由)进栈,然后再根据路由守卫的next()把route出栈,跳转到出栈的route。

  • route是什么?
    在这里插入图片描述

3.3 代码中的主要逻辑

在3.2中提到,路由守卫是用来判断是否有权限正常跳转的,admin代码给出的判断逻辑如下图:
在这里插入图片描述
PS:一般好像也就是这样的逻辑

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vxe-table解决虚拟滚动时的白屏问题

项目中使用虚拟滚动&#xff0c;滚动一快就会白屏&#xff0c;花了两天时间看了源码&#xff0c;检查渲染元素&#xff0c;发现元素渲染的是很快的&#xff0c;跟得上的&#xff0c;但是还是会出现白屏&#xff0c;后面检查样式&#xff0c;发现只要把表格背景色去掉&#xff0…

平安私人银行慈善沙龙广州站:善财传承公益有道,广州分行聚爱同行

近年来&#xff0c;平安私人银行将慈善作为客户服务的王牌权益之一&#xff0c;激发和满足客户公益慈善心愿&#xff0c;打造财富人群和困境人群的桥梁&#xff0c;并链接公益机构等专业组织&#xff0c;深度挖掘金融赋能慈善的多种可能性&#xff0c;让财富通过慈善事业释放出…

STM32CubeMX配置STM32G0 Standby模式停止IWDG(HAL库开发)

1.打开STM32CubeMX选择好对应的芯片&#xff0c;打开IWDG 2.打开串口1进行调试 3.配置好时钟 4.写好项目名称&#xff0c;选好开发环境&#xff0c;最后获取代码。 5.打开工程&#xff0c;点击魔术棒&#xff0c;勾选Use Micro LIB 6.修改main.c #include "main.h"…

springCloud整合Zookeeper的时候调用找不到服务

SpringCloud整合Zookeeper的时候调用找不到服务 首先&#xff0c;我们在注册中心注册了这个服务&#xff1a; 然后我们使用RestTemplate 调用的时候发现失败了&#xff1a;找不到这个服务&#xff1a; 找了很多资料发现这个必须要加上负载才行 BeanLoadBalanced //负载publi…

javaee idea创建maven项目,使用el和jstl

如果使用el表达式出现下图问题 解决办法 这是因为maven创建项目时&#xff0c;web.xml头部声明默认是2.3&#xff0c;这个默认jsp关闭el表达式 办法1 在每个需要用到el和jstl的页面的上面加一句: <% page isELIgnored"false" %> 方法2 修改web.xml文件开…

node和前端项目宝塔部署

首先需要一台服务器 购买渠道&#xff1a;阿里云、腾讯云、百度云、华为云 一、以阿里云为例 购买esc 可临时购买测试服务器 二、安装宝塔 复制公网ip地址 通过Xshell 进行账号密码的连接 连接后访问宝塔官网 宝塔面板下载&#xff0c;免费全能的服务器运维软件 找到自己…

jenkins Linux如何修改jenkins 默认的工作空间workspace

由于jenkins默认存放数据的目录是/var/lib/jenkins&#xff0c;一般这个var目录的磁盘空间很小的&#xff0c;就几十G,所以需要修改jenkins的默认工作空间workspace 环境 jenkins使用yum安装的 centos 7 正题 1 查看jenkins安装路径 [rootlocalhost jenkins_old_data]# rpm…

二叉树题目:二叉树的层序遍历 II

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的层序遍历 II 出处&#xff1a;107. 二叉树的层序遍历 II 难度 4 级 题目描述 要求 给你二叉树的根结点 root \texttt{root} root&#x…

shopee开店前期要怎么做!新手必须知道的小技巧

在shopee开店前期可以先做以下准备&#xff1a; 1.shopee开店前精准定位 确定目标人群&#xff1a;做店铺定位离不开目标人群需求&#xff0c;人群定位解决的是产品卖给谁的问题&#xff0c;这就需要了解客户群体的各方面信息。加之&#xff0c;Shopee平台作为东南亚土生土长的…

Docker容器学习:搭建私有镜像仓库Harbor

系统环境&#xff1a; Centos7.9Docker-ce:24 安装Docker-Compose curl -L "https://github.com/docker/compose/releases/download/v2.20.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose添加执行权限并验证 [rootnode4 ~]# chmod…

dolphinschedule配置企微告警服务(WeChat群组)

一、前置说明 ds配置好工作流后&#xff0c;比较重要的一个就是上线后的监控报警服务&#xff0c;如果你是基于企微作为协同办公的&#xff0c;WeChat群组预警必须是要安排上的&#xff0c;文章基于自建应用配合群组方式构建预警群&#xff0c;接入后&#xff0c;任务成功或者…

多线程与高并发——并发编程(1)

文章目录 并发编程一、线程的基本概念1 基础概念1.1 进程和线程1.2 多线程1.3 串行、并行、并发1.4 同步异步、阻塞非阻塞 2 线程的创建2.1 继承Thread类&#xff0c;重写run方法2.2 实现Runnable接口&#xff0c;实现run方法2.3 实现Callable接口&#xff0c;实现call方法&…

Spring Boot+Atomikos进行多数据源的分布式事务管理详解和实例

文章目录 0.前言1.参考文档2.基础介绍3.步骤1. 添加依赖到你的pom.xml文件:2. 配置数据源及其对应的JPA实体管理器和事务管理器:3. Spring BootMyBatis集成Atomikos4. 在application.properties文件中配置数据源和JPA属性&#xff1a; 4.使用示例5.底层原理 0.前言 背景&#x…

漏洞复现 || SXF应用交付系统RCE(0Day)

漏洞描述 SXF应用交付管理系统login存在远程命令执行漏洞&#xff0c;攻击者通过漏洞可以获取服务器权限&#xff0c;执行任意命令。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织使用网络应当遵守宪法法律&#xff0c;遵守公共秩序&#xff0c;尊重社会公德&#…

【Linux】ctime、mtime、atime

/etc/fstab中的noatime noatimedir 挂载选项linux下文件的ctime、mtime、atime一、/etc/fstab /etc/fstab是用来存放文件系统的静态信息的文件。 系统开机时会主动读取/etc/fstab这个文件中的内容,根据文件里面的配置挂载磁盘。这样我们只需要将磁盘的挂载信息写入这个文件中…

libevent源码学习3---事件event

libevent源码学习3—事件event libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括: 文件描述符已经就绪, 可以读取或者写入文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)超时事件发生某信号用户触发事件 所有事件具有相似的生命周期。…

C++ string 类

文章目录 引用头文件初始化赋值1. 空串2. 拷贝复制3. 直接初始化赋值4. 单个字符初始化 遍历 string 类1. 下标索引遍历2. 迭代器遍历3. 使用 range for 循环遍历字符串&#xff08;需要 C11 或更新的版本&#xff09; string 常用方法判断字符串是否为空串获取字符串中字符个数…

Mysql简短又易懂

MySql 连接池:的两个参数 最大连接数&#xff1a;可以同时发起的最大连接数 单次最大数据报文&#xff1a;接受数据报文的最大长度 数据库如何存储数据 存储引擎&#xff1a; InnoDB:通过执行器对内存和磁盘的数据进行写入和读出 优化SQL语句innoDB会把需要写入或者更新的数…

FFI绕过disable_functions

文章目录 FFI绕过disable_functions[RCTF 2019]NextphpPHP7.4 FFI参考 FFI绕过disable_functions [RCTF 2019]Nextphp 首先来看这道题目 index.php <?php if (isset($_GET[a])) {eval($_GET[a]); } else {show_source(__FILE__); }查看一下phpinfo 发现过滤了很多函数&…

如何高效管理生产物料?

导 读 ( 文/ 2358 ) 所谓巧妇难为无米之炊&#xff0c;物料是生产过程的必需品&#xff0c;确保物料及时到料尤为关键&#xff0c;但堆积过多的物料库存将导致不必要的成本挤压。做好生产物料管理是门技术活&#xff0c;它的目标是确保所需的物料按时提供给生产线&#xff0c;以…