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、index.less文件
// BaseLayout.tsximport { 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.tsximport { 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)}><Dropdowntrigger={['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 (<TabshideAddsize='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.效果
相关文章:

react umi/max 页签(react-activation)
思路:通过react-activation实现页面缓存,通过umi-plugin-keep-alive将react-activation注入umi框架,封装页签组件最后通过路由的wrappers属性引入页面。 浏览本博客之前先看一下我的博客实现的功能是否满足需求,实现功能…...

计算机网络编程
一、计算机网络(概述、简介) 说起网络,相信大家都不陌生,把分散在不同地点的计算机设备,通过传输介质、通信设施和网络通信协议,实现资源共享和信息传输的系统,我们称之为:计算机网…...

【计算机网络实训】期末考题-路由重分发+三层交换机VLAN间路由
路由重分发三层交换机VLAN间路由 实验目的实验内容及步骤仿真配置环境搭建要求:实验步骤配置Switch0配置Switch1配置交换机Multilayer Switch 0路由器Router0上的配置路由器Router1的配置 测试PC0 自动获取地址成功,PC0 可 ping 通 switch0,网…...

git 常规操作及设置
git 常规操作及设置 Git是一个分布式版本控制系统,可以用来跟踪文件的修改历史并与其他人进行协作开发。下面是一些常见的Git操作及设置: 初始化仓库:使用命令git init在当前目录创建一个新的Git仓库。 克隆仓库:使用命令git clo…...

element中表格组件的row-class-name和class-name属性的使用以及无效处理
1.这两个属性的使用,row-class-name用在el-table标签上,class-name用在el-table-column标签上。两个属性即可绑定类名也可绑定函数 <!-- 这里是绑定函数,也可以绑定类名 --> <el-table :data"tableData" selection-chang…...
【AI理论知识】EM算法
基本定义 期望最大化算法(Expectation-Maximization,EM算法)是一种用于估计包含潜在变量的概率模型参数的迭代优化算法。EM算法的主要目标是在存在未观测数据或缺失数据的情况下,通过迭代地进行期望步骤(E步ÿ…...
03 OSPF
参考文章 1 初步认识OSPF的大致内容(第三课)-CSDN博客 2...

node.js(express.js)+mysql实现注册功能
文章目录 实现步骤一、获取客户端提交到服务器的用户信息,对表单中的数据,进行合法性的效验 代码如下:二、检测用户名是否被占用三、对密码进行加密四、插入新用户(完整代码)总结 实现步骤 一、获取客户端提交到服务器的用户信息…...

AI绘画Stable Diffusion进阶使用
本文讲解,模型底模,VAE美化模型,Lora模型,hypernetwork。 文本Stable Diffusion 简称sd 欢迎关注 使用模型 C站:https://civitai.com/ huggingface:https://huggingface.co/models?pipeline_tagtext-to-…...
C 练习实例33 - 质数(素数)判断
题目:判断一个数字是否为质数。 程序分析:质数(prime number)又称素数,有无限个。一个大于1的自然数,除了1和它本身外,不能被其他自然数整除。 这题做过很多遍了,懂得都懂。 代码…...

docker环境下mongo副本集的部署及异常修复
最近更换了办公地点。部署在本地docker环境里的mongo数据库不能使用了。原因是本地的ip地址变更。以前的mongo副本集的配置需要更新。处理完后,索性重新记录一下mongo副本集在docker中的部署流程。 mongo的事务及副本集 我们先了解一下什么是事务,事务…...

【Java】Maven的安装与配置
初识Maven Maven是专门用于管理和构建Java项目的工具,它的主要功能有: 提供了一套标准化的项目结构 提供了一套标准化的构建流程(编译,测试,打包,发布……) 提供了一套依赖管理机制 标准化的…...

向量和向量如何相乘?
向量与向量相乘主要有两种方式:点积(内积)和叉积(外积)。这两种运算的结果和应用是不同的。 点积(内积): 点积是两个向量的对应元素相乘后再求和的结果,通常用于计算两个…...

计算机组成原理 指令流水线
文章目录 指令流水线指令流水线的概念流水线性能分析流水线的吞吐率流水线的加速比流水线的效率 影响流水线的因素结构相关 (资源冲突)数据相关 (数据冲突)控制相关 (控制冲突) 流水线分类超量流水线 指令流水线 #mermaid-svg-sWaRASMFAvh8sLJk {font-family:"trebuchet m…...
macOS - md5 | md5sum
文章目录 简单使用介绍文档Linux - md5summacOS - md5 大文件传输是否完整,你可以使用 md5 进行校验 linux 上使用 md5sum 命令,在macOS 上 md5 命令是和 md5sum 等效的 简单使用介绍 参考:https://blog.csdn.net/cnds123321/article/detail…...

Tomcat快速入门
1.Tomcat介绍 Apache Tomcat 是由 Apache Software Foundation(ASF)开发的一个开源 Java WEB 应用服务器,如apache处理静态HTML能力突出不同,tomcat处理动态HTML能力相当强大,因此一般项目都是部署apachetomcat&#…...
如何结合antd design pro 5 结合express 上传多个文件
在Ant Design Pro 5(基于React)的前端界面结合Express后端实现上传整个文件夹的文件,实际上是在前端进行多文件选择,并通过POST请求将文件列表发送到后端,然后由后端处理上传。由于浏览器API限制,直接上传整…...

Django随笔
关于Django的admin 1. 在url中把 from django.contrib import admin 重新解开 把path(admin/,admin.site.urls), 解开 2. 注册app,在配置文件中写 django.contrib.admin, 3.输入命令进行数据库迁移 Django国际化 配置文件中(改成中文) LA…...

线程和进程的区别(从JVM角度出发)
进程与线程的区别 线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进…...

手把手教你如何快速定位bug,如何编写测试用例,快来观摩......
手把手教你如何快速定位bug,如何编写测试用例,快来观摩......手把手教你如何快速定位bug,如何编写测试用例,快来观摩......作为一名测试人员如果连常见的系统问题都不知道如何分析,频繁将前端人员问题指派给后端人员,后端人员问题指派给前端人员…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...