feat(workbench): refine feature detail popover

This commit is contained in:
2026-07-10 18:44:07 +08:00
parent 66de96d9e4
commit 2258177726
4 changed files with 478 additions and 30 deletions
+143 -3
View File
@@ -1,3 +1,4 @@
import type { WaterNetworkSourceId } from "../map/sources";
import { formatValue } from "./format-value";
export type LocalizedFeatureProperty = {
@@ -6,6 +7,19 @@ export type LocalizedFeatureProperty = {
value: string;
};
export type FeaturePanelBadgeTone = "active" | "inactive" | "warning" | "critical" | "info";
export type FeaturePanelBadge = {
label: string;
tone: FeaturePanelBadgeTone;
};
export type FeaturePanelModel = {
id: string;
badge?: FeaturePanelBadge;
attributes: LocalizedFeatureProperty[];
};
const PROPERTY_LABELS: Record<string, string> = {
id: "编号",
gid: "要素编号",
@@ -20,6 +34,20 @@ const PROPERTY_LABELS: Record<string, string> = {
dn: "管径",
length: "长度",
roughness: "粗糙系数",
shape: "断面形状",
max_depth: "最大深度",
invert_elevation: "井底高程",
orifice_type: "孔口类型",
discharge_coeff: "流量系数",
gated: "闸门",
offset_height: "偏移高度",
geom1: "几何参数 1",
geom2: "几何参数 2",
pump_curve: "泵曲线",
startup_depth: "启动水深",
shutoff_depth: "停泵水深",
outfall_type: "排放类型",
stage_data: "阶段数据",
status: "状态",
material: "材质",
elevation: "高程",
@@ -42,6 +70,12 @@ const PROPERTY_ORDER: Record<string, number> = {
dn: 20,
length: 21,
roughness: 22,
max_depth: 30,
invert_elevation: 31,
orifice_type: 32,
outfall_type: 32,
shape: 33,
pump_curve: 34,
material: 23,
node1: 24,
from_node: 24,
@@ -56,6 +90,8 @@ const PROPERTY_ORDER: Record<string, number> = {
};
const STATUS_LABELS: Record<string, string> = {
on: "运行",
off: "停止",
active: "运行中",
inactive: "停用",
online: "在线",
@@ -67,6 +103,32 @@ const STATUS_LABELS: Record<string, string> = {
critical: "严重"
};
const OUTFALL_TYPE_LABELS: Record<string, string> = {
free: "自由出流"
};
const GATED_LABELS: Record<string, string> = {
no: "否",
false: "否",
"0": "否",
yes: "是",
true: "是",
"1": "是"
};
const FEATURE_PANEL_PROPERTY_KEYS: Record<WaterNetworkSourceId, readonly string[]> = {
pumps: ["id", "status", "startup_depth", "shutoff_depth"],
conduits: ["id", "length", "diameter"],
junctions: ["id", "invert_elevation", "max_depth"],
orifices: ["id", "discharge_coeff", "gated", "shape", "geom1", "geom2"],
outfalls: ["id", "invert_elevation", "outfall_type"]
};
const FEATURE_PANEL_BADGE_KEYS: Partial<Record<WaterNetworkSourceId, string>> = {
pumps: "status",
orifices: "gated"
};
export function getLocalizedFeatureProperties(properties: Record<string, unknown>): LocalizedFeatureProperty[] {
return Object.entries(properties).map(([key, value]) => ({
key,
@@ -75,6 +137,39 @@ export function getLocalizedFeatureProperties(properties: Record<string, unknown
})).sort((first, second) => getPropertyOrder(first.key) - getPropertyOrder(second.key));
}
export function getFeaturePanelProperties(
layer: WaterNetworkSourceId,
properties: Record<string, unknown>,
featureId?: string
): LocalizedFeatureProperty[] {
return FEATURE_PANEL_PROPERTY_KEYS[layer].map((key) => {
const value = key === "id" ? properties[key] ?? featureId : properties[key];
return {
key,
label: getFeaturePropertyLabel(key),
value: formatFeaturePropertyValue(key, value)
};
});
}
export function getFeaturePanelModel(
layer: WaterNetworkSourceId,
properties: Record<string, unknown>,
featureId?: string
): FeaturePanelModel {
const entries = getFeaturePanelProperties(layer, properties, featureId);
const id = entries.find((entry) => entry.key === "id")?.value ?? "暂无";
const badgeKey = FEATURE_PANEL_BADGE_KEYS[layer];
const badgeEntry = badgeKey ? entries.find((entry) => entry.key === badgeKey) : undefined;
return {
id,
badge: badgeEntry ? getFeaturePanelBadge(layer, badgeEntry.value) : undefined,
attributes: entries.filter((entry) => entry.key !== "id" && entry.key !== badgeKey)
};
}
export function getFeaturePropertyLabel(key: string) {
const normalizedKey = normalizePropertyKey(key);
return PROPERTY_LABELS[normalizedKey] ?? key;
@@ -87,11 +182,23 @@ export function formatFeaturePropertyValue(key: string, value: unknown) {
return STATUS_LABELS[value.toLowerCase()] ?? value;
}
if ((normalizedKey === "diameter" || normalizedKey === "dn") && value !== null && value !== undefined && value !== "") {
return `DN${formatValue(value)}`;
if (normalizedKey === "outfall_type" && typeof value === "string") {
return OUTFALL_TYPE_LABELS[value.toLowerCase()] ?? value;
}
if ((normalizedKey === "length" || normalizedKey === "elevation") && value !== null && value !== undefined && value !== "") {
if (normalizedKey === "gated" && value !== null && value !== undefined && value !== "") {
return GATED_LABELS[String(value).toLowerCase()] ?? formatValue(value);
}
if ((normalizedKey === "diameter" || normalizedKey === "dn") && value !== null && value !== undefined && value !== "") {
const diameterInMeters = typeof value === "number" ? value : Number(value);
return Number.isFinite(diameterInMeters)
? `${formatValue(diameterInMeters * 1000)} mm`
: formatValue(value);
}
if (["length", "elevation", "invert_elevation", "max_depth", "startup_depth", "shutoff_depth"].includes(normalizedKey)
&& value !== null && value !== undefined && value !== "") {
return `${formatValue(value)} m`;
}
@@ -109,3 +216,36 @@ function normalizePropertyKey(key: string) {
function isStatusKey(key: string) {
return normalizePropertyKey(key) === "status";
}
function getFeaturePanelBadge(
layer: WaterNetworkSourceId,
value: string
): FeaturePanelBadge {
if (layer === "orifices") {
return value === "是"
? { label: "有闸门", tone: "info" }
: value === "否"
? { label: "无闸门", tone: "inactive" }
: { label: `闸门 ${value}`, tone: "inactive" };
}
const normalizedValue = value.toLowerCase();
if (["运行", "运行中", "在线", "开启", "正常"].includes(value)) {
return { label: value, tone: "active" };
}
if (["停止", "停用", "离线", "关闭"].includes(value)) {
return { label: value, tone: "inactive" };
}
if (normalizedValue === "warning" || value === "告警") {
return { label: value, tone: "warning" };
}
if (normalizedValue === "critical" || value === "严重") {
return { label: value, tone: "critical" };
}
return { label: value, tone: "inactive" };
}