react 页签(自行封装)

思路:封装一个页签组件,包裹页面组件,页面渲染之后把数据缓存到全局状态实现页面缓存。

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

- 页面缓存
- 关闭当前页
- 鼠标右键>关闭当前
- 鼠标右键>关闭其他
- 鼠标右键>关闭左侧
- 鼠标右键>关闭右侧
- 鼠标右键>全部关闭(默认跳转到首页)

如果您需要实现刷新页签功能,建议react umi/max 页签(react-activation)-CSDN博客

1. models/tabs

// 全局共享数据示例
import { useState } from 'react';const useUser = () => {const [items, setItems] = useState<any[]>([]);  // 页签的全局Item数据const [key, setKey] = useState<string>('/home');  // 页签的高亮Keyreturn {items,setItems,key,setKey,};
};export default useUser;

2. components/PageHeadTabs

import { Home } from '@/pages/Home';
import { useLocation, useModel, useRouteProps } from '@umijs/max';
import { Dropdown, Tabs } from 'antd';
import { useEffect } from 'react';type PageHeadTabsProps = {children: any;
};const PageHeadTabs = (props: PageHeadTabsProps) => {const { name } = useRouteProps(); //获取当前路由名称const { children } = props; // Props获取元素、页面名称const { items, setItems, key, setKey } = useModel('tabs'); // 获取全局Item和Keyconst { pathname } = useLocation(); // 获取当前页的Pathname// 页签点击事件const onTabClick = (value: any) => {setKey(value); // 设置高亮的Keyhistory.replaceState(null, '', value); // 拼接URL路径、但不执行跳转};// 关闭页签const onEdit = (targetKey: any, action: 'add' | 'remove') => {if (action === 'remove') {let newActiveKey = key;const lastIndex = items.findIndex((item) => item.key === targetKey);const newPanes = items.filter((item) => item.key !== targetKey);if (newPanes.length && newActiveKey === targetKey) {if (lastIndex - 1 >= 0) {newActiveKey = newPanes[lastIndex - 1].key;} else {newActiveKey = newPanes[0].key;}}setItems(newPanes);setKey(newActiveKey);history.replaceState(null, '', newActiveKey);}};// 关闭当前页const onCurrent = (e: any) => {e.domEvent.stopPropagation();let targetKey = JSON.parse(e?.key).name;let newActiveKey = key;const lastIndex = items.findIndex((item) => item.key === targetKey);const newPanes = items.filter((item) => item.key !== targetKey);if (newPanes.length && newActiveKey === targetKey) {if (lastIndex - 1 >= 0) {newActiveKey = newPanes[lastIndex - 1].key;} else {newActiveKey = newPanes[0].key;}}setItems(newPanes);setKey(newActiveKey);history.replaceState(null, '', newActiveKey);};// 关闭其他const onOther = (e: any) => {e.domEvent.stopPropagation();let targetKey = JSON.parse(e?.key).name;const newPanes = items.filter((item) => item.key === targetKey || item.key === '/home',);setItems(newPanes);setKey(targetKey);history.replaceState(null, '', targetKey);};//关闭左侧const onLeft = (e: any) => {e.domEvent.stopPropagation();let targetKey = JSON.parse(e?.key).name;const lastIndex = items.findIndex((item) => item.key === targetKey);const newPanes = items.splice(0, lastIndex + 1).filter((item) => item.key === targetKey || item.key === '/home');const oldIndex = newPanes.findIndex((item) => item.key === key);setItems(newPanes);if (oldIndex) {setKey(targetKey);history.replaceState(null, '', targetKey);}};// 关闭右侧const onRight = (e: any) => {e.domEvent.stopPropagation();let targetKey = JSON.parse(e?.key).name;const lastIndex = items.findIndex((item) => item.key === targetKey);const newPanes = items.splice(0, lastIndex + 1);const oldIndex = newPanes.findIndex((item) => item.key === key);setItems(newPanes);if (oldIndex) {setKey(targetKey);history.replaceState(null, '', targetKey);}};// 关闭全部const onAll = (e: any) => {e.domEvent.stopPropagation();const newPanes = items.splice(0, 1);setItems(newPanes);setKey('/home');history.replaceState(null, '', '/home');};const labelDropdown = (name: string, label: string) => {const lastIndex = items.findIndex((item) => item.key === name);return (<Dropdownmenu={{items: [{label: '关闭当前',key: JSON.stringify({ name, key: 'current' }),disabled: name === '/home',onClick: onCurrent,},{label: '关闭其他',key: JSON.stringify({ name, key: 'other' }),disabled:(name === '/home' && items.length <= 1) ||(name !== '/home' && items.length <= 2),onClick: onOther,},{label: '关闭左侧',key: JSON.stringify({ name, key: 'left' }),disabled: lastIndex < 2,onClick: onLeft,},{label: '关闭右侧',key: JSON.stringify({ name, key: 'right' }),disabled:(name === '/home' && items.length <= 1) ||(name !== '/home' && items.length - lastIndex < 2),onClick: onRight,},{label: '全部关闭',key: JSON.stringify({ name, key: 'all' }),onClick: onAll,disabled: name === '/home' && items.length <= 1,},],}}trigger={['contextMenu']}><span>{label}</span></Dropdown>);};useEffect(() => {const index = !items.find(({ key }) => key === pathname);const indexHome = !items.find(({ key }) => key === '/home');// 如果用户部署从主页进入,引入主页组件作为默认页签if (indexHome && pathname !== '/home') {const arr = {key: '/home',label: '首页',title: '首页',closable: false,children: <Home />,};setItems((item) => item?.concat([arr]));}// 添加当前页面到页签if (index) {const arr = {key: pathname,label: name,title: name,closable: pathname !== '/home',children: children,};setItems((item) => item?.concat([arr]));}setKey(pathname);}, []);useEffect(() => {// 页签长度发生变化时,塞入、更新所有标签右键下拉菜单setItems((items) =>items.map((item) => {return { ...item, label: labelDropdown(item.key, item.title) };}),);}, [items.length]);return (<TabshideAddsize="small"type="editable-card"activeKey={key}onEdit={onEdit}onTabClick={onTabClick}items={items}/>);
};
export default PageHeadTabs;

3. pages/Home

import PageHeadTabs from '@/components/PageHeadTabs';
import React from 'react';// *因为首页是默认页面所以有两种进入方式
// *第一种是通过/home进入,正常加载HomePage;
// *第二种是通过其他页面进入,加载Home即可。export const Home: React.FC = () => {return <div>Home</div>;
};const HomePage: React.FC = () => {return (<PageHeadTabs title="首页"><Home /></PageHeadTabs>);
};export default HomePage;

4. 其他页面 

import PageHeadTabs from '@/components/PageHeadTabs';
import { Button } from 'antd';// *除了Home页面,其他的包裹一层PageHeadTabs即可实现。const AccessPage: React.FC = () => {return (<PageHeadTabs title="权限演示"><Button>按钮</Button></PageHeadTabs>);
};export default AccessPage;

5. 效果 

自己临时封装的一个小组件,功能如上图。

缺点:没有刷新和拖拽功能。

优点:可以缓存页面。 

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

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

相关文章

Python中类的继承实现

""派生类继承了基类的一切"&#xff0c;这里创建基类曲线&#xff0c;派生类有椭圆。派生类调用基类的方法&#xff0c;只需方法名前加上基类基类名作为前缀&#xff0c;再将派生类的self和其他参数传入。 当派生类与基类有同名的方法时&#xff0c;调用的是派…

Linux中关于head命令详解

head的作用 head用于查看文件的开头部分的内容。 head的参数 -q隐藏文件名-v 显示文件名-c<数目>显示的字节数-n<数目>显示的行数 head的案例 # 查看yum.log前五行内容 head -5 yum.log

Cortex-M3/M4内核NVIC及HAL库函数详解(4):使用HAL库配置外部中断

0 工具准备 Keil uVision5 Cortex M3权威指南&#xff08;中文&#xff09; Cortex M3与M4权威指南 stm32f407的HAL库工程 STM32F4xx中文参考手册 1 使用HAL库配置外部中断 前面我们已经熟悉了有关内核部分的寄存器配置&#xff0c;接下来我们结合stm32f407的GPIO外设&#xf…

站长为什么都说WordPress太复杂不会用要放弃?

网络上经常看到有站长说要放弃WordPress&#xff0c;理由各有不同&#xff0c;比如有些说WordPress太复杂不会用&#xff1b;有些说WordPress是国外建站系统&#xff0c;在国内用来搭建访问速度太慢&#xff1b;也有些说WordPress是针对谷歌优化的&#xff0c;不适合国内的搜索…

UE 可靠UDP实现原理

发送 我们的消息发送都是通过 UChannel 来处理的&#xff0c;通过调用 UChannel::SendBunch 统一处理。 发送的 Bunch 是以 FOutBunch 的形式存在的。当 bReliable 为 True 的时候&#xff0c;表示 Bunch 是可靠的。 发送逻辑直接从UChannel::SendBunch处开始分析 1、大小限…

【算法与数据结构】1049、LeetCode 最后一块石头的重量 II

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题需要得到石头之间两两粉碎之后的最小值&#xff0c;那么一个简单的思路就是将这堆石头划分成大小相…

【MySQL】where和having的区别

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;数据库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 用途: 使用位置: 操作对象: 聚合函数: 示例&#xff1a; 结语 我的其他博客 前言 数据库中的 WHERE 和 HAVING 子句在 SQL 查…

线程池最佳实践!这几个坑使用不当绝对生产事故!!

拿来即用!这篇文章我会介绍我使用线程池的时候应该注意的坑以及一些优秀的实践。 1、正确声明线程池 线程池必须手动通过 ThreadPoolExecutor 的构造函数来声明&#xff0c;避免使用Executors 类创建线程池&#xff0c;会有 OOM 风险。 Executors 返回线程池对象的弊端如下(…

【机器学习300问】14、什么是特征工程?

当我学习到这个知识点的时候十分困惑&#xff0c;因为从名字中我完全无法理解这个什么东西。于是呢我就去问了一下维基百科&#xff0c;下面是他的回答&#xff1a; 特征工程&#xff08;英语&#xff1a;feature engineering&#xff09;又称特征提取&#xff08;英语&#xf…

H3C设备配置端口镜像案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​ https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle O…

C语言练习day8

变种水仙花 变种水仙花_牛客题霸_牛客网 题目&#xff1a; 思路&#xff1a;我们拿到题目的第一步可以先看一看题目给的例子&#xff0c;1461这个数被从中间拆成了两部分&#xff1a;1和461&#xff0c;14和61&#xff0c;146和1&#xff0c;不知道看到这大家有没有觉得很熟…

计算机服务器中了mallox勒索病毒怎么办,mallox勒索病毒解密数据恢复

企业的计算机服务器存储着企业重要的信息数据&#xff0c;为企业的生产运营提供了极大便利&#xff0c;但网络安全威胁随着技术的不断发展也在不断增加&#xff0c;近期&#xff0c;云天数据恢复中心接到许多企业的求助&#xff0c;企业的计算机服务器中了mallox勒索病毒&#…

线性代数的学习和整理23:用EXCEL,VBA,python计算向量/矩阵的各种乘法:内积,点积,外积,叉积(建设ing)

目录 前言&#xff1a;EXCEL里的的向量相关计算公式 0.1 EXCEL里相关公式 0.2 先说结论&#xff1a;向量组的点乘公式和 向量组的点乘公式不一样 1 向量的点乘 (内积) 1.1 向量的点乘公式 1.2 EXCEL里向量点乘的计算 ​编辑 1.3 向量点乘的性质 1.3.1 内积的公式…

014-信息打点-JS架构框架识别泄漏提取API接口枚举FUZZ爬虫插件项目

014-信息打点-JS架构&框架识别&泄漏提取&API接口枚举&FUZZ爬虫&插件项目 #知识点&#xff1a; 1、JS前端架构-识别&分析 2、JS前端架构-开发框架分析 3、JS前端架构-打包器分析 4、JS前端架构-提取&FUZZ 解决&#xff1a; 1、如何从表现中的JS提取…

使用Element中的input组件如何实现文字和输入框在一行显示

利用 <el-form-item label"商品名称&#xff1a;">标签包裹即可&#xff0c;label写提示文字 <el-form ref"form" label-width"100px"><el-form-item label"商品名称&#xff1a;"><el-input v-model"na…

【设计模式-08】Flyweight享元模式

简要说明 简要的理解&#xff1a;享元模式就是新建一个池(Pool)&#xff0c;该池子(Pool)中有新建好的一堆对象&#xff0c;当需要使用时&#xff0c;从池子(Pool)中直接获取&#xff0c;不用重新新建一个对象。通俗的讲就是&#xff1a;共享元数据。 比如Java中的String就是使…

基于JavaWeb+SSM+Vue停车场微信小程序系统的设计和实现

基于JavaWebSSMVue停车场微信小程序系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 滑到文末获取源码 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关…

【网络安全】-入门版

secure 一、基本工具1、metasploit framework ps.本着兴趣爱好&#xff0c;加强电脑的安全防护能力&#xff0c;并严格遵守法律和道德规范。一、基本工具 1、metasploit framework msf&#xff08;metasploit framework&#xff09;是一个开源的渗透测试框架&#xff0c;用于…

小程序系列--10.小程序WXS 脚本

一、概述 1. 什么是 wxs&#xff1f; WXS&#xff08;WeiXin Script&#xff09;是小程序独有的一套脚本语言&#xff0c;结合 WXML&#xff0c;可以构建出页面的结构。 2. wxs 的应用场景 wxml 中无法调用在页面的 .js 中定义的函数&#xff0c;但是&#xff0c;wxml 中可…

Vagrant安装Oracle Data Guard环境示例

在Windows 11下&#xff0c;通过Vagrant安装标准的Data Guard环境&#xff08;默认为non-CDB模式&#xff09;&#xff0c;耗时约26分钟&#xff0c;共生成2台虚机。以下为安装日志&#xff1a; ...host2: Welcome to DGMGRL, type "help" for information.host2: C…