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;
案例效果图

在这里插入图片描述

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

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

相关文章

入门Java爬虫:认识其基本概念和应用方法

Java爬虫初探&#xff1a;了解它的基本概念与用途&#xff0c;需要具体代码示例 随着互联网的快速发展&#xff0c;获取并处理大量的数据成为企业和个人不可或缺的一项任务。而爬虫&#xff08;Web Scraping&#xff09;作为一种自动化的数据获取方法&#xff0c;不仅能够快速…

vue2(vue-cli3x[vue.config.js])使用cesium新版(1.117.0)配置过程

看来很多解决方法都没有办法&#xff0c;最后终于。呜呜呜呜 这里我用的是vue-cli去搭建的项目的vue2 项目&#xff0c;其实不建议用vue2搭配cesium。因为目前cesium停止了对vue2的版本更新&#xff0c;现在默认安装都是vue3版本&#xff0c;因此需要控制版本&#xff0c;否则…

Node.js简介

一&#xff1a;Node.js简介 Node.js是一个跨平台的JavaScript运行环境&#xff0c;使开发者可以搭建服务器端的JavaScript应用程序 作用&#xff1a;使用Node.js编写服务器端程序 编写数据接口&#xff0c;提供网页资源浏览功能有利于前端工程化&#xff0c;可以集成各种开发…

【力扣高频题】011. 盛最多水的容器

前面的算法文章&#xff0c;更新了许多 专题系列 。包括&#xff1a;滑动窗口、动态规划、加强堆、二叉树递归套路 等。 还没读过的小伙伴可以关注一下&#xff0c;在主页中点击对应链接查看哦~ 接下来的一段时间&#xff0c;将持续 「力扣高频题」 系列文章&#xff0c;想刷 …

idea2024使用springboot3.x系列新建java项目,使用jdk17,启动项目报错

身为一名开发人员&#xff0c;敲代码无数&#xff0c;竟被一个小小启动给我卡了大半天&#xff0c;太丢脸了 报错一&#xff1a;Field infoSysRepository in com.erectile.Impl.PersonalInfoServiceImpl required a bean of type ‘com.erectile.jpa.repository.InfoSysReposit…

IP地址与在线教育平台资源分配优化

IP地址的资源分配与优化策略可以帮助在线教育平台提供更高质量、稳定且个性化的教育服务。 IP地址作为网络设备的标识符&#xff0c;能够为在线教育平台提供有关学生地理位置和网络环境信息。通过对学生IP地址的分析&#xff0c;平台可以初步了解学生所在的地区、网络服务提供商…

回收站的照片删除了怎么找回?

大家在日常使用电脑的过程中&#xff0c;难免会遇到不小心删除重要文件的情况&#xff0c;尤其是珍贵的照片。当我们意识到误删照片时&#xff0c;第一反应通常是去回收站找回。然而&#xff0c;如果连回收站的照片都被删除了&#xff0c;该如何恢复呢&#xff1f;本文将详细探…

【MySQL】事务的快照生成时间点和薛定谔的猫相关?

概述 最近因为工作需要&#xff0c;对MySQL的事务处理进行了一系列测试验证&#xff0c;其中&#xff0c;对于MySQL的事务到底时什么时候生成了数据的快照&#xff0c;结果似乎跟薛定谔的猫理念很像&#xff0c;很有意思&#xff1b;过程我贴出来&#xff0c;有兴趣的朋友可以一…

simulink开发stm32,使用中断模块,无法产生中断,其中包括使用timer模块,以及ADC都无法产生中断,需要注意的地方

1&#xff0c;其中包括使用timer模块&#xff0c;以及ADC都无法产生中断&#xff0c;需要注意的地方 原来是需要在配置文件里开启一下timer的中断&#xff0c;其他模块自动加载ioc就可以了&#xff0c;这个timer需要注意力&#xff0c;需要自己勾选一下 如下图&#xff1a; 看…

致敬经典:在国产开源操作系统 RT-Thread 重温 UNIX 彩色终端

引言 上篇文章里我们向大家介绍了 RT-Thread v5.1.0 的一些新特性。其中包括了终端环境的进一步完善。终端是人机交互的重要接口。实用的终端工具可以显著地提升系统使用者的幸福指数。举例来说&#xff0c;当我们想要修改一些系统配置&#xff0c;或是编写脚本时&#xff0c;一…

Linux——echo命令,管道符,vi/vim 文本编辑器

1.echo 命令 作用 向终端设备上输出字符串或变量的存储数据 格式 echo " 字符串 " echo $ 变 量名 [rootserver ~] # echo $SHELL # 输出变量的值必须加 $ /bin/bash [rootserver ~] # str1" 我爱中国 " # 自定义变量 echo 重定向输出到文件 ec…

MySQL数据库——在Centos7环境安装

MySQL在Centos7环境安装 1.切换root用户 安装与卸载中&#xff0c;用户全部切换成为root&#xff0c;安装好后&#xff0c;普通用户也能使用 2.卸载不要的环境 要将自己环境中有关mysql的全都删除&#xff0c;避免安装过程中被影响 ps axj | grep mariadb 先检查是否有mari…

自制全网最便宜的雷达感应灯光画,成本只需5元

自制全网最便宜的雷达感应灯光画&#xff0c;成本5元 ​ 成本组成&#xff1a;带热释电的人体感应灯&#xff08;0.5元&#xff09;雷达感应模块&#xff08;3.5元&#xff09;首饰盒&#xff08;0.45元&#xff09;微喷油画布&#xff08;1元&#xff09;5.45元 ​ 说一下做灯…

Flutter学习:从搭建环境到运行

一、开发环境的搭建 本文所示内容都是在Windows系统下进行的。 1、下载 Flutter SDK Flutter 官网&#xff08;https://docs.flutter.cn/release/archive?tabwindows&#xff09; 或者通过 git clone -b master https://github.com/flutter/flutter.git 下载 2、配置环境…

[数据集][目标检测]井盖未盖好检测数据集VOC+YOLO格式20123张2类别

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

.net 奇葩问题调试经历之2——内存暴涨,来自非托管的内存泄露

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔序言 这是一个序列文章,请看以往文…

代码统计工具V1.0.0(支持各种文件类型)

点击下载《代码统计工具&#xff08;支持各种文件类型&#xff09;》 1. 前言 本文介绍了一款使用C#开发的代码行数统计软件。该软件允许用户通过选择文件目录和设置统计项目类型&#xff0c;来统计指定目录下的代码行数。软件提供了三种统计方式&#xff1a;按文件名统计、按…

线性图标绘制指南:从基础到精通

图标在生活中随处可见。相比文字来说&#xff0c;图标可以让人在更短的时间内认知并了解信息&#xff0c;并且大大提升信息的视觉美观性&#xff0c;增加设计的艺术感染力。在用户界面中使用图标&#xff0c;是一种用户熟知的设计模式。而线性图标是通过提炼图形轮廓&#xff0…

jquery动态插件之gsap和TextPlugin

<!DOCTYPE html> <html> <head><title>数字化人才认证数动画</title><script src"https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script src"https://cdnjs.cloudflare.com/ajax…

【强化学习】第02期:动态规划方法

笔者近期上了国科大周晓飞老师《强化学习及其应用》课程&#xff0c;计划整理一个强化学习系列笔记。笔记中所引用的内容部分出自周老师的课程PPT。笔记中如有不到之处&#xff0c;敬请批评指正。 文章目录 2.1 动态规划&#xff1a;策略收敛法/策略迭代法2.2 动态规划&#xf…