import React, { useState, useEffect, useCallback } from 'react'; import { listLogs, getLogContent, deleteLog } from './api/logs'; const PAGE_SIZE = 1000; const LogManagementPanel = ({ isAdmin }) => { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(false); const [selectedLog, setSelectedLog] = useState(null); const [logContent, setLogContent] = useState(''); const [showModal, setShowModal] = useState(false); const [filterType, setFilterType] = useState(''); const [totalLines, setTotalLines] = useState(0); const [currentOffset, setCurrentOffset] = useState(0); const [searchTerm, setSearchTerm] = useState(''); const loadLogs = useCallback(async () => { setLoading(true); try { const data = await listLogs(filterType || null); setLogs(data); } catch (error) { console.error('加载日志列表失败:', error); alert(`加载日志列表失败:${error.response?.data?.detail || error.message}`); } finally { setLoading(false); } }, [filterType]); const loadLogContent = useCallback(async (logPath, offset = 0) => { try { const data = await getLogContent(logPath, offset, PAGE_SIZE); setLogContent(data.content || ''); setTotalLines(data.total_lines || 0); setCurrentOffset(offset); } catch (error) { console.error('加载日志内容失败:', error); alert(`加载日志内容失败:${error.response?.data?.detail || error.message}`); } }, []); useEffect(() => { loadLogs(); }, [loadLogs]); const handleViewLog = async log => { setSelectedLog(log); setShowModal(true); setCurrentOffset(0); setSearchTerm(''); await loadLogContent(log.path, 0); }; const handleDeleteLog = async log => { if (!isAdmin) { alert('只有管理员可以删除日志。'); return; } if (!window.confirm(`确定要删除日志文件“${log.name}”吗?\n\n此操作不可恢复。`)) { return; } try { await deleteLog(log.path); alert('日志文件已删除。'); await loadLogs(); if (selectedLog && selectedLog.path === log.path) { setShowModal(false); setSelectedLog(null); setLogContent(''); setTotalLines(0); setCurrentOffset(0); } } catch (error) { console.error('删除日志失败:', error); alert(`删除日志失败:${error.response?.data?.detail || error.message}`); } }; const handlePrevPage = () => { if (selectedLog && currentOffset > 0) { const newOffset = Math.max(0, currentOffset - PAGE_SIZE); loadLogContent(selectedLog.path, newOffset); } }; const handleNextPage = () => { if (selectedLog && currentOffset + PAGE_SIZE < totalLines) { const newOffset = currentOffset + PAGE_SIZE; loadLogContent(selectedLog.path, newOffset); } }; const formatSize = bytes => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }; const getTypeLabel = type => { const labels = { app: '应用日志', task: '任务日志', error: '错误日志', other: '其他', }; return labels[type] || type; }; const getTypeColor = type => { const colors = { app: '#3b82f6', task: '#10b981', error: '#ef4444', other: '#6b7280', }; return colors[type] || '#6b7280'; }; const filteredContent = searchTerm ? logContent .split('\n') .filter(line => line.toLowerCase().includes(searchTerm.toLowerCase())) .join('\n') : logContent; return (

日志管理

{logs.length === 0 ? (
暂无日志文件
) : ( {logs.map((log, index) => ( ))}
文件名 类型 大小 修改时间 操作
{log.name} {getTypeLabel(log.type)} {formatSize(log.size)} {log.modified_at}
{isAdmin && ( )}
)} {showModal && selectedLog && (

{selectedLog.name}

大小:{formatSize(selectedLog.size)} | 修改时间:{selectedLog.modified_at} | 总行数:{totalLines}
setSearchTerm(event.target.value)} style={{ flex: 1, padding: '6px 12px', border: '1px solid #d1d5db', borderRadius: '4px', fontSize: '13px', }} />
显示行 {totalLines === 0 ? 0 : currentOffset + 1} - {Math.min(currentOffset + PAGE_SIZE, totalLines)}
                {filteredContent || '(空日志)'}
              
)}
); }; export default LogManagementPanel;