import { useEffect, useMemo, useState } from 'react'; import { Alert, Button, Card, InputNumber, Space, Typography } from 'antd'; import { api, ApiError } from '../lib/api'; export function LogsPage() { const [lines, setLines] = useState(200); const [loading, setLoading] = useState(false); const [downloadLoading, setDownloadLoading] = useState(false); const [error, setError] = useState(null); const [file, setFile] = useState(''); const [updatedAt, setUpdatedAt] = useState(''); const [logLines, setLogLines] = useState([]); const logText = useMemo(() => logLines.join('\n'), [logLines]); const load = async () => { setLoading(true); setError(null); try { const res = await api.getRuntimeLogs(lines); if (!res?.success) { setError(res?.message || '运行日志获取失败'); return; } setFile(res?.data?.file || ''); setUpdatedAt(res?.data?.updated_at || ''); setLogLines(Array.isArray(res?.data?.lines) ? res.data.lines : []); } catch (e) { if (e instanceof ApiError) { setError(e.message); } else { setError(e instanceof Error ? e.message : '运行日志获取失败'); } } finally { setLoading(false); } }; useEffect(() => { void load(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const downloadArchive = async () => { setDownloadLoading(true); setError(null); try { const { blob, filename } = await api.downloadLogsArchive(); const url = URL.createObjectURL(blob); try { const a = document.createElement('a'); a.href = url; a.download = filename || 'logs.tar.gz'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } finally { URL.revokeObjectURL(url); } } catch (e) { if (e instanceof ApiError) { setError(e.message); } else { setError(e instanceof Error ? e.message : '日志下载失败'); } } finally { setDownloadLoading(false); } }; return ( {error ? : null} 行数 setLines(typeof v === 'number' ? v : 200)} /> } > {file ? `文件:${file}` : '文件:-'} {updatedAt ? ` 更新时间:${updatedAt}` : ''}
{logText || '暂无日志'}
); }