前端实现菜单&按钮级权限

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

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

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

前端实现菜单&按钮级权限_第1张图片

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

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

stores 里面的 counter.ts

import { ref } from 'vue'
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  // token
  const token = ref('')
  // 动态路由
  const dynamicRoutes = ref([])

  // 设置token
  const setToken = (t: string) => { token.value = t }
  // 设置动态路由
  const setDynamicRoutes = (r: any) => { dynamicRoutes.value = r }

  // 清空token
  const 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



点击登录页面的登录按钮后,会跳转到  '/'  ,路由变化了,就会触发全局前置导航守卫,全局前置导航守卫我写在了 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().token
        if (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 { // 不存在token
            next('/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 

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






递归组件MenuTree.vue






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



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

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

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

你可能感兴趣的:(前端,javascript,vue.js)