当前位置: 首页 > news >正文

前端实现菜单按钮级权限

核心思想就是通过登录请求此用户对应的权限菜单,然后跳转首页,触发全局前置导航守卫,在全局导航守卫中通过 addRoute 添加动态路由进去。addRoute有一个需要注意的地方,就是我们添加完动态路由后,地址栏上立即访问添加的动态路由,它不会跳转,需要我们手动触发下,push或者replace都可以进行触发。但是用在全局前置导航守卫中,写法又不太一样,可以参照官网说明

动态路由 | Vue Router (vuejs.org)

这是我自己练习的项目文件分布

我这里把主要文件的代码都贴出来

首先是 pinia 文件,这里面主要存储了 token 与 动态路由 数据。我做了一个持久化存储

stores 里面的 counter.ts

import { ref } from 'vue'
import { defineStore } from 'pinia'export const useUserStore = defineStore('user', () => {// tokenconst token = ref('')// 动态路由const dynamicRoutes = ref([])// 设置tokenconst setToken = (t: string) => { token.value = t }// 设置动态路由const setDynamicRoutes = (r: any) => { dynamicRoutes.value = r }// 清空tokenconst clearToken = () => { token.value = '' }// 清空动态路由const clearDynamicRoutes = () => { dynamicRoutes.value = [] }return {token,dynamicRoutes,setToken,setDynamicRoutes,clearToken,clearDynamicRoutes}
}, {persist: {enabled: true // true 表示开启持久化保存}
})

路由信息  router 文件夹里面的 index.ts 

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',name: 'home',component: HomeView,// 重定向的首页redirect: '/test'},{path: '/login',name: 'login',component: () => import('../views/Login.vue')}]
})export default router

登录页面:views 文件夹里面的 Login.vue

<template><div><el-button @click="submit">登录</el-button></div>
</template><script setup lang="ts">
import { useUserStore } from '@/stores/counter'
import { useRouter } from 'vue-router'const userStore = useUserStore()
const router = useRouter()const submit = () => {// 模拟登录setTimeout(() => {// 存储token和动态路由userStore.setToken('Bearen Xxx')userStore.setDynamicRoutes([{path: '/about',name: 'about',children: [{ path: '/about/music', name: 'music', component: 'Music' },{ path: '/about/movie', name: 'movie', component: 'Movie' },{path: '/about/parent',name: 'parent',children: [{ path: '/about/parent/child', name: 'parent', component: 'Parents' }]},]},{ path: '/test', name: 'test', component: 'Test' },])// 跳转路由。触发全局前置导航守卫router.replace('/')})
}
</script>

点击登录页面的登录按钮后,会跳转到  '/'  ,路由变化了,就会触发全局前置导航守卫,全局前置导航守卫我写在了 mian.ts 文件中

自定义指令可以忽略,那是我做按钮级权限用的(可以看我上一篇文章)。

需要注意的地方就是 hasAddAliveRoutes 这个变量,记录是否已经添加过动态路由了,如果添加过了,就赋值为true。在去其他路由的时候,就不会重新添加动态路由了。还有一个作用就是,刷新的时候,hasAddAliveRoutes会重新变为false,会重新添加一下动态路由,防止刷新路由丢失

import { createApp } from 'vue'
import { createPinia } from 'pinia'import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'import piniaPersist from 'pinia-plugin-persist'
import { useUserStore } from './stores/counter'const app = createApp(App)const pinia = createPinia()
app.use(pinia)
app.use(router)
app.use(ElementPlus)pinia.use(piniaPersist)// 处理动态路由,下面的全局前置导航守卫会用到
const getNewRoutes = (routes: any) => {let res = routes.map((i: any) => {return {...i,// 动态添加component,有就添加,没有就不添加 (带有子级的路由是没有component的)...(i.component && { component: () => import(`./views/${i.component}.vue`) }),// 再把内层的处理一下...(i.children && { children: getNewRoutes(i.children) }),}})return res
}// 是否添加过动态路由 这里的标识作用:刷新的时候,会变为false,然后就会重新添加动态路由,防止路由丢市的
let hasAddAliveRoutes = false
router.beforeEach((to, from, next) => {if (to.path === '/login') {next()} else {let token = useUserStore().tokenif (token) { // 存在token// 判断是否有动态路由添加了if (!hasAddAliveRoutes) { // 没有添加,则添加动态路由并进行触发// 对pinia中的动态路由进行处理,component字段只是一个文件名称,不是我们想要的动态引入,所以需要修改let arr = getNewRoutes(useUserStore().dynamicRoutes)console.log('处理之后的路由', arr)// 开始添加动态路由for (let i = 0; i < arr.length; i++) {router.addRoute('home', arr[i])}// 修改添加动态路由的状态hasAddAliveRoutes = true// 触发添加的动态路由next(to.fullPath)} else { // 已经添加了,则直接通过next()}} else { // 不存在tokennext('/login')}}
})// 假装此用户在tset页面只有 改 和 查 的按钮权限
let buttonAuth = [{ path: '/test', btn: ['check', 'change'] }
]
// 自定义指令: 控制按钮级权限
app.directive('permission', {mounted(el, binding) {// console.log(el) // 元素// console.log(binding.value) // 值// console.log(binding.arg) // 路由// 遍历按钮数组,根绝当前的路由找到这一项的按钮权限let btnAuth = buttonAuth.find(item => item.path === binding.arg)if (btnAuth) { // 找到了// 不包含此按钮权限就移除按钮!btnAuth.btn.includes(binding.value) && el.parentNode.removeChild(el)}}
})app.mount('#app')

然后就会跳往首页了,也就是 HomeView.vue 页面,这个文件里面用到了递归组件MenuTree.vue 

这个递归组件就是用来递归菜单的,多少级菜单都能进行展示

<template><el-container class="layout-container-demo" style="height: 100%"><el-aside :width="aside"><el-scrollbar><el-menu:router="true":collapse="isCollapse":mode="mode":collapse-transition="false":default-active="$router.currentRoute.value.path"><MenuTree :routes="routes" :isCollapse="isCollapse"></MenuTree></el-menu></el-scrollbar></el-aside><el-container><el-header style="text-align: right; font-size: 12px"><div class="toolbar"><el-button @click="collapse">折叠菜单</el-button><el-button @click="changeMode">改变布局</el-button><el-dropdown><el-icon style="margin-right: 8px; margin-top: 1px"><setting /></el-icon><template #dropdown><el-dropdown-menu><el-dropdown-item>View</el-dropdown-item><el-dropdown-item>Add</el-dropdown-item><el-dropdown-item>Delete</el-dropdown-item></el-dropdown-menu></template></el-dropdown><span @click="reback">退出</span></div></el-header><el-main><el-scrollbar><router-view></router-view></el-scrollbar></el-main></el-container></el-container>
</template><script lang="ts" setup>
import { Setting } from '@element-plus/icons-vue'
import MenuTree from './MenuTree.vue'
import { useUserStore } from '@/stores/counter'
import { ref } from 'vue'
import { useRouter } from 'vue-router'const userStore = useUserStore()
const router = useRouter()// 路由
const routes = ref(userStore.dynamicRoutes)
// 是否折叠
const isCollapse = ref(false)
// 宽度
const aside = ref('200px')
// 菜单展示方式
const mode = ref('vertical')// 点击折叠菜单
const collapse = () => {isCollapse.value = !isCollapse.valueaside.value = isCollapse.value ? '60px' : '200px'
}
// 点击改变布局
const changeMode = () => {mode.value = mode.value === 'vertical' ? 'horizontal' : 'vertical'
}
// 点击退出
const reback = () => {userStore.clearToken()userStore.clearDynamicRoutes()router.replace('/login')
}
</script><style scoped>
.layout-container-demo .el-header {position: relative;background-color: var(--el-color-primary-light-7);color: var(--el-text-color-primary);
}.layout-container-demo .el-aside {color: var(--el-text-color-primary);background: var(--el-color-primary-light-8);/* 新加的过度效果 */transition: width 0.15s;-webkit-transition: width 0.15s;-moz-transition: width 0.15s;-webkit-transition: width 0.15s;-o-transition: width 0.15s;
}.layout-container-demo .el-menu {border-right: none;
}.layout-container-demo .el-main {padding: 0;
}.layout-container-demo .toolbar {display: inline-flex;align-items: center;justify-content: center;height: 100%;right: 20px;
}
</style>

递归组件MenuTree.vue

<template><div v-for="item in routes" :key="item.path"><!-- 一级菜单 --><el-menu-item :index="item.path" v-if="!item.children"><el-icon><message /></el-icon><span v-show="!isCollapse">{{ item.path }}</span></el-menu-item><!-- 多级菜单 --><el-sub-menu :index="item.path" v-else><template #title><!-- <el-icon> --><el-icon class="more-menu-icon-hover"><message /></el-icon><span v-show="!isCollapse">{{ item.path }}</span></template><MenuTree :routes="item.children"></MenuTree></el-sub-menu></div>
</template><script setup lang="ts">
import { Message } from '@element-plus/icons-vue'defineProps({routes: {type: Array,default: () => [],},isCollapse: Boolean,
})
</script><style scoped>
/* 新增的样式,解决箭头问题 */
.more-menu-icon-hover {z-index: 99;background-color: #fff;transition: all 0.3s;
}:deep(.el-sub-menu__title:hover) {.more-menu-icon-hover {background-color: #ecf5ff;}
}
</style>

但是路由规则数组中,我其实做了重定向。也就是路由匹配到 '/' 的时候,会重定向到 /test ,也就是Test.vue页面。首页HomeView.vue文件中我指定的有二级路由出口,所以Test.vue页面的内容会展示在HomeView.vue页面的路由出口处

<template><div><el-button v-permission:[currentRoute]="'add'">增加</el-button><el-button v-permission:[currentRoute]="'delete'">删除</el-button><el-button v-permission:[currentRoute]="'change'">修改</el-button><el-button v-permission:[currentRoute]="'check'">查看</el-button></div>
</template><script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'const router = useRouter()
// 获取当前的路由
const currentRoute = ref(router.currentRoute.value.path)
</script>

好了,到这里,前端路由其实就已经做好了。实现的是菜单级别和按钮级别的权限

下面的百度网盘地址,有需要的可以自提:安装依赖后,直接 npm run dev 即可启动

链接:https://pan.baidu.com/s/1qYq8TzsroanggPfhVQEPoQ?pwd=x89u 
提取码:x89u

相关文章:

前端实现菜单按钮级权限

核心思想就是通过登录请求此用户对应的权限菜单&#xff0c;然后跳转首页&#xff0c;触发全局前置导航守卫&#xff0c;在全局导航守卫中通过 addRoute 添加动态路由进去。addRoute有一个需要注意的地方&#xff0c;就是我们添加完动态路由后&#xff0c;地址栏上立即访问添加…...

STM32:TTL串口调试

一.TTL串口概要 TTL只需要两个线就可以完成两个设备之间的双向通信&#xff0c;一个发送电平的I/O称之为TX&#xff0c;与另一个设备的接收I/O口RX相互连接。两设备之间还需要连接地线(GND)&#xff0c;这样两设备就有相同的0V参考电势。 二.TTL串口调试 实现电脑通过STM32发送…...

【Jenkins 安装】

一&#xff1a;安装文件夹准备 在/home/admin 界面下新建三个文件夹&#xff0c;用来安装tomcat、maven 1.打开&#xff0c;/home/admin目录 cd /home/admin 2.新建三个文件夹 mkdir tomcat mkdir maven 二&#xff1a;安装tomcat 1.打开tomcat目录进行tomcat的安装 访问:h…...

JVM——GC垃圾回收器

GC垃圾回收器 JVM在进行GC时&#xff1a;并不是对这三个区域&#xff08;新生区&#xff0c;幸存区&#xff08;from&#xff0c;to&#xff09;&#xff0c;老年区&#xff09;统一回收&#xff0c;大部分时候&#xff0c;回收都是新生区 GC两种类&#xff1a;轻GC&#xff…...

【三维重建-PatchMatchNet复现笔记】

【三维重建-PatchMatchNet复现笔记】 1 突出贡献2 数据集描述3 训练PatchMatchNet3.1 输入参数3.2 制定数据集加载方式 1 突出贡献 在计算机GPU和运行时间受限的情况下&#xff0c;PatchMatchNet测试DTU数据集能以较低GPU内存和较低运行时间&#xff0c;整体误差位列中等&#…...

CSS - 常用属性和布局方式

目录 前言 一、常用属性 1.1、字体相关 1.2、文本相关 1.3、背景相关 1.3.1、背景颜色 1.3.2、背景图片 1.4、圆角边框 二、常用布局相关 2.1、display 2.2、盒子模型 2.2.1、基本概念 2.2.2、border 边框 2.2.3、padding 内边距 2.2.4、margin 外边距 2.3、弹…...

数据结构与算法之矩阵: Leetcode 134. 螺旋矩阵 (Typescript版)

螺旋矩阵 https://leetcode.cn/problems/spiral-matrix/ 描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示…...

LVS+keepalived高可用负载均衡集群

keepalived介绍 keepalived为LVS应运而生的高可用服务。LVS的调度器无法做高可用&#xff0c;于是keepalived这个软件。实现的是调度器的高可用。 但是keepalived不是专门为LVS集群服务的&#xff0c;也可以做其他代理服务器的高可用。 LVS高可用集群的组成 主调度器备调度器&…...

解密Kubernetes:探索开源容器编排工具的内核

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

苹果手机怎么设置壁纸?解锁设置壁纸的2种方法!

手机壁纸便是我们常说的屏幕背景图&#xff0c;一张好看的手机壁纸能使我们的心情变得愉悦。这个壁纸可以是风景、美食、喜欢的偶像、自己养的宠物&#xff0c;或者是你的家人、朋友。 拥有特殊含义的照片会更让人想要设置成壁纸。苹果手机怎么设置壁纸&#xff1f;本文将给大…...

解决LOGITECH 罗技驱动 MAC版出现的一些问题汇总!

安装前将之前的安装文件清理干净&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 罗技驱动最新安装包下载 我的鼠标是G304 1. 驱动安装一直卡在99% 运行官方下载的安装程序&#xff0c;卡住后关掉窗口。 在终端中执行以下命令&#xff1a; sudo /Users/用户名…...

PyQt5入门4——给目标检测算法构建一个简单的界面

PyQt5入门4——给目标检测算法构建一个简单的界面 学习前言要构建怎么样的界面实例使用1、窗口构建a、按钮&#xff1a;获取图片b、Inputs、Outputs文本提示c、Inputs、Outputs图片显示d、箭头显示e、整点祝福 2、主程序运行 全部代码 学习前言 搞搞可视化界面哈&#xff0c;虽…...

Pandas数据分析系列9-数据透视与行列转换

Pandas 数据透视表 当数据量较大时,为了更好的分析数据特征,通常会采用数据透视表。数据透视表是一种对数据进行汇总和分析的工具,通过重新排列和聚合原始数据,可以快速获得更全面的数据洞察。数据透视表在Excel中也是经常使用的一个强大功能,在Pandas模块,其提供了pivot…...

部分背包问题细节(贪心)

有一种情况是&#xff0c;背包可以把金币全部拿走&#xff1a; 如果num小于0则返回值...

windows协议详解之-RPC/SMB/LDAP/LSA/SAM域控协议关系

如果你在windows域控环境中&#xff0c;例如企业的网络中开启wireshark抓包&#xff0c;你一定会遇到一大堆各种各样的协议。不同于互联网服务&#xff08;大多基于HTTP&#xff09;&#xff0c;为了实现域控中各种各样的服务&#xff0c;windows的域控环境中采用了非常多的协议…...

uniapp中 background-image 设置背景图片不展示问题

有问题 <view class"file-picker__box jsz" tap"jszxszUpload(jsz)"></view>.jsz {background-image: url(../../static/example_drive.png); }解决1 <view class"file-picker__box jsz" :style"{ background-image: url(…...

Elasticsearch打分机制

一 例子 xx搜索引擎,就搜索结果本身而言,xx返回了正确的结果。因为返回的结果中,都包含了搜索的关键字。而我们从逻辑上来看,这一堆广告算是咋回事!这个吐槽是从用户的角度出发的。很显然,返回的结果中,尤其是前几条,有时甚至是前几页,都跟我们想要的结果相差深远! …...

【pdf密码】为什么我的PDF文件不能复制文字?

大家现在接触PDF文件越来越多&#xff0c;有的时候在网上下载的PDF文件打开之后&#xff0c;发现选中文字之后无法复制。甚至其他功能也都无法使用&#xff0c;这是怎么回事&#xff1f;该怎么办&#xff1f; 当我们发现文件打开之后&#xff0c;编辑功能无法使用&#xff0c;很…...

vim程序编辑器

最近读书读完感觉没有读一样&#xff0c;所以决定写笔记记录下 vi的使用包括三种模式 一般模式 编辑模式 a&#xff0c;i&#xff0c;o&#xff0c;r&#xff0c;A,I,O,R 用来插入或者替换内容 命令行模式 &#xff1a;wq保存退出&#xff1a;wq!强制保存退出&#xf…...

新手如何学习挖漏洞?看这篇就够了【网络安全】

什么是漏洞挖掘 漏洞挖掘是指通过分析软件、系统或网络中存在的安全漏洞来发现并利用这些漏洞。漏洞挖掘是信息安全领域的一项重要工作&#xff0c;可以帮助企业和组织提高系统的安全性&#xff0c;避免黑客攻击和数据泄露。 漏洞挖掘的流程一般可以概括为以下几个步骤&#x…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...