import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { Table, Button, Modal, Form, Input, Select, message, Card, Space, InputNumber, Progress, Drawer, Tag, Tooltip, Dropdown, Badge, Row, Col, Statistic, Typography, Empty, Spin, Alert, Checkbox, } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, ReloadOutlined, SearchOutlined, FilterOutlined, MoreOutlined, EyeOutlined, CloudOutlined, EnvironmentOutlined, DashboardOutlined, ExpandOutlined, CompressOutlined, CheckCircleOutlined, WarningOutlined, SyncOutlined, DeleteFilled, } from '@ant-design/icons'; import axios from 'axios'; import { designTokens } from '../config/theme'; import CloseButton from '../components/CloseButton'; const { Option } = Select; const { Title, Text } = Typography; const containerStyle = { minHeight: '100vh', background: 'linear-gradient(180deg, #f5f7fa 0%, #e8ecf1 100%)', padding: '24px', }; const headerStyle = { marginBottom: '24px', padding: '24px', background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', borderRadius: '20px', color: '#fff', boxShadow: '0 8px 32px rgba(102, 126, 234, 0.3)', }; const statCardStyle = color => ({ background: 'rgba(255, 255, 255, 0.15)', borderRadius: designTokens.borderRadius.medium, padding: '16px', border: '1px solid rgba(255, 255, 255, 0.2)', backdropFilter: 'blur(10px)', }); const cardStyle = { borderRadius: designTokens.borderRadius.large, border: 'none', boxShadow: designTokens.shadows.medium, background: '#fff', overflow: 'hidden', }; const cardHeadStyle = { borderBottom: '1px solid #f0f0f0', padding: '16px 24px', background: 'linear-gradient(135deg, #f8f9ff 0%, #ffffff 100%)', }; const primaryButtonStyle = { height: '42px', borderRadius: '10px', background: designTokens.colors.primary.gradient, border: 'none', boxShadow: '0 4px 16px rgba(102, 126, 234, 0.35)', fontWeight: '500', }; const actionButtonStyle = { height: '36px', borderRadius: '8px', border: '1px solid #e8e8e8', }; const searchInputStyle = { borderRadius: '10px', height: '42px', border: '1px solid #e8e8e8', }; const statusConfig = { active: { text: '在用', color: 'success', icon: }, maintenance: { text: '维护中', color: 'warning', icon: }, inactive: { text: '停用', color: 'default', icon: }, }; const CapacityProgress = ({ used, capacity, color }) => { const percentage = Math.min((used / capacity) * 100, 100); const status = percentage >= 90 ? 'exception' : percentage >= 70 ? 'active' : 'success'; return (
{used} / {capacity} {percentage.toFixed(1)}%
); }; const RoomCard = ({ room, onEdit, onDelete, onView, selected, onSelect }) => { const rackCount = room.Racks?.length || 0; const capacityUsage = (rackCount / room.capacity) * 100; const statusInfo = statusConfig[room.status]; return ( onSelect(room.roomId)} onDoubleClick={() => onView(room)} styles={{ body: { padding: '20px' } }} >
{room.name}
{room.roomId}
{statusInfo.text}
{room.location}
= 90 ? designTokens.colors.error.main : capacityUsage >= 70 ? designTokens.colors.warning.main : designTokens.colors.success.main } />
面积
{room.area} ㎡
机柜
{rackCount}
); }; function RoomManagement() { const [rooms, setRooms] = useState([]); const [loading, setLoading] = useState(true); const [modalVisible, setModalVisible] = useState(false); const [drawerVisible, setDrawerVisible] = useState(false); const [editingRoom, setEditingRoom] = useState(null); const [viewingRoom, setViewingRoom] = useState(null); const [selectedRoomIds, setSelectedRoomIds] = useState([]); const [viewMode, setViewMode] = useState('table'); const [searchKeyword, setSearchKeyword] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [form] = Form.useForm(); const fetchRooms = useCallback(async () => { try { setLoading(true); const response = await axios.get('/api/rooms'); setRooms(response.data.rooms || []); } catch (error) { message.error('获取机房列表失败'); console.error('获取机房列表失败:', error); } finally { setLoading(false); } }, []); useEffect(() => { fetchRooms(); }, [fetchRooms]); const showModal = (room = null) => { setEditingRoom(room); if (room) { form.setFieldsValue(room); } else { form.resetFields(); } setModalVisible(true); }; const handleCancel = () => { setModalVisible(false); setEditingRoom(null); }; const handleSubmit = async values => { try { if (editingRoom) { await axios.put(`/api/rooms/${editingRoom.roomId}`, values); message.success('机房更新成功'); } else { await axios.post('/api/rooms', values); message.success('机房创建成功'); } setModalVisible(false); fetchRooms(); setEditingRoom(null); } catch (error) { message.error(editingRoom ? '机房更新失败' : '机房创建失败'); console.error(editingRoom ? '机房更新失败:' : '机房创建失败:', error); } }; const handleDelete = async roomId => { Modal.confirm({ title: '确认删除', content: '确定要删除这个机房吗?删除后无法恢复。', okText: '删除', okType: 'danger', cancelText: '取消', onOk: async () => { try { await axios.delete(`/api/rooms/${roomId}`); message.success('机房删除成功'); fetchRooms(); } catch (error) { message.error('机房删除失败'); console.error('机房删除失败:', error); } }, }); }; const handleBatchDelete = async () => { if (selectedRoomIds.length === 0) { message.warning('请先选择要删除的机房'); return; } Modal.confirm({ title: '批量删除', content: `确定要删除选中的 ${selectedRoomIds.length} 个机房吗?`, okText: '删除', okType: 'danger', cancelText: '取消', onOk: async () => { try { await Promise.all(selectedRoomIds.map(id => axios.delete(`/api/rooms/${id}`))); message.success(`成功删除 ${selectedRoomIds.length} 个机房`); setSelectedRoomIds([]); fetchRooms(); } catch (error) { message.error('批量删除失败'); console.error('批量删除失败:', error); } }, }); }; const handleView = room => { setViewingRoom(room); setDrawerVisible(true); }; const filteredRooms = useMemo(() => { return rooms.filter(room => { const matchKeyword = !searchKeyword || room.name?.toLowerCase().includes(searchKeyword.toLowerCase()) || room.roomId?.toLowerCase().includes(searchKeyword.toLowerCase()) || room.location?.toLowerCase().includes(searchKeyword.toLowerCase()); const matchStatus = statusFilter === 'all' || room.status === statusFilter; return matchKeyword && matchStatus; }); }, [rooms, searchKeyword, statusFilter]); const stats = useMemo( () => ({ total: rooms.length, active: rooms.filter(r => r.status === 'active').length, maintenance: rooms.filter(r => r.status === 'maintenance').length, inactive: rooms.filter(r => r.status === 'inactive').length, totalRacks: rooms.reduce((sum, r) => sum + (r.Racks?.length || 0), 0), totalCapacity: rooms.reduce((sum, r) => sum + (r.capacity || 0), 0), }), [rooms] ); const tableColumns = [ { title: '机房信息', key: 'roomInfo', render: (_, record) => (
{record.name}
{record.roomId}
), }, { title: '位置', dataIndex: 'location', key: 'location', render: location => (
{location}
), }, { title: '面积/容量', key: 'areaCapacity', render: (_, record) => (
{record.area} ㎡
), }, { title: '状态', dataIndex: 'status', key: 'status', render: status => { const config = statusConfig[status]; return ( {config.text} ); }, filters: [ { text: '在用', value: 'active' }, { text: '维护中', value: 'maintenance' }, { text: '停用', value: 'inactive' }, ], onFilter: (value, record) => record.status === value, }, { title: '机柜数', key: 'rackCount', render: (_, record) => (
{record.Racks?.length || 0}
), sorter: (a, b) => (a.Racks?.length || 0) - (b.Racks?.length || 0), }, { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', render: date => (date ? new Date(date).toLocaleString() : '-'), sorter: (a, b) => new Date(a.createdAt || 0) - new Date(b.createdAt || 0), }, { title: '操作', key: 'action', fixed: 'right', width: 160, render: (_, record) => ( )}
{viewMode === 'table' ? ( `共 ${total} 条记录`, }} scroll={{ x: 1000 }} rowClassName={() => 'table-row'} /> ) : ( {filteredRooms.length > 0 ? ( filteredRooms.map(room => ( { if (selectedRoomIds.includes(id)) { setSelectedRoomIds(selectedRoomIds.filter(rid => rid !== id)); } else { setSelectedRoomIds([...selectedRoomIds, id]); } }} /> )) ) : ( )} )}
{editingRoom ? '编辑机房' : '添加机房'}
} open={modalVisible} closeIcon={} onCancel={handleCancel} footer={null} width={600} destroyOnHidden styles={{ body: { padding: '24px' }, header: { borderBottom: '1px solid #f0f0f0', padding: '16px 24px', position: 'relative', }, }} style={{ borderRadius: '16px' }} >
} placeholder="请输入机房位置" style={{ borderRadius: '8px' }} />
机房详情 - {viewingRoom?.name} } open={drawerVisible} onClose={() => setDrawerVisible(false)} width={480} styles={{ header: { borderBottom: '1px solid #f0f0f0' }, body: { padding: '24px' }, }} > {viewingRoom && (
{viewingRoom.name}
{viewingRoom.roomId}
{statusConfig[viewingRoom.status].text}
位置
{viewingRoom.location}
面积
{viewingRoom.area} ㎡
容量
{viewingRoom.capacity} 机柜
机柜使用情况
{viewingRoom.description && (
描述
{viewingRoom.description}
)}
)} ); } export default RoomManagement;