umi实现动态获取菜单权限
文章目录
- 前景
- 登录组件编写
- 登录逻辑
- 菜单的时机
- 动态路由
- 页面刷新
- 手动修改地址
前景
不同用户拥有不同的菜单权限,现在我们实现登录动态获取权限菜单。
登录组件编写
//当我们需要使用dva的dispatch函数时,除了通过@connect函数包裹组件还可以使用这种方式来实现
import { getDvaApp } from 'umi';
const Login = ({ form ,dispatch}) => {const { getFieldDecorator, validateFields } = form; // 从 props 中解构出 form 方法/*** export default Form.create()(Login);*/const onSubmit = () => {validateFields((err, values) => {if (!err) {// 处理登录逻辑getDvaApp()._store.dispatch({type:"globalModel/login",payload:{username: values.username,password: values.password,}});} else {console.log('Validation Failed:', err);}});};return (<div className="login-page"><div className="login-container" style ={{marginTop: '7%',marginRight: '13%'}}><div className="image-container"><img src={loginImage} alt="Login" /></div><div className="form-container"><h2>欢迎回来</h2><Formname="login"layout="vertical"><Form.Item label="用户名">{getFieldDecorator('username', {rules: [{ required: true, message: '请输入用户名!' }],})(<Input placeholder="请输入用户名" />)}</Form.Item><Form.Item label="密码">{getFieldDecorator('password', {rules: [{ required: true, message: '请输入密码!' }],})(<Input.Password placeholder="请输入密码" />)}</Form.Item><Form.Item><Button type="primary" onClick={onSubmit} className="login-button">登录</Button></Form.Item></Form></div></div></div>);
};
// 使用 Form.create 包裹 Login 组件 从而获取form中相关的函数 否则需要使用队Form组件设置ref 通过ref来实现详见loginbak.js
export default Form.create()(Login);
效果如下所示
登录逻辑
上面登录组件我们点击登录后,使用dispatch函数发送了一个访问请求,该请求处理逻辑处理来自dva中一个namespace叫做globalModel的model,代码如下所示:
getDvaApp()._store.dispatch({type:"globalModel/login",payload:{username: values.username,password: values.password,}});
在globalModel这个model中,我们在effect块中定义了一个login函数,他主要做一下操作:
- 访问后端接口
- 存储登录的数据
- 跳转到/index页面
menuList: 是当前用户拥有的菜单权限
//import {routerRedux} from 'dva/router'//import * as requestUtil from "../utils/request";//globalModel文件内state: {menuList: [],//当前用户拥有的菜单权限},*login({payload:{username,password}}, { select, call, put }) {const {data, code, msg} = yield call(globalModelService.login,{username,password});if (code === 200){requestUtil.save(data);yield put({type: 'updateState',payload:{menuList: data.menuList,}});//去首页信息yield put(routerRedux.push('/index'))} else {message.error("登陆失败!");}},
//requestUtil内容
export function save({accessToken,refreshToken,menuList}){sessionStorage.setItem("accessToken",accessToken);sessionStorage.setItem("refreshToken",refreshToken);sessionStorage.setItem("menuList",JSON.stringify(menuList));
}
菜单的时机
上面我们将数据存储在了sessionStorage中,下面我们来看如何使用这些数据,首先我们看看路由信息。
//src/routes 这个是定义的全局路由,各个模块的路由信息将在这里汇总module.exports = [{path: "/",exact: true,redirect:"/login",//跳转到登录页},{path: "/login",exact: true,component: "@/layouts/login/login.js",},{//不能加exact=truepath: "/",component: "@/layouts/index.js",routes: [//routes将会作为 index.js 中BasicRoute组件的props信息{path: '/index',component: '@/pages/atscript/basic.tsx'},...routeConsole(practice_routes),...routeConsole(lesson_routes)]}
];
这个routes将在
.umirc.ts
作为系统的路由配置
登录完成后页面被重定向到/index
中,通过路由可以发现首先会加载父路径/
下的@/layout/index.js
的资源,相关代码如下所示,在该组件中我有一个SysMenu
组件,所需的参数是当前用户获取的菜单权限信息
。
const BasicRoute = (props) => {const {globalModel,dispatch} = props;const historyHook = useHistory();let {menuList} = globalModel;useEffect(()=>{const curMenuList = (!!menuList && menuList.length > 0 )? menuList :(!!sessionStorage.getItem("menuList")? JSON.parse(sessionStorage.getItem("menuList")) : []);dispatch({type: 'globalModel/updateState',payload:{menuList: curMenuList,}});},[menuList]);const sysMenuProps ={menuList: menuList}return (<SysMenu {...sysMenuProps}>{props.children}</SysMenu>);
}// 指定订阅数据,这里关联了 model的namespace = globalModel
function mapStateToProps({globalModel }) {return {globalModel};
}// 建立数据关联关系 对于关联组件名称
export default connect(mapStateToProps)(BasicRoute);
useEffect 一是为了防止页面刷新数据导致菜单信息丢失,二是可以避免menuList数据有延迟导致渲染问题(第一次默认值为[])
下面我们来到SysMenu
组件,最终生成菜单信息来自MenuTree
组件。
<Layout><Sider trigger={null} collapsible collapsed={collapsed}><div className={layoutModule.logo} >学习系统 !!!</div><MenuTree {...menuTreeProps} /></Sider><Layout><Header style={{ background: '#fff', padding: 0 }}><IconclassName={layoutModule.trigger}type={collapsed ? 'menu-unfold' : 'menu-fold'}onClick={onCollapse}/></Header><Content className={layoutModule.content}><Breadcrumb separator=">" style={{ margin: '16px 0' }}><Breadcrumb.Item>User</Breadcrumb.Item><Breadcrumb.Item>Bill</Breadcrumb.Item></Breadcrumb><div style={{ padding: 24, background: '#fff', minHeight: '90%' }}>{props.children}</div></Content><Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer></Layout></Layout>
动态路由
前面我们配置了路由地址,实际情况会根据用户的不同权限展示不同的菜单,下面我们将菜单抽取出一个专门的组件来动态处理。文件目录如下所示:
其中SysMenu
文件中菜单配置变化如下:
目前我们指定菜单属性只有5个属性(后台已封装为父子结构),分别为name
,url
,icon
,key
,children
,按照顺序依次表现为菜单名称,菜单路由,菜单图标,唯一key,菜单的子级节点。数据结构如下所示:
public static List<Menu> getMenuList() {List<Menu> menuList = new ArrayList<>();menuList.addAll(Arrays.asList(new Menu("学习模块", "#", "build", "lesson", new ArrayList<>()).addChild(new Menu("Ref学习", "/lesson/reftest", "skin", "/lesson/reftest", new ArrayList<>())),new Menu("练习程序", "#", "book", "practice", new ArrayList<>()).addChild(new Menu("计算程序", "/practice/calculate", "snippets", "/practice/calculate", new ArrayList<>())).addChild(new Menu("中央空调", "/practice/air", "skin", "/practice/air", new ArrayList<>())).addChild(new Menu("流程管理", "/practice/activiti", "user", "/practice/activiti", new ArrayList<>())),// new Menu("文件操作", "#", "build", "fileManage", new ArrayList<>())
// .addChild(new Menu("整体上传", "/fileManage/commonUploadFile", "user", "/fileManage/commonUploadFile", new ArrayList<>()))
// .addChild(new Menu("分片上传", "/fileManage/filePartUploadFile", "user", "/fileManage/filePartUploadFile", new ArrayList<>()))
// .addChild(new Menu("文件秒传", "/fileManage/flashUploadFile", "user", "/fileManage/flashUploadFile", new ArrayList<>()))
// .addChild(new Menu("断点续传", "/fileManage/breakPointUploadFile", "user", "/fileManage/breakPointUploadFile", new ArrayList<>())),new Menu("支付对接", "#", "build", "pay", new ArrayList<>()).addChild(new Menu("支付宝web支付", "/pay/AliWebPay", "user", "/pay/AliWebPay", new ArrayList<>())),new Menu("二维码", "/qrcode/qrcode", "build", "/qrcode/qrcode", new ArrayList<>())));return menuList;}
MenuTree
组件整体结构如下,其中extractMenus方法
则是将后台返回的权限菜单进行转化为对应的配置。
const MenuTree = ()=>{ return(<Menu theme="dark" mode="inline" defaultSelectedKeys={[menuList[0].key]}>{extractMenus(menuList)}</Menu>);
}
export default MenuTree;
const extractMenus = (list) =>{return list.map((item, index) => (doExtractMenus(item)));}//显示菜单项const doExtractMenus = (item)=>{if(item.children.length == 0){return (<Menu.Item key={item.key}><Icon type={item.icon}/><span>{item.name}</span><Link to={item.url}/></Menu.Item>);}else{return (<SubMenu key={item.key} title={<span><Icon type={item.icon}/><span>{item.name}</span></span>}>{extractMenus(item.children)}</SubMenu>);}}
页面刷新
当我们刷新页面后丢失了菜单的选中信息,实际上需要还是在对应选中的菜单节点,下面我们来避免这个问题,我们需要记录以下信息:
- 原来展开的菜单节点信息
- 原来选中的菜单
//import { useHistory } from 'react-router-dom';
//import {useEffect, useState} from "react";
const MenuTree = (props)=>{const historyHook = useHistory();const menuList = props.menuList;const [state,setState] = useState ({visible:false,menuTree:[],defaultOpenKeys:[],defaultSelectedKeys:[historyHook.location.pathname]});useEffect(()=>{const defaultOpenKeys = [historyHook.location.pathname];const menuTree = extractMenus(menuList,null,defaultOpenKeys);console.log("defaultOpenKeys",defaultOpenKeys);setState({...state,visible: true,defaultOpenKeys: defaultOpenKeys,menuTree:menuTree,})},[menuList])//显示菜单列表const extractMenus = (list, parent,curOpenKeys) =>{return list.map((item, index) => doExtractMenus(item, parent,curOpenKeys));}//显示菜单项const doExtractMenus = (item, parent,curOpenKeys)=> {//需要展开父节点if (!!parent && item.url == state.defaultSelectedKeys[0]) {console.log("parent", parent);curOpenKeys.push( parent.key);}//没有子节点if(item.children.length == 0){return (<Menu.Item key={item.key}><Icon type={item.icon}/><span>{item.name}</span><Link to={item.url}/></Menu.Item>);}//当前时父节点else{return (<SubMenu key={item.key} title={<span><Icon type={item.icon}/><span>{item.name}</span></span>}>{extractMenus(item.children, item,curOpenKeys)}</SubMenu>);}}console.log("state",state);/*** defaultOpenKeys 的使用:defaultOpenKeys 只在组件首次渲染时生效。组件执行顺序* render => useEffect 所以利用state.visible来控制*/return(state.visible &&<Menu theme="dark" mode="inline" defaultSelectedKeys={state.defaultSelectedKeys} defaultOpenKeys={state.defaultOpenKeys}>{state.menuTree}</Menu>);}
上面我们通过监听菜单信息menuList(第一次进来为[])将原来
extractMenus
方法新增parent
,curOpenKeys
,前者是为了处理选中节点,后者是为了记录需要展开的父节点信息
当第一次进来时menuList为[],导致defaultOpenKeys一直为[] ,为了避免Menu 第一次挂在后,后续刷新defaultOpenKeys将不再生效,组件使用state.visible 来控制挂载时机
手动修改地址
登录完成后避免可以通过修改浏览器直接访问/login
,也就是跳转到登录页面,我们需要配合globalModel
中的监听函数subscriptions
函数
//globalModel.js中subscriptions: {setup({ dispatch, history}) {return history.listen(location => {//如果有登录信息,直接访问/login则重定向到/index页面if (!!sessionStorage.getItem("refreshToken") && history.location.pathname === "/login"){history.push("/index");}}});},},
相关文章:
umi实现动态获取菜单权限
文章目录 前景登录组件编写登录逻辑菜单的时机动态路由页面刷新手动修改地址 前景 不同用户拥有不同的菜单权限,现在我们实现登录动态获取权限菜单。 登录组件编写 //当我们需要使用dva的dispatch函数时,除了通过connect函数包裹组件还可以使用这种方…...
Pytest-Bdd-Playwright 系列教程(14):Docstring 参数
Pytest-Bdd-Playwright 系列教程(14):Docstring 参数 前言一、什么是docstring?二、基本语法三、主要特点四、实际例子五、注意事项六、使用建议总结 前言 在自动化测试的过程中,我们经常需要处理复杂的测试数据或需要输入多行文…...
交互开发---测量工具(适用VTK或OpenGL开发的应用程序)
简介: 经常使用RadiAnt DICOM Viewer来查看DICOM数据,该软件中的测量工具比较好用,就想着仿照其交互方式自己实现下。后采用VTK开发应用程序时,经常需要开发各种各样的测量工具,如果沿用VTK的widgets的思路,…...
Qt 一个简单的QChart 绘图
Qt 一个简单的QChart 绘图 先上程序运行结果图: “sample9_1QChart.h” 文件代码如下: #pragma once#include <QtWidgets/QMainWindow> #include "ui_sample9_1QChart.h"#include <QtCharts> //必须这么设置 QT_CHARTS_USE_NAME…...
【Java笔记】LinkedList 底层结构
一、LinkedList 的全面说明 LinkedList底层实现了双向链表和双端队列特点可以添加任意元素(元素可以重复),包括null线程不安全,没有实现同步 二、LinkedList 的底层操作机制 三、LinkedList的增删改查案例 public class LinkedListCRUD { public stati…...
el-table组件树形数据修改展开箭头
<style lang"scss" scoped> ::v-deep .el-table__expand-icon .el-icon-arrow-right:before {content: ">"; // 箭头样式font-size: 16px; }::v-deep .el-table__expand-icon{ // 没有展开的状态background-color: rgba(241, 242, 245, 1);color:…...
太速科技-FMC154-基于FMC 八路SFP+万兆光纤子卡
FMC154-基于FMC 八路SFP万兆光纤子卡 一、板卡概述 本卡是一个FPGA夹层卡(FMC)模块,可提供高达8个SFP / SFP 模块接口,直接插入千兆位级收发器(MGT)的赛灵思FPGA。支持业界标准的小型可插拔࿰…...
记:排查设备web时慢时快问题,速度提升100%
问题描述 问题1: 发现web登录界面刷新和登录功能都比较卡,开浏览器控制台看了下,让我很惊讶,居然能这么慢: 公司2个局域网内的表现不同,局域网A中的都比较卡,局域网B中的又不存在该现象。 问…...
音视频入门基础:MPEG2-TS专题(13)——FFmpeg源码中,解析Section Header的实现
一、引言 在《音视频入门基础:MPEG2-TS专题(11)—— TS中的Section》中讲述了Section Header的基本概念,本文讲述FFmpeg源码中是怎样解析Section Header的。 二、parse_section_header函数的定义 FFmpeg源码中通过parse_section…...
根据PDF模板单个PDF导出到浏览器和多个PDF打包ZIP导出到浏览器
一、单个PDF导出到浏览器 /*** * param templatePath 模板路径* param fileName 文件名称* param data 填充文本* param images 填充图片* param response* throws IOException*/public static void generateTempPDF(String templatePath, String fileName, Map<String, S…...
如何创建一个基本的Spring Boot应用程序
以下是一个简单的Spring Boot应用开发代码示例,它展示了如何创建一个基本的Spring Boot应用程序,并实现一个简单的RESTful API服务。 步骤1:创建项目 使用Spring Initializr或您喜欢的IDE(如IntelliJ IDEA或Eclipse)…...
1.2 计算机网络的分类和应用(重要知识点)
1.2.1 计算机网络的分类 计算机网络的定义: 由通信线路互相连接的、能自主工作的计算机构成,强调各计算机(工作站)拥有独立的计算资源和任务能力。与多终端分时系统不同,后者终端仅作为主机接口,不具备计…...
@JsonSerialize失效解决
当在实体类中加入这个注解时,本意是想如果是空值则返回0给页面,但是发现使用 JsonSerialize(using BigSerializer.class)无效,因为如果是null值会不走序列化的接口实现类,需要使用nullUsing 需要这样使用...
Docker部署WebRTC-Streamer
文章目录 WebRTC-Streamer概述Docker部署WebRTC-StreamerVue使用WebRTC-Streamer一些问题 WebRTC-Streamer概述 WebRTC-Streamer是一个基于WebRTC技术的流媒体传输工具,它可以通过Web浏览器实现实时音视频流的传输和播放。它提供了一种简单而强大的方式ÿ…...
2025年的大模型计划重点在于跨领域智能、工作流自动化、多模态能力强化
明年的计划和大模型发展方向可以围绕以下几个方面展开,结合实际应用场景和技术趋势,明确可执行的目标和期待的成果: 2025 年计划与展望:大模型能做些什么? 1. 更深层次的跨领域能力融合 目标:构建更强的跨…...
day12 接口测试 ——入门→精通→实战(1)
【没有所谓的运气🍬,只有绝对的努力✊】 目录 1、接口测试分类 1.1 内部接口: 1.2 外部接口: 2、目前接口架构设计 2.1、基于SOAP架构, 2.2、基于RPC架构, 2.3、基于RestFul架构, 2.3.1…...
伏羲0.07(文生图)
为了使0.06代码能够有效运行并输出项目目录及所有文件,我们在代码中添加一些额外的功能。 项目目录结构 项目目录结构如下: text_to_image_project/ │ ├── config.yaml ├── data/ │ ├── train_data.csv │ └── test_data.txt ├── mod…...
scala的泛型特质的应用场景
//泛型特质的应用场景 //作比较找出最大值 //定义一个函数,用来求List元素中的最大值参考代码:object Test4 {def getMax[T](list:List[T])(implicit ev:T > Ordered[T]): T {list.reduce((a:T,b:T)> if(a>b) a else b)}def main(args: Array…...
Win10环境vscode+latex+中文快速配置
安装vscodelatex workshop 配置: {"liveServer.settings.donotVerifyTags": true,"liveServer.settings.donotShowInfoMsg": true,"explorer.confirmDelete": false,"files.autoSave": "afterDelay","exp…...
【vue2】el-select,虚拟滚动(vue-virtual-scroller)
需求背景 vue2+element-ui项目中,当el-select中数据量较大时(超出5000个dom节点),会导致页面加载和渲染卡顿、el-select下拉列表延迟展开。 在现在的el-select的基础上使用分页或者虚拟列表的形式去处理大量的下拉菜单,可以保证页面的正常渲染及el-select的…...
【ETCD】[源码阅读]深度解析 EtcdServer 的 processInternalRaftRequestOnce 方法
在分布式系统中,etcd 的一致性与高效性得益于其强大的 Raft 协议模块。而 processInternalRaftRequestOnce 是 etcd 服务器处理内部 Raft 请求的核心方法之一。本文将从源码角度解析这个方法的逻辑流程,帮助读者更好地理解 etcd 的内部实现。 方法源码 …...
【RabbitMQ】RabbitMQ中核心概念交换机(Exchange)、队列(Queue)和路由键(Routing Key)等详细介绍
博主介绍:✌全网粉丝21W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
【AI知识】过拟合、欠拟合和正则化
一句话总结: 过拟合和欠拟合是机器学习中的两个相对的概念,正则化是用于解决过拟合的方法。 1. 欠拟合: 指模型在训练数据上表现不佳,不能充分捕捉数据的潜在规律,导致在训练集和测试集上的误差都很高。欠拟合意味着模…...
计算机毕设-基于springboot的航空散货调度系统的设计与实现(附源码+lw+ppt+开题报告)
博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…...
视图、转发与重定向、静态资源处理
目录 视图 默认视图 视图机制原理 自定义视图 请求转发与重定向 静态资源处理 视图 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性 可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高 默认是最低优先级,Integer.MAX_…...
优选算法——分治(快排)
1. 颜色分类 题目链接:75. 颜色分类 - 力扣(LeetCode) 题目展示: 题目分析:本题其实就要将数组最终分成3块儿,这也是后面快排的优化思路,具体大家来看下图。 这里我们上来先定义了3个指针&…...
【Linux系统】文件系统
Windows 和 Linux 的文件系统: windows:NTFS —> NTFS:磁盘大于目录:目录是磁盘的一部分。ubuntu :EXT4 —> EXT4: 目录大于磁盘:磁盘是目录的一部分。 Windows文件系统的特点 基于分区的文件系统: Windows…...
javaweb的基础
文章的简介: 页面的展示(HTML)页面的修改、绑定、弹窗(js的dom、bom等)页面的请求(Ajax) 1、在HTML中用标签和css样式实现了浏览器页面。 2、用JS实现页面内容(图片,复选框、文本颜色内容)的修改和弹框&…...
家里养几条金鱼比较好?
金鱼,作为备受喜爱的家庭水族宠物,其饲养数量一直是众多养鱼爱好者关注的焦点。究竟养几条金鱼最为适宜,实则需要综合考量多方面因素,方能达到美观、健康与和谐的理想养鱼境界。 从风水文化的视角来看,金鱼数量有着诸…...
写作词汇积累:差池、一体两面、切实可行极简理解
差池 【差池】可以是名词,是指意外的事或错误。 【差池】也可以是形容词,是指参差不齐、差劲或不行。 1. 由于操作不当,导致这次实验出现了【差池】,我们需要重新分析原因并调整方案。(名词,表示意外的事…...
招聘网站源码下载/app推广兼职是诈骗吗
ps命令时显示当前系统进程信息的命令,与top不同的是,ps返回的事当前活动进程的信息清单。所以,如果想要得到动态更新显示的信息,使用top命令。 默认ps: 选定所有有效进程(EUID)转载于:https://w…...
怎样做公司的网站首页/关键词优化 搜索引擎
题意: 有t组测试数据,每组测试数据给一个矩阵n,m。 接下来给出n行,每行第一个数字为该行的编号(从1开始),然后给出这行不能走的y坐标。 问从出发点(1,1)&…...
网站建设分析报告/搜一搜
note:本文短代码实现环境:win10,python3 本文代码执行情况 python打开浏览器方法一: 通过引用os包,调用system方法调用系统的ie程序来打开网址 代码如下: import os os.system("C:/Program Files/Internet Explorer/iexplore…...
html5 网站模板/网页设计模板素材图片
前言HashMap的储存是没有顺序的,而是按照key的HashCode实现.key手机品牌,value价格,这里以这个例子实现按名称排序和按价格排序.Map phonenew HashMap();phone.put("Apple",7299);phone.put("SAMSUNG",6000);phone.put("Meizu",2698);phone.put(…...
网站制作杭州/桂平seo关键词优化
SEOer们都知道网站运营是一件长期需要做的事,所以网站上线后也是需要进行维护的,企业搭建网站的目的是使我们的公司更好地显示其信息。那么该如何维护刚上线的网站呢?一、网站结构维护也许是因为网站刚刚上线,或者是由于网站建设者的技术能力…...
兴仁企业建站公司/谷歌手机版下载安装
01. ip address show/ip a 检查网卡地址配置02. ping 测试网络连通性03. nmtui 图形界面修改网卡地址信息04. exit 注销05. shutdown 关机命令shutdown -h 5 指定关机时间 (推荐)shutdown -r 5 重启主机时间 (推荐)shutdown -…...