前言
本文将详细介绍页面布局的搭建过程,以及其中涉及的相关功能。主要包括头部、侧边栏和内容区域。我们将讲解其中用到的相关功能:如何注册子路由、监听路由变化、实现侧边栏收缩功能、observer监听器的使用、在头部展示和更新个人信息以及使用MobX进行信息管理、登录、登出和记录路由等相关功能的实现。
一、Layout父级组件封装
分析:
(1)主要分为侧边栏,头部,内容;
(2)使用Outlet来注册子路由
(3)使用useEffect,useLocation监听路由,当路由变化时,重新记录当前路由
(4)调用store侧边栏状态并使用
注:
store方法请查看之前文章:React后台管理(五)-- 状态管理工具mobx安装以及本项目登录,表格,用户信息状态管理封装使用
// @/layout/index.jsx
import React, { useState, useEffect } from "react";
import { Outlet, useLocation } from "react-router-dom";
import { Layout, Menu } from "antd";
import { observer } from "mobx-react-lite";
import useStore from "@/store";
import items from "@/components/menuItem.jsx";
import MyHeader from "@/layout/components/header/index.jsx";
import "./index.scss";
const { Sider, Content } = Layout;
const LayOut = () => {
const { UserStore, TableStore } = useStore();
const [defaultOpenKeys] = useState(["Workbench", "chainManage"]);
const { pathname } = useLocation();
// 仅在pathname变化时执行一次
useEffect(() => {
TableStore.SET_CURRENT_PATH(pathname); // 执行副作用,设置当前路由
}, [pathname]); // 依赖于location变化来执行副作用
const collapsed = UserStore.state.collapse; // store: 侧边栏展开收起状态
return (
<Layout>
<Sider trigger={null} collapsible width="208" collapsedWidth="80" collapsed={collapsed} className="sider">
{!collapsed ? <div className="logo" /> : <div className="logo1" />}
<Menu theme="dark" mode="inline" defaultSelectedKeys={[pathname]} defaultOpenKeys={defaultOpenKeys} items={items} />
</Sider>
<Layout className="site-layout">
<MyHeader />
<Content>
<Outlet />
</Content>
</Layout>
</Layout>
);
};
export default observer(LayOut);
// 样式文件如下
// ./index.scss
.ant-layout {
height: 100%;
}
.ant-layout-sider {
}
.sider {
padding: 0;
}
.logo,
.logo1 {
width: 155px;
height: 22px;
background: url("~@/assets/s-logo.png") no-repeat center;
margin: 16px auto;
}
.logo1 {
width: 36px;
height: 36px;
background: url("~@/assets/s-logo1.png") no-repeat center;
}
.ant-layout-header svg {
font-size: 15px;
margin-left: 15px;
}
.user-name {
position: absolute;
right: 5%;
margin-right: 10px;
margin-left: 22px;
}
.user-logout {
position: absolute;
right: 2%;
display: inline-block;
cursor: pointer;
}
#components-layout-demo-custom-trigger .trigger {
padding: 0 24px;
font-size: 18px;
line-height: 64px;
cursor: pointer;
transition: color 0.3s;
}
#components-layout-demo-custom-trigger .trigger:hover {
color: #1890ff;
}
#components-layout-demo-custom-trigger .logo {
height: 32px;
margin: 16px;
background: rgba(255, 255, 255, 0.3);
}
二、MyHeader头部组件封装
分析:
(1)使用observer包裹函数组件,使其可以监听状态变化,实时更新
(2)使用createElement创建一个可收缩元素,并添加点击事件,实现展开收起功能
(3)调用状态管理方法setCollapse实时更新收缩状态
(4)引入用户信息函数组件UserInfo.jsx
注:
setCollapse方法请查看之前文章:React后台管理(五)-- 状态管理工具mobx安装以及本项目登录,表格,用户信息状态管理封装使用
// @/layout/components/header/index.jsx
import React from "react";
import { observer } from "mobx-react-lite";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
import useStore from "@/store";
import UserInfo from "@/layout/components/header/components/user/index.jsx";
import "./index.scss";
const MyHeader = (props) => {
const { UserStore } = useStore();
// store: 侧边栏展开收起状态
const collapsed = UserStore.state.collapse;
return (
<>
<div className="layout-header">
<div className="header-left">
{React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: "trigger",
onClick: () => UserStore.setCollapse(),
})}
</div>
<div className="header-menu">
<UserInfo />
</div>
</div>
</>
);
};
export default observer(MyHeader);
// @/layout/components/header/index.jsx
.layout-header {
position: relative;
display: flex;
align-items: center;
height: 40px;
padding: 0 24px;
background-color: #fff;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.05);
.header-left {
flex: 1;
width: 0;
display: flex;
align-items: center;
}
.header-menu {
display: flex;
align-items: center;
}
}
三、UseInfo用户下拉信息组件封装
分析:
(1)使用observer包裹函数组件,以监听个人信息变化并实时更新
(2)调用LoginStore,实现退出登录功能
注:
LoginStore实例方法请查看之前文章:React后台管理(五)-- 状态管理工具mobx安装以及本项目登录,表格,用户信息状态管理封装使用
// @/layout/components/header/components/user/index.jsx
import { Modal, Dropdown } from "antd";
import useStore from "@/store";
import { useNavigate } from "react-router-dom";
import { observer } from "mobx-react-lite";
import { ExclamationCircleFilled } from "@ant-design/icons";
import "./index.scss";
const { confirm } = Modal;
const UserInfo = () => {
const { LoginStore } = useStore();
const navigation = useNavigate();
const loginOut = () => {
confirm({
title: "退出登录",
icon: <ExclamationCircleFilled />,
content: <>确定退出登录</>,
onOk() {
LoginStore.logOutAction().then(() => {
navigation("/login", { replace: true });
});
},
onCancel() {
console.log("Cancel");
},
});
};
const items = [
{
key: "1",
label: (
<div className="meta">
<img src={require("@/assets/admin.png")} />
<div>
<h4 className="ellipse-1" title={LoginStore?.state?.username}>
<strong>{LoginStore?.state?.username}</strong>
</h4>
<span className="ellipse-1" title={LoginStore?.state?.role}>
{LoginStore?.state?.role}
</span>
</div>
</div>
),
},
{
key: "2",
label: (
<a>
<i className="iconfont icon-user-2"></i>个人中心
</a>
),
},
{
key: "3",
label: (
<a>
<i className="iconfont icon-dingdan"></i>平台资料
</a>
),
},
{
key: "4",
label: (
<a onClick={loginOut}>
<i className="iconfont icon-tuichu"></i>退出登录
</a>
),
},
];
return (
<Dropdown menu={{ items }} overlayClassName={"userinfo-popup"} placement="bottomRight" arrow>
<div>
<div className="user-info">
<img src={require("@/assets/admin.png")} />
<div>{LoginStore?.state?.username}</div>
</div>
</div>
</Dropdown>
);
};
export default observer(UserInfo);
// @/layout/components/header/components/user/index.scss
.user-info {
padding-left: 16px;
height: 40px;
display: flex;
align-items: center;
img {
width: 24px;
height: 24px;
border-radius: 100px;
margin-right: 8px;
}
span,
strong {
display: block;
line-height: 1;
font-size: 14px;
}
span {
margin-top: 4px;
font-size: 12px;
color: #666666;
}
}
.userinfo-popup {
background-color: #fff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.15);
min-width: 164px !important;
li {
&:not(:first-child) {
position: relative;
display: flex;
align-items: center;
height: 32px;
padding: 0 12px;
color: #555555;
font-size: 14px;
margin: 4px 0;
cursor: pointer;
&:hover {
background-color: #f2f2f2;
}
i {
margin-right: 8px;
}
}
&:nth-child(1):hover {
cursor: initial;
background-color: #ffffff !important;
}
&:nth-last-child(1) {
border-top: 1px solid #e9e9e9;
padding-top: 20px !important;
padding-bottom: 20px !important;
}
}
.meta {
display: flex;
align-items: center;
padding-bottom: 10px;
img {
width: 36px;
height: 36px;
border-radius: 100px;
}
> div {
padding-left: 8px;
flex: 1;
width: 0;
span {
font-size: 12px;
color: #666666;
}
}
}
}
四、效果展示
总结
下一篇讲【开发页面前准备—插槽以及函数组件传值】。关注本栏目,将实时更新。