• Vue项目实战之人力资源平台系统(三)主页模块


    前言

    一、主页的token拦截处理

    上一天的学习中,我们已经完成了登录的过程,并且存储了token,但是此时主页并没有根据token的有无而被控制访问权限
    访问权限拦截的流程图如下所示:
    在这里插入图片描述

    在src/permission.js中添加权限拦截代码,使用了路由导航守卫功能:

    // 权限拦截 导航守卫 路由守卫  router
    import router from '@/router' // 引入路由实例
    import store from '@/store' // 引入vuex store实例
    import NProgress from 'nprogress' // 引入一份进度条插件
    import 'nprogress/nprogress.css' // 引入进度条样式
    
    
    const whiteList = ['/login', '/404'] // 定义白名单  所有不受权限控制的页面
    // 路由的前置守卫
    router.beforeEach(function(to, from, next) {
      NProgress.start() // 开启进度条
      //  首先判断有无token
      if (store.getters.token) {
        //   如果有token 继续判断是不是去登录页
        if (to.path === '/login') {
          //  表示去的是登录页
          next('/') // 跳到主页
        } else {
          next() // 直接放行
        }
      } else {
        // 如果没有token
        if (whiteList.indexOf(to.path) > -1) {
          // 如果找到了 表示在在名单里面
          next()
        } else {
          next('/login') // 跳到登录页
        }
      }
      NProgress.done() // 手动强制关闭一次  为了解决 手动切换地址时  进度条的不关闭的问题
    })
    // 后置守卫
    router.afterEach(function() {
      NProgress.done() // 关闭进度条
    })
    注意:在导航守卫的位置,我们添加了NProgress的插件,可以完成进入时的进度条效果
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    二、主页的左侧导航样式

    2.1 左侧导航效果图

    在这里插入图片描述

    2.2 在src/styles/siderbar.scss中添加左侧导航样式代码:

    2.2.1 设置背景渐变色

    .sidebar-container {
          background: -webkit-linear-gradient(bottom, #3d6df8, #5b8cff);
    }
    
    • 1
    • 2
    • 3

    2.2.2 设置左侧导航背景图片

    .scrollbar-wrapper {
        background: url('~@/assets/common/leftnavBg.png') no-repeat 0 100%;
    }
    注意:在scss中,如果我们想要使用@别名,需要在前面加上一个~才可以
    
    • 1
    • 2
    • 3
    • 4

    2.2.3 设置菜单选中颜色

       .el-menu {
          border: none;
          height: 100%;
          width: 100% !important;
          a{
            li{
              .svg-icon{
                color: #fff;
                font-size: 18px;
                vertical-align: middle;
                .icon{
                  color:#fff;
                }
              }
              span{
                color: #fff;
              }
              &:hover{
                .svg-icon{
                  color: #43a7fe
                }
                span{
                  color: #43a7fe;
                }
              }
            }
          }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.2.4 显示左侧logo图片

    需要先在src/setttings.js中配置:

    module.exports = {
      title: '人力资源管理平台',
    
      /**
       * @type {boolean} true | false
       * @description Whether show the logo in sidebar
       */
      sidebarLogo: true // 显示logo
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后在src/layout/components/Sidebar/Logo.vue中设置Logo图片路径:

    <div class="sidebar-logo-container" :class="{'collapse':collapse}">
        <transition name="sidebarLogoFade">
          <router-link key="collapse" class="sidebar-logo-link" to="/">
            <img src="@/assets/common/logo.png" class="sidebar-logo  ">
          </router-link>
        </transition>
      </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    最后配置Logo图片样式:

     // 小图样式 当导航栏缩小时显示的logo样式
     &.collapse {
        .sidebar-logo {
          margin-right: 0px;
          width: 32px;
          height: 32px;
        }
      }
    
    // 大图样式 当导航栏展开时显示的logo样式
    .sidebar-logo {
          width: 140px;
          vertical-align: middle;
          margin-right: 12px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    三、设置头部内容的布局和样式

    3.1 头部内容的效果图

    在这里插入图片描述

    3.2 在src/layout/components/Navbar.vue中添加头部内容布局样式代码

    3.2.1 公司名布局和样式

      <div class="app-breadcrumb">
          XXX股份有限公司
          <span class="breadBtn">体验版</span>
      </div>
    
    
    .app-breadcrumb {
      display: inline-block;
      font-size: 18px;
      line-height: 50px;
      margin-left: 10px;
      color: #ffffff;
      cursor: text;
      .breadBtn {
        background: #84a9fe;
        font-size: 14px;
        padding: 0 10px;
        display: inline-block;
        height: 30px;
        line-height: 30px;
        border-radius: 10px;
        margin-left: 15px;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.2.2 头部内容整体背景颜色

    .navbar {
        background-image: -webkit-linear-gradient(left, #3d6df8, #5b8cff);
    }
    
    • 1
    • 2
    • 3

    3.2.3 右侧下拉菜单布局和样式

     <div class="right-menu">
          <el-dropdown class="avatar-container" trigger="click">
            <div class="avatar-wrapper">
              <img src="@/assets/common/bigUserHeader.png" class="user-avatar">
              <span class="name">管理员</span>
              <i class="el-icon-caret-bottom" style="color:#fff" />
            </div>
            <el-dropdown-menu slot="dropdown" class="user-dropdown">
              <router-link to="/">
                <el-dropdown-item>
                  首页
                </el-dropdown-item>
              </router-link>
              <a target="_blank" href="https://gitee.com/shuiruohanyu/hrsaas53">
                <el-dropdown-item>项目地址</el-dropdown-item>
              </a>
              <el-dropdown-item divided @click.native="logout">
                <span style="display:block;">退出登录</span>
              </el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </div>
    
      .user-avatar {
              cursor: pointer;
              width: 30px;
              height: 30px;
              border-radius: 15px;
              vertical-align: middle;
       }
       .name {
              color: #fff;
              vertical-align: middle;
              margin-left:5px;
       }
       .user-dropdown {
               color: #fff;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    四、获取用户资料接口

    4.1 在src/api/user.js中构建获取用户资料的请求

    /**
    *  获取用户的基本资料
    *
    * **/
    export function getUserInfo() {
      return request({
        url: '/sys/profile',
        method: 'post'
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.2 在axios请求拦截器中统一注入token

    在src/utils/request.js文件中统一注入token:

    service.interceptors.request.use(config => {
      // 在这个位置需要统一的去注入token
      if (store.getters.token) {
        // 如果token存在 注入token
        config.headers['Authorization'] = `Bearer ${store.getters.token}`
      }
      return config // 必须返回配置
    }, error => {
      return Promise.reject(error)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.3 在vuex模块中封装获取用户资料的action,mutations,action,getters

    在src/store/modules/user.js文件中添加如下代码:

    import { getUserInfo } from '@/api/user'
    
    const state = {
      userInfo: {} // 定义一个空的对象
    }  
    
    const mutations = {
        // 设置用户信息
      setUserInfo(state, userInfo) {
        state.userInfo = { ...userInfo } // 用 浅拷贝的方式去赋值对象 因为这样数据更新之后,才会触发组件的更新
      },
    }
    
    const action = {
      // 获取用户资料action
      async getUserInfo (context) {
        const result = await getUserInfo()  // 获取返回值
        context.commit('setUserInfo', result) // 将整个的个人信息设置到用户的vuex数据中
        return result 
      }
    }
    
    在src/store/getters.js文件中建立用户名的映射:
    
    const getters = {
      name: state => state.user.userInfo.username // 建立用户名称的映射
    }
    export default getters
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4.4 权限拦截处调用获取资料action

    用户资料要求必须有token才可以获取,那么我们就可以在确定有token的位置去获取用户资料
    在src/permission.js文件中添加如下代码:

    // 如果没有用户id说明还没获取到用户资料,才会调用vuex的获取资料的action
    if(!store.state.user.userInfo.userId) {
           await store.dispatch('user/getUserInfo')
    }
    
    • 1
    • 2
    • 3
    • 4

    4.5 将获取到的用户资料导入头部内容

    在layout/components/Navbar.vue文件中添加如下代码:

    ...mapGetters([
          'name',
          'staffPhoto'
        ])
         <img :src="staffPhoto" class="user-avatar">
         <span class="name">{{ name }}</span>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    五、用户登出功能

    5.1 登出流程图

    在这里插入图片描述

    5.2 在vuex模块中封装登出的action

    在src/store/modules/user.js文件中添加如下代码:

    const mutations = {
      removeToken(state) {
        state.token = null // 将vuex的数据置空
        removeToken() // 同步到缓存
      }
      removeUseInfo(state) {
        state.userInfo = {}
      }
    }
    
    const action = {  
      // 登出的action
      logout(context) {
        // 删除token
        context.commit('removeToken') // 不仅仅删除了vuex中的 还删除了缓存中的
        // 删除用户资料
        context.commit('removeUserInfo') // 删除用户信息
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5.3 头部内容调用登出action

    在src/layout/components/Navbar.vue中添加如下代码:

    <el-dropdown-item divided @click.native="logout">
       <span style="display:block;">退出登录</span>
    </el-dropdown-item>  
    
      async logout() {
          await this.$store.dispatch('user/logout') // 这里不论写不写 await 登出方法都是同步的
          this.$router.push(`/login`) // 跳到登录
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    六、token超时处理-主动介入

    6.1 主动介入流程图

    在这里插入图片描述

    6.2 配置获取时间戳函数

    在src/utils/auth.js文件中添加如下代码:

    const timeKey = 'hrsaas-timestamp-key' // 设置一个独一无二的key
    
    // 获取时间戳
    export function getTimeStamp() {
      return Cookies.get(timeKey)
    }
    // 设置时间戳
    export function setTimeStamp() {
      Cookies.set(timeKey, Date.now())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    6.3 在axios的请求拦截器中判断token是否超时

    在src/utils/request.js文件中添加如下代码:

    import axios from 'axios'
    import store from '@/store'
    import router from '@/router'
    import { Message } from 'element-ui'
    import { getTimeStamp } from '@/utils/auth'
    const TimeOut = 3600 // 定义超时时间
    
    
    // 请求拦截器
    service.interceptors.request.use(config => {
      // config 是请求的配置信息
      // 注入token
      if (store.getters.token) {
        // 只有在有token的情况下 才有必要去检查时间戳是否超时
        if (IsCheckTimeOut()) {
          // 如果它为true表示 过期了
          // token没用了 因为超时了
          store.dispatch('user/logout') // 登出操作
          // 跳转到登录页
          router.push('/login')
          return Promise.reject(new Error('token超时了'))
        }
        config.headers['Authorization'] = `Bearer ${store.getters.token}`
      }
      return config // 必须要返回的
    }, error => {
      return Promise.reject(error)
    })
    
    // 是否超时
    // 超时逻辑  (当前时间  - 缓存中的时间) 是否大于 时间差
    function IsCheckTimeOut() {
      var currentTime = Date.now() // 当前时间戳
      var timeStamp = getTimeStamp() // 缓存时间戳
      return (currentTime - timeStamp) / 1000 > TimeOut
    }
    export default service
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    6.4 登录成功设置时间戳

    在src/modules/user.js中添加如下代码:

    import { getToken, setToken, removeToken, setTimeStamp } from '@/utils/auth' 
    
    async login(context, data) {
        const result = await login(data) 
        context.commit('setToken', result)
        // 写入时间戳
        setTimeStamp() // 将当前的最新时间写入缓存
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    七、token超时处理-被动接收

    被动接收的意思是当我们发送请求给后端时,后端告诉我们token超时了,我们被迫做出处理

    7.1 被动接收的流程图

    在这里插入图片描述

    7.2 通过axios的响应拦截器对后端返回的错误码进行处理

    在src/utils/request.js中添加如下代码:

    // 响应拦截器
    service.interceptors.response.use(response => {
      // axios默认加了一层data
      const { success, message, data } = response.data
      //   要根据success的成功与否决定下面的操作
      if (success) {
        return data
      } else {
        // 业务已经错误了 还能进then ? 不能 ! 应该进catch
        Message.error(message) // 提示错误消息
        return Promise.reject(new Error(message))
      }
    }, error => {
      // error 信息 里面 response的对象
      if (error.response && error.response.data && error.response.data.code === 10002) {
        // 当等于10002的时候 表示 后端告诉我token超时了
        store.dispatch('user/logout') // 登出action 删除token
        router.push('/login')
      } else {
        Message.error(error.message) // 提示错误信息
      }
      return Promise.reject(error)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    总结

    今天主要实现了左侧和顶部导航栏模块和的布局,以及补全了登录认证流程,添加了退出登录功能,代码较多,请细细品味

  • 相关阅读:
    已完成的python项目-环境离线部署
    开源基于Rust编写的Web服务器
    R语言——taxize(第一部分)
    【HAL库】STM32CubeMX开发----STM32F407----CAN通信实验
    基于springboot的“老年智慧云”老年人在线学习平台毕业设计源码262221
    【数据结构】队列和栈
    C语言的5个内存段你了解吗?( 代码段/数据段/栈/堆)
    .NET性能优化-你应该为集合类型设置初始大小
    Hudi源码|bootstrap源码分析总结(写Hudi)
    Spring Security(8)
  • 原文地址:https://blog.csdn.net/qq_40652101/article/details/126766619