Merge pull request #64 from ShicYang/main

feat: 使用日志新增自动刷新功能
This commit is contained in:
Cita
2026-03-13 23:38:03 +08:00
committed by GitHub
2 changed files with 36 additions and 10 deletions
+6
View File
@@ -877,6 +877,12 @@ body {
background: color-mix(in srgb, var(--color-primary) 22%, var(--color-bg-card)); background: color-mix(in srgb, var(--color-primary) 22%, var(--color-bg-card));
} }
.btn-ghost-active {
background: color-mix(in srgb, var(--color-primary) 22%, var(--color-bg-card));
border-color: var(--color-primary) !important;
color: var(--color-primary);
}
.btn-link { .btn-link {
background: transparent; background: transparent;
border: none; border: none;
+25 -5
View File
@@ -219,6 +219,7 @@ export default function ProxyLogs() {
const [detailById, setDetailById] = useState<Record<number, ProxyLogDetailState>>({}); const [detailById, setDetailById] = useState<Record<number, ProxyLogDetailState>>({});
const [showFilters, setShowFilters] = useState(false); const [showFilters, setShowFilters] = useState(false);
const [sites, setSites] = useState<Array<{ id: number; name: string; status?: string | null }>>([]); const [sites, setSites] = useState<Array<{ id: number; name: string; status?: string | null }>>([]);
const [autoRefresh, setAutoRefresh] = useState(false);
const isMobile = useIsMobile(768); const isMobile = useIsMobile(768);
const toast = useToast(); const toast = useToast();
const loadSeq = useRef(0); const loadSeq = useRef(0);
@@ -320,7 +321,7 @@ export default function ProxyLogs() {
return siteOptions.find((option) => option.value === String(siteFilter))?.label || `站点 #${siteFilter}`; return siteOptions.find((option) => option.value === String(siteFilter))?.label || `站点 #${siteFilter}`;
}, [siteFilter, siteOptions]); }, [siteFilter, siteOptions]);
const load = useCallback(async () => { const load = useCallback(async (silent = false) => {
const seq = ++loadSeq.current; const seq = ++loadSeq.current;
if (hasInvalidTimeRange) { if (hasInvalidTimeRange) {
setLogs([]); setLogs([]);
@@ -329,7 +330,7 @@ export default function ProxyLogs() {
if (seq === loadSeq.current) setLoading(false); if (seq === loadSeq.current) setLoading(false);
return; return;
} }
setLoading(true); if (!silent) setLoading(true);
try { try {
const params = { const params = {
limit: pageSize, limit: pageSize,
@@ -347,7 +348,7 @@ export default function ProxyLogs() {
setSummary(data.summary || EMPTY_SUMMARY); setSummary(data.summary || EMPTY_SUMMARY);
} catch (e: any) { } catch (e: any) {
if (seq !== loadSeq.current) return; if (seq !== loadSeq.current) return;
toast.error(e.message || '加载日志失败'); if (!silent) toast.error(e.message || '加载日志失败');
} finally { } finally {
if (seq === loadSeq.current) setLoading(false); if (seq === loadSeq.current) setLoading(false);
} }
@@ -357,6 +358,12 @@ export default function ProxyLogs() {
void load(); void load();
}, [load]); }, [load]);
useEffect(() => {
if (!autoRefresh) return;
const timer = setInterval(() => { void load(true); }, 2000);
return () => clearInterval(timer);
}, [autoRefresh, load]);
useEffect(() => { useEffect(() => {
if (page <= totalPages) return; if (page <= totalPages) return;
setPage(totalPages); setPage(totalPages);
@@ -504,13 +511,26 @@ export default function ProxyLogs() {
{summary.totalTokensAll.toLocaleString()} tokens {summary.totalTokensAll.toLocaleString()} tokens
</span> </span>
</div> </div>
<button onClick={load} disabled={loading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)', padding: '6px 14px' }}> <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<button
onClick={() => setAutoRefresh((v) => !v)}
className={`btn btn-ghost${autoRefresh ? ' btn-ghost-active' : ''}`}
style={{ border: '1px solid var(--color-border)', padding: '6px 14px' }}
title={autoRefresh ? '关闭自动刷新' : '开启自动刷新(每2秒)'}
>
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ animation: autoRefresh ? 'spin 1s linear infinite' : 'none' }}>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
{autoRefresh ? '自动刷新中' : '自动刷新'}
</button>
<button onClick={() => load()} disabled={loading} className="btn btn-ghost" style={{ border: '1px solid var(--color-border)', padding: '6px 14px' }}>
<svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ animation: loading ? 'spin 1s linear infinite' : 'none' }}> <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ animation: loading ? 'spin 1s linear infinite' : 'none' }}>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg> </svg>
{loading ? '加载中...' : '刷新'} {loading ? '加载中...' : '刷新'}
</button> </button>
</div> </div>
</div>
{isMobile ? ( {isMobile ? (
<> <>
@@ -816,7 +836,7 @@ export default function ProxyLogs() {
{detail && pathMeta.errorMessage.trim().length > 0 && ( {detail && pathMeta.errorMessage.trim().length > 0 && (
<div style={{ display: 'flex', gap: 6 }}> <div style={{ display: 'flex', gap: 6 }}>
<span style={{ fontWeight: 600, color: 'var(--color-danger)', flexShrink: 0 }}>错误信息</span> <span style={{ fontWeight: 600, color: 'var(--color-danger)', flexShrink: 0 }}>错误信息</span>
<span style={{ color: 'var(--color-danger)' }}>{pathMeta.errorMessage}</span> <span style={{ color: 'var(--color-danger)', whiteSpace: 'pre-wrap' }}>{pathMeta.errorMessage}</span>
</div> </div>
)} )}
</div> </div>