Vue后台管理系统项目——实现登录功能

本文详细介绍了Vue后台管理系统中登录功能的实现过程,包括登录业务流程、相关技术点如cookie、session与token的选择,以及登录功能的具体实现步骤,如表单布局、数据绑定、验证、重置和弹窗提示等。还涉及路由导航守卫控制访问权限的设置。

登录功能

1. 登录业务流程
  1. 在登录页输入用户名和密码
  2. 点击登录按钮,调用后台接口进行验证
  3. 通过验证后,根据后台的响应状态跳转到项目主页
2. 登录业务相关技术点
  • http 是无状态的

登录成功后需要记录用户的登录状态,有以下两种方案:

  • 通过 cookie 在客户端记录状态
  • 通过 session 在服务端记录状态
  • 通过 token 方式维持状态

那么这两种方案该如何选择呢?

前端与后台接口不存在跨域问题时,推荐使用 cookie 和 session 来记录登录状态;

存在跨域问题时,推荐使用 token 方式来维持状态

跨域:协议、域名、端口号只要有一个不一样就是跨域

3. 登录—— token 原理分析

在这里插入图片描述

4. 登录功能的实现(都记牢点)
登录页面的布局

通过 Element-UI 组件来实现布局

  • el-form:整个登录框是一个form
  • el-form-item:用户名,密码,登录和重置按钮都属于表单item项,有3个
  • el-input
  • el-button
  • 字体图标

打开项目之后,先打开终端输入 git status 查看一下当前工作区是否干净,然后创建新分支,注意哦,在实际开发中,当我们要开发一个新功能时,尽量把新功能放到新分支去进行开发

查看工作区是否干净

PS D:\前端学习路程\Vue\vue_shop> git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

创建新分支login

PS D:\前端学习路程\Vue\vue_shop> git checkout -b login
Switched to a new branch 'login'

查看项目主分支和所有分支

PS D:\前端学习路程\Vue\vue_shop> git branch
* login
  master

在绘制登录页面之前,我们需要先对项目的结构进行梳理,将不必要的代码以及组件清除掉,在梳理前,要查看一下项目的运行效果,可以直接在终端输入

npm run serve

或者是打开vue可视化面板,在菜单中选择任务,运行serve命令 ,等待编译完成然后点击启动app

清空 App 组件的内容,重新运行前,如果下载了ESlint的话,记得关闭语法检查,在vue.config.js文件夹中添加lintOnSave:false然后再重新运行,否则报错

创建登录组件
  • 定义Login组件

    <template>
      <div>登录组件</div>
    </template>
    
    <script>
    export default {
      name: "Login",
    };
    </script>
    
    <style lang="less" scoped>
    </style>
    
  • 在route/index.js文件中通过路由的形式把它渲染到App根组件中,先导入Login组件,然后在路由规则数组中导入一个新的路由规则

  • 在App根组件中放置一个路由占位符<router-view>

    <template>
      <div class="app">
        <router-view></router-view>        <!-- 3.添加占位符 -->
      </div>
    </template>
    
    <script>
    export default {
      name: "app",
    };
    </script>
    
    <style>
    </style>
    
  • 添加重定向路由规则,使得访问根组件时会自动重定向到login组件中

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Login from '../components/Login.vue'   //1.先导入Login组件
    
    Vue.use(VueRouter)
    
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          redirect: '/login'                       //4.添加重定向路由规则
        },
        {
          path: '/login',                          //2.然后导入一个新的路由规则
          component: Login
        }
      ]
    })
    
    export default router
    
登录组件页面布局

如果使用的是less语法,运行后会报错,需要提前下载好lessless-loader,注意版本

在终端输入命令

npm install less less-loader --save-dev
npm install less --save-dev

在这里插入图片描述

  1. 设置背景颜色

    这里我有一个疑惑点:为什么需要设置一个全局样式表,将html,body,#app撑满整个浏览器?

  2. 将登录盒子login_box设置为居中,有很多方法,要熟练掌握哦!

https://vue3js.cn/interview/css/center.html#%E4%B8%80%E3%80%81%E8%83%8C%E6%99%AF

登录组件头部布局

绘制顶部默认头像区域

在这里插入图片描述

登录组件表单布局

绘制登录表单区域,需要用到Element-UI组件,找到plugins/element.js文件导入所需的element ui组件

import Vue from 'vue'
import { Button } from 'element-ui'
import { Form, FormItem } from "element-ui";
import { Input } from "element-ui";

Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)

一些疑惑点

  1. 为什么表单添加了绝对定位之后,会缩小成那么一点大?在这里插入图片描述

  2. 注意CSS3 box-sizing:border-box 的使用

登录组件表单小图标布局

在element ui组件库中找到input输入框,找到带icon的输入框,可以通过 prefix-iconsuffix-icon 属性在 input 组件首部和尾部增加显示图标,也可以通过 slot 来放置图标。
注意:如果图标不存在的话,就需要从外部引入,fonts文件夹放入assets文件夹中

<!-- 用户名 -->
<el-form-item>
    <!-- 添加小图标 -->
    <el-input prefix-icon="el-icon-user-solid"></el-input>
</el-form-item>
<!-- 密码 -->
<el-form-item>
    <!-- 添加小图标 -->
    <el-input prefix-icon="el-icon-lock"></el-input>
</el-form-item>
登录组件表单的数据绑定

a.通过属性绑定给el-form绑定一个model属性,值是一个数据对象

b.在data里面定义,为每个具体的表单项通过v-model属性双向绑定到这个数据对象的某个属性上

<!-- 登录表单区域 -->
<el-form :model="loginForm" label-width="0" class="login_form">
    <!-- 用户名 -->
    <el-form-item>
        <!-- 添加小图标 -->
        <el-input v-model="loginForm.username" prefix-icon="el-icon-user-solid"></el-input>
    </el-form-item>
    <!-- 密码 -->
    <el-form-item>
        <!-- 添加小图标 -->
        <el-input v-model="loginForm.password" prefix-icon="el-icon-lock" type="password"></el-input>
    </el-form-item>
    <!-- 按钮区域 -->
    <el-form-item class="btn">
        <el-button type="primary">登录</el-button>
        <el-button type="info">重置</el-button>
    </el-form-item>
</el-form>

<script>
export default {
  name: "Login",
  data() {
    return {
      //这是登录表单的数据绑定对象
      loginForm: {
        username: "admin",
        password: "123456",
      },
    };
  },
};
</script>
登录组件表单的数据验证

a.为el-form通过属性绑定指定一个rules校验对象

b.在data数据中,定义这个校验对象,其中每一个属性都是一个验证规则

c.可以为不同的表单item项通过prop指定不同的验证规则,来进行表单的验证

<!-- 登录表单区域 -->
<el-form :model="loginForm" :rules="loginFormRules" label-width="0" class="login_form">
    <el-form-item prop="username"></el-form-item>
</el-form>

      // 登录表单的验证规则
      loginFormRules: {
        //   验证用户名是否合法
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
          {
            min: 3,
            max: 10,
            message: "用户名为 3 到 10 个字符",
            trigger: "blur",
          },
        ],
        //   验证密码是否合法
        password: [
          { required: true, message: "请输入登录密码", trigger: "blur" },
          {
            min: 6,
            max: 12,
            message: "密码为 6 到 12 个字符",
            trigger: "blur",
          },
        ],
      },
登录组件实现表单的重置

Form Methods:

resetFields:对整个表单进行重置,将所有字段值重置为初始值并移除校验结果

  1. 要重置一个表单的话,就给表单添加一个ref引用,它的值就是组件的实例对象
  2. 给重置按钮绑定一个点击事件
  3. 接着可以通过this访问到$refs,访问表单的引用对象并调用resetFields函数方法
<!-- 登录表单区域 -->
<el-form
         ref="loginFormRef" 
         :model="loginForm" 
         :rules="loginFormRules" 
         label-width="0" 
         class="login_form">
    <!-- 按钮区域 -->
    <el-form-item class="btn">
        <el-button type="primary">登录</el-button>
        <el-button type="info" @click="resetLoginForm">重置</el-button>
    </el-form-item>
</el-form>
methods: {
    // 点击重置按钮,重置登录表单
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields();
    },
  },
登录前的预验证

获取到表单的引用对象,然后通过这个引用对象可以调用validate函数进行表单的预校验,在validate中接收一个回调函数,第一个形参是验证结果的布尔值,可以通过判断这个布尔值来判断表单是否通过了验证,部分代码如下

methods: {
    // 点击重置按钮,重置登录表单
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields();
    },
    // 点击登录按钮,进行登录前的预验证
    login() {
      this.$refs.loginFormRef.validate((valid) => {
        console.log(valid);
      });
    },
  },
根据预验证判断是否发起请求
  1. 在具体请求之前,应该在入口文件main.js中,对axios进行全局配置

    先导入axios包,把axios包挂载到vue的原型对象上,这样的话,每一个vue的组件都可以通过this直接访问到 h t t p , 从 而 去 发 起 A J A X 请 求 , http,从而去发起AJAX请求, httpAJAXhttp是自定义的属性名,可以随便起,当挂载完这个原型的属性之后,回过头来,尽量为axios设置一下请求的根路径

import axios from 'axios'
// 配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
Vue.prototype.$http = axios
  1. 启动MySQL数据库,运行node .\app.js启动API接口,打印一下上述的返回结果,得到Promise {<pending>}
methods: 
    // 点击登录按钮,进行预验证
    login() {
      this.$refs.loginFormRef.validate((valid) => {
        // console.log(valid);
        if (!valid) return;
        const result = this.$http.post("login", this.loginForm);
        console.log(result);
      });
    },
  },

在这里插入图片描述

  1. 如果某个方法的返回结果是Promise,我们可以用async await来简化这次Promise操作,await只能用在被async修饰的方法中,
methods: {
    // 点击登录按钮,进行预验证
    login() {
      this.$refs.loginFormRef.validate(async (valid) => {
        // console.log(valid);
        if (!valid) return;
        const result = await this.$http.post("login", this.loginForm);
        console.log(result);
      });
    },
  },

在这里插入图片描述

  1. 服务器返回的对象中包含了6个属性,其中data才是服务器返回的真实数据,因此从这个数据对象身上解构赋值出了data属性,同时重命名为res对象
methods: {
    // 点击登录按钮,进行预验证
    login() {
      this.$refs.loginFormRef.validate(async (valid) => {
        // console.log(valid);
        if (!valid) return;
        const { data: res } = await this.$http.post("login", this.loginForm);
        console.log(res);
      });
    },
  },

在这里插入图片描述

  1. 进行判断请求成功与否,根据res的meta属性来判断是否登录成功
methods: {
    // 点击登录按钮,进行预验证
    login() {
      this.$refs.loginFormRef.validate(async (valid) => {
        // console.log(valid);
        if (!valid) return;
        const { data: res } = await this.$http.post("login", this.loginForm);
        if (res.meta.status !== 200) return alert("登录失败了");
        alert("登录成功了");
      });
    },
  },

在这里插入图片描述

在这里插入图片描述

表单方法的小总结

Form Methods

方法名说明参数
validate对整个表单进行校验的方法,参数为一个回调函数。该回调函数会在校验结束后被调用,并传入两个参数:是否校验成功和未通过校验的字段。若不传入回调函数,则会返回一个 promiseFunction(callback: Function(boolean, object))
validateField对部分表单字段进行校验的方法Function(props: array | string, callback: Function(errorMessage: string))
resetFields对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
clearValidate移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果Function(props: array | string)
  • rules是实时验证,输完信息就会显示,validate是提交的时候验证,放置直接f12改数据提交
登录组件配置弹窗提示

使用的是Element UI组件库中提供的 Message 消息提示

配置过程:在element.js文件中,import导入弹框提示组件Message,它需要进行全局挂载 Vue.prototype.$message = Message

$message是自定义属性,名称只要合法就行

//element.js
// 导入弹框提示组件
import { Message } from "element-ui";

Vue.prototype.$message = Message
methods: {
    // 点击登录按钮,进行预验证
    login() {
      this.$refs.loginFormRef.validate(async (valid) => {
        // console.log(valid);
        if (!valid) return;
        const { data: res } = await this.$http.post("login", this.loginForm);
        if (res.meta.status !== 200) return this.$message.error("登录失败了");
        this.$message.success("登录成功了");
      });
    },
  },

在这里插入图片描述

登录成功后的操作行为
  1. 将登录成功之后的 token 保存到客户端的 sessionStorage 中

    • 项目中除了登录之外的其他API接口,必须在登录之后才能访问成功
    • token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中
    window.sessionStorage.setItem("token", res.data.token);
    
  2. 通过编程式导航跳转到后台主页,路由地址是 /home,所以需要新建一个路由规则

    $router编程式导航对象调用push方法

    this.$router.push("/home", res.data.token);
    
路由导航守卫控制访问权限

home是一个有权限的页面,只有登录成功之后才可以被访问,如果用户没有登录,但是直接通过URL访问到了特定页面,需要重新导航到登录页面(我们希望用户从home路径直接跳转到login登录页)

// 为路由对象,添加 beforeEach 导航守卫
router.beforeEach((to,from,next) => {
    // 如果用户访问的登录页,直接放行
    if(to.path === '/login') return next()
    // 从 sessionStorage 中获取到保存的 token 值
    const tokenStr = window.sessionStorage.getItem('token')
    // 没有 token,强制跳转到登录页
    if(!tokenStr) return next('/login')
    next()
})

$router编程式导航对象调用push方法

this.$router.push("/home", res.data.token);
路由导航守卫控制访问权限

home是一个有权限的页面,只有登录成功之后才可以被访问,如果用户没有登录,但是直接通过URL访问到了特定页面,需要重新导航到登录页面(我们希望用户从home路径直接跳转到login登录页)

// 为路由对象,添加 beforeEach 导航守卫
router.beforeEach((to,from,next) => {
    // 如果用户访问的登录页,直接放行
    if(to.path === '/login') return next()
    // 从 sessionStorage 中获取到保存的 token 值
    const tokenStr = window.sessionStorage.getItem('token')
    // 没有 token,强制跳转到登录页
    if(!tokenStr) return next('/login')
    next()
})

记得及时的复习哦!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值