上一篇文章中(从0开始手把手教你做极客园项目(一)——登录功能实现)详细讲解了登录功能,在登录跳转后就是首页,本文详细讲解跳转到首页的功能实现源码地址极客园github
在Layout/index.tsx里参考antd的官方文档将首页的界面搭建,items表示首页右边三个导航栏的切换
import { Layout, Menu, Popconfirm } from 'antd'
import {
HomeOutlined,
DiffOutlined,
EditOutlined,
LogoutOutlined,
} from '@ant-design/icons'
import './index.scss'
const { Header, Sider } = Layout
const items = [
{
label: '首页',
key: '1',
icon: <HomeOutlined />,
},
{
label: '文章管理',
key: '2',
icon: <DiffOutlined />,
},
{
label: '创建文章',
key: '3',
icon: <EditOutlined />,
},
]
const GeekLayout = () => {
return (
<Layout>
<Header className="header">
<div className="logo" />
<div className="user-info">
<span className="user-name">zwb</span>
<span className="user-logout">
<Popconfirm title="是否确认退出?" okText="退出" cancelText="取消">
<LogoutOutlined /> 退出
</Popconfirm>
</span>
</div>
</Header>
<Layout>
<Sider width={200} className="site-layout-background">
<Menu
mode="inline"
theme="dark"
defaultSelectedKeys={['1']}
items={items}
style={{ height: '100%', borderRight: 0 }}></Menu>
</Sider>
<Layout className="layout-content" style={{ padding: 20 }}>
内容
</Layout>
</Layout>
</Layout>
)
}
export default GeekLayout
对应的index.scss样式文件如下
.ant-layout {
height: 100%;
}
.header {
padding: 0;
}
.logo {
width: 200px;
height: 60px;
background: url('~@/assets/logo.png') no-repeat center / 160px auto;
}
.layout-content {
overflow-y: auto;
}
.user-info {
position: absolute;
right: 0;
top: 0;
padding-right: 20px;
color: #fff;
.user-name {
margin-right: 20px;
}
.user-logout {
display: inline-block;
cursor: pointer;
}
}
.ant-layout-header {
padding: 0 !important;
}
同时安装normalize.css用于消除浏览器默认样式,在根目录下的index.tsx里引入,并且创建一个index.scss文件用于设置整个界面充满的样式
npm install normalize.css
import 'normalize.css'
import './index.scss'
html,
body {
margin: 0;
height: 100%;
}
#root {
height: 100%;
}
首先在pages里创建三个二级路由界面组件,分别表示首页,文章管理,创建文章的模块,rafce初始化组件,在router/index.tsx里添加layout路由的children表示二级路由,并在layout组件里导出二级路由
{
path: '/',
element: <AuthRouter><Layout /></AuthRouter>,
children: [
{
index:true,//默认二级路由
element: <Home />
},
{
path: 'article',
element: <Article />
},
{
path: 'publish',
element: <Publish />
}
]
},
<Layout className="layout-content" style={{ padding: 20 }}>
<Outlet/>//二级路由出口
</Layout>
实现跳转路由需要拿到当前点击的二级路由地址,可以将原来items里的key换成路径地址,然后通过添加点击函数获取到当前点击的key,利用navigate方法实现路由跳转
const items = [
{
label: '首页',
key: '/',
icon: <HomeOutlined />,
},
{
label: '文章管理',
key: '/article',
icon: <DiffOutlined />,
},
{
label: '创建文章',
key: '/publish',
icon: <EditOutlined />,
},
]
const navigate=useNavigate()
<Menu
mode="inline"
theme="dark"
defaultSelectedKeys={['1']}
items={items}
style={{ height: '100%', borderRight: 0 }}
onClick={(router)=>{navigate(router.key)}}//点击跳转到相应key的路径路由
></Menu>
原来的菜单栏是只有点击的时候才会显示高光,但是在刷新界面的时候高光消失了,要解决这个问题可以使用useLocation获取到当前路径,在antd的Menu组件中增加一个selectedKeys这个属性,代表当前选中的值
const location=useLocation()
const selected=location.pathname
<Menu
mode="inline"
theme="dark"
selectedKeys={[selected]}
items={items}
style={{ height: '100%', borderRight: 0 }}
onClick={(router)=>{navigate(router.key)}}
></Menu>
获取用户信息我们可以使用redux状态管理来对用户信息进行保存,在user.tsx文件中添加用户的状态和设置状态的函数,并通过一个异步方法获取后端的数据,在组件Layout/index.tsx中通过useEffect钩子函数在页面加载的时候就进行渲染
interface User {
id: string;
photo: string;
name: string;
mobile: string;
gender: number;
birthday: string;
intro: string | null;
}
const userStore = createSlice({
name: 'user',
initialState: {
token: getToken() || '',
user:{} as User
},
reducers: {
setToken(state, action) {
state.token = action.payload
_setToken(action.payload)
},
setUser(state,action){
state.user=action.payload
}
}
})
const fetchUser: () => (dispatch: Dispatch) => Promise<void> = () => {
return async (dispatch: Dispatch) => {
const res= await request.get('/user/profile')
dispatch(setUser(res.data))
}
}
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(fetchUser())
},[dispatch])
可以看见redux状态里已经有了后端取到的user对象,使用useAppSelector函数获取状态数据进行渲染
const name =useAppSelector(state=>state.user.user.name)
<span className="user-name">{name}</span>
退出登录时,redux状态里关于所有用户的信息都要删除,并且localstorage里的token也要清除,所以在user.tsx的reducers中添加一个清除的函数
exitUser(state){
state.token=''
state.user={} as User
removeToken()
}
在组建中绑定点击函数,点击函数调用清除的方法和跳转到登录界面
<Popconfirm title="是否确认退出?" okText="退出" cancelText="取消" onConfirm={onconfirm}>
<LogoutOutlined /> 退出
</Popconfirm>
const onconfirm=()=>{
dispatch(exitUser())
navigate('/login')
}
用户在长时间未在网站做任何操作,且规定时间到达时,当前的token失效,token失效后,后端返回401状态码,而前端需要监控这个状态,在拦截器中监控401,清除token返回登录,在utils里的request.tsx里的响应拦截器添加清除和跳转界面以及刷新界面的操作
request.interceptors.response.use((response)=> {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response.data
}, (error)=> {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
if(error.response.status===401){
removeToken()
router.navigate('/login')
window.location.reload()
}
return Promise.reject(error)
})
可以手动模拟token失效,自行在localstorage里的token修改,然后手动按下刷新
安装echarts
npm install echarts
封装一个柱状图组件,在Home里添加一个components文件夹,新建一个BarChart.tsx文件,文件当中引入echarts官网的柱状图模板,添加自己需要改的数据,使用useRef与节点进行绑定,
import * as echarts from 'echarts';
import { useEffect, useRef } from 'react';
interface Title{
title:string
}
const BarChart = ({title}:Title) => {
const chartRef = useRef(null)
useEffect(() => {
const chartDom = chartRef.current
const myChart = echarts.init(chartDom);//初始化生成图表实例对象
const option = {
title: {
text: title
},
xAxis: {
type: 'category',
data: ['vue', 'react', 'angular']
},
yAxis: {
type: 'value'
},
series: [
{
data: [10, 40, 70],
type: 'bar'
}
]
};
option && myChart.setOption(option);
})
return (
<div><div ref={chartRef} style={{ width: '500px', height: '400px' }}></div></div>
)
}
export default BarChart
在父组件中index.tsx中调用组件,通过props传入title参数
<div style={{display:"flex",flexDirection:"row"}}>
<BarChart title="三大框架满意度"/>
<BarChart title="三大框架使用度"/>
</div>
本次实现了首页的基本功能,例如菜单跳转,二级路由,高亮显示以及图表数据显示(这里的图表数据有需要的话可以跟后端获取),再进行了优化例如token失效的情况,剩下的功能和优化会更新在后续,喜欢的小伙伴们点点关注点点赞,你们的支持就是我的动力