效果:
原理:
1、所有需要页签页面,都需要一个共同父组件
2、如何缓存,用的是ant的Tabs组件,在共同父组件中,实际是展示的Tabs组件
3、右键,用的是ant的Dropdown组件,当点击时,记录所对应的key值及坐标,做后续操作
代码:
1、路由处理,需要用一个共同的父组件
在ant design pro4中,不支持隐藏父路由,展示子路由的功能,需要在app.tsx文件中,对路由做额外处理
共同父组件,BaseLayout.tsx文件,记录所有路由组件,在tab中展示。在不同版本的ant design pro中,props提供的路由组件的值会有差别
import {useEffect, useRef, useState} from "react";
import {Tabs} from "antd";
import { history } from 'umi';
import RightMenu from "@/components/RightMenu";
import './baseLayout.less'const BaseLayout = (props: any) => {const [activeTab, setActiveTab] = useState<any>('');const [nodeKey, setNodeKey] = useState('')const [tabItems, setTabItems] = useState<{name: string,pathname: string}[]>([]);const pathname = props.location.pathnameconst refPathObj = useRef<any>({})const refRightMenu = useRef<any>(null)const refLastPath = useRef<any>('')const setPathObj = (list: any[]) => {list.forEach((v: any) => {if (v.routes) {setPathObj(v.routes)} else if (!v.redirect) {const C = v.componentrefPathObj.current[v.path] = {name: v.name,component: <C />}}})}// 获取默认路由useEffect(() => {const routes = props.route.routes || []setPathObj(routes)}, []);// 更新tab列表useEffect(() => {const currtabItem = {name: refPathObj.current?.[pathname]?.['name'],pathname,};const isReplace = props.history.action === "REPLACE"const lastPath = refLastPath.currentif (pathname !== '/') {setTabItems((prev) => {let next = prev.find((item) => item.pathname === pathname)? prev: [...prev, currtabItem];// 如果是replace,则隐藏上一个if (isReplace) {next = next.filter((v) => v.pathname !== lastPath)}return next.slice(-5)});setActiveTab(pathname)} else {history.push('/orderManage')}refLastPath.current = pathname}, [pathname]);const clickMouseRight = (e: any) => {const $target = e.targetconst left = e.clientXconst top = e.clientYconst getNodeKey: any = (el: any) => {const nKey = el.getAttribute('data-node-key')if (nKey) {return nKey}return getNodeKey(el.parentNode)}const nKey = getNodeKey($target)setNodeKey(nKey)refRightMenu.current.setShow(true)refRightMenu.current.setStyle({left, top})}// 右击事件useEffect(() => {const right: any = document.querySelector('.base-layout-tab-menu');if (!right) return () => {}const $tabBox = right.children[0].children[0].children[0]$tabBox!.oncontextmenu = function(e: any){e.preventDefault();clickMouseRight(e)};return () => {$tabBox!.oncontextmenu = null}}, []);const removeTab = (targetKey: any) => {const next = tabItems.filter((v) => {return v.pathname !== targetKey})setTabItems(next)if (activeTab === targetKey) {history.push(next[next.length-1].pathname)}};const clickRightMenu: any = (p: any) => {const key = p.keyswitch (key) {case 'current':removeTab(nodeKey)breakcase 'other':setTabItems(prev => {const next = prev.filter((v) => {return v.pathname === nodeKey})history.push(nodeKey)return next})break}refRightMenu.current.setShow(false)}const rightMenuItem = [{key: 'current',label: '关闭',disabled: tabItems.length <= 1,onClick: clickRightMenu},{key: 'other',label: '关闭其它',disabled: tabItems.length <= 1,onClick: clickRightMenu}]return <><TabsclassName={'base-layout-tab-menu'}type="editable-card"hideAddonChange={(activeKey) => {history.push(activeKey)setActiveTab(activeKey)}}activeKey={activeTab}onEdit={removeTab}>{tabItems.length > 0 &&tabItems.map((tabItem) => {return (<Tabs.TabPanetab={tabItem.name}key={tabItem.pathname}closable={tabItems.length > 1}>{/* 替换原来直接输出的 children */}{refPathObj.current[tabItem.pathname]['component']}</Tabs.TabPane>);})}</Tabs>{/*{ props.children }*/}<RightMenuref={refRightMenu}items={rightMenuItem}/></>
}BaseLayout.displayName = 'BaseLayout'
export default BaseLayout
自定义右键操作,RightMenu.tsx文件
import React, {FC, useEffect, useImperativeHandle, useState} from "react";
import {Dropdown} from "antd";
import './index.less'interface IProps {ref: anyitems: {key: string,label: string,onClick: any}[]
}const RightMenu: FC<IProps> = React.forwardRef((props, ref) => {const [visible, setVisible] = useState(false)const [sty, setSty] = useState<{left: number,top: number}>({left: 0, top: 0})const {items} = propsuseEffect(() => {const fn = function () {setVisible(false)}document.addEventListener('click', fn)return () => {document.removeEventListener('click', fn)}}, []);const setShow = (b: boolean) => {setVisible(b)}const setStyle = (s: any) => {setSty(s)}useImperativeHandle(ref, () => ({setShow,setStyle}))return <><Dropdownmenu={{ items }}open={visible}><spanclassName={'right-menu-holder'}style={{...sty,display: visible ? 'block' : 'none',}}> </span></Dropdown></>
})RightMenu.displayName = 'RightMenu'
export default RightMenu