refactor(ui): refine glass surfaces

This commit is contained in:
2026-07-15 11:41:51 +08:00
parent 2b768c2c06
commit 8978f2d167
11 changed files with 271 additions and 208 deletions
@@ -46,12 +46,12 @@ const FEATURE_LAYER_META: Record<
iconClassName: string;
}
> = {
conduits: { icon: ConduitFeatureIcon, label: "管渠", headerClassName: "glass-normal", labelClassName: "text-teal-800", iconClassName: "bg-teal-700 text-white ring-teal-900/10" },
junctions: { icon: JunctionFeatureIcon, label: "检查井", headerClassName: "glass-info", labelClassName: "text-blue-800", iconClassName: "bg-sky-700 text-white ring-sky-900/10" },
orifices: { icon: OrificeFeatureIcon, label: "孔口", headerClassName: "bg-[var(--glass-readable)]", labelClassName: "text-slate-700", iconClassName: "bg-slate-600 text-white ring-slate-900/10" },
outfalls: { icon: OutfallFeatureIcon, label: "排放口", headerClassName: "glass-info", labelClassName: "text-blue-800", iconClassName: "bg-slate-700 text-white ring-slate-900/10" },
pumps: { icon: PumpFeatureIcon, label: "泵", headerClassName: "glass-normal", labelClassName: "text-teal-800", iconClassName: "bg-cyan-800 text-white ring-cyan-950/10" },
scada: { icon: JunctionFeatureIcon, label: "SCADA 测点", headerClassName: "glass-info", labelClassName: "text-blue-800", iconClassName: "bg-blue-700 text-white ring-blue-900/10" }
conduits: { icon: ConduitFeatureIcon, label: "管渠", headerClassName: "bg-teal-50/70", labelClassName: "text-teal-800", iconClassName: "bg-teal-700 text-white ring-teal-900/10" },
junctions: { icon: JunctionFeatureIcon, label: "检查井", headerClassName: "bg-blue-50/70", labelClassName: "text-blue-800", iconClassName: "bg-sky-700 text-white ring-sky-900/10" },
orifices: { icon: OrificeFeatureIcon, label: "孔口", headerClassName: "bg-slate-50/70", labelClassName: "text-slate-700", iconClassName: "bg-slate-600 text-white ring-slate-900/10" },
outfalls: { icon: OutfallFeatureIcon, label: "排放口", headerClassName: "bg-blue-50/70", labelClassName: "text-blue-800", iconClassName: "bg-slate-700 text-white ring-slate-900/10" },
pumps: { icon: PumpFeatureIcon, label: "泵", headerClassName: "bg-teal-50/70", labelClassName: "text-teal-800", iconClassName: "bg-cyan-800 text-white ring-cyan-950/10" },
scada: { icon: JunctionFeatureIcon, label: "SCADA 测点", headerClassName: "bg-blue-50/70", labelClassName: "text-blue-800", iconClassName: "bg-blue-700 text-white ring-blue-900/10" }
};
const BADGE_CLASS_NAMES: Record<FeaturePanelBadgeTone, string> = {
@@ -128,7 +128,7 @@ export function FeaturePopover({ feature, onClose }: FeaturePopoverProps) {
return (
<aside
className={cn(
"glass-focus pointer-events-auto absolute left-1/2 top-[92px] z-20 w-[min(380px,calc(100vw-24px))] -translate-x-1/2 overflow-hidden border",
"pointer-events-auto absolute left-1/2 top-[92px] z-20 w-[min(380px,calc(100vw-24px))] -translate-x-1/2 overflow-hidden bg-white/[0.94] shadow-2xl shadow-blue-950/20 ring-1 ring-white/80 backdrop-blur-xl",
MAP_MAJOR_PANEL_RADIUS_CLASS_NAME
)}
>
@@ -218,7 +218,7 @@ function ScadaFeaturePopover({ feature, copied, canCopy, onCopy, onClose }: Scad
return (
<aside
className={cn(
"glass-focus pointer-events-auto absolute left-1/2 top-[92px] z-20 w-[min(380px,calc(100vw-24px))] border",
"pointer-events-auto absolute left-1/2 top-[92px] z-20 w-[min(380px,calc(100vw-24px))] bg-white/[0.94] shadow-2xl shadow-blue-950/20 ring-1 ring-white/80 backdrop-blur-xl",
"-translate-x-1/2 overflow-hidden",
MAP_MAJOR_PANEL_RADIUS_CLASS_NAME
)}
@@ -232,7 +232,8 @@ function ScadaFeaturePopover({ feature, copied, canCopy, onCopy, onClose }: Scad
{formatFeaturePropertyValue("device_type_name", properties.device_type_name)}
</span>
<span className={cn(
"inline-flex shrink-0 items-center gap-1.5 rounded-full bg-[var(--glass-menu)] px-2 py-0.5 text-xs font-semibold ring-1 ring-inset",
"inline-flex shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-semibold ring-1 ring-inset",
active ? "bg-emerald-500/10" : "bg-slate-500/10",
active ? "text-emerald-700 ring-emerald-600/20" : "text-slate-600 ring-slate-500/20"
)}>
<span className={cn("h-1.5 w-1.5 rounded-full", active ? "bg-emerald-500" : "bg-slate-400")} />
@@ -288,8 +289,8 @@ function ScadaFeaturePopover({ feature, copied, canCopy, onCopy, onClose }: Scad
function getScadaPresentation(rawTypeId: unknown) {
const typeId = Number(rawTypeId);
if (typeId === 72) return { iconPath: SCADA_ICON_PATHS.waterQuality, headerClassName: "glass-normal", labelClassName: "text-teal-800" };
if (typeId === 58) return { iconPath: SCADA_ICON_PATHS.radarLevel, headerClassName: "glass-info", labelClassName: "text-blue-800" };
if (typeId === 45) return { iconPath: SCADA_ICON_PATHS.ultrasonicFlow, headerClassName: "glass-warning", labelClassName: "text-orange-800" };
return { iconPath: SCADA_ICON_PATHS.unknown, headerClassName: "bg-[var(--glass-readable)]", labelClassName: "text-slate-700" };
if (typeId === 72) return { iconPath: SCADA_ICON_PATHS.waterQuality, headerClassName: "bg-teal-50/70", labelClassName: "text-teal-800" };
if (typeId === 58) return { iconPath: SCADA_ICON_PATHS.radarLevel, headerClassName: "bg-blue-50/70", labelClassName: "text-blue-800" };
if (typeId === 45) return { iconPath: SCADA_ICON_PATHS.ultrasonicFlow, headerClassName: "bg-orange-50/70", labelClassName: "text-orange-800" };
return { iconPath: SCADA_ICON_PATHS.unknown, headerClassName: "bg-slate-50/70", labelClassName: "text-slate-700" };
}
@@ -242,10 +242,10 @@ export function ScheduledConditionFeed({
"scheduled-feed-panel-shell pointer-events-auto max-w-[calc(100vw-6rem)] overflow-hidden p-3 motion-reduce:transition-none",
expanded ? "flex max-h-[calc(100dvh-8rem)] w-[880px] flex-col 2xl:w-[960px]" : "w-[432px]",
"rounded-2xl border",
"glass-control"
expanded ? "glass-panel" : "glass-control"
)}
>
<header className={cn("flex items-center gap-2.5 px-2.5 py-2", "rounded-xl", MAP_READABLE_SURFACE_STRONG_CLASS_NAME)}>
<header className={cn("flex items-center gap-2.5 px-2.5 py-2", "rounded-xl", expanded ? "bg-white/30" : MAP_READABLE_SURFACE_STRONG_CLASS_NAME)}>
<span className={cn("grid h-8 w-8 shrink-0 place-items-center bg-blue-50 text-blue-700", "rounded-lg")}>
<CalendarClock size={17} aria-hidden="true" />
</span>
@@ -607,7 +607,7 @@ function ConditionDetailSkeleton() {
<DetailScroll>
<div
className={cn(
"relative min-h-[420px] overflow-hidden border border-slate-200/80 bg-[var(--glass-menu)] px-4 py-3",
"relative min-h-[420px] overflow-hidden border border-slate-200/60 bg-white/30 px-4 py-3",
"rounded-xl"
)}
role="status"
@@ -307,7 +307,7 @@ export function useWorkbenchAgent({ onUiEnvelope, onFrontendAction }: UseWorkben
setSelectedModel((current) => current || backendConnection.defaultModel || backendConnection.models[0]?.id || "");
if (chatRef.current.status === "ready") {
setRuntimeAvailability("connected");
setStatusLabel("Agent 后端已连接");
setStatusLabel("准备就绪");
}
}, [backendConnection]);
+3 -4
View File
@@ -57,6 +57,7 @@ import type { DetailFeature, ScheduledConditionItem, ScheduledConditionRecord, W
import { createAlertQueueConversationPrompt } from "./utils/scheduled-condition-prompts";
const HEADER_DATA_TIME_STEP_MINUTES = 5;
const INITIAL_HEADER_DATA_TIME = "同步中";
function formatHeaderDataTime(date: Date) {
const snappedMinutes = Math.floor(date.getMinutes() / HEADER_DATA_TIME_STEP_MINUTES) * HEADER_DATA_TIME_STEP_MINUTES;
@@ -85,9 +86,7 @@ export function MapWorkbenchPage() {
const mapContainerRef = useRef<HTMLDivElement | null>(null);
const [detailFeature, setDetailFeature] = useState<DetailFeature | null>(null);
const [impactVisible, setImpactVisible] = useState(false);
const [isLargeScreen, setIsLargeScreen] = useState(
() => typeof window !== "undefined" && window.matchMedia("(min-width: 1024px)").matches
);
const [isLargeScreen, setIsLargeScreen] = useState(false);
const [activeToolId, setActiveToolId] = useState<ToolbarToolId | null>(null);
const [conditionFeedVisible, setConditionFeedVisible] = useState(true);
const [conditionFeedMounted, setConditionFeedMounted] = useState(true);
@@ -107,7 +106,7 @@ export function MapWorkbenchPage() {
const [agentUiResults, setAgentUiResults] = useState<AgentUiResult[]>([]);
const [scheduledConditions, setScheduledConditions] = useState<ScheduledConditionItem[]>([]);
const [scheduledConditionsLoading, setScheduledConditionsLoading] = useState(true);
const [headerDataTime, setHeaderDataTime] = useState(() => formatHeaderDataTime(new Date()));
const [headerDataTime, setHeaderDataTime] = useState(INITIAL_HEADER_DATA_TIME);
const baseLayerOptions = useMemo(
() =>
BASE_LAYER_OPTIONS.map((option) => ({