Vue + ElementPlus 实现权限管理系统(六): 自定义指令实现前端按钮权限控制

文摘   2024-11-04 09:02   上海  

在 Vue 中"指令"相信大家都不陌生,比如v-if,v-for,v-model等等,这些都是 Vue 中内置的指令。在 Vue 中指令其实也可以自定义,本篇文章就带大家来看一下如何使用自定义指令来控制前端的按钮权限。

了解自定义指令

自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑,比如我们就需要根据权限标识控制某个元素的显示和隐藏。

一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。比如

<template>
  <input v-focus />
</template>
<script setup>
  // 在模板中启用 v-focus
  const myDirective = {
    // 在绑定元素的 attribute 前
    // 或事件监听器应用前调用
    created(el, binding, vnode) {
      // 下面会介绍各个参数的细节
    },
    // 在元素被插入到 DOM 前调用
    beforeMount(el, binding, vnode) {},
    // 在绑定元素的父组件
    // 及他自己的所有子节点都挂载完成后调用
    mounted(el, binding, vnode) {},
    // 绑定元素的父组件更新前调用
    beforeUpdate(el, binding, vnode, prevVnode) {},
    // 在绑定元素的父组件
    // 及他自己的所有子节点都更新后调用
    updated(el, binding, vnode, prevVnode) {},
    // 绑定元素的父组件卸载前调用
    beforeUnmount(el, binding, vnode) {},
    // 绑定元素的父组件卸载后调用
    unmounted(el, binding, vnode) {},
  };
</script>

其中钩子中的参数表示的含义如下:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。

  • binding:一个对象,包含以下属性。

    • value:传递给指令的值。例如在  v-my-directive="1 + 1"  中,值是  2
    • oldValue:之前的值,仅在  beforeUpdate  和  updated  中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在  v-my-directive:foo  中,参数是  "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在  v-my-directive.foo.bar  中,修饰符对象是  { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。

  • prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在  beforeUpdate  和  updated  钩子中可用。

其中elbinding是我们最常用的两个参数

举例来说,下面这个指令

<template>
 <div v-example:foo.bar="baz">
</template>
<script setup>
...
const vExample = {
  mounted(el) => {}
}

const baz = ref('123')
</script>


binding 参数会是一个这样的对象:

{
  arg'foo',
  modifiers: { bartrue },
  value'123'/* `baz` 的值 */,
  oldValue/* 上一次更新时 `baz` 的值 */
}

通常情况下我们使用自定义指令的时候只会用到mountedupdated这两个生命周期,因此 Vue 给我们提供了一个简化的写法,可以直接用一个函数来定义

<template>
  <input v-focus />
</template>
<script setup>
  // 在模板中启用 v-focus
  const vFocus = (el, bindig) => {
    // 这会在 `mounted` 和 `updated` 时都调用
    el.focus();
  };
</script>

除了在一个组件中定义之外,我们还可以在全局注册自定义指令,如在main.js中全局注册vFocus指令

app.directive("focus", (el, binding) => {
  el.focus();
});

了解了自定义指令的用法,接下来实现一个权限自定义指令就很简单了。

实现权限v-hasPerm指令

为了便于维护,我们新建src/directive来存放自定义指令,然后再新建一个permisson/hasPerm.ts来写我们的指令逻辑。

import { DirectiveBinding } from "vue";
import useAppStore from "@/store/index";
export function hasPerm(el: Element, binding: DirectiveBinding{
  const appStore = useAppStore();
  const { value } = binding;

  if (!value) throw new Error("请传入权限标识");
  if (typeof value == "string") {
    //没有权限就移除当前元素
    if (!appStore.permissions.includes(value)) {
      el.parentNode?.removeChild(el);
    }
    return;
  }
  if (Array.isArray(value)) {
    //用户权限是否拥有数组中的任意一个权限
    const hasPerm = value.some((item) => appStore.permissions.includes(item));
    if (!hasPerm) {
      el.parentNode?.removeChild(el);
    }
    return;
  }

  throw new Error("权限标识格式不正确");
}

这里我们支持传入字符串和数组两种格式,如果传入的是字符串,则直接判断当前用户是否拥有该权限,如果传入的是数组,则判断当前用户是否拥有数组中的任意一个权限。如果没有就移除当前元素。

最后新建directive/index.ts统一注册我们的指令

import { App } from "vue";
import { hasPerm } from "./permisson/hasPerm";
export default function directive(app: App<Element>{
  app.directive("hasPerm", hasPerm);
  //如果有其它的指令可以继续添加
}

然后在main.ts引入即可

//...
import directive from "@/directive";

//...
const app = createApp(App);
//...
directive(app);

最后我们在角色管理中测试一下我们的指令是否生效,看一下有没有sys:user:list权限;(当前登录用户拥有的权限标识为sys:role:list和sys:menu:list)

<template>
  <div>
    <div>角色管理</div>
    <div>
      <el-button v-hasPerm="['sys:user:list']">按钮</el-button>
    </div>
  </div>
</template>
image.png

可以看到按钮已经被移除了。

我们再将权限标识改为该用户拥有的权限试试

//...
<el-button v-hasPerm="['sys:role:list']">按钮</el-button>
image.png

可以看到按钮已经显示出来了。

总结

本篇文章详细介绍了自定义指令的使用方法以及它的常用简写方式。同时展示了如何在实际项目中定义和注册自定义指令,以及如何在组件中应用这些指令来实现按钮权限控制。通过传入权限标识,判断用户是否具有相应权限,从而决定是否显示或隐藏元素。通过这种方式,我们可以更好地管理前端页面按钮的权限控制,提高系统的安全性和可维护性。

如果本篇文章对您有所帮助,欢迎关注、点赞、收藏、评论和分享。感谢您的阅读!

源码地址[1] 希望能得到您的Star( ̄▽ ̄)ブ

参考资料
[1]

https://github.com/qddidi/fs-admin


web前端进阶
坚持原创,分享前端技术文章。
 最新文章