react umi/max 封装页签组件

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 } from '@umijs/max';
import { Dropdown, Tabs } from 'antd';
import { useEffect } from 'react';type PageHeadTabsProps = {children: any;title: string;
};const PageHeadTabs = (props: PageHeadTabsProps) => {const { children, title } = 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: title,title: title,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/631435.shtml

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

相关文章

Unity3D和three.js的比较

一、Unity3D和three.js简介 Unity3D是一款跨平台的游戏引擎,可以用于开发2D和3D游戏。它提供了一个可视化的开发环境,包含了强大的编辑器和工具,使开发者可以方便地创建游戏场景、添加物体、设置物理效果、编写脚本等。Unity3D支持多种平台,包括PC、移动设备、主机等,可以…

解析exe文件

概述&#xff1a;上次用java生成exe文件。那如何解析exe文件呢&#xff1f;0.0 首先我们要了解exe文件里面有什么。用360压缩解压一下。 .data&#xff1a; 存储程序中的初始化的全局和静态变量。在程序运行之前&#xff0c;这些变量会被赋予特定的初始值。 .pdata (Procedu…

销售方法用得好,业绩蹭蹭蹭!

新零售模式是随着科技的发展而崭露头角的零售业态&#xff0c;它融合了传统零售与先进技术&#xff0c;为消费者带来了更便捷、智能的购物体验。 其中&#xff0c;自动售货机作为新零售的一种代表形式&#xff0c;通过高度自动化和数字化的手段&#xff0c;为消费者提供更加便利…

【RT-DETR有效改进】移动设备网络ShuffleNetV1(超轻量化网络主干)

前言 大家好&#xff0c;这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进&#xff0c;内容持续更新&#xff0c;每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本&#xff0c;同时修改内容也支持ResNet32、ResNet101和PP…

计算机网络-计算机网络的概念 功能 发展阶段 组成 分类

文章目录 计算机网络的概念 功能 发展阶段总览计算机网络的概念计算机网络的功能计算机网络的发展计算机网络的发展-第一阶段计算机网络的发展-第二阶段-第三阶段计算机网络的发展-第三阶段-多层次ISP结构 小结 计算机网络的组成与分类计算机网络的组成计算机网络的分类小结 计…

【史上最全】前端页面深入浅出浏览器渲染原理

前言 浏览器的核心组件&#xff0c;即通常所说的浏览器内核&#xff0c;是支撑整个浏览器运行的关键性底层软件架构&#xff0c;它由两个关键组成部分构成&#xff1a;一个是负责网页内容解析和渲染的渲染引擎&#xff0c;另一个则是用于执行JavaScript代码的JS引擎。各浏览器厂…

双向搜索的理解和板子

"互相奔赴&#xff0c;各司其职。“ ——双向搜索 双搜的要求&#xff1a; 当我们发现&#xff0c;要从一种状态开始&#xff0c;经过很多次操作&#xff0c;来得到一种给定的状态。 这时候&#xff0c;我们就可以考虑用双向搜索。 从起点和终点开始搜。当二者相遇&…

【React】组件性能优化、高阶组件

文章目录 React性能优化SCUReact更新机制keys的优化render函数被调用shouldComponentUpdatePureComponentshallowEqual方法高阶组件memo 获取DOM方式refs如何使用refref的类型 受控和非受控组件认识受控组件非受控组件 React的高阶组件认识高阶函数高阶组件的定义应用一 – pro…

如何用Python进行数据分析(保姆级教程)

有小伙伴在学Python新手教程的时候说学Python比较复杂的地方就是资料太多了&#xff0c;比较复杂。 很多网上的资料都是从语法教起的&#xff0c;花了很多时间还是云里雾里&#xff0c;摸不清方向。今天就给大家来捋一捋思路&#xff01;帮助大家提高学习效率&#xff01; Pyt…

Markdown 类图绘制详解

✍️作者简介&#xff1a;小北编程&#xff08;专注于HarmonyOS、Android、Java、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&#x1f…

水电站智能监测泄洪预警系统介绍

一、背景 近年来由于危险河道管理措施不到位&#xff0c;调峰电站泄水风险长期存在&#xff0c;信息通报制度缺失以及民众安全警觉性不高等因素导致的水电站在泄洪时冲走下游河道游客以及人民财产的事故频发。 二、系统介绍 水电站智能监测泄洪预警系统是一种集成了物联网、云…

ElasticSearch的常用增删改查DSL和代码

es增删改查常用语法 我们日常开发中&#xff0c;操作数据库写sql倒是不可能忘记&#xff0c;但是操作es的dsl语句有时候很容易忘记&#xff0c;特地记录一下方便查找。 DSL语句 1、创建索引 -- 创建索引 PUT /my_index {"mappings": {"properties": {&…

GEE中Landsat、Sentinel、Modis主要数据集区别

一、Landsat 1. Collection 1/2 的区别 Collection 2 是Landsat Level 1 数据的又一次重大再处理&#xff0c;显著提高了绝对地理定位精度。 Collection1Collection2时间跨度1972~2021底1972~至今数据等级level 1level1&#xff1a;1972~2021底 level2&#xff1a;1982~至今 …

路由器初始化配置、功能配置

实验环境 拓扑图 Ip规划表&#xff08;各组使用自己的IP规划表&#xff09; 部门 主机数量 网络地址 子网掩码 网关 可用ip Vlan 市场部 38 192.168.131.0 255.255.255.0 192.168.131.1 2-254 11 研发部 53 192.168.132.0 255.255.255.0 192.168.132.1 2-2…

Oracle21C + PLSQL Developer 15 + Oracle客户端21安装配置完整图文版

一、Oracle21C PLSQL Developer 15 Oracle客户端文件下载 1、Oracl21C下载地址&#xff1a;Database Software Downloads | Oracle 中国 2、 PLSQL Developer 15下载地址&#xff1a;Registered download PL/SQL Developer - Allround Automations 3、 Oracle 客户端下载地址…

全球光伏知名企业-晶科能源联合泛微采知连,建立文控管理平台

晶科能源股份有限公司&#xff08;简称“晶科能源”&#xff09;是一家全球知名、极具创新力的太阳能科技企业。 &#xff08;图片素材来自晶科能源官网&#xff09; 公司战略性布局光伏产业链核心环节&#xff0c;聚焦光伏产品一体化研发制造和清洁能源整体解决方案提供&…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--具身智能、强化学习

专属领域论文订阅 VX关注 晓理紫&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 分类: 大语言模型LLM视觉模型VLM扩散模型视觉导航具身智能&#xff0c;机器人强化学习开放词汇&#xff0c;检测分割 [晓理紫]每日论文分享…

【LabVIEW FPGA入门】FPGA中的数学运算

数值控件选板上的大部分数学函数都支持整数或定点数据类型&#xff0c;但是需要请注意&#xff0c;避免使用乘法、除法、倒数、平方根等函数&#xff0c;此类函数比较占用FPGA资源&#xff0c;且如果使用的是定点数据或单精度浮点数据仅适用于FPGA终端。 1.整数运算 支持的数…

【Java】IDEA中的JFormDesigner使用教程

目录 1 安装 JFormDesigner 插件2 JFormDesigner 使用教程2.1 新建JFormDesigner Form时的选项2.2 JFormDesigner Form界面布局2.3 JFormDesigner 组件2.3.1 Components基本组件2.3.2 Containers中间容器&#xff08;面板&#xff09;2.3.3 Windows顶级容器&#xff08;窗口&am…

给科研人的 ML 开源发布工具包

什么是开源发布工具包&#xff1f; 恭喜你的论文成功发表&#xff0c;这是一个巨大的成就&#xff01;你的研究成果将为学界做出贡献。 其实除了发表论文之外&#xff0c;你还可以通过发布研究的其他部分&#xff0c;如代码、数据集、模型等&#xff0c;来增加研究的可见度和采…