效果
1. index.ts
import { useEffect, useState } from 'react';
import style from './index.less'
import { history } from 'umi';
interface Props {
children: any
route: any
location: any
}
/**
* 路由信息
*/
interface RouteInfo {
// 路由路径
path: string,
// 路由名称
name: string,
}
/**
* 页面容器
*/
export default (props: Props) => {
// 全部路由数据
const [routeList, setRouteList] = useState<RouteInfo[]>([])
// 当前选中的路由
const [checkRouteList, setCheckRouteList] = useState<RouteInfo[]>([])
// 当前选中的路径
const [currentPath, setCurrentPath] = useState<string>('')
// 获取路由全部数据
useEffect(() => {
let routeList: any = []
recurGetRoutes(props.route, routeList)
setRouteList(routeList)
}, [])
let pathName = props.location.pathname
/**
* 当路由数据加载完毕后,如果当前路径和当前所在路由不同,则需要判断是否已经push到选中的路由中。
* 如果已经选中则不需要重复push。
*/
if (routeList?.length) {
if (currentPath !== pathName) {
setCurrentPath(pathName)
if (!checkRouteList.some(item => item.path === pathName)) {
let currentRoutes: RouteInfo[] = routeList.filter(item => item.path === pathName)
if (currentRoutes?.length) {
checkRouteList.push(currentRoutes[0])
}
}
}
}
return <>
<div style={{
margin: 10
}}>
<div
style={{
display: 'flex'
}}
>
{
checkRouteList.map(item => {
return <TitleBar routeInfo={item}
activePath={currentPath}
onContextMenu={function (path: string): void {
debugger
}}
onClick={function (path: string): void {
// 设置当前路径
setCurrentPath(path)
history.push(path)
}} onClose={function (path: string): void {
// 删除当前路径
let residualRouteList = checkRouteList.filter(item => item.path !== path)
setCheckRouteList(residualRouteList)
// 如果选中的路由不存在则回到首页
if (residualRouteList?.length) {
history.push(residualRouteList[0].path)
} else {
history.push('/')
}
}} />
})
}
</div>
{props.children}
</div>
</>
};
/**
* 递归获取路由对象中的路径及名称
* @param route 路由对象
* @param routeList 路由集合
*/
function recurGetRoutes(route: any, routeList: any[]) {
route?.children?.forEach((item: any) => {
routeList.push({
path: item.path,
name: item.name
})
recurGetRoutes(item, routeList)
})
}
function TitleBar(props: {
// 路由信息
routeInfo: RouteInfo,
// 点击事件
onClick: (path: string) => void,
// 关闭事件
onClose: (path: string) => void,
// 右键菜单事件
onContextMenu: (path: string) => void,
// 是否选中
activePath: string,
}) {
return <>
<>
<div className={style['background-color-white'] + (props.activePath === props.routeInfo.path ? ' ' + style['background-color-theme'] : '')} style={{
borderTopLeftRadius: '10px',
borderTopRightRadius: '10px',
width: 150,
height: 40,
position: 'relative',
}}>
<div
onClick={() => {
props.onClick(props.routeInfo.path)
}}
onContextMenu={(event) => {
event.preventDefault()
props.onContextMenu(props.routeInfo.path)
}}
style={{
textAlign: 'center',
verticalAlign: 'middle',
lineHeight: '42px'
}}>
<span className={style['cursor-pointer']}
style={{ color: 'gray' }}
>{props.routeInfo.name}</span>
</div>
<svg className={style.magnify} onClick={() => {
props.onClose(props.routeInfo.path)
}} style={{
position: 'absolute',
right: '8px',
top: '5px',
color: 'gray'
}}
viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
</div>
</>
</>
}
2. index.less
:hover.magnify {
transform: scale(1.2);
cursor: pointer;
}
:hover.cursor-pointer {
cursor: pointer;
}
:hover.cursor-pointer {
cursor: pointer;
}
.background-color-white {
background-color: white;
}
.background-color-theme {
background-color:
}