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,目前还不到为什么。

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

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

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

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

相关文章

Spring boot 第一个程序

新建工程 选择spring-boot版本 右键创建类TestController&#xff1a; 代码如下&#xff1a; package com.example.demo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springf…

【图卷积神经网络】1-入门篇:为什么使用图神经网络(下)

为什么使用图神经网络? 在本书中,我们将重点介绍图学习技术中的深度学习家族,通常称为图神经网络。GNNs是一种新的深度学习架构类别,专门设计用于处理图结构化数据。与主要用于文本和图像的传统深度学习算法不同,GNNs明确地用于处理和分析图数据集(见图1.4)。 图1.4 - …

内网穿透:FRP(Forwarding Remote Proxy)反向代理

frp 是一个可用于内网穿透的高性能的反向代理应用&#xff0c;支持 tcp, udp 协议&#xff0c;为 http 和 https 应用协议提供了额外的能力&#xff0c;且尝试性支持了点对点穿透 下载地址 https://github.com/fatedier/frp/releases 选择最新的就行&#xff0c;linux和windo…

ac7260网卡不能连5g

之前路由器是双频&#xff0c;最近为了连物联网一堆&#xff0c;把双频拆成两个wifi 结果电脑上装的pdd网卡就罢工了&#xff0c;连4g可以&#xff0c;但是连5g网络就不行&#xff0c;连上却没网&#xff0c;导致网盘下东西慢。刚开始以为是tplink的易展问题&#xff0c;结果看…

无涯教程-Flutter - 数据库

SQLite" class"css-1occaib">SQLite数据库是基于事实和标准SQL的嵌入式数据库引擎&#xff0c;它是小型且经过时间考验的数据库引擎&#xff0c;sqflite软件包提供了许多函数&#xff0c;可以有效地与SQLite数据库一起使用&#xff0c;它提供了操作SQLite数据…

sql:SQL优化知识点记录(十)

&#xff08;1&#xff09;慢查询日志 Group by的优化跟Order by趋同&#xff0c;只是多了一个having 开启慢查询日志&#xff1a; 演示一下慢sql&#xff1a;4秒之后才会出结果 查看一下&#xff1a;下方显示慢查询的sql &#xff08;2&#xff09;批量插入数据脚本 函数和存…

【广州华锐互动】智慧园区3D数据可视化系统有什么作用?

随着科技的不断发展&#xff0c;智慧园区3D数据可视化系统已经成为了现代园区管理的重要组成部分。它通过将大量的数据进行整合、分析和展示&#xff0c;为企业提供了一个直观、高效的数据管理平台&#xff0c;帮助企业实现精细化管理&#xff0c;提高运营效率&#xff0c;降低…

Python实操 PDF自动识别并提取Excel文件

最近几天&#xff0c;paddleOCR开发了新的功能&#xff0c;通过将图片中的表格提取出来&#xff0c;效果还不错&#xff0c;今天&#xff0c;作者按照步骤测试了一波。 首先&#xff0c;讲下这个工具是干什么用的&#xff1a;它的功能主要是针对一张完整的PDF图片&#xff0c;可…

uni-app 可视化创建的项目 移动端安装调试插件vconsole

可视化创建的项目&#xff0c;在插件市场找不到vconsole插件了。 又不好npm install vconsole 换个思路&#xff0c;先创建一个cli脚手架脚手架的uni-app项目&#xff0c;然后再此项目上安装vconsole cli脚手架创建uni-app项目 安装插件 项目Terminal运行命令&#xff1a;npm…

Docker-基础命令使用

文章目录 前言命令帮助命令执行示意图docker rundocker psdocker inspectdocker execdocker attachdocker stopdocker startdocker topdocker rmdocker prune参考说明 前言 本文主要介绍Docker基础命令的使用方法。 命令帮助 Docker命令获取帮助方法 # docker -h Flag shor…

python library reference

文章目录 1. 标准库2. Python标准库介绍3. 示例 1. 标准库 https://docs.python.org/zh-cn/3/library/ https://pypi.org/ https://pypi.org/search/ 2. Python标准库介绍 Python 语言参考手册 描述了 Python 语言的具体语法和语义&#xff0c;这份库参考则介绍了与 Pytho…

网络协议从入门到底层原理学习(一)—— 简介及基本概念

文章目录 网络协议从入门到底层原理学习&#xff08;一&#xff09;—— 简介及基本概念一、简介1、网络协议的定义2、网络协议组成要素3、广泛的网络协议类型网络通信协议网络安全协议网络管理协议 4、网络协议模型对比图 二、基本概念1、网络互连模型2、计算机之间的通信基础…

7.Xaml Image控件

1.运行图片 2.运行源码 a.xaml源码 <!--Source="/th.gif" 图像源--><!--Stretch="Fill" 填充模式--><Image x:Name

OpenCV 04(通道分离与合并 | 绘制图形)

一、通道的分离与合并 - split(mat)分割图像的通道 - merge((ch1,ch2, ch3)) 融合多个通道 import cv2 import numpy as npimg np.zeros((480, 640, 3), np.uint8)b,g,r cv2.split(img)b[10:100, 10:100] 255 g[10:100, 10:100] 255img2 cv2.merge((b, g, r))cv2.imshow…

0401hive入门-hadoop-大数据学习.md

文章目录 1 Hive概述2 Hive部署2.1 规划2.2 安装软件 3 Hive体验4 Hive客户端4.1 HiveServer2 服务4.2 DataGrip 5 问题集5.1 Could not open client transport with JDBC Uri 结语 1 Hive概述 Apache Hive是一个开源的数据仓库查询和分析工具&#xff0c;最初由Facebook开发&…

stm32之31.iic

iic双线制。一根是SCL&#xff0c;作为时钟同步线;一根是SDA&#xff0c;作为数据传输线 SDN #include "iic.h"#define SCL PBout(8)#define SDA_W PBout(9) #define SDA_R PBin(9)void IIC_GPIOInit(void) {GPIO_InitTypeDef GPIO_InitStructure;//使能时钟GR…

vue: 使用下拉树组件@riophae/vue-treeselect

前言: 在vue中, 因为element-ui 2.X是没有tree-select组件的&#xff0c;到了element-plus就有了 riophae/vue-treeselect是一个基于 Vue.js 的树形选择器组件&#xff0c;可以用于选择树形结构的数据。它支持多选、搜索、异步加载等功能&#xff0c;可以自定义选项的样式和模…

php版 短信跳转微信小程序

实现这功能首先&#xff0c;小程序端添加业务域名 php代码 <?php declare (strict_types1);namespace app\controller\Admin;use app\model\Set; use app\Request;class Admin_Url_Scheme {public function getScheme(Request $request) {$appid 小程序appid;$secret 小…

嵌入式基础-电路

目录 1、电流 1.1电流方向 1.2交流电和直流电 2、电压 3、电阻 4、欧姆定律 1、电流 电流是指单位时间内通过导体的电荷量&#xff0c;用符号I表示&#xff0c;单位是安培&#xff08;A&#xff09;。电流是电磁学中的基本量纲之一&#xff0c;是七个基本量纲之一。电流的…

深度学习入门教学——卷积神经网络CNN

一、CNN简介 1、应用领域 检测任务 分类与检索 超分辨率重构 2、卷积网络与传统网咯的区别 传统神经网络和卷积神经网络都是用来提取特征的。神经网络&#xff1a; 可以将其看作是一个二维的。卷积神经网络&#xff1a; 可以将其看作是一个三维的。 3、整体框架 二、输入层 …