import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Modal, Form, Select, Input, Button, message, Space, Spin } from 'antd'; import { SwapOutlined } from '@ant-design/icons'; import axios from 'axios'; const { Option } = Select; const debounce = (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; const CableCreateModal = ({ visible, onClose, onSuccess, sourceDevice }) => { const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [devices, setDevices] = useState([]); const [sourcePorts, setSourcePorts] = useState([]); const [targetPorts, setTargetPorts] = useState([]); const [fetchingDevices, setFetchingDevices] = useState(false); const prevVisibleRef = useRef(false); const fetchDevices = useCallback(async (keyword = '') => { try { setFetchingDevices(true); const params = { pageSize: 50 }; if (keyword && keyword.trim()) { params.keyword = keyword.trim(); } const response = await axios.get('/api/devices', { params }); setDevices(response.data.devices || []); } catch (error) { console.error('Failed to fetch devices:', error); message.error('获取设备列表失败'); } finally { setFetchingDevices(false); } }, []); const handleDeviceSearch = useCallback( debounce(value => { fetchDevices(value); }, 300), [fetchDevices] ); // Initialize when visible changes from false to true useEffect(() => { if (visible && !prevVisibleRef.current) { form.resetFields(); if (sourceDevice) { form.setFieldsValue({ sourceDeviceId: sourceDevice.deviceId || sourceDevice.id, }); fetchDevicePorts(sourceDevice.deviceId || sourceDevice.id, 'source'); } fetchDevices(); } prevVisibleRef.current = visible; }, [visible, sourceDevice, form, fetchDevices]); const fetchDevicePorts = async (deviceId, type) => { if (!deviceId) { if (type === 'source') setSourcePorts([]); else setTargetPorts([]); return; } try { const response = await axios.get(`/api/device-ports/device/${deviceId}`); const ports = response.data || []; if (type === 'source') { setSourcePorts(ports); } else { setTargetPorts(ports); } } catch (error) { console.error(`Failed to fetch ${type} ports:`, error); message.error('获取端口列表失败'); } }; const handleSourceDeviceChange = deviceId => { form.setFieldsValue({ sourcePort: undefined }); fetchDevicePorts(deviceId, 'source'); }; const handleTargetDeviceChange = deviceId => { form.setFieldsValue({ targetPort: undefined }); fetchDevicePorts(deviceId, 'target'); }; const handleSubmit = async () => { try { const values = await form.validateFields(); setLoading(true); // Logic to ensure Switch is always Source if possible (for Cable Management view consistency) const sourceDev = devices.find(d => d.deviceId === values.sourceDeviceId); const targetDev = devices.find(d => d.deviceId === values.targetDeviceId); let payload = { ...values }; // If Source is NOT Switch AND Target IS Switch, swap them if (sourceDev && targetDev && sourceDev.type !== 'switch' && targetDev.type === 'switch') { payload = { ...values, sourceDeviceId: values.targetDeviceId, sourcePort: values.targetPort, targetDeviceId: values.sourceDeviceId, targetPort: values.sourcePort, }; } await axios.post('/api/cables', payload); message.success('接线创建成功'); onSuccess?.(); onClose(); } catch (error) { if (error.errorFields) return; console.error('Failed to create cable:', error); message.error('接线创建失败'); } finally { setLoading(false); } }; return ( 新增接线 } open={visible} onCancel={onClose} onOk={handleSubmit} confirmLoading={loading} width={600} maskClosable={false} >
{/* Source Side */}
源设备 (起点)
{/* Target Side */}
目标设备 (终点)
); }; export default CableCreateModal;