vue element plus 管理系统路由菜单简要设计(后端获取菜单)

1 需求

  • 管理系统“菜单”由后端接口返回,前端需要根据后端返回的“菜单”数组,构造路由,渲染侧栏菜单
  • 有些菜单是子菜单,有对应的路由,但是不在侧栏显示(比如一些详情页面) 注:这里的“菜单”,不是字面意思的菜单,即可以是菜单也可以是按钮,如果是菜单则对应路由

2 分析

一般我们“菜单“保存在数据库时,不会仅仅保存路由相关参数,还会增加一下自定义参数,类似下面的数据结构:

const menu = {id: "1",pid: "-1", // 父idnameCn: "平台管理", //中文名type: "menu", //类别:menu 菜单,button 按钮icon: "platform", //图标routeName: "platform", //路由名routePath: "/platform", //路由地址routeLevel: 1, //路由级别: 一级路由(左侧显示)  二级路由(左侧不显示)componentPath: "", //组件路径sort: 1, //排序children: [],
};

所以需要手动构造路由,渲染侧栏菜单

需要注意的地方

  • 通过设置 routeLevel 字段来判断当前“菜单“是否在左侧菜单显示
  • 添加 el-menu 的 router 属性,这样可以点击 menu 自动跳转到 index 绑定的路径,否则需要绑定 click 事件,进行手动跳转(适用于需要跳转外链的情况,需要再递归菜单时进行判断,不给index赋值path)
  • 如果当前“菜单“存在 children,但是 children 所有 child 是二级路由并且 path 没有以“/”开头(如下面路由中的硬件管理),那么需要将一个 Empty.vue 组件指向当前“菜单“,并且创建一个 path 为空的 child 来匹配当前“菜单“,否则二级路由无法跳转(无法跳转指的是是当前“菜单“组件路径直接配置目的组件路径)
  • 如果当前“菜单“存在 children,但是 children 中有 child 的 path 没有以“/”开头,在构造左侧菜单时,需要拼接祖先 path 后再赋值给 index,否则无法跳转 这里使用 router.getRoutes()返回的所有路由,并且使用路由 name 匹配,这样比自己递归拼接方便,如下面 MenuItem.vue 组件中使用的 processRoutePath 函数

3 实现

效果图:

3.1 目录结构

3.2 代码:

components/SvgIcon.vue

<template><svgaria-hidden="true":class="svgClass":style="{ width: size, height: size }"><use :xlink:href="symbolId" :fill="color" /></svg>
</template><script setup lang="ts">
const props = defineProps({prefix: {type: String,default: "icon",},name: {type: String,required: false,default: "",},color: {type: String,default: "",},size: {type: String,default: "1em",},className: {type: String,default: "",},
});const symbolId = computed(() => `#${props.prefix}-${props.name}`);
const svgClass = computed(() => {if (props.className) {return `svg-icon ${props.className}`;}return "svg-icon";
});
</script><style scoped>
.svg-icon {display: inline-block;width: 1em;height: 1em;overflow: hidden;vertical-align: -0.15em;outline: none;fill: currentcolor;
}
</style>

layout/index.vue

<template><div class="container"><div class="aside"><el-scrollbar height="100%"><el-menu:default-active="activeIndex":ellipsis="false":mode="'vertical'":collapse="false"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"router><template v-for="item in menus" :key="item.id"><MenuItem :menu="item"></MenuItem></template></el-menu></el-scrollbar></div><div class="content"><RouterView></RouterView></div></div>
</template>
<script setup lang="ts">
import MenuItem from "./components/MenuItem.vue";
import { menus } from "../router/index";const route = useRoute();
const activeIndex = ref<string>(route.path);
</script>
<style lang="scss">
.container {width: 100%;height: 100%;display: flex;.aside {width: 300px;height: 100%;background-color: #545c64;}.content {flex: 1;}
}
</style>

layout/components/MenuItem.vue

<template><!-- 不存在children --><template v-if="!menu.children?.length"><el-menu-itemv-if="menu.type === 'menu' && menu.routeLevel === 1":index="processRoutePath(menu)"><template #title><SvgIcon :name="menu.icon" class="icon"></SvgIcon><span>{{ menu.nameCn }}</span></template></el-menu-item></template><!-- 存在children --><template v-else><el-sub-menuv-if="menu.children.some((c: any) => c.type === 'menu' && c.routeLevel === 1)":index="menu.routePath"><template #title><SvgIcon :name="menu.icon" class="icon"></SvgIcon><span>{{ menu.nameCn }}</span></template><template v-for="item in menu.children" :key="item.id"><MenuItem :menu="item"></MenuItem></template></el-sub-menu><el-menu-itemv-else-if="menu.type === 'menu' && menu.routeLevel === 1":index="processRoutePath(menu)"><template #title><SvgIcon :name="menu.icon" class="icon"></SvgIcon><span>{{ menu.nameCn }}</span></template></el-menu-item></template>
</template><script lang="ts" setup>
import MenuItem from "./MenuItem.vue";
import SvgIcon from "../../components/SvgIcon.vue";const props = defineProps({menu: {type: Object,required: true,},
});const router = useRouter();
const routes = router.getRoutes();
<!-- 用于处理子路由path没有以/开头的情况 -->
const processRoutePath = (item) => {for (let route of routes) {if (route.name === item.routeName) {return route.path;}}return item.routePath;
};
</script>
<style lang="scss">
.icon {margin-right: 10px;
}
</style>

router/index.ts

import { createRouter, createWebHistory } from "vue-router";const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: "/",name: "index",component: () => import("../layout/index.vue"),meta: {title: "index",sort: 1,},},],// 刷新时,滚动条位置还原scrollBehavior: () => ({ left: 0, top: 0 }),
});export const menus = [{id: "1",pid: "-1", // 父idnameCn: "平台管理", //中文名type: "menu", //类别:menu 菜单,button 按钮icon: "platform", //图标routeName: "platform", //路由名routePath: "/platform", //路由地址routeLevel: 1, //路由级别: 一级路由(左侧显示)  二级路由(左侧不显示)componentPath: "", //组件路径sort: 1, //排序children: [{id: "1-1",nameCn: "角色管理",permission: "",type: "menu",pid: "1",icon: "role",iconColor: "",routeName: "role",routePath: "role",routeLevel: 1,componentPath: "/platform/role/index.vue",sort: 1,children: [{id: "1-1-1",nameCn: "新增",permission: "account:add",type: "button",pid: "1-1",icon: "user",iconColor: "",routeName: "",routePath: "",routeLevel: null,componentPath: "",sort: 1,children: [],},],},{id: "1-2",nameCn: "账户管理",permission: "",type: "menu",pid: "1",icon: "user",iconColor: "",routeName: "account",routePath: "account",routeLevel: 1,componentPath: "/platform/account/index.vue",sort: 2,children: [],},],},{id: "2",pid: "-1",nameCn: "资产管理",type: "menu",icon: "property",routeName: "",routePath: "/property",routeLevel: 1,componentPath: "",sort: 2,children: [{id: "2-1",pid: "2",nameCn: "文档管理",permission: "",type: "menu",icon: "document",iconColor: "",routeName: "document",routePath: "document",routeLevel: 1,componentPath: "/property/document/index.vue",sort: 1,children: [],},{id: "2-2",pid: "2",nameCn: "硬件管理",permission: null,type: "menu",icon: "hardware",iconColor: "",routeName: "",routePath: "hardware",routeLevel: 1,componentPath: "/property/layout/Empty.vue",sort: 2,children: [{id: "2-2-1",pid: "2-2",nameCn: "硬件管理",permission: null,type: "menu",icon: "",routeName: "hardware",routePath: "",routeLevel: 2,componentPath: "/property/hardware/index.vue",sort: 1,children: [],},{id: "2-2-2",pid: "2-2",nameCn: "硬件配置",permission: null,type: "menu",icon: "",routeName: "config",routePath: "config",routeLevel: 2,componentPath: "/property/hardware/Config.vue",sort: 2,children: [],},],},],},
];const initRouter = (routerTree: any) => {const routerArr: any = [];routerTree.forEach((item: any) => {if (item.type === "menu") {routerArr.push({meta: { title: item.nameCn },name: item.routeName || "",path: item.routePath || "",component: item.componentPath? () => import(/* @vite-ignore */ "../view" + item.componentPath): "",children: item.children ? initRouter(item.children) : [],});}});return routerArr;
};const routers = initRouter(menus);
routers.forEach((item: any) => {router.addRoute("index", item);
});console.log(router.getRoutes());export default router;

view/platform/account/index.vue

<template>账户管理</template>

view/platform/role/index.vue

<template>角色管理</template>

view/property/layout/Empty.vue

<template><router-view />
</template>

view/property/hardware/index.vue

<template><div>硬件管理</div><el-button type="primary" @click="handleCilck">硬件配置</el-button>
</template>
<script setup lang="ts">
const router = useRouter();
const handleCilck = () => {router.push({name: "config",});
};
</script>

view/property/hardware/Config.vue

<template>硬件配置</template>

view/property/document/index.vue

<template>文档管理</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/237618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Asp.Net Core 项目中常见中间件调用顺序

常用的 AspNetCore 项目中间件有这些&#xff0c;调用顺序如下图所示&#xff1a; 最后的 Endpoint 就是最终生成响应的中间件。 Configure调用如下&#xff1a; public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseD…

LIGA-Stereo:为基于立体 3D 检测器的学习 LiDAR 几何感知表示

论文地址&#xff1a;https://openaccess.thecvf.com/content/ICCV2021/papers/Guo_LIGA-Stereo_Learning_LiDAR_Geometry_Aware_Representations_for_Stereo-Based_3D_Detector_ICCV_2021_paper.pdf 论文代码&#xff1a;https://github.com/xy-guo/LIGA-Stereo 摘要 基于立…

CloudCanal x Debezium 打造实时数据流动新范式

简述 Debezium 是一个开源的数据订阅工具&#xff0c;主要功能为捕获数据库变更事件发送到 Kafka。 CloudCanal 近期实现了从 Kafka 消费 Debezium 格式数据&#xff0c;将其 同步到 StarRocks、Doris、Elasticsearch、MongoDB、ClickHouse 等 12 种数据库和数仓&#xff0c;…

js 图片 手动上传,并回显

效果展示&#xff1a; 代码&#xff1a; <label for"avatarUpload"><div><img v-if"avatatImageUrl" :src"avatatImageUrl" class"avatar"><img v-else src"../../assets/images/account/avatar-upload.png…

vite 引入构建分析插件

yarn add rollup-plugin-visualizer vite.config.js里配置 plugins: [copy({targets: [{ src: node_modules/echarts/dist/component/echarts.min.js, dest: public/js }]}),visualizer({// 打包完成后自动打开浏览器&#xff0c;显示产物体积报告open: true,gzipSize: true,b…

图像识别与人工智能到底是何关系?有何区别?

图像识别是人工智能领域的一个重要应用领域&#xff0c;它利用人工智能技术和算法来分析和理解图像内容。图像识别是使计算机能够模拟和理解人类视觉系统的能力&#xff0c;并从图像中提取出有用的信息和特征。 人工智能在图像识别中扮演着至关重要的角色&#xff0c;主要体现…

ruoyi若依前后端分离版部署centos7服务器(全)

目录 VMware虚拟机 centos7 安装环境如下 一、msql 5.7 二、nginx1.23.3 三、java8 四、redis 3.2.1 五、部署若依前端 六、部署若依后端 前言 虚拟机的桥接与nat模式 : 重点 重点&#xff01;&#xff01;&#xff01; 无线不可以用桥接模式 &#xff0c;而你用了nat模式会…

OpenSergo使用详解

简介 OpenSergo是一个基于微服务治理的标准和生态&#xff0c;覆盖了服务元信息、流量治理、服务容错、数据库/缓存治理、服务注册发现、配置治理等十几个关键领域&#xff0c;覆盖了完整的微服务生命周期&#xff08;从开发态到测试态&#xff0c;到发布态&#xff0c;再到运…

融云数智办公获 IT168「2023 年度信创卓越贡献奖」

近期&#xff0c;业界知名 IT 垂直门户媒体 IT168 正式揭晓其年度大型评选“2023 年技术卓越奖”结果&#xff0c;融云榜上有名。关注【融云 RongCloud】&#xff0c;了解协同办公平台更多干货。 融云数智办公作为信创领域明星产品荣获“2023 年度信创卓越贡献奖”。 复杂多变…

flink使用sql-client-defaults.yml无效

希望在flink sql脚本启动时自动选择catalog&#xff0c;减少麻烦。于是乎配置sql-client-defaults.yaml&#xff1a; catalogs:- name: hive_catalogtype: icebergcatalog-type: hiveproperty-version: 1cache-enabled: trueuri: thrift://localhost:9083client: 5warehouse: …

MySQL数据库8.0+版本部署安装

1、安装前准备 1.1、安装文件上传 以mysql-8.0.32版本为例&#xff0c;将 mysql-8.0.32-linux-glibc2.12-x86_64.tar.xz 、my.cnf上传至/opt/software目录。 安装文件百度网盘下载地址&#xff1a; mysql-8.0.32&#xff1a;https://pan.baidu.com/s/1gtPYEa2aT0V2-1Q3-KKxl…

使用HTTP协议有哪些风险?HTTP与HTTPS的区别是什么

作为两种常见的网络协议&#xff0c;HTTP和HTTPS都是用于在浏览器和服务器之间传输数据的。然而在保障数据安全性方面&#xff0c;HTTPS远远优于HTTP。在网络安全愈发重要的当下&#xff0c;HTTP协议的不安全性使得其逐渐被淘汰弃用。那么使用HTTP协议有哪些风险呢&#xff1f;…

关于“Python”的核心知识点整理大全34

目录 第&#xff11;3 章 外星人 13.1 回顾项目 game_functions.py 13.2 创建第一个外星人 13.2.1 创建 Alien 类 alien.py 13.2.2 创建 Alien 实例 alien_invasion.py 13.2.3 让外星人出现在屏幕上 game_functions.py 13.3 创建一群外星人 13.3.1 确定一行可容纳…

使用Pycharm一键将.ui文件生成.py文件配置教程、一键打开QTDesigner教程

2df3621a-7ffd-4f18-9735-b86464b83a5b 前言 我痛恨所有将白嫖归为理所应当的猪&#x1f416;。 教程 打开pycharm之后&#xff0c;依次点击File->Settings->Tools->External Tools&#xff0c;进入如下界面&#xff1a; 1、配置快捷打开Qt Designer 点击号&…

探索鸿蒙:了解华为鸿蒙操作系统的基础课程

目录 学习目标&#xff1a; 学习内容&#xff1a; 学习时间&#xff1a; 学习产出&#xff1a; 介绍鸿蒙操作系统的起源和发展历程。 理解鸿蒙操作系统的核心概念和体系结构。 学习如何搭建和配置鸿蒙开发环境。 掌握基础的鸿蒙应用开发技术&#xff0c;包括应用的创建、…

在x64上构建智能家居(home assistant)(二)(新版Debain12)连接Postgresql数据库

新版数据库安装基本和旧版相同,大部分可以参考旧版本在x64上构建智能家居(home assistant)&#xff08;二&#xff09;连接Postgresql数据库_homeassist 数据库-CSDN博客 新版本的home assistant系统安装,我在原来写的手顺上直接修改了,需要的可以查看在x64上构建智能家居(home…

【小白专用】php pdo方式连接sqlserver 设置方法 更新23.12.21

windows系统的拓展相对来说比较好安装&#xff0c;直接下载对应的dll文件&#xff0c;修改php.ini配置文件即可。 添加PHP对SQL SERVER的支持 1.新建PHP 文件&#xff0c;输入内容&#xff1a; <?php echo phpinfo(); ?> 2.运行后&#xff0c;可以查看到如下数据&…

微服务之服务注册与发现

服务注册发现 服务注册就是维护一个登记簿&#xff0c;它管理系统内所有的服务地址。当新的服务启动后&#xff0c;它会向登记簿交待自己的地址信息。服务的依赖方直接向登记簿要 Service Provider 地址就行了。当下用于服务注册的工具非常多 ZooKeeper&#xff0c;Consul&…

Git 软件安装及配置指南

目录 前言1 访问 Git 官网2 选择适用于操作系统的版本3 下载并运行Git 安装程序4 安装过程的选择组件和配置5 验证安装结语 前言 在进行软件开发或协作项目时&#xff0c;使用版本控制工具至关重要。Git 是一个强大且流行的版本控制系统&#xff0c;本文将介绍 Git 的安装步骤…

vue2 之 实现pdf电子签章

一、前情提要 1. 需求 仿照e签宝&#xff0c;实现pdf电子签章 > 拿到pdf链接&#xff0c;移动章的位置&#xff0c;获取章的坐标 技术 : 使用fabric pdfjs-dist vuedraggable 2. 借鉴 一位大佬的代码仓亏 : 地址 一位大佬写的文章 &#xff1a;地址 3. 优化 在大佬的代码…