记录--按钮级别权限怎么控制

news2024/12/25 9:32:45

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

最近的面试中有一个面试官问我按钮级别的权限怎么控制,我说直接v-if啊,他说不够好,我说我们项目中按钮级别的权限控制情况不多,所以v-if就够了,他说不够通用,最后他对我的评价是做过很多东西,但是都不够深入,好吧,那今天我们就来深入深入。

因为我自己没有相关实践,所以接下来就从这个有16.2k星星的后台管理系统项目Vue vben admin中看看它是如何做的。

获取权限码

要做权限控制,肯定需要一个code,无论是权限码还是角色码都可以,一般后端会一次性返回,然后全局存储起来就可以了,Vue vben admin是在登录成功以后获取并保存到全局的store中:

import { defineStore } from 'pinia';
export const usePermissionStore = defineStore({
    state: () => ({
        // 权限代码列表
        permCodeList: [],
    }),
    getters: {
        // 获取
        getPermCodeList(){
      		return this.permCodeList;
    	},	
    },
    actions: {
        // 存储
        setPermCodeList(codeList) {
            this.permCodeList = codeList;
        },

        // 请求权限码
        async changePermissionCode() {
            const codeList = await getPermCode();
            this.setPermCodeList(codeList);
        }
    }
})

接下来它提供了三种按钮级别的权限控制方式,一一来看。

函数方式

使用示例如下:

<template>
  <a-button v-if="hasPermission(['20000', '2000010'])" color="error" class="mx-4">
    拥有[20000,2000010]code可见
  </a-button>
</template>

<script lang="ts">
  import { usePermission } from '/@/hooks/web/usePermission';

  export default defineComponent({
    setup() {
      const { hasPermission } = usePermission();
      return { hasPermission };
    },
  });
</script>

本质上就是通过v-if,只不过是通过一个统一的权限判断方法hasPermission

export function usePermission() {
    function hasPermission(value, def = true) {
        // 默认视为有权限
        if (!value) {
            return def;
        }

        const allCodeList = permissionStore.getPermCodeList;
        if (!isArray(value)) {
            return allCodeList.includes(value);
        }
        // intersection是lodash提供的一个方法,用于返回一个所有给定数组都存在的元素组成的数组
        return (intersection(value, allCodeList)).length > 0;

        return true;
    }
}

很简单,从全局store中获取当前用户的权限码列表,然后判断其中是否存在当前按钮需要的权限码,如果有多个权限码,只要满足其中一个就可以。

组件方式

除了通过函数方式使用,也可以使用组件方式,Vue vben admin提供了一个Authority组件,使用示例如下:

<template>
  <div>
    <Authority :value="RoleEnum.ADMIN">
      <a-button type="primary" block> 只有admin角色可见 </a-button>
    </Authority>
  </div>
</template>
<script>
  import { Authority } from '/@/components/Authority';
  import { defineComponent } from 'vue';
  export default defineComponent({
    components: { Authority },
  });
</script>

使用Authority包裹需要权限控制的按钮即可,该按钮需要的权限码通过value属性传入,接下来看看Authority组件的实现。

<script lang="ts">
  import { defineComponent } from 'vue';
  import { usePermission } from '/@/hooks/web/usePermission';
  import { getSlot } from '/@/utils/helper/tsxHelper';

  export default defineComponent({
    name: 'Authority',
    props: {
      value: {
        type: [Number, Array, String],
        default: '',
      },
    },
    setup(props, { slots }) {
      const { hasPermission } = usePermission();

      function renderAuth() {
        const { value } = props;
        if (!value) {
          return getSlot(slots);
        }
        return hasPermission(value) ? getSlot(slots) : null;
      }

      return () => {
        return renderAuth();
      };
    },
  });
</script>

同样还是使用hasPermission方法,如果当前用户存在按钮需要的权限码时就原封不动渲染Authority包裹的内容,否则就啥也不渲染。

指令方式

最后一种就是指令方式,使用示例如下:

<a-button v-auth="'1000'" type="primary" class="mx-4"> 拥有code ['1000']权限可见 </a-button>

实现如下:

import { usePermission } from '/@/hooks/web/usePermission';

function isAuth(el, binding) {
  const { hasPermission } = usePermission();

  const value = binding.value;
  if (!value) return;
  if (!hasPermission(value)) {
    el.parentNode?.removeChild(el);
  }
}

const mounted = (el, binding) => {
  isAuth(el, binding);
};

const authDirective = {
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted,
};

// 注册全局指令
export function setupPermissionDirective(app) {
  app.directive('auth', authDirective);
}

只定义了一个mounted钩子,也就是在绑定元素挂载后调用,依旧是使用hasPermission方法,判断当前用户是否存在通过指令插入的按钮需要的权限码,如果不存在,直接移除绑定的元素。

很明显,Vue vben admin的实现有两个问题,一是不能动态更改按钮的权限,二是动态更改当前用户的权限也不会生效。

解决第一个问题很简单,因为上述只有删除元素的逻辑,没有加回来的逻辑,那么增加一个updated钩子:

app.directive("auth", {
    mounted: (el, binding) => {
        const value = binding.value
        if (!value) return
        if (!hasPermission(value)) {
            // 挂载的时候没有权限把元素删除
            removeEl(el)
        }
    },
    updated(el, binding) {
        // 按钮权限码没有变化,不做处理
        if (binding.value === binding.oldValue) return
        // 判断用户本次和上次权限状态是否一样,一样也不用做处理
        let oldHasPermission = hasPermission(binding.oldValue)
        let newHasPermission = hasPermission(binding.value)
        if (oldHasPermission === newHasPermission) return
        // 如果变成有权限,那么把元素添加回来
        if (newHasPermission) {
            addEl(el)
        } else {
        // 如果变成没有权限,则把元素删除
            removeEl(el)
        }
    },
})

const hasPermission = (value) => {
    return [1, 2, 3].includes(value)
}

const removeEl = (el) => {
    // 在绑定元素上存储父级元素
    el._parentNode = el.parentNode
    // 在绑定元素上存储一个注释节点
    el._placeholderNode = document.createComment("auth")
    // 使用注释节点来占位
    el.parentNode?.replaceChild(el._placeholderNode, el)
}

const addEl = (el) => {
    // 替换掉给自己占位的注释节点
    el._parentNode?.replaceChild(el, el._placeholderNode)
}

主要就是要把父节点保存起来,不然想再添加回去的时候获取不到原来的父节点,另外删除的时候创建一个注释节点给自己占位,这样下次想要回去能知道自己原来在哪。

第二个问题的原因是修改了用户权限数据,但是不会触发按钮的重新渲染,那么我们就需要想办法能让它触发,这个可以使用watchEffect方法,我们可以在updated钩子里通过这个方法将用户权限数据和按钮的更新方法关联起来,这样当用户权限数据改变了,可以自动触发按钮的重新渲染:

import { createApp, reactive, watchEffect } from "vue"
const codeList = reactive([1, 2, 3])

const hasPermission = (value) => {
    return codeList.includes(value)
}

app.directive("auth", {
    updated(el, binding) {
        let update = () => {
            let valueNotChange = binding.value === binding.oldValue
            let oldHasPermission = hasPermission(binding.oldValue)
            let newHasPermission = hasPermission(binding.value)
            let permissionNotChange = oldHasPermission === newHasPermission
            if (valueNotChange && permissionNotChange) return
            if (newHasPermission) {
                addEl(el)
            } else {
                removeEl(el)
            }
        };
        if (el._watchEffect) {
            update()
        } else {
            el._watchEffect = watchEffect(() => {
                update()
            })
        }
    },
})

updated钩子里更新的逻辑提取成一个update方法,然后第一次更新在watchEffect中执行,这样用户权限的响应式数据就可以和update方法关联起来,后续用户权限数据改变了,可以自动触发update方法的重新运行。

本文转载于:

https://juejin.cn/post/7209648356530896953

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/567035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

wifiWG233移植

驱动的移植 交叉编译WIFI原厂固件驱动 根据网上资料&#xff0c;和官方文档编译成功。 修改Makefile 修改交叉编译环境 执行 make 编译成功&#xff0c;生成88x2bu.ko 加载原厂驱动 Insmod 88x2bu.ko 生成wlan0 Hostapd工具移植生成可连接热点AP 安装libnl库 使用命令tar …

ApiKit 接口调用、自动化测试工具

作为一位后端开发&#xff0c;我们平时经常需要维护API文档、对API接口进行调试、有时候还得Mock数据。Postman虽然作为接口调试工具非常好用&#xff0c;但是对于维护API文档这类工作却不太合适。今天给大家推荐一款功能更强大的工具ApiKit&#xff0c;足以满足我们对API的各种…

如何清除电脑缓存?分享3个有效的方法!

案例&#xff1a;我发现我的电脑运行速度越来越慢&#xff0c;影响使用体验。朋友说可能是我没有及时清理电脑缓存文件所致&#xff0c;那如何清理缓存的文件&#xff1f; 【我想通过清理电脑缓存文件的方法&#xff0c;提高我电脑运行的速度&#xff0c;但是我不知道具体的操…

如何利用Citespace和vosviewer既快又好地写出高质量的论文及快速锁定热点和重点文献进行可视化分析?

基于Citespace和vosviewer文献计量学可视化SCI论文高效写作方法 CiteSpace是什么&#xff1f; 简单来说&#xff0c;它一款通过将国内外文献进行可视化分析来帮助你了解一门学科前世今生的软件。 面对成千上万篇的文献&#xff0c;怎样才能快速锁定自己最感兴趣的主题及科学…

取代 Docker Desktop?Podman Desktop 发布 GA 版本 1.0

Podman&#xff08;POD MANager&#xff09;是一个跨平台的容器管理工具&#xff0c;可用于管理容器、镜像、卷以及以容器组形式存在的 Pod。Podman 可以在 Linux 上直接运行容器&#xff0c;但在像 macOS 和 Windows 这样的平台&#xff0c;是通过虚拟机间接运行容器。 Podma…

「实在RPA·电商数字员工」契合电商数智转型需求

一、为什么说电商数智化转型很重要&#xff1f; 如今&#xff0c;电商发展速度惊人&#xff0c;并且已经取代了实体店购物的时代。在众多新型的消费方式下&#xff0c;各式的电商行业如何运作&#xff0c;并且在短时间内完成各项任务&#xff0c;提升人们的生活质量。人们生活…

django admin后台列表页、修改/详情页图片预览功能

目录 一、admin后台列表页的图片预览功能 二、admin后台修改/详情页图片预览功能 1&#xff0c;添加html前端代码 2、在admin.py文件中添加以下代码&#xff1a; 1.列表页图片问题&#xff1a;在admin列表页中&#xff0c;直接在list_display中填写图片字段时在列表页展示的…

YOLOv5从训练到移植

一、图像采集和标注 图像采集 覆盖所有的数据目标&#xff0c;不同场景&#xff08;视角、光照、可能的干扰&#xff09;、距离、运动、背景等&#xff0c;用深度和广度摄像头都行。 若兼顾效率和准确率&#xff0c;可以用迁移学习思路训练&#xff0c;则不同场景下采集的图…

系统方面对文件的打开,读写,关闭

系统方面对文件的操作 1. 系统方面打开文件的函数2. 系统方面对文件的写入3. 系统方面对文件的读取4. 关闭文件close 1. 系统方面打开文件的函数 open函数得到一个指定文件的文件描述符&#xff0c;如果出现错误则返回-1。open函数需要传入一个文件路径和操作模式&#xff0c;…

高德地图AMap.MouseTool插件多次测距不能清除bug

AMap.MouseTool插件是一个很有用的插件&#xff0c;可以在地图上画折线测量距离&#xff0c;也可以在地图上画区域测量面积&#xff0c;这些在客户的一些高级需求里经常出现&#xff0c;最近使用出现了bug&#xff0c;此bug在官网的示例里也能重现 官网demo上重现步骤如下图&a…

剑指 Offer 58 - I. 翻转单词顺序

剑指 Offer 58 - I. 翻转单词顺序 题目&#xff1a; 输入一个英文句子&#xff0c;翻转句子中单词的顺序&#xff0c;但单词内字符的顺序不变。为简单起见&#xff0c;标点符号和普通字母一样处理。例如输入字符串"I am a student. “&#xff0c;则输出"student. a …

Spring Tool Suite(STS)初始化配置记录

目录 1.前言 2.STS安装 3.STS配置 3.1.SpringToolSuite4.ini 3.2、配置maven 3.3.配置jdk 3.4.全局编码设置 3.5.字体配置 3.6.设置自动提示 4. Spring插件 4.1.MyBatipse--mybatis插件 4.2.Spark Builder Generator 4.3.Properties Editor 4.4.Checkstyle 4.5.…

【MySQL】数据库的基本操作

&#x1f3e0; 大家好&#xff0c;我是 兔7 &#xff0c;一位努力学习C的博主~&#x1f4ac; &#x1f351; 如果文章知识点有错误的地方&#xff0c;请指正&#xff01;和大家一起学习&#xff0c;一起进步&#x1f440; &#x1f680; 如有不懂&#xff0c;可以随时向我提问&…

Smoothieware_best-for-pnp 工程文件编译选项含义整理

文章目录 Smoothieware_best-for-pnp 工程文件编译选项含义整理概述arm-none-eabi-gcc 的编译选项含义整理 - S(汇编)文件arm-none-eabi-gcc 的编译选项含义整理 - C文件arm-none-eabi-gcc 的编译选项含义整理 - CPP文件库的打包arm-none-eabi-gcc 的编译选项含义整理 - C文件 …

Python入门教程+项目实战-12.3节-使用字典进行格式化

目录 12.3.1 字符串的格式化 12.3.2 使用字典进行格式化 12.3.3 格式化操作方法的优缺点 12.3.4 知识要点 12.3.5 系统学习python 12.3.1 字符串的格式化 在9.4节介绍了字符串的格式化&#xff0c;我们先来回顾下字符串格式化的定义&#xff0c;以及主要的格式化方法&…

万字长文详述ClickHouse在京喜达实时数据的探索与实践 | 京东云技术团队

1 前言 京喜达技术部在社区团购场景下采用JDQFlinkElasticsearch架构来打造实时数据报表。随着业务的发展 Elasticsearch开始暴露出一些弊端&#xff0c;不适合大批量的数据查询&#xff0c;高频次深度分页导出导致ES宕机、不能精确去重统计&#xff0c;多个字段聚合计算时性能…

从零开始的python教程:全面又好用的学习资料

Hi&#xff0c;大家好&#xff0c;我是蛋糕 最近因为接连带过一些训练营和成长营&#xff0c;也是可以与各位小伙伴进行更多的讨论&#xff0c;发现各位小伙伴最近也是迫切的想要学习一些新的技能&#xff0c;其中呼声最高的可能就是Python了&#xff0c;当然理由也是很多啦&a…

LeetCode 84 柱状图中最大的矩形

题目&#xff1a; 给定n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释&#xf…

职工人事管理系统_项目整合以及salary模块逻辑整理

项目&#xff1a; groupId 在所有项目中唯一标识您的项目。artifactId 是没有版本的 jar 的名称。Maven 中groupId 和artifactId 的主要区别在于&#xff0c;groupId 指定项目组的id&#xff0c;而artifactId 指定项目的id 配置完环境&#xff0c;如何测试自己的依赖安装完毕…

TDengine 成功“晋级” Percona Live 2023 银牌赞助商,开发者驻足关注

带着创新的数据技术走遍全球 这一次 陶建辉带着 TDengine 飞到了丹佛...... 2023 年 5 月 22-24 日&#xff0c;一年一度的开源数据库领域全球最具影响力峰会 Percona Live 2023 在丹佛技术中心万豪酒店举办。Percona Live 是全球持续举办最久的独立开源数据大会&#xff0c…