vue3.x+element-plus 实现动态路由菜单功能

2,530 阅读3分钟

废话不多说直接上代码:

首先一般的后台管理都是有登录页面的 我们在登陆页面实现 具体的 接口调用 获取 动态菜单和权限

这里我们就简单mock一下 上代码:

vue3.x-admin\src\mock\menu.json

[    {        "path": "/home",        "meta": { "title":"首页", "icon":"house", "isHideSubMenu": true, "isAuth": true },        "component" : "components/layout/Template.vue",        "children" : [            {                "path": "",                "meta": { "title":"首页" },                "component" : "views/home/Index.vue"            }        ]    },    {        "path": "/article",        "redirect": "/article/list",        "meta": { "title":"文章管理", "icon":"document", "isAuth": true },        "auths": [ "article_add", "article_view", "article_delete", "article_edit" ],        "component" : "components/layout/Template.vue",        "children" : [            {                "path": "list",                "meta": { "title":"文章管理" },                "component" : "views/article/List.vue"            },            {                "path": "add",                "meta": { "isHide": true, "title":"新增文章" },                "component" : "views/article/Add.vue"            }        ]    }]

这是模拟的mock 菜单数据 我们在 登陆成功后 开始初始化 菜单

vue3.x-admin\src\views\login\Index.vue

<script>import { ref, watch, reactive, toRefs } from 'vue'import { ElLoading } from 'element-plus'import { useRoute, useRouter } from 'vue-router'import CookieUtil from 'cookie-tool-plug'import { useStore } from 'vuex'export default {    setup(props, context) {            const store = useStore()            const router = useRouter()            const state = reactive({                formInline: {                    account: 'admin',                    password: 'admin',                }            })            const onSubmit = () => {                const loading = ElLoading.service({                    lock: true,                    text: '初始化配置......',                    background: 'rgba(255,255,255, 1)',                })                // 模拟接口                setTimeout(() => {                    const menus = require('@/mock/menu.json')                           // 初始化菜单 和  按钮权限                    store.commit('global/SET_MENUS', menus)                    // 设置 token                    CookieUtil.set("token", "token");                    router.push('/')                    loading.close()                }, 2000)            }            return {                ...toRefs(state),                onSubmit            }    }    }</script><template>    <el-form :inline="true" :model="formInline" style="text-align: center;padding-top: 200px;">        <el-form-item label="账户名:">            <el-input v-model="formInline.account" placeholder="账户名"></el-input>        </el-form-item>        <el-form-item label="密码:">            <el-input v-model="formInline.password" type="password" show-password placeholder="密码"></el-input>        </el-form-item>        <el-form-item>        <el-button type="primary" @click="onSubmit">登陆</el-button>        </el-form-item>    </el-form></template>

这里我们就把菜单缓存到了store当中了,

这时候我们的动态路由在 路由守卫 beforeEach 中实现,其中有几点需要注意,有些坑 坑了我挺长时间的:

  1. 就是借口返回的component 只是个字符串 如何动态拼接生成真正的模板引入呢?
  2. vue-router4.x 当中路由守卫的地方如何实现 只初始化一次动态路由?
  3. 初始化动态路由后 如何保证 页面刷新路由不丢失?
  4. 如何动态判断 默认跳转页面?

这些都是问题 我研究了半天 可能也是文档没看太明白!!!不多说了 上代码

vue3.x-admin\src\router\index.js

import { createRouter, createWebHashHistory } from 'vue-router'import CookieUtil from 'cookie-tool-plug'import store from '@/store'import _ from 'lodash'const routes = [    // 主模板    {        path: '',        name: 'app',        meta: {},        component: () => import('@/views/Main.vue'),        children: [            {                path: '/no-auth',                meta: { title:'暂无权限', isHide: true },                component: () => import('@/views/404/noAuth.vue'),            },            {                path: '/404',                name: '404',                meta: { title:'404', isHide: true },                component: () => import('@/views/404/404.vue'),            },        ]    },    {        path: '/login',        name: 'login',        component: () => import('@/views/login/Index.vue'),    },    {        path: '/:pathMatch(.*)*',        redirect: '/404'    }];const router = createRouter({    history: createWebHashHistory(),    routes,})// 权限检查const asyncGetAuthFun = to => {    return new Promise((resolve, reject) => {        if(!CookieUtil.get('token') && to.path !== '/login'){            resolve(false)        } else {            resolve(true)        }    })}// 动态路由const hasNecessaryRoute = () => {    return router.getRoutes().find(v=>v.path === '')['children'].length !== 2}// 生产路由// const _import = file => require('@/' + file).default const generateRoute = () => {    const menus = _.cloneDeep(store.state.global.menus)    // 获取组件的方法    const getMenus = (menus) => {        menus.forEach(item=>{            item.component = import('@/' + item.component)            if(item.children) {                getMenus(item.children)            }        })    }    getMenus(menus)    router.addRoute({path: '', meta: {}, redirect:menus[0]['path'], component: () => import('@/views/Main.vue'), name: 'app' , children: [ ...menus, ...routes[0]['children'] ]})}router.beforeEach(async (to, from) => {    try {        const res = await asyncGetAuthFun(to)        if(!res) {            return '/login'        } else if(to.path !== '/login' && !hasNecessaryRoute()) {            generateRoute()            if(to.redirectedFrom){                return to.redirectedFrom.fullPath            } else {                return to.fullPath            }        } else {            return true        }      } catch (error) {        if (error instanceof NotAllowedError) {          return false        } else {          throw error        }    }})export { router, routes }

这个地方实现了 路由拦截, 登陆权限验证, 动态添加路由 以及默认导航 页面实现

可以看到我们 使用 item.component = import('@/' + item.component)

这个来动态拼接 实现了 把字符串转换成了 实际模板,前面好像必须@/ 这个我也没细细研究,有老铁知道可以评论。

我们让动态路由只添加一次的 判断条件是 hasNecessaryRoute

这个函数 主要是根据 默认子路由数量来的 因为默认静态路由 是有两条的 如果超过两条 就代表已经添加过了。

同时我们是怎么实现 刷新重新跳转到 原来的页面的 因为刷新 默认会重定向到404 我们根据

return to.redirectedFrom.fullPath

这个来判断是否重定向 在没有动态添加的判断里面 如果有 那么添加完 动态路由就跳转回去

vue-router4.x 的路由守卫 有点饶人 建议多看几遍 不然你一不小心就陷入死循环当中了 , 嘎嘎嘎嘎

最后成功实现了 具体 面包屑 还有左侧菜单生成 可以看下仓库。就不贴了 比较麻烦!!!

vue3.x-admin