学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/humanResourceIntelligentManagementProject
搭建页面结构
<template>
<div class="container">
<div class="app-container">
<el-button class="btn-add" type="primary" size="mini">添加权限</el-button>
<el-table>
<el-table-column label="名称" />
<el-table-column label="标识" />
<el-table-column label="描述" />
<el-table-column label="操作">
<el-button type="text">添加</el-button>
<el-button type="text">编辑</el-button>
<el-button type="text">删除</el-button>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default {
name: 'Permission'
}
</script>
<style>
.btn-add {
margin: 10px;
}
</style>
获取数据转化树形
import request from '@/utils/request'
/**
*
* 获取权限列表
*
*/
export function getPermissionList () {
return request({
url: '/sys/permission',
method: 'GET'
})
}
<template>
<div class="container">
<div class="app-container">
<el-button class="btn-add" type="primary" size="mini">添加权限</el-button>
<el-table default-expand-all :data="list" row-key="id">
<el-table-column prop="name" label="名称" />
<el-table-column prop="code" label="标识" />
<el-table-column prop="description" label="描述" />
<el-table-column label="操作">
<template v-slot="{ row }">
<el-button v-if="row.type === 1" size="mini" type="text"
>添加</el-button
>
<el-button type="text" size="mini">编辑</el-button>
<el-button type="text" size="mini">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import { getPermissionList } from '@/api/permission'
import { transListToTreeData } from '@/utils/index'
export default {
name: 'Permission',
data () {
return {
list: []
}
},
created () {
this.getPermissionList()
},
methods: {
async getPermissionList () {
this.list = transListToTreeData(await getPermissionList(), 0)
console.log(this.list)
}
}
}
</script>
<style>
.btn-add {
margin: 10px;
}
</style>
作业 ( 基于权限接口和线上效果完成 权限点的新增- 删除- 编辑 )
本次作业我封装了一个添加组件
子组件:
<template>
<el-dialog :title="showTitle" :visible="showDialog" @close="close">
<!-- 放置弹层内容 -->
<el-form ref="addDept" label-width="120px" :model="formData" :rules="rules">
<el-form-item label="权限名称" prop="name">
<el-input v-model="formData.name" style="width: 80%" size="mini" />
</el-form-item>
<el-form-item label="权限标识" prop="code">
<el-input v-model="formData.code" style="width: 80%" size="mini" />
</el-form-item>
<el-form-item label="权限描述" prop="description">
<el-input
v-model="formData.description"
style="width: 80%"
size="mini"
/>
</el-form-item>
<!-- 如果不需要校验,就不需要写prop属性 -->
<!-- 重置表单数据,需要prop属性 -->
<el-form-item label="启用" prop="enVisible">
<el-switch
v-model="formData.enVisible"
:active-value="1"
:inactive-value="0"
size="mini"
/>
</el-form-item>
<el-form-item>
<el-row type="flex" justify="center">
<el-col :span="12">
<el-button type="primary" size="mini" @click="btnOk"
>确定</el-button
>
<el-button size="mini" @click="close">取消</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script>
import {
addPermission,
getPermissionDetail,
editPermission
} from '@/api/permission'
export default {
props: {
showDialog: {
type: Boolean,
default: false
},
pid: {
// 获取主组件传递的id,根据这个id进行第二级权限的添加
type: Number,
default: null
},
sonId: {
// 获取主组件传递的id,根据这个id进行权限的编辑
type: Number,
default: null
}
},
data () {
return {
list: [],
formData: {
name: '', // 权限点名字
code: '', // 权限点标识
description: '', // 权限点描述
enVisible: '', // 权限点开启状态
type: null, // 权限点类型
pid: null // 权限点父级id
},
rules: {
name: [
{ required: true, message: '权限点名字不能为空', trigger: 'blur' }
], // 权限点名字
code: [
{ required: true, message: '权限点标识不能为空', trigger: 'blur' }
] // 权限点标识
}
}
},
computed: {
// 标题不是表单所以不能绑定v-model事件,使用计算属性进行更新
showTitle () {
return this.sonId ? '编辑权限点' : '新增权限点'
}
},
watch: {
sonId: function () {
this.getPermissionDetail()
}
},
methods: {
close () {
// 修改父组件的值,子传父
// .sync会自动监听update:showDialog事件
// resetFields只能重置在模板中绑定的数据
this.formData = {
name: '', // 权限点名字
code: '', // 权限点标识
description: '', // 权限点描述
enVisible: '0', // 权限点开启状态
type: null, // 权限点类型
pid: null // 权限点父级id
}
this.$refs.addDept.resetFields() // 重置表单
this.$emit('clearId')
this.$emit('update:showDialog', false)
},
btnOk () {
this.$refs.addDept.validate(async isOk => {
if (isOk) {
let msg = '新增'
if (this.sonId == null) {
// 如果父组件传id回来了,应该是二级组件
if (this.pid != null) {
this.formData.pid = this.pid
}
// 判断是否是一级添加
if (this.formData.pid == null) {
this.formData.type = 1
this.formData.pid = 0
await addPermission(this.formData)
console.log('一级添加')
} else {
this.formData.type = 2
await addPermission(this.formData)
console.log('二级添加')
}
} else {
msg = '修改'
await editPermission(this.formData)
}
// 通知父组件更新
this.$emit('updatePermission')
// 提示信息
this.$message.success(`${msg}权限点成功`)
this.close()
}
})
},
async getPermissionDetail () {
if (this.sonId != null) {
this.formData = await getPermissionDetail(this.sonId)
}
} // 获取权限点的详情
}
}
</script>
<style></style>
api接口:
import request from '@/utils/request'
/**
*
* 获取权限列表
*
*/
export function getPermissionList () {
return request({
url: '/sys/permission',
method: 'GET'
})
}
/**
*
* 删除-权限点
*
*/
export function delPermission (id) {
return request({
url: `/sys/permission/${id}`,
method: 'DELETE'
})
}
/**
*
* 新增-权限点
*
*/
export function addPermission (data) {
return request({
url: '/sys/permission',
method: 'POST',
data
})
}
/**
*
* 获取-权限点详情
*
*/
export function getPermissionDetail (id) {
return request({
url: `/sys/permission/${id}`,
method: 'GET'
})
}
/**
*
* 修改-权限点详情
*
*/
export function editPermission (data) {
return request({
url: `/sys/permission/${data.id}`,
method: 'PUT',
data
})
}
父组件:
<template>
<div class="container">
<div class="app-container">
<el-button
class="btn-add"
type="primary"
size="mini"
@click="showDialog = true"
>添加权限</el-button
>
<el-table default-expand-all :data="list" row-key="id">
<el-table-column prop="name" label="名称" />
<el-table-column prop="code" label="标识" />
<el-table-column prop="description" label="描述" />
<el-table-column label="操作">
<template v-slot="{ row }">
<el-button
v-if="row.type === 1"
size="mini"
type="text"
@click="addPermissionSecond(row.id)"
>添加</el-button
>
<el-button type="text" size="mini" @click="editPermission(row.id)"
>编辑</el-button
>
<el-button type="text" size="mini" @click="delPermission(row.id)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<addPermission
:son-id="id"
:show-dialog.sync="showDialog"
:pid="fatherId"
@updatePermission="getPermissionList"
@clearId="clearId"
/>
</div>
</template>
<script>
import { getPermissionList, delPermission } from '@/api/permission'
import { transListToTreeData } from '@/utils/index'
import addPermission from './components/add-permission.vue'
export default {
name: 'Permission',
components: { addPermission },
data () {
return {
list: [],
showDialog: false, // 控制弹层的显隐
fatherId: null, // 传递父节点的id给子组件用于增加二级权限点
id: null // 传递父节点的id给子组件用于编辑权限点
}
},
created () {
this.getPermissionList()
},
methods: {
async getPermissionList () {
this.list = transListToTreeData(await getPermissionList(), 0)
},
// 删除权限点
delPermission (id) {
this.$confirm('此操作将永久删除该部门, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
await delPermission(id)
this.$message({
type: 'success',
message: '删除成功!'
})
this.getPermissionList()
})
.catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
},
// 增加二级权限点
addPermissionSecond (id) {
this.showDialog = true
this.fatherId = id
},
// 编辑权限点
editPermission (id) {
this.showDialog = true
this.id = id
},
clearId () {
this.id = null
}
}
}
</script>
<style>
.btn-add {
margin: 10px;
}
</style>
权限概念
分配过程
员工分配角色-弹出层
/**
*
* 获取-已启用的角色列表
*
*/
export function getEnableRoleList () {
return request({
url: '/sys/role/list/enabled',
method: 'GET'
})
}
showRoleDialog: false, // 控制角色弹层的显隐
roleList: [], // 接收角色列表
roleIds: [], // 用于多选框数据双向绑定,存储选中的id
// 点击角色按钮弹出层
async btnRole () {
this.roleList = await getEnableRoleList()
this.showRoleDialog = true
},
<!-- 放置角色弹层 -->
<el-dialog :visible.sync="showRoleDialog" title="分配角色">
<!-- 内容 -->
<el-checkbox-group v-model="roleIds">
<!-- 要执行checkbox的存储值 item.id -->
<el-checkbox v-for="item in roleList" :key="item.id" :label="item.id">
{{ item.name }}</el-checkbox
>
</el-checkbox-group>
</el-dialog>
<el-button size="mini" type="text" @click="btnRole">角色</el-button>
员工分配角色-回显数据并提交
/**
*
* 获取-员工-基本信息
*
*/
export function getEmployeeDetail (id) {
return request({
url: `/sys/user/${id}`,
method: 'GET'
})
}
/**
*
* 获取-已启用的角色列表
*
*/
export function assignRole (data) {
return request({
url: '/sys/user/assignRoles',
method: 'PUT',
data
})
}
// 点击角色按钮弹出层
async btnRole (id) {
this.roleList = await getEnableRoleList()
// 记录当前点击的id 因为后边 确定取消要存取给对应的用户
this.currentUserId = id
const { roleIds } = await getEmployeeDetail(id)
this.roleIds = roleIds
this.showRoleDialog = true
},
// 点击角色的确定
async btnRoleOk () {
await assignRole({
id: this.currentUserId,
roleIds: this.roleIds
})
this.$message.success('分配用户角色成功')
this.showRoleDialog = false
}
<!-- 放置角色弹层 -->
<el-dialog :visible.sync="showRoleDialog" title="分配角色">
<!-- 内容 -->
<el-checkbox-group v-model="roleIds">
<!-- 要执行checkbox的存储值 item.id -->
<el-checkbox v-for="item in roleList" :key="item.id" :label="item.id">
{{ item.name }}</el-checkbox
>
</el-checkbox-group>
<el-row slot="footer" type="flex" justify="center">
<el-col :span="6">
<el-button
type="primary"
size="mini"
justify="center"
@click="btnRoleOk"
>确定</el-button
>
<el-button size="mini" @click="showRoleDialog = false"
>取消</el-button
>
</el-col>
</el-row>
</el-dialog>
<el-button size="mini" type="text" @click="btnRole(row.id)"
>角色</el-button
>
给角色分配权限-弹出层
showPermissionDialog: false, // 控制权限弹层的显隐
permissionData: [] // 接收权限树数据
<el-button size="mini" type="text" @click="btnPermission"
>分配权限</el-button>
<!-- 放置权限弹层 -->
<el-dialog :visible.sync="showPermissionDialog" title="分配权限">
<!-- 内容 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
show-checkbox
default-expand-all=""
/>
</el-dialog>
// 分配权限
async btnPermission () {
this.showPermissionDialog = true
this.permissionData = transListToTreeData(await getPermissionList(), 0)
}
角色分配权限-显示已有权限数据
/**
*
* 获取-角色详情
*
*/
export function getRoleDetail (id) {
return request({
url: `/sys/role/${id}`,
method: 'GET'
})
}
// 分配权限
async btnPermission (id) {
this.currentRoleId = id
const { permIds } = await getRoleDetail(id)
this.permIds = permIds
this.permissionData = transListToTreeData(await getPermissionList(), 0)
this.showPermissionDialog = true
}
<!-- 内容 -->
<el-tree
node-key="id"
:data="permissionData"
:props="{ label: 'name' }"
show-checkbox
default-expand-all
:default-checked-keys="permIds"
/>
角色分配权限-确定提交
/**
*
* 分配权限-角色
*
*/
export function assignPrem (data) {
return request({
url: '/sys/role/assignPrem',
method: 'PUT',
data
})
}
<el-row slot="footer" type="flex" justify="center">
<el-col :span="6">
<el-button type="primary" size="mini" @click="btnPermissionOk"
>确定</el-button
>
<el-button size="mini" @click="showPermissionDialog = false"
>取消</el-button
>
</el-col>
</el-row>
async btnPermissionOk () {
await assignPrem({
id: this.currentRoleId,
permIds: this.$refs.permTree.getCheckedKeys()
})
this.$message.success('角色分配权限成功')
this.showPermissionDialog = false
}
拆分静态路由和动态路由
// 静态路由
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}
]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
// 动态路由
export const asyncRoutes = [
department,
role,
employee,
permission,
attendance,
approval,
salary,
social
]
const createRouter = () =>
new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes // 默认引入静态路由
})
根据用户权限添加动态路由
permission完整代码
router.beforeEach(async (to, from, next) => {
nprogress.start() // 开启进度条
// 判断是否有token
if (store.getters.token) {
// 如果有token,则判断是否是登录页
if (to.path === '/login') {
// 如果是则跳转到主页
next('/')
// next有地址的话并没有执行后置守卫,所以要手动调用一下进度条关闭
nprogress.done()
} else {
// 如果不是则放过
// 判断是否获取过资料
if (!store.getters.userId) {
// 如果没有则获取资料
const { roles } = await store.dispatch('user/getUserInfo')
const filterRoutes = asyncRoutes.filter(item => {
return roles.menus.includes(item.name)
}) // 筛选后的路由
router.addRoutes([
...filterRoutes,
{ path: '*', redirect: '/404', hidden: true }
]) // 添加动态路由信息到路由表
// router添加动态路由之后 需要转发一下
next(to.path) // 目的是让路由拥有信息 router的已知缺陷
} else {
next() // 放过
}
}
} else {
// 判断是否在白名单里面
if (whiteList.includes(to.path)) {
// 如果在就放过
next()
} else {
// 如果不在,就跳转到登录
next('/login')
nprogress.done()
}
}
})
// 静态路由
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}
]
}
// 404 page must be placed at the end !!!
]
// 动态路由
export const asyncRoutes = [
department,
role,
employee,
permission,
attendance,
approval,
salary,
social
]
const createRouter = () =>
new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes // 默认引入静态路由
})
// 获取用户的基本资料
async getUserInfo (context) {
const result = await getUserInfo()
context.commit('setUserInfo', result)
return result // 返回数据
}
这里以approval路由为例,其他路由都要添加name属性
import layout from '@/layout'
export default {
path: '/approval',
name: 'approval',
component: layout,
children: [
{
path: '',
name: 'approval',
component: () => import('@/views/approval'),
meta: {
title: '审批',
icon: 'tree-table'
}
}
]
}
根据权限显示左侧菜单
import { constantRoutes } from '@/router'
// 存放数据
const state = {
routes: constantRoutes // 存储路由信息,默认存储的是静态路由
}
// 修改数据
const mutations = {
setRoutes (state, newRoutes) {
state.routes = [...constantRoutes, ...newRoutes] // 静态路由+动态路由
}
}
const getters = {
routes: state => state.user.routes
}
// getters编辑访问
export default getters
const { roles } = await store.dispatch('user/getUserInfo')
const filterRoutes = asyncRoutes.filter(item => {
return roles.menus.includes(item.name)
}) // 筛选后的路由
store.commit('user/setRoutes', filterRoutes)
router.addRoutes([
...filterRoutes,
{ path: '*', redirect: '/404', hidden: true }
]) // 添加动态路由信息到路由表
computed: {
...mapGetters(['sidebar', 'routes']),
// 路由信息的计算属性
// routes () {
// // 当前路由的所有信息
// return this.$router.options.routes
// },
}
退出登录重置路由
import { resetRouter } from '@/router'
// 异步操作
const actions = {
// 退出登录
logout (context) {
// 1.删除token
context.commit('removeToken')
// 2.删除用户信息
context.commit('setUserInfo', {})
// 重置路由
resetRouter()
}
}
功能权限-按钮权限标识
自定义指令应用功能权限
代码层面:
// 注册自定义指令,控制功能权限
Vue.directive('permission', {
// 会在指令作用的元素插入dom之后执行
inserted (el, binding) {
// el是当前指令作用的dom元素的对象
// binding是v-permission="表达式"的信息
const points = store.state.user.userInfo?.roles?.points || []
// 判断当前登录用户(数组)是否包含权限点,不存在就要将对应的按钮删除或禁用
if (!points.includes(binding.value)) {
// 删除
el.remove()
// 禁用
// el.disabled = true
}
}
})
<el-button
v-permission="add - employee"
size="mini"
type="primary"
@click="$router.push('/employee/detail')"
>添加员工</el-button
>