首页 > 其他分享 >权限控制基础

权限控制基础

时间:2023-11-17 17:11:20浏览次数:38  
标签:控制 return el 基础 value hasPermission const 权限

从这个有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方法的重新运行。

标签:控制,return,el,基础,value,hasPermission,const,权限
From: https://www.cnblogs.com/wp-leonard/p/17839212.html

相关文章

  • OpenCL任务调度基础介绍
    当前,科学计算需求急剧增加,基于CPU-GPU异构系统的异构计算在科学计算领域得到了广泛应用,OpenCL由于其跨平台特性在异构计算领域渐为流行,其调度困难的问题也随之暴露,传统的OpenCL任务调度需要在编码阶段确定调度方案,这种人工调度难度高、适应性差、效率低下、且存在资源竞争问题。Mu......
  • task02:免模型预测、免模型控制学习总结
    免模型预测这节学习的主要是蒙特卡洛方法和时序差分法有模型与免模型状态转移概率是已知的,这种情况下使用算法我们称为有模型算法,而对于智能体来说环境是未知的,在该情况下使用算法,我们称之为免模型算法。在这里应该注意,除了动态规划外,其他的基础强化学习算法都是免模型的。有......
  • 手写基础vuex
    实现一个插件:声明Store类,挂载$storeStore具体实现:创建响应式的state,保存mutations、actions和getters实现commit根据用户传入type执行对应mutation实现dispatch根据用户传入type执行对应action,同时传递上下文实现getters,按照getters定义对state做派......
  • 学期2023-2024-1 20231416《计算机基础与程序设计》第八周学习总结
    作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里2023-2024-1计算机基础与程序设计第八周作业这个作业的目标《计算机科学概论》第9章《C语言程序设计》第7章并完成云班课测试,功能设计与面向对象设计,面向对象设计过程,面向对象语言三......
  • 麒麟v10 基础镜像制作
    下载:https://archive.org/download/kylin-server-10-sp-2-x-86-release-build-09-20210524 需求:通过麒麟v10iso文件创建一个docker镜像说一下我是在麒麟的系统下创建的麒麟docker,其他系统可以对应修正,区别不大进程:挂载iso文件到目录mount-oloop/media/kylin/xxx.iso......
  • 每日一题:通过css变量来控制主题
    1、定义不同主题颜色:root{--theme-color:blue;--font-size:18px;;}html[theme="dark"]{--theme-color:#000;2、通过切换html自定义属性来控制主题<!DOCTYPEhtml><htmllang="en"><head><metacharset="UT......
  • Animator.Enable一帧内两次设置之间的动画控制属性变更失效
    1)Animator.Enable一帧内两次设置之间的动画控制属性变更失效2)移动端Shader的Varying插值后是否会写回主存3)UnityAvatar在大型MMO中使用情况如何4)UnityWebRequest加载外部图片,尺寸是否必须是4的倍数这是第360篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区......
  • 安装 IIS 访问临时文件夹 C:\WINDOWS\TEMP\3C 读取/写入权限 错误: 0x80070005
    在windows中使用命令行方式安装IIS(Web服务器)WindowsServer2022安装IIS报错访问临时文件夹C:\WINDOWS\TEMP\3C读取/写入权限错误:0x80070005,可以使用命令行方式来安装和配置Web服务(IIS)。以下是使用DeploymentImageServicingandManagement(DISM)工具的步骤:1.打......
  • 【Java基础】Java中switch的多种写法
    Java中switch的多种写法代码需求:键盘录入一个数字(代表星期几),判断是工作日还是休息日switch最基础写法 publicstaticvoidswitchTest(){while(true){System.out.println("请输入:");Scannersc=newScanner(System.in);......
  • 1.面向对象基础面试题小结
    面向对象基础1面向对象三大特点封装、继承、多态面向过程是将解决问题的过程拆分为一个个方法执行;面向对象是先抽象出对象,由对象执行方法的方式解决问题。1)封装:将一个对象的属性封装在对象内部,不允许外部对象直接访问对象内部信息。2)继承:不同类型对象,相互之间经常有......