feat(workbench): expand SCADA monitoring and investigation presentation
This commit is contained in:
@@ -18,6 +18,7 @@ import type { OperationalTask } from "../api/operational-task-client";
|
||||
import type { InvestigationOverlay } from "../api/investigation-presentation";
|
||||
import type { InvestigationChart } from "../hooks/use-investigation-presentation";
|
||||
|
||||
import { FloatingWindow } from "@/shared/ui/floating-window";
|
||||
import { PresentationHistorySwitcher } from "./presentation-history-switcher";
|
||||
import { PresentationBusinessResults } from "./presentation-business-results";
|
||||
import type { PresentationHistoryControls } from "../utils/presentation-view-history";
|
||||
@@ -84,6 +85,7 @@ export function InvestigationPresentationPanel({
|
||||
const immediate = Boolean(reducedMotion) || keyboardAction;
|
||||
const contentId = useId();
|
||||
const reviewing = history?.selectedId != null;
|
||||
const chartWindowKey = scope === "chat" ? undefined : JSON.stringify([scope, task?.task_id, reviewing ? "history" : "live"]);
|
||||
const selected = history?.entries.find((entry) => entry.id === history.selectedId);
|
||||
const pending = selected ? history!.entries.filter((entry) => entry.id > selected.id).length : 0;
|
||||
const title = scope === "chat" ? "对话展示" : localPreview ? "本地预览展示" : "调查实时展示";
|
||||
@@ -104,7 +106,7 @@ export function InvestigationPresentationPanel({
|
||||
|
||||
return (
|
||||
<motion.aside
|
||||
layout={immediate ? false : "size"}
|
||||
layout={immediate || localPreview ? false : "size"}
|
||||
{...presentationFade(immediate)}
|
||||
onKeyDownCapture={() => setKeyboardAction(true)}
|
||||
onPointerDownCapture={() => setKeyboardAction(false)}
|
||||
@@ -112,18 +114,18 @@ export function InvestigationPresentationPanel({
|
||||
data-testid="investigation-presentation-panel"
|
||||
data-presentation-scope={scope}
|
||||
data-investigation-collapsed={collapsed}
|
||||
className={`workbench-shadcn-surface pointer-events-auto ${embedded ? "relative shrink-0" : "absolute bottom-[calc(var(--workbench-timeline-height)+1.25rem)] left-3 z-40"} flex max-h-[55dvh] flex-col overflow-hidden rounded-xl border border-border bg-background text-foreground shadow-lg`}
|
||||
className={`workbench-shadcn-surface pointer-events-auto ${embedded ? "relative shrink-0" : "absolute bottom-[calc(var(--workbench-timeline-height)+1.25rem)] left-3 z-40"} flex max-h-[55dvh] flex-col overflow-hidden rounded-xl border border-border bg-background text-foreground shadow-lg ${localPreview ? `${collapsed ? "h-28" : "h-[min(480px,55dvh)]"} [&>[data-investigation-content]]:flex-1` : ""}`}
|
||||
style={{ width, maxWidth: embedded ? "100%" : "calc(100% - 24px)" }}
|
||||
>
|
||||
<motion.header
|
||||
layout={immediate ? false : "position"}
|
||||
layout={immediate || localPreview ? false : "position"}
|
||||
transition={presentationTransition(immediate)}
|
||||
className="shrink-0 border-b border-border px-3 py-2.5"
|
||||
className={`shrink-0 border-b border-border px-3 ${localPreview ? `${collapsed ? "h-[110px]" : "min-h-[110px]"} py-2` : "py-2.5"}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0 space-y-1">
|
||||
<div className={localPreview ? "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-2 gap-y-1" : "flex items-start justify-between gap-2"}>
|
||||
<div className={localPreview ? "contents" : "min-w-0 space-y-1"}>
|
||||
<h2 className="text-sm font-semibold leading-6">{title}</h2>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className={`flex flex-wrap items-center gap-2 ${localPreview ? "col-span-2 row-start-2" : ""}`}>
|
||||
<Badge variant="secondary" className="px-1.5 py-0 text-[10px] font-medium">
|
||||
{reviewing
|
||||
? "历史回看"
|
||||
@@ -147,18 +149,14 @@ export function InvestigationPresentationPanel({
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="shrink-0"
|
||||
className={`shrink-0 ${localPreview ? "col-start-2 row-start-1" : ""}`}
|
||||
aria-label={collapsed ? "展开展示" : "收起展示"}
|
||||
title={collapsed ? "展开展示" : "收起展示"}
|
||||
aria-expanded={!collapsed}
|
||||
aria-controls={contentId}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
<motion.span
|
||||
className="inline-flex"
|
||||
animate={{ rotate: collapsed ? 180 : 0 }}
|
||||
transition={presentationTransition(immediate)}
|
||||
>
|
||||
<motion.span className="inline-flex" animate={{ rotate: collapsed ? 180 : 0 }} transition={presentationTransition(immediate)}>
|
||||
<ChevronDown size={16} aria-hidden />
|
||||
</motion.span>
|
||||
</Button>
|
||||
@@ -180,12 +178,12 @@ export function InvestigationPresentationPanel({
|
||||
? "已载入所选调查保存的展示结果"
|
||||
: "跟随所选调查,展示地图操作与结果"}
|
||||
</p>
|
||||
{history?.entries.length ? <PresentationHistorySwitcher {...history} /> : null}
|
||||
{history?.entries.length ? <PresentationHistorySwitcher {...history} playback={collapsed ? undefined : history.playback} /> : null}
|
||||
</motion.header>
|
||||
<AnimatePresence initial={false} mode="popLayout">
|
||||
{!collapsed ? (
|
||||
<PresentationViewport
|
||||
key={history?.selectedId ?? "current"}
|
||||
key="presentation-content"
|
||||
immediate={immediate}
|
||||
id={contentId}
|
||||
>
|
||||
@@ -306,22 +304,31 @@ export function InvestigationPresentationPanel({
|
||||
{item.chart.title}
|
||||
</h3>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground">
|
||||
{item.chart.chart_type === "bar" ? "柱状图" : "折线图"} ·{" "}
|
||||
{item.chart.series.some((series) => series.type === "scatter") ? "折线与历史散点" : item.chart.chart_type === "bar" ? "柱状图" : "折线图"} ·{" "}
|
||||
{item.chart.x_data.length} 个
|
||||
{item.chart.chart_type === "bar" ? "类别" : "时间点"}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="shrink-0"
|
||||
disabled={reviewing}
|
||||
aria-label={`关闭图表 ${item.chart.title}`}
|
||||
onClick={() => onCloseChart(item.chart_id)}
|
||||
>
|
||||
<X size={14} aria-hidden />
|
||||
</Button>
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<FloatingWindow title={item.chart.title} fillContent
|
||||
openRequestKey={chartWindowKey === undefined ? undefined : JSON.stringify([chartWindowKey, item])}>
|
||||
{() => <div className="flex h-full min-h-[420px] min-w-0 flex-col gap-3">
|
||||
<AgentChartView chart={item.chart} embedded expanded />
|
||||
<div className="shrink-0">{references(item.evidence_refs)}</div>
|
||||
</div>}
|
||||
</FloatingWindow>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="shrink-0"
|
||||
disabled={reviewing}
|
||||
aria-label={`关闭图表 ${item.chart.title}`}
|
||||
onClick={() => onCloseChart(item.chart_id)}
|
||||
>
|
||||
<X size={14} aria-hidden />
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="px-3 pb-2 pt-0">
|
||||
<AgentChartView chart={item.chart} embedded />
|
||||
@@ -335,6 +342,7 @@ export function InvestigationPresentationPanel({
|
||||
blocks={blocks}
|
||||
records={records}
|
||||
reviewing={reviewing}
|
||||
chartWindowKey={chartWindowKey}
|
||||
onLocateTargets={onLocateTargets}
|
||||
/>
|
||||
{!charts.length &&
|
||||
|
||||
Reference in New Issue
Block a user