一个普通的vue权限管理方案-菜单权限控制

devtools/2025/3/24 4:22:19/

渲染左侧菜单

javascript"><template><div class="sidebar"><el-menuref="sideMenu"class="sidebar_menu":default-active="activeNav"unique-opened><divclass="sidebar_item"v-for="sidebar in sidebarList":key="sidebar.id"><!-- 有子级 --><el-submenu:index="sidebar.path"v-if="sidebar.children &&sidebar.children.length > 0 &&sidebar.name !== sidebar.children[0].name"><template slot="title"><span>{{ sidebar.meta.title }}</span></template><divv-for="child1 in sidebar.children":key="child1.name"class="sidebar_child"><el-submenu:index="child1.path"v-if="child1.children && child1.children.length > 0"><template slot="title"><span>{{ child1.meta.title }}</span></template><divv-for="child in child1.children":key="child.name"class="sidebar_child"><el-menu-item:index="child.path":disabled="!child.path"@click="handleClickMenu(child1.path, child.path)"><span slot="title">{{ child.name }}</span></el-menu-item></div></el-submenu><!-- 无子级 --><el-menu-itemv-else:index="child1.path":disabled="!child1.path"@click="handleClickMenu(sidebar.path, child1.path)"><span slot="title">{{ child1.name }}</span></el-menu-item></div></el-submenu><!-- 无子级 --><el-menu-itemv-else:index="sidebar.path + '/index'":disabled="!sidebar.path"@click="handleClickMenu(sidebar.path)"><span slot="title">{{ sidebar.name }}</span></el-menu-item></div></el-menu></div>
</template><script>
import { getUserFuncPerm } from '@/api/user'
export default {name: 'SideNavigation',props: {activePath: {type: String,default: '',},},data() {return {defaultActive: '/',}},computed: {sidebarList(){return this.$store.getter.sidebarList},activeNav() {return this.activePath ? this.activePath : this.defaultActive},},mounted() {this.getMenuList()},methods: {// 菜单getMenuList() {//},handleClickMenu(path, subPath) {const p = subPath ? subPath : `${path}/index`this.$router.push(p)this.defaultActive = p},// 跳转刷新handleClickMenuAndRefresh(path, subPath) {const p = subPath ? subPath : `${path}/index`// 先跳转至空白页,再跳转至目标页,实现刷新目标页this.$router.push({path: '/redirect',query: {path: p,},})this.defaultActive = p},},
}
</script>

首先需要渲染左侧菜单
vuex内部会存贮一个变量sidebarList, 这个变量的结构就是类似于权限接口的返回值,是一个层级结构。
如何存储sidebarList呢,
路由跳转之前会经过 router.beforeEach方法

当进入一个路由之前需要判断是否有能力进入

数据准备sidebarList

有一个获取左侧菜单权限的接口,得到数据

javascript">{"data": {"funcPerm": [{"children": [{"id": "10010","name": "日程管理","path": "/schedule/index", // 路由地址"component": "meeting/meetingRoom/index", // 页面地址}],"component": "Layout","id": "10000","meta": {"title": "会议日程"},"name": "会议日程","path": "/index",}]},"errors": [],"msg": "操作成功","status": "0"
}

上图是一个基本的权限接口的返回值

控制权限展示

javascript">import router from "./router";
import store from "./store";
import { Message } from "element-ui";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({ showSpinner: false });const whiteList = ['/login'];
router.beforeEach(async (to, from, next) => {NProgress.start();let userInfo = {};const storedUserInfo = localStorage.getItem("userInfo");if (storedUserInfo) {userInfo = JSON.parse(storedUserInfo);}const userId = userInfo ? userInfo.userId : "";// 如果拉取到了登录人信息if (userId) {if (whiteList.includes(to.path)) {next();NProgress.done();} else {// 如果本地已经拉取过路由信息了const routerLength = store.getters.router_length;if (routerLength) {// 这里其实应该需要判断下目标路由是否在权限列表里面next();NProgress.done();} else {try {let accessRoutes = "";const params = {userId: userId,};const res = await store.dispatch("user/getUserFuncPerm", params);accessRoutes = await store.dispatch("permission/generateRouter",res.funcPerm);router.addRoutes(accessRoutes);next({ ...to, replace: true });} catch (error) {Message.error(error || "Has Error");next(`/uim-view/#/login`);NProgress.done();}}}} else {if (whiteList.includes(to.path)) {next();NProgress.done();}// 这里应该还有登出操作}
});router.afterEach(() => {NProgress.done();
});

上面代码中有个whiteList常量,这里是一个白名单,当路由进入白名单里面的的时候,直接渲染页面,保证登录页面一直保持着能登录的权限
当进入非白名单时,判断用户是否登录userId,没有登录进入登录页
如果登录过,则判断是否获取过用户的菜单权限,这里的菜单权限使用的时vuex保存的,router_length代表这用户菜单的长度,如果有值则表示获取过权限,直接进入对应页面。(其实,这里需要做一个权限判断,如果用户手动输入路由,且不在路由权限里,这里有问题)

动态路由

如果我们不写一个全静态的路由,全由后端的权限给的路径拼上路由列表

javascript">accessRoutes = await store.dispatch("permission/generateRouter",res.funcPerm);

上面就是实现此功能的代码

javascript">  generateRouter({ commit }, data = []) {return new Promise(resolve => {let serverRouterMap = []serverRouterMap = dataif (Array.isArray(data) && data.length === 0) {Message({message: '无权限, 请配置权限',type: 'warning',duration: 3 * 1000,})return}try {constantRoutes[0].redirect = data[0].children[0].path} catch (err) {console.log(err)}const createRouter = () =>new Router({scrollBehavior: () => ({y: 0,}),routes: constantRoutes,})router.matcher = createRouter().matcherserverRouterMap.push({ path: '*', redirect: '/404', hidden: true })const asyncRouterMap = filterAsyncRouter(serverRouterMap)commit('SET_ROUTES', asyncRouterMap)resolve(asyncRouterMap)})},
javascript">function filterAsyncRouter(asyncRouterMap) {// 遍历后台传来的路由字符串,转换为组件对象const accessedRouters = asyncRouterMap.filter(route => {if (route.component) {if (route.component === 'Layout') {// Layout组件特殊处理route.component = Layout} else {route.component = _import(route.component)}}if (route.children && route.children.length > 0) {route.children = filterAsyncRouter(route.children)} else {delete route.children}return true})return accessedRouters
}

http://www.ppmy.cn/devtools/169103.html

相关文章

算法及数据结构系列 - 树

系列文章目录 算法及数据结构系列 - 二分查找 算法及数据结构系列 - BFS算法 算法及数据结构系列 - 动态规划 算法及数据结构系列 - 双指针 算法及数据结构系列 - 回溯算法 文章目录 树框架树遍历框架N叉树遍历框架 经典题型124.二叉树的最大路径和105.从前序与中序遍历序列构造…

springboot的 nacos 配置获取不到导致启动失败及日志不输出问题

前言 问题 1. 本地启动应用时&#xff0c;一切正常&#xff0c;但是部署 docker 后&#xff0c;会因为获取不到 nacos 中的配置导致服务启动失败。 2.当 docker 中的服务一直重启&#xff0c;可能会突然某一次启动成功&#xff0c;之后只要不重新构建 docker 镜像&#xff0c…

什么是 React Router?如何使用它?

React Router 是一个用于在 React 应用程序中实现路由的库。它使得开发者可以在单页应用&#xff08;SPA&#xff09;中创建多个视图&#xff0c;并在不同的 URL 之间进行导航。通过 React Router&#xff0c;开发者能够处理 URL 的变化&#xff0c;渲染不同的组件&#xff0c;…

第七章 狄克斯特拉算法

狄克斯特拉算法找出的是总权重最小的路径 术语介绍&#xff1a; 狄克斯特拉算法用于每条边都有关联数字的图&#xff0c;这些数字称为权重。 带权重的图称为加权图&#xff0c;不带权重的图称为非加权图。 计算非加权图的最短路径&#xff0c;可使用广度优先搜索。要计算加…

cursor无限续杯软件操作教程

软件使用教程&#xff1a; 在这里插入图片描述 软件界面&#xff1a; 破解流程&#xff1a; 1.退出 cursor 软件的账号&#xff0c;点击 log out 按钮&#xff0c;可以手动退出并关闭软件。 2.删除账号&#xff0c;点击按钮会自动打开网页&#xff0c;手动删除即可。 3.确保…

Web 小项目: 网页版图书管理系统

目录 最终效果展示 代码 Gitee 地址 1. 引言 2. 留言板 [热身小练习] 2.1 准备工作 - 配置相关 2.2 创建留言表 2.3 创建 Java 类 2.4 定义 Mapper 接口 2.5 controller 2.6 service 3. 图书管理系统 3.1 准备工作 - 配置相关 3.2 创建数据库表 3.2.1 创建用户表…

知识蒸馏:让大模型“瘦身“而不失智慧的魔术

引言&#xff1a;当AI模型需要"减肥" 在人工智能领域&#xff0c;一个有趣的悖论正在上演&#xff1a;大模型的参数规模每年以10倍速度增长&#xff0c;而移动设备的算力却始终受限。GPT-4的1750亿参数需要价值500万美元的GPU集群运行&#xff0c;但现实中的智能设备…

k8s中service概述(二)NodePort

NodePort 是 Kubernetes 中一种用于对外暴露服务的 Service 类型。它通过在集群的每个节点上开放一个静态端口&#xff08;NodePort&#xff09;&#xff0c;使得外部用户可以通过节点的 IP 地址和该端口访问集群内部的服务。以下是关于 NodePort Service 的详细说明&#xff1…