学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/humanResourceIntelligentManagementProject
觉得有帮助的同学,可以点心心支持一下哈
主页权限验证(permission.js)
import router from '@/router'
import nprogress from 'nprogress' // 进度条
import 'nprogress/nprogress.css' // 进度条样式
import store from '@/store'
/**
* 前置守卫
*
*/
// 白名单
const whiteList = ['/login', '/404']
router.beforeEach((to, from, next) => {
nprogress.start() // 开启进度条
if (store.getters.token) {
// 存在token
if (to.path === '/login') {
// 跳转到主页
next('/') // 中转到主页
// next有地址的话并没有执行后置守卫
nprogress.done()
} else {
next() // 放过
}
} else {
// 没有token
if (whiteList.includes(to.path)) {
next() // 放过
} else {
next('/login') // 中转到登录页
nprogress.done()
}
}
})
/**
* 后置守卫
*
*/
router.afterEach(() => {
console.log(123)
nprogress.done() // 关闭进度条
})
获取用户资料在Vuex中共享
获取资料action的调用位置
1.封装API请求
export function getUserInfo () {
// request执行之后会得到promise对象(再通过使用async和await可以获取结果)
return request({
url: '/sys/profile',
method: 'GET'
})
}
2.Vuex应用
// 存放数据
const state = {
token: getToken(), // 从缓存中读取初始值
userInfo: {} // 存储用户基本资料状态
}
setUserInfo (state, userInfo) {
state.userInfo = userInfo
}
// 获取用户的基本资料
async getUserInfo (context) {
const result = await getUserInfo()
context.commit('setUserInfo', result)
}
3.getters声明userId
userId: state => state.user.userInfo.userId, // 取用户id,用于判断页面是否获取过资料
4.权限拦截处调用action
// 如果不是则放过
// 判断是否获取过资料
if (!store.getters.userId) {
// 如果没有则获取资料
await store.dispatch('user/getUserInfo')
}
next()
显示用户头像和用户名
1.Vuex使用getters暴露属性
avatar: state => state.user.userInfo.staffPhoto, // 用户头像
name: state => state.user.userInfo.username // 用户姓名
2.Navbar组件引入
// 辅助函数,自动引入getters中的属性
// 引入用户头像和名称
...mapGetters(['sidebar', 'avatar', 'name'])
},
3.Navbar设置
<!-- 用户头像 -->
<img :src="avatar" class="user-avatar" />
<!-- 用户名称 -->
<span class="name">{{ name }}</span>
<!-- 图标 -->
<i class="el-icon-setting" />
4.设置样式
.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;
}
处理用户头像为空的场景
实例代码
<!-- 用户头像 -->
<img v-if="avatar" :src="avatar" class="user-avatar" />
<span v-else class="username">{{ name?.charAt(0) }}</span>
.username {
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
background-color: #04c9be;
color: #fff;
border-radius: 50%;
margin-right: 4px;
}
npm i vue@2.7.0 vue-template-compiler@2.7.0
处理token失效
实例代码
// 判断token值是否等于401
if (error.response.status === 401) {
Message({ type: 'warning', message: '登录超时了' })
// token超时了
await store.dispatch('user/logout') // 退出登录action
router.push('/login') // 跳到登录页
return Promise.reject(error)
}
// 退出登录
logout (context) {
// 1.删除token
context.commit('removeToken')
// 2.删除用户信息
context.commit('setUserInfo', {})
}
调整下拉菜单,实现退出登录
<!-- native事件修饰符 -->
<!-- 注册组件的根元素的原生事件 -->
<el-dropdown-item @click.native="logout">
<span style="display: block">退出登录</span>
</el-dropdown-item>
async logout () {
// 调用退出登录的action
await this.$store.dispatch('user/logout')
this.$router.push('/login')
}
修改密码功能实现
修改密码-弹出层
<!-- prevent阻止默认事件 -->
<a target="_blank" @click.prevent="updatePassword">
<el-dropdown-item>修改密码</el-dropdown-item>
</a>
<!-- 放置dialog -->
<!-- sync可以接收子组件传过来的事件和值 -->
<el-dialog width="500px" title="修改密码" :visible.sync="showDialog">
<!-- 放置表单 -->
</el-dialog>
showDialog: true // 控制弹层的显示和隐藏
updatePassword () {
this.showDialog = true // 显示弹层
}
修改密码-表单结构
<!-- 放置表单 -->
<el-form label-width="120px">
<el-form-item label="旧密码">
<el-input type="password" size="small" />
</el-form-item>
<el-form-item label="新密码">
<el-input type="password" size="small" />
</el-form-item>
<el-form-item label="重复密码">
<el-input type="password" size="small" />
</el-form-item>
<el-form-item>
<el-button size="mini" type="primary">确认密码</el-button>
<el-button size="mini">取消</el-button>
</el-form-item>
</el-form>
修改密码-表单校验
rules: {
// 旧密码
oldPassword: [
{ required: true, message: '旧密码不能为空', trigger: 'blur' }
], // 新密码
newPassword: [
{ required: true, message: '新密码不能为空', trigger: 'blur' },
{
trigger: 'blur',
min: 6,
max: 16,
message: '新密码的长度6-16位之间'
}
], // 确认密码字段
confirmPassword: [
{ required: true, message: '重复密码不能为空', trigger: 'blur' },
{
trigger: 'blur',
validator: (rule, value, callback) => {
if (this.passForm.newPassword === value) {
callback()
} else {
callback(new Error('重复密码和新密码输入不一致'))
}
}
}
]
}
修改密码-确定和取消
btnOk () {
this.$refs.passForm.validate(async isOk => {
if (isOk) {
// 调用接口
await updatePassword(this.passForm)
this.$message.success('修改密码成功')
this.btnCancel()
}
})
},
btnCancel () {
// 成功之后重置表单
this.$refs.passForm.resetFields()
// 关闭弹层
this.showDialog = false
}
@close="btnCancel"
/**
* 更新密码
*/
export function updatePassword (data) {
return request({
url: '/sys/user/updatePass',
method: 'PUT',
data
})
}
清理组件和路由并创建项目所需组件和路由
删除多余的组件和路由
业务模块
实例代码(department)
<template>
<div class="container">
<div class="app-container">
组织架构
</div>
</div>
</template>
<script>
export default {
name: 'Department'
}
</script>
import layout from '@/layout'
export default {
// 路由信息
path: '/department',
component: layout, // 一级路由
children: [{
path: '', // 二级路由地址为空时 表示 /department 显示一级路由 + 二级路由
component: () => import('@/views/department'),
name: 'department', // 可以用来跳转 也可以标记路由
meta: {
// 路由元信息 存储数据的
icon: 'tree', // 图标
title: '组织' // 标题
}
}]
}
import Layout from '@/layout'
import department from './modules/department'
import approval from './modules/approval'
import attendance from './modules/attendance'
import employee from './modules/employee'
import permission from './modules/permission'
import role from './modules/role'
import salary from './modules/salary'
import social from './modules/social'
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}
]
},
department,
role,
employee,
permission,
attendance,
approval,
salary,
social,
解析左侧菜单
显示项目logo
<router-link key="collapse" class="sidebar-logo-link" to="/">
<img src="@/assets/common/logo.png" class="sidebar-logo" />
</router-link>
& .sidebar-logo {
width: 140px;
vertical-align: middle;
margin-right: 12px;
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
width: 32px;
height: 32px;
}
}
\src\settings.js
sidebarLogo: true