更新爆管定位功能,优化数据处理和展示
This commit is contained in:
@@ -23,7 +23,12 @@ import dayjs, { Dayjs } from "dayjs";
|
||||
import { useNotification } from "@refinedev/core";
|
||||
import { api } from "@/lib/api";
|
||||
import { NETWORK_NAME, config } from "@config/config";
|
||||
import { BurstLocationResult, BurstSchemeRecord } from "./types";
|
||||
import {
|
||||
BurstLocationResult,
|
||||
BurstLocationSchemeDetail,
|
||||
BurstSchemeRecord,
|
||||
} from "./types";
|
||||
import { DMA_FLOW_DISPLAY_UNIT } from "../DMALeakDetection/utils";
|
||||
|
||||
interface Props {
|
||||
onViewResult: (result: BurstLocationResult) => void;
|
||||
@@ -37,6 +42,39 @@ const SchemeQuery: React.FC<Props> = ({ onViewResult }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [expandedId, setExpandedId] = useState<number | null>(null);
|
||||
|
||||
const buildDisplayResult = (
|
||||
scheme: Pick<BurstSchemeRecord, "scheme_name" | "username" | "create_time">,
|
||||
detail?: BurstLocationSchemeDetail,
|
||||
): BurstLocationResult | null => {
|
||||
const payload = detail?.result_payload;
|
||||
const locatedPipe = payload?.located_pipe ?? detail?.result_summary?.located_pipe;
|
||||
if (!locatedPipe) return null;
|
||||
|
||||
return {
|
||||
located_pipe: locatedPipe,
|
||||
burst_leakage: payload?.burst_leakage ?? detail?.algorithm_params?.burst_leakage ?? 0,
|
||||
elapsed_seconds: payload?.elapsed_seconds ?? 0,
|
||||
min_dpressure: payload?.min_dpressure ?? detail?.algorithm_params?.min_dpressure,
|
||||
basic_pressure: payload?.basic_pressure ?? detail?.algorithm_params?.basic_pressure,
|
||||
simulation_times: payload?.simulation_times ?? detail?.result_summary?.simulation_times ?? 0,
|
||||
top_candidates: payload?.top_candidates ?? [],
|
||||
similarity_mode:
|
||||
payload?.similarity_mode ?? detail?.result_summary?.similarity_mode ?? "-",
|
||||
scheme_name: payload?.scheme_name ?? scheme.scheme_name,
|
||||
username: payload?.username ?? scheme.username,
|
||||
network: payload?.network ?? detail?.network,
|
||||
data_source: payload?.data_source,
|
||||
observed_source: payload?.observed_source ?? detail?.observed_source,
|
||||
pressure_scada_ids: payload?.pressure_scada_ids ?? detail?.pressure_scada_ids,
|
||||
flow_scada_ids: payload?.flow_scada_ids ?? detail?.flow_scada_ids,
|
||||
create_time: payload?.create_time ?? scheme.create_time,
|
||||
scada_window: payload?.scada_window ?? detail?.scada_window,
|
||||
pressure_samples: payload?.pressure_samples,
|
||||
flow_samples: payload?.flow_samples,
|
||||
simulation_scheme: payload?.simulation_scheme,
|
||||
};
|
||||
};
|
||||
|
||||
const handleQuery = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -47,7 +85,7 @@ const SchemeQuery: React.FC<Props> = ({ onViewResult }) => {
|
||||
if (!queryAll && queryDate) {
|
||||
params.query_date = queryDate.startOf("day").toISOString();
|
||||
}
|
||||
|
||||
|
||||
const response = await api.get(url, { params });
|
||||
setSchemes(response.data);
|
||||
open?.({
|
||||
@@ -73,10 +111,23 @@ const SchemeQuery: React.FC<Props> = ({ onViewResult }) => {
|
||||
`${config.BACKEND_URL}/api/v1/burst-location/schemes/${encodeURIComponent(schemeName)}`,
|
||||
{ params: { network: NETWORK_NAME } },
|
||||
);
|
||||
// The backend returns { scheme_detail: ... } inside the response or just the result?
|
||||
// Based on burst_location.py: get_burst_location_scheme_detail returns the stored detail.
|
||||
// Let's assume response.data is the BurstLocationResult
|
||||
onViewResult(response.data as BurstLocationResult);
|
||||
const schemeRecord = response.data as BurstSchemeRecord & {
|
||||
result_payload?: BurstLocationResult;
|
||||
};
|
||||
const normalizedResult =
|
||||
schemeRecord.result_payload ??
|
||||
buildDisplayResult(
|
||||
{
|
||||
scheme_name: schemeRecord.scheme_name,
|
||||
username: schemeRecord.username,
|
||||
create_time: schemeRecord.create_time,
|
||||
},
|
||||
schemeRecord.scheme_detail,
|
||||
);
|
||||
if (!normalizedResult) {
|
||||
throw new Error("方案详情缺少定位结果数据");
|
||||
}
|
||||
onViewResult(normalizedResult);
|
||||
open?.({
|
||||
type: "success",
|
||||
message: "方案加载成功",
|
||||
@@ -169,80 +220,118 @@ const SchemeQuery: React.FC<Props> = ({ onViewResult }) => {
|
||||
<Typography variant="caption" className="text-gray-500 px-2">
|
||||
共 {schemes.length} 条记录
|
||||
</Typography>
|
||||
{schemes.map((scheme) => (
|
||||
<Card key={scheme.scheme_id} variant="outlined" className="hover:shadow-md transition-shadow">
|
||||
<CardContent className="p-3 pb-2 last:pb-3">
|
||||
<Box className="flex items-start justify-between gap-2 mb-2">
|
||||
<Box className="flex-1 min-w-0">
|
||||
<Box className="flex items-center gap-2 mb-1">
|
||||
<Typography variant="body2" className="font-medium truncate" title={scheme.scheme_name}>
|
||||
{scheme.scheme_name}
|
||||
{schemes.map((scheme) => {
|
||||
const summary = scheme.scheme_detail?.result_summary;
|
||||
const payload = scheme.scheme_detail?.result_payload;
|
||||
const locatedPipe = payload?.located_pipe ?? summary?.located_pipe ?? "-";
|
||||
const leakage =
|
||||
payload?.burst_leakage ?? scheme.scheme_detail?.algorithm_params?.burst_leakage;
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={scheme.scheme_id}
|
||||
variant="outlined"
|
||||
className="hover:shadow-md transition-shadow"
|
||||
>
|
||||
<CardContent className="p-3 pb-2 last:pb-3">
|
||||
<Box className="flex items-start justify-between gap-2 mb-2">
|
||||
<Box className="flex-1 min-w-0">
|
||||
<Box className="flex items-center gap-2 mb-1">
|
||||
<Typography
|
||||
variant="body2"
|
||||
className="font-medium truncate"
|
||||
title={scheme.scheme_name}
|
||||
>
|
||||
{scheme.scheme_name}
|
||||
</Typography>
|
||||
<Chip
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color={
|
||||
payload?.data_source === "simulation" ? "secondary" : "primary"
|
||||
}
|
||||
label={
|
||||
payload?.data_source === "simulation" ? "模拟方案" : "监测数据"
|
||||
}
|
||||
className="h-5"
|
||||
/>
|
||||
</Box>
|
||||
{payload?.data_source === "simulation" &&
|
||||
payload?.simulation_scheme?.name ? (
|
||||
<Typography
|
||||
variant="caption"
|
||||
className="mb-1 block truncate text-xs text-purple-600"
|
||||
title={payload.simulation_scheme.name}
|
||||
>
|
||||
方案: {payload.simulation_scheme.name}
|
||||
</Typography>
|
||||
) : null}
|
||||
<Typography variant="caption" className="block text-gray-500">
|
||||
ID: {scheme.scheme_id} · 日期:{" "}
|
||||
{dayjs(scheme.create_time).format("MM-DD HH:mm")}
|
||||
</Typography>
|
||||
<Chip size="small" variant="outlined" color="primary" label="爆管定位" className="h-5" />
|
||||
</Box>
|
||||
<Typography variant="caption" className="text-gray-500 block">
|
||||
ID: {scheme.scheme_id} · 日期: {dayjs(scheme.create_time).format("MM-DD HH:mm")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box className="flex gap-1 ml-2">
|
||||
<Tooltip title={expandedId === scheme.scheme_id ? "收起详情" : "查看详情"}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setExpandedId(expandedId === scheme.scheme_id ? null : scheme.scheme_id)}
|
||||
color="primary"
|
||||
className="p-1"
|
||||
>
|
||||
<InfoIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Collapse in={expandedId === scheme.scheme_id}>
|
||||
<Box className="mt-2 pt-3 border-t border-gray-200">
|
||||
<Box className="mb-3 rounded-md bg-gray-50 px-3 py-2 space-y-2">
|
||||
{/* Summary details */}
|
||||
<Box className="grid grid-cols-[78px_1fr] items-center gap-x-2">
|
||||
<Typography variant="caption" className="text-gray-600">
|
||||
定位管段:
|
||||
</Typography>
|
||||
<Typography variant="caption" className="font-medium text-gray-900">
|
||||
{scheme.scheme_detail?.located_pipe || "-"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box className="grid grid-cols-[78px_1fr] items-center gap-x-2">
|
||||
<Typography variant="caption" className="text-gray-600">
|
||||
漏损量:
|
||||
</Typography>
|
||||
<Typography variant="caption" className="font-medium text-gray-900">
|
||||
{scheme.scheme_detail?.burst_leakage ? `${scheme.scheme_detail.burst_leakage} L/s` : "-"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box className="grid grid-cols-[78px_1fr] items-center gap-x-2">
|
||||
<Typography variant="caption" className="text-gray-600">
|
||||
用户:
|
||||
</Typography>
|
||||
<Typography variant="caption" className="font-medium text-gray-900">
|
||||
{scheme.username || "-"}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className="pt-2 border-t border-gray-100">
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
size="small"
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
sx={{ textTransform: "none", fontWeight: 500 }}
|
||||
onClick={() => handleViewSchemeResult(scheme.scheme_name)}
|
||||
>
|
||||
查看定位结果
|
||||
</Button>
|
||||
<Box className="flex gap-1 ml-2">
|
||||
<Tooltip title={expandedId === scheme.scheme_id ? "收起详情" : "查看详情"}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setExpandedId(expandedId === scheme.scheme_id ? null : scheme.scheme_id)
|
||||
}
|
||||
color="primary"
|
||||
className="p-1"
|
||||
>
|
||||
<InfoIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
<Collapse in={expandedId === scheme.scheme_id}>
|
||||
<Box className="mt-2 pt-3 border-t border-gray-200">
|
||||
<Box className="mb-3 rounded-md bg-gray-50 px-3 py-2 space-y-2">
|
||||
<Box className="grid grid-cols-[78px_1fr] items-center gap-x-2">
|
||||
<Typography variant="caption" className="text-gray-600">
|
||||
定位管段:
|
||||
</Typography>
|
||||
<Typography variant="caption" className="font-medium text-gray-900">
|
||||
{locatedPipe}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box className="grid grid-cols-[78px_1fr] items-center gap-x-2">
|
||||
<Typography variant="caption" className="text-gray-600">
|
||||
漏损量:
|
||||
</Typography>
|
||||
<Typography variant="caption" className="font-medium text-gray-900">
|
||||
{typeof leakage === "number" ? `${leakage} ${DMA_FLOW_DISPLAY_UNIT}` : "-"}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box className="grid grid-cols-[78px_1fr] items-center gap-x-2">
|
||||
<Typography variant="caption" className="text-gray-600">
|
||||
用户:
|
||||
</Typography>
|
||||
<Typography variant="caption" className="font-medium text-gray-900">
|
||||
{scheme.username || "-"}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className="pt-2 border-t border-gray-100">
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
size="small"
|
||||
className="bg-blue-600 hover:bg-blue-700"
|
||||
sx={{ textTransform: "none", fontWeight: 500 }}
|
||||
onClick={() => handleViewSchemeResult(scheme.scheme_name)}
|
||||
>
|
||||
查看定位结果
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user