admin管理员组文章数量:1582715
React+FluentUI实现导航栏和页面跳转
大多数企业级应用后台都有个菜单栏,如图:
路由和跳转页面搭建好以后整个框架雏形就出来了,接下来我们介绍一下路由页面跳转的设计思路;
需求:
- 此案例分为一级菜单和二级菜单,一级菜单分可展开多项子菜单和不可展开的一级跳转菜单;学生页和老师页两个一级菜单可以展开和闭合,并记录状态,点击学生1,学生2,老师1,老师2子页面,页面跳转,闭合状态不重置,页面不整体刷新,只是页面内容被刷新。
实现思路:
- 左侧导航栏菜单作为父级页面,页面具体内容作为子页面,每次跳转父页面不变,子页面替换;
- 左侧导航栏菜单抽象出一个数组,方便以后维护,此数组只记录菜单节点内容,不应有其他逻辑;
- 一级菜单闭合状态由
useState
记录,每次点击更新state,遍历路由后由一级菜单state重新赋值再传给解析路由,实现闭合状态记录;
具体代码
//C:\Code\ReactCode\react-demo-ts-menu\src\routes.tsx
import Layout from './layouts/index';
import Stu1 from './pages/student/stu1';
import Stu2 from './pages/student/stu2';
// import Student from './pages/student/student';
import Teacher from './pages/teacher/teacher';
import Teacher1 from './pages/teacher/teacher1';
import Teacher2 from './pages/teacher/teacher2';
import { NavFabricDemoAppExample, routeConfig } from './nav';
const routes: routeConfig[] = [
{
path: '/',
name:'首页',
component: Layout,
key: '/',
icon: 'Home',
exact: true
},
{
path: '/school',
name:'学生页',
icon: 'BulletedList',
// key: 'student',
isExpanded: true,
component: Teacher,
children: [
{
path: '/school/stu1',
name: '学生1',
component: Stu1
},
{
path: '/school/stu2',
name: '学生2',
component: Stu2
}
]
},
{
path: '/school',
name: '老师页',
icon: 'BulletedList',
// key: 'teacher',
component: Teacher,
isExpanded: true,
children: [
{
path: '/school/teacher1',
name: '老师1',
component: Teacher1
},
{
path: '/school/teacher2',
name: '老师2',
component: Teacher2
},
]
},
{
path: '/school/about',
name: '关于我们',
icon: 'ContactInfo',
// isExpanded: true,
key: '/school/about',
component: NavFabricDemoAppExample,
exact: true,
},
]
export { routes };
//C:\Code\ReactCode\react-demo-ts-menu\src\nav.tsx
import * as React from 'react';
import { Nav, INavLink, INavStyles, INavLinkGroup } from 'office-ui-fabric-react/lib/Nav';
import { initializeIcons } from '@uifabric/icons';
import { routes } from './routes';
import { withRouter } from 'react-router-dom';
import { useState } from 'react';
initializeIcons();
const navStyles: Partial<INavStyles> = { root: { width: 300 } };
export interface expandedModel {
isExpanded: boolean;
key: string;
}
// const [isExpandedState, setIsExpandedState] = useState<expandedModel>({ isExpanded: true, key: "student" });
// const [stuIsExpanded, setStuIsExpanded] = useState(true);
// const [teacherIsExpanded, setTeacherIsExpanded] = useState(true);
// const navLinkGroups: INavLinkGroup[] = [
// {
// links: [
// {
// name: 'Home',
// url: 'http://example',
// // expandAriaLabel: 'Expand Home section',
// // collapseAriaLabel: 'Collapse Home section',
// links: [
// {
// name: 'Activity',
// url: 'http://msn',
// key: 'key1',
// target: '_blank',
// },
// {
// name: 'MSN',
// url: 'http://msn',
// disabled: true,
// key: 'key2',
// target: '_blank',
// },
// ],
// isExpanded: true,
// },
// {
// name: 'Documents',
// url: 'http://example',
// key: 'key3',
// isExpanded: true,
// target: '_blank',
// },
// {
// name: 'Pages',
// url: 'http://msn',
// key: 'key4',
// target: '_blank',
// },
// {
// name: 'Notebook',
// url: 'http://msn',
// key: 'key5',
// disabled: true,
// },
// {
// name: 'Communication and Media',
// url: 'http://msn',
// key: 'key6',
// target: '_blank',
// },
// {
// name: 'News',
// url: 'http://cnn',
// icon: 'News',
// key: 'key7',
// target: '_blank',
// },
// ],
// },
// ];
export interface routeConfig {
path: string;
name: string;
key?: string;
children?: routeConfig[];
component?: any;
hide?: boolean;
exact?: boolean;
icon?: string;
isExpanded?: boolean;
}
const ParseRoute = (route: routeConfig[]): INavLinkGroup => {
let navLinkGroupArr: INavLinkGroup = { links: [] };
// eslint-disable-next-line
routes.map((item: routeConfig, index: number) => {
const parent_link: INavLink = { name: '', url: '' };
const child_links: INavLink[] = [];
if (item.children !== undefined && item.children !== null) {
// eslint-disable-next-line
item.children.map((iitem: any, index: number) => {
const navlink: INavLink = { url: '', key: '', name: '' };
navlink.url = iitem.path;
navlink.name = iitem.name;
navlink.key = iitem.path;
navlink.icon = 'News';
// navlink.isExpanded = true;
child_links.push(navlink);
parent_link.links = child_links;
})
}
parent_link.icon = item.icon;
// parent_link.icon = 'Brightness';
parent_link.name = item.name;
parent_link.key = item.key;
parent_link.path = item.path;
parent_link.isExpanded = item.isExpanded == null ? true : item.isExpanded;
navLinkGroupArr.links.push(parent_link);
})
return navLinkGroupArr;
}
export class NavFabricDemoAppExample extends React.Component<any, any> {
navwithRouter = withRouter(({ history }) => {
const [isExpandedStuState, setIsExpandedStuState] = useState<boolean>(true);
const [isExpandedTeacherState, setIsExpandedTeacherState] = useState<boolean>(true);
const route: routeConfig[] = routes;
// eslint-disable-next-line
route.map((item: routeConfig, index: number) => {
if (item.name === '学生页') {
item.isExpanded = isExpandedStuState;
}
if (item.name === '老师页') {
item.isExpanded = isExpandedTeacherState;
}
})
let groups = ParseRoute(route);
// console.log(groups)
return (
<Nav
groups={[groups]}
onLinkClick={
(ev?: React.MouseEvent<HTMLElement>, item?: INavLink) => {
if (ev) {
ev.nativeEvent.preventDefault();
}
// console.log("item.path: " + item?.path);
if (item && item.name === '学生页') {
// alert('学生页 link clicked');
setIsExpandedStuState(!isExpandedStuState);
}
if (item && item.name === '老师页') {
// alert('学生页 link clicked');
setIsExpandedTeacherState(!isExpandedTeacherState);
}
if (item && item.key) {
history.push(item.key);
// console.log("item.key: " + item.key);
// console.log("history: " + history.location.pathname);
}
}
}
selectedKey={
history.location.pathname
}
ariaLabel="Nav basic example"
styles={navStyles}
/>
);
});
render() {
return (<div className='sidebar'><this.navwithRouter /></div>);
}
};
//C:\Code\ReactCode\react-demo-ts-menu\src\pages\teacher\teacher.tsx
import * as React from 'react';
import { renderRoutes } from 'react-router-config';
// import { Link } from 'react-router-dom';
import { routes } from '../../routes';
import { NavFabricDemoAppExample, routeConfig } from '../../nav';
export interface Props {
name: string;
enthusiasmLevel?: number;
route: routeConfig[];
}
function Teacher({ name = 'chunyu, this is the parent page', enthusiasmLevel = 1, route = routes }: Props) {
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
// console.log("route in teacher page: " + routes[1].children);
return (
<div className="hello">
<NavFabricDemoAppExample></NavFabricDemoAppExample>
{/* {renderRoutes(route.children)} */}
{renderRoutes(routes[1].children)}
{renderRoutes(routes[2].children)}
{/* {renderChildRoutes()} */}
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
</div>
);
}
export default Teacher;
// helpers
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
}
//why can not foreach route to render.
// function renderChildRoutes() {
// routes.map((item: routeConfig, index: number) => {
// if (item.children && item.children !== null && item.children !== undefined) {
// renderRoutes(item.children)
// console.log("....." + item.children);
// }
// })
// return null;
// }
//C:\Code\ReactCode\react-demo-ts-menu\src\pages\teacher\teacher1.tsx
import * as React from 'react';
// import { renderRoutes } from 'react-router-config';
// import { NavFabricDemoAppExample } from '../../nav';
export interface Props {
name: string;
enthusiasmLevel?: number;
}
function Teacher1({ name = 'teacher1...', enthusiasmLevel = 1 }: Props) {
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
</div>
);
}
export default Teacher1;
// helpers
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
}
//C:\Code\ReactCode\react-demo-ts-menu\src\pages\teacher\teacher2.tsx
import * as React from 'react';
// import { NavFabricDemoAppExample } from '../../nav';
export interface Props {
name: string;
enthusiasmLevel?: number;
}
function Teacher2({ name = 'teacher222...', enthusiasmLevel = 1 }: Props) {
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
</div>
);
}
export default Teacher2;
// helpers
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
}
//C:\Code\ReactCode\react-demo-ts-menu\src\pages\student\stu1.js
import React from 'react';
// import { renderRoutes } from 'react-router-config';
export default class Stu1 extends React.Component {
constructor(props) {
super(props);
this.state = {
route: props.route,
}
// console.log(props);
}
render() {
// const route = this.state.route;
// console.log(route);
const name = 'Stu1 Page.';
return <div>
{/* {renderRoutes(route.children)} */}
Hello {name}
</div>;
}
}
//C:\Code\ReactCode\react-demo-ts-menu\src\pages\student\stu2.js
import React from 'react';
// import { renderRoutes } from 'react-router-config';
export default class Stu2 extends React.Component {
constructor(props) {
super(props);
this.state = {
route: props.route,
}
// console.log(props);
}
render() {
// const route = this.state.route;
// console.log(route);
const name = 'Stu2 Page.';
return <div>
{/* {renderRoutes(route.children)} */}
Hello {name}
</div>;
}
}
以上附上路由设计核心代码和学生老师父子页面;
参考说明:
路由是依赖react-router-dom
包的
UI是微软的Fluent UI又叫Fabric,之所以选择这个原因有两点,它没有封装太多冗余样式和功能,运行起来不是很慢,第二,提供丰富微软风格的UI界面,只是有好多插件需要自己实现,倒也很灵活;
Fluent UI官网点这里,
仅供学习参考,如有侵权联系我删除
版权声明:本文标题:React+FluentUI实现导航栏和页面跳转 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1727896251a1136824.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论