import React, { useState, useEffect, useCallback } from 'react'; import { Badge, Space, Typography, Spin, Empty, Button, Tooltip, Tag, Modal } from 'antd'; import { CloudServerOutlined, PlusOutlined, ReloadOutlined, ApiOutlined, ThunderboltOutlined, DesktopOutlined, UsbOutlined, MonitorOutlined, SettingOutlined } from '@ant-design/icons'; import PortPanel from './PortPanel'; import axios from 'axios'; const { Text } = Typography; const designTokens = { colors: { primary: { main: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, success: '#10b981', error: '#ef4444', warning: '#f59e0b', metal: { light: '#9ca3af', DEFAULT: '#6b7280', dark: '#4b5563' }, slot: { empty: '#d1d5db', occupied: '#3b82f6' } } }; /** * 服务器背板可视化组件 * 按照真实服务器背板布局展示网卡和端口 * * @param {string} deviceId - 设备ID * @param {string} deviceName - 设备名称 * @param {Object[]} cables - 接线列表 * @param {Object[]} allDevices - 所有设备列表 * @param {Function} onPortClick - 端口点击回调 * @param {Function} onManageNetworkCards - 网卡管理回调 */ const ServerBackplanePanel = ({ deviceId, deviceName, cables, allDevices, onPortClick, onManageNetworkCards }) => { const [cards, setCards] = useState([]); const [loading, setLoading] = useState(false); const [selectedSlot, setSelectedSlot] = useState(null); // 获取网卡及端口数据 const fetchData = useCallback(async () => { if (!deviceId) return; try { setLoading(true); const response = await axios.get(`/api/network-cards/device/${deviceId}/with-ports`); const cardsData = response.data || []; setCards(cardsData); } catch (error) { console.error('获取网卡数据失败:', error); setCards([]); } finally { setLoading(false); } }, [deviceId]); useEffect(() => { fetchData(); }, [fetchData]); // 按类型和槽位号对网卡进行分类 const categorizeCards = () => { const onboard = []; // 板载网卡 const management = []; // 管理口 const expansionSlots = []; // 扩展插槽 cards.forEach(card => { const slotNum = card.slotNumber; const name = (card.name || '').toLowerCase(); // 判断网卡类型 if (name.includes('idrac') || name.includes('ilo') || name.includes('bmc') || name.includes('mgmt') || name.includes('管理')) { management.push({ ...card, type: 'management' }); } else if (slotNum === 0 || name.includes('onboard') || name.includes('板载') || name.includes('内置')) { onboard.push({ ...card, type: 'onboard' }); } else { expansionSlots.push({ ...card, type: 'expansion', slotIndex: slotNum }); } }); // 按槽位号排序扩展插槽 expansionSlots.sort((a, b) => (a.slotNumber || 0) - (b.slotNumber || 0)); return { onboard, management, expansionSlots }; }; const { onboard, management, expansionSlots } = categorizeCards(); // 渲染管理口区域(左侧) const renderManagementArea = () => { const mgmtCard = management[0]; return (
MGMT
{mgmtCard ? (
setSelectedSlot(mgmtCard)} style={{ width: '48px', height: '48px', background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: '4px', border: `2px solid ${mgmtCard.ports?.length > 0 ? designTokens.colors.success : '#6b7280'}`, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', transition: 'all 0.2s', boxShadow: '0 2px 4px rgba(0,0,0,0.3)' }} > {mgmtCard.ports?.length || 0}口
) : (
)} {/* 其他接口占位 */}
VGA
USB
); }; // 渲染板载网卡区域 const renderOnboardArea = () => { const onboardCard = onboard[0]; return (
板载网卡 (Onboard) {onboardCard && ( )}
{onboardCard ? (
setSelectedSlot(onboardCard)} style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: '6px', padding: '12px', border: `2px solid ${onboardCard.ports?.length > 0 ? designTokens.colors.primary.main : '#6b7280'}`, cursor: 'pointer', transition: 'all 0.2s' }} > {/* 4个RJ45端口布局 */}
{[0, 1, 2, 3].map((idx) => { const port = onboardCard.ports?.[idx]; const hasPort = !!port; const isOccupied = hasPort && port.status === 'occupied'; return (
{/* LED指示灯 */}
{hasPort ? idx + 1 : '-'}
); })}
) : (
添加板载网卡
)}
); }; // 渲染扩展插槽区域 const renderExpansionSlots = () => { // 标准2U服务器通常有4-8个PCIe插槽 const totalSlots = 6; const slots = []; for (let i = 1; i <= totalSlots; i++) { const card = expansionSlots.find(c => c.slotNumber === i); slots.push({ slotNumber: i, card }); } return (
PCIe 扩展插槽 /{totalSlots}
{slots.map(({ slotNumber, card }) => (
card && setSelectedSlot(card)} style={{ width: '70px', height: '90px', background: card ? 'linear-gradient(145deg, #1f2937 0%, #111827 100%)' : '#374151', borderRadius: '4px', border: `2px solid ${card ? designTokens.colors.slot.occupied : designTokens.colors.slot.empty}`, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'space-between', padding: '6px', cursor: card ? 'pointer' : 'default', transition: 'all 0.2s', boxShadow: card ? '0 2px 8px rgba(59, 130, 246, 0.3)' : 'none' }} > Slot {slotNumber} {card ? ( <>
{card.ports?.slice(0, 4).map((port, idx) => (
))} {card.ports?.length > 4 && ( +{card.ports.length - 4} )}
{card.ports?.length || 0}口 ) : ( <>
空闲 )}
))}
); }; // 渲染电源区域(右侧) const renderPowerArea = () => { return (
电源 {[1, 2].map((psu) => (
PSU {psu}
))}
); }; if (loading) { return (
); } return (
{/* 工具栏 */}
!c.isUngrouped).length} style={{ backgroundColor: designTokens.colors.primary.main }} /> 个网卡 acc + (card.ports?.length || 0), 0)} style={{ backgroundColor: '#667eea' }} /> 个端口
{/* 服务器背板主体 */}
{/* 服务器标识 */}
{deviceName || '服务器'} - 背板视图
{/* 背板布局 */}
{/* 左侧:管理口区域 */} {renderManagementArea()} {/* 中间左:板载网卡 */} {renderOnboardArea()} {/* 中间:扩展插槽 */} {renderExpansionSlots()} {/* 右侧:电源 */} {renderPowerArea()}
{/* 选中插槽的端口详情模态框 */} {selectedSlot?.name} {selectedSlot?.slotNumber > 0 && ` (Slot ${selectedSlot.slotNumber})`}
} open={!!selectedSlot} onCancel={() => setSelectedSlot(null)} footer={null} width={700} destroyOnClose > {selectedSlot && (
类型: {selectedSlot.type === 'onboard' ? '板载网卡' : selectedSlot.type === 'management' ? '管理口' : '扩展网卡'} {selectedSlot.description && 描述: {selectedSlot.description}}
端口统计: 空闲: {selectedSlot.stats?.free || 0} 占用: {selectedSlot.stats?.occupied || 0} {selectedSlot.stats?.fault > 0 && 故障: {selectedSlot.stats.fault}} 总计: {selectedSlot.ports?.length || 0}
{selectedSlot.ports && selectedSlot.ports.length > 0 ? ( ) : ( 该网卡暂无端口
} /> )}
)}
); }; export default ServerBackplanePanel;