前言
最近在做react项目中,有个功能点需要实现多tab页,查阅资料发现 Ant Design Pro 已经有这个feature了,只是没有相关文档,查阅了些资料,将一些用法和注意点记录了下来
该组件目前支持
- 刷新
- 关闭左侧、右侧、全部标签
下面是实现的效果图
config 配置
config/config.ts 或者 umirc.ts 文件
- keepalive 开启需要缓存的路径
- hasCustomTabs 为 true 时,可在app.tsx 中自定义keepalive组件
javascript
export default defineConfig({
keepalive: [/./],
tabsLayout: {
hasCustomTabs: true,
},
})
app.tsx 配置
javascript
export const getCustomTabs = () => (data: TabsViewPropsData) => {
return <TabsView data={data} />
}
自定义TabViews组件
组件代码
ini
import { Dropdown, Menu, message, Tabs } from 'antd';
import type { MenuInfo } from 'rc-menu/lib/interface';
import type { FC } from 'react';
import routes from '../../../config/routes';
import './index.less';
import { getLanguage } from '@/utils/utils';
import RightContent from '../RightContent';
const { TabPane } = Tabs;
export interface TabsViewPropsData {
activeKey: string;
dropByCacheKey: (path: string) => void;
isKeep: boolean;
keepElements: {
current: object;
};
local: object;
navigate: (to: string, options?: any) => void;
}
export interface TabsViewProps {
data: TabsViewPropsData;
}
const TabsView: FC<TabsViewProps> = ({ data }) => {
const onEdit = (activeKey: any) => {
const buffer = Object.keys(data.keepElements.current);
if (buffer.length <= 1) {
message.destroy();
message.warning('至少留一个标签页');
return;
}
if (activeKey === data.activeKey) {
const index = buffer.findIndex((it) => it === activeKey);
if (index === 0) {
data.navigate(buffer[1]);
data.dropByCacheKey(activeKey);
return;
}
data.navigate(buffer[index - 1]);
data.dropByCacheKey(activeKey);
} else {
data.dropByCacheKey(activeKey);
}
};
const getRoute = (routesArr: any[], path: any) => {
const buffer: any[] = [];
const eachItem = (list: { routes: any }[], path2: any) => {
list.forEach((it: { routes: any }) => {
if (it.routes) {
eachItem(it.routes, path2);
} else {
buffer.push(it);
}
});
};
eachItem(routesArr, path);
const item = buffer.find((it) => it.path === path);
return { ...item, name: getLanguage(item?.title) };
};
const getOperation = (arr: any[], path: any) => {
let buffer: any = [];
const index = arr.findIndex((it: any) => it === path);
if (index === 0 && arr.length === 1) {
buffer = [
{
key: 1,
label: <a>刷新</a>,
},
];
} else if (index === 0 && arr.length > 1) {
buffer = [
{
key: 1,
label: <a>刷新</a>,
},
{
key: 2,
label: <a>关闭右边侧标签</a>,
},
{
key: 4,
label: <a>关闭全部标签</a>,
},
];
} else if (index === arr.length - 1) {
buffer = [
{
key: 1,
label: <a>刷新</a>,
},
{
key: 3,
label: <a>关闭左边侧标签</a>,
},
{
key: 4,
label: <a>关闭全部标签</a>,
},
];
} else {
buffer = [
{
key: 1,
label: <a>刷新</a>,
},
{
key: 2,
label: <a>关闭右侧标签</a>,
},
{
key: 3,
label: <a>关闭左边侧标签</a>,
},
{
key: 4,
label: <a>关闭全部标签</a>,
},
];
}
return buffer;
};
const operationAction = (event: MenuInfo, it: string) => {
const buffer = Object.keys(data.keepElements.current);
if (event.key === '1') {
data.navigate(it);
return;
}
if (event.key === '2') {
const currentIndex = buffer.findIndex((it2) => it2 === it);
const bufferArr = buffer.filter((_, index) => {
return index > currentIndex;
});
bufferArr.forEach((item: any) => {
data.dropByCacheKey(item);
});
}
if (event.key === '3') {
const currentIndex = buffer.findIndex((it2) => it2 === it);
const bufferArr = buffer.filter((_, index) => {
return index < currentIndex;
});
bufferArr.forEach((item: any) => {
data.dropByCacheKey(item);
});
}
if (event.key === '4') {
const bufferArr = buffer.filter((item) => {
return item != it;
});
bufferArr.forEach((item: any) => {
data.dropByCacheKey(item);
});
}
};
return (
<div className="card-container">
<Tabs
hideAdd
type="editable-card"
activeKey={data.activeKey}
onChange={(activeKey) => {
data.navigate(activeKey);
}}
onEdit={(activeKey) => onEdit(activeKey)}
>
{Object.keys(data.keepElements.current).map((it: any) => {
return (
it && (
<TabPane
closable={Object.keys(data.keepElements.current).length > 1}
tab={
<div className={'layout-tabs-title'}>
<Dropdown
overlay={
<Menu
style={{ width: 150 }}
items={getOperation(Object.keys(data.keepElements.current), it)}
onClick={(event) => operationAction(event, it)}
/>
}
trigger={['contextMenu']}
>
<div>
<span style={{ marginLeft: '2px' }}>{getRoute(routes, it)?.name}</span>
</div>
</Dropdown>
</div>
}
key={it}
/>
)
);
})}
</Tabs>
<RightContent />
</div>
);
};
export default TabsView;
样式文件
less
@var1: #FFFFFF;
.card-container {
padding: 0 16px;
display: flex;
align-items: center;
justify-content: space-between;
background: @var1;
margin-bottom: 0;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
height: 64px;
overflow-y: hidden;
p {
margin: 0;
}
&>.ant-tabs-card .ant-tabs-content {
height: 120px;
margin-top: -16px;
&>.ant-tabs-tabpane {
padding: 16px;
}
}
&>.ant-tabs-card>.ant-tabs-nav::before {
display: none;
}
.ant-tabs {
height: 28px;
}
.ant-tabs-tab {
padding: 0 8px !important;
margin-right: 6px !important;
border-radius: 4px !important;
height: 28px;
margin: 0 -4px 0 8px;
&-active {
.ant-tabs-tab-remove {
color: @var1 !important;
}
}
}
.ant-tabs-content-holder {
display: none;
}
}
Tips
在其它地方操作keepalive的相关数据,例如退出时清除所有的keepalive
javascript
import { KeepAliveContext } from '@umijs/max';
const {
keepElements,
dropByCacheKey,
} = useContext(KeepAliveContext);
const clearKeepAlives = () => {
Object.keys(keepElements.current).map((item) => {
dropByCacheKey(item);
})
}