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.pswp.cn/diannao/37372.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/37372.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/37372.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

入門Java爬蟲:認識其基本概念和應用方法

Java爬蟲初探&#xff1a;了解它的基本概念與用途&#xff0c;需要具體代碼示例 隨著互聯網的快速發展&#xff0c;獲取并處理大量的數據成為企業和個人不可或缺的一項任務。而爬蟲&#xff08;Web Scraping&#xff09;作為一種自動化的數據獲取方法&#xff0c;不僅能夠快速…

Pegasus平臺Pytorch源碼編譯

和Drive PX2的編譯沒有什么區別 源碼編譯步驟 注意&#xff1a;當完全按照以下步驟進行編譯時&#xff0c;如果出現錯誤&#xff0c;注意檢查CUDA版本、對應的庫是否存在 # Download PyTorch sources git clone --recursive --branch <version> http://github.com/pyt…

vtk渲染過程

vtk渲染 VTK的渲染過程主要包括創建渲染器、添加演員到渲染器、創建渲染窗口、創建交互器、將渲染器添加到渲染窗口中、將交互器與渲染窗口關聯、啟動交互器循環等步驟。12 創建渲染器(Renderer): 首先&#xff0c;需要創建一個或多個渲染器。渲染器是VTK中的一個重要組件&am…

在Ubuntu 16.04上安裝和配置Elasticsearch的方法

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到網站。 簡介 Elasticsearch 是一個用于實時分布式搜索和數據分析的平臺。它因易用性、強大功能和可擴展性而備受歡迎。 Elasticsearch 支持 R…

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

看來很多解決方法都沒有辦法&#xff0c;最后終于。嗚嗚嗚嗚 這里我用的是vue-cli去搭建的項目的vue2 項目&#xff0c;其實不建議用vue2搭配cesium。因為目前cesium停止了對vue2的版本更新&#xff0c;現在默認安裝都是vue3版本&#xff0c;因此需要控制版本&#xff0c;否則…

Kylin支持哪些數據源,它們之間有什么區別

Apache Kylin支持多種數據源&#xff0c;這些數據源為Kylin提供了從不同類型和存儲方式的數據中獲取和處理數據的能力。以下是Kylin支持的主要數據源及其之間的區別&#xff1a; Apache Hadoop HDFS&#xff1a; 描述&#xff1a;HDFS是Hadoop生態系統中用于存儲大數據的文件系…

Node.js簡介

一&#xff1a;Node.js簡介 Node.js是一個跨平臺的JavaScript運行環境&#xff0c;使開發者可以搭建服務器端的JavaScript應用程序 作用&#xff1a;使用Node.js編寫服務器端程序 編寫數據接口&#xff0c;提供網頁資源瀏覽功能有利于前端工程化&#xff0c;可以集成各種開發…

鴻蒙系統的前世今生

2019年8月9日&#xff0c;華為在開發者大會上發布EMUI 10的同時宣告了HarmonyOS 1.0的誕生。鴻蒙誕生的背景是&#xff0c;美限制華為與谷歌以及其他美國科技公司開展業務。 前華為開發者大會上&#xff0c;HarmonyOS NEXT&#xff08;原生鴻蒙&#xff09;正式公布&#xff0c…

【力扣高頻題】011. 盛最多水的容器

前面的算法文章&#xff0c;更新了許多 專題系列 。包括&#xff1a;滑動窗口、動態規劃、加強堆、二叉樹遞歸套路 等。 還沒讀過的小伙伴可以關注一下&#xff0c;在主頁中點擊對應鏈接查看哦~ 接下來的一段時間&#xff0c;將持續 「力扣高頻題」 系列文章&#xff0c;想刷 …

Java基礎知識-線程池

1、為什么要用線程池&#xff1f; 創建線程要花費昂貴的資源和時間&#xff0c;如果任務來了才創建線程那么響應時間會變長&#xff0c;而且一個進程能創建的線程數 有限。為了避免這些問題&#xff0c;在程序啟動的時候就創建若干線程來響應處理&#xff0c;它們被稱為線程池&…

使用pywinauto自動重連easyconnect

啟動easyconnect后&#xff0c;運行該腳本&#xff0c;實現自動重連。需要填一下連接的地址&#xff0c;用戶名和密碼(替換一下腳本里的xxx) from pywinauto import application from pywinauto import timings import time# 初始化應用程序對象 app1 application.Applicatio…

2710. 移除字符串中的尾隨零 Easy

給你一個用字符串表示的正整數 num &#xff0c;請你以字符串形式返回不含尾隨零的整數 num 。 示例 1&#xff1a; 輸入&#xff1a;num "51230100" 輸出&#xff1a;"512301" 解釋&#xff1a;整數 "51230100" 有 2 個尾隨零&#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…

Spring:Spring中分布式事務解決方案

一、前言 在Spring中&#xff0c;分布式事務是指涉及多個數據庫或系統的事務處理&#xff0c;其中事務的參與者、支持事務的服務器、資源管理器以及事務管理器位于分布式系統的不同節點上。這樣的架構使得兩個或多個網絡計算機上的數據能夠被訪問并更新&#xff0c;同時將這些操…

使用通用的響應格式

使用泛型響應類&#xff08;或者類似的響應封裝類&#xff09;在網絡編程和API設計中有很多好處&#xff0c;包括但不限于以下幾點&#xff1a; 統一響應格式&#xff1a; 使用R<T>可以確保API的所有響應都遵循相同的格式&#xff0c;這有助于客戶端更容易地解析和處理響…

IP地址與在線教育平臺資源分配優化

IP地址的資源分配與優化策略可以幫助在線教育平臺提供更高質量、穩定且個性化的教育服務。 IP地址作為網絡設備的標識符&#xff0c;能夠為在線教育平臺提供有關學生地理位置和網絡環境信息。通過對學生IP地址的分析&#xff0c;平臺可以初步了解學生所在的地區、網絡服務提供商…

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

大家在日常使用電腦的過程中&#xff0c;難免會遇到不小心刪除重要文件的情況&#xff0c;尤其是珍貴的照片。當我們意識到誤刪照片時&#xff0c;第一反應通常是去回收站找回。然而&#xff0c;如果連回收站的照片都被刪除了&#xff0c;該如何恢復呢&#xff1f;本文將詳細探…

【MySQL】事務的快照生成時間點和薛定諤的貓相關?

概述 最近因為工作需要&#xff0c;對MySQL的事務處理進行了一系列測試驗證&#xff0c;其中&#xff0c;對于MySQL的事務到底時什么時候生成了數據的快照&#xff0c;結果似乎跟薛定諤的貓理念很像&#xff0c;很有意思&#xff1b;過程我貼出來&#xff0c;有興趣的朋友可以一…

Python提供API給JAVA調用,實現Python和Java之間的交互

一、Java 調用Python 提供的API接口&#xff0c;有多種方法&#xff0c;本文通過Python 提供的Rest API進行調用 二、在Python中創建一個REST API&#xff0c;你可以使用許多框架&#xff0c;其中兩個最流行的框架是Flask和Django REST framework。這兩個框架都提供了創建REST…

Dockerfile詳情,Django項目中使用Dockerfile

Dockerfile詳情&#xff0c;Django項目中使用Dockerfile 目錄 Dockerfile詳情&#xff0c;Django項目中使用Dockerfile介紹常用指令Dokcerfile部署Django項目安裝Docker獲取項目源碼Dockerfile文件構建Docker鏡像運行Docker容器 介紹 Dockerfile是一個文本文件&#xff0c;一般…