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

废话不多说直接上代码:

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

这里我们就简单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

 博客:夏日里的一把阳光 – 全栈开发程序员 可以画原型,前端开发,后端开发 记录生活点点滴滴 负重前行,努力生活

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
为了实现动态路由和用户权限路由,我们需要进行以下步骤: 1. 安装依赖 ```shell npm install vue-router@4 pinia element-plus mock -S npm install @types/mock -D ``` 2. 创建路由配置文件 在src文件夹下创建router文件夹,并在其中创建index.ts文件,用于配置路由。在该文件中,我们需要定义路由的各个页面组件,并根据需要配置路由的子路由路由守卫。例如: ```typescript import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import Home from '@/views/home/indexName.vue' import Login from '@/views/login/index.vue' import NotFound from '@/views/404/index.vue' const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Home', component: Home, meta: { title: '首页', requireAuth: true // 需要登录才能访问 } }, { path: '/login', name: 'Login', component: Login, meta: { title: '登录' } }, { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound, meta: { title: '404' } } ] const router = createRouter({ history: createWebHistory(), routes }) // 路由守卫 router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requireAuth && !token) { next('/login') } else { next() } }) export default router ``` 3. 在main.ts中挂载路由 在main.ts中,我们需要将路由配置文件挂载到Vue实例中,以便在应用中使用路由。例如: ```typescript import { createApp } from 'vue' import App from './App.vue' import router from './router' createApp(App).use(router).mount('#app') ``` 4. 创建权限控制文件 在src文件夹下创建permission文件夹,并在其中创建index.ts文件,用于控制用户权限。在该文件中,我们需要定义用户的权限列表,并根据需要判断用户是否有权限访问某个路由。例如: ```typescript import router from '@/router' const whiteList = ['/login'] // 不需要登录即可访问的页面 router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (token) { if (to.path === '/login') { next('/') } else { // 判断用户是否有权限访问该路由 const hasPermission = true // 根据实际情况判断用户是否有权限 if (hasPermission) { next() } else { next('/404') } } } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { next('/login') } } }) ``` 5. 在App.vue中挂载路由渲染入口 在App.vue中,我们需要将路由渲染入口挂载到模板中,以便在应用中渲染路由。例如: ```html <template> <div id="app"> <router-view /> </div> </template> ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值