import React, { useState } from 'react'; import { Tooltip, Badge, Divider, Pagination } from 'antd'; import { LinkOutlined, SwapRightOutlined, AimOutlined, NodeIndexOutlined, CheckCircleOutlined } from '@ant-design/icons'; const PortPanel = ({ ports, deviceName, deviceId, cables = [], devices = [], onPortClick, compact = false, selectedPort = null, }) => { const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(48); // 默认每页48个端口 // 按端口名称排序(升序) const sortedPorts = [...ports].sort((a, b) => { // 尝试按数字部分排序,支持格式如:1/0/1, eth0/1, GigabitEthernet1/0/1 等 const extractNumbers = str => { const matches = str.match(/\d+/g); return matches ? matches.map(Number) : []; }; const numsA = extractNumbers(a.portName); const numsB = extractNumbers(b.portName); // 逐个比较数字部分 for (let i = 0; i < Math.min(numsA.length, numsB.length); i++) { if (numsA[i] !== numsB[i]) { return numsA[i] - numsB[i]; } } // 如果数字部分相同,按字符串排序 return a.portName.localeCompare(b.portName); }); // 分页数据 const totalPorts = sortedPorts.length; const startIndex = (currentPage - 1) * pageSize; const endIndex = startIndex + pageSize; const paginatedPorts = sortedPorts.slice(startIndex, endIndex); // 获取端口状态颜色 const getPortStatusColor = status => { switch (status) { case 'free': return '#6b7280'; // 灰色 - 空闲 case 'occupied': return '#10b981'; // 绿色 - 占用 case 'fault': return '#ef4444'; // 红色 - 故障 case 'disabled': return '#374151'; // 深灰色 - 禁用 default: return '#6b7280'; } }; // 获取端口状态文本 const getPortStatusText = status => { switch (status) { case 'free': return '空闲'; case 'occupied': return '已连接'; case 'fault': return '故障'; case 'disabled': return '禁用'; default: return '空闲'; } }; // 获取端口类型图标 - 使用更真实的端口符号 const getPortTypeIcon = portType => { switch (portType) { case 'RJ45': return '⬡'; // 六边形表示网口 case 'SFP': case 'SFP+': case 'SFP28': return '▭'; // 矩形表示SFP case 'QSFP': case 'QSFP28': return '▯'; // 宽矩形表示QSFP default: return '⬡'; } }; // 获取简化端口显示名称(只显示数字) const getPortDisplayName = portName => { // 提取最后的数字 const match = portName.match(/(\d+)$/); if (match) { return match[1]; } // 如果没有数字,返回原名称 return portName; }; // 获取线缆类型文本 const getCableTypeText = cableType => { const typeMap = { ethernet: '网线', fiber: '光纤', copper: '铜缆', power: '电源线', }; return typeMap[cableType] || cableType || '未知'; }; // 获取线缆类型颜色 const getCableTypeColor = cableType => { const colorMap = { ethernet: '#52c41a', fiber: '#1890ff', copper: '#faad14', power: '#ff4d4f', }; return colorMap[cableType] || '#999'; }; // 查找端口关联的接线 const findPortCable = port => { if (!cables || cables.length === 0) return null; return cables.find( cable => (cable.sourceDeviceId === deviceId && cable.sourcePortId === port.portId) || (cable.targetDeviceId === deviceId && cable.targetPortId === port.portId) || (cable.sourceDeviceId === deviceId && cable.sourcePort === port.portName) || (cable.targetDeviceId === deviceId && cable.targetPort === port.portName) ); }; // 获取连接的对端信息 const getPeerInfo = (cable, currentPort) => { if (!cable) return null; const isSource = cable.sourceDeviceId === deviceId || (cable.sourcePortId && cable.sourcePortId === currentPort.portId) || cable.sourcePort === currentPort.portName; if (isSource) { // 当前是源端,返回目标端信息 const targetDevice = devices.find(d => d.deviceId === cable.targetDeviceId); return { deviceName: targetDevice?.name || cable.targetDeviceId, deviceId: cable.targetDeviceId, portName: cable.targetPort || cable.targetPortId, direction: 'out', }; } else { // 当前是目标端,返回源端信息 const sourceDevice = devices.find(d => d.deviceId === cable.sourceDeviceId); return { deviceName: sourceDevice?.name || cable.sourceDeviceId, deviceId: cable.sourceDeviceId, portName: cable.sourcePort || cable.sourcePortId, direction: 'in', }; } }; // 渲染端口详情提示 const renderPortTooltip = port => { const cable = findPortCable(port); const peerInfo = cable ? getPeerInfo(cable, port) : null; return (
{/* 端口基本信息 */}
{port.portName}
端口类型: {port.portType}
端口速率: {port.portSpeed}
状态: {getPortStatusText(port.status)}
{port.vlanId && (
VLAN: {port.vlanId}
)} {port.description && (
描述: {port.description}
)}
{/* 接线信息 */} {cable && peerInfo && ( <>
接线详情
{/* 线缆类型和长度 */}
{getCableTypeText(cable.cableType)} {cable.cableLength && ( {cable.cableLength}m )}
{/* 连接方向 */}
{peerInfo.direction === 'out' ? '📤' : '📥'}
{peerInfo.direction === 'out' ? '输出' : '输入'}
{peerInfo.deviceName}
端口: {peerInfo.portName}
ID: {peerInfo.deviceId}
{/* 线缆标签/备注 */} {cable.label && (
标签: {cable.label}
)} {cable.notes && (
备注: {cable.notes}
)}
⚠️ 此端口已被占用,无法选择
)} {/* 空闲端口提示 */} {port.status === 'free' && !cable && ( <>
端口空闲,暂无接线
)}
); }; return (
{/* 设备标题 - compact 模式下隐藏 */} {!compact && (
🔌
{deviceName || '交换机'}
{sortedPorts.length} 个端口
{/* 状态图例 */}
空闲
已连接
故障
)} {/* 端口网格 - 固定每行24个端口 */}
{paginatedPorts.map(port => { const statusColor = getPortStatusColor(port.status); const isClickable = onPortClick && port.status !== 'disabled' && port.status !== 'occupied'; const cable = findPortCable(port); const isSelected = selectedPort?.portId === port.portId || selectedPort?.portName === port.portName; const handlePortClick = () => { if (isClickable) { onPortClick(port); } }; const portElement = (
{isSelected && (
)}
{getPortTypeIcon(port.portType)}
{cable && (
)}
{getPortDisplayName(port.portName)}
); return ( {portElement} ); })}
{/* 分页 */} {totalPorts > pageSize && (
{ setCurrentPage(page); if (size) setPageSize(size); }} showSizeChanger showQuickJumper showTotal={total => `共 ${total} 个端口`} pageSizeOptions={['24', '48', '96']} size="small" style={{ color: 'rgba(255, 255, 255, 0.8)', }} />
)} {/* 添加脉冲动画 */}
); }; export default PortPanel;