import React, { useState, useCallback, useMemo, useEffect } from 'react';
import {
Drawer,
Tabs,
Tag,
Space,
Typography,
Empty,
Card,
Tooltip,
Button,
Popconfirm,
Table,
Badge,
Row,
Col,
Divider,
} from 'antd';
import {
ApiOutlined,
CloudServerOutlined,
EnvironmentOutlined,
EditOutlined,
PlusCircleOutlined,
DeleteOutlined,
FileTextOutlined,
ToolOutlined,
EyeOutlined,
DesktopOutlined,
FieldTimeOutlined,
InfoCircleOutlined,
LinkOutlined,
} from '@ant-design/icons';
import NetworkCardPanel from './NetworkCardPanel';
import { deviceAPI } from '../api';
import dayjs from 'dayjs';
const { Text, Title } = Typography;
const designTokens = {
colors: {
primary: '#1890ff',
success: '#52c41a',
error: '#f5222d',
warning: '#faad14',
info: '#13c2c2',
gray: '#8c8c8c',
bgLight: '#f6ffed',
bgBlue: '#e6f7ff',
bgGray: '#f5f5f5',
bgOrange: '#fff7e6',
},
};
function DeviceDetailDrawer({
device,
visible,
onClose,
cables,
onRefreshCables,
onEdit,
onAddNic,
onAddPort,
onAddCable,
onDeleteCable,
tooltipFields,
refreshTrigger,
onViewTicket,
onCreateTicket,
}) {
const [activeTab, setActiveTab] = useState('ports');
const [tickets, setTickets] = useState([]);
const [ticketsLoading, setTicketsLoading] = useState(false);
const [ticketsPagination, setTicketsPagination] = useState({
current: 1,
pageSize: 5,
total: 0,
});
// 获取设备关联的工单列表
const fetchDeviceTickets = useCallback(async (page = 1, pageSize = 5) => {
if (!device?.deviceId) return;
setTicketsLoading(true);
try {
const response = await deviceAPI.getTickets(device.deviceId, {
page,
pageSize,
});
setTickets(response.data || []);
setTicketsPagination({
current: response.page || 1,
pageSize: response.pageSize || 5,
total: response.total || 0,
});
} catch (error) {
console.error('获取设备工单失败:', error);
} finally {
setTicketsLoading(false);
}
}, [device?.deviceId]);
// 当设备变化或标签页切换到工单时,加载工单数据
useEffect(() => {
if (visible && device?.deviceId && activeTab === 'tickets') {
fetchDeviceTickets(1, 5);
}
}, [visible, device?.deviceId, activeTab, fetchDeviceTickets]);
// 工单表格列定义
const ticketColumns = useMemo(() => [
{
title: '工单编号',
dataIndex: 'ticketId',
key: 'ticketId',
width: 120,
render: (text) => {text},
},
{
title: '标题',
dataIndex: 'title',
key: 'title',
ellipsis: true,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 90,
render: (status) => {
const statusConfig = {
pending: { color: 'warning', text: '待处理' },
processing: { color: 'processing', text: '处理中' },
completed: { color: 'success', text: '已完成' },
closed: { color: 'default', text: '已关闭' },
};
const config = statusConfig[status] || { color: 'default', text: status };
return ;
},
},
{
title: '优先级',
dataIndex: 'priority',
key: 'priority',
width: 80,
render: (priority) => {
const priorityConfig = {
low: { color: 'success', text: '低' },
medium: { color: 'warning', text: '中' },
high: { color: 'error', text: '高' },
critical: { color: 'purple', text: '紧急' },
};
const config = priorityConfig[priority] || { color: 'default', text: priority };
return {config.text};
},
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
width: 150,
render: (date) => {
if (!date) return '-';
return dayjs(date).format('YYYY-MM-DD HH:mm');
},
},
{
title: '操作',
key: 'action',
width: 80,
render: (_, record) => (
}
onClick={() => onViewTicket?.(record)}
>
查看
),
},
], [onViewTicket]);
const deviceCables = useMemo(() => {
if (!device || !cables) return [];
return cables.filter(
c => c.sourceDeviceId === device.deviceId || c.targetDeviceId === device.deviceId
);
}, [device, cables]);
const getStatusTag = useCallback(status => {
const config = {
running: { color: 'success', text: '运行中' },
normal: { color: 'success', text: '正常' },
warning: { color: 'warning', text: '警告' },
error: { color: 'error', text: '故障' },
fault: { color: 'error', text: '故障' },
offline: { color: 'default', text: '离线' },
maintenance: { color: 'processing', text: '维护中' },
};
const { color, text } = config[status] || { color: 'default', text: status };
return {text};
}, []);
const getDeviceTypeName = useCallback(type => {
const typeMap = {
server: '服务器',
switch: '交换机',
router: '路由器',
storage: '存储设备',
firewall: '防火墙',
ups: 'UPS',
pdu: 'PDU',
};
return typeMap[type?.toLowerCase()] || type || '未知设备';
}, []);
if (!device) return null;
// 解析自定义字段
const customFields = device.customFields || {};
const standardFields = ['deviceId', 'name', 'type', 'model', 'serialNumber', 'status', 'ipAddress', 'position', 'height', 'powerConsumption', 'purchaseDate', 'warrantyExpiry', 'description'];
const customFieldEntries = Object.entries(customFields).filter(([key]) => !standardFields.includes(key));
const tabItems = [
{
key: 'ports',
label: (
端口与网卡
),
children: (
),
},
{
key: 'cables',
label: (
接线 ({deviceCables.length})
),
children: (
{deviceCables.length === 0 ? (
) : (
{deviceCables.map(cable => (
onDeleteCable?.(cable.cableId)}
okText="确定"
cancelText="取消"
>
} size="small" />
}
>
源设备
{cable.sourceDevice?.name || '-'}
{cable.sourcePort}
目标设备
{cable.targetDevice?.name || '-'}
{cable.targetPort}
{cable.status === 'normal'
? '正常'
: cable.status === 'fault'
? '故障'
: '未连接'}
{cable.cableType === 'ethernet'
? '网线'
: cable.cableType === 'fiber'
? '光纤'
: '铜缆'}
{cable.cableLength && {cable.cableLength}m}
{cable.description && (
{cable.description}
)}
))}
)}
),
},
{
key: 'tickets',
label: (
工单记录 ({ticketsPagination.total})
),
children: (
}
onClick={() => onCreateTicket?.(device)}
>
创建工单
fetchDeviceTickets(page, pageSize),
}}
size="small"
/>
),
},
];
return (
设备详情
}
placement="right"
width={700}
open={visible}
onClose={onClose}
extra={
} onClick={() => onEdit?.(device)}>
编辑
} onClick={() => onAddNic?.(device)}>
加网卡
} onClick={() => onAddPort?.(device)}>
加端口
} onClick={() => onAddCable?.(device)}>
加接线
}
styles={{ body: { padding: '0', overflow: 'auto' } }}
>
{/* 设备头部信息 */}
{device.name}
{getDeviceTypeName(device.type)}
|
{device.deviceId}
|
{getStatusTag(device.status)}
{/* 设备信息内容 */}
{/* 基本信息卡片 */}
设备型号
{device.model || '-'}
序列号
{device.serialNumber || '-'}
IP地址
{device.ipAddress || '-'}
位置
U{device.position || '-'}
高度
{device.height ? `${device.height}U` : '-'}
功耗
{device.powerConsumption ? `${device.powerConsumption}W` : '-'}
{/* 维保信息卡片 */}
购买日期
{device.purchaseDate ? dayjs(device.purchaseDate).format('YYYY-MM-DD') : '-'}
保修到期
{device.warrantyExpiry ? dayjs(device.warrantyExpiry).format('YYYY-MM-DD') : '-'}
{/* 描述信息 */}
{device.description && (
{device.description}
)}
{/* 自定义字段卡片 */}
{customFieldEntries.length > 0 && (
{customFieldEntries.map(([key, value]) => (
{key}
{String(value)}
))}
)}
{/* 标签页 */}
);
}
export default React.memo(DeviceDetailDrawer);