import React, { useState, useEffect, useCallback } from 'react'; import { Table, Button, Space, Tag, Tooltip, Popconfirm, Empty, Spin, Badge, Collapse, Card } from 'antd'; import { PlusOutlined, DeleteOutlined, ReloadOutlined, ApiOutlined, CloudServerOutlined, FolderOutlined } from '@ant-design/icons'; import axios from 'axios'; import PortCreateModal from './PortCreateModal'; import NetworkCardCreateModal from './NetworkCardCreateModal'; const { Panel } = Collapse; const designTokens = { colors: { primary: { main: '#667eea' }, success: '#10b981', error: '#ef4444', warning: '#f59e0b' } }; function NetworkCardPanel({ deviceId, deviceName, onRefresh, refreshTrigger }) { const [cards, setCards] = useState([]); const [networkCards, setNetworkCards] = useState([]); const [loading, setLoading] = useState(false); const [createPortModalVisible, setCreatePortModalVisible] = useState(false); const [createCardModalVisible, setCreateCardModalVisible] = useState(false); const [selectedCard, setSelectedCard] = useState(null); const [expandedCards, setExpandedCards] = useState([]); const fetchData = useCallback(async () => { if (!deviceId) return; try { setLoading(true); const [cardsResponse, networkCardsResponse] = await Promise.all([ axios.get(`/api/network-cards/device/${deviceId}/with-ports`), axios.get(`/api/network-cards/device/${deviceId}`) ]); const cardsData = cardsResponse.data || []; setCards(cardsData); setNetworkCards(networkCardsResponse.data || []); const initialExpanded = cardsData .filter(card => card.ports && card.ports.length > 0) .map(card => card.nicId); setExpandedCards(initialExpanded); } catch (error) { console.error('获取网卡数据失败:', error); setCards([]); setNetworkCards([]); } finally { setLoading(false); } }, [deviceId]); useEffect(() => { fetchData(); }, [fetchData, refreshTrigger]); const handleDeleteCard = useCallback(async (card) => { try { await axios.delete(`/api/network-cards/${card.nicId}`); import('antd').then(({ message }) => message.success('网卡删除成功')); fetchData(); onRefresh?.(); } catch (error) { import('antd').then(({ message }) => message.error(error.response?.data?.error || '网卡删除失败')); } }, [fetchData, onRefresh]); const handleDeletePort = useCallback(async (port) => { try { await axios.delete(`/api/device-ports/${port.portId}`); import('antd').then(({ message }) => message.success('端口删除成功')); fetchData(); onRefresh?.(); } catch (error) { import('antd').then(({ message }) => message.error('端口删除失败')); } }, [fetchData, onRefresh]); const handleCreateCardSuccess = useCallback(() => { fetchData(); onRefresh?.(); }, [fetchData, onRefresh]); const handleCreatePortSuccess = useCallback(() => { fetchData(); onRefresh?.(); }, [fetchData, onRefresh]); const handleExpand = (nicId) => { setExpandedCards(prev => { if (prev.includes(nicId)) { return prev.filter(id => id !== nicId); } return [...prev, nicId]; }); }; const getStatusTag = (status) => { const config = { free: { color: 'success', text: '空闲' }, occupied: { color: 'processing', text: '占用' }, fault: { color: 'error', text: '故障' }, normal: { color: 'success', text: '正常' }, warning: { color: 'warning', text: '警告' }, offline: { color: 'default', text: '离线' } }; const { color, text } = config[status] || { color: 'default', text: status }; return {text}; }; const getTypeTag = (type) => { const config = { 'RJ45': { color: 'blue', text: 'RJ45' }, 'SFP': { color: 'green', text: 'SFP' }, 'SFP+': { color: 'cyan', text: 'SFP+' }, 'SFP28': { color: 'purple', text: 'SFP28' }, 'QSFP': { color: 'orange', text: 'QSFP' }, 'QSFP28': { color: 'red', text: 'QSFP28' } }; const { color, text } = config[type] || { color: 'default', text: type }; return {text}; }; const renderPortTable = (ports, nicId) => { const columns = [ { title: '端口名称', dataIndex: 'portName', key: 'portName', width: 120, render: (text) => {text} }, { title: '类型', dataIndex: 'portType', key: 'portType', width: 80, render: (type) => getTypeTag(type) }, { title: '速率', dataIndex: 'portSpeed', key: 'portSpeed', width: 70 }, { title: '状态', dataIndex: 'status', key: 'status', width: 70, render: (status) => getStatusTag(status) }, { title: 'VLAN', dataIndex: 'vlanId', key: 'vlanId', width: 60, render: (vlanId) => vlanId || '-' }, { title: '操作', key: 'action', width: 80, render: (_, record) => ( handleDeletePort(record)} okText="确定" cancelText="取消" > ) } ]; return ( ); }; const renderCardHeader = (card) => { const stats = card.stats || { free: 0, occupied: 0, fault: 0, total: 0 }; return (
{card.isUngrouped ? : }
{card.name} {card.slotNumber && 插槽 {card.slotNumber}}
{card.description || (card.isUngrouped ? '未分配到网卡的端口' : '网卡')}
空闲 占用 故障 {!card.isUngrouped && ( handleDeleteCard(card)} okText="确定" cancelText="取消" > )}
); }; if (loading) { return (
); } const totalStats = cards.reduce((acc, card) => { const stats = card.stats || {}; acc.total += stats.total || 0; acc.free += stats.free || 0; acc.occupied += stats.occupied || 0; acc.fault += stats.fault || 0; return acc; }, { total: 0, free: 0, occupied: 0, fault: 0 }); return (
个网卡 个端口
{cards.length === 0 ? (
该设备暂无网卡和端口
} >
) : ( setExpandedCards(keys)} expandIconPosition="end" style={{ background: 'transparent' }} > {cards.map((card) => ( {card.ports && card.ports.length > 0 ? ( renderPortTable(card.ports, card.nicId) ) : (
该{card.isUngrouped ? '分组' : '网卡'}暂无端口
)}
))}
)} setCreateCardModalVisible(false)} onSuccess={handleCreateCardSuccess} /> { setCreatePortModalVisible(false); setSelectedCard(null); }} onSuccess={handleCreatePortSuccess} defaultNicId={selectedCard?.nicId} />
); } export default React.memo(NetworkCardPanel);