feat(flushing): 添加阀门状态与设置值控制

This commit is contained in:
2026-08-17 18:29:00 +08:00
parent 1ac7372585
commit 9e79d52dc8
7 changed files with 451 additions and 152 deletions
+6 -37
View File
@@ -25,6 +25,12 @@ import {
} from "./toolbarFeatureHelpers";
import { useToolbarChatActions } from "./useToolbarChatActions";
import { useStyleEditor } from "./useStyleEditor";
import {
type LinkStatus,
isLinkStatus,
normalizeValveSetting,
validateValveSetting,
} from "./valveControl";
import { config, NETWORK_NAME } from "@/config/config";
import { useProject } from "@/contexts/ProjectContext";
@@ -41,7 +47,6 @@ interface ToolbarProps {
enableCompare?: boolean;
}
type LinkStatus = "OPEN" | "CLOSED" | "ACTIVE";
type ValveProperties = {
vType: string | null;
setting: string | null;
@@ -50,42 +55,6 @@ type ValveProperties = {
const isValveLayer = (layerId: string | undefined) =>
layerId === "geo_valves_mat" || layerId === "geo_valves";
const isLinkStatus = (value: unknown): value is LinkStatus =>
value === "OPEN" || value === "CLOSED" || value === "ACTIVE";
const normalizeValveSetting = (value: unknown): string | null => {
if (value === undefined || value === null) return null;
return String(value);
};
const validateValveSetting = (
valveType: string | null,
value: string,
): string | null => {
const normalizedType = valveType?.toUpperCase();
const trimmedValue = value.trim();
const numericTypes = new Set(["PRV", "PSV", "PBV", "FCV", "TCV"]);
if (normalizedType === "GPV") {
return trimmedValue ? null : "GPV 阀门设置值必须是非空曲线 ID。";
}
if (numericTypes.has(normalizedType ?? "")) {
if (!trimmedValue) {
return "阀门设置值必须是 0 或正数。";
}
const numericValue = Number(trimmedValue);
if (!Number.isFinite(numericValue) || numericValue < 0) {
return "阀门设置值必须是有限数字,且大于或等于 0。";
}
return null;
}
return trimmedValue ? null : "阀门设置值不能为空。";
};
const Toolbar: React.FC<ToolbarProps> = ({
hiddenButtons,
queryType,
@@ -1,6 +1,10 @@
import Feature from "ol/Feature";
import { FLOW_DISPLAY_UNIT, toM3h } from "@utils/units";
import {
getValveSettingHelperText,
VALVE_STATUS_OPTIONS,
} from "./valveControl";
type ToolbarBaseProperty = {
label: string;
@@ -66,30 +70,6 @@ export type ValveSettingPropertyOptions = {
onSave: (value: string) => Promise<void>;
};
const getValveSettingHelperText = (
vType: string | null,
status?: string | null,
) => {
if (status === "OPEN" || status === "CLOSED") {
return "开启/关闭状态下 EPANET 会忽略阀门设置值";
}
switch (vType?.toUpperCase()) {
case "PRV":
case "PSV":
case "PBV":
return "压力设置值,需为 0 或正数";
case "FCV":
return "流量设置值,需为 0 或正数";
case "TCV":
return "损失系数,需为 0 或正数";
case "GPV":
return "水头损失曲线 ID";
default:
return "阀门类型相关设置值";
}
};
const getFeatureHistoryType = (feature: Feature): string | null => {
const layerId = feature.getId()?.toString().split(".")[0] || "";
if (layerId.includes("pipe")) return "pipe";
@@ -366,11 +346,7 @@ export const buildFeatureProperties = (
type: "select" as const,
label: "开关状态",
value: valveStatus.value ?? "",
options: [
{ label: "开启", value: "OPEN" },
{ label: "关闭", value: "CLOSED" },
{ label: "激活", value: "ACTIVE" },
],
options: VALVE_STATUS_OPTIONS,
placeholder: valveStatus.loading ? "加载中" : "未设置",
disabled: valveStatus.loading,
saving: valveStatus.saving,
@@ -0,0 +1,36 @@
import {
getValveSettingHelperText,
isLinkStatus,
normalizeValveSetting,
VALVE_STATUS_OPTIONS,
validateValveSetting,
} from "./valveControl";
describe("valveControl", () => {
it("provides the status translations shared by valve editors", () => {
expect(VALVE_STATUS_OPTIONS).toEqual([
{ label: "开启", value: "OPEN" },
{ label: "关闭", value: "CLOSED" },
{ label: "激活", value: "ACTIVE" },
]);
expect(isLinkStatus("ACTIVE")).toBe(true);
expect(isLinkStatus("UNKNOWN")).toBe(false);
});
it("normalizes setting values without dropping zero", () => {
expect(normalizeValveSetting(0)).toBe("0");
expect(normalizeValveSetting(null)).toBeNull();
});
it("validates numeric and curve settings by valve type", () => {
expect(validateValveSetting("PRV", "2.5")).toBeNull();
expect(validateValveSetting("PRV", "-1")).toContain("大于或等于 0");
expect(validateValveSetting("GPV", "curve-1")).toBeNull();
expect(validateValveSetting("GPV", " ")).toContain("曲线 ID");
});
it("explains that OPEN and CLOSED ignore the setting", () => {
expect(getValveSettingHelperText("PRV", "OPEN")).toContain("忽略");
expect(getValveSettingHelperText("FCV", "ACTIVE")).toContain("流量");
});
});
@@ -0,0 +1,70 @@
export type LinkStatus = "OPEN" | "CLOSED" | "ACTIVE";
export const VALVE_STATUS_OPTIONS: Array<{
label: string;
value: LinkStatus;
}> = [
{ label: "开启", value: "OPEN" },
{ label: "关闭", value: "CLOSED" },
{ label: "激活", value: "ACTIVE" },
];
export const isLinkStatus = (value: unknown): value is LinkStatus =>
value === "OPEN" || value === "CLOSED" || value === "ACTIVE";
export const normalizeValveSetting = (value: unknown): string | null => {
if (value === undefined || value === null) return null;
return String(value);
};
export const validateValveSetting = (
valveType: string | null | undefined,
value: string,
): string | null => {
const normalizedType = valveType?.toUpperCase();
const trimmedValue = value.trim();
const numericTypes = new Set(["PRV", "PSV", "PBV", "FCV", "TCV"]);
if (normalizedType === "GPV") {
return trimmedValue ? null : "GPV 阀门设置值必须是非空曲线 ID。";
}
if (numericTypes.has(normalizedType ?? "")) {
if (!trimmedValue) {
return "阀门设置值必须是 0 或正数。";
}
const numericValue = Number(trimmedValue);
if (!Number.isFinite(numericValue) || numericValue < 0) {
return "阀门设置值必须是有限数字,且大于或等于 0。";
}
return null;
}
return trimmedValue ? null : "阀门设置值不能为空。";
};
export const getValveSettingHelperText = (
valveType: string | null | undefined,
status?: LinkStatus | string | null,
) => {
if (status === "OPEN" || status === "CLOSED") {
return "开启/关闭状态下 EPANET 会忽略阀门设置值";
}
switch (valveType?.toUpperCase()) {
case "PRV":
case "PSV":
case "PBV":
return "压力设置值,需为 0 或正数";
case "FCV":
return "流量设置值,需为 0 或正数";
case "TCV":
return "损失系数,需为 0 或正数";
case "GPV":
return "水头损失曲线 ID";
default:
return "阀门类型相关设置值";
}
};