本文实现umi/umiMax 项目菜单支持动态tab展示,直接看目标效果:
umi其实已经有现成的方案,参考, 但是我这个方案相对接入快,代码少(有BUG你自己也方便修,hh~),直接上代码:
新建一个组件:src/layouts/tab-layout.tsx,内容如下:
import { Tabs } from 'antd';
import type { ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { useAppData, useLocation, useNavigate, useOutlet } from 'umi';
interface tabLayoutProps {
/** 用于自定义keepAlive组件等场景 */
renderElement?: (ele: ReactNode) => ReactNode;
/** 动态路由需要传入,路由名称映射: {'/welcome': '欢迎页面', ...} */
routeNameMap?: Record<string, string>;
}
interface typeElement {
key: string;
child: ReactNode;
label: string;
location: Location;
}
const TabLayout = (props: tabLayoutProps) => {
const location = useLocation();
const pathname = location.pathname?.toLowerCase();
const element = useOutlet();
const navigate = useNavigate();
const [keepElements, setKeepElements] = useState<typeElement[]>([]);
const { routes } = useAppData();
const getDefaultRoute = () => {
const resMap = {};
Object.keys(routes || {}).forEach((key) => {
const routeObj = routes[key];
// @ts-ignore
resMap[`${routeObj.path?.toLowerCase()}`] = routeObj.name;
});
return resMap;
};
const tabNameMap: Record<string, string> = {
...getDefaultRoute(),
...(props.routeNameMap || {}),
};
const hasTab = (pathnameVal: string) => {
return keepElements.some((item) => item.key === pathnameVal);
};
const closeTab = (key: string) => {
const index = keepElements.findIndex((item) => item.key === key);
if (keepElements.length > 1) {
const newList: typeElement[] = [...keepElements];
newList.splice(index, 1);
setKeepElements(newList);
const target = newList[newList.length - 1];
const { pathname: targetPath, hash, search } = target?.location;
navigate(`${targetPath}${search}${hash}`);
}
};
const RenderDom = props.renderElement || (() => element);
const targetDom = keepElements.find((obj) => obj.key === pathname);
useEffect(() => {
const newKpp = keepElements.map((obj) => {
if (obj.label) {
return obj;
}
const curRouteLowerPath = obj.location.pathname.toLowerCase();
return {
...obj,
label: tabNameMap[curRouteLowerPath],
};
});
setKeepElements([...newKpp]);
}, [props.routeNameMap]);
useEffect(() => {
if (!hasTab(pathname)) {
const newList: typeElement[] = [
// @ts-ignore
...keepElements,
{
key: pathname,
label: tabNameMap[pathname],
child: RenderDom(element),
// @ts-ignore
location,
},
];
setKeepElements(newList);
}
}, [location]);
return (
<>
<Tabs
hideAdd
onChange={(key: string) => {
const target = keepElements.find((obj) => obj.key === key);
const {
pathname: targetPath,
hash,
search,
} = target?.location as Location;
navigate(`${targetPath}${search}${hash}`);
}}
size='small'
items={keepElements}
activeKey={`${location.pathname?.toLowerCase()}`}
type='editable-card'
// @ts-ignore
onEdit={(key: string) => {
closeTab(key?.toLowerCase());
}}
/>
{targetDom?.child || element}
</>
);
};
export default TabLayout;
使用: umi项目中在src下新建layouts/index.tsx,导出即可!是不是很快
import React from 'react';
import TabLayout from './tab-layout'; // 上面组件的路径,灵活调整
const LayoutsCmp = (props: typeProps) => {
return (
<div>
<TabLayout></TabLayout>
</div>
);
}
export default LayoutsCmp;
如果对你带了实质性的帮助,或者解决了你现在的需求痛点,不妨私信请我喝杯☕咖啡,哈哈哈哈哈哈,嗝~