import React, { useState, useCallback, useEffect, useMemo } from 'react'; import { Modal, Form, Input, Select, InputNumber, message, Space, Button, Tooltip, Alert, Tag, } from 'antd'; import { PlusOutlined, InfoCircleOutlined } from '@ant-design/icons'; import axios from 'axios'; const { Option } = Select; const { TextArea } = Input; const designTokens = { colors: { primary: { main: '#667eea', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', }, }, borderRadius: { medium: '10px', }, }; function parsePortRange(portName) { if (!portName || typeof portName !== 'string') { return null; } const trimmed = portName.trim(); if (!trimmed.includes('-')) { return null; } const [startPart, endPart] = trimmed.split('-').map(s => s.trim()); if (!startPart || !endPart) { return null; } const startNumMatch = startPart.match(/(\d+)$/); const endNumMatch = endPart.match(/(\d+)$/); if (!startNumMatch || !endNumMatch) { return null; } const startNum = parseInt(startNumMatch[1], 10); const endNum = parseInt(endNumMatch[1], 10); if (startNum >= endNum || endNum - startNum > 1000) { return null; } const prefix = startPart.replace(startNumMatch[0], ''); const portCount = endNum - startNum + 1; const ports = []; for (let i = 0; i < portCount; i++) { const num = startNum + i; ports.push(`${prefix}${num}`); } return { isRange: true, prefix, startNum, endNum, portCount, ports, }; } function generatePortNames(portName) { const result = parsePortRange(portName); if (result && result.isRange) { return result.ports; } return [portName]; } function PortCreateModal({ device, visible, onClose, onSuccess, defaultNicId, networkCards = [] }) { const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [previewPorts, setPreviewPorts] = useState([]); const [showPreview, setShowPreview] = useState(false); const [nicList, setNicList] = useState([]); const prevVisibleRef = React.useRef(false); useEffect(() => { // 只有当 visible 从 false 变为 true 时才执行初始化 if (visible && !prevVisibleRef.current) { setPreviewPorts([]); setShowPreview(false); form.resetFields(); if (defaultNicId) { form.setFieldsValue({ nicId: defaultNicId }); } if (device?.deviceId && (!networkCards || networkCards.length === 0)) { fetchNetworkCards(); } else if (networkCards && networkCards.length > 0) { setNicList(networkCards); } } prevVisibleRef.current = visible; }, [visible, device, defaultNicId, networkCards, form]); const fetchNetworkCards = async () => { try { const response = await axios.get(`/api/network-cards/device/${device.deviceId}`); setNicList(response.data || []); } catch (error) { console.error('获取网卡列表失败:', error); setNicList([]); } }; const handlePortNameChange = useCallback(e => { const value = e.target.value; const ports = generatePortNames(value); if (ports.length > 1) { setPreviewPorts(ports.slice(0, 20)); setShowPreview(true); } else { setPreviewPorts([]); setShowPreview(false); } }, []); const handleSubmit = useCallback(async () => { try { const values = await form.validateFields(); setLoading(true); const portNames = generatePortNames(values.portName); if (portNames.length === 1) { await axios.post('/api/device-ports', { deviceId: device.deviceId, nicId: values.nicId || null, portName: portNames[0], portType: values.portType, portSpeed: values.portSpeed, vlanId: values.vlanId, status: values.status, description: values.description, }); message.success('端口创建成功'); } else { const portsData = portNames.map((portName, index) => ({ portId: `PORT-${Date.now()}-${index}`, deviceId: device.deviceId, nicId: values.nicId || null, portName, portType: values.portType, portSpeed: values.portSpeed, vlanId: values.vlanId, status: values.status, description: values.description, })); await axios.post('/api/device-ports/batch', { ports: portsData }); message.success(`成功创建 ${portNames.length} 个端口`); } form.resetFields(); setPreviewPorts([]); setShowPreview(false); onSuccess?.(); onClose(); } catch (error) { if (error.errorFields) { return; } message.error(error.response?.data?.error || '端口创建失败'); console.error('创建端口失败:', error); } finally { setLoading(false); } }, [device, form, onClose, onSuccess]); const handleCancel = useCallback(() => { form.resetFields(); setPreviewPorts([]); setShowPreview(false); onClose(); }, [form, onClose]); const portCount = previewPorts.length || (form.getFieldValue('portName') && !showPreview ? 1 : 0); return ( 新增端口 - {device?.name || '设备'} {portCount > 1 && {portCount} 个端口} } open={visible} onOk={handleSubmit} onCancel={handleCancel} confirmLoading={loading} okText={portCount > 1 ? `创建 ${portCount} 个端口` : '创建'} cancelText="取消" width={560} zIndex={1050} styles={{ body: { padding: '20px 24px' } }} >
所属网卡 } > 端口名称 } rules={[ { required: true, message: '请输入端口名称' }, { pattern: /^[\w\/:\-]+$/, message: '端口名称格式不正确', }, { validator: (_, value) => { if (!value) return Promise.resolve(); const ports = generatePortNames(value); if (ports.length > 1000) { return Promise.reject(new Error('单次最多创建1000个端口')); } return Promise.resolve(); }, }, ]} > { // 确保 Form 值更新 form.setFieldValue('portName', e.target.value); handlePortNameChange(e); }} /> {showPreview && ( {previewPorts.map((port, index) => ( {port} ))} {previewPorts.length < parsePortRange(form.getFieldValue('portName'))?.portCount && ( ...等 )} } type="info" showIcon style={{ marginBottom: 16 }} /> )}