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...

相关推荐
过期的H2O26 分钟前
【H2O2|全栈】关于CSS(4)CSS基础(四)
前端·css
纳尼亚awsl20 分钟前
无限滚动组件封装(vue+vant)
前端·javascript·vue.js
八了个戒25 分钟前
【TypeScript入坑】TypeScript 的复杂类型「Interface 接口、class类、Enum枚举、Generics泛型、类型断言」
开发语言·前端·javascript·面试·typescript
西瓜本瓜@27 分钟前
React + React Image支持图像的各种转换,如圆形、模糊等效果吗?
前端·react.js·前端框架
黄毛火烧雪下28 分钟前
React 的 useEffect 钩子,执行一些异步操作来加载基本信息
前端·chrome·react.js
蓝莓味柯基33 分钟前
React——点击事件函数调用问题
前端·javascript·react.js
资深前端之路34 分钟前
react jsx
前端·react.js·前端框架
cc蒲公英1 小时前
vue2中使用vue-office库预览pdf /docx/excel文件
前端·vue.js
Sam90291 小时前
【Webpack--013】SourceMap源码映射设置
前端·webpack·node.js
小兔崽子去哪了1 小时前
Element plus 图片手动上传与回显
前端·javascript·vue.js