vue权限管理,自定义权限指令

基于角色的权限管理。

在这种方式下,系统中的用户可以被分配不同的角色,每个角色具有不同的权限。通过将用户与角色进行关联,可以实现对用户的权限控制。具体的实现步骤如下:
  1. 定义角色和权限:首先需要定义系统中的角色和每个角色所具有的权限。权限可以按照功能模块或者操作类型进行划分。
  2. 用户角色关联:将用户与角色进行关联,可以通过在用户表中添加一个角色ID字段来实现。当用户登录系统时,可以根据用户的角色ID获取用户所具有的权限。
  3. 路由权限控制:在Vue中,可以通过路由来控制用户的权限。可以在路由配置中为每个路由设置一个meta字段,用于表示该路由所需要的权限。在用户登录后,可以根据用户的权限动态生成路由表,并将其传递给Vue Router进行路由配置。
  4. 页面权限控制:在Vue中,可以通过指令或者组件的方式来实现页面级别的权限控制。可以为每个需要进行权限控制的组件添加一个v-if指令,根据用户的权限来判断是否显示该组件。

实现

1、路由定义界面操作

界面上的操作定义在路由meta上

const dataRouter: Array<RouteRecordRaw> = [
  {
    path: "/resourceManage",
    component: Layout,
    redirect: "/resourceManage/filesCatalog",
    children: [
      {
        path: "/resourceManage/filesCatalog",
        name: "files",
        component: () => import("@/views/resourceManage/filesCatalog/index.vue"),
        meta: {
          icon: "Reading",
          keepAlive: true,
          requiresAuth: true,
          title: "资源管理",
          key: "files",
          features: {//路由上定义界面操作
            add: "新增资源",
            pubDel: "批量删除",
            edit: "编辑",
            upload: "上传",
            import: "导入",
          },
        },
      },
    ],
  },
];

2、角色权限配置

把路由meta定义的操作显示到界面上,操作权限

import dataRouter from "@/routers/modules/dataManage";
import { RouteRecordRaw } from "vue-router";

export default function useUiBtnPress() {
  type btnUiPerssType = { [key: string]: any[] };
  type featuresType = { [key: string]: string };
  const btnUiPerss: btnUiPerssType = {};
  type btnFeaturesType = { [key: string]: string[] };
  const btnFeatures: btnFeaturesType = {};
  const featuresForm = (router: Array<RouteRecordRaw>) => {  //转化成和后端商量好的格式
    let res = false;
    for (let rou of router) {
      if ("children" in rou && rou.children?.length) {
        featuresForm(rou.children); //有children递归
      } else if ("features" in rou.meta!) {
        //有features改变结构
        btnUiPerss[rou.path] = [];
        btnFeatures[rou.path] = [];
        for (let feat in rou.meta.features as featuresType) {
          btnUiPerss[rou.path].push({
            directive: feat,
            depict: (rou.meta.features as featuresType)[feat],
          });
          btnFeatures[rou.path].push(feat);
        }
      } else res = true;
    }
    if (res) return; //都没有features退出递归
  };

  featuresForm(dataRouter);
  console.log(btnUiPerss);
  console.log(btnFeatures);
  // UI 界面按钮权限字段,转化后的格式
  // const btnUiPerss: any = {
  //   "/resourceManage/filesCatalog": [
  //     {
  //       directive: "add",
  //       depict: "新增资源"
  //      },
  //     {
  //       directive: "edit",
  //       depict: "编辑"
  //      },
  //......省略了

  //   ],
  // };

  // const btnFeatures: any = {
  //   "/resourceManage/filesCatalog": ["add", "edit", "upload", "pubDel","import"],
  // };

  return {
    btnUiPerss,
    btnFeatures,
  };
}
import useUiBtnPress from "@/hooks/useUiBtnPres";

const { btnUiPerss } = useUiBtnPress();
//根据各自界面进行渲染,编辑权限

界面效果 

3、登录获取角色id

修改权限后,用户登录后,获取角色id存储在本地

(本项目存在localStorage中,全局配置piniaPersistStorage: window.localStorage)

import { defineStore, createPinia } from "pinia";
import { GlobalState } from "./interface";
import { UserInfo } from "@/api/interface";
import { meApi } from "@/api/modules/auth";//用户信息接口
import { Config } from "@/config";
import { piniaPersistConfig } from "@/store/helper/pinia";//pinia本地持久化
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
//import Cookies from "js-cookie";
import { ElMessage } from "element-plus";


export const GlobalStore = defineStore({
  // id: 必须的,在所有 Store 中唯一
  id: "GlobalState",
  // state: 返回对象的函数
  state: (): GlobalState => ({
    // token
    token: "",
    // userInfo
    userInfo: null,
  }),
  getters: {
    // 用户是否登录
    isAuthenticated(state): boolean {
      if (!Config.authEnable) {
        return true;
      }
      // 检查token或是session存在
      if (!state.userInfo) {
        return false;
      }
      return true;
    },
  },
  actions: {
    // setToken
    setToken(token: string) {
      this.token = token;
    },
    // setUserInfo
    setUserInfo(userInfo: UserInfo | null) {
      this.userInfo = userInfo;
    },

    // 更新用户信息
    updateUserInfo(realName?: string) {
      if (!Config.authEnable) {
        this.userInfo = { id: 0, username: "", real_name: "", role_list: [], status: 1 };
      }
      // 检查token或是session存在
      //if (!this.token && !Cookies.get("session")) {
      //  return;
      //}
      // 检查用户信息存在
      if (!this.userInfo) {
        // 只存在登录信息,但是没有用户信息,尝试获取用户信息
        meApi()
          .then(res => {
            this.setUserInfo(res);
          })
          .catch(err => {
            // 错误处理
            ElMessage.error(err.response.data.message);
            //err.response.data
            //err.response.status
          });
      }
      if (realName) {
        this.userInfo.real_name = realName;
      }
    },
  },
  persist: piniaPersistConfig("GlobalState"),
});

// piniaPersist(持久化)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

export default pinia;

4、获取角色权限

在layout布局组件根据角色id获取用户权限,存放权限路由,权限菜单,权限按钮

//布局hook,useLayout
import { computed, onMounted } from "vue";
import { useRoute } from "vue-router";
import { MenuStore } from "@/store/modules/menu";
import { AuthStore } from "@/store/modules/auth";
import { GlobalStore } from "@/store/index";
import { getPermissionApi, getRolePermissionApi } from "@/api/modules/auth";
import { handleRouter, filterRouter, filterPermissionRouter, createMenu } from "@/routers/util";
import { routes } from "@/routers/router";
import { Config } from "@/config";

export default function () {
  const route = useRoute();
  const menuStore = MenuStore();
  const authStore = AuthStore();
  const globalStore = GlobalStore();

  onMounted(async () => {
    // 获取菜单列表
     let id=globalStore.userInfo.id
     const res = await getRolePermissionApi(id); //获取角色权限
 
    if (!res) return;
    authStore.setSuper(res.super || false);//super为true就是有全部权限,不用过滤
    // 生成需要渲染到目录中的路由树
    if (res.routers) {
      // 过滤生成需要渲染的路径树,filterRouter,filterPermissionRouter,createMenu三个工具函数,看思路就好
      const data = filterRouter(routes, Config.availability || []);
      // 使用权限过滤生成需要渲染的路径树
      const data2 = filterPermissionRouter(data, res.routers || [], res.super || false);
      const menu = createMenu(data2);
      const dynamicRouter = handleRouter(menu);
      authStore.setAuthRouter(dynamicRouter);//路由
      menuStore.setMenuList(menu);//菜单
    }
    // 生成页面中的要素权限
    if (res.features) {
      authStore.setAuthButtons(res.features);//按钮
    }
  });

  const activeMenu = computed((): string => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
  const isCollapse = computed((): boolean => menuStore.isCollapse);
  const accordion = computed((): boolean => menuStore.accordion);
  const menuList = computed((): Menu.MenuOptions[] => menuStore.menuList);

  return {
    menuList,//布局渲染菜单
    isCollapse,
    accordion,
    activeMenu,
  };
}

5、菜单控制

layout.vue根据useLayout返回的menuList权限菜单进行渲染

6、按钮控制(vue自定义权限指令)

//自定义按钮权限指令
import { AuthStore } from "@/store/modules/auth";
import { Directive, toRaw } from "vue";

type UiPer = { [key: string]: string[] };
export const permission: Directive = {
  mounted(el, binding) {
    const authStore = AuthStore();
    //value按钮上的权限
    const value: string[] = binding.value;
    //获取用户所有的权限
    const permissions: UiPer = toRaw(authStore.authButtons);//角色按钮权限
    const sup = toRaw(authStore.super);
    if (sup) return;
    //判断传递进来的按钮权限,是否存在
    if (value && value instanceof Array && value.length > 0) {
      const vd = value[0].split(":");
      //判断传递进来的按钮权限字段,是否存在当前用户的permissions
      const hasPermission = () => {
        if (permissions[vd[0]]) {
          return permissions[vd[0]].includes(vd[1]);
        }
        return false;
      };
      if (!hasPermission()) {
        //没有权限时,隐藏
        el.style.display = "none";
      }
    } else {
      throw new Error("need roles! Like v-permission=\"['/mapManager/tile:add']\"");
    }
  },
};

export default permission;

没有特定权限按钮就不会显示 

  <el-button
    size="small"
    type="danger"
    :icon="Delete"
    @click="handleInitDele('one', scope.row.id)"
    v-permission="['/modelManage/model:delete']"
     >删除
   </el-button>
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值