当前位置: 首页 > news >正文

前端权限流程(基于rbac实现思想)

1. 权限控制

1.1. 实现思想

基于rbac权限控制思想实现,给用户分配角色,给角色分配权限

给用户分配角色业务

注意:上方图片是个示例图,代表给用户分配职位(角色),页面中使用了Element-plus的el-         checkbox组件和el-checkbox-group组件

静态结构(不完整)

          <el-form-item label="用户姓名">
            <el-input v-model="userParams.username" :disabled="true"></el-input>
          </el-form-item>
          <el-form-item label="职位列表">
            <el-checkbox>
              全选
            </el-checkbox>
            <!-- 显示职位的的复选框 -->
            <el-checkbox-group>
              <el-checkbox
                v-for="(role, index) in 10"
                :key="index"
                :label="index"
              >
                {{ index }}
              </el-checkbox>
            </el-checkbox-group>
          </el-form-item>

获取&&存储数据

首先先获取全部的职位(角色)的数据与当前用户已有的职位(角色)的数据,分别进行保存

//收集顶部复选框全选数据
let checkAll = ref<boolean>(false)
//控制顶部全选复选框不确定的样式
let isIndeterminate = ref<boolean>(true)
//存储全部职位的数据
let allRole = ref<AllRole>([])
//当前用户已有的职位
let userRole = ref<AllRole>([])
//分配角色按钮的回调
const setRole = async (row: User) => {
  //存储已有的用户信息
  Object.assign(userParams, row)
  //获取全部的职位的数据与当前用户已有的职位的数据
  let result: AllRoleResponseData = await reqAllRole(userParams.id as number)
  if (result.code == 200) {
    //存储全部的职位
    allRole.value = result.data.allRolesList
    //存储当前用户已有的职位
    userRole.value = result.data.assignRoles
    //抽屉显示出来
    drawer1.value = true
  }
}

展示数据

<!-- 抽屉结构:用户某一个已有的账号进行职位分配 -->
    <el-drawer v-model="drawer1">
      <template #header>
        <h4>分配角色(职位)</h4>
      </template>
      <template #default>
        <el-form>
          <el-form-item label="用户姓名">
            <el-input v-model="userParams.username" :disabled="true"></el-input>
          </el-form-item>
          <el-form-item label="职位列表">
            <el-checkbox
              @change="handleCheckAllChange"
              v-model="checkAll"
              :indeterminate="isIndeterminate"
            >
              全选
            </el-checkbox>
            <!-- 显示职位的的复选框 -->
            <el-checkbox-group
              v-model="userRole"
              @change="handleCheckedCitiesChange"
            >
              <el-checkbox
                v-for="(role, index) in allRole"
                :key="index"
                :label="role"
              >
                {{ role.roleName }}
              </el-checkbox>
            </el-checkbox-group>
          </el-form-item>
        </el-form>
      </template>
    </el-drawer>

详细解释

全选部分:

@change:全选框点击时的回调

v-model:绑定的数据,根据这个值决定是否全选(是一个布尔值)

:indeterminate:不确定状态,既没有全选也没有全不选

 


复选框部分:

v-for="(role, index) in allRole":遍历allRole。

:label="role":收集的数据(勾上的数据)

v-model="userRole":绑定收集的数据,也就是收集的数据存储到userRole中。

@change:勾选变化时的回调(点击每个复选框都会执行的回调)

 


全选框勾选的回调:

实现原理:函数会将勾选与否注入到val中,如果是,就将全部数据(allRole)赋值给选中的数据(userRole),选中的数据通过v-model实现页面的同步变化

该函数会接收到一个布尔值,如果为真代表全选按钮被勾选,在回调中需要将全部角色(职位)数据赋值给在el-checkbox-group使用v-model绑定的变量中(该变量表示被选中的数据)

//顶部的全部复选框的change事件
const handleCheckAllChange = (val: boolean) => {
  //val:true(全选)|false(没有全选)
  userRole.value = val ? allRole.value : []
  //不确定的样式(确定样式)
  isIndeterminate.value = false
}

复选框

每勾选一个复选框就会执行该函数,并收集勾选的数据到数组中传递进该函数

在函数内部判断数组的长度是否等于全部角色数据,如果等于需要将上方的权限按钮勾上,因为已经使用v-model绑定了,所以直接给checkAll.value赋一个布尔值即可

//顶部全部的复选框的change事件
const handleCheckedCitiesChange = (value: string[]) => {
  //顶部复选框的勾选数据
  //代表:勾选上的项目个数与全部的职位个数相等,顶部的复选框勾选上
  checkAll.value = value.length === allRole.value.length
  //不确定的样式
  isIndeterminate.value = value.length !== allRole.value.length
}

当点击确定按钮的回调

需要收集两个参数:第一个:当前用户的id标识,第二个:从收集好的userRole中使用map过滤出选中的角色的id标识(数组)

然后发起分配用户角色的请求,请求成功,重新获取更新完毕用户的信息。

//确定按钮的回调(分配职位)
const confirmClick = async () => {
  //收集参数
  let data: SetRoleData = {
    userId: userParams.id as number,
    roleIdList: userRole.value.map((item) => {
      return item.id as number
    }),
  }
  //分配用户的职位
  let result: any = await reqSetUserRole(data)
  if (result.code == 200) {
    //提示信息
    ElMessage({ type: 'success', message: '分配职务成功' })
    //关闭抽屉
    drawer1.value = false
    //获取更新完毕用户的信息,更新完毕留在当前页
    getHasUser(pageNo.value)
  }
}

 看到这里,相信你一定有收获!!!

给角色分配权限业务

静态结构

当点击上图中的分配权限按钮时,会弹出一个抽屉组件(el-drawer),利用树组件展示所有权限数据,并可以看到点击的角色已有的权限数据(勾选的)

注意:上方图片是个示例图,代表给角色分配权限,页面中使用了Element-plus的el-table和el-drawer,el-tree组件。

注意:因为这是在vue3和typescript项目中,所以进行了类型限制。

type这里MenuData与MenuList互相调用,适合这种树状的数据结构(type还可以定义多个复杂类型)

//菜单与按钮数据的ts类型
export interface MenuData {
  id: number
  createTime: string
  updateTime: string
  pid: number
  name: string
  code: string
  toCode: string
  type: number
  status: null
  level: number
  children?: MenuList
  select: boolean
}
export type MenuList = MenuData[]

分配权限按钮

获取&&存储数据

根据角色id发送请求获取全部角色权限数据(包含该角色已有的职位)

//准备一个数组:数组用于存储勾选的节点的ID(四级的)
let selectArr = ref<number[]>([])
//已有的职位的数据
const setPermisstion = async (row: RoleData) => {
  //抽屉显示出来
  drawer.value = true
  //收集当前要分类权限的职位的数据
  Object.assign(RoleParams, row)
  //根据职位获取权限的数据
  let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)
  if (result.code == 200) {
    menuArr.value = result.data
    //下面这行代码下面会具体讲解,可以先往下看
    selectArr.value = filterSelectArr(menuArr.value, [])
  }
}

 使用树组件展示全部权限数据(树组件详细使用说明)

我们重点关注el-tree组件,先介绍一些常用属性和方法

const defaultProps = {
  //子树为节点对象的children
  children: 'children',
  //节点标签为节点对象的name属性
  label: 'name',
}

常用属性: 

1. data:展示的数据(数据源)

2. show-checkbox:节点是否可被选择(点击可以选中)

3. node-key:每个树节点用来作为唯一标识的属性,整棵树应该是唯一的(如果树中包含children子数据,该属性不能省略)

4. default-expand-all:默认展开所有节点

5. default-checked-keys:默认勾选的节点的 key 的数组(是一个数组,数组中存放的就是上面node-key存放的唯一标识)

6. default-expanded-keys:默认展开的节点的 key 的数组(是一个数组,数组中存放的就是上面node-key存放的唯一标识)

7. current-node-key:当前选中的节点(可以是number或string类型)

8. props:接收一个对象,对象中可以包含以下两个属性(还可以包含其他属性,这里只列举了以下两个)

label:指定节点标签为节点对象的某个属性值(就是代表了要在页面中展示的节点名称) ,children:指定子树为节点对象的某个属性值(就是代表去哪个字段下读取数据当作子节点的数据)(注意:label和children这两个属性名是不变的,属性值需要根据项目需要进行修改)


常用方法:

使用el-tree树组件提供的方法时,需要先在el-tree组件标签上利用ref打上标识(<el-tree ref="xxx"> </el-tree>),然后通过ref得到el-tree组件实例才能调用对应方法!

1. getCheckedKeys:如果在el-tree标签上设置了show-checkbox属性且被选中,通过树组件实例.getCheckedKeys进行调用,它将返回当前选中节点key的数组(该数组由所有被选中的节点的id属性组成【为什么是id属性呢?        因为:在el-tree标签上设置了node-key="id"属性。所以该方法会收集所有选中的节点对象的id属性】)


 2. getHalfCheckedKeys:如果在el-tree标签上设置了show-checkbox属性且被选中,通过树组件实例.getHalfCheckedKeys进行调用,它将返回当前半选中的节点的id属性组成的数组
如遇这种情况该方法一般会和上面的getCheckedKeys配合使用


在树组件中展示已分配的权限

为什么要使用递归函数?
因为不能直接根据1级,2级,3级的select属性进行判断,要判断最内层的职位对象的select属性是真是假,因为层级较深要判断最内层的职位对象的select属性,所以使用了递归函数

具体实现思路:
    目的:要先对返回的全部权限数据进行过滤,找到最内层的职位对象,根据其select属性判断!
    封装一个递归函数,接收两个参数,一个全部权限数据,一个空数组(用于存放满足level为4级职位对象中的select属性为真的职位的id),在递归函数    中使用for Each遍历全部权限数据,判断item.select && item.level === 4如果满足说明到最内层了,将当前这个对象的id属性(item.id)push到空数组中,否则代表没有到最内层,继续判断item.children && item.children.length > 0 满足该条件递归调用    filterSelectArr(item.children,initArr),最后返回这个过滤好的数组,交给el-tree组件的default-checked-keys属性,那么就可以找出该角色已有的权限就可以在树组件选中了

//分配权限按钮的回调
//已有的职位的数据
const setPermisstion = async (row: RoleData) => {
  //抽屉显示出来
  drawer.value = true
  //收集当前要分类权限的职位的数据
  Object.assign(RoleParams, row)
  //根据职位获取权限的数据
  let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)
  if (result.code == 200) {
    menuArr.value = result.data
    /*
        最后返回这个过滤好的数组,交给el-tree组件的default-checked-keys属性,
        那么就可以找出该角色已有的权限就可以在树组件选中了
    */
    selectArr.value = filterSelectArr(menuArr.value, [])
  }
}
// 递归函数
const filterSelectArr = (allData: any, initArr: any) => {
  allData.forEach((item: any) => {
    if (item.select && item.level == 4) {
      initArr.push(item.id)
    }
    if (item.children && item.children.length > 0) {
      //层层递进,直到最内层的职位对象的level属性===4
      filterSelectArr(item.children, initArr)
    }
  })
  //返回这个过滤好的数组
  return initArr
} 

收集用户分配的权限

我们这里收集主要用到了2个方法:getCheckedKeys、getHalfCheckedKeys。它们会返回已选择以及半选择用户的id数组(这两个方法上方都有详细解释)

//抽屉确定按钮的回调
const handler = async () => {
  //职位(角色)的ID
  const roleId = RoleParams.id as number
  //选中节点的ID		getCheckedKeys方法会得到show-checkbox为true选中的全部节点对象的id组成的数组
  //为什么是能收集到id 		因为el-tree配置了node-key="id"属性
  let arr = tree.value.getCheckedKeys()
  //半选的ID
  let arr1 = tree.value.getHalfCheckedKeys()
  let permissionId = arr.concat(arr1)
  //下发权限
  let result: any = await reqSetPermisstion(roleId, permissionId)
  if (result.code == 200) {
    //抽屉关闭
    drawer.value = false
    //提示信息
    ElMessage({ type: 'success', message: '分配权限成功' })
    //页面刷新
    window.location.reload()
  }
}

继续加油!


1.2. 实现深度

3级权限:模块权限、页面权限、按钮权限

 

相关文章:

  • 【C语言】宏定义中X-Micro的使用
  • PAT乙级真题 / 知识点(2)
  • React Native v0.78 更新
  • 基于Asp.net的零食购物商城网站
  • Java多线程与高并发专题——ConcurrentHahMap 在 Java7 和 8 有何不同?
  • AIGC(生成式AI)试用 26 -- 跟着清华教程学习 - 个人理解
  • 微服务通信:用gRPC + Protobuf 构建高效API
  • java面试项目介绍,详细说明
  • 如何同步this.goodAllData里面的每一项给到row
  • 基于PyTorch的深度学习3——基于autograd的反向传播
  • 为AI聊天工具添加一个知识系统 之136 详细设计之77 通用编程语言 之7
  • MARL零样本协调之Fictitious Co-Play学习笔记
  • Python练习(握手问题,进制转换,日期问题,位运算,求和)
  • 数据流图(实例)
  • 【ArcGIS/GeoScene Server】修改密码
  • 颈椎X光数据集(cervical spine X-ray dataset)
  • MySQL 数据库连接池爆满问题排查与解决
  • 数据挖掘校招面经二
  • 爬虫面试:关于爬虫破解验证码的13个经典面试题
  • c++类继承的一些反思
  • 杭州做卖房子的工作哪个网站好/优化什么建立生育支持政策体系
  • 猪八戒兼职网站怎么做任务赚钱/上海公司网站seo
  • 在58做网站推广有用没/搜索引擎网站排名
  • 手机app下载软件安装/免费seo网站推荐一下
  • 模板网站怎么修改/山东seo多少钱
  • 网站建设价格是多少/廊坊seo