iHRM人力资源 - 主页权限认证、主页内容展示
2.IHRM人力资源 - 登录-CSDN博客
文章目录
- iHRM人力资源 - 主页权限认证、主页内容展示
- 一、主页权限认证
- 1.1 主页权限认证分析
- 1.2 主页权限认证 - permission.js
- 1.2.1 进度条部分
- 1.2.2 token 认证
- 二、Vuex共享用户资料
- 2.1 需求分析
- 2.2 路由前置守卫 实现共享用户资料
- 2.2.1 vuex - user.js
- 2.2.2 vuex - getters.js
- 2.2.3 api - getUserInfo
- 2.2.4 permission.js 前置路由器
- 2.2.5 测试
- 2.3 显示用户名称及头像
- 2.3.1 getters.js暴露用户名称与头像
- 2.3.2 Navbar.vue 顶部组件
- 2.3.3 最终效果图
- 2.4 处理用户头像为空场景
一、主页权限认证
1.1 主页权限认证分析
主页权限认证其实就是验证有没有token
token所在位置
访问主页时
-
有token
放过请求,可以跳转到主页
-
无token
拦截访问主页的请求并跳转到登录页让用户进行登录
访问登录页时
-
有token
直接跳转到主页
-
无token
正常访问登录页面
我们可以看一下下面permission.js代码
将代码收起来看很简洁,一个前置路由守卫beforeEach,一个后置路由守卫router.afterEach
Vue - 路由守卫
然后再main.js文件中将路由守卫配置引入
1.2 主页权限认证 - permission.js
主页权限验证就参考下图来就可以
1.2.1 进度条部分
如下所示是一个进度条的插件,要想使用这个插件,直接引入到我们package.json文件中即可
进度条的效果如下所示
// 导入路由实例
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中共享
流程图如下所示:
假如我们把获取用户资料的代码写在"Dashboard"模块,那首先访问Dashboard也就是主页的时候我们完全可以调用请求/sys/profile得到用户信息的资料,然后放在Vuex中做成数据共享
但是假如用户首先访问的不是Dashboard主页模块,而是“Example”或其他模块的话,就不会调用请求/sys/profile得到用户信息的资料,然后放在Vuex中做成数据共享
所以我们在哪里实现请求/sys/profile获取数据再把数据放在Vuex中显得非常的重要
我们在哪个位置能保证让所有的模块都能调用咱的action方法实现用户资料共享呢?
也就是说不管我们切换到哪个页面,我们都能拿到用户共享资料
路由前置守卫,我们在这里编写,只要切换页面就会执行,而且也会判断是否有token
假如说我们的Vuex中有用户资料信息,就不要再重复进行获取了,我们只保证没有用户资料信息的时候进行获取
2.2 路由前置守卫 实现共享用户资料
我们可以在“src/permission.js”中的前置守卫进行实现
2.2.1 vuex - user.js
我们在vuex中的用户模块user.js中获取用户资料信息,并存储在vuex中
// @符号表示根路径
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存在数据说明已经获取了,之后就不要再获取了
const getters = {sidebar: state => state.app.sidebar,device: state => state.app.device,token: state => state.user.token,// vuex中user模块的userInfo里面的userIduserId: state => state.user.userInfo.userId,avatar: state => state.user.avatar,name: state => state.user.name
}
export default getters
2.2.3 api - getUserInfo
// 获取用户基本信息
export function getUserInfo() {return request({// 请求地址url: '/sys/profile',// 请求方式method: 'GET'// 请求参数// data: Data})
}
2.2.4 permission.js 前置路由器
在此模块的前置路由中编写,逻辑的主要实现会在这里
// @符号表示根路径
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 = {// 修改tokensetToken(state, token) {state.token = token// console.log(state.token)// 将token值同步到缓存setToken(token)},removeToken() {// 删除vuex的tokenstate.token = null// 删除缓存中的tokenremoveToken()},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
}
其实这个地方我们只写了这一点代码而已,其他的都是之前写过的
2.2.5 测试
随便打开一个页面刷新测试即可,如果有信息说明哪个界面都能获取到用户基本信息,我们做的就是对对对对对对的!!!
2.3 显示用户名称及头像
将Vuex中共享的用户信息显示在顶部组件上
顶部组件也就是下面标红的“navbar”
2.3.1 getters.js暴露用户名称与头像
我们的用户资料信息都存储在vuex中的user.js模块
接下来使用getters便捷访问用户的名称和头像
const getters = {sidebar: state => state.app.sidebar,device: state => state.app.device,token: state => state.user.token,// vuex中user模块的userInfo里面的userIduserId: 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 顶部组件
<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 最终效果图
2.4 处理用户头像为空场景
当用户没有自己设置用户名时,用户的头像就显示用户名的第一个字
首先升级一下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;}}}}
}
效果图