动态路由去重问题

问题:在切换组织或者菜单权限更改导致路由改变,使用addRoutes API添加路由的时候,重复添加已存在的路由导致出现重复路由的警告。权限路由改变时,知道地址,低级权限也可以进入高级权限的页面。

router.js  路由文件

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'
export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: '首页', icon: 'dashboard' }
    }]
  }]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
// 解决切换权限时重复添加路由,某些路由已经注入的问题
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

对从后端接口取得的数据进行递归处理

import router from '@/router';

export function filterRouter(asyncRouterMap){
	return asyncRouterMap.filter(route => {

        if (route.children) {
            route.children = filterChildren(route.children)
        }
        if (route.component) {

            // Layout ParentView 组件特殊处理
            if (route.component === 'Layout') {
                route.component = Layout
            } else if (route.component === 'ParentView') {
                route.component = ParentView
            } else if (route.component === 'InnerLink') {
                route.component = InnerLink
            } else {
                route.component = loadView(route.component)
            }
        }
        if (route.children != null && route.children && route.children.length) {
            route.children = filterRouter(route.children)
        } else {
            delete route.children
            delete route.redirect
        }
        return true
    })
};

function filterChildren(childrenMap) {
    var children = []
    childrenMap.forEach((el, index) => {
        if (el.children && el.children.length) {
            if (el.component === 'ParentView') {
                el.children.forEach(c => {
                    c.path = el.path + '/' + c.path
                    if (c.children && c.children.length) {
                        children = children.concat(filterChildren(c.children, c))
                        return
                    }
                    children.push(c)
                })
                return
            }
        }
        children = children.concat(el)
    })
    return children
}

export const loadView = (view) => {
    if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
        return (resolve) => require([`@/views${view}`], resolve)
    } else {
        // 使用 import 实现生产环境的路由懒加载
        return () => import(`@/views${view}`)
    }
}

使用,调用获取路由的接口

在登录成功调用,或者切换权限菜单的时候

写在store/modules/permission.js中

//permission.js
import {constantRoutes} from '@/router'
import store from '../index'
import  router ,{resetRouter} from '@/router'
import  {filterRouter}  from './../setRouter.js'
 
state: {
        routes: [],
        addRoutes: [],
        sidebarRouters:[],
    },
mutations: {
    SET_ROUTES: (state, routes) => {
            state.addRoutes = routes
            state.routes = constantRoutes.concat(routes)
        },
    SET_SIDEBAR_ROUTERS: (state, routes) => {
            state.sidebarRouters = routes
        },
   
},
 actions: {
        // 生成路由
        generateRoutes({commit}) {
            return new Promise(resolve => {
                getRouters({
                    appId: sessionStorage.getItem('appId'),
                    tenantId: store.getters.tenantInfo.tenantId
                }).then(res => {
                    // 过滤菜单
                    const data = JSON.parse(JSON.stringify(res.data))
                    const routes = filterRouter(data)
                    commit('SET_ROUTES', routes)
                    commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(routes))
                    resetRouter();  //路由去重
                    router.addRoutes(routes) // 动态添加可访问路由表
                    resolve(routes)
                })
            })
        }
    }

export default {
    namespaced: true,
    state,
    mutations,
    actions
};

store.js中引入permission.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    permission,
  },
  getters
})

export default store

main.js中引入store.js

import Vue from "vue";
import App from "./App";
import store from "./store";
import router from "./router";


new Vue({
  el: "#app",
  router,
  store,
  render: h => h(App)
});

最后在登录成功或者权限改变的时候调用

 this.$store.dispatch('permission/generateRoutes')

若需要对动态路由进行数据持久化,满足每次刷新重新添加一次路由,不刷新持久化,就用store存储来实现,路由拦截器里

const whiteList = ['/login']
router.beforeEach(async (to, from, next) => {
    let flag = sessionStorage.getItem("token");
    // progress
    NProgress.start()
    document.title = getPageTitle(to.meta.title)
    if (flag) {
        if (to.path === '/login') {
            sessionStorage.clear()
        }
        //判断store.state.permission.sidebarRouters.length 是否为0
	  	//如果为0  则证明页面刷新过 
        if (store.state.permission.sidebarRouters.length === 0) {
            this.$store.dispatch('permission/generateRoutes').then(()=> {    
                   next({...to, replace: true}) // hack方法 确保addRoutes已完成
            })
             
        } else {
            next()
        }
        NProgress.done()
    } else if (to.path === '/login') { // 未登录,重定向首页
        next()
    } else { // 未登录
      if (whiteList.indexOf(to.path) !== -1) {
            // in the free login whitelist, go directly
            next()
        } else {
            // other pages that do not have permission to access are redirected to the login page.
            next(`/login?redirect=${to.path}`)
            NProgress.done()
        }
    }
})

页面按钮权限判断

utils/btnPermission.js

//首先引入Vue
import Vue from "vue";

const btnPermission = Vue.directive("btnPermission", {
  //绑定元素插入到DOM中  要做的事
  // 参数: 绑定的元素, 信息 , 虚拟节点对象
  //binding
    //binding:一个对象,包含以下 property:
	//name:指令名,不包括 v- 前缀。
	//value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
	//oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
	//expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
	//arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
	//modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
	//vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
	//oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 
	
  inserted: function(el, binding, vnode) {
    let btn = "";
    //获取单个按钮权限指令所绑定的值
    let btnAdmin = binding.value;
    //判断是否有绑定值  没有就直接return
    if (btnAdmin) {
      btn = btnAdmin;
    } else {
      //无权限直接返回
      return;
    }
	//从虚拟节点中获取路由元信息
	//判断是否为空对象 如果是直接return
    if (JSON.stringify(vnode.context.$route.meta) == "{}") {
      return;
    }
    //从传入的虚拟节点中获取按钮权限列表
    let list = vnode.context.$route.meta.permission;
	// 判断列表中是否含有 这个指令绑定的值 没有就隐藏这个按钮
    if (list.indexOf(btn) == -1) {
      el.style.display = "none"; //隐藏元素
    }
  }
});

main.js 中引入 utils/permission.js

//自定义权限指令has 必须引入
import btnPermission from "./utils/btnPermission";
Vue.use(btnPermission)

页面使用按钮权限,从路由中取出按钮权限和页面权限比较,然后判断是否显示

<el-button @click='editClick' v-btnPermission="'edit'">编辑</el-button>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值