react umi/max 页签(react-activation)

思路:通过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文件

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

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

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

  // 获取缓存列表
  const { getCachingNodes, dropScope, clear, refreshScope } =
    useAliveController();
  const cachingNodes = getCachingNodes();

  /**
   * 点击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) => {
    e.domEvent.stopPropagation();

    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) => {
    e.domEvent.stopPropagation();
    let targetKey = JSON.parse(e?.key).name;
    history.push(targetKey);
    clear();
  };

  //关闭左侧
  const onLeft = (e: any) => {
    e.domEvent.stopPropagation();
    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) => {
    e.domEvent.stopPropagation();
    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 = (e: any) => {
    e.domEvent.stopPropagation();
    history.push('/home');
    clear();
  };

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

  const labelDropdown = (name: string, label: string) => {
    const lastIndex = cachingNodes.findIndex((item) => item.name === name);
    return (
      <Dropdown
        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,
            },
          ],
        }}
        trigger={['contextMenu']}
      >
        <span>{label}</span>
      </Dropdown>
    );
  };

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

  return (
    <Tabs
      type="editable-card"
      hideAdd
      size="small"
      activeKey={pathname}
      onTabClick={clickTab}
      onEdit={editTab}
      items={tabItems}
    />
  );
};

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++I/O流——(4)格式化输入/输出(第二节)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 含泪播种的人一定能含笑收获&#xff…

一文极速了解【自注意力机制】

当下如火如荼的大模型&#xff0c;其中的关键技术就是注意力机制&#xff08;Attention&#xff09;&#xff0c;于2015年提出。2017年的“Attention is all you need”一文提出了Transformer模型&#xff0c;去掉RNN&#xff0c;只保留注意力&#xff0c;性能吊打所有机器翻译…

用Airtest快速实现手机文件读写与删除功能

1. 前言 前几天有同学留言&#xff0c;能不能安排“读写手机文件”的示例。我们今天就来实现这个小功能。 当然&#xff0c;熟悉adb的同学&#xff0c;看到这个需求&#xff0c;肯定很开心&#xff0c;不就是一个 adb push 和 adb pull 嘛&#xff0c;非常简单呀。 确实如此&…

Python 利用pandas对数据进行特定排序

背景 小编最近在处理hive表存储大小时&#xff0c;需要对每个表的大小进行排序&#xff0c;因通过 hadoop fs -du -s -h /path/table 命令获取的数据表大小&#xff0c;其结果是展示为人能直观理解的大小&#xff0c;例如 1.1T、1.9G、49.6M 等&#xff0c;如果想对这些表根据…

如何安装配置VisualSVN服务并实现公网访问本地服务【内网穿透】

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统…

2018年认证杯SPSSPRO杯数学建模A题(第一阶段)海豚与沙丁鱼全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 探究海豚猎捕时沙丁鱼群的躲避运动模型 A题 海豚与沙丁鱼 原题再现&#xff1a; 沙丁鱼以聚成大群的方式来对抗海豚的捕食。由于水下光线很暗&#xff0c;所以在距离较远时&#xff0c;海豚只能使用回声定位方法来判断鱼群的整体位置&#xf…

网页版短信系统功能简介|短信平台开发搭建源码

网页版短信系统功能简介|短信平台开发搭建源码 随着互联网的发展&#xff0c;科技的进步和人们对通讯方式的需求不断增加&#xff0c;短信成为了人们日常生活中必不可少的沟通工具之一。而网页版短信系统的出现&#xff0c;为人们提供了更加便捷和灵活的短信发送和接收方式。 网…

Ant Design Vue上传多个图片

模板代码&#xff1a; 定义变量&#xff1a; 文件限制的函数&#xff1a; 上传的函数&#xff1a; 样式函数&#xff1a; 完整代码&#xff1a; <template><div class"dialog-upload" v-if"showUploadDialog"><div class"dialog-uplo…

MySQL 基于创建时间进行RANGE分区

MySQL是一款广泛使用的关系型数据库。在MySQL中&#xff0c;大量数据场景提高查询效率是非常关键的&#xff0c;所以&#xff0c;对数据表进行分区是一个很好的选择。 在创建分区表之前&#xff0c;需要了解一下MySQL分区的基本概念。MySQL分区可以将一个大表分成多个小表&…

学习JavaEE的日子 day14 继承,super(),this(),重写

Day14 1.继承的使用 理解&#xff1a;子类继承父类所有的属性和方法 使用场景&#xff1a;多个类似的类&#xff0c;有相同的属性和方法&#xff0c;就可以把相同属性和方法抽取到父类 优点&#xff1a;减少代码的冗余&#xff1b; 使类与类之间产生了关系(多态的前提) 缺点&a…

前端实现轮训和长连接

简介 轮训和长连接相关内容可以参考之前的文章消息推送系统。消息推送系统-CSDN博客文章浏览阅读106次。在餐饮行业中&#xff0c;店内应用有pos、厨显屏等&#xff0c;云端应用为对应数据中心。为了实现云端数据和操作指令下发到店内应用&#xff0c;需要有一个系统实现这个功…

群晖nas内网穿透

目录 一、前言 二、操作步骤 &#xff08;一&#xff09;查看群晖是否有ipv6地址 &#xff08;二&#xff09;下载安装docker &#xff08;三&#xff09;docker里面安装ddns-go &#xff08;四&#xff09;阿里云官网购买域名 &#xff08;五&#xff09;域名解析 阿里…

Yolov8_使用自定义数据集训练模型1

前面几篇文章介绍了如何搭建Yolov8环境、使用默认的模型训练和推理图片及视频的效果、并使用GPU版本的torch加速推理、导出.engine格式的模型进一步利用GPU加速&#xff0c;本篇介绍如何自定义数据集&#xff0c;这样就可以训练出识别特定物体的模型。 《Yolov8_使用自定义数据…

AuxTools - 浮鱼渗透辅助工具箱 V4.2

免责声明 由于传播、利用本文章所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章及作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担&#xff01;如有侵权烦请告知&#xff0c;我们会立即删除并致歉。谢谢&#xff…

【学习总结】动力学方程的龙格库塔积分法(含具体例子与代码)

本文仅用于个人学习总结&#xff0c;如有错误请批评指正。 参考资料 徐超江等&#xff0c;常微分方程基础教程&#xff0c;高等教育出版社&#xff0c;2023年。 1、欧拉法 1.1 前向欧拉 欧拉积分部分不用展开介绍&#xff0c;较为简单。直接拍照课本。 1.2 梯形法/隐式格式…

4D毫米波雷达——原理、对比、优势、行业现状

前言 4D 毫米波雷达是传统毫米波雷达的升级版&#xff0c;4D指的是速度、距离、水平角度、垂直高度四个维度。 相比传统 3D 毫米波雷达&#xff0c;4D 毫米波雷达增加了“高度”的探测&#xff0c;将第四个维度整合到传统毫米波雷达中。 4D毫米波雷达被视为未来车载雷达的一…

eBPF运行时安全

引言 eBPF作为当前linux系统上最为炙手可热的技术&#xff0c;通常被用于网络流量过滤和分析、系统调用跟踪、性能优化、安全监控&#xff0c;当下比较知名的项目有Cilium、Falco等。 Cilium 是一个开源的容器网络和安全性项目&#xff0c;致力于提供高效的容器通信和强大的安…

【备战蓝桥杯】探索Python内置标准库collections的使用

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-q0zvWxZtAIdSGZ8R {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

大模型学习之书生·浦语大模型5——基于LMDeploy大模型量化部署实践

目录 大模型部署背景 LMDeploy部署 量化 TurboMind API server 动手实践环节 1.创建开发机 2.创建虚拟环境 3.服务部署 在线转换模型 离线转换 4.TurboMind推理 TurboMindAPI服务 提供了一些API的接口 Gradio Demo演示 API server作为后端 注意这里要同时启动API serv…

7款值得收藏的前端动画特效(附效果图在线预览)

分享7款有趣也实用的前端动画特效 其中有CSS动画、canvas动画、js小游戏等等 下面我会给出特效样式图或演示效果图 但你也可以点击在线预览查看源码的最终展示效果及下载源码资源 canvas粒子空间特效 基于canvas实现的一款粒子空间特效 该特效初始时会从四周扩散粒子并随时间…