Vue3+Vue-Router+Element-Plus根据后端数据实现前端动态路由——权限管理模块

这篇具有很好参考价值的文章主要介绍了Vue3+Vue-Router+Element-Plus根据后端数据实现前端动态路由——权限管理模块。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提示:文章内容仔细看一些,或者直接粘贴复制,效果满满


前言

提示:文章大概

1、项目:前后端分离
2、前端:基于Vite创建的Vue3项目
3、后端:没有,模拟的后端数据
4、关于路径“@”符号——vite.config.js 文件里修改
vue3动态路由权限,vue,前端,vue.js,javascript,elementui


提示:以下是本篇文章正文内容,下面案例可供复制粘贴使用,嘎嘎爽

一、技术栈

  1. Vite 创建 Vue3 项目
# 1.创建项目
npm create vite@latest

# 2.下载依赖
npm install

# 3.运行项目
npm run dev
  1. Element-plus
# 1.下载
npm install element-plus --save

# 2.main.js 引入
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')
  1. Vue-Router
# 1.安装
npm install vue-router@4
  1. nprogress (进度条——非必选,好看而已)
npm i nprogress -S

vue3动态路由权限,vue,前端,vue.js,javascript,elementui

二、项目结构

vue3动态路由权限,vue,前端,vue.js,javascript,elementui

三、菜单组件和数据

说明:

  1. AsideMenu.vue 引用 LeftSubMenu.vue 组件,并父传子传入后端数据
  2. LeftSubMenu.vue 组件加载数据
  3. menuData.json 后端模拟数据文件

1、AsideMenu.vue 组件

代码如下(示例):

<template>
    <el-menu router :default-active="activeMenu" :class="'menu-left'" :default-openeds="openedsArr" text-color="#fff">
        <LeftSubMenu :menuData="treeMenu"></LeftSubMenu>
    </el-menu>
</template>
   
<script setup>
import LeftSubMenu from "./LeftSubMenu.vue";
import { computed } from "vue";
import { useRouter } from "vue-router";
import treeMenu from './menuData.json';


const openedsArr = treeMenu.map((item) => {
    return item.path;
});


const activeMenu = computed(() => {
    const router = useRouter();
    const { meta, path } = router.currentRoute.value;
    if (meta.matchPath2) {
        return meta.matchPath2;
    } else {
        return path;
    }
});


</script>
   

<style scoped>
.menu-left {
    flex: 1;
    padding: 0 8px;
    border-right: none;
    background: none;
}

.menu-left:deep(.el-menu),
.menu-left:deep(.el-sub-menu__title:hover) {
    background: none;
}

.menu-left:deep(.el-menu-item),
.menu-left:deep(.el-sub-menu__title) {
    height: 36px;
    margin-bottom: 4px;
    border-radius: 4px;
    color: var(--text-main-color) !important;
}

.menu-left:deep(.el-menu-item:hover .icon),
.menu-left:deep(.el-menu-item.is-active .icon) {
    filter: invert(100%);
    -webkit-filter: invert(100%);
}

.menu-left:deep(.el-menu-item:hover),
.menu-left:deep(.el-menu-item.is-active) {
    color: #ffffff !important;
    background-color: #eecece;
}
</style>

2、LeftSubMenu.vue

代码如下(示例):

<template>
    <template v-for="item in props.menuData">
        <el-sub-menu :key="item.path" v-if="item.children && item.children.length > 0" :index="item.path">

            <template #title>
                <el-icon>
                    <component :is="item.icon"></component>
                </el-icon>
                <span>{{ item.meta.title }}</span>
            </template>
            <LeftSubMenu :menuData="item.children"></LeftSubMenu>
        </el-sub-menu>


        <el-menu-item :key="item.id" v-else :index="item.path" :disabled="item.disabled">
            <template #title>
                <!-- <img class="icon pd-r-10" :src="item.icon" /> -->
                <el-icon>
                    <component :is="item.icon"></component>
                </el-icon>
                <span>{{ item.meta.title }}</span>
            </template>
        </el-menu-item>


    </template>
</template>
   
<script setup>
import LeftSubMenu from "./LeftSubMenu.vue";
import { computed, onMounted } from "vue";
import { useRouter } from "vue-router";

const props = defineProps({
    menuData: {
        type: Array,
        default: [],
    },
});

onMounted(() => {
    console.log(props.menuData, "Item打印数据");
});

const curRoute = computed(() => {
    const router = useRouter();
    const { path } = router.currentRoute.value;
    return path;
});
</script>


3、menuData.json 数据

数据参数说明:

  1. menuType: 0为菜单组,1为菜单(可跳转)
  2. children: 子路由
数据说明:不复制
{
        "id": "1",   // 唯一id
        "name": "Home",  // 组件名称
        "path": "/home",  // 路由
        "component": "/home/index.vue",   // 组件文件位置
        "menuType": "1",  // 组件类型
        "icon": "Discount",  // 图标
        "sort": 0,   // 排序规则
        "meta": {
            "title": "系统首页",  // 组件名称
            "requiresAuth": null, // 是否需要身份验证
            "roles": [],  // 用户角色或权限
            "breadcrumb": [  // 定义面包屑导航
                {}
            ],
            "keepAlive": null  // 是否需要缓存
        },
        "children": []   // 子路由
    }

代码如下(示例):


[
    {
        "id": "1",
        "name": "Home",
        "path": "/home",
        "component": "/home/index.vue",
        "menuType": "1",
        "icon": "Discount",
        "sort": 0,
        "meta": {
            "title": "系统首页",
            "requiresAuth": null,
            "roles": [],
            "breadcrumb": [
                {}
            ],
            "keepAlive": null
        },
        "children": []
    },
    {
        "id": "2",
        "name": "System",
        "path": "/system",
        "component": "/system/index.vue",
        "menuType": "0",
        "icon": "Operation",
        "sort": 0,
        "meta": {
            "title": "系统管理",
            "requiresAuth": null,
            "roles": [],
            "breadcrumb": [
                {}
            ],
            "keepAlive": null
        },
        "children": [
            {
                "id": "211",
                "name": "User",
                "path": "/user",
                "component": "/user/index.vue",
                "menuType": "1",
                "icon": "user",
                "sort": 0,
                "meta": {
                    "title": "用户管理",
                    "requiresAuth": null,
                    "roles": [],
                    "breadcrumb": [
                        {}
                    ],
                    "keepAlive": null
                },
                "children": []
            },
            {
                "id": "222",
                "name": "Menu",
                "path": "/menu",
                "component": "/menu/index.vue",
                "menuType": "1",
                "icon": "Menu",
                "sort": 0,
                "meta": {
                    "title": "菜单管理",
                    "requiresAuth": null,
                    "roles": [],
                    "breadcrumb": [
                        {}
                    ],
                    "keepAlive": null
                },
                "children": []
            },
            {
                "id": "223",
                "name": "Role",
                "path": "/role",
                "component": "/role/index.vue",
                "menuType": "1",
                "icon": "Avatar",
                "sort": 0,
                "meta": {
                    "title": "角色管理",
                    "requiresAuth": null,
                    "roles": [],
                    "breadcrumb": [
                        {}
                    ],
                    "keepAlive": null
                },
                "children": []
            }
        ]
    },
    {
        "id": "3",
        "name": "Log",
        "path": "/log",
        "component": "/log/index.vue",
        "menuType": "1",
        "icon": "Notebook",
        "sort": 0,
        "meta": {
            "title": "日志管理",
            "requiresAuth": null,
            "roles": [],
            "breadcrumb": [
                {}
            ],
            "keepAlive": null
        },
        "children": []
    },
    {
        "id": "4",
        "name": "Study",
        "path": "/study",
        "component": "/study/index.vue",
        "menuType": "0",
        "icon": "Notebook",
        "sort": 0,
        "meta": {
            "title": "学习管理",
            "requiresAuth": null,
            "roles": [],
            "breadcrumb": [
                {}
            ],
            "keepAlive": null
        },
        "children": [
            {
                "id": "441",
                "name": "StudyUser",
                "path": "/studyUser",
                "component": "/study/user/index.vue",
                "menuType": "0",
                "icon": "Notebook",
                "sort": 0,
                "meta": {
                    "title": "用户管理",
                    "requiresAuth": null,
                    "roles": [],
                    "breadcrumb": [
                        {}
                    ],
                    "keepAlive": null
                },
                "children": [
                    {
                        "id": "4441",
                        "name": "Student",
                        "path": "/student",
                        "component": "/study/user/student/index.vue",
                        "menuType": "1",
                        "icon": "Notebook",
                        "sort": 0,
                        "meta": {
                            "title": "学生管理",
                            "requiresAuth": null,
                            "roles": [],
                            "breadcrumb": [
                                {}
                            ],
                            "keepAlive": null
                        },
                        "children": []
                    },
                    {
                        "id": "4442",
                        "name": "Teacher",
                        "path": "/teacher",
                        "component": "/study/user/teacher/index.vue",
                        "menuType": "1",
                        "icon": "Notebook",
                        "sort": 0,
                        "meta": {
                            "title": "教师管理",
                            "requiresAuth": null,
                            "roles": [],
                            "breadcrumb": [
                                {}
                            ],
                            "keepAlive": null
                        },
                        "children": []
                    }
                ]
            },
            {
                "id": "3",
                "name": "Log",
                "path": "/log",
                "component": "/log/index.vue",
                "menuType": "1",
                "icon": "Notebook",
                "sort": 0,
                "meta": {
                    "title": "打卡记录",
                    "requiresAuth": null,
                    "roles": [],
                    "breadcrumb": [
                        {}
                    ],
                    "keepAlive": null
                },
                "children": []
            }
        ]
    }
]


四、router 配置

说明:

  1. router.addRouter({}) 函数即动态路由,它是临时性的,就是一旦刷新就会清除掉添加的动态路由信息
  2. 需要重新定位到 localhost:8080 来刷新,重新获取路由信息,方便调试
  3. 因为是前端静态数据,所以正常,只要连接后端,请求数据后,缓存本地,每次刷新从本地获取即可
  4. 文章只是完成动态路由的实现,数据的持久性存储,各位根据自己项目自身完善

1、router/index.js

代码如下(示例):

import {
    createRouter,
    createWebHashHistory
} from 'vue-router';

import NotFound from '@/pages/404/404.vue'  // pages 文件下创建404文件,再创建一个404.vue

const routes = [
    { path: "/", component: () => import('@/pages/manage/ManageMain.vue') },  // 登录页
    {
        path: "/manage", name: 'Manage', component: () => import('@/pages/manage/ManageMain.vue'),  // 主页        
    },
    { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]

const router = createRouter({
    // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
    history: createWebHashHistory(),
    routes, // `routes: routes` 的缩写
})

// 导出实例, permission.js 引入
export default router


2、permission.js (与main.js 同级)

说明:

  1. 注意 NProgress 的引入、配置、使用
  2. 动态路由的添加(主要是 router.addRoute ,其他的都是根据后端 json 文件的参数来判断,不同的参数配置,不同的判断,这只是我喜欢的参数配置)
  3. 路径的拼接(component: () => import(/* @vite-ignore */ ./views${item.component}))
  4. 具体的根据自己的情况配置,打印就知道了,
  5. 按照我的配置,就不需要改动

代码如下(示例):

// 说明:路由守卫文件

// 引入
import router from "./router";
// 判断用户无token 返回登录页提示有用
import { ElMessage } from 'element-plus';
// 进度条
import NProgress from 'nprogress';

// 简单配置  进度条,可以不配置:在axios中我们不再做配置,以用来区分。
NProgress.inc(0.2)
NProgress.configure({ easing: 'ease', speed: 500, showSpinner: false })


// 一、前置守卫
router.beforeEach((to, from, next) => {
    // 进度条
    NProgress.start();
    // 1、动态路由
    addRoutes();
    // 2、中间处理(token)
    // 3、最后放行
    next();
})

// 动态路由获取:注:之后完善项目直接考虑在登录的时候直接获取
// 直接缓存在 pinia 里
// 这里直接取数据,不请求
import { getTreeMenu } from '@/api/index.js';
import menuData from '@/components/menu2/menuData.json';
function addRoutes() {
    // 1、后端数据
    createRouters(menuData);
    console.log("router/index.js打印router已有的路由信息", router.getRoutes());
}
// 拼接路由
function createRouters(result) {
    result.forEach((item) => {
        // 1、类型为0的菜单,子路由不为空,将子路由添加到manage里
        if (item.menuType === '0' && item.children.length > 0) {
            item.children.forEach((children) => {
                createRouterTemplate('Manage', children);
            })
        }
        // 2、menuType == 1, 子路由为空
        if (item.menuType === '1' && item.children.length === 0) {
            createRouterTemplate('Manage', item);
        }

        // 3、递归层级
        if (item.children.length > 0) {
            createRouters(item.children);
        }
    });
}
// 把router 的动态路由进行封装
function createRouterTemplate(fatherRouter, item) {
    router.addRoute(fatherRouter, {
        path: item.path,
        name: item.name,
        meta: {
            title: item.meta.title, // 面包屑用
            requiresAuth: item.meta.requiresAuth,
            roles: item.meta.roles,
            breadcrumb: item.meta.breadcrumb,
            keepAlive: item.meta.keepAlive
        },
        // /* @vite-ignore */ :处理vite动态导入的警告
        component: () => import(/* @vite-ignore */ `./views${item.component}`)
    })
}


// 二、后置守卫
router.afterEach((to) => {
    // 标签抬头
    document.title = to.meta.title;

    // 进度条
    NProgress.done();
})


// main.js 导入的为这个router
export default router


3、main.js

说明:

  • 1.注意 router 的引用文件
  • 2.注意 nprogress 的引用
  • 3.注意全局定义Element-Plus图标
  • 4.注意Vue3动态图标的使用
# Vue3 动态图标的使用
 <el-icon><component :is="item.icon"></component></el-icon>

代码如下(示例):

import { createApp } from 'vue'
import './style.css';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
// import router from './router';  // 原router
import router from './permission'; // 现router
//Icon全局引入
import * as icons from "@element-plus/icons-vue";
// 进度条
import 'nprogress/nprogress.css';

const app = createApp(App);
// ElementPlus
app.use(ElementPlus);
// Icon全局注册
Object.keys(icons).forEach(key => {
    app.component(key, icons[key])
})

app.use(router);

app.mount('#app')

五、效果

vue3动态路由权限,vue,前端,vue.js,javascript,elementui

vue3动态路由权限,vue,前端,vue.js,javascript,elementui

删除menuData.json 文件的某一个路由,界面将不展示!!!

六、给个点赞和收藏

七、参考文献

参考文章 — https://www.cnblogs.com/lpkshuai/p/17346600.html文章来源地址https://www.toymoban.com/news/detail-787176.html

到了这里,关于Vue3+Vue-Router+Element-Plus根据后端数据实现前端动态路由——权限管理模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • NodeJS+Vue+Element-Ui/Plus+Axios+Vue-router+vuex 详细下载、安装、创建项目、引入

    1.下载过程默认下一步 (1)这个是官网全版目录,下载太慢(一般下载不了);但是它写了所有nodejs和npm相互对应的版本,可以以此为参考、防止版本不对应; NodeJS各个历史版本下载 https://nodejs.org/zh-cn/download/releases/ (2)这里有一个快速下载地址,只有16.18.1这个版本,但这

    2023年04月18日
    浏览(54)
  • vue3使用element-plus

    element-ui 是配合 vue2 使用,element-plus 是配置 vue3 使用的 1. 包管理器的方式 如果是使用 webpack 或者 vite 打包工具新建的项目 2. 浏览器直接导入 直接通过浏览器的 HTML 标签导入 Element Plus,然后就可以使用全局变量 ElementPlus 1. 导入全部组件且注册所有的图标 声明使用 ElementPl

    2024年02月08日
    浏览(79)
  • Vue3导入Element-plus方法

    先引入依赖 main.js中要引入两个依赖 然后 这个东西 我们最好还是挂载vue上 所以 还是 然后 我们可以在组件上试一下用一个ElementUi的表格组件 参考代码如下 运行结果如下 也是没有任何问题

    2024年02月06日
    浏览(55)
  • vue3 element-plus 实现图片预览

    element-plus下有这么一个组件 el-image-viewer /,但是这个组件是没写在文档上面的,像普通组件一样使用即可 可以通过点击按钮实现图片预览,而非el-image组件只能通过点击图片实现预览 2.1封装组件 2.3组件使用 在需要使用的地方引入,然后使用即可,这不是重点,每个人使用的

    2024年02月15日
    浏览(59)
  • vue3项目搭建并配置element-plus

    安装完成后,输入如下指令查看vue的版本: 选择一个要存放项目的目录,打开小黑窗输入如下命令: 一开始输入项目名称或者默认vue-project,然后根据需求选择Yes/No 生成完项目后,输入如下指令: src/main.js里引入 index.css的文件位置根据实际情况写,也有可能是 const app后面加

    2024年02月13日
    浏览(61)
  • vue3+element-plus上传文件,预览文件

    vue3+ts+element-plus上传文件,预览文件 场景:使用element-plus的el-upload标签,手动上传文件,可预览docx,xlsx,pdf,jpg,jpeg,png(本地资源以及网络资源)。 1、使用el-upload标签 检查上传文件的文件格式与大小 上传的附件信息在fileList中,组装接口所需数据进行上传 使用docx-preview插件预览

    2024年02月11日
    浏览(56)
  • Vue3 封装 element-plus 图标选择器

    效果一: 效果二:   效果一的这个是把全部的icon图标都让它显示出来,让我们自己选择说选图标 2.1. 全局注册 icon 组件 2.2. 组件实现  2.3. 使用  效果二的这个是渲染后端返回的icon图标 3.1. 全局注册 icon 组件 3.2. 组件实现  3.3. 使用 

    2024年02月07日
    浏览(100)
  • (二) Vue3 + Element-Plus 实现动态菜单栏

    系列介绍:Vue3 + Vite + TS 从零开始学习 项目搭建:(一) Vue3 + Vite + TS 项目搭建 实现动态菜单栏:(二) Vue3 + Element-Plus 实现动态菜单栏 实现动态面包屑:(三) Vue3 + Element-Plus 实现动态面包屑 实现动态标签页:(四) Vue3 + Element-Plus 实现动态标签页 实现动态主题色切换(demo):(五)

    2023年04月23日
    浏览(59)
  • vue3 element-plus动态菜单及动态图标

    引入element-plus 注册图标组件 动态引入图标代码 完整代码 路由如下

    2024年01月18日
    浏览(47)
  • vue3+element-plus 表单输入框无法输入

    Element-Plus在进行reactive在对登录用户密码输入输入时失效,最后发现是el-form,在进行ref和model进行绑定的时候,绑定的属性名称都是一致的,导致界面无法输入,如下图所示都绑定的是:loginForm,代码入下图所示: 此时界面操作输入框,是无法编辑的: 而el-form修改的model=\\\"logi

    2024年02月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包