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 (
| 文件名 | 类型 | 大小 | 修改时间 | 操作 |
|---|---|---|---|---|
| {log.name} | {getTypeLabel(log.type)} | {formatSize(log.size)} | {log.modified_at} |
{isAdmin && (
)}
|
{filteredContent || '(空日志)'}