当前位置: 首页 > news >正文

React:tabs或标签页自定义右击菜单内容,支持内嵌iframe关闭菜单方案

React:tabs或标签页自定义右击菜单内容,支持内嵌iframe关闭菜单方案

不管是react、vue还是原生js,原理是一样的。
注意如果内嵌iframe情况下,iframe无法使用事件监听,但是可以使用iframe的任何点击行为都会往父级window通信,使用window的message事件监听即可。

场景

前端自定义标签页,一个标签对应一个路由页面,通过切换标签快速切换不同应用或者页面

代码

变量
state = {contextMenuIndex: '', // 右击菜单索引contextMenuPosition: { // 右击菜单定位信息clientX: '',clientY: '',},visiableContextMenu: false, // 右击菜单是否显示};
事件加载
componentDidMount() {// 监听当前document的鼠标右击事件document.addEventListener('contextmenu', (event) => {event.preventDefault();if (this.state.visiableContextMenu === -1) {return;}this.setState({contextMenuPosition: {clientX: `${event.clientX}px`,clientY: `${event.clientY}px`,},});});// 监听当前document的鼠标点击事件,用于关闭自定义菜单document.addEventListener('click', () => {this.setState({visiableContextMenu: false,});});// 监听当前window的messag事件(有内嵌iframe时使用,若无可不使用)// 无法使用iframe监听,可以通过和父级window的消息通信达到目的。window.addEventListener('message', () => {this.setState({visiableContextMenu: false,});});}
标签
		<div>{/* ... */}{/* 自定义右击菜单 */}{visiableContextMenu ? (<MenuclassName="contextMenuList"style={{ left: clientX, top: clientY }}><Menu.Item onClick={() => this.hadleCloseByIndex([contextMenuIndex])}>关闭当前</Menu.Item><Menu.Item onClick={() => this.closeLeft()}>关闭左侧</Menu.Item><Menu.Item onClick={() => this.closeRight()}>关闭右侧</Menu.Item><Menu.Item onClick={() => this.closeAll()}>关闭全部</Menu.Item></Menu>) : ('')}</div>
完整案例代码
import React, { Component } from 'react';
import { SyncOutlined } from '@ant-design/icons';
import { Tabs, Menu } from 'antd';
import store from 'store';// styl
import './IndexTabsNavigation.styl';class IndexTabsNavigation extends Component {state = {contextMenuIndex: '',contextMenuPosition: {clientX: '',clientY: '',},visiableContextMenu: false,};onClick(id) {this.props.updateOpenModuleId(id);}onEdit(targetKey, action) {// e.stopPropagation();if (action === 'remove') {// 多租户首页最后一个数据不能删除if (this.props.isTenant && this.props.openModule.length === 1) return;const index = this.props.openModule.findIndex((item) => String(item.id) === String(targetKey),);this.props.removeModule(targetKey, index);}}onReset(item, index, e) {e.stopPropagation();const getIframe = document.querySelectorAll('.inner-iframe')[index];if (getIframe) {getIframe.setAttribute('src', `${item.path}&_t=${Math.random() * 1e18}`);}}componentDidMount() {document.addEventListener('contextmenu', (event) => {event.preventDefault();if (this.state.visiableContextMenu === -1) {return;}this.setState({contextMenuPosition: {clientX: `${event.clientX}px`,clientY: `${event.clientY}px`,},});});document.addEventListener('click', () => {this.setState({visiableContextMenu: false,});});window.addEventListener('message', () => {this.setState({visiableContextMenu: false,});});}// 设置右击菜单onContextMenuFun(contextMenuIndex) {this.setState({contextMenuIndex,visiableContextMenu: true,});}hadleCloseByIndex(indexList) {if (this.props.isTenant && this.props.openModule.length === 1) return;indexList.map((index, idx) => {const item = this.props.openModule[index];setTimeout(() => {this.props.removeModule(item.id, index);}, 100 * idx)})}// 关闭左侧closeLeft() {const { contextMenuIndex } = this.state;if (contextMenuIndex <= 0) return;const closeList = Array.from({length: contextMenuIndex}).map((item, index) => index)this.props.removeModuleListByIndex(closeList);}// 关闭右侧closeRight() {const { contextMenuIndex } = this.state;const openModule = this.props.openModule;const delLength = openModule.length - 1 - contextMenuIndex;if (delLength <= 0) return;const closeList = Array.from({length: delLength}).map((item, index) => item = contextMenuIndex + index + 1)this.props.removeModuleListByIndex(closeList);setTimeout(() => {// 判断当前tabs是否有高亮const newOpenModule = [...this.props.openModule];const openModuleOpenInfo = store.get('openModuleOpenInfo') || {};const openObj = newOpenModule.find((item) => String(item.id) === String(openModuleOpenInfo.id),);if (!openObj) {this.props.updateOpenModuleId(newOpenModule[newOpenModule.length - 1].id);}}, 300)}// 关闭全部closeAll() {const openModule = this.props.openModule;if (openModule.length - 1 <= 0) return;const openModuleOpenInfo = store.get('openModuleOpenInfo') || {};const openIndex = openModule.findIndex((item) => String(item.id) === String(openModuleOpenInfo.id),);const closeList = openModule.map((item, index) => index).filter((item, index) => index !== openIndex)this.props.removeModuleListByIndex(closeList);}render() {const {contextMenuIndex,visiableContextMenu,contextMenuPosition: { clientX, clientY },} = this.state;return (<div className="index-tabs-navigation-box"><divref={(indexTabs) => (this.indexTabs = indexTabs)}className={`${this.props.isTenant? 'index-tabs-navigation-isTenant': 'index-tabs-navigation'}`}><TabshideAddtype="editable-card"activeKey={String(this.props.openModuleId)}onChange={this.onClick.bind(this)}onEdit={this.onEdit.bind(this)}items={this.props.openModule.map((item, index) => {return {key: String(item.id),label: (<divclassName="customLabel"onContextMenu={this.onContextMenuFun.bind(this,index,)}><span className="customLabel-title">{item.title}</span>{String(this.props.openModuleId) === String(item.id) ? (<SyncOutlinedonClick={this.onReset.bind(this, item, index)}className="customLabel-reset"/>) : ('')}</div>),};})}/>{/* 自定义右击菜单 */}{visiableContextMenu ? (<MenuclassName="contextMenuList"style={{ left: clientX, top: clientY }}><Menu.Item onClick={() => this.hadleCloseByIndex([contextMenuIndex])}>关闭当前</Menu.Item><Menu.Item onClick={() => this.closeLeft()}>关闭左侧</Menu.Item><Menu.Item onClick={() => this.closeRight()}>关闭右侧</Menu.Item><Menu.Item onClick={() => this.closeAll()}>关闭全部</Menu.Item></Menu>) : ('')}</div></div>);}
}export default IndexTabsNavigation;

样式代码styl:

.contextMenuListposition: fixedz-index 1001border: solid 1px #e9ecf0padding: 5px 0.ant-menu-itemmargin-bottom: 0 !importantpadding: 5px 12px;line-height: 22px;height: 32px;margin-top: 0 !important.ant-menu-title-contentmargin-right: 5px !important;
案例效果图

在这里插入图片描述

相关文章:

React:tabs或标签页自定义右击菜单内容,支持内嵌iframe关闭菜单方案

React&#xff1a;tabs或标签页自定义右击菜单内容&#xff0c;支持内嵌iframe关闭菜单方案 不管是react、vue还是原生js&#xff0c;原理是一样的。 注意如果内嵌iframe情况下&#xff0c;iframe无法使用事件监听&#xff0c;但是可以使用iframe的任何点击行为都会往父级wind…...

Taro +vue3 中的微信小程序中的分享

微信小程序 右上角分享 的触发 以及配 useShareAppMessage(() > {return {title: "电影属全国通兑券",page: /pages/home/index,imageUrl: "http:///chuanshuo.jpg",};}); 置 就是Taro框架中提供的一个分享Api 封装好的...

视频监控EasyCVR视频汇聚/智能边缘网关:EasySearch无法探测到服务器如何处理?

安防监控EasyCVR智能边缘网关/视频汇聚网关/视频网关属于软硬一体的边缘计算硬件&#xff0c;可提供多协议&#xff08;RTSP/RTMP/国标GB28181/GAT1400/海康Ehome/大华/海康/宇视等SDK&#xff09;的设备接入、音视频采集、视频转码、处理、分发等服务&#xff0c;系统具备实时…...

openlayer 鼠标点击船舶,打开船舶简单弹框

背景&#xff1a; 对创建的地图对象&#xff0c;可以添加上监听事件&#xff0c;常用的有&#xff1a;地图点击事件、鼠标移动事件。 通过监听这些事件&#xff0c;又可以区分不同图层的不同要素&#xff0c;获取不同数据&#xff1b; 根据这些数据&#xff0c;又可以发起网络请…...

数据挖掘常见算法(关联)

Apriori算法 Apriori算法基于频繁项集性质的先验知识&#xff0c;使用由下至上逐层搜索的迭代方法&#xff0c;即从频繁1项集开始&#xff0c;采用频繁k项集搜索频繁k1项集&#xff0c;直到不能找到包含更多项的频繁项集为止。 Apriori算法由以下步骤组成&#xff0c;其中的核…...

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档&#xff1a;https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址&#xff1a;https://github.com/Hufe921/canvas-editor 前提声明&#xff1a; 由于CanvasEditor目前不支持vue、react 等框架开箱即用版&#xff0c;所以…...

Redis Stream Redisson Stream

目录 一、Redis Stream1.1 场景1&#xff1a;多个客户端可以同时接收到消息1.1.1 XADD - 向stream添加Entry&#xff08;发消息 &#xff09;1.1.2 XREAD - 从stream中读取Entry&#xff08;收消息&#xff09;1.1.3 XRANGE - 从stream指定区间读取Entry&#xff08;收消息&…...

threadX netx 设置IP地址以及获取IP地址

ThreadX 是一个实时操作系统&#xff08;RTOS&#xff09;内核&#xff0c;而 NetX 则是 Express Logic 提供的一个嵌入式 TCP/IP 网络栈&#xff0c;它经常与 ThreadX 一起使用来提供网络功能。在 ThreadX 和 NetX 中设置和获取 IP 地址通常涉及几个步骤。 设置 IP 地址 初始…...

计算机毕业设计hadoop+spark+hive知识图谱医生推荐系统 医生数据分析可视化大屏 医生爬虫 医疗可视化 医生大数据 机器学习 大数据毕业设计

测试过程及结果 本次对于医生推荐系统测试通过手动测试的方式共进行了两轮测试。 &#xff08;1&#xff09;第一轮测试中执行了个20个测试用例&#xff0c;通过16个&#xff0c;失败4个&#xff0c;其中属于严重缺陷的1个&#xff0c;属于一般缺陷的3个。 &#xff08;2&am…...

lammps已经运算结束,有数据忘记算:rerun 命令

需要的文件 1、模拟运算的所有文件&#xff08;模型 、in文件、力场文件&#xff09; 2、模拟计算所得到的dump文件&#xff08;原子轨迹文件&#xff09; rerun命令的使用&#xff08;修改in文件&#xff09; 1、删除or注释掉 输出dump文件的那一行命令 2、加上需要补充计…...

CARLA自动驾驶模拟器基础

CARLA 使用服务器-客户端架构运行&#xff0c;其中 CARLA 服务器运行模拟并由客户端向其发送指令。客户端代码使用 API 与服务器进行通信。要使用 Python API&#xff0c;您必须通过 PIP 安装该模块&#xff1a; pip3 install carla-simulator # Python 3World and client 客…...

华为HCIP Datacom H12-821 卷16

1.判断题 在 VRRP 中,当设备状态变为 Master 后,,会立刻发送免费 ARP 来刷新下游设备的 MAC 表项,从而把用户的流量引到此台设备上来 A、对 B、错 正确答案: A 解析: 2.判断题 路由选择工具 route- policy 能够基于预先定义的条件来进行过滤并设置 BGP...

Python学习打卡:day17

day17 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day17121、Python 操作 MySQL 基础使用pymysql创建到 MySQL 的数据库链接执行 SQL 语句执行非查询性质的SQL语句执行查询性质的SQL语句 122、Pyth…...

Spring Cloud Gateway 与 Nacos 的完美结合

在现代微服务架构中&#xff0c;服务网关扮演着至关重要的角色。它不仅负责路由请求到相应的服务&#xff0c;还承担着诸如负载均衡、安全认证、限流熔断等重要功能。Spring Cloud Gateway 作为 Spring Cloud 生态系统中的一员&#xff0c;以其强大的功能和灵活的配置&#xff…...

vue2 element ui 表单 动态增加表单项 表单项值不可重复 select多选

案例 <template><el-form :model"form" ref"form" label-width"70px"><el-form-item><el-button icon"el-icon-plus" type"primary" plain click"add">新增</el-button><el-b…...

[数据集][目标检测]电力场景下电柜箱门把手检测数据集VOC+YOLO格式1167张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1167 标注数量(xml文件个数)&#xff1a;1167 标注数量(txt文件个数)&#xff1a;1167 标注…...

OverTheWire Bandit 靶场通关解析(上)

介绍 OverTheWire Bandit 是一个针对初学者设计的网络安全挑战平台&#xff0c;旨在帮助用户掌握基本的命令行操作和网络安全技能。Bandit 游戏包含一系列的关卡&#xff0c;每个关卡都需要解决特定的任务来获取进入下一关的凭证。通过逐步挑战更复杂的问题&#xff0c;用户可…...

【Python实战因果推断】4_因果效应异质性4

目录 Cumulative Gain Target Transformation Cumulative Gain 如果采用与累积效应曲线完全相同的逻辑&#xff0c;但将每个点乘以累积样本 Ncum/N&#xff0c;就会得到累积增益曲线。现在&#xff0c;即使曲线的起点具有最高的效果&#xff08;对于一个好的模型来说&#x…...

大模型推理知识总结

一、大模型推理概念 大多数流行的only-decode LLM&#xff08;例如 GPT-3&#xff09;都是针对因果建模目标进行预训练的&#xff0c;本质上是作为下一个词预测器。这些 LLM 将一系列tokens作为输入&#xff0c;并自回归生成后续tokens&#xff0c;直到满足停止条件&#xff0…...

[笔记] keytool 导入服务器证书和证书私钥

背景 我当前手头已有一个服务器证书和对应的私钥&#xff0c;现在需要转换为 Java KeyStore 格式使用&#xff0c;找了一大圈才发现 keytool 无法直接导入服务器证书和私钥&#xff0c;当然证书可以直接导入&#xff0c;但是私钥是无法直接导入。找了一大圈发现可以先将服务器…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...