当前位置: 首页 > news >正文

Vue + Element UI 前端篇(十):动态加载菜单

Vue + Element UI 实现权限管理系统 前端篇(十):动态加载菜单 

动态加载菜单

之前我们的导航树都是写死在页面里的,而实际应用中是需要从后台服务器获取菜单数据之后动态生成的。

我们在这里就用上一篇准备好的数据格式Mock出模拟数据,然后动态生成我们的导航菜单。

接口模块化

我们向来讲究模块化,之前接口都集中在,interface.js,我们现在把它改名为 api.js,并把里边原来登录、用户、菜单的相关接口都转移到我们新建的接口模块文件中。

模块化之后的文件结构如下图所示

模块化之后,模块接口写在相应的模块接口文件中,如下面是登录模块

login.js

复制代码

import axios from '../axios'/* * 系统登录模块*/// 登录
export const login = data => {return axios({url: '/login',method: 'post',data})
}// 登出
export const logout = () => {return axios({url: '/logout',method: 'get'})
}

复制代码

模块化之后,父模块可以像这样引入

api.js

复制代码

/* * 接口统一集成模块*/
import * as login from './moudules/login'
import * as user from './moudules/user'
import * as menu from './moudules/menu'// 默认全部导出
export default {login,user,menu
}

复制代码

因为我们这里是导出的是父模块,所以在具体接口调用的时候,也需要在原来的基础上加上模块了,像这样。

如上面 api.js 中,我们导出了 login 的整个文件,而 login 文件下有 login,logout 等多个方法。

导航菜单树接口

我们在 menu.js 下创建一个查询导航菜单树的接口。

复制代码

import axios from '../axios'/* * 菜单管理模块*/export const findMenuTree = () => {return axios({url: '/menu/findTree',method: 'get'})
}

复制代码

 api.js 中如果没引入要记得引入。

页面接口调用

接口已经有了,我们在导航菜单组件 MenuBar.vue 中,加载菜单并存入 store 。

页面菜单渲染

还是在  MenuBar.vue 中,页面通过封装的菜单树组件读取store数据,递归生成菜单。

新建菜单树组件,递归生成菜单,并在点击响应函数里面根据菜单URL跳转到指定路由。

components/MenuTree/index.js

复制代码

<template><el-submenu v-if="menu.children && menu.children.length >= 1" :index="menu.menuId + ''"><template slot="title"><i :class="menu.icon"></i><span slot="title">{{menu.name}}</span></template><MenuTree v-for="item in menu.children" :key="item.menuId" :menu="item"></MenuTree></el-submenu><el-menu-item v-else :index="menu.menuId + ''" @click="handleRoute(menu)"><i :class="menu.icon"></i><span slot="title">{{menu.name}}</span></el-menu-item>
</template><script>export default {name: 'MenuTree',props: {menu: {type: Object,required: true}},methods: {handleRoute (menu) {// 通过菜单URL跳转至指定路由this.$router.push(menu.url)}}}
</script>

复制代码

提供Mock数据

接口有了,页面调用和渲染也写好了,该提供Mock数据了。

mock/modules/menu.js 中 mock findTree接口,data 对应数据太多,这里不贴了。

复制代码

export function findTree() {return {url: 'http://localhost:8080/menu/findTree',type: 'get',data: menuTreeData // json 对象数据}
}

复制代码

测试效果

启动完成,进入主页,我们看到导航菜单已经成功加载进来了,oh yeah!

然而,我们愉悦的点了点菜单,发现是这样的情况,oh no !

毛都没有,不过显然,聪明的你已经看穿了一切,我们之前只提供了一个叫 /user 的路由,并没有提供 /sys/user 的路由。

好吧,我们稍微修改一下,打开路由配置,把 /user 改成 /sys/user 试试。

果不其然,修改完之后便可以正常跳转到用户界面了。

但不对呀,这里路由配置是写死的,导航菜单是菜单数据动态生成的,这个路由配置也应该是根据菜单数据动态添加的啊,嗯,所以接下来我们就来讨论动态路由配置的问题。

动态路由实现

在 vue 的 route 中提供了 addRoutes 来实现动态路由,打开 MenuBar.vue ,我们在加载导航菜单的同时添加动态路由配置。

MenuBar.vue

其中 addDynamicMenuRoutes 是根据菜单返回动态路由配置的关键代码。

addDynamicMenuRoutes 方法详情:

复制代码

    /*** 添加动态(菜单)路由* @param {*} menuList 菜单列表* @param {*} routes 递归创建的动态(菜单)路由*/addDynamicMenuRoutes (menuList = [], routes = []) {var temp = []for (var i = 0; i < menuList.length; i++) {if (menuList[i].children && menuList[i].children.length >= 1) {temp = temp.concat(menuList[i].children)} else if (menuList[i].url && /\S/.test(menuList[i].url)) {menuList[i].url = menuList[i].url.replace(/^\//, '')// 创建路由配置var route = {path: menuList[i].url,component: null,name: menuList[i].name,meta: {menuId: menuList[i].menuId,title: menuList[i].name,isDynamic: true,isTab: true,iframeUrl: ''}}// url以http[s]://开头, 通过iframe展示if (isURL(menuList[i].url)) {route['path'] = menuList[i].urlroute['name'] = menuList[i].nameroute['meta']['iframeUrl'] = menuList[i].url} else {try {// 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储// 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到let array = menuList[i].url.split('/')let url = array[0].substring(0,1).toUpperCase()+array[0].substring(1) + '/' + array[1].substring(0,1).toUpperCase()+array[1]  .substring(1)route['component'] = resolve => require([`@/views/${url}`], resolve)} catch (e) {}}routes.push(route)}}if (temp.length >= 1) {this.addDynamicMenuRoutes(temp, routes)} else {console.log(routes)}return routes}

复制代码

动态菜单页面的组件结构稍微调整下,需要跟菜单url匹配,才能根据菜单url确定组件路径来动态加载组件。

把路由文件清理一下,把动态菜单相关的路由配置处理掉,留下一些固定的全局路由就好。

动态路由测试

启动完成,进入主页,点击用户管理,路由到了用户管理页面。

 点击机构管理,路由到了机构管理页面。

 

好了,到这里动态路由功能已经实现了,给自己鼓个掌吧。

页面刷新出大坑

先前我们是将导航菜单和路由的加载放在菜单栏页面MenuBar.vue中,一切显示和路由也都正常,看起来没什么问题。然而当我们在非根据路径刷新页面时,问题出现了。

如下图所示,我们在用户管理页面的时候,点击刷新浏览器,然后就白茫茫一片了,这是因为浏览器的刷新会导致整个vue重新加载,路由被重新初始化了,后面在Menu.bar添加的动态路由没有了,所以跳转的时候没有找到匹配路由,跳转的是一个不存在的页面,故而白茫茫一片。

专业填坑指南

这显然是动态菜单和路由的加载时机不对,怎么解决这个问题呢,既然问题出在加载时机,那就找一个在页面刷新的时候也能触发重新加载的地方就好了。

这样的地方也不少,像vue加载过程中的钩子函数,路由导航守卫函数等都可以,我们这里就选择在路由导航守卫的 beforeEach 函数内加载,保证每次路由跳转的时候都能够拥有动态菜单和路由。

把原先在MenuBar.vue中加载动态菜单和路由的代码,转移到路由配置 router/index 中来。

beforeEach:

复制代码

router.beforeEach((to, from, next) => {// 登录界面登录成功之后,会把用户信息保存在会话// 存在时间为会话生命周期,页面关闭即失效。let isLogin = sessionStorage.getItem('user')if (to.path === '/login') {// 如果是访问登录界面,如果用户会话信息存在,代表已登录过,跳转到主页if(isLogin) {next({ path: '/' })} else {next()}} else {// 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面if (!isLogin) {next({ path: '/login' })} else {// 加载动态菜单和路由addDynamicMenuAndRoutes()next()}}
})

复制代码

addDynamicMenuAndRoutes:

复制代码

/**
* 加载动态菜单和路由
*/
function addDynamicMenuAndRoutes() {api.menu.findMenuTree().then( (res) => {store.commit('setMenuTree', res.data)// 添加动态路由let dynamicRoutes = addDynamicRoutes(res.data)router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)router.addRoutes(router.options.routes);}).catch(function(res) {alert(res);});
}

复制代码

addDynamicRoutes:

复制代码

/**
* 添加动态(菜单)路由
* @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由
*/
function addDynamicRoutes (menuList = [], routes = []) {var temp = []for (var i = 0; i < menuList.length; i++) {if (menuList[i].children && menuList[i].children.length >= 1) {temp = temp.concat(menuList[i].children)} else if (menuList[i].url && /\S/.test(menuList[i].url)) {menuList[i].url = menuList[i].url.replace(/^\//, '')// 创建路由配置var route = {path: menuList[i].url,component: null,name: menuList[i].name,meta: {menuId: menuList[i].menuId,title: menuList[i].name,isDynamic: true,isTab: true,iframeUrl: ''}}// url以http[s]://开头, 通过iframe展示if (isURL(menuList[i].url)) {route['path'] = menuList[i].urlroute['name'] = menuList[i].nameroute['meta']['iframeUrl'] = menuList[i].url} else {try {// 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储// 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到let array = menuList[i].url.split('/')let url = array[0].substring(0,1).toUpperCase()+array[0].substring(1) + '/' + array[1].substring(0,1).toUpperCase()+array[1]  .substring(1)route['component'] = resolve => require([`@/views/${url}`], resolve)} catch (e) {}}routes.push(route)}}if (temp.length >= 1) {addDynamicRoutes(temp, routes)} else {console.log(routes)}return routes
}

复制代码

当然,别忘了把要用到的几个东西引入进来,把导航菜单栏的代码清理一下。

测试效果

启动完成,进入主页,点击用户管理,点击刷新按钮。

刷新后,菜单收起来了,然而页面还是正确的停留在用户管理页面。妈妈再也不用担心我会刷新了!

保存加载状态

现在每次路由跳转前都会重新获取菜单数据生成菜单和路由,及时页面没有刷新也会重复获取,这样很影响性能。我们改良一下,加载成功之后把状态保存到store,每次加载之前先检查store的加载状态,这样就可以避免在非页面刷新的情形下还频发重复的加载了。

 在 store 中添加菜单路由加载状态,避免页面未刷新而重复加载。

修改路由配置,在加载之前判断加载状态,只有未加载的情况下才加载,并在加载之后保存加载状态。

求解一个问题

在路由跳转的时候,路由好像是在原路径基础上叠加路由路径跳转的。

如路径在 http://localhost:8090/#/sys/dept 的时候,点击用户管理。

代码对应 this.$router.push(‘’sys/user),路由就赚到了 http://localhost:8090/#/sys/sys/user。

比正确路由多了一个 sys,目前还不到为什么。

目前我是在实际跳转之前,先跳回主页面然后在做真正的跳转。

这样问题可以解决,但无端端多了一步跳转总归不好,求解中。。。

相关文章:

Vue + Element UI 前端篇(十):动态加载菜单

Vue Element UI 实现权限管理系统 前端篇&#xff08;十&#xff09;&#xff1a;动态加载菜单 动态加载菜单 之前我们的导航树都是写死在页面里的&#xff0c;而实际应用中是需要从后台服务器获取菜单数据之后动态生成的。 我们在这里就用上一篇准备好的数据格式Mock出模…...

图的应用(最小生成树,最短路径,有向无环图)

目录 一.最小生成树 1.生成树 2.无向图的生成树 3.最小生成树算法 二.最短路径 1.单源最短路径---Dijkstra&#xff08;迪杰斯特拉&#xff09;算法 2.所有顶点间的最短路径---Floyd&#xff08;弗洛伊德&#xff09;算法 三.有向无环图的应用 1.AOV网&#xff08;拓扑…...

python正则表达式笔记2

由 \ 和一个字符组成的特殊序列在以下列出。 如果普通字符不是ASCII数位或者ASCII字母&#xff0c;那么正则样式将匹配第二个字符。比如&#xff0c;\$ 匹配字符 $. \number 匹配数字代表的组合。每个括号是一个组合&#xff0c;组合从1开始编号。 比如 (.) \1 匹配 the the 或…...

matplotlib 的默认字体和默认字体系列

matplotlib 的默认字体和默认字体系列 查看默认字体和默认字体系列查看默认字体系列下包含的字体查看 plt.rcParams 设置的所有参数查看所有支持的字体格式设置默认字体方法1&#xff1a;方法2 今天给大家介绍一下 matplotlib 包中的默认字体以及默认字体系列。 查看默认字体和…...

STMCUBEMX_IIC_DMA_AT24C64读取和写入

STMCUBEMX_IIC_DMA_AT24C64读取和写入 说明&#xff1a; 1、此例程只是从硬件IIC升级到DMA读写&#xff0c;因为暂时存储的掉电不丢失数据不多&#xff0c;一页就可以够用&#xff0c;不用担心跨页读写的问题 2、使用DMA后&#xff0c;程序确实是变快了&#xff0c;但是也要注意…...

wsl2相关问题

磁盘空间 wsl 删除相关文件后&#xff0c;如删除docker 无用的容器和镜像&#xff0c;windows上磁盘仍然无法自动回收空间 &#xff08;参考&#xff1a;[microsoft/WSL](https://github.com/microsoft/WSL/issues/4699#issuecomment-627133168)&#xff09; # 如清除无用do…...

使用idea时,光标变成了不能按空格键,只能修改的vim格式,怎么切换回正常光标

情况1 你可能不小心启用了 IntelliJ IDEA 中的 Vim 插件。你可以尝试以下步骤来禁用它&#xff1a; 在 IntelliJ IDEA 中&#xff0c;选择 "File" -> "Settings" &#xff08;如果你在 macOS 上&#xff0c;选择 "IntelliJ IDEA" -> &quo…...

vue+antd——实现table表格的打印——分页换行,每页都有表头——基础积累

这里写目录标题 场景效果图功能实现1&#xff1a;html代码功能实现2&#xff1a;css样式功能实现3&#xff1a;js代码补充内容page-break-inside 属性page-break-after属性page-break-before 属性 场景 最近在写后台管理系统时&#xff0c;遇到一个需求&#xff0c;就是要实现…...

linux C MD5计算

#include <stdio.h> #include <string.h> #include <openssl/md5.h>int main() {char str[] "Hello, world!"; // 需要计算MD5哈希值的字符串unsigned char digest[MD5_DIGEST_LENGTH]; // 存储MD5哈希值的数组MD5((unsigned char*)&str, str…...

vue3学习源码笔记(小白入门系列)------ 组件更新流程

目录 说明例子processComponentcomponentUpdateFnupdateComponentupdateComponentPreRender 总结 说明 由于响应式相关内容太多&#xff0c;决定先接着上文组件挂载后&#xff0c;继续分析组件后续更新流程&#xff0c;先不分析组件是如何分析的。 例子 将这个 用例 使用 vi…...

数学建模B多波束测线问题B

数学建模多波束测线问题 1.问题重述&#xff1a; 单波束测深是一种利用声波在水中传播的技术来测量水深的方法。它通过测量从船上发送声波到声波返回所用的时间来计算水深。然而&#xff0c;由于它是在单一点上连续测量的&#xff0c;因此数据在航迹上非常密集&#xff0c;但…...

Pytest 框架执行用例流程浅谈

背景&#xff1a; 根据以下简单的代码示例&#xff0c;我们将从源码的角度分析其中的关键加载执行步骤&#xff0c;对pytest整体流程架构有个初步学习。 代码示例&#xff1a; import pytest def test_add(): assert 1 1 2 def test_sub(): assert 2 - 1 1 通过 pytes…...

C#__资源访问冲突和死锁问题

/// 线程的资源访问冲突&#xff1a;多个线程同时申请一个资源&#xff0c;造成读写错乱。 /// 解决方案&#xff1a;上锁&#xff0c;lock{执行的程序段}:同一时刻&#xff0c;只允许一个线程访问该程序段。 /// 死锁问题&#xff1a; /// 程序中的锁过多&#xf…...

机器学习——Logistic Regression

0、前言&#xff1a; Logistic回归是解决分类问题的一种重要的机器学习算法模型 1、基本原理&#xff1a; Logistic Regression 首先是针对二分类任务提出的一种分类方法如果将概率看成一个数值属性&#xff0c;则二元分类问题的概率预测就可以转化为一个回归问题。这种思路最…...

创建husky规范前端项目

创建husky规范前端项目 .husky文件是一个配置文件&#xff0c;用于配置Git钩子。Git钩子是在Git操作时触发的脚本&#xff0c;可以用于自动化一些任务&#xff0c;比如代码格式化、代码检查、测试等。.husky文件可以指定在Git的不同操作&#xff08;如commit、push等&#xff…...

深浅拷贝与赋值

数据类型 数据类型 在JavaScript中&#xff0c;数据类型有两大类。一类是基本数据类型&#xff0c;一类是引用数据类型。 基本数据类型有六种&#xff1a;number、string、boolean、null、undefined、symbol。 基本数据类型存放在栈中。存放在栈中的数据具有数据大小确定&a…...

bert ranking pairwise demo

下面是用bert 训练pairwise rank 的 demo import torch from torch.utils.data import DataLoader, Dataset from transformers import BertModel, BertTokenizer from sklearn.metrics import pairwise_distances_argmin_minclass PairwiseRankingDataset(Dataset):def __ini…...

GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图

GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。例如在科研编程、绘图领域&#xff1a;1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言&#xff0c;都可以为你提供相关的代码示例。2、数据可视…...

SpringBoot整合Swagger3

前言 swagger是啥&#xff0c;是干什么的&#xff0c;有什么用&#xff0c;我想在这里我就不用介绍了&#xff0c;下面直接代码演示。 添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0…...

detectron2 install path

>>> import detectron2 >>> detectron2_path detectron2.__file__ >>> print(detectron2.__file__)...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

Qt的学习(一)

1.什么是Qt Qt特指用来进行桌面应用开发&#xff08;电脑上写的程序&#xff09;涉及到的一套技术Qt无法开发网页前端&#xff0c;也不能开发移动应用。 客户端开发的重要任务&#xff1a;编写和用户交互的界面。一般来说和用户交互的界面&#xff0c;有两种典型风格&…...