import { useEffect, useRef, useState } from 'react'; import { Button, Empty, Modal, Tag } from 'antd'; import { SafetyCertificateOutlined } from '@ant-design/icons'; import type { SecurityUpdateIssue, SecurityUpdateStatus } from '../types'; import { getSecurityUpdateIssueActionMeta, getSecurityUpdateIssueSeverityMeta, getSecurityUpdateItemStatusMeta, getSecurityUpdateStatusMeta, sortSecurityUpdateIssues, } from '../utils/securityUpdatePresentation'; import { hasSecurityUpdateRecentResult, resolveSecurityUpdateFocusState, type SecurityUpdateFocusState, type SecurityUpdateSettingsFocusTarget, } from '../utils/securityUpdateRepairFlow'; import type { OverlayWorkbenchTheme } from '../utils/overlayWorkbenchTheme'; import { SECURITY_UPDATE_ACTION_BUTTON_CLASS, SECURITY_UPDATE_MODAL_CLASS, SECURITY_UPDATE_RESULT_CARD_ACTIVE_CLASS, SECURITY_UPDATE_RESULT_CARD_CLASS, getSecurityUpdateActionButtonStyle, getSecurityUpdateSectionSurfaceStyle, getSecurityUpdateShellSurfaceStyle, } from '../utils/securityUpdateVisuals'; interface SecurityUpdateSettingsModalProps { open: boolean; darkMode: boolean; overlayTheme: OverlayWorkbenchTheme; surfaceOpacity?: number; status: SecurityUpdateStatus; focusTarget?: SecurityUpdateSettingsFocusTarget | null; focusRequest?: number; onClose: () => void; onStart: () => void; onRetry: () => void; onRestart: () => void; onIssueAction: (issue: SecurityUpdateIssue) => void; } const sectionStyle = ( overlayTheme: OverlayWorkbenchTheme, surfaceOpacity: number, options?: { emphasized?: boolean }, ) => ({ borderRadius: 14, padding: 16, ...getSecurityUpdateSectionSurfaceStyle(overlayTheme, { ...options, surfaceOpacity, }), }); const EMPTY_FOCUS_STATE: SecurityUpdateFocusState = { target: null, pulseKey: null, }; const SecurityUpdateSettingsModal = ({ open, darkMode, overlayTheme, surfaceOpacity = 1, status, focusTarget = null, focusRequest = 0, onClose, onStart, onRetry, onRestart, onIssueAction, }: SecurityUpdateSettingsModalProps) => { const statusMeta = getSecurityUpdateStatusMeta(status); const sortedIssues = sortSecurityUpdateIssues(status.issues); const showRecentResult = hasSecurityUpdateRecentResult(status); const showStart = status.overallStatus === 'pending' || status.overallStatus === 'postponed'; const showRetry = status.overallStatus === 'needs_attention'; const showRestart = status.overallStatus === 'needs_attention' || status.overallStatus === 'rolled_back'; const actionButtonStyle = getSecurityUpdateActionButtonStyle(); const [activeFocus, setActiveFocus] = useState(EMPTY_FOCUS_STATE); const statusSectionRef = useRef(null); const recentResultRef = useRef(null); useEffect(() => { const nextFocus = resolveSecurityUpdateFocusState(open, focusTarget, focusRequest); if (!nextFocus.target || !nextFocus.pulseKey) { setActiveFocus(EMPTY_FOCUS_STATE); return undefined; } const targetNode = nextFocus.target === 'recent_result' ? recentResultRef.current : statusSectionRef.current; if (!targetNode) { return undefined; } setActiveFocus(EMPTY_FOCUS_STATE); const animationFrame = window.requestAnimationFrame(() => { targetNode.scrollIntoView({ block: 'nearest', behavior: 'smooth', }); targetNode.focus({ preventScroll: true }); setActiveFocus(nextFocus); }); const highlightTimer = window.setTimeout(() => { setActiveFocus((current) => ( current.pulseKey === nextFocus.pulseKey ? EMPTY_FOCUS_STATE : current )); }, 1800); return () => { window.cancelAnimationFrame(animationFrame); window.clearTimeout(highlightTimer); }; }, [focusRequest, focusTarget, open]); return (
安全更新
管理已保存配置的安全更新状态与待处理项。
)} open={open} onCancel={onClose} footer={[ showRetry ? ( ) : null, showRestart ? ( ) : null, showStart ? ( ) : null, , ]} width={760} styles={{ content: getSecurityUpdateShellSurfaceStyle(overlayTheme, surfaceOpacity), header: { background: 'transparent', borderBottom: 'none', paddingBottom: 8 }, body: { paddingTop: 8, maxHeight: 640, overflowY: 'auto' }, footer: { background: 'transparent', borderTop: 'none', paddingTop: 10 }, }} >
当前状态:{statusMeta.label}
{statusMeta.description}
{statusMeta.label}
影响范围
{[ { label: '总计', value: status.summary.total }, { label: '已更新', value: status.summary.updated }, { label: '待处理', value: status.summary.pending }, { label: '已跳过', value: status.summary.skipped }, { label: '失败', value: status.summary.failed }, ].map((item) => (
{item.label}
{item.value}
))}
待处理清单
{sortedIssues.length === 0 ? ( ) : (
{sortedIssues.map((issue) => { const actionMeta = getSecurityUpdateIssueActionMeta(issue); const itemStatusMeta = getSecurityUpdateItemStatusMeta(issue.status); const issueSeverityMeta = getSecurityUpdateIssueSeverityMeta(issue.severity); return (
{issue.title || issue.message || issue.id}
状态:{itemStatusMeta.label} 级别:{issueSeverityMeta.label}
{issue.message || '当前项需要进一步处理后才能完成安全更新。'}
); })}
)}
{showRecentResult ? (
最近一次结果
{status.backupPath ? (
备份位置:{status.backupPath}
) : null} {status.lastError ? (
最近错误:{status.lastError}
) : null}
) : null}
); }; export type { SecurityUpdateSettingsModalProps }; export default SecurityUpdateSettingsModal;