umi Ant Design Pro keepAlive多tabs配置

前言

最近在做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);
  })
}

参考文档

juejin.cn/post/710949...

相关推荐
文火冰糖的硅基工坊17 分钟前
[嵌入式系统-146]:五次工业革命对应的机器人形态的演进、主要功能的演进以及操作系统的演进
前端·网络·人工智能·嵌入式硬件·机器人
2401_8370885030 分钟前
ResponseEntity - Spring框架的“标准回复模板“
java·前端·spring
yaoganjili38 分钟前
用 Tinymce 打造智能写作
前端
angelQ1 小时前
Vue 3 中 ref 获取 scrollHeight 属性为 undefined 问题定位
前端·javascript
Dontla1 小时前
(临时解决)Chrome调试避免跳入第三方源码(设置Blackbox Scripts、将目录添加到忽略列表、向忽略列表添加脚本)
前端·chrome
我的div丢了肿么办1 小时前
js函数声明和函数表达式的理解
前端·javascript·vue.js
云中雾丽1 小时前
React.forwardRef 实战代码示例
前端
朝歌青年说1 小时前
一个在多年的技术债项目中写出来的miniHMR热更新工具
前端
Moonbit1 小时前
倒计时 2 天|Meetup 议题已公开,Copilot 月卡等你来拿!
前端·后端
Glink1 小时前
现在开始将Github作为数据库
前端·算法·github