vite-plugin-pages实现路由权限和组件权限

项目基本架构跟 vite实现element-plus按需配置,自定义主题和读取/修改系统主题色 相同。项目地址

目标:在vite-plugin-pages 自动读取文件夹配置下,设置前端路由权限和单组件权限。

权限模块后台返回数据假设:返回 与前端文件夹匹配的路径数据,并包含权限信息。

假设,无权限数据为:

{
  "code": 200,
  "data": [
    {
      "menu": [
        {
          "label": "面板1",
          "key": "index",
          "meta": {
            "isAdmin": false,
            "requiresAuth": false
          }
        },
        {
          "label": "统计分析",
          "key": "index-analysis",
          "meta": {
            "isAdmin": true,
            "requiresAuth": false
          }
        },
        {
          "label": "编辑器",
          "key": "index-editor",
          "meta": {
            "isAdmin": false,
            "requiresAuth": false
          }
        },
        {
          "label": "权限管理",
          "key": "index-permission",
          "meta": {
            "isAdmin": false,
            "requiresAuth": true
          }
        },
        {
          "label": "关注",
          "key": "attention",
          "meta": {
            "isAdmin": false,
            "requiresAuth": false
          }
        }
      ]
    }
  ]
}

 

有权限数据为:

......
        {
          "label": "权限管理",
          "key": "index-permission",
          "meta": {
            "isAdmin": true,
        .....
          }
        },
......

实现功能:判断当前用户是否为管理员,如果不是,切换为管理员。

<template>
  <div class="index-permission w-full overflow-hidden bg-slate-100 p-4">
    <p v-if="route.meta.isAdmin">当前是admin用户</p>
    <p v-else>当前不是admin用户</p>
    <el-button v-if="!route.meta.isAdmin" type="primary" size="default" @click="changeMenu">切换为admin用户</el-button>
  </div>
</template>

<script setup lang="ts">
import { handleRouterMeta } from '@/router';
import { useMenuStore } from '@/stores/menu';

let route = useRoute()
const { changeMenus, sideMenusFlat } = useMenuStore()
const changeMenu = async () => {
  await changeMenus()
  handleRouterMeta(sideMenusFlat);
  location.reload()
}
</script>
View Code

方案:使用router的beforeEach对路由进行修改。

import {IMenuItem} from "@/models/menu";
import {useMenuStore} from "./stores/menu";
import {createWebHistory, createRouter, RouteMeta} from "vue-router";
import routes from "~pages";
import "vue-router";
declare module "vue-router" {
  interface RouteMeta {
    // 是可选的
    isAdmin?: boolean;
    // 每个路由都必须声明
    requiresAuth: boolean;
  }
}
export const router = createRouter({
  history: createWebHistory("/"),
  routes,
});

export const handleRouterMeta = (sideMenusFlat: IMenuItem[]) => {
  let routesFlat = router.getRoutes();
  sideMenusFlat.forEach((curRoute) => {
    let match = routesFlat.find((i) => i.name == curRoute.key);
    if (match) {
      match.meta = curRoute.meta as RouteMeta;
    }
  });
};
let isFresh = true; //刷新页面重新处理路由
router.beforeEach(async (to, _, next) => {
  if (to.path === "/login") {
    next();
    return;
  }
  const {sideMenus, getMenus, sideMenusFlat} = useMenuStore();
  if (isFresh && sideMenus.length) {
    isFresh = false;
    handleRouterMeta(sideMenusFlat);
    next({...to});
    return;
  }
  if (sideMenus.length === 0) {
    isFresh = false;
    await getMenus();
    handleRouterMeta(sideMenusFlat);
    next({...to});
  } else {
    isFresh = false;
    handleRouterMeta(sideMenusFlat);
    next();
  }
});

这里我把路由和导航的处理放在了一起 menu.Store.ts

import {IMenuItem} from "@/models/menu";
import {IMenu} from "@/models/menu";
import {defineStore} from "pinia";
import {useSideMenu1, useSideMenu2} from "@/utils/api";

export const useMenuStore = defineStore(
  "menu",
  () => {
    let sideMenus = ref<IMenu[]>([]);
    let sideMenusFlat = ref<IMenuItem[]>([]);

    const flatMenu = (menus: IMenu[]) => {
      menus.forEach((m: any) => {
        m.menu ? flatMenu(m.menu) : sideMenusFlat.value.push(m);
      });
    };
    const getMenus = async () => {
      const {code, data} = await useSideMenu1();
      if (code === 200) {
        sideMenus.value = data;
        flatMenu(sideMenus.value);
        return;
      }
    };

    const changeMenus = async () => {
      sideMenus.value = [];
      const {code, data} = await useSideMenu2();
      if (code === 200) {
        sideMenus.value = data;
        flatMenu(sideMenus.value);
        return;
      }
    };

    return {
      getMenus,
      sideMenus,
      sideMenusFlat,
      changeMenus,
    };
  },
  {
    persist: {
      paths: ["sideMenus", "sideMenusFlat"],
    },
  }
);
View Code

页面示例:

<template>
  <div class="index-permission w-full overflow-hidden bg-slate-100 p-4">
    <p v-if="route.meta.isAdmin">当前是admin用户</p>
    <p v-else>当前不是admin用户</p>
    <el-button v-if="!route.meta.isAdmin" type="primary" size="default" @click="changeMenu">切换为admin用户</el-button>
  </div>
</template>

<script setup lang="ts">
import { handleRouterMeta } from '@/router';
import { useMenuStore } from '@/stores/menu';

let route = useRoute()
const { changeMenus, sideMenusFlat } = useMenuStore()
const changeMenu = async () => {
  await changeMenus()
  handleRouterMeta(sideMenusFlat);
  location.reload()
}
</script>

 单组件权限

app.directive("permission", (el, binding) => {
  const permission = binding.value;
  let routeMeta = router.currentRoute.value.meta;
  if (routeMeta[permission]) {
    el.style.display = "block";
  } else {
    el.style.display = "none";
  }
});

 

posted @ 2023-03-02 18:33  Merrys  阅读(1225)  评论(0编辑  收藏  举报