2.2 iHRM人力资源 - 主页权限认证、Vux共享用户资料

iHRM人力资源 - 主页权限认证、主页内容展示

2.IHRM人力资源 - 登录-优快云博客

一、主页权限认证

1.1 主页权限认证分析

主页权限认证其实就是验证有没有token

token所在位置

image-20240115190158125

访问主页时

  • 有token

    放过请求,可以跳转到主页

  • 无token

    拦截访问主页的请求并跳转到登录页让用户进行登录

访问登录页时

  • 有token

    直接跳转到主页

  • 无token

    正常访问登录页面

我们可以看一下下面permission.js代码

image-20240115190355269

将代码收起来看很简洁,一个前置路由守卫beforeEach,一个后置路由守卫router.afterEach

Vue - 路由守卫

image-20240115190439134

然后再main.js文件中将路由守卫配置引入

image-20240115190625980

1.2 主页权限认证 - permission.js

主页权限验证就参考下图来就可以

image-20240115191124459

1.2.1 进度条部分

如下所示是一个进度条的插件,要想使用这个插件,直接引入到我们package.json文件中即可

image-20240115212452236

进度条的效果如下所示

image-20240115212853073

// 导入路由实例
import router from '@/router'
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条对应的样式
import 'nprogress/nprogress.css'
// 引入vuex实例对象
import store from '@/store'

/**
 * 前置守卫
 * 参数:
 *  to:信息要到哪里去
 *  from:信息从哪里来
 *  next:必须要执行的函数。如果不执行next,跳转就不会执行。说白了就是让着通行,如果没有next的话,页面会直接空白
 */
router.beforeEach((to, from, next) => {
  // 前置守卫开启进度条
  nprogress.start()
  next()
})

/**
 * 后置守卫
 */
router.afterEach(() => {
  // 关闭进度条 
  nprogress.done()
})

1.2.2 token 认证

// 导入路由实例
import router from '@/router'
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条对应的样式
import 'nprogress/nprogress.css'
// 引入vuex实例对象
import store from '@/store'

// 定义白名单,此路径不需要token就可以访问
const whiteList = ['/login', '/404']
/**
 * 前置守卫
 * 参数:
 *  to:信息要到哪里去
 *  from:信息从哪里来
 *  next:必须要执行的函数。如果不执行next,跳转就不会执行。说白了就是让着通行,如果没有next的话,页面会直接空白
 */
router.beforeEach((to, from, next) => {
  // 前置守卫开启进度条
  nprogress.start()
  if (store.getters.token) {
    // 存在token
    // 判断要去的地方是不是登录页面
    if (to.path === '/login') {
      // 此时已经登录了,那就直接跳转到主页而不是登录页
      // next("/") 表示中转页面到主页
      next('/')
      // 当next(地址)时并没有执行后置路由守卫,所以说也不会再后置守卫中关闭进度条了
      nprogress.done()
    } else {
      // 说明访问的不是登录页,直接放过就好了
      next()
    }
  } else {
    //   没有token
    // 首先先验证白名单中是否包含路径to.path 使用whiteList.indexOf(to.path) > -1判断也可以
    if (whiteList.includes(to.path)) {
      // 直接放行
      next()
    } else {
      // 中转到登录页
      next('/login')
      // 手动关闭进度条
      nprogress.done()
    }
  }
})

/**
 * 后置守卫
 */
router.afterEach(() => {
  nprogress.done()
})

二、Vuex共享用户资料

我们要获取用户的资料并使用Vuex实现用户资料的共享

2.1 需求分析

我们登录之后要获取用户的资料,比如说用户名、用户头像,然后把用户资料放在Vuex中进行共享

流程分析

  • 登录之后获取当前用户的资料

    其实就是有了token之后我们才能知道是谁来获取用户的资料

  • 把获取的用户资料在Vuex中共享

流程图如下所示

image-20240116104515358

假如我们把获取用户资料的代码写在"Dashboard"模块,那首先访问Dashboard也就是主页的时候我们完全可以调用请求/sys/profile得到用户信息的资料,然后放在Vuex中做成数据共享

但是假如用户首先访问的不是Dashboard主页模块,而是“Example”或其他模块的话,就不会调用请求/sys/profile得到用户信息的资料,然后放在Vuex中做成数据共享

所以我们在哪里实现请求/sys/profile获取数据再把数据放在Vuex中显得非常的重要

image-20240119164545073

我们在哪个位置能保证让所有的模块都能调用咱的action方法实现用户资料共享呢

也就是说不管我们切换到哪个页面,我们都能拿到用户共享资料

路由前置守卫,我们在这里编写,只要切换页面就会执行,而且也会判断是否有token

假如说我们的Vuex中有用户资料信息,就不要再重复进行获取了,我们只保证没有用户资料信息的时候进行获取

image-20240119165905283

2.2 路由前置守卫 实现共享用户资料

我们可以在“src/permission.js”中的前置守卫进行实现

image-20240119171434173

2.2.1 vuex - user.js

我们在vuex中的用户模块user.js中获取用户资料信息,并存储在vuex中

image-20240119175900805

// @符号表示根路径
import { getToken, setToken, removeToken } from '@/utils/auth'
import { login, getUserInfo } from '@/api/user'
// State理解为数据源,像极了我们之前学的data,用来存放数据
const state = {
    
  // 存储用户基本资料状态
  userInfo: {}
    .......
}

/**
 * actions似java中的业务逻辑层,对逻辑操作,然后向mutations发送数据,在这个业务逻辑中也可以互相调用
 * actions可以做异步操作
 */
const actions = {
  // 获取用户基本资料
  async getUserInfo(context) {
    // 获取用户基本资料
    const result = await getUserInfo()
    context.commit('setUserInfo', result)
  }
    ......................
}

2.2.2 vuex - getters.js

我们可以通过userId判断是否已经获取了用户资料信息,如果userId存在数据说明已经获取了,之后就不要再获取了

image-20240119175355240

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  // vuex中user模块的userInfo里面的userId
  userId: state => state.user.userInfo.userId,
  avatar: state => state.user.avatar,
  name: state => state.user.name
}
export default getters

2.2.3 api - getUserInfo

image-20240119161023537

// 获取用户基本信息
export function getUserInfo() {
  return request({
    // 请求地址
    url: '/sys/profile',
    // 请求方式
    method: 'GET'
    // 请求参数
    // data: Data
  })
}

2.2.4 permission.js 前置路由器

在此模块的前置路由中编写,逻辑的主要实现会在这里

image-20240119171434173

// @符号表示根路径
import { getToken, setToken, removeToken } from '@/utils/auth'
import { login, getUserInfo } from '@/api/user'
// State理解为数据源,像极了我们之前学的data,用来存放数据
const state = {
  // 这样就算我们给state.token赋值之后,但刷新页面后仍然是null,所以此时永远不能取到我们缓存的token值
  // token: null
  // 从缓存中读取初始值
  token: getToken(),
  // 存储用户基本资料状态
  userInfo: {}
}

// Mutations类似java中的数据层,只对数据进行操作,不对业务操作(比如数据加减乘除)
const mutations = {
  // 修改token
  setToken(state, token) {
    state.token = token
    // console.log(state.token)
    // 将token值同步到缓存
    setToken(token)
  },
  removeToken() {
    // 删除vuex的token
    state.token = null
    // 删除缓存中的token
    removeToken()
  },
  setUserInfo(state, userInfo) {
    state.userInfo = userInfo
  }
}

/**
 * actions似java中的业务逻辑层,对逻辑操作,然后向mutations发送数据,在这个业务逻辑中也可以互相调用
 * actions可以做异步操作
 */
const actions = {
  /**
   * 参数1: context上下文对象,有commit和dispatch方法
   * 参数2:我们要传入的参数
   */
  async login(context, data) {
    // 只有后台返回的success的值为true时才走await,如果是false的话就走了axios响应拦截器中的reject,就不会向下走了
    const token = await login(data)
    // 假设登录成功之后就会返回一个token,我们要将此token实现共享就要存储在state中
    // vuex中想修改state中数据必须通过mutations,不能直接修改state中属性
    // 运行到这里说明登录已经成功了
    context.commit('setToken', token)
  },
  // 获取用户基本资料
  async getUserInfo(context) {
    console.log('打印用户基本信息')
    // 获取用户基本资料
    const result = await getUserInfo()
    console.log(result)
    context.commit('setUserInfo', result)
  }
}

// 对三个变量进行一个默认的导出
export default {
  // 开启命名空间。表示之后调用state/mutations/actions时必须带上模块名称
  namespaced: true,
  state,
  mutations,
  actions
}

其实这个地方我们只写了这一点代码而已,其他的都是之前写过的

image-20240119180647710

2.2.5 测试

随便打开一个页面刷新测试即可,如果有信息说明哪个界面都能获取到用户基本信息,我们做的就是对对对对对对的!!!

image-20240119180915485

2.3 显示用户名称及头像

将Vuex中共享的用户信息显示在顶部组件上

image-20240122152228661

顶部组件也就是下面标红的“navbar”

image-20240122152359457

2.3.1 getters.js暴露用户名称与头像

我们的用户资料信息都存储在vuex中的user.js模块

image-20240122155058524

接下来使用getters便捷访问用户的名称和头像

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  // vuex中user模块的userInfo里面的userId
  userId: state => state.user.userInfo.userId,
    
  avatar: state => state.user.userInfo.staffPhoto, // 用户头像
  name: state => state.user.userInfo.username // 用户名称
}
export default getters

2.3.2 Navbar.vue 顶部组件

image-20240122155750257

  <div class="navbar">
    <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar"/>

    <breadcrumb class="breadcrumb-container"/>

    <div class="right-menu">
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <!--用户头像-->
          <img :src="avatar" class="user-avatar">
          <!--用户名称-->
          <span class="name">{{ name }}</span>
          <!--图标(设置图标,是一个齿轮的样式)-->
          <i class="el-icon-setting"/>
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown">
        ..................
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

Navbar组件引入用户名与用户头像

computed: {
  // 辅助函数,自动引入getters中的属性
  // 引入头像和用户名称
  ...mapGetters([
    'sidebar',
    'avatar',
    'name'
  ])
}

样式

.navbar {

  .right-menu {
    .avatar-container {
      margin-right: 30px;

      .avatar-wrapper {
        margin-top: 5px;
        position: relative;
        // 水平居中
        display: flex;
        align-items: center;
        // 用户名样式
        .name {
          // 用户名称距离右侧一定的距离
          margin-right: 10px;
          // 文字大小
          font-size: 16px;
        }

        // 用户头像样式
        .user-avatar {
          cursor: pointer;
          width: 30px;
          height: 30px;
          // 设置圆角
          border-radius: 50%;
        }

        // 齿轮图标样式
        .el-icon-setting {
          font-size: 20px;
        }

        .el-icon-caret-bottom {
          cursor: pointer;
          position: absolute;
          right: -20px;
          top: 25px;
          font-size: 12px;
        }
      }
    }
  }
}

2.3.3 最终效果图

image-20240122163628153

2.4 处理用户头像为空场景

当用户没有自己设置用户名时,用户的头像就显示用户名的第一个字

image-20240122164012364

首先升级一下vue版本,目的是我们想要支持可选操作符的语法

npm i vue@2.7.0 vue-template-compiler@2.7.0
  <div class="navbar">
    <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar"/>

    <breadcrumb class="breadcrumb-container"/>

    <div class="right-menu">
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <!--如果用户头像不存在的时候执行下面的v-else,显示用户名的第一个字-->
          <!--当name时null或者undefined时name.charAt(0)会报错,但是当在name之后加上“?”后,如果name为null或者undefined,就不会执行charAt(0),也不会报错了-->
          <!-- "name?" 可选操作符,表示验证name是否一定有值。 此语法需要vue2.7.0之后的版本-->
          <span v-else class="username">{{ name?.charAt(0) }}</span>
          <!--如果用户头像不存在的时候执行下面的v-else,显示用户名的第一个字-->
          <span v-else class="username">{{name.charAt(0)}}</span>
          <!--用户名称-->
          <span class="name">{{ name }}</span>
          <!--图标(设置图标,是一个齿轮的样式)-->
          <i class="el-icon-setting"/>
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown">
        ..................
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

对应的样式

.navbar {

  .right-menu {
    .avatar-container {
      margin-right: 30px;

      .avatar-wrapper {
        margin-top: 5px;
        position: relative;
        // 水平居中
        display: flex;
        align-items: center;
 
                  // 用户头像不存在时,默认使用使用用户名的第一个字当做头像
        .username {
          // 垂直居中
          width: 30px;
          height: 30px;
          line-height: 30px;
          // 水平居中
          text-align: center;
          // 背景颜色
          background-color: #04c9be;
          // 字体颜色
          color: #fff;
          // 圆角
          border-radius: 50%;
          // 距离右边距
          margin-right: 4px;
        }
      }
    }
  }
}

效果图

image-20240122225003642

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱布朗熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值