import React, { useState, useRef, useEffect, useCallback } from 'react';
import { Button, Tooltip, Dropdown } from 'antd';
import {
ArrowLeftOutlined,
CloudServerOutlined,
ReloadOutlined,
EyeOutlined,
SettingOutlined,
FullscreenOutlined,
MenuOutlined,
SwapOutlined,
} from '@ant-design/icons';
import CascadingRackPanel from './CascadingRackPanel';
import { useResponsiveLayout } from '../../hooks/useResponsiveLayout';
const ACTION_BUTTONS_CONFIG = [
{
key: 'refresh',
icon: ,
label: '刷新',
tooltip: '刷新数据',
},
{
key: 'resetView',
icon: ,
label: '重置视角',
tooltip: '重置3D视图',
},
{
key: 'config',
icon: ,
label: '显示配置',
tooltip: '配置显示字段',
},
];
const RackSelectorHeader = ({
rooms,
selectedRoomKey,
selectedRack,
onRackSelect,
onPrevRack,
onNextRack,
racksInSelectedRoom,
deviceSlideEnabled,
onDeviceSlideToggle,
onRefresh,
onResetView,
onOpenConfig,
onBack,
}) => {
const [selectorVisible, setSelectorVisible] = useState(false);
const selectorRef = useRef(null);
const { screenSize, config, isMobile } = useResponsiveLayout();
useEffect(() => {
const handleClickOutside = event => {
if (selectorRef.current && !selectorRef.current.contains(event.target)) {
setSelectorVisible(false);
}
};
if (selectorVisible) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [selectorVisible]);
const handleRackSelect = useCallback(
(rack, room) => {
onRackSelect(rack, room);
setSelectorVisible(false);
},
[onRackSelect]
);
const handleKeyDown = useCallback(e => {
if (e.key === 'Escape') {
setSelectorVisible(false);
}
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
setSelectorVisible(prev => !prev);
}
}, []);
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [handleKeyDown]);
const selectedRoom = rooms.find(r => r.key === selectedRoomKey);
const displayText = selectedRack
? `${selectedRoom?.name || ''} / ${selectedRack.name}`
: '选择机房 / 机柜';
const canNavigatePrev = racksInSelectedRoom && racksInSelectedRoom.length > 1 && selectedRack;
const canNavigateNext = racksInSelectedRoom && racksInSelectedRoom.length > 1 && selectedRack;
const getCurrentRackIndex = () => {
if (!selectedRack || !racksInSelectedRoom) return -1;
return racksInSelectedRoom.findIndex(r => r.rackId === selectedRack.rackId);
};
const dropdownMenuItems = ACTION_BUTTONS_CONFIG.map(btn => ({
key: btn.key,
label: (
{btn.icon}
{btn.label}
),
onClick: () => {
switch (btn.key) {
case 'refresh':
onRefresh();
break;
case 'resetView':
onResetView();
break;
case 'config':
onOpenConfig();
break;
default:
break;
}
},
}));
const renderLeftSection = () => (
}
onClick={onBack}
className="header-icon-btn"
style={{ marginRight: 8 }}
/>
3D 机柜可视化
);
const renderSelector = () => {
const panelWidth = isMobile ? '100%' : 520;
return (
setSelectorVisible(prev => !prev)}
style={{
display: 'flex',
alignItems: 'center',
background: selectorVisible ? 'rgba(59, 130, 246, 0.15)' : 'rgba(255, 255, 255, 0.08)',
border: selectorVisible
? '1px solid rgba(59, 130, 246, 0.5)'
: '1px solid rgba(255, 255, 255, 0.12)',
borderRadius: 10,
padding: '8px 14px',
cursor: 'pointer',
transition: 'all 0.2s ease',
minHeight: 40,
}}
>
{displayText}
{canNavigatePrev && (
{
e.stopPropagation();
onPrevRack();
}}
style={{
padding: '4px 8px',
borderRadius: 6,
background: 'rgba(255,255,255,0.08)',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
color: 'rgba(255,255,255,0.7)',
fontSize: 12,
transition: 'all 0.15s',
}}
onMouseEnter={e => {
e.currentTarget.style.background = 'rgba(255,255,255,0.15)';
}}
onMouseLeave={e => {
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
}}
>
←
)}
{canNavigateNext && (
{
e.stopPropagation();
onNextRack();
}}
style={{
padding: '4px 8px',
borderRadius: 6,
background: 'rgba(255,255,255,0.08)',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
color: 'rgba(255,255,255,0.7)',
fontSize: 12,
transition: 'all 0.15s',
}}
onMouseEnter={e => {
e.currentTarget.style.background = 'rgba(255,255,255,0.15)';
}}
onMouseLeave={e => {
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
}}
>
→
)}
▼
setSelectorVisible(false)}
triggerRef={selectorRef}
/>
);
};
const renderActionButton = (btn, index) => {
if (isMobile && btn.key === 'config') {
return null;
}
return (
);
};
const renderDeviceSlideToggle = () => {
if (!config.showDeviceSlide) return null;
return (
{!isMobile && (
设备弹出
)}
onDeviceSlideToggle(!deviceSlideEnabled)}
style={{
width: 32,
height: 18,
borderRadius: 9,
background: deviceSlideEnabled ? '#22c55e' : 'rgba(255,255,255,0.2)',
position: 'relative',
cursor: 'pointer',
transition: 'background 0.2s',
}}
>
);
};
const renderRightSection = () => {
if (config.collapseActions) {
return (
{renderDeviceSlideToggle()}
}
className="header-icon-btn"
style={{ borderRadius: 8 }}
/>
);
}
return (
{renderDeviceSlideToggle()}
{ACTION_BUTTONS_CONFIG.map(renderActionButton)}
);
};
return (
{renderLeftSection()}
{renderSelector()}
{renderRightSection()}
);
};
export default RackSelectorHeader;