From cc76fd270b9b5c7f5247caf6e77b20a1e63a9453 Mon Sep 17 00:00:00 2001 From: zhang1106 <849185023@qq.com> Date: Fri, 19 Dec 2025 15:05:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E4=BB=AA=E8=A1=A8?= =?UTF-8?q?=E7=9B=98=E7=9A=84=E9=A1=B5=E9=9D=A2=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BA=86=E8=AE=BE=E5=A4=87=E7=AE=A1=E7=90=86=E7=9A=84=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E5=AD=97=E6=AE=B5=E6=A1=86=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E6=8B=96=E6=8B=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.jsx | 52 +-- frontend/src/pages/Dashboard.jsx | 468 +++++++++++++++++++++--- frontend/src/pages/DeviceManagement.jsx | 370 +++++++++++++++---- 3 files changed, 751 insertions(+), 139 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 64cb6cc..446e039 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -9,7 +9,7 @@ import RoomManagement from './pages/RoomManagement'; import DeviceFieldManagement from './pages/DeviceFieldManagement'; -const { Header, Content, Sider } = Layout; +const { Content, Sider } = Layout; function App() { const { @@ -19,18 +19,7 @@ function App() { return ( -
- IDC设备管理系统 -
- - + - - - - } /> - } /> - } /> - } /> - } /> - - - + + + + } /> + } /> + } /> + } /> + } /> + + diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 122f6b0..79b8b4a 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -1,6 +1,18 @@ import React, { useState, useEffect } from 'react'; -import { Card, Row, Col, Statistic, Spin, message } from 'antd'; -import { DatabaseOutlined, CloudServerOutlined, WarningOutlined, PoweroffOutlined } from '@ant-design/icons'; +import { Card, Row, Col, Statistic, Spin, message, Button, Tag } from 'antd'; +import { + DatabaseOutlined, + CloudServerOutlined, + WarningOutlined, + PoweroffOutlined, + HomeOutlined, + MonitorOutlined, + SettingOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + DashboardOutlined, + ReloadOutlined +} from '@ant-design/icons'; import axios from 'axios'; function Dashboard() { @@ -8,7 +20,9 @@ function Dashboard() { totalDevices: 0, totalRacks: 0, totalRooms: 0, - faultDevices: 0 + faultDevices: 0, + deviceGrowth: 2.5, // 示例增长数据 + faultTrend: -12.3 // 示例趋势数据 }); const [loading, setLoading] = useState(true); @@ -54,56 +68,424 @@ function Dashboard() { fetchStats(); }, []); + // 科技感配色主题 + const theme = { + primary: '#1890ff', // 科技蓝 + primaryDark: '#096dd9', // 深蓝 + secondary: '#722ed1', // 紫色 + success: '#52c41a', // 绿色 + warning: '#faad14', // 橙色 + error: '#ff4d4f', // 红色 + background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + cardBg: 'rgba(255, 255, 255, 0.95)', + textPrimary: '#262626', + textSecondary: '#8c8c8c' + }; + + const containerStyle = { + minHeight: '100vh', + background: 'linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%)', + padding: '24px' + }; + + const headerStyle = { + textAlign: 'center', + marginBottom: '32px' + }; + + const titleStyle = { + fontSize: '2.5rem', + fontWeight: '700', + background: theme.background, + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + margin: '0 0 8px 0' + }; + + const subtitleStyle = { + fontSize: '1.1rem', + color: theme.textSecondary, + margin: '0' + }; + + const statCardStyle = { + borderRadius: '16px', + border: 'none', + boxShadow: '0 4px 16px rgba(24, 144, 255, 0.1)', + background: theme.cardBg, + backdropFilter: 'blur(10px)', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + position: 'relative', + overflow: 'hidden', + cursor: 'pointer' + }; + + const statCardHoverStyle = { + transform: 'translateY(-4px)', + boxShadow: '0 8px 32px rgba(24, 144, 255, 0.2)' + }; + + const trendStyle = (trend) => ({ + display: 'flex', + alignItems: 'center', + fontSize: '0.875rem', + fontWeight: '500', + color: trend > 0 ? theme.success : theme.error, + marginTop: '8px' + }); + + const systemOverviewStyle = { + marginTop: '32px' + }; + + const overviewCardStyle = { + borderRadius: '20px', + border: 'none', + boxShadow: '0 8px 32px rgba(24, 144, 255, 0.15)', + background: theme.cardBg, + backdropFilter: 'blur(10px)' + }; + + const welcomeSectionStyle = { + background: theme.background, + borderRadius: '12px', + padding: '20px', + textAlign: 'center', + color: 'white', + marginBottom: '24px' + }; + + const navigationGridStyle = { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', + gap: '16px', + marginBottom: '24px' + }; + + const navButtonStyle = { + height: 'auto', + padding: '20px 16px', + borderRadius: '12px', + border: '2px solid rgba(24, 144, 255, 0.1)', + background: 'rgba(24, 144, 255, 0.02)', + transition: 'all 0.3s ease', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '8px' + }; + + const navButtonHoverStyle = { + borderColor: theme.primary, + background: 'rgba(24, 144, 255, 0.1)', + transform: 'translateY(-2px)' + }; + + const navIconStyle = { + fontSize: '2rem', + color: theme.primary + }; + + const navTextStyle = { + fontSize: '0.95rem', + fontWeight: '600', + color: theme.textPrimary + }; + + const systemInfoStyle = { + background: 'linear-gradient(135deg, #e8f4fd 0%, #f0f9ff 100%)', + borderRadius: '12px', + padding: '16px', + border: '1px solid rgba(24, 144, 255, 0.1)' + }; + return ( -
-

仪表盘

- - - - } - loading={loading} - /> +
+ {/* 页面头部 */} +
+

+ + IDC设备管理系统 +

+

实时监控 • 智能管理 • 高效运维

+
+ + {/* 数据概览卡片 */} + + + +
+
+ + 总设备数 + + } + value={stats.totalDevices} + prefix={} + valueStyle={{ + fontSize: '2rem', + fontWeight: '700', + color: theme.textPrimary, + marginBottom: '8px' + }} + loading={loading} + /> +
+ {stats.deviceGrowth > 0 ? : } + {Math.abs(stats.deviceGrowth)}% + 本月 +
+
- - - } - loading={loading} - /> + + + +
+
+ + 总机柜数 + + } + value={stats.totalRacks} + prefix={} + valueStyle={{ + fontSize: '2rem', + fontWeight: '700', + color: theme.textPrimary, + marginBottom: '8px' + }} + loading={loading} + /> +
+ + 正常运行 +
+
- - - } - loading={loading} - /> + + + +
+
+ + 总机房数 + + } + value={stats.totalRooms} + prefix={} + valueStyle={{ + fontSize: '2rem', + fontWeight: '700', + color: theme.textPrimary, + marginBottom: '8px' + }} + loading={loading} + /> +
+ + 全部在线 +
+
- - - } - valueStyle={{ color: '#cf1322' }} - loading={loading} - /> + + + +
+
+ + 故障设备 + + } + value={stats.faultDevices} + prefix={} + valueStyle={{ + fontSize: '2rem', + fontWeight: '700', + color: theme.error, + marginBottom: '8px' + }} + loading={loading} + /> +
+ {stats.faultTrend < 0 ? : } + {Math.abs(stats.faultTrend)}% + 本周 +
+
- -

欢迎使用IDC设备管理系统!

-

本系统用于管理IDC机房中的设备、机柜和机房信息,提供设备状态监控、资源分配等功能。

-
+ + {/* 系统概览 */} +
+ +
+ {/* 欢迎区域 */} +
+

+ 欢迎使用IDC设备管理系统 +

+

+ 专业的机房设备管理解决方案 +

+
+ + {/* 导航功能网格 */} +
+ + + + + + + +
+ + {/* 系统信息 */} +
+
+
+

+ 系统版本: v1.0.0 +

+

+ 最后更新:{new Date().toLocaleDateString()} +

+
+ +
+
+
+
+
); } diff --git a/frontend/src/pages/DeviceManagement.jsx b/frontend/src/pages/DeviceManagement.jsx index 6ac08ed..a9392dc 100644 --- a/frontend/src/pages/DeviceManagement.jsx +++ b/frontend/src/pages/DeviceManagement.jsx @@ -1,12 +1,72 @@ import React, { useState, useEffect } from 'react'; import { Table, Button, Modal, Form, Input, Select, DatePicker, message, Card, Space, InputNumber, Switch, Upload, Progress, Checkbox } from 'antd'; -import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, UploadOutlined, DownloadOutlined, SettingOutlined } from '@ant-design/icons'; +import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, UploadOutlined, DownloadOutlined, SettingOutlined, UndoOutlined, CloudServerOutlined, SwapOutlined, SafetyOutlined, DatabaseOutlined, AppstoreOutlined } from '@ant-design/icons'; import axios from 'axios'; import dayjs from 'dayjs'; const { Option } = Select; const { RangePicker } = DatePicker; +// 可调整列宽的表头组件 +const ResizeableTitle = (props) => { + const { onResize, width, ...restProps } = props; + + if (!width) { + return ; + } + + const handleMouseDown = (e) => { + if (!onResize) return; + + + const startX = e.pageX; + const startWidth = width; + + const handleMouseMove = (moveEvent) => { + const diff = moveEvent.pageX - startX; + const newWidth = Math.max(50, startWidth + diff); // 最小宽度50px + onResize(newWidth); + }; + + const handleMouseUp = () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + }; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + }; + + return ( + + {restProps.children} +
+ + ); +}; + function DeviceManagement() { const [devices, setDevices] = useState([]); const [racks, setRacks] = useState([]); @@ -38,13 +98,18 @@ function DeviceManagement() { const [isImporting, setIsImporting] = useState(false); const [importResult, setImportResult] = useState(null); const [selectedDevices, setSelectedDevices] = useState([]); - const [selectAll, setSelectAll] = useState(false); + const [detailModalVisible, setDetailModalVisible] = useState(false); + const [selectedDevice, setSelectedDevice] = useState(null); + // 自定义字段状态 const [customFieldName, setCustomFieldName] = useState(''); const [customFieldValue, setCustomFieldValue] = useState(''); // 字段配置模态框 const [fieldConfigModalVisible, setFieldConfigModalVisible] = useState(false); + + // 列宽状态 + const [columnWidths, setColumnWidths] = useState({}); // 获取所有设备(支持搜索、筛选和分页) const fetchDevices = async (page = 1, pageSize = 10, searchParams = {}) => { @@ -341,6 +406,12 @@ function DeviceManagement() { }); }; + // 显示设备详情 + const handleShowDetail = (device) => { + setSelectedDevice(device); + setDetailModalVisible(true); + }; + // 导出设备数据 const handleExport = async () => { try { @@ -369,26 +440,7 @@ function DeviceManagement() { } }; - // 处理单个设备选择 - const handleDeviceSelect = (deviceId) => { - setSelectedDevices(prev => { - if (prev.includes(deviceId)) { - return prev.filter(id => id !== deviceId); - } else { - return [...prev, deviceId]; - } - }); - }; - // 处理全选/取消全选 - const handleSelectAll = (e) => { - setSelectAll(e.target.checked); - if (e.target.checked) { - setSelectedDevices(devices.map(device => device.deviceId)); - } else { - setSelectedDevices([]); - } - }; @@ -464,6 +516,36 @@ function DeviceManagement() { other: '其他设备' }; + // 获取设备类型图标 + const getDeviceTypeIcon = (type) => { + const iconMap = { + server: , + switch: , + router: , + storage: , + other: + }; + return iconMap[type] || ; + }; + + // 处理列宽变化 + const handleColumnResize = (key, width) => { + setColumnWidths(prev => ({ ...prev, [key]: width })); + }; + + // 重置列宽到默认值 + const resetColumnWidths = () => { + setColumnWidths({}); + message.success('列宽已重置为默认值'); + }; + + // 处理表头单元格拖拽 + const handleHeaderCellResize = (key) => (column) => ({ + width: column.width, + onResize: (width) => handleColumnResize(key, width), + }); + + // 动态生成表格列配置 const columns = React.useMemo(() => { const generatedColumns = []; @@ -480,12 +562,16 @@ function DeviceManagement() { title: '所在机房', dataIndex: ['Rack', 'Room', 'name'], key: 'roomName', + width: columnWidths.roomName || 120, + onHeaderCell: handleHeaderCellResize('roomName'), }); // 添加机柜信息列 generatedColumns.push({ title: field.displayName, dataIndex: ['Rack', 'name'], key: field.fieldName, + width: columnWidths[field.fieldName] || 120, + onHeaderCell: handleHeaderCellResize(field.fieldName), }); } // 特殊处理设备类型 @@ -494,17 +580,15 @@ function DeviceManagement() { title: field.displayName, dataIndex: field.fieldName, key: field.fieldName, + width: columnWidths[field.fieldName] || 100, + onHeaderCell: handleHeaderCellResize(field.fieldName), render: (type) => { - if (Array.isArray(type)) { - return ( - - {type.map(t => ( - {typeMap[t]} - ))} - - ); - } - return typeMap[type]; + return ( + + {getDeviceTypeIcon(type)} + {typeMap[type]} + + ); }, }); } @@ -514,6 +598,8 @@ function DeviceManagement() { title: field.displayName, dataIndex: field.fieldName, key: field.fieldName, + width: columnWidths[field.fieldName] || 100, + onHeaderCell: handleHeaderCellResize(field.fieldName), render: (status) => { if (Array.isArray(status)) { return ( @@ -540,6 +626,8 @@ function DeviceManagement() { title: field.displayName, dataIndex: field.fieldName, key: field.fieldName, + width: columnWidths[field.fieldName] || 120, + onHeaderCell: handleHeaderCellResize(field.fieldName), render: (date) => { if (!date) return ''; @@ -565,11 +653,46 @@ function DeviceManagement() { } // 普通字段 else { - generatedColumns.push({ + // 为不同类型的字段设置不同的默认宽度 + let defaultWidth = 120; + if (field.fieldName === 'deviceId' || field.fieldName === 'name') { + defaultWidth = 150; + } else if (field.fieldName === 'description') { + defaultWidth = 200; + } else if (field.fieldType === 'number') { + defaultWidth = 80; + } else if (field.fieldType === 'textarea') { + defaultWidth = 180; + } + + // 为设备名称和ID列添加点击查看详情功能 + const columnConfig = { title: field.displayName, dataIndex: field.fieldName, key: field.fieldName, - }); + width: columnWidths[field.fieldName] || defaultWidth, + onHeaderCell: handleHeaderCellResize(field.fieldName), + }; + + // 设备名称和ID列添加点击效果 + if (field.fieldName === 'deviceId' || field.fieldName === 'name') { + columnConfig.render = (value, record) => ( + handleShowDetail(record)} + style={{ + color: '#1890ff', + textDecoration: 'none', + cursor: 'pointer' + }} + onMouseEnter={(e) => e.target.style.textDecoration = 'underline'} + onMouseLeave={(e) => e.target.style.textDecoration = 'none'} + > + {value || '-'} + + ); + } + + generatedColumns.push(columnConfig); } }); @@ -577,6 +700,8 @@ function DeviceManagement() { generatedColumns.push({ title: '操作', key: 'action', + width: columnWidths.action || 120, + onHeaderCell: handleHeaderCellResize('action'), render: (_, record) => ( +
)} + + {/* 设备详情模态框 */} + { + setDetailModalVisible(false); + setSelectedDevice(null); + }} + footer={[ + , + + ]} + width={800} + destroyOnClose + > + {selectedDevice && ( +
+ {/* 基本信息区域 */} + +
+
+ + {selectedDevice.deviceId || '-'} +
+
+ + {selectedDevice.name || '-'} +
+
+ + + {selectedDevice.type ? ( + + {getDeviceTypeIcon(selectedDevice.type)} + {typeMap[selectedDevice.type] || selectedDevice.type} + + ) : '-'} + +
+
+ + {selectedDevice.model || '-'} +
+
+ + {selectedDevice.serialNumber || '-'} +
+
+ + {selectedDevice.ipAddress || '-'} +
+
+ + {selectedDevice.Rack?.Room?.name || '-'} +
+
+ + {selectedDevice.Rack?.name || '-'} +
+
+ + {selectedDevice.position || '-'} +
+
+ + {selectedDevice.height || '-'} +
+
+ + {selectedDevice.power || '-'} +
+
+ + + {selectedDevice.status ? statusMap[selectedDevice.status]?.text || selectedDevice.status : '-'} + +
+
+ + + {selectedDevice.purchaseDate ? new Date(selectedDevice.purchaseDate).toLocaleDateString('zh-CN') : '-'} + +
+
+ + + {selectedDevice.warrantyExpiry ? new Date(selectedDevice.warrantyExpiry).toLocaleDateString('zh-CN') : '-'} + +
+
+ {selectedDevice.description && ( +
+ +
+ {selectedDevice.description} +
+
+ )} +
+
+ )} +
); }