工作记录 根据菜单列表生成路由数据

// 根据菜单列表,生成路由数据

json

{
  "code": 0,
  "msg": "success",
  "data": [
    {
      "id": 1,
      "name": "系统管理",
      "url": null,
      "openStyle": 0,
      "icon": "icon-setting",
      "children": [
        {
          "id": 11,
          "name": "菜单管理",
          "url": "menu/index",
          "openStyle": 0,
          "icon": "icon-menu"
        },
        {
          "id": 12,
          "name": "用户管理",
          "url": "user/index",
          "openStyle": 0,
          "icon": "icon-user"
        },
        {
          "id": 13,
          "name": "机构管理",
          "url": "org/index",
          "openStyle": 0,
          "icon": "icon-cluster"
        },
        {
          "id": 14,
          "name": "角色管理",
          "url": "role/index",
          "openStyle": 0,
          "icon": "icon-team"
        },
        {
          "id": 15,
          "name": "岗位管理",
          "url": "post/index",
          "openStyle": 0,
          "icon": "icon-addteam"
        }
      ]
    },
    {
      "id": 3,
      "name": "图表",
      "url": null,
      "openStyle": 0,
      "icon": "icon-barchart",
      "children": [
        {
          "id": 31,
          "name": "堆叠面积图",
          "url": "charts/AreaStack",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 32,
          "name": "虚线柱状图",
          "url": "charts/CategoryStack",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 33,
          "name": "上证指数图",
          "url": "charts/Candlestick",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        }
      ]
    },
    {
      "id": 4,
      "name": "编辑器",
      "url": null,
      "openStyle": 0,
      "icon": "icon-edit",
      "children": [
        {
          "id": 41,
          "name": "富文本编辑器",
          "url": "editor/WangEditor",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 42,
          "name": "Markdown编辑器",
          "url": "editor/MdEditor",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        }
      ]
    },
    {
      "id": 5,
      "name": "界面",
      "url": null,
      "openStyle": 0,
      "icon": "icon-windows",
      "children": [
        {
          "id": 51,
          "name": "Icon 图标",
          "url": "icons/index",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 52,
          "name": "二维码生成",
          "url": "qrcode/index",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 53,
          "name": "页面打印",
          "url": "printJs/index",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 54,
          "name": "图片裁剪",
          "url": "cropper/index",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        },
        {
          "id": 55,
          "name": "复制文本",
          "url": "copy/index",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        }
      ]
    },
    {
      "id": 10,
      "name": "多级菜单",
      "url": null,
      "openStyle": 0,
      "icon": "icon-unorderedlist",
      "children": [
        {
          "id": 101,
          "name": "菜单1",
          "url": null,
          "openStyle": 0,
          "icon": "icon-unorderedlist",
          "children": [
            {
              "id": 1011,
              "name": "菜单11",
              "url": "menu/menu1/menu11/index",
              "openStyle": 0,
              "icon": "icon-unorderedlist"
            },
            {
              "id": 1012,
              "name": "菜单13",
              "url": null,
              "openStyle": 0,
              "icon": "icon-unorderedlist",
              "children": [
                {
                  "id": 10121,
                  "name": "菜单121",
                  "url": "menu/menu1/menu12/menu121/index",
                  "openStyle": 0,
                  "icon": "icon-unorderedlist"
                },
                {
                  "id": 10122,
                  "name": "菜单122",
                  "url": "menu/menu1/menu12/menu122/index",
                  "openStyle": 0,
                  "icon": "icon-unorderedlist"
                }
              ]
            },
            {
              "id": 1013,
              "name": "菜单13",
              "url": "menu/menu1/menu13/index",
              "openStyle": 0,
              "icon": "icon-unorderedlist"
            }
          ]
        },
        {
          "id": 102,
          "name": "菜单2",
          "url": "menu/menu2/index",
          "openStyle": 0,
          "icon": "icon-unorderedlist"
        }
      ]
    }
  ]
}
 

定义

// 加载vue组件

const layoutModules = import.meta.glob('/src/views/**/*.vue')

// 根据路径,动态获取vue组件

const getDynamicComponent = (path)=> {

    const component = layoutModules[`/src/views/${path}.vue`]

    if (!component) {

        console.error('component error', path)

    }

    return component

}

export const generateRoutes = (menuList):  => {

    const routerList = []

    menuList.forEach((menu: any) => {

        let component

        let path

        if (menu.children && menu.children.length > 0) {

            component = () => import('@/layout/index.vue')

            path = '/p/' + menu.id

        } else {

            component = getDynamicComponent(menu.url)

            path = '/' + menu.url

        }

        const route: RouteRecordRaw = {

            path: path,

            name: pathToCamel(path),

            component: component,

            children: [],

            meta: {

                title: menu.name,

                icon: menu.icon,

                id: '' + menu.id,

                cache: true,

                _blank: menu.openStyle === 1,

                breadcrumb: []

            }

        }

        // 有子菜单的情况

        if (menu.children && menu.children.length > 0) {

            route.children?.push(...generateRoutes(menu.children))

        }

        routerList.push(route)

    })

    return routerList

}

使用

import { defineStore } from 'pinia'

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

import { generateRoutes } from '@/router'

import { RouteRecordRaw } from 'vue-router'

export const routerStore = defineStore('routerStore', {

    state: () => ({

        menuRoutes: [],

        searchMenu: [],

        routes: [] 

    }),

    actions: {

        async getMenuRoutes() {

            const { data } = await useMenuNavApi()

            const routes = generateRoutes(data)

            this.menuRoutes.push(...routes)

            return this.menuRoutes

        },

        setSearchMenu(routers) {

            this.searchMenu = routers

        },

        setRoutes(routers) {

            this.routes = routers

        }

    }

})

页面中使用

<template>

    <el-scrollbar>

        <el-menu

            :default-active="defaultActive"

            :collapse="!store.appStore.sidebarOpened"

            :unique-opened="store.appStore.theme.uniqueOpened"

            background-color="transparent"

            :collapse-transition="false"

            mode="vertical"

            router

        >

            <menu-item v-for="menu in store.routerStore.menuRoutes" :key="menu.path" :menu="menu"></menu-item>

        </el-menu>

    </el-scrollbar>

</template>

<script setup >

import store from '@/store'

import MenuItem from './MenuItem.vue'

import { useRoute } from 'vue-router'

import { computed } from 'vue'

const route = useRoute()

const defaultActive = computed(() => {

    const { path } = route

    return path

})

</script>

<template>

    <el-sub-menu v-if="menu.children.length > 0" :key="menu.path" :index="menu.path">

        <template #title>

            <svg-icon :icon="menu.meta.icon"></svg-icon>

            <span>{{ menu.meta.title }}</span>

        </template>

        <menu-item v-for="sub in menu.children" :key="sub.path" :menu="sub"></menu-item>

    </el-sub-menu>

    <el-menu-item v-else :key="menu.path" :index="menu.path">

        <svg-icon :icon="menu.meta.icon"></svg-icon>

        <template #title>{{ menu.meta.title }}</template>

    </el-menu-item>

</template>

<script setup >

defineProps({

    menu: {

        type:Array,

        required: true

    }

})

</script>

完整路由

import { createRouter, createWebHistory, createWebHashHistory, RouteRecordRaw } from 'vue-router'

import NProgress from 'nprogress'

import 'nprogress/nprogress.css'

import store from '@/store'

import { i18n } from '@/i18n'

import { pathToCamel } from '@/utils/tool'

NProgress.configure({ showSpinner: false })

const constantRoutes: RouteRecordRaw[] = [

    {

        path: '/redirect',

        component: () => import('../layout/index.vue'),

        children: [

            {

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

                component: () => import('../layout/components/Router/Redirect.vue')

            }

        ]

    },

    {

        path: '/login',

        component: () => import('../views/login/login.vue')

    },

    {

        path: '/404',

        component: () => import('../views/404.vue')

    }

]

const asyncRoutes: RouteRecordRaw = {

    path: '/',

    component: () => import('../layout/index.vue'),

    redirect: '/home',

    children: [

        {

            path: '/home',

            name: 'Home',

            component: () => import('../views/home.vue'),

            meta: {

                title: i18n.global.t('router.home'),

                affix: true

            }

        },

        {

            path: '/profile/password',

            name: 'ProfilePassword',

            component: () => import('../views/profile/password.vue'),

            meta: {

                title: i18n.global.t('router.profilePassword'),

                cache: true

            }

        }

    ]

}

export const errorRoute: RouteRecordRaw = {

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

    redirect: '/404'

}

export const router = createRouter({

    history: createWebHashHistory(),

    routes: constantRoutes

})

// 白名单列表

const whiteList = ['/login']

// 路由加载前

router.beforeEach(async (to, from, next) => {

    NProgress.start()

    // token存在的情况

    if (store.userStore.token) {

        if (to.path === '/login') {

            next('/home')

        } else {

            // 用户信息不存在,则重新拉取用户等信息

            if (!store.userStore.user.id) {

                await store.userStore.getUserInfoAction()

                const menuRoutes = await store.routerStore.getMenuRoutes()

                // 根据后端菜单路由,生成KeepAlive路由

                const keepAliveRoutes = getKeepAliveRoutes(menuRoutes, [])

                // 添加菜单路由

                asyncRoutes.children?.push(...keepAliveRoutes)

                router.addRoute(asyncRoutes)

                // 错误路由

                router.addRoute(errorRoute)

                // 保存路由数据

                store.routerStore.setRoutes(constantRoutes.concat(asyncRoutes))

                // 搜索菜单需要使用

                store.routerStore.setSearchMenu(keepAliveRoutes)

                next({ ...to, replace: true })

            } else {

                next()

            }

        }

    } else {

        // 没有token的情况下,可以进入白名单

        if (whiteList.indexOf(to.path) > -1) {

            next()

        } else {

            next('/login')

        }

    }

})

// 路由加载后

router.afterEach(() => {

    NProgress.done()

})

// 获取扁平化路由,将多级路由转换成一级路由

export const getKeepAliveRoutes = (rs: RouteRecordRaw[], breadcrumb: string[]): RouteRecordRaw[] => {

    const routerList: RouteRecordRaw[] = []

    rs.forEach((item: any) => {

        if (item.meta.title) {

            breadcrumb.push(item.meta.title)

        }

        if (item.children && item.children.length > 0) {

            routerList.push(...getKeepAliveRoutes(item.children, breadcrumb))

        } else {

            item.meta.breadcrumb.push(...breadcrumb)

            routerList.push(item)

        }

        breadcrumb.pop()

    })

    return routerList

}

// 加载vue组件

const layoutModules = import.meta.glob('/src/views/**/*.vue')

// 根据路径,动态获取vue组件

const getDynamicComponent = (path: string): any => {

    const component = layoutModules[`/src/views/${path}.vue`]

    if (!component) {

        console.error('component error', path)

    }

    return component

}

// 根据菜单列表,生成路由数据

export const generateRoutes = (menuList: any): RouteRecordRaw[] => {

    const routerList: RouteRecordRaw[] = []

    menuList.forEach((menu: any) => {

        let component

        let path

        if (menu.children && menu.children.length > 0) {

            component = () => import('@/layout/index.vue')

            path = '/p/' + menu.id

        } else {

            component = getDynamicComponent(menu.url)

            path = '/' + menu.url

        }

        const route: RouteRecordRaw = {

            path: path,

            name: pathToCamel(path),

            component: component,

            children: [],

            meta: {

                title: menu.name,

                icon: menu.icon,

                id: '' + menu.id,

                cache: true,

                _blank: menu.openStyle === 1,

                breadcrumb: []

            }

        }

        // 有子菜单的情况

        if (menu.children && menu.children.length > 0) {

            route.children?.push(...generateRoutes(menu.children))

        }

        routerList.push(route)

    })

    return routerList

}

多级菜单

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值