Ant Design Vue 动态路由

本文详细介绍了基于Vue.js的前端路由配置,包括main.js中引入router、router-index.js的基础路由设置、permission.js中的权限管理、以及动态路由生成。同时,展示了BasicLayout.vue的渲染过程,涉及全局布局、侧边菜单、路由视图等组件的使用。通过这些配置,实现了登录验证、页面跳转、404错误页面以及动态加载路由等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

main.js 引入 router

import router from './router'
new Vue({
  router,
  i18n,
  created: bootstrap,
  render: h => h(App)
}).$mount('#app')

router - index.js new Router

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

Vue.use(Router)

import { constantRouterMap } from '@/config/router.config'
const createRouter = () => new Router({
  mode: 'history',
  base:  "booking",
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRouterMap
})

const router = createRouter()
export default router

配置基础路由

import { UserLayout} from '@/layouts'

export const constantRouterMap = [
  {
    component: UserLayout,
    path: '/',
    redirect: '/Login',
    children: [{
      path: '/Login',
      name:"Login",
      component: () => import('@/views/Home/Login')
    },
    {
      path: '/404',
      name:"404",
      component: () => import('@/views/exception/404'),
    },
    
    ]
  },
]

路由处理 - permission.js

import router from '@/router'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import TokenCache from '@/utils/cache/TokenCache'
import OperatorCache from '@/utils/cache/OperatorCache'
import { initRouter } from '@/utils/routerUtil'
import defaultSettings from '@/config/defaultSettings'

NProgress.configure({ showSpinner: false }) // NProgress Configuration  

const whiteList = ['Login'] // no redirect whitelist

router.beforeEach((to, from, next) => {
  NProgress.start() // start progress bar
  to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
  // 已授权
  if (TokenCache.getToken()) {
    OperatorCache.init(() => {
      if (to.path === '/Home/Login') {
        next({ path: '/' })
        NProgress.done()
      } else { 
        initRouter(to, from, next).then(() => {
          const redirect = decodeURIComponent(from.query.redirect || to.path)
          //桌面特殊处理
          if (to.path == defaultSettings.desktopPath || to.path == '/404') {
            next()
          } else {
            if (to.path === redirect) {
              next()
            } else {
              // 跳转到目的路由
              next({ path: redirect })
            }
          }
        })
      }
    })
  } else {
    if (whiteList.includes(to.name)) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next({ path: '/', query: { redirect: to.fullPath } })
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
  }
})

router.afterEach(() => {
  NProgress.done() // finish progress bar
})

路由接口

import router from '@/router'
import { Axios } from '@/utils/plugin/axios-plugin'
import { BasicLayout, PageView } from '@/layouts'
import ProcessHelper from '@/utils/helper/ProcessHelper'
import defaultSettings from '@/config/defaultSettings'
import i18n from '../../config/i18n'
var uuid = require('node-uuid')

let inited = false
let addRouter = []

export const getAddRouter = () => {
  return addRouter
}

// 前端未找到页面路由(固定不用改)
const notFoundRouter = {
  path: '*',
  redirect: '/404',
  hidden: true
}

// 开发模式额外路由
const devRouter = [
  {
    title: '开发',
    icon: 'code',
    children: [
      {
        path: '/Base_Manage/Base_DbLink/List',
        title: '数据库连接'
      }
    ]
  },
]
//初始化
export const initRouter = (to, from, next) => {
  return new Promise((res, rej) => {
    if (inited) {
      res()
    } else {
      generatorDynamicRouter().then(dynamicRouter => {
        router.addRoutes(dynamicRouter)
        addRouter = dynamicRouter
        inited = true
        //路由缓存
        localStorage.setItem('routers',dynamicRouter);
        next({ ...to, replace: true })
      })
    }
  })
}

/**
 * 获取路由菜单信息
 * @returns {Promise<any>}
 */
const generatorDynamicRouter = () => {
  return new Promise((resolve, reject) => {
    // ajax
    getRouterByUser().then(res => {
        let allRouters = []

        //首页根路由
        let rootRouter = {
          // 路由地址 动态拼接生成如 /dashboard/workplace
          path: '/',
          redirect: defaultSettings.desktopPath,
          // 路由名称,建议唯一
          name: uuid.v4(),
          // 该路由对应页面的 组件
          component: BasicLayout,
          // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
          meta: { title: '首页' },
          children: []
        }
        allRouters.push(rootRouter)

        if (!ProcessHelper.isProduction()) {
          res.push(...devRouter)
        }
        //格式化路由
        rootRouter.children = generator(res)
        allRouters.push(notFoundRouter)

        //返回组装后的路由
        resolve(allRouters)
      })
      .catch(err => {
        reject(err)
      })
  })
}

/**
 * 获取后端路由
 * @returns {Promise}
 */
const getRouterByUser = () => {
  return new Promise((resolve, reject) => {
    Axios.post('/Home/GetMenu', {}).then(resJson => {
      if (resJson.Success) {
        resolve(resJson.Data)
      }
    })
  })
}

/**
 * 格式化 后端 结构信息并递归生成层级路由表
 *
 * @param routerMap
 * @param parent
 * @returns {*}
 */
const generator = (routerMap, parent) => {
  return routerMap.map(item => {
    let hasChildren = item.children && item.children.length > 0
    let component = {}
    if (hasChildren) {
      component = PageView
    } else if (item.path) {
      component = () => import(`@/views${item.path.replace(new RegExp(/\/\:.*$/), '')}`)
    }
    let currentRouter = {
      path: '',
      // 路由名称,建议唯一
      name: uuid.v4(),
      // 该路由对应页面的 组件
      component: component,
      // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
      // 这里添加了国际化
      meta: { title: i18n.t(`menu.${item.Lan_id}`), icon: item.icon || undefined }
    }

    //有子菜单
    if (hasChildren) {
      currentRouter.path = `/${uuid.v4()}`
    } else if (item.path) {
      //页面
      currentRouter.path = item.path
      currentRouter.path = currentRouter.path.replace('//', '/')
    }

    // 重定向
    item.redirect && (currentRouter.redirect = item.redirect)
    // 是否有子菜单,并递归处理
    if (hasChildren) {
      // Recursion
      currentRouter.children = generator(item.children, currentRouter)
    }
    return currentRouter
  })
}

BasicLayout.vue的渲染

<template>
  <a-layout :class="['layout', device]">
    <!-- SideMenu -->
    <a-drawer
      v-if="isMobile()"
      placement="left"
      :wrapClassName="`drawer-sider ${navTheme}`"
      :closable="false"
      :visible="collapsed"
      @close="drawerClose"
    >
      <side-menu
        mode="inline"
        :menus="menus"
        :theme="navTheme"
        :collapsed="false"
        :collapsible="true"
        @menuSelect="menuSelect"
      ></side-menu>
    </a-drawer>

    <side-menu
      v-else-if="isSideMenu()"
      mode="inline"
      :menus="menus"
      :theme="navTheme"
      :collapsed="collapsed"
      :collapsible="true"
    ></side-menu>

    <a-layout
      :class="[layoutMode, `content-width-${contentWidth}`]"
      :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }"
    >
      <!-- layout header -->
      <global-header
        :mode="layoutMode"
        :menus="menus"
        :theme="navTheme"
        :collapsed="collapsed"
        :device="device"
        @toggle="toggle"
      />

      <!-- layout content -->
      <a-layout-content
        :style="{ height: '100%', margin: '24px 24px 0', paddingTop: fixedHeader ? '64px' : '0' }"
      >
        <multi-tab v-if="multiTab"></multi-tab>
        <transition name="page-transition">
          <!-- <route-view /> -->
          <div class="content">
            <div class="page-header-index-wide">
              <slot>
                <!-- keep-alive  -->
                <keep-alive v-if="multiTab">
                  <router-view ref="content" />
                </keep-alive>
                <router-view v-else ref="content" />
              </slot>
            </div>
          </div>
        </transition>
      </a-layout-content>

      <!-- layout footer -->
      <!-- <a-layout-footer>
        <global-footer />
      </a-layout-footer>-->

      <!-- Setting Drawer (show in development mode) -->
      <!-- <setting-drawer v-if="!production"></setting-drawer> -->
    </a-layout>
  </a-layout>
</template>

<script>
import { triggerWindowResizeEvent } from '@/utils/util'
import { mapState, mapActions } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin'
import config from '@/config/defaultSettings'

import RouteView from './RouteView'
import MultiTab from '@/components/MultiTab'
import SideMenu from '@/components/Menu/SideMenu'
import GlobalHeader from '@/components/GlobalHeader'
import GlobalFooter from '@/components/GlobalFooter'
// import SettingDrawer from '@/components/SettingDrawer'
import { getAddRouter } from '@/utils/routerUtil'

export default {
  name: 'BasicLayout',
  mixins: [mixin, mixinDevice],
  components: {
    RouteView,
    MultiTab,
    SideMenu,
    GlobalHeader,
    GlobalFooter
    // SettingDrawer
  },
  data() {
    return {
      production: config.production,
      collapsed: false,
      menus: []
    }
  },
  computed: {
    ...mapState({
      // 动态主路由
      mainMenu: state => getAddRouter()
    }),
    contentPaddingLeft() {
      if (!this.fixSidebar || this.isMobile()) {
        return '0'
      }
      if (this.sidebarOpened) {
        return '220px'
      }
      return '80px'
    }
  },
  watch: {
    sidebarOpened(val) {
      this.collapsed = !val
    }
  },
  created() {
    //menus赋值
    this.menus = this.mainMenu.find(item => item.path === '/').children
    this.collapsed = !this.sidebarOpened
  },
  mounted() {
    const userAgent = navigator.userAgent
    if (userAgent.indexOf('Edge') > -1) {
      this.$nextTick(() => {
        this.collapsed = !this.collapsed
        setTimeout(() => {
          this.collapsed = !this.collapsed
        }, 16)
      })
    }
  }
}
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值