react umi/max 页签(react-activation)

news2025/2/26 3:36:26

思路:通过react-activation实现页面缓存,通过umi-plugin-keep-alive将react-activation注入umi框架,封装页签组件最后通过路由的wrappers属性引入页面。

浏览本博客之前先看一下我的博客实现的功能是否满足需求,实现功能:

- 页面缓存
- 关闭当前页
- 阻止事件传播
- 鼠标右键>关闭当前
- 鼠标右键>关闭其他
- 鼠标右键>关闭左侧
- 鼠标右键>关闭右侧
- 鼠标右键>全部关闭(默认跳转到首页)
- 鼠标右键>重新加载(刷新缓存页面)

1.下载依赖

pnpm install react-activation@0.12.4

pnpm install umi-plugin-keep-alive@0.0.1-beta.35

2.修改.umirc.ts文件配置

import { defineConfig } from '@umijs/max';

export default defineConfig({
  plugins: ['umi-plugin-keep-alive'],
  ...
});

3.封装组件 

src目录下创建layouts文件夹,创建BaseLayout.tsx文件和BaseTabs.tsx、index.less文件

// BaseLayout.tsx

import { KeepAlive, Outlet, useRouteProps } from '@umijs/max';
import React from 'react';
import BaseTabs from './BaseTabs';

export default (): React.ReactElement => {
  const { originPath, name } = useRouteProps();

  return (
    <>
      <BaseTabs />
      <KeepAlive id={originPath} name={originPath} tabName={name}>
        <Outlet />
      </KeepAlive>
    </>
  );
};

// BaseTabs/index.tsx

import { history, useAliveController, useLocation } from '@umijs/max';
import { Dropdown, Tabs } from 'antd';
import React, { useState } from 'react';
import './index.less';

export default (): React.ReactElement => {
  const { pathname } = useLocation();

  // 获取缓存列表
  const { getCachingNodes, dropScope, clear, refreshScope } =
    useAliveController();
  const cachingNodes = getCachingNodes();
  const [open, setOpen] = useState<{ path: string; open: boolean }>({
    path: '',
    open: false,
  });

  // 阻止右键事件冒泡
  const onRightClick = (
    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
    name: string,
  ) => open.open && open.path === name && e.stopPropagation();

  // 点击tab,跳转页面
  const clickTab = (path: string) => {
    history.push(path);
  };

  // 关闭tab,销毁缓存
  const editTab = (path: any) => {
    dropScope(path);
    // 关闭当前页面,需跳转到其他页签
    if (path === pathname) {
      const index = cachingNodes.findIndex((item) => item.name === path);
      if (index > 0) {
        history.push(cachingNodes[index - 1].name as string);
      } else {
        history.push(cachingNodes[1].name as string);
      }
    }
  };

  // 关闭当前页
  const onCurrent = (e: any) => {
    let targetKey = JSON.parse(e?.key).name;
    dropScope(targetKey);
    // 关闭当前页面,需跳转到其他页签
    if (targetKey === pathname) {
      const index = cachingNodes.findIndex((item) => item.name === targetKey);
      if (index > 0) {
        history.push(cachingNodes[index - 1].name as string);
      } else {
        history.push(cachingNodes[1].name as string);
      }
    }
  };

  // 关闭其他
  const onOther = (e: any) => {
    let targetKey = JSON.parse(e?.key).name;
    history.push(targetKey);
    clear();
  };

  //关闭左侧
  const onLeft = (e: any) => {
    let targetKey = JSON.parse(e?.key).name;
    const lastIndex = cachingNodes.findIndex((item) => item.name === pathname);
    const currIndex = cachingNodes.findIndex((item) => item.name === targetKey);
    if (currIndex > lastIndex) history.push(targetKey);
    cachingNodes.forEach((item, index) => {
      if (index < currIndex) {
        dropScope(item?.name || '');
      }
    });
  };

  // 关闭右侧
  const onRight = (e: any) => {
    let targetKey = JSON.parse(e?.key).name;
    const lastIndex = cachingNodes.findIndex((item) => item.name === pathname);
    const currIndex = cachingNodes.findIndex((item) => item.name === targetKey);
    if (currIndex < lastIndex) history.push(targetKey);
    cachingNodes.forEach((item, index) => {
      if (index > currIndex) {
        dropScope(item?.name || '');
      }
    });
  };

  // 关闭全部
  const onAll = () => {
    history.push('/home');
    clear();
  };

  // 重新加载
  const onRefresh = (e: any) => {
    let targetKey = JSON.parse(e?.key).name;
    refreshScope(targetKey);
  };

  const labelDropdown = (name: string, label: string) => {
    const lastIndex = cachingNodes.findIndex((item) => item.name === name);
    return (
      <div onClick={(e) => onRightClick(e, name)}>
        <Dropdown
          trigger={['contextMenu']}
          onOpenChange={(e) => setOpen({ path: name, open: e })}
          menu={{
            items: [
              {
                label: '关闭当前',
                key: JSON.stringify({ name, key: 'current' }),
                disabled: cachingNodes.length <= 1,
                onClick: onCurrent,
              },
              {
                label: '关闭其他',
                key: JSON.stringify({ name, key: 'other' }),
                disabled: cachingNodes.length <= 1,
                onClick: onOther,
              },
              {
                label: '关闭左侧',
                key: JSON.stringify({ name, key: 'left' }),
                disabled: lastIndex === 0,
                onClick: onLeft,
              },
              {
                label: '关闭右侧',
                key: JSON.stringify({ name, key: 'right' }),
                disabled: lastIndex === cachingNodes.length - 1,
                onClick: onRight,
              },
              {
                label: '全部关闭',
                key: JSON.stringify({ name, key: 'all' }),
                onClick: onAll,
                disabled: cachingNodes.length <= 1,
              },
              {
                label: '重新加载',
                key: JSON.stringify({ name, key: 'refresh' }),
                onClick: onRefresh,
              },
            ],
          }}
        >
          <div className={cachingNodes.length > 1 ? 'dropdown-label' : ''}>
            {label}
          </div>
        </Dropdown>
      </div>
    );
  };

  const tabItems = cachingNodes.map((item: any) => ({
    label: labelDropdown(item.name, item.tabName),
    key: item.name,
    closable: cachingNodes.length > 1,
  }));

  return (
    <Tabs
      hideAdd
      size='middle'
      type="editable-card"
      className="base-tabs"
      activeKey={pathname}
      onTabClick={clickTab}
      onEdit={editTab}
      items={tabItems}
    />
  );
};
// index.less

.base-tabs {
  .ant-dropdown-trigger {
    padding: 5px 10px;
    height: 100%;
  }
  .dropdown-label {
    padding: 5px 6px 5px 10px;
    height: 100%;
  }
  .ant-tabs-tab {
    padding: 0 !important;
  }
  .ant-tabs-tab-remove {
    margin-left: 0 !important;
    margin-right: 2px !important;
    padding-left: 0px !important;
  }
}

 4.修改路由

  routes: [
    {
      name: '首页',
      path: '/home',
      component: './Home',
    },
    {
      name: '示例',
      path: '/example',
      routes: [
        {
          name: '权限演示',
          path: '/example/access',
          component: './Access',
          wrappers: ['@/layouts/BaseLayout'],
        },
        {
          name: ' CRUD 示例',
          path: '/example/table',
          component: './Table',
          wrappers: ['@/layouts/BaseLayout'],
        },
      ],
    },
  ],

5.效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1402311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

日常常见应用组件升级记录

一、前言 因近期安全扫描&#xff0c;发现java后端应用涉及多个引用组件版本过低&#xff0c;涉及潜在漏洞利用风险&#xff0c;特记录相关处理升级处理过程&#xff0c;以备后续确认&#xff1b; 二、升级处理过程 2.1、Java类应用内置Spring Boot版本升级 Spring Boot是一…

【数据结构与算法】之字符串系列-20240121

这里写目录标题 一、344. 反转字符串二、125. 验证回文串三、205. 同构字符串四、242. 有效的字母异位词五、290. 单词规律 一、344. 反转字符串 简单 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额…

java SSM政府采购管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM政府采购管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

【UE5】第一次尝试项目转插件(Plugin)的时候,无法编译

VS显示100条左右的错误&#xff0c;UE热编译也不能通过。原因可能是[名字.Build.cs]文件的错误&#xff0c;缺少一些内容&#xff0c;比如说如果要写UserWidget类&#xff0c;那么就要在 ]名字.Build.cs] 中加入如下内容&#xff1a; public class beibaoxitong : ModuleRules …

UML序列图入门

使用工具&#xff1a;processOn可免费建9个文件 UML&#xff1a;Unified Modeling Language 统一建模语言 ProcessOn - 我的文件 一、介绍 序列图是基于对象而非基于类&#xff1b; 从上到下&#xff1a;展示了处理过程中&#xff0c;每个对象的生命周期&#xff1b; 从左到…

从白子画到东方青苍,你选择谁来守护你的修仙之旅?

从白子画到东方青苍,你选择谁来守护你的修仙之旅? 在繁花似锦的修仙世界中&#xff0c;每一位追梦者都渴望有那么一位守护者&#xff0c;与你共患难&#xff0c;共成长。热血与浪漫交织的《花千骨》与《苍兰诀》&#xff0c;给我们带来了两位风华绝代的守护者&#xff1a;白子…

ST-GCN 人体姿态估计模型 代码实战

构建一个ST-GCN&#xff08;Spatio-Temporal Graph Convolutional Network&#xff09;模型需要结合图卷积网络&#xff08;GCN&#xff09;的思想&#xff0c;以处理时空数据。以下是一个简单的例子&#xff0c;演示如何使用PyTorch构建ST-GCN模型&#xff1a; import torch …

偷偷浏览小网站时,原来有这么多人已经知道

最近看到一篇挺有意思文章&#xff0c;偷偷浏览小网站时&#xff0c;都有谁会知道你看了啥。思量之下&#xff0c;从更广泛的技术角度看&#xff0c;仍有大量补充的空间&#xff0c;于是就有了这样一篇文章。本文的目的在于增强大家的网络安全意识&#xff0c;非必要不要浏览不…

护眼灯品牌哪个好?护眼灯最好的品牌排行

很多家长在帮助孩子选购台灯时可能会遇到一些困扰&#xff0c;而市面上存在许多质量不合格的台灯。曾经有央视节目曝光了许多LED台灯存在严重不合格的情况。尽管目前对于台灯执行强制性产品认证&#xff0c;并不定期进行抽样检查&#xff0c;但仍然存在一些未强制认证的产品在市…

EF Core实操,数据库生成实体,迁移

EF Core实操,数据库生成实体,迁移 大家好,我是行不更名,坐不改姓的宋晓刚,下面将带领大家进入C#编程EF Core数据库基础入门知识,如何连接数据库,如何编写代码,跟上我的步伐进入EF Core数据库下的世界。 家人们,如果有什么不懂,可以留言,或者加我联系方式,一起进入…

python系列-顺序/条件/循环语句

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 顺序语句 条件语句 什么是条件语句 语法格式 缩进和代码块 空语句pass 循环语句 while循环 for循环 continue break 顺序语句 默认情况下&#xff0c;Python的代码执行…

Elasticsearch+Kibana 学习记录

文章目录 安装Elasticsearch 安装Kibana 安装 Rest风格API操作索引基本概念示例创建索引查看索引删除索引映射配置&#xff08;不配置好像也行、智能判断&#xff09;新增数据随机生成ID自定义ID 修改数据删除数据 查询基本查询查询所有&#xff08;match_all&#xff09;匹配查…

为什么 HTTPS 协议能保障数据传输的安全性?

HTTP 协议 在谈论 HTTPS 协议之前&#xff0c;先来回顾一下 HTTP 协议的概念。 HTTP 协议介绍 HTTP 协议是一种基于文本的传输协议&#xff0c;它位于 OSI 网络模型中的应用层。 HTTP 协议是通过客户端和服务器的请求应答来进行通讯&#xff0c;目前协议由之前的 RFC 2616 拆…

C++ | 五、哈希表 Hash Table(数组、集合、映射)、迭代器

哈希表基础 哈希表是一类数据结构&#xff08;哈希表包含数组、集合和映射&#xff0c;和前两篇文章叙述的字符串、链表平级&#xff09;哈希表概念&#xff1a;类似于Python里的字典类型&#xff0c;哈希表把关键码key值通过哈希函数来和哈希表上的索引对应起来&#xff0c;之…

BGP AS-Path 选路试验

一、拓朴图&#xff1a; 实验要求&#xff1a;R5 通过改变对端传入的 AS-Path 路由策略&#xff0c;使原来较长的 AS-Path 成为最优 二、步骤&#xff1a; 1、配置IP 2、R1、R2、R3 配置 IGP 3、R1、R2、R3 配置 BGP&#xff0c;将 R1 创建的 Loop 100.1.1.1 的环回口在 IBGP …

uni-app小程序:文件下载打开文件方法苹果安卓都适用

api: const filetype e.substr(e.lastIndexOf(.)1)//获取文件地址的类型 console.log(文档,filetype) uni.downloadFile({url: e,//e是图片地址success(res) {console.log(res)if (res.statusCode 200) {console.log(下载成功,);var filePath encodeURI(res.tempFilePath);…

【第三课课后作业】基于 InternLM 和 LangChain 搭建你的知识库

基于 InternLM 和 LangChain 搭建你的知识库 1. 基础作业&#xff1a; 环境配置 1.1 InternLM 模型部署 创建开发机 进入 conda 环境之后&#xff0c;使用以下命令从本地一个已有的 pytorch 2.0.1 的环境&#xff0c;激活环境&#xff0c;在环境中安装运行 demo 所需要的依…

【赠书第17期】Excel高效办公:文秘与行政办公(AI版)

文章目录 前言 1 了解Excel的强大功能和工具 2 提升Excel技能的方法 3 结合AI技术提升Excel应用 4 注意事项 5 推荐图书 6 粉丝福利 前言 随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;我们的工作方式也在发生深刻变革。其中&#xff0c;Excel 作…

Ubuntu上安装部署Qt

首先需要下载对应的虚拟机软件和ubuntu镜像&#xff0c;虚拟机软件使用VMware或者Virtual Box都行&#xff0c;我用的是前者&#xff0c;这里是VMware的下载链接&#xff1a;下载 VMware Workstation Pro | CN。Ubuntu镜像推荐去清华的网站下载&#xff1a;Index of /ubuntu-re…

如何查找设备的公共IP地址?这里提供三种方法

每个以某种方式连接到互联网的设备都有一个IP地址。这是你的设备使用的唯一标识符,可以被互联网或本地网络上的其他设备识别。 如果你使用的是直接连接到互联网的智能手机或平板电脑等设备,你的公共IP地址将由你的移动运营商直接分配。 另一方面,如果你使用的是连接到本地…