权限管理是后台管理系统的核心功能,要给不同工作岗位的用户分配不同的操作权限,就需要进行权限管理
功能说明
权限管理内部划分为:
- 菜单权限
- 资源权限
- 角色
菜单权限
控制登录到后台的用户能够访问到哪些后台菜单页面,比如负责广告的人员只能看到广告管理,课程人员只能看到课程管理,就需要进行不同的菜单权限分配
资源权限
资源对应的是接口,资源权限用于控制用户能够操作哪些接口功能,比如分配资源权限的时候没有禁用用户权限,指的是没有操作这个接口的权限。资源权限与菜单权限不冲突,如果有的用户能够看到用户管理页面,也可以添加用户(有权限操作新增用户接口),但是没法进行禁用用户操作(无禁用用户的接口权限)
角色
代表了菜单权限和资源权限的一种组合方式,比如我设置了多个用户需要相同的菜单权限和资源权限, 就可以将这些权限组合起来,设置为角色,再将角色分配给用户简化操作
在项目中,不会直接对某个用户进行菜单权限或者资源权限的分配,而是提前根据岗位清空设定不同的角色,再将角色分配给用户就可以了
功能关系:
用户需要分配角色,角色需要分配菜单权限和资源权限
由于功能之间存在依赖,我们先从菜单权限和资源权限功能开始制作,最后再依次完成角色和用户的功能
菜单管理
添加菜单,整体布局
使用Element的Card卡片
添加到views/menu/index.vue,将标题区域更改为添加菜单按钮,添加后跳转到菜单组件
在menu目录下创建menuCreate.vue,并且创建初始结构
在路由表中添加上去
// router/index.js
...
{
path: '/menu/menuCreate',
name: 'menu-create',
component: () => import(/* webpackChunkName: 'menu-create' */'@/views/menu/menuCreate')
}
]
下面通过Element的Card套Form的方式给menu-create布局
- 将Card头部设置为标题
- 将Card内容替换为Form
数据绑定的名称可以通过接口来设置,请求的接口为:菜单管理->保存或者新增菜单
由于接口的请求参数需要为application/json,使用postman测试的时候要进行对应的选择
将数据声明在data中,并且绑定到视图上
完成
上级菜单的处理
上级菜单数据需要请求接口才能得到,并且要渲染到模板中
用于获取菜单的接口有两个
- 获取所有的菜单
- 获取编辑菜单页面信息
通过功能实例观察可以得知,菜单分为一级和二级,二级菜单无法作用“上一级菜单”来使用,所以我们应该使用列举的第二个菜单来操作
接口使用时需要传请求参数id,添加传入id=-1,编辑传入相对应id,区别为添加操作的菜单信息menuInfo为null
我们需要使用的是parentMenuList字段信息,其中的每个元素都为一级菜单,元素如果有subMenuList就说明他存在有子菜单(二级)
接下来是设置接口的请求方法
import request from '@/utils/request'
// 获取编辑菜单页面信息
export const getEditMenuInfo = (id = -1) => {
return request({
method: 'GET',
// url: `/boss/menu/getEditMenuInfo?id=${id}`
// 上面是模板字符串写法
// 下面是GET请求时,使用params,如果请求方式是POST,那就是data
url: '/boss/menu/getEditMenuInfo',
params: {
id
}
})
}
menuCreate.vue中请求数据
// menu-create.vue
...
...
添加无上级菜单选项(如果你添加的是一级菜单的话就这么选)
// menu-create.vue
...
...
添加菜单提交
首先要将用于添加菜单的请求功能封装到services/menu.js模块中,由于文件不存在,先进行创建
// services/menu.js
import request from '@/utils/request'
// 添加菜单请求功能
export const createOrUpdateMenu = data => {
return request({
method: 'POST',
url: '/boss/menu/saveOrUpdate',
// 当前请求参数为 application/json,无需通过 qs 处理
data
})
}
在createMenu.vue中引入并在在点击提交按钮时发送请求,顺便检验一下是否成功了,成功了要记得提示和跳转
import { getEditMenuInfo, creatOrUpdateMenu } from '@/services/menu'
...
async onSubmit () {
// 1.先进行一个表单校验
// 2.发送请求
const { data } = await creatOrUpdateMenu(this.form)
if (data.code === '000000') {
this.$message.success('提交成功')
this.$router.push({
name: 'menu'
})
}
},
...
菜单列表展示
展示数据相关列表,使用Element的Table表格组件进行处理,并且根据我们的项目功能,修改模板表格的内容
// menu/index.vue
封装接口请求功能
// services/menu.js
...
// 获取所有菜单
export const getAllmenu = () => {
return request({
method: 'GET',
url: '/boss/menu/getAll'
})
}
引入并且请求数据,请求成功保存到data中
数据展示
// menu/index.vue
操作部分的内容需要对Table进行自定义
注意:
- Element的Table组件使用的
slot-scope="scope"
是Vue.js在2.6版本之前的作用域插槽语法现在已经被废弃,现行版本语法中应该使用v-slot
指令进行作用域插槽的设置 - scope是作用域插槽中接收的,由组件内部提供的数据,可以自行命名并且在template中使用
- $index代表的是索引
- row代表当前行信息(数据)
是否使用取决于我们的需求
编辑
删除
...
删除菜单
点击删除的时候,提示用户确认,并且使用当前行数据信息进行删除请求
// menu/index.vue
...
handleDelete () {
// 确认提示(参数3的具体配置不需要可省略)
this.$confirm('确认删除吗?', '删除提示')
.then(() => {
// 发送删除请求
})
.catch(() => {
// 取消提示
this.$message.info('已取消删除')
})
}
...
删除接口:地址,使用Postman测试没问题之后投入使用
// services/menu.js
...
// 删除指定菜单
export const deleteMenu = id => {
return request({
method: 'DELETE',
url: `/boss/menu/${id}`
})
}
// menu/index.vue
...
删除
...
编辑菜单
布局处理
观察项目的时候发现,添加菜单和编辑菜单的组件结构几乎是一模一样的,可以封装为组件进行复用
- 创建menu/components/CreateOrEdit.vue
- 将menu/menuCreate.vue内容复制到CreateOrEdit.vue中
- 更改name
- create-or-edit组件通过props接收父组件的数据isEdit来判断展示哪种结构
- 标题判断处理
// menu/components/create-or-edit.vue
...
{{ isEdit ? '编辑菜单' : '添加菜单' }}
...
去除menuCreate.vue中的多余内容
- 根元素内的所有结构
- data,created,methods中的内容
- 引入组件CreateOrEdit
创建menuEdit.vue组件,设置内容
将菜单编辑添加到路由表中
- 由于编辑为某个菜单的编辑,应设置动态路由展示菜单项id
// router/index.js
...
{
path: '/menu/:id/edit',
name: 'menu-edit',
component: () => import(/* webpackChunkName: 'menu-edit' */'@/views/menu/edit')
}
]
...
给menu/index.vue中的编辑按钮设置点击后的路由跳转
// menu/index.vue
...
编辑
...
逻辑处理
开始之前补充一点,组件CreateOrEdit.vue中的重置按钮,应当设置一个全部清空重置的点击事件
设置完毕,让我们开始逻辑处理的部分吧:
编辑功能中,CreateOrEdit的表单不需要重置按钮,通过v-if判断
重置
编辑时,将要编辑的菜单项信息展示在表单中
- 之前操作中分析过,getEditMenuInfo接口在编辑功能时可以获取到菜单信息,添加时为空
- 这里需要将动态路由的参数传入,并给添加功能设置默认值-1
- 将响应数据的menuInfo赋值给data中的data.form就可以了(属性名是对应的,没有差错)
async loadMenuInfo () {
// 检测是否存在路由参数id,并且进行对应处理
const id = this.$route.params.id || -1
// 请求菜单数据,上级菜单数据(一级)
const { data } = await getEditMenuInfo(id)
if (data.code === '000000') {
// 上级菜单数据保存,进行数据绑定
this.parentMenuList = data.data.parentMenuList
// 检测是否存在菜单数据menuinfo,有的话就保存到form
if (data.data.menuInfo) {
this.form = data.data.menuInfo
}
}
},
由于添加和编辑时同一个接口,区别在于编辑时是否多了参数id,由于提交时传入为form数据,编辑提交时就会自动包含id,所以提交操作就不需要处理了