fix(analysis): preserve tab panel state
This commit is contained in:
@@ -25,14 +25,17 @@ import dayjs, { Dayjs } from "dayjs";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import { api } from "@/lib/api";
|
||||
import { NETWORK_NAME, config } from "@config/config";
|
||||
import { useControllableObjectState } from "@components/olmap/core/useControllableState";
|
||||
import { FLOW_DISPLAY_UNIT, toM3s } from "@utils/units";
|
||||
import { BurstLocationResult } from "./types";
|
||||
|
||||
interface Props {
|
||||
onResult: (result: BurstLocationResult) => void;
|
||||
state?: BurstLocationAnalysisParametersState;
|
||||
onStateChange?: (state: BurstLocationAnalysisParametersState) => void;
|
||||
}
|
||||
|
||||
interface SchemeItem {
|
||||
export interface SchemeItem {
|
||||
scheme_id: number;
|
||||
scheme_name: string;
|
||||
scheme_type: string;
|
||||
@@ -45,24 +48,60 @@ interface SchemeItem {
|
||||
|
||||
type DataSource = "monitoring" | "simulation";
|
||||
|
||||
const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
export interface BurstLocationAnalysisParametersState {
|
||||
schemeName: string;
|
||||
dataSource: DataSource;
|
||||
schemes: SchemeItem[];
|
||||
selectedSchemeId: number | "";
|
||||
burstLeakage: number;
|
||||
enableFlow: boolean;
|
||||
burstStartTime: Dayjs | null;
|
||||
burstEndTime: Dayjs | null;
|
||||
minDpressure: number;
|
||||
basicPressure: number;
|
||||
advancedOpen: boolean;
|
||||
}
|
||||
|
||||
export const createBurstLocationAnalysisParametersState =
|
||||
(): BurstLocationAnalysisParametersState => ({
|
||||
schemeName: `Burst_Locate_${Date.now()}`,
|
||||
dataSource: "monitoring",
|
||||
schemes: [],
|
||||
selectedSchemeId: "",
|
||||
burstLeakage: 1440,
|
||||
enableFlow: false,
|
||||
burstStartTime: dayjs().subtract(20, "minute"),
|
||||
burstEndTime: dayjs().subtract(5, "minute"),
|
||||
minDpressure: 2,
|
||||
basicPressure: 10,
|
||||
advancedOpen: false,
|
||||
});
|
||||
|
||||
const AnalysisParameters: React.FC<Props> = ({
|
||||
onResult,
|
||||
state,
|
||||
onStateChange,
|
||||
}) => {
|
||||
const { open } = useNotification();
|
||||
const [schemeName, setSchemeName] = useState(`Burst_Locate_${Date.now()}`);
|
||||
const [dataSource, setDataSource] = useState<DataSource>("monitoring");
|
||||
const [schemes, setSchemes] = useState<SchemeItem[]>([]);
|
||||
const [selectedSchemeId, setSelectedSchemeId] = useState<number | "">("");
|
||||
const [parametersState, setParametersState, setFormField] = useControllableObjectState(
|
||||
state,
|
||||
onStateChange,
|
||||
createBurstLocationAnalysisParametersState(),
|
||||
);
|
||||
const {
|
||||
schemeName,
|
||||
dataSource,
|
||||
schemes,
|
||||
selectedSchemeId,
|
||||
burstLeakage,
|
||||
enableFlow,
|
||||
burstStartTime,
|
||||
burstEndTime,
|
||||
minDpressure,
|
||||
basicPressure,
|
||||
advancedOpen,
|
||||
} = parametersState;
|
||||
const [schemeLoading, setSchemeLoading] = useState(false);
|
||||
const [burstLeakage, setBurstLeakage] = useState<number>(1440);
|
||||
const [enableFlow, setEnableFlow] = useState(false);
|
||||
const [burstStartTime, setBurstStartTime] = useState<Dayjs | null>(
|
||||
dayjs().subtract(20, "minute"),
|
||||
);
|
||||
const [burstEndTime, setBurstEndTime] = useState<Dayjs | null>(
|
||||
dayjs().subtract(5, "minute"),
|
||||
);
|
||||
const [minDpressure, setMinDpressure] = useState<number>(2);
|
||||
const [basicPressure, setBasicPressure] = useState<number>(10);
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||
const [running, setRunning] = useState(false);
|
||||
const isSimulationMode = dataSource === "simulation";
|
||||
|
||||
@@ -71,9 +110,12 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
const durationSeconds = scheme.scheme_detail?.modify_total_duration ?? 3600;
|
||||
const end = start.add(durationSeconds, "second");
|
||||
|
||||
setBurstStartTime(start);
|
||||
setBurstEndTime(end);
|
||||
}, []);
|
||||
setParametersState((previous) => ({
|
||||
...previous,
|
||||
burstStartTime: start,
|
||||
burstEndTime: end,
|
||||
}));
|
||||
}, [setParametersState]);
|
||||
|
||||
const fetchSchemes = useCallback(
|
||||
async ({ force = false, notify = false }: { force?: boolean; notify?: boolean } = {}) => {
|
||||
@@ -88,7 +130,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
(scheme) => scheme.scheme_type === "burst_analysis",
|
||||
);
|
||||
|
||||
setSchemes(burstSchemes);
|
||||
setFormField("schemes", burstSchemes);
|
||||
|
||||
if (selectedSchemeId) {
|
||||
const matchedScheme = burstSchemes.find(
|
||||
@@ -97,7 +139,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
if (matchedScheme) {
|
||||
applySchemeTimeRange(matchedScheme);
|
||||
} else {
|
||||
setSelectedSchemeId("");
|
||||
setFormField("selectedSchemeId", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,18 +161,18 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
setSchemeLoading(false);
|
||||
}
|
||||
},
|
||||
[applySchemeTimeRange, open, schemeLoading, schemes.length, selectedSchemeId],
|
||||
[applySchemeTimeRange, open, schemeLoading, schemes.length, selectedSchemeId, setFormField],
|
||||
);
|
||||
|
||||
const handleDataSourceChange = (value: DataSource) => {
|
||||
setDataSource(value);
|
||||
setFormField("dataSource", value);
|
||||
if (value === "simulation") {
|
||||
void fetchSchemes();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSchemeSelect = (schemeId: number) => {
|
||||
setSelectedSchemeId(schemeId);
|
||||
setFormField("selectedSchemeId", schemeId);
|
||||
const scheme = schemes.find((item) => item.scheme_id === schemeId);
|
||||
if (scheme) {
|
||||
applySchemeTimeRange(scheme);
|
||||
@@ -220,7 +262,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
</Typography>
|
||||
<TextField
|
||||
value={schemeName}
|
||||
onChange={(e) => setSchemeName(e.target.value)}
|
||||
onChange={(e) => setFormField("schemeName", e.target.value)}
|
||||
placeholder="请输入方案名称"
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -321,7 +363,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
</Typography>
|
||||
<DateTimePicker
|
||||
value={burstStartTime}
|
||||
onChange={setBurstStartTime}
|
||||
onChange={(value) => setFormField("burstStartTime", value)}
|
||||
maxDateTime={burstEndTime ?? undefined}
|
||||
disabled={isSimulationMode}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@@ -334,7 +376,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
</Typography>
|
||||
<DateTimePicker
|
||||
value={burstEndTime}
|
||||
onChange={setBurstEndTime}
|
||||
onChange={(value) => setFormField("burstEndTime", value)}
|
||||
minDateTime={burstStartTime ?? undefined}
|
||||
disabled={isSimulationMode}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@@ -354,7 +396,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
value={burstLeakage}
|
||||
onChange={(e) => {
|
||||
const value = Number(e.target.value);
|
||||
setBurstLeakage(Number.isNaN(value) ? 1440 : Math.max(0, value));
|
||||
setFormField("burstLeakage", Number.isNaN(value) ? 1440 : Math.max(0, value));
|
||||
}}
|
||||
fullWidth
|
||||
inputProps={{ min: 0, step: 10 }}
|
||||
@@ -370,9 +412,9 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
<Box
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setAdvancedOpen((prev) => !prev)}
|
||||
onClick={() => setFormField("advancedOpen", !advancedOpen)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") setAdvancedOpen((prev) => !prev);
|
||||
if (e.key === "Enter" || e.key === " ") setFormField("advancedOpen", !advancedOpen);
|
||||
}}
|
||||
sx={{
|
||||
display: "flex",
|
||||
@@ -412,7 +454,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
<FormControl fullWidth size="small">
|
||||
<Select
|
||||
value={enableFlow ? "enabled" : "disabled"}
|
||||
onChange={(e) => setEnableFlow(e.target.value === "enabled")}
|
||||
onChange={(e) => setFormField("enableFlow", e.target.value === "enabled")}
|
||||
>
|
||||
<MenuItem value="disabled">禁用</MenuItem>
|
||||
<MenuItem value="enabled">启用(使用流量计)</MenuItem>
|
||||
@@ -425,14 +467,14 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
label="最小压降 (m)"
|
||||
size="small"
|
||||
value={minDpressure}
|
||||
onChange={(e) => setMinDpressure(Number(e.target.value))}
|
||||
onChange={(e) => setFormField("minDpressure", Number(e.target.value))}
|
||||
/>
|
||||
<TextField
|
||||
type="number"
|
||||
label="基础压力 (m)"
|
||||
size="small"
|
||||
value={basicPressure}
|
||||
onChange={(e) => setBasicPressure(Number(e.target.value))}
|
||||
onChange={(e) => setFormField("basicPressure", Number(e.target.value))}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user