Files
xingrin/frontend/components/scan/scan-log-list.tsx
yyhuni 8bb737a9fa feat(scan-history): add auto-refresh toggle and improve layout
- Add auto-refresh toggle switch to scan logs section for manual control
- Implement flexible polling based on auto-refresh state and scan status
- Restructure scan overview layout to use left-right split (stages + logs)
- Move stage progress to left column with vulnerability statistics
- Implement scrollable logs panel on right side with proper height constraints
- Update component imports to use Switch and Label instead of Button
- Add full-height flex layout to parent containers for proper scrolling
- Refactor grid layout from 2-column to fixed-width left + flexible right
- Update translations for new UI elements and labels
- Improve responsive design with better flex constraints and min-height handling
2026-01-07 23:30:27 +08:00

83 lines
2.1 KiB
TypeScript

"use client"
import { useMemo, useRef } from "react"
import { AnsiLogViewer } from "@/components/settings/system-logs"
import type { ScanLog } from "@/services/scan.service"
interface ScanLogListProps {
logs: ScanLog[]
loading?: boolean
}
/**
* 格式化时间为 HH:mm:ss
*/
function formatTime(isoString: string): string {
try {
const date = new Date(isoString)
const h = String(date.getHours()).padStart(2, '0')
const m = String(date.getMinutes()).padStart(2, '0')
const s = String(date.getSeconds()).padStart(2, '0')
return `${h}:${m}:${s}`
} catch {
return isoString
}
}
/**
* 扫描日志列表组件
* 复用 AnsiLogViewer 组件
*/
export function ScanLogList({ logs, loading }: ScanLogListProps) {
// 稳定的 content 引用,只有内容真正变化时才更新
const contentRef = useRef('')
const lastLogCountRef = useRef(0)
const lastLogIdRef = useRef<number | null>(null)
// 将日志转换为纯文本格式
const content = useMemo(() => {
if (logs.length === 0) return ''
// 检查是否真正需要更新
const lastLog = logs[logs.length - 1]
if (
logs.length === lastLogCountRef.current &&
lastLog?.id === lastLogIdRef.current
) {
// 日志没有变化,返回缓存的 content
return contentRef.current
}
// 更新缓存
lastLogCountRef.current = logs.length
lastLogIdRef.current = lastLog?.id ?? null
const newContent = logs.map(log => {
const time = formatTime(log.createdAt)
const levelTag = log.level.toUpperCase()
return `[${time}] [${levelTag}] ${log.content}`
}).join('\n')
contentRef.current = newContent
return newContent
}, [logs])
if (loading && logs.length === 0) {
return (
<div className="h-full flex items-center justify-center bg-[#1e1e1e] text-[#808080]">
...
</div>
)
}
if (logs.length === 0) {
return (
<div className="h-full flex items-center justify-center bg-[#1e1e1e] text-[#808080]">
</div>
)
}
return <AnsiLogViewer content={content} />
}