fix(analysis): preserve tab panel state
This commit is contained in:
@@ -24,13 +24,16 @@ 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 { BurstDetectionResult } from "./types";
|
||||
|
||||
interface Props {
|
||||
onResult: (result: BurstDetectionResult) => void;
|
||||
state?: BurstDetectionAnalysisParametersState;
|
||||
onStateChange?: (state: BurstDetectionAnalysisParametersState) => void;
|
||||
}
|
||||
|
||||
interface SchemeItem {
|
||||
export interface SchemeItem {
|
||||
scheme_id: number;
|
||||
scheme_name: string;
|
||||
scheme_type: string;
|
||||
@@ -41,20 +44,60 @@ interface SchemeItem {
|
||||
};
|
||||
}
|
||||
|
||||
const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
export interface BurstDetectionAnalysisParametersState {
|
||||
schemeName: string;
|
||||
dataSource: "monitoring" | "simulation";
|
||||
schemes: SchemeItem[];
|
||||
selectedSchemeId: number | "";
|
||||
scadaStart: Dayjs | null;
|
||||
scadaEnd: Dayjs | null;
|
||||
mu: number;
|
||||
pointsPerDay: number;
|
||||
nEstimators: number;
|
||||
contaminationInput: string;
|
||||
advancedOpen: boolean;
|
||||
}
|
||||
|
||||
export const createBurstDetectionAnalysisParametersState =
|
||||
(): BurstDetectionAnalysisParametersState => ({
|
||||
schemeName: `Burst_Detection_${Date.now()}`,
|
||||
dataSource: "monitoring",
|
||||
schemes: [],
|
||||
selectedSchemeId: "",
|
||||
scadaStart: dayjs().subtract(3, "day"),
|
||||
scadaEnd: dayjs(),
|
||||
mu: 100,
|
||||
pointsPerDay: 96,
|
||||
nEstimators: 50,
|
||||
contaminationInput: "auto",
|
||||
advancedOpen: false,
|
||||
});
|
||||
|
||||
const AnalysisParameters: React.FC<Props> = ({
|
||||
onResult,
|
||||
state,
|
||||
onStateChange,
|
||||
}) => {
|
||||
const { open } = useNotification();
|
||||
const [schemeName, setSchemeName] = useState(`Burst_Detection_${Date.now()}`);
|
||||
const [dataSource, setDataSource] = useState<"monitoring" | "simulation">("monitoring");
|
||||
const [schemes, setSchemes] = useState<SchemeItem[]>([]);
|
||||
const [selectedSchemeId, setSelectedSchemeId] = useState<number | "">("");
|
||||
const [parametersState, setParametersState, setFormField] = useControllableObjectState(
|
||||
state,
|
||||
onStateChange,
|
||||
createBurstDetectionAnalysisParametersState(),
|
||||
);
|
||||
const {
|
||||
schemeName,
|
||||
dataSource,
|
||||
schemes,
|
||||
selectedSchemeId,
|
||||
scadaStart,
|
||||
scadaEnd,
|
||||
mu,
|
||||
pointsPerDay,
|
||||
nEstimators,
|
||||
contaminationInput,
|
||||
advancedOpen,
|
||||
} = parametersState;
|
||||
const [schemeLoading, setSchemeLoading] = useState(false);
|
||||
const [scadaStart, setScadaStart] = useState<Dayjs | null>(dayjs().subtract(3, "day"));
|
||||
const [scadaEnd, setScadaEnd] = useState<Dayjs | null>(dayjs());
|
||||
const [mu, setMu] = useState<number>(100);
|
||||
const [pointsPerDay, setPointsPerDay] = useState<number>(96);
|
||||
const [nEstimators, setNEstimators] = useState<number>(50);
|
||||
const [contaminationInput, setContaminationInput] = useState<string>("auto");
|
||||
const [advancedOpen, setAdvancedOpen] = useState(false);
|
||||
const [running, setRunning] = useState(false);
|
||||
const isSimulationMode = dataSource === "simulation";
|
||||
|
||||
@@ -63,9 +106,12 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
const durationSeconds = scheme.scheme_detail?.modify_total_duration ?? 3600;
|
||||
const end = start.add(durationSeconds, "second");
|
||||
|
||||
setScadaStart(start);
|
||||
setScadaEnd(end);
|
||||
}, []);
|
||||
setParametersState((previous) => ({
|
||||
...previous,
|
||||
scadaStart: start,
|
||||
scadaEnd: end,
|
||||
}));
|
||||
}, [setParametersState]);
|
||||
|
||||
const fetchSchemes = useCallback(
|
||||
async ({ force = false, notify = false }: { force?: boolean; notify?: boolean } = {}) => {
|
||||
@@ -80,7 +126,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
(scheme) => scheme.scheme_type === "burst_analysis",
|
||||
);
|
||||
|
||||
setSchemes(burstSchemes);
|
||||
setFormField("schemes", burstSchemes);
|
||||
|
||||
if (selectedSchemeId) {
|
||||
const matchedScheme = burstSchemes.find(
|
||||
@@ -89,7 +135,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
if (matchedScheme) {
|
||||
applySchemeTimeRange(matchedScheme);
|
||||
} else {
|
||||
setSelectedSchemeId("");
|
||||
setFormField("selectedSchemeId", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,18 +157,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: "monitoring" | "simulation") => {
|
||||
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);
|
||||
@@ -239,7 +285,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
</Typography>
|
||||
<TextField
|
||||
value={schemeName}
|
||||
onChange={(event) => setSchemeName(event.target.value)}
|
||||
onChange={(event) => setFormField("schemeName", event.target.value)}
|
||||
placeholder="请输入方案名称"
|
||||
fullWidth
|
||||
size="small"
|
||||
@@ -318,7 +364,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
</Typography>
|
||||
<DateTimePicker
|
||||
value={scadaStart}
|
||||
onChange={setScadaStart}
|
||||
onChange={(value) => setFormField("scadaStart", value)}
|
||||
maxDateTime={scadaEnd ? scadaEnd.subtract(2, "day") : undefined}
|
||||
disabled={isSimulationMode}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@@ -331,7 +377,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
</Typography>
|
||||
<DateTimePicker
|
||||
value={scadaEnd}
|
||||
onChange={setScadaEnd}
|
||||
onChange={(value) => setFormField("scadaEnd", value)}
|
||||
minDateTime={scadaStart ? scadaStart.add(2, "day") : undefined}
|
||||
disabled={isSimulationMode}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@@ -356,10 +402,10 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
<Box
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setAdvancedOpen((prev) => !prev)}
|
||||
onClick={() => setFormField("advancedOpen", !advancedOpen)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
setAdvancedOpen((prev) => !prev);
|
||||
setFormField("advancedOpen", !advancedOpen);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
@@ -397,7 +443,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
type="number"
|
||||
label="频域截断系数"
|
||||
value={mu}
|
||||
onChange={(event) => setMu(Number(event.target.value))}
|
||||
onChange={(event) => setFormField("mu", Number(event.target.value))}
|
||||
size="small"
|
||||
fullWidth
|
||||
inputProps={{ min: 1 }}
|
||||
@@ -406,7 +452,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
type="number"
|
||||
label="每日采样点数"
|
||||
value={pointsPerDay}
|
||||
onChange={(event) => setPointsPerDay(Number(event.target.value))}
|
||||
onChange={(event) => setFormField("pointsPerDay", Number(event.target.value))}
|
||||
size="small"
|
||||
fullWidth
|
||||
inputProps={{ min: 1 }}
|
||||
@@ -415,7 +461,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
type="number"
|
||||
label="孤立森林树数量"
|
||||
value={nEstimators}
|
||||
onChange={(event) => setNEstimators(Number(event.target.value))}
|
||||
onChange={(event) => setFormField("nEstimators", Number(event.target.value))}
|
||||
size="small"
|
||||
fullWidth
|
||||
inputProps={{ min: 1 }}
|
||||
@@ -423,7 +469,7 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
<TextField
|
||||
label="异常比例"
|
||||
value={contaminationInput}
|
||||
onChange={(event) => setContaminationInput(event.target.value)}
|
||||
onChange={(event) => setFormField("contaminationInput", event.target.value)}
|
||||
size="small"
|
||||
fullWidth
|
||||
helperText="填写 auto 或 0~0.5 之间的小数。"
|
||||
@@ -442,13 +488,16 @@ const AnalysisParameters: React.FC<Props> = ({ onResult }) => {
|
||||
disabled={running}
|
||||
sx={{ textTransform: "none", fontWeight: 500 }}
|
||||
onClick={() => {
|
||||
setSchemeName(`Burst_Detection_${Date.now()}`);
|
||||
setScadaStart(dayjs().subtract(3, "day"));
|
||||
setScadaEnd(dayjs());
|
||||
setMu(100);
|
||||
setPointsPerDay(96);
|
||||
setNEstimators(50);
|
||||
setContaminationInput("auto");
|
||||
setParametersState((previous) => ({
|
||||
...previous,
|
||||
schemeName: `Burst_Detection_${Date.now()}`,
|
||||
scadaStart: dayjs().subtract(3, "day"),
|
||||
scadaEnd: dayjs(),
|
||||
mu: 100,
|
||||
pointsPerDay: 96,
|
||||
nEstimators: 50,
|
||||
contaminationInput: "auto",
|
||||
}));
|
||||
}}
|
||||
>
|
||||
重置
|
||||
|
||||
Reference in New Issue
Block a user