动态路由router

permission.js

import auth from '@/plugins/auth'

import router, { constantRoutes, dynamicRoutes } from '@/router'

import { getRouters } from '@/api/menu'

import Layout from '@/layout/index'

import ParentView from '@/components/ParentView'

import InnerLink from '@/layout/components/InnerLink'

const permission = {

  state: {

    routes: [],

    addRoutes: [],

    defaultRoutes: [],

    topbarRouters: [],

    sidebarRouters: []

  },

  mutations: {

    SET_ROUTES: (state, routes) => {

      state.addRoutes = routes

      state.routes = constantRoutes.concat(routes)

    },

    SET_DEFAULT_ROUTES: (state, routes) => {

      state.defaultRoutes = constantRoutes.concat(routes)

    },

    SET_TOPBAR_ROUTES: (state, routes) => {

      state.topbarRouters = routes

    },

    SET_SIDEBAR_ROUTERS: (state, routes) => {

      state.sidebarRouters = routes

    },

  },

  actions: {

    // 生成路由

    GenerateRoutes({ commit }) {

      return new Promise(resolve => {

        // 向后端请求路由数据

        getRouters().then(res => {

          const sdata = JSON.parse(JSON.stringify(res.data))

          const rdata = JSON.parse(JSON.stringify(res.data))

          const sidebarRoutes = filterAsyncRouter(sdata)

          const rewriteRoutes = filterAsyncRouter(rdata, false, true)

          const asyncRoutes = filterDynamicRoutes(dynamicRoutes);

          rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })

          router.addRoutes(asyncRoutes);

          commit('SET_ROUTES', rewriteRoutes)

          commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))

          commit('SET_DEFAULT_ROUTES', sidebarRoutes)

          commit('SET_TOPBAR_ROUTES', sidebarRoutes)

          resolve(rewriteRoutes)

        })

      })

    }

  }

}

// 遍历后台传来的路由字符串,转换为组件对象

function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {

  return asyncRouterMap.filter(route => {

    if (type && 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 = filterAsyncRouter(route.children, route, type)

    } else {

      delete route['children']

      delete route['redirect']

    }

    return true

  })

}

function filterChildren(childrenMap, lastRouter = false) {

  var children = []

  childrenMap.forEach((el, index) => {

    if (el.children && el.children.length) {

      if (el.component === 'ParentView' && !lastRouter) {

        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

      }

    }

    if (lastRouter) {

      el.path = lastRouter.path + '/' + el.path

    }

    children = children.concat(el)

  })

  return children

}

// 动态路由遍历,验证是否具备权限

export function filterDynamicRoutes(routes) {

  const res = []

  routes.forEach(route => {

    if (route.permissions) {

      if (auth.hasPermiOr(route.permissions)) {

        res.push(route)

      }

    } else if (route.roles) {

      if (auth.hasRoleOr(route.roles)) {

        res.push(route)

      }

    }

  })

  return res

}

export const loadView = (view) => {

  if (process.env.NODE_ENV === 'development') {

    return (resolve) => require([`@/views/${view}`], resolve)

  } else {

    // 使用 import 实现生产环境的路由懒加载

    return () => import(`@/views/${view}`)

  }

}

export default permission

main.js

import './permission'

SiderBar/index

<template>

    <div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">

        <logo v-if="showLogo" :collapse="isCollapse" />

        <el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">

            <el-menu

                :default-active="activeMenu"

                :collapse="isCollapse"

                :background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"

                :text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"

                :unique-opened="true"

                :active-text-color="settings.theme"

                :collapse-transition="false"

                mode="vertical"

            >

                <sidebar-item

                    v-for="(route, index) in sidebarRouters"

                    :key="route.path  + index"

                    :item="route"

                    :base-path="route.path"

                />

            </el-menu>

        </el-scrollbar>

    </div>

</template>

<script>

import { mapGetters, mapState } from "vuex";

import Logo from "./Logo";

import SidebarItem from "./SidebarItem";

import variables from "@/assets/styles/variables.scss";

export default {

    components: { SidebarItem, Logo },

    computed: {

        ...mapState(["settings"]),

        ...mapGetters(["sidebarRouters", "sidebar"]),

        activeMenu() {

            const route = this.$route;

            const { meta, path } = route;

            // if set path, the sidebar will highlight the path you set

            if (meta.activeMenu) {

                return meta.activeMenu;

            }

            return path;

        },

        showLogo() {

            return this.$store.state.settings.sidebarLogo;

        },

        variables() {

            return variables;

        },

        isCollapse() {

            return !this.sidebar.opened;

        }

    }

};

</script>

SideBarItem

<template>

  <div v-if="!item.hidden">

    <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">

      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">

        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">

          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />

        </el-menu-item>

      </app-link>

    </template>

    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>

      <template slot="title">

        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />

      </template>

      <sidebar-item

        v-for="child in item.children"

        :key="child.path"

        :is-nest="true"

        :item="child"

        :base-path="resolvePath(child.path)"

        class="nest-menu"

      />

    </el-submenu>

  </div>

</template>

<script>

import path from 'path'

import { isExternal } from '@/utils/validate'

import Item from './Item'

import AppLink from './Link'

import FixiOSBug from './FixiOSBug'

export default {

  name: 'SidebarItem',

  components: { Item, AppLink },

  mixins: [FixiOSBug],

  props: {

    // route object

    item: {

      type: Object,

      required: true

    },

    isNest: {

      type: Boolean,

      default: false

    },

    basePath: {

      type: String,

      default: ''

    }

  },

  data() {

    this.onlyOneChild = null

    return {}

  },

  methods: {

    hasOneShowingChild(children = [], parent) {

      if (!children) {

        children = [];

      }

      const showingChildren = children.filter(item => {

        if (item.hidden) {

          return false

        } else {

          // Temp set(will be used if only has one showing child)

          this.onlyOneChild = item

          return true

        }

      })

      // When there is only one child router, the child router is displayed by default

      if (showingChildren.length === 1) {

        return true

      }

      // Show parent if there are no child router to display

      if (showingChildren.length === 0) {

        this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }

        return true

      }

      return false

    },

    resolvePath(routePath, routeQuery) {

      if (isExternal(routePath)) {

        return routePath

      }

      if (isExternal(this.basePath)) {

        return this.basePath

      }

      if (routeQuery) {

        let query = JSON.parse(routeQuery);

        return { path: path.resolve(this.basePath, routePath), query: query }

      }

      return path.resolve(this.basePath, routePath)

    }

  }

}

</script>

Item.vue

<script>

export default {

  name: 'MenuItem',

  functional: true,

  props: {

    icon: {

      type: String,

      default: ''

    },

    title: {

      type: String,

      default: ''

    }

  },

  render(h, context) {

    const { icon, title } = context.props

    const vnodes = []

    if (icon) {

      vnodes.push(<svg-icon icon-class={icon}/>)

    }

    if (title) {

      if (title.length > 5) {

        vnodes.push(<span slot='title' title={(title)}>{(title)}</span>)

      } else {

        vnodes.push(<span slot='title'>{(title)}</span>)

      }

    }

    return vnodes

  }

}

</script>

router/index.js

import Vue from 'vue'

import Router from 'vue-router'

Vue.use(Router)

/* Layout */

import Layout from '@/layout'

/**

 * Note: 路由配置项

 *

 * hidden: true                     // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1

 * alwaysShow: true                 // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面

 *                                  // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面

 *                                  // 若你想不管路由下面的 children 声明的个数都显示你的根路由

 *                                  // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由

 * redirect: noRedirect             // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击

 * name:'router-name'               // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题

 * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数

 * roles: ['admin', 'common']       // 访问路由的角色权限

 * permissions: ['a:a:a', 'b:b:b']  // 访问路由的菜单权限

 * meta : {

    noCache: true                   // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)

    title: 'title'                  // 设置该路由在侧边栏和面包屑中展示的名字

    icon: 'svg-name'                // 设置该路由的图标,对应路径src/assets/icons/svg

    breadcrumb: false               // 如果设置为false,则不会在breadcrumb面包屑中显示

    activeMenu: '/system/user'      // 当路由设置了该属性,则会高亮相对应的侧边栏。

  }

 */

// 公共路由

export const constantRoutes = [

  {

    path: '/redirect',

    component: Layout,

    hidden: true,

    children: [

      {

        path: '/redirect/:path(.*)',

        component: () => import('@/views/redirect')

      }

    ]

  },

  {

    path: '/login',

    component: () => import('@/views/login'),

    hidden: true

  },

  {

    path: '/register',

    component: () => import('@/views/register'),

    hidden: true

  },

  {

    path: '/404',

    component: () => import('@/views/error/404'),

    hidden: true

  },

  {

    path: '/401',

    component: () => import('@/views/error/401'),

    hidden: true

  },

  {

    path: '',

    component: Layout,

    redirect: 'index',

    children: [

      {

        path: 'index',

        component: () => import('@/views/index'),

        name: 'Index',

        meta: { title: '首页', icon: 'dashboard', affix: true }

      }

    ]

  },

  {

    path: '/user',

    component: Layout,

    hidden: true,

    redirect: 'noredirect',

    children: [

      {

        path: 'profile',

        component: () => import('@/views/system/user/profile/index'),

        name: 'Profile',

        meta: { title: '个人中心', icon: 'user' }

      }

    ]

  }

]

// 动态路由,基于用户权限动态去加载

export const dynamicRoutes = [

  {

    path: '/system/user-auth',

    component: Layout,

    hidden: true,

    permissions: ['system:user:edit'],

    children: [

      {

        path: 'role/:userId(\\d+)',

        component: () => import('@/views/system/user/authRole'),

        name: 'AuthRole',

        meta: { title: '分配角色', activeMenu: '/system/user' }

      }

    ]

  },

  {

    path: '/system/role-auth',

    component: Layout,

    hidden: true,

    permissions: ['system:role:edit'],

    children: [

      {

        path: 'user/:roleId(\\d+)',

        component: () => import('@/views/system/role/authUser'),

        name: 'AuthUser',

        meta: { title: '分配用户', activeMenu: '/system/role' }

      }

    ]

  },

  {

    path: '/system/dict-data',

    component: Layout,

    hidden: true,

    permissions: ['system:dict:list'],

    children: [

      {

        path: 'index/:dictId(\\d+)',

        component: () => import('@/views/system/dict/data'),

        name: 'Data',

        meta: { title: '字典数据', activeMenu: '/system/dict' }

      }

    ]

  },

  {

    path: '/monitor/job-log',

    component: Layout,

    hidden: true,

    permissions: ['monitor:job:list'],

    children: [

      {

        path: 'index/:jobId(\\d+)',

        component: () => import('@/views/monitor/job/log'),

        name: 'JobLog',

        meta: { title: '调度日志', activeMenu: '/monitor/job' }

      }

    ]

  },

  {

    path: '/tool/gen-edit',

    component: Layout,

    hidden: true,

    permissions: ['tool:gen:edit'],

    children: [

      {

        path: 'index/:tableId(\\d+)',

        component: () => import('@/views/tool/gen/editTable'),

        name: 'GenEdit',

        meta: { title: '修改生成配置', activeMenu: '/tool/gen' }

      }

    ]

  }

]

// 防止连续点击多次路由报错

let routerPush = Router.prototype.push;

let routerReplace = Router.prototype.replace;

// push

Router.prototype.push = function push(location) {

  return routerPush.call(this, location).catch(err => err)

}

// replace

Router.prototype.replace = function push(location) {

  return routerReplace.call(this, location).catch(err => err)

}

export default new Router({

  mode: 'history', // 去掉url中的#

  scrollBehavior: () => ({ y: 0 }),

  routes: constantRoutes

})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值