import React, { useState, useEffect, useCallback } from 'react'; import { Table, Card, Space, Select, DatePicker, Input, Tag, Button, message, Modal, Row, Col, Descriptions, Typography, Drawer, Statistic, } from 'antd'; import { HistoryOutlined, SearchOutlined, EyeOutlined, FilterOutlined, ClearOutlined, } from '@ant-design/icons'; import api from '../api'; import CloseButton from '../components/CloseButton'; import dayjs from 'dayjs'; import { selectStyles, filterInputStyles, inputPlaceholders, } from '../styles/deviceManagementStyles'; const { RangePicker } = DatePicker; const { Option } = Select; const { Text } = Typography; const MODULE_OPTIONS = [ { value: 'device', label: '设备管理' }, { value: 'user', label: '用户管理' }, { value: 'role', label: '角色管理' }, ]; const OPERATION_TYPE_OPTIONS = [ { value: 'create', label: '创建' }, { value: 'update', label: '更新' }, { value: 'delete', label: '删除' }, { value: 'batch_delete', label: '批量删除' }, { value: 'status_change', label: '状态变更' }, { value: 'move', label: '移动' }, { value: 'permission_change', label: '权限变更' }, ]; const RESULT_OPTIONS = [ { value: 'success', label: '成功' }, { value: 'failed', label: '失败' }, ]; function OperationLogs() { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); const [pagination, setPagination] = useState({ current: 1, pageSize: 20, total: 0 }); const [filters, setFilters] = useState({ module: null, operationType: null, keyword: '', dateRange: null, result: null, }); const [detailVisible, setDetailVisible] = useState(false); const [currentLog, setCurrentLog] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const fetchLogs = useCallback(async (page = 1, pageSize = 20, currentFilters = filters) => { try { setLoading(true); const params = { page, pageSize }; if (currentFilters.module) { params.module = currentFilters.module; } if (currentFilters.operationType) { params.operationType = currentFilters.operationType; } if (currentFilters.keyword) { params.keyword = currentFilters.keyword; } if (currentFilters.result) { params.result = currentFilters.result; } if (currentFilters.dateRange && currentFilters.dateRange.length === 2) { params.startDate = currentFilters.dateRange[0].format('YYYY-MM-DD'); params.endDate = currentFilters.dateRange[1].format('YYYY-MM-DD'); } const response = await api.get('/operation-logs', { params }); if (response.success) { setLogs(response.data.logs); setPagination(prev => ({ ...prev, current: page, pageSize, total: response.data.total, })); } } catch (error) { message.error('获取操作日志失败'); console.error('获取操作日志失败:', error); } finally { setLoading(false); } }, []); useEffect(() => { fetchLogs(1, pagination.pageSize, filters); }, []); const handleTableChange = (newPagination, tableFilters) => { fetchLogs(newPagination.current, newPagination.pageSize, filters); }; const handleFilterChange = (key, value) => { const newFilters = { ...filters, [key]: value }; setFilters(newFilters); fetchLogs(1, pagination.pageSize, newFilters); }; const handleClearFilters = () => { const clearedFilters = { module: null, operationType: null, keyword: '', dateRange: null, result: null, }; setFilters(clearedFilters); fetchLogs(1, pagination.pageSize, clearedFilters); }; const handleViewDetail = async record => { setDetailLoading(true); setDetailVisible(true); try { const response = await api.get(`/operation-logs/${record.recordId}`); if (response.success) { setCurrentLog(response.data); } } catch (error) { message.error('获取日志详情失败'); console.error('获取日志详情失败:', error); } finally { setDetailLoading(false); } }; const getModuleTag = module => { const colors = { device: 'blue', user: 'green', role: 'purple', consumable: 'orange', rack: 'cyan', room: 'magenta', ticket: 'red', backup: 'gold', }; const names = { device: '设备', user: '用户', role: '角色', consumable: '耗材', rack: '机柜', room: '机房', ticket: '工单', backup: '备份', }; return {names[module] || module}; }; const getOperationTag = type => { const colors = { create: 'green', update: 'blue', delete: 'red', batch_delete: 'red', batch_update: 'orange', status_change: 'cyan', move: 'purple', permission_change: 'gold', import: 'lime', export: 'lime', }; const names = { create: '创建', update: '更新', delete: '删除', batch_delete: '批量删除', batch_update: '批量更新', status_change: '状态变更', move: '移动', permission_change: '权限变更', import: '导入', export: '导出', }; return {names[type] || type}; }; const getResultTag = result => { return result === 'success' ? 成功 : 失败; }; const columns = [ { title: '时间', dataIndex: 'createdAt', key: 'createdAt', width: 180, sorter: (a, b) => new Date(a.createdAt) - new Date(b.createdAt), render: date => dayjs(date).format('YYYY-MM-DD HH:mm:ss'), }, { title: '模块', dataIndex: 'module', key: 'module', width: 100, render: module => getModuleTag(module), }, { title: '操作类型', dataIndex: 'operationType', key: 'operationType', width: 120, render: type => getOperationTag(type), }, { title: '操作描述', dataIndex: 'operationDescription', key: 'operationDescription', ellipsis: true, }, { title: '操作对象', dataIndex: 'targetName', key: 'targetName', width: 150, ellipsis: true, render: (text, record) => text || record.targetId, }, { title: '操作人', dataIndex: 'operatorName', key: 'operatorName', width: 120, }, { title: '结果', dataIndex: 'result', key: 'result', width: 80, render: result => getResultTag(result), }, { title: 'IP地址', dataIndex: 'ipAddress', key: 'ipAddress', width: 140, render: ip => ip || '-', }, { title: '操作', key: 'action', width: 80, fixed: 'right', render: (_, record) => ( ), }, ]; const hasFilters = filters.module || filters.operationType || filters.keyword || filters.dateRange || filters.result; return (
操作日志审计 } extra={ 共 {pagination.total} 条记录 } > } style={{ ...filterInputStyles }} value={filters.keyword} onChange={e => handleFilterChange('keyword', e.target.value)} allowClear /> handleFilterChange('dateRange', dates)} placeholder={['开始日期', '结束日期']} /> {hasFilters && ( )} `共 ${total} 条`, pageSizeOptions: ['10', '20', '50', '100'], }} onChange={handleTableChange} scroll={{ x: 1200 }} /> { setDetailVisible(false); setCurrentLog(null); }} open={detailVisible} extra={ currentLog && ( {getModuleTag(currentLog.module)} {getOperationTag(currentLog.operationType)} {getResultTag(currentLog.result)} ) } > {detailLoading ? (
加载中...
) : currentLog ? ( {currentLog.recordId} {dayjs(currentLog.createdAt).format('YYYY-MM-DD HH:mm:ss')} {currentLog.module} {currentLog.operationType} {currentLog.operationDescription} {currentLog.targetId || '-'} {currentLog.targetName || '-'} {currentLog.operatorId} {currentLog.operatorName} {currentLog.operatorRole && ( {currentLog.operatorRole} )} {currentLog.ipAddress || '-'} {currentLog.userAgent || '-'} {currentLog.result === 'success' ? '成功' : '失败'} ) : null} {currentLog && (currentLog.beforeState || currentLog.afterState) && ( <>

状态变更

{currentLog.beforeState && ( <> 变更前:
                  {JSON.stringify(currentLog.beforeState, null, 2)}
                
)} {currentLog.afterState && ( <> 变更后:
                  {JSON.stringify(currentLog.afterState, null, 2)}
                
)} )} {currentLog && currentLog.metadata && Object.keys(currentLog.metadata).length > 0 && ( <>

扩展信息

              {JSON.stringify(currentLog.metadata, null, 2)}
            
)}
); } export default OperationLogs;