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>
方案:使用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"], }, } );
页面示例:
<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"; } });