前端之vue 根据菜单自动生成路由(动态配置前端路由)

作者 : admin 本文共7234个字,预计阅读时间需要19分钟 发布时间: 2023-12-18 共3人阅读

在需要权限控制的页面,往往存在根据用户来显示菜单的情况,单独根据用户类型判断显然不是很好,如果后面用户类型发生变化,项目修改维护可能就会比较麻烦,所以比较好的做法是根据后端返回的菜单动态生成页面路由,以达到完全权限控制的目的,并且若权限发生变化,仅需该配置数据即可

1.创建项目

首先用vue-cli3创建好项目

2.新建文件

创建好项目后,新建我们需要的文件。结构如图

前端之vue 根据菜单自动生成路由(动态配置前端路由)插图

在这里插入图片描述

下载相关依赖包 :element-ui(菜单样式用) 和 axios(获取菜单用)

npm i element-ui axios --save

3.到main.js中

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import elementUi from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(elementUi)

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

4.先把菜单组件写好,到menu.vue中

这里使用element-ui的el-menu组件


  
    
      
        
          
            
              
                
                  
                  
                
                {{menu.label}}
              
            
            
          
        
        
          {{cMenu.label}}
        
      
    
  




  .menulist-inner{
    min-height:calc(100vh - 50px);
    .menuItemBox{
      height:36px;
      &:hover{
        background:rgba(62,70,120,.4);
        &>span{
          color: #fff;
        }
      }
      span{
        color:#ffffff;
        flex-wrap:nowrap;
        width:150px;
        cursor:pointer;
      }
    }
    .activeMenu{
      background:rgba(62,70,120,1);
    }
  }
  .menuTitlBox{
    .menuTitle{
      position: relative;
      z-index: 10;
    }
    .firstMenuBk{
      position: absolute;
      left:0;
      top:50%;
      width:200px;
      height:56px;
      background:#3e4678;
      -webkit-transform: translateY(-50%);
      -moz-transform: translateY(-50%);
      -ms-transform: translateY(-50%);
      -o-transform: translateY(-50%);
      transform: translateY(-50%);
    }
    .whiteText{
      color:#ffffff;
    }
  }



  .el-submenu{
    position: relative;
  }
  .el-menu{
    border:none !important;
  }
  .el-submenu__icon-arrow{
    width:0;
    height:0;
    color:transparent;
  }
  .el-submenu__icon-arrow:before{
    content:'' !important;
  }
  .el-submenu__icon-arrow:before{
    content:'' !important;
  }
  .el-submenu__title{
    background:rgba(44,55,71,1) !important;
    margin-bottom:5px;
    height:46px !important;
    line-height: 46px !important;
  }

  .el-menu.el-menu--inline{
    background-color: rgba(0,0,0,.2) !important;
  }

  .menulist-inner .menuItemBox.rowEnd span{
    color: #A7BAC6;
  }
  .menulist-inner .menuItemBox.rowEnd.activeMenu span{
    color: #fff;
  }

5.注册全局组件

将menu组件注册为全局组件,方便需要的地方直接引入
到global-components.js文件中

/*
 全局组件引入注册
  */

import Menu from "../global-components/menu" //菜单

export default {
    install(Vue) {
        Vue.component('Menu',Menu)
    }
};

6.到router文件夹写好路由模块

6.1 base-router.js中写好我们需要的固定的路由
/**
 * 页面上固定路由
 */
import Layout from '../views/layout/layout'
import Login from '../views/login/login'
import Loading from '../views/loading/loading'
const routes = [
    {
        path: '/',
        redirect: '/loading',
    },
    {
        path: '/layout',
        component:Layout,
        redirect: '/loading',
    },
    {
        path: '/login',
        name: 'Login',
        component: Login
    },
    {
        path: '/loading',
        name: 'Loading',
        component: Loading
    },
]
export default routes
6.2 lm-router.js中写动态配置路由的方法
import {setUserRoutesData} from "../utils/global-methods";

const RouterPlugin = function() {
    this.$router = null
    this.$store = null
}
RouterPlugin.install = function(router, store) {
    this.$router = router
    this.$store = store
    this.$router.$lmRouter = {
        // 全局配置
        safe: this,
        // 动态路由
        formatRoutes:function (routes) {
            let routers=setUserRoutesData(routes)
            this.safe.$router.addRoutes(routers)
            return routers
        }
    }
}
export default RouterPlugin
6.3 index.js中写路由入口
import Vue from 'vue'
import VueRouter from 'vue-router'
import baseRoutes from './base-router'
import store from '../store'
import LmRouter from './lm-router'


Vue.use(VueRouter)

const createRouter = () => {
    return new VueRouter({
        scrollBehavior (to, from, savedPosition) {
            if (savedPosition) {
                return savedPosition
            } else {
                return {
                    x: 0,
                    y: to.meta.savedPosition || 0
                }
            }
        },
        routes: [...baseRoutes],
        mode:'history',//
    })
}
let router = createRouter()
/**
resetRouter函数用于重置路由,每一次动态配置路由之前要先重置路由
**/
export function resetRouter() {
    const newRouter = createRouter()
    router.matcher = newRouter.matcher // reset router
    LmRouter.install(router, store)
}
/**
当用户刷新页面时,路由数据会丢失,如果已经登录,需要重新渲染路由。渲染的路由数据在登录时存在浏览器本地
**/
if(sessionStorage.getItem('hasLogin')){
    let sessionMenus=localStorage.getItem('menus')
    sessionMenus=sessionMenus ? JSON.parse(sessionMenus) : []
    let sessionRoutes=localStorage.getItem('userRoutes')
    LmRouter.install(router, store)
    sessionRoutes=sessionRoutes ? JSON.parse(sessionRoutes) : []
    router.$lmRouter.formatRoutes(sessionRoutes, true)
    store.dispatch('setUserRoutes',sessionRoutes)
    store.dispatch('setMenus',sessionMenus)
}

router.beforeEach((to, from, next) => {
    let hasLogin = sessionStorage.getItem('hasLogin')
    // console.log(to, from)
    if(to.name===from.name){
        return
    }
    console.log(hasLogin)
    if(to.name==='Login'){
        sessionStorage.clear()
        localStorage.clear()
        store.state.userRoutes=[]
        store.state.menus=[]
        next({replace:true})
        resetRouter()
        return
    }
    if (!hasLogin) {
        next({path: '/login',replace:true})
        return
    }
    next()

})
/**
 * 解决element-ui点击同一个菜单报错
 * @type {VueRouter.push|*}
 */
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
    return originalPush.call(this, location).catch(err => err)
}
export default router

7.容器页和加载页

大部分有二级菜单的页面,父级页面通常只需要路由跳转功能,代码一致,为了精简代码,我们新建通用的路由容器页面layout.vue,二级菜单路由页面经layout页面跳转

为避免用户在浏览器直接输入、粘贴路径时,或者直接访问页面根路径时找不到路由(因为用户可能已经登录了,但这是没有已经登录界面),我们新建loading.vue,当用户粘贴路径,或者访问根路径时,在loading.vue中渲染路由

7.1 layout.vue


    
        
头部
7.2 loading.vue


    
        
            
            页面加载中...
        

    





    .loadingBox{
        .loadingContentbox{
            position: absolute;
            left:50%;
            top:50%;
            -webkit-transform: translate(-50%,-50%);
            -moz-transform: translate(-50%,-50%);
            -ms-transform: translate(-50%,-50%);
            -o-transform: translate(-50%,-50%);
            transform: translate(-50%,-50%);
            .el-icon-loading{
                font-size: 40px;
                margin-bottom:10px;
            }
        }
    }

8.写筛选菜单和路由的方法

utils,global-methods.js中写菜单筛选和路由数据方法
首先我们假设后端返回的数据为如下结构(仅列出关键字段)

[
  {
    label:'人员管理',
    path:'/person',
    isLeftMenu:1,
    routeName:'Person',
    isContainer:1,
    children:[
     {
        label:'人员列表',
        path:'/person/person-list',
        routeName:'PersonList',
         isLeftMenu:1,
         component:'/person/person-list',
    },
     {
        label:'新增人员',
        path:'/person/person-add',
        routeName:'PersonAdd',
         component:'/person/person-add',
         isLeftMenu:0
     }
    ]
  },
    {
      label:'订单管理',
      path:'/order',
      isLeftMenu:1,
      routeName:'Order',
       component:'/order/order',
      isContainer:0,
    }
]

其中 label 为菜单显示标题,path 是路由路径,isLeftMenu用于区分是否为菜单,这里isLeftMenu=1表示是菜单,routeName路由名,isContainer用于区分路由是否经过容器组件layout,这里isContainer=1表示使用layout容器,component用于指定组件引入的路径
根据上面的数据结构,我们新建好对应得vue文件
结构如下图:

在这里插入图片描述

// 公共函数模块,用import引用
// 根据日期时间值获取字符串各是日期
import Layout from '../views/layout/layout'

//获取并筛选菜单
export const getAndFilterMenus=(menus)=> {
    // console.log(menus)
    menus=hanldeChildAppRoute(menus)
    for(let i=0;i{
    // console.log(routes)
    !isChild && (routes=hanldeChildAppRoute(routes))
    for(let i=0;i import( `../views${component}`)
        }
        routes[i].name=routeName
        /**
        如果有子路由,那就用递归继续生成路由
        **/
        if((routes[i].children instanceof Array) && routes[i].children.length){
            setUserRoutesData(routes[i].children,isChild)
        }
    }
    return routes
}

9. 登录成功后生成路由

最后看下登录页,我们将在登录成功后获取菜单数据,然后动态生成页面路由,并且通过Vue路由的addRoutes方法将路由添加到VueRouter中


    
        
            
                
                    
                        登录/Login
                    
                    
                        
                    
                    
                        
                    
                    
                        
                            登录
                            
                                
                                
                            
                        
                    
                
            
        
    



    .login-wrap{
        position: relative;
        background:#111111;
        height:100vh;
        overflow: hidden;
        .loginContent{
            .loginFormBox{
                position: absolute;
                left:50%;
                top:50%;
                -webkit-transform: translate(-50%,-50%);
                -moz-transform: translate(-50%,-50%);
                -ms-transform: translate(-50%,-50%);
                -o-transform: translate(-50%,-50%);
                transform: translate(-50%,-50%);
                .loginForm{
                    width:300px;
                    padding:20px;
                    .loginTitleBox{
                        margin-bottom:36px;
                        .loginTitle{
                            color:#ffffff;

                        }
                    }

                    .loginBtnBox{
                        width:100%;
                        margin-top:5vh;
                        .loginBtn{
                            width:100%;
                            height:40px;
                            background:linear-gradient(90deg,rgba(15,70,193,1),rgba(0,147,168,1));
                            border-radius:4px;
                            color:#ffffff;
                            cursor:pointer;
                        }
                    }
                }
            }

        }
    }



    .login-wrap .el-input{
        border-bottom: 1px solid;
        border-image: -webkit-linear-gradient(90deg,rgba(1,233,189,1) 0%,rgba(0,124,222,1) 100%) 30 30;
        border-image: -moz-linear-gradient(90deg,rgba(1,233,189,1) 0%,rgba(0,124,222,1) 100%) 30 30;
        border-image: linear-gradient(90deg,rgba(1,233,189,1) 0%,rgba(0,124,222,1) 100%) 30 30;
    }
    .login-wrap .el-input__inner{
        background:transparent;
        -webkit-border-radius: 0;
        -moz-border-radius: 0;
        border-radius: 0;
        border:none;
        color:#ffffff;
    }
    .login-wrap .el-input-group__append{
        padding:0 !important;
        border:none !important;
        background:transparent !important;
    }


本站无任何商业行为
个人在线分享 » 前端之vue 根据菜单自动生成路由(动态配置前端路由)
E-->