import { CloseOutlined, ReloadOutlined } from '@ant-design/icons'; import { PageContainer, PageContainerProps } from '@ant-design/pro-components'; import { history, useLocation } from '@umijs/max'; import type { MenuProps } from 'antd'; import { Dropdown, message, Space, Tabs } from 'antd'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import './MyPageContainer.scss'; export interface TabItem { key: string; label: string; path: string; closable?: boolean; } export interface MyPageContainerProps extends PageContainerProps { enableTabs?: boolean; tabKey?: string; tabLabel?: string; onTabChange?: (activeKey: string) => void; } // 全局标签页状态管理 class TabsManager { private tabs: TabItem[] = []; private activeKey: string = ''; private listeners: Set<() => void> = new Set(); subscribe(listener: () => void) { this.listeners.add(listener); return () => this.listeners.delete(listener); } private notify() { this.listeners.forEach((listener) => listener()); } getTabs() { return this.tabs; } getActiveKey() { return this.activeKey; } addTab(tab: TabItem) { const existingIndex = this.tabs.findIndex((t) => t.key === tab.key); if (existingIndex === -1) { this.tabs.push(tab); } else { this.tabs[existingIndex] = { ...this.tabs[existingIndex], ...tab }; } this.activeKey = tab.key; this.notify(); } removeTab(targetKey: string) { const targetIndex = this.tabs.findIndex((tab) => tab.key === targetKey); if (targetIndex === -1) return; const newTabs = this.tabs.filter((tab) => tab.key !== targetKey); if (newTabs.length === 0) { this.tabs = []; this.activeKey = ''; history.push('/'); } else { this.tabs = newTabs; if (this.activeKey === targetKey) { // 如果关闭的是当前激活的标签,激活相邻的标签 const newActiveKey = targetIndex > 0 ? newTabs[targetIndex - 1].key : newTabs[0].key; this.activeKey = newActiveKey; const targetTab = newTabs.find((tab) => tab.key === newActiveKey); if (targetTab) { history.push(targetTab.path); } } } this.notify(); } setActiveKey(key: string) { this.activeKey = key; const targetTab = this.tabs.find((tab) => tab.key === key); if (targetTab) { history.push(targetTab.path); } this.notify(); } closeOtherTabs(currentKey: string) { const currentTab = this.tabs.find((tab) => tab.key === currentKey); if (currentTab) { this.tabs = [currentTab]; this.activeKey = currentKey; this.notify(); } } closeLeftTabs(currentKey: string) { const currentIndex = this.tabs.findIndex((tab) => tab.key === currentKey); if (currentIndex > 0) { this.tabs = this.tabs.slice(currentIndex); this.notify(); } } closeRightTabs(currentKey: string) { const currentIndex = this.tabs.findIndex((tab) => tab.key === currentKey); if (currentIndex !== -1) { this.tabs = this.tabs.slice(0, currentIndex + 1); this.notify(); } } refreshTab(key: string) { // 通过路由跳转的方式刷新当前标签页,避免整个页面刷新导致标签页状态丢失 const targetTab = this.tabs.find((tab) => tab.key === key); if (targetTab) { const originalPath = targetTab.path; // 移除可能存在的刷新参数,确保获取干净的原始路径 const cleanPath = originalPath .replace(/[?&]_refresh=\d+/g, '') .replace(/\?$/, ''); // 添加时间戳参数强制刷新 const refreshPath = cleanPath.includes('?') ? `${cleanPath}&_refresh=${Date.now()}` : `${cleanPath}?_refresh=${Date.now()}`; // 先更新为带刷新参数的路径并跳转 targetTab.path = refreshPath; this.setActiveKey(key); // 延迟恢复原始路径,确保路由跳转完成 setTimeout(() => { // 恢复为干净的原始路径 targetTab.path = cleanPath; // 再次跳转到干净路径,移除URL中的刷新参数 history.push(cleanPath); this.notify(); }, 300); } } } // 全局标签页管理器实例 const tabsManager = new TabsManager(); export function MyPageContainer({ title, children, enableTabs = true, tabKey, tabLabel, onTabChange, ...rest }: MyPageContainerProps) { const location = useLocation(); const [tabs, setTabs] = useState([]); const [activeKey, setActiveKey] = useState(''); const contextMenuRef = useRef<{ x: number; y: number; targetKey: string; } | null>(null); // 订阅标签页状态变化 useEffect(() => { const unsubscribe = tabsManager.subscribe(() => { setTabs([...tabsManager.getTabs()]); setActiveKey(tabsManager.getActiveKey()); }); // 初始化状态 setTabs([...tabsManager.getTabs()]); setActiveKey(tabsManager.getActiveKey()); return () => { unsubscribe(); }; }, []); // 当组件挂载时,添加当前页面到标签页 useEffect(() => { if (enableTabs && tabKey && tabLabel) { tabsManager.addTab({ key: tabKey, label: tabLabel, path: location.pathname + location.search, closable: true, }); } }, [enableTabs, tabKey, tabLabel, location.pathname, location.search]); const handleTabChange = useCallback( (key: string) => { tabsManager.setActiveKey(key); onTabChange?.(key); }, [onTabChange], ); const handleTabEdit = useCallback( ( targetKey: string | React.MouseEvent | React.KeyboardEvent, action: 'add' | 'remove', ) => { if (action === 'remove' && typeof targetKey === 'string') { tabsManager.removeTab(targetKey); } }, [], ); // 右键菜单配置 const getContextMenuItems = useCallback( (targetKey: string): MenuProps['items'] => { const currentIndex = tabs.findIndex((tab) => tab.key === targetKey); const hasLeftTabs = currentIndex > 0; const hasRightTabs = currentIndex < tabs.length - 1; const hasOtherTabs = tabs.length > 1; return [ { key: 'refresh', label: '刷新', icon: , onClick: () => tabsManager.refreshTab(targetKey), }, { key: 'close', label: '关闭', icon: , onClick: () => tabsManager.removeTab(targetKey), }, { type: 'divider', }, { key: 'closeOthers', label: '关闭其他', disabled: !hasOtherTabs, onClick: () => { tabsManager.closeOtherTabs(targetKey); message.success('已关闭其他标签页'); }, }, { key: 'closeLeft', label: '关闭左侧', disabled: !hasLeftTabs, onClick: () => { tabsManager.closeLeftTabs(targetKey); message.success('已关闭左侧标签页'); }, }, { key: 'closeRight', label: '关闭右侧', disabled: !hasRightTabs, onClick: () => { tabsManager.closeRightTabs(targetKey); message.success('已关闭右侧标签页'); }, }, ]; }, [tabs], ); // 自定义标签页渲染 const renderTabBar: React.ComponentProps['renderTabBar'] = ( props, DefaultTabBar, ) => { return ( {(node) => { const tabKey = node.key as string; return (
{node}
); }}
); }; if (!enableTabs || tabs.length === 0) { return ( {children} ); } return ( ({ key: tab.key, label: tab.label, closable: tab.closable, }))} style={{ marginBottom: 0, }} className="tabs-header-only" /> ), style: { backgroundColor: '#FFF' }, }} token={{ paddingBlockPageContainerContent: 0, paddingInlinePageContainerContent: 0, }} {...rest} > {children} ); } // 导出标签页管理器,供其他组件使用 export { tabsManager };