diff --git a/.changeset/desktop-alert-log-jump.md b/.changeset/desktop-alert-log-jump.md new file mode 100644 index 00000000..92617dd2 --- /dev/null +++ b/.changeset/desktop-alert-log-jump.md @@ -0,0 +1,5 @@ +--- +"@sfmc-bds/desktop": minor +--- + +点击告警后打开日志控制台,并定位到对应行高亮。 diff --git a/packages/desktop/src/renderer/app/App.tsx b/packages/desktop/src/renderer/app/App.tsx index 618a9dd5..7d41af4f 100644 --- a/packages/desktop/src/renderer/app/App.tsx +++ b/packages/desktop/src/renderer/app/App.tsx @@ -139,7 +139,7 @@ function Workspace() { /** 外壳:三栏布局、快捷键与全局对话框 */ function Shell() { - const { selected, model, page, setPage, appInfo } = useDesktop(); + const { selected, model, page, setPage, appInfo, logFocus } = useDesktop(); const [layout, setLayout] = useState(readLayout); const narrow = useMediaQuery(NARROW_QUERY); /** 窄窗口下右侧栏以浮层打开(不持久化) */ @@ -163,6 +163,9 @@ function Shell() { } }, [model.handshake, page, setPage]); useEffect(() => setRailOverlay(false), [narrow]); + useEffect(() => { + if (logFocus) setRailOverlay(false); + }, [logFocus]); const railOpen = narrow ? railOverlay : layout.rail; const toggleSidebar = useCallback(() => setLayout((value) => ({ ...value, sidebar: !value.sidebar })), []); diff --git a/packages/desktop/src/renderer/app/desktop.tsx b/packages/desktop/src/renderer/app/desktop.tsx index 8ef0b792..1ae1ab7a 100644 --- a/packages/desktop/src/renderer/app/desktop.tsx +++ b/packages/desktop/src/renderer/app/desktop.tsx @@ -82,6 +82,10 @@ interface DesktopValue { model: Model; page: PageKey; setPage: (page: PageKey) => void; + /** 打开日志控制台并定位到这一条 */ + logFocus: { entry: LogEntryWire; nonce: number } | null; + openLog: (entry: LogEntryWire) => void; + clearLogFocus: () => void; busy: boolean; /** 包裹异步操作:显示全局进度、捕获错误并提示 */ guarded: (action: () => Promise) => Promise; @@ -148,7 +152,17 @@ export function DesktopProvider({ children }: { children: ReactNode }) { const [profiles, setProfiles] = useState([]); const [current, setCurrentState] = useState(""); const [models, setModels] = useState>({}); - const [page, setPage] = useState("overview"); + const [page, setPageState] = useState("overview"); + const [logFocus, setLogFocus] = useState<{ entry: LogEntryWire; nonce: number } | null>(null); + const setPage = useCallback((next: PageKey) => { + if (next !== "logs") setLogFocus(null); + setPageState(next); + }, []); + const openLog = useCallback((entry: LogEntryWire) => { + setLogFocus({ entry, nonce: Date.now() }); + setPageState("logs"); + }, []); + const clearLogFocus = useCallback(() => setLogFocus(null), []); const [busyCount, setBusyCount] = useState(0); const [flow, setFlow] = useState(null); const [profileDialog, setProfileDialog] = useState({ open: false, editing: null }); @@ -167,6 +181,7 @@ export function DesktopProvider({ children }: { children: ReactNode }) { const model = models[current] ?? blank(); const setCurrent = useCallback((id: string) => { setCurrentState(id); + setLogFocus(null); if (id) localStorage.setItem(LAST_INSTANCE_KEY, id); }, []); @@ -526,6 +541,9 @@ export function DesktopProvider({ children }: { children: ReactNode }) { model, page, setPage, + logFocus, + openLog, + clearLogFocus, busy: busyCount > 0, guarded, patch, @@ -549,7 +567,7 @@ export function DesktopProvider({ children }: { children: ReactNode }) { appInfo, desktopUpdate, }), - [ready, profiles, current, setCurrent, selected, models, model, page, busyCount, guarded, patch, refresh, request, submit, editable, connect, disconnect, startAttach, createDeployment, applyAttachment, confirmHost, flow, profileDialog, saveProfile, removeProfile, appInfo, desktopUpdate] + [ready, profiles, current, setCurrent, selected, models, model, page, setPage, logFocus, openLog, clearLogFocus, busyCount, guarded, patch, refresh, request, submit, editable, connect, disconnect, startAttach, createDeployment, applyAttachment, confirmHost, flow, profileDialog, saveProfile, removeProfile, appInfo, desktopUpdate] ); return {children}; } diff --git a/packages/desktop/src/renderer/components/RightRail.tsx b/packages/desktop/src/renderer/components/RightRail.tsx index 653fb111..5f6ffb6a 100644 --- a/packages/desktop/src/renderer/components/RightRail.tsx +++ b/packages/desktop/src/renderer/components/RightRail.tsx @@ -24,7 +24,7 @@ function RailSection({ title, action, onAction, children }: { title: string; act /** 右侧动态栏 */ export function RightRail() { - const { model, selected, setPage, page } = useDesktop(); + const { model, selected, setPage, page, openLog } = useDesktop(); const [now, setNow] = useState(Date.now()); useEffect(() => { const timer = setInterval(() => setNow(Date.now()), 30_000); @@ -42,7 +42,7 @@ export function RightRail() { ) : ( <> setPage("logs")}> - setPage("logs")} /> + setPage("tasks")}> setPage("tasks")} /> diff --git a/packages/desktop/src/renderer/components/TitleBar.tsx b/packages/desktop/src/renderer/components/TitleBar.tsx index 0b2bf65b..1a5fd178 100644 --- a/packages/desktop/src/renderer/components/TitleBar.tsx +++ b/packages/desktop/src/renderer/components/TitleBar.tsx @@ -111,7 +111,7 @@ function ConnectionDetails() { /** 告警按钮:角标为"上次打开后新增"的警告/错误数,打开弹层即视为已读(按实例分别记录) */ function AlertsButton() { - const { model, current, setPage } = useDesktop(); + const { model, current, openLog } = useDesktop(); const [seen, setSeen] = useState>({}); const [open, setOpen] = useState(false); const seenAt = seen[current] ?? 0; @@ -143,9 +143,9 @@ function AlertsButton() { logs={model.logs} now={Date.now()} limit={8} - onSelect={() => { + onSelect={(log) => { setOpen(false); - setPage("logs"); + openLog(log); }} /> diff --git a/packages/desktop/src/renderer/components/feeds.tsx b/packages/desktop/src/renderer/components/feeds.tsx index d575d04b..0f47d755 100644 --- a/packages/desktop/src/renderer/components/feeds.tsx +++ b/packages/desktop/src/renderer/components/feeds.tsx @@ -33,14 +33,14 @@ export function serviceState(row: ServiceStatusRow): { tone: Tone; label: string } /** 告警列表:错误用红色色块、警告用琥珀色块,正文取日志首行 */ -export function AlertFeed({ logs, now, limit = 6, onSelect, emptyText = "已加载日志中没有警告或错误" }: { logs: LogEntryWire[]; now: number; limit?: number; onSelect?: () => void; emptyText?: string }) { +export function AlertFeed({ logs, now, limit = 6, onSelect, emptyText = "已加载日志中没有警告或错误" }: { logs: LogEntryWire[]; now: number; limit?: number; onSelect?: (log: LogEntryWire) => void; emptyText?: string }) { const alerts = groupAlerts(logs, limit); if (!alerts.length) return ; return (
    {alerts.map(({ log, count, firstTime }, index) => (
  • - } > - setPage("logs")} /> +