搭建购物网站,合肥公司制作网站的,龙海网站建设,网站建设与维护成绩查询文章目录 前景登录组件编写登录逻辑菜单的时机动态路由页面刷新手动修改地址 前景
不同用户拥有不同的菜单权限#xff0c;现在我们实现登录动态获取权限菜单。
登录组件编写
//当我们需要使用dva的dispatch函数时#xff0c;除了通过connect函数包裹组件还可以使用这种方… 文章目录 前景登录组件编写登录逻辑菜单的时机动态路由页面刷新手动修改地址 前景
不同用户拥有不同的菜单权限现在我们实现登录动态获取权限菜单。
登录组件编写
//当我们需要使用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 classNamelogin-pagediv classNamelogin-container style {{marginTop: 7%,marginRight: 13%}}div classNameimage-containerimg src{loginImage} altLogin //divdiv classNameform-containerh2欢迎回来/h2FormnameloginlayoutverticalForm.Item label用户名{getFieldDecorator(username, {rules: [{ required: true, message: 请输入用户名! }],})(Input placeholder请输入用户名 /)}/Form.ItemForm.Item label密码{getFieldDecorator(password, {rules: [{ required: true, message: 请输入密码! }],})(Input.Password placeholder请输入密码 /)}/Form.ItemForm.ItemButton typeprimary onClick{onSubmit} classNamelogin-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,},{//不能加exacttruepath: /,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 组件。 LayoutSider trigger{null} collapsible collapsed{collapsed}div className{layoutModule.logo} 学习系统 !!!/divMenuTree {...menuTreeProps} //SiderLayoutHeader style{{ background: #fff, padding: 0 }}IconclassName{layoutModule.trigger}type{collapsed ? menu-unfold : menu-fold}onClick{onCollapse}//HeaderContent className{layoutModule.content}Breadcrumb separator style{{ margin: 16px 0 }}Breadcrumb.ItemUser/Breadcrumb.ItemBreadcrumb.ItemBill/Breadcrumb.Item/Breadcrumbdiv style{{ padding: 24, background: #fff, minHeight: 90% }}{props.children}/div/ContentFooter style{{ textAlign: center }}Ant Design ©2018 Created by Ant UED/Footer/Layout/Layout动态路由
前面我们配置了路由地址实际情况会根据用户的不同权限展示不同的菜单下面我们将菜单抽取出一个专门的组件来动态处理。文件目录如下所示 其中SysMenu文件中菜单配置变化如下
目前我们指定菜单属性只有5个属性(后台已封装为父子结构)分别为nameurliconkeychildren按照顺序依次表现为菜单名称菜单路由菜单图标唯一key菜单的子级节点。数据结构如下所示 public static ListMenu getMenuList() {ListMenu 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 themedark modeinline 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}/spanLink to{item.url}//Menu.Item);}else{return (SubMenu key{item.key} title{spanIcon 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}/spanLink to{item.url}//Menu.Item);}//当前时父节点else{return (SubMenu key{item.key} title{spanIcon 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 themedark modeinline 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);}}});},},