新增全部清洗状态和提示;重新设计定位结果页面
This commit is contained in:
@@ -19,7 +19,10 @@ import {
|
|||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import AnalysisParameters from "./AnalysisParameters";
|
import AnalysisParameters from "./AnalysisParameters";
|
||||||
import SchemeQuery from "./SchemeQuery";
|
import SchemeQuery from "./SchemeQuery";
|
||||||
import LocationResults from "./LocationResults";
|
import LocationResults, { LocationResult } from "./LocationResults";
|
||||||
|
import axios from "axios";
|
||||||
|
import { config } from "@config/config";
|
||||||
|
import { useNotification } from "@refinedev/core";
|
||||||
interface SchemeDetail {
|
interface SchemeDetail {
|
||||||
burst_ID: string[];
|
burst_ID: string[];
|
||||||
burst_size: number[];
|
burst_size: number[];
|
||||||
@@ -74,6 +77,10 @@ const BurstPipeAnalysisPanel: React.FC<BurstPipeAnalysisPanelProps> = ({
|
|||||||
|
|
||||||
// 持久化方案查询结果
|
// 持久化方案查询结果
|
||||||
const [schemes, setSchemes] = useState<SchemeRecord[]>([]);
|
const [schemes, setSchemes] = useState<SchemeRecord[]>([]);
|
||||||
|
// 定位结果数据
|
||||||
|
const [locationResults, setLocationResults] = useState<LocationResult[]>([]);
|
||||||
|
|
||||||
|
const { open } = useNotification();
|
||||||
|
|
||||||
// 使用受控或非受控状态
|
// 使用受控或非受控状态
|
||||||
const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
|
const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
|
||||||
@@ -88,6 +95,24 @@ const BurstPipeAnalysisPanel: React.FC<BurstPipeAnalysisPanelProps> = ({
|
|||||||
const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
|
const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
|
||||||
setCurrentTab(newValue);
|
setCurrentTab(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLocateScheme = async (scheme: SchemeRecord) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.BACKEND_URL}/postgresql/burst-locate-result/${scheme.schemeName}`
|
||||||
|
);
|
||||||
|
setLocationResults(response.data);
|
||||||
|
setCurrentTab(2); // 切换到定位结果标签页
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取定位结果失败:", error);
|
||||||
|
open?.({
|
||||||
|
type: "error",
|
||||||
|
message: "获取定位结果失败",
|
||||||
|
description: "无法从服务器获取该方案的定位结果",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const drawerWidth = 520;
|
const drawerWidth = 520;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -214,19 +239,21 @@ const BurstPipeAnalysisPanel: React.FC<BurstPipeAnalysisPanelProps> = ({
|
|||||||
<SchemeQuery
|
<SchemeQuery
|
||||||
schemes={schemes}
|
schemes={schemes}
|
||||||
onSchemesChange={setSchemes}
|
onSchemesChange={setSchemes}
|
||||||
onLocate={(id) => {
|
onLocate={handleLocateScheme}
|
||||||
console.log("定位方案:", id);
|
|
||||||
// TODO: 在地图上定位
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel value={currentTab} index={2}>
|
<TabPanel value={currentTab} index={2}>
|
||||||
<LocationResults
|
<LocationResults
|
||||||
onLocate={(coordinates) => {
|
results={locationResults}
|
||||||
console.log("定位到:", coordinates);
|
onLocate={(result) => {
|
||||||
|
console.log("定位到:", result.locate_result);
|
||||||
// TODO: 地图定位到指定坐标
|
// TODO: 地图定位到指定坐标
|
||||||
}}
|
}}
|
||||||
|
onLocateAll={(results) => {
|
||||||
|
console.log("定位全部结果:", results);
|
||||||
|
// TODO: 地图定位到所有结果坐标
|
||||||
|
}}
|
||||||
onViewDetail={(id) => {
|
onViewDetail={(id) => {
|
||||||
console.log("查看节点详情:", id);
|
console.log("查看节点详情:", id);
|
||||||
// TODO: 显示节点详细信息
|
// TODO: 显示节点详细信息
|
||||||
|
|||||||
@@ -1,132 +1,188 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Paper,
|
|
||||||
Chip,
|
Chip,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Link,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import { LocationOn as LocationIcon } from "@mui/icons-material";
|
||||||
|
import { queryFeaturesByIds } from "@/utils/mapQueryService";
|
||||||
|
import { useMap } from "@app/OlMap/MapComponent";
|
||||||
|
import { GeoJSON } from "ol/format";
|
||||||
|
import VectorLayer from "ol/layer/Vector";
|
||||||
|
import VectorSource from "ol/source/Vector";
|
||||||
|
import { Stroke, Style, Icon } from "ol/style";
|
||||||
|
import Feature, { FeatureLike } from "ol/Feature";
|
||||||
import {
|
import {
|
||||||
LocationOn as LocationIcon,
|
along,
|
||||||
Visibility as VisibilityIcon,
|
lineString,
|
||||||
} from "@mui/icons-material";
|
length,
|
||||||
|
toMercator,
|
||||||
|
bbox,
|
||||||
|
featureCollection,
|
||||||
|
} from "@turf/turf";
|
||||||
|
import { Point } from "ol/geom";
|
||||||
|
import { toLonLat } from "ol/proj";
|
||||||
|
import moment from "moment";
|
||||||
|
import "moment-timezone";
|
||||||
|
|
||||||
interface LocationResult {
|
export interface LocationResult {
|
||||||
id: number;
|
id: number;
|
||||||
nodeName: string;
|
type: string;
|
||||||
nodeId: string;
|
burst_incident: string;
|
||||||
pressure: number;
|
leakage: number | null;
|
||||||
waterLevel: number;
|
detect_time: string;
|
||||||
flow: number;
|
locate_result: string[] | null;
|
||||||
status: "normal" | "warning" | "danger";
|
|
||||||
coordinates: [number, number];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LocationResultsProps {
|
interface LocationResultsProps {
|
||||||
onLocate?: (coordinates: [number, number]) => void;
|
results?: LocationResult[];
|
||||||
onViewDetail?: (id: number) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LocationResults: React.FC<LocationResultsProps> = ({
|
const LocationResults: React.FC<LocationResultsProps> = ({ results = [] }) => {
|
||||||
onLocate,
|
const [highlightLayer, setHighlightLayer] =
|
||||||
onViewDetail,
|
useState<VectorLayer<VectorSource> | null>(null);
|
||||||
}) => {
|
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
|
||||||
const [results, setResults] = useState<LocationResult[]>([
|
const map = useMap();
|
||||||
// 示例数据
|
|
||||||
// {
|
|
||||||
// id: 1,
|
|
||||||
// nodeName: '节点A',
|
|
||||||
// nodeId: 'N001',
|
|
||||||
// pressure: 0.35,
|
|
||||||
// waterLevel: 12.5,
|
|
||||||
// flow: 85.3,
|
|
||||||
// status: 'normal',
|
|
||||||
// coordinates: [120.15, 30.25],
|
|
||||||
// },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
// 格式化时间为 UTC+8
|
||||||
switch (status) {
|
const formatTime = (timeStr: string) => {
|
||||||
case "normal":
|
return moment(timeStr).utcOffset(8).format("YYYY-MM-DD HH:mm:ss");
|
||||||
return "success";
|
};
|
||||||
case "warning":
|
|
||||||
return "warning";
|
const handleLocatePipes = (pipeIds: string[]) => {
|
||||||
case "danger":
|
if (pipeIds.length > 0) {
|
||||||
return "error";
|
queryFeaturesByIds(pipeIds).then((features) => {
|
||||||
default:
|
if (features.length > 0) {
|
||||||
return "default";
|
// 设置高亮要素
|
||||||
|
setHighlightFeatures(features);
|
||||||
|
// 将 OpenLayers Feature 转换为 GeoJSON Feature
|
||||||
|
const geojsonFormat = new GeoJSON();
|
||||||
|
const geojsonFeatures = features.map((feature) =>
|
||||||
|
geojsonFormat.writeFeatureObject(feature)
|
||||||
|
);
|
||||||
|
|
||||||
|
const extent = bbox(featureCollection(geojsonFeatures as any));
|
||||||
|
|
||||||
|
if (extent) {
|
||||||
|
map?.getView().fit(extent, { maxZoom: 18, duration: 1000 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusText = (status: string) => {
|
// 初始化管道图层和高亮图层
|
||||||
switch (status) {
|
useEffect(() => {
|
||||||
case "normal":
|
if (!map) return;
|
||||||
return "正常";
|
|
||||||
case "warning":
|
const burstPipeStyle = function (feature: FeatureLike) {
|
||||||
return "预警";
|
const styles = [];
|
||||||
case "danger":
|
// 线条样式(底层发光,主线条,内层高亮线)
|
||||||
return "危险";
|
styles.push(
|
||||||
default:
|
new Style({
|
||||||
return "未知";
|
stroke: new Stroke({
|
||||||
|
color: "rgba(255, 0, 0, 0.3)",
|
||||||
|
width: 12,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
new Style({
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: "rgba(255, 0, 0, 1)",
|
||||||
|
width: 6,
|
||||||
|
lineDash: [15, 10],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
new Style({
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: "rgba(255, 102, 102, 1)",
|
||||||
|
width: 3,
|
||||||
|
lineDash: [15, 10],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const geometry = feature.getGeometry();
|
||||||
|
const lineCoords =
|
||||||
|
geometry?.getType() === "LineString"
|
||||||
|
? (geometry as any).getCoordinates()
|
||||||
|
: null;
|
||||||
|
if (geometry && lineCoords) {
|
||||||
|
const lineCoordsWGS84 = lineCoords.map((coord: []) => {
|
||||||
|
const [lon, lat] = toLonLat(coord);
|
||||||
|
return [lon, lat];
|
||||||
|
});
|
||||||
|
// 计算中点
|
||||||
|
const lineStringFeature = lineString(lineCoordsWGS84);
|
||||||
|
const lineLength = length(lineStringFeature);
|
||||||
|
const midPoint = along(lineStringFeature, lineLength / 2).geometry
|
||||||
|
.coordinates;
|
||||||
|
// 在中点添加 icon 样式
|
||||||
|
const midPointMercator = toMercator(midPoint);
|
||||||
|
styles.push(
|
||||||
|
new Style({
|
||||||
|
geometry: new Point(midPointMercator),
|
||||||
|
image: new Icon({
|
||||||
|
src: "/icons/burst_pipe.svg",
|
||||||
|
scale: 0.2,
|
||||||
|
anchor: [0.5, 1],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return styles;
|
||||||
|
};
|
||||||
|
// 创建高亮图层
|
||||||
|
const highlightLayer = new VectorLayer({
|
||||||
|
source: new VectorSource(),
|
||||||
|
style: burstPipeStyle,
|
||||||
|
maxZoom: 24,
|
||||||
|
minZoom: 12,
|
||||||
|
properties: {
|
||||||
|
name: "爆管管段高亮",
|
||||||
|
value: "burst_pipe_highlight",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
map.addLayer(highlightLayer);
|
||||||
|
setHighlightLayer(highlightLayer);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
map.removeLayer(highlightLayer);
|
||||||
|
};
|
||||||
|
}, [map]);
|
||||||
|
|
||||||
|
// 高亮要素的函数
|
||||||
|
useEffect(() => {
|
||||||
|
if (!highlightLayer) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
const source = highlightLayer.getSource();
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 清除之前的高亮
|
||||||
|
source.clear();
|
||||||
|
// 添加新的高亮要素
|
||||||
|
highlightFeatures.forEach((feature) => {
|
||||||
|
if (feature instanceof Feature) {
|
||||||
|
source.addFeature(feature);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [highlightFeatures, highlightLayer]);
|
||||||
|
|
||||||
|
// 取第一条记录或空对象
|
||||||
|
const result = results.length > 0 ? results[0] : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="flex flex-col h-full">
|
<Box className="flex flex-col h-full">
|
||||||
{/* 统计信息 */}
|
{/* 结果展示 */}
|
||||||
<Box className="mb-4 p-4 bg-gray-50 rounded">
|
<Box className="flex-1 overflow-auto bg-white rounded border border-gray-200">
|
||||||
<Typography variant="subtitle2" className="mb-2 font-medium">
|
{!result ? (
|
||||||
定位结果统计
|
<Box className="flex flex-col items-center justify-center h-full text-gray-400 p-4">
|
||||||
</Typography>
|
|
||||||
<Box className="flex gap-4">
|
|
||||||
<Box>
|
|
||||||
<Typography variant="caption" className="text-gray-600">
|
|
||||||
总数
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h6" className="font-bold">
|
|
||||||
{results.length}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Typography variant="caption" className="text-gray-600">
|
|
||||||
正常
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h6" className="font-bold text-green-600">
|
|
||||||
{results.filter((r) => r.status === "normal").length}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Typography variant="caption" className="text-gray-600">
|
|
||||||
预警
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h6" className="font-bold text-orange-600">
|
|
||||||
{results.filter((r) => r.status === "warning").length}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Typography variant="caption" className="text-gray-600">
|
|
||||||
危险
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="h6" className="font-bold text-red-600">
|
|
||||||
{results.filter((r) => r.status === "danger").length}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 结果列表 */}
|
|
||||||
<Box className="flex-1 overflow-auto">
|
|
||||||
{results.length === 0 ? (
|
|
||||||
<Box className="flex flex-col items-center justify-center h-full text-gray-400">
|
|
||||||
<Box className="mb-4">
|
<Box className="mb-4">
|
||||||
<svg
|
<svg
|
||||||
width="80"
|
width="80"
|
||||||
@@ -183,65 +239,155 @@ const LocationResults: React.FC<LocationResultsProps> = ({
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<TableContainer component={Paper} className="shadow-none">
|
<Box className="p-5 h-full overflow-auto">
|
||||||
<Table size="small" stickyHeader>
|
{/* 头部:标识信息 */}
|
||||||
<TableHead>
|
<Box className="mb-5">
|
||||||
<TableRow className="bg-gray-50">
|
<Box className="flex items-center gap-2 mb-1">
|
||||||
<TableCell>节点名称</TableCell>
|
<Typography
|
||||||
<TableCell>节点ID</TableCell>
|
variant="h6"
|
||||||
<TableCell align="right">压力 (MPa)</TableCell>
|
className="font-bold text-gray-900"
|
||||||
<TableCell align="right">水位 (m)</TableCell>
|
title={result.burst_incident}
|
||||||
<TableCell align="right">流量 (m³/h)</TableCell>
|
>
|
||||||
<TableCell align="center">状态</TableCell>
|
{result.burst_incident}
|
||||||
<TableCell align="center">操作</TableCell>
|
</Typography>
|
||||||
</TableRow>
|
<Chip
|
||||||
</TableHead>
|
label={result.type}
|
||||||
<TableBody>
|
size="small"
|
||||||
{results.map((result) => (
|
color="primary"
|
||||||
<TableRow key={result.id} hover>
|
variant="outlined"
|
||||||
<TableCell>{result.nodeName}</TableCell>
|
sx={{
|
||||||
<TableCell>{result.nodeId}</TableCell>
|
fontWeight: 600,
|
||||||
<TableCell align="right">
|
fontSize: "0.75rem",
|
||||||
{result.pressure.toFixed(3)}
|
height: "24px",
|
||||||
</TableCell>
|
}}
|
||||||
<TableCell align="right">
|
/>
|
||||||
{result.waterLevel.toFixed(3)}
|
</Box>
|
||||||
</TableCell>
|
<Typography variant="caption" className="text-gray-500">
|
||||||
<TableCell align="right">
|
ID: {result.id}
|
||||||
{result.flow.toFixed(1)}
|
</Typography>
|
||||||
</TableCell>
|
</Box>
|
||||||
<TableCell align="center">
|
|
||||||
<Chip
|
{/* 主要信息:三栏卡片布局 */}
|
||||||
label={getStatusText(result.status)}
|
<Box className="grid grid-cols-3 gap-3 mb-5">
|
||||||
size="small"
|
{/* 检测时间卡片 */}
|
||||||
color={getStatusColor(result.status) as any}
|
<Box className="bg-gradient-to-br from-blue-50 to-blue-100 rounded-lg p-3 border border-blue-200 shadow-sm hover:shadow-md transition-shadow">
|
||||||
/>
|
<Box className="flex items-center gap-1.5 mb-2">
|
||||||
</TableCell>
|
<Box className="w-1.5 h-1.5 rounded-full bg-blue-600"></Box>
|
||||||
<TableCell align="center">
|
<Typography
|
||||||
<Tooltip title="定位">
|
variant="caption"
|
||||||
<IconButton
|
className="text-blue-700 font-semibold uppercase tracking-wide"
|
||||||
size="small"
|
sx={{ fontSize: "0.7rem" }}
|
||||||
onClick={() => onLocate?.(result.coordinates)}
|
>
|
||||||
color="primary"
|
检测时间
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="font-bold text-blue-900 leading-tight"
|
||||||
|
sx={{ fontSize: "0.875rem" }}
|
||||||
|
>
|
||||||
|
{formatTime(result.detect_time)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 漏损量卡片 */}
|
||||||
|
<Box className="bg-gradient-to-br from-orange-50 to-orange-100 rounded-lg p-3 border border-orange-200 shadow-sm hover:shadow-md transition-shadow">
|
||||||
|
<Box className="flex items-center gap-1.5 mb-2">
|
||||||
|
<Box className="w-1.5 h-1.5 rounded-full bg-orange-600"></Box>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
className="text-orange-700 font-semibold uppercase tracking-wide"
|
||||||
|
sx={{ fontSize: "0.7rem" }}
|
||||||
|
>
|
||||||
|
漏损量
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="font-bold text-orange-900"
|
||||||
|
sx={{ fontSize: "0.875rem" }}
|
||||||
|
>
|
||||||
|
{result.leakage !== null
|
||||||
|
? `${result.leakage.toFixed(2)} m³/h`
|
||||||
|
: "N/A"}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 定位管段数量卡片 */}
|
||||||
|
<Box className="bg-gradient-to-br from-green-50 to-green-100 rounded-lg p-3 border border-green-200 shadow-sm hover:shadow-md transition-shadow">
|
||||||
|
<Box className="flex items-center gap-1.5 mb-2">
|
||||||
|
<Box className="w-1.5 h-1.5 rounded-full bg-green-600"></Box>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
className="text-green-700 font-semibold uppercase tracking-wide"
|
||||||
|
sx={{ fontSize: "0.7rem" }}
|
||||||
|
>
|
||||||
|
定位管段
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="font-bold text-green-900"
|
||||||
|
sx={{ fontSize: "0.875rem" }}
|
||||||
|
>
|
||||||
|
{result.locate_result ? result.locate_result.length : 0}{" "}
|
||||||
|
个管段
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 定位管段详细列表 */}
|
||||||
|
{result.locate_result && result.locate_result.length > 0 && (
|
||||||
|
<Box className="bg-white rounded-lg p-4 border-2 border-blue-200 shadow-sm">
|
||||||
|
<Box className="flex items-center justify-between mb-3">
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
className="text-gray-900 font-bold"
|
||||||
|
sx={{ fontSize: "0.95rem" }}
|
||||||
|
>
|
||||||
|
管段列表
|
||||||
|
</Typography>
|
||||||
|
<Tooltip title="定位所有管道">
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleLocatePipes(result.locate_result!)}
|
||||||
|
color="primary"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "rgba(37, 125, 212, 0.1)",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "rgba(37, 125, 212, 0.2)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LocationIcon sx={{ fontSize: "1.2rem" }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<Box className="grid grid-cols-2 gap-2">
|
||||||
|
{result.locate_result.map((pipeId, idx) => (
|
||||||
|
<Box
|
||||||
|
key={idx}
|
||||||
|
className="bg-gradient-to-r from-blue-50 to-white rounded-lg px-3 py-2 border border-blue-200 hover:border-blue-400 hover:shadow-md transition-all cursor-pointer group"
|
||||||
|
onClick={() => handleLocatePipes([pipeId])}
|
||||||
|
>
|
||||||
|
<Box className="flex items-center justify-between">
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="font-semibold text-blue-700 group-hover:text-blue-900"
|
||||||
>
|
>
|
||||||
<LocationIcon fontSize="small" />
|
{pipeId}
|
||||||
</IconButton>
|
</Typography>
|
||||||
</Tooltip>
|
<LocationIcon
|
||||||
<Tooltip title="查看详情">
|
sx={{ fontSize: "1rem" }}
|
||||||
<IconButton
|
className="text-blue-400 group-hover:text-blue-600 transition-colors"
|
||||||
size="small"
|
/>
|
||||||
onClick={() => onViewDetail?.(result.id)}
|
</Box>
|
||||||
color="primary"
|
</Box>
|
||||||
>
|
))}
|
||||||
<VisibilityIcon fontSize="small" />
|
</Box>
|
||||||
</IconButton>
|
</Box>
|
||||||
</Tooltip>
|
)}
|
||||||
</TableCell>
|
</Box>
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ interface SchemaItem {
|
|||||||
interface SchemeQueryProps {
|
interface SchemeQueryProps {
|
||||||
schemes?: SchemeRecord[];
|
schemes?: SchemeRecord[];
|
||||||
onSchemesChange?: (schemes: SchemeRecord[]) => void;
|
onSchemesChange?: (schemes: SchemeRecord[]) => void;
|
||||||
onLocate?: (id: number) => void;
|
onLocate?: (scheme: SchemeRecord) => void;
|
||||||
network?: string;
|
network?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,7 +486,7 @@ const SchemeQuery: React.FC<SchemeQueryProps> = ({
|
|||||||
<Tooltip title="定位">
|
<Tooltip title="定位">
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => onLocate?.(scheme.id)}
|
onClick={() => onLocate?.(scheme)}
|
||||||
color="primary"
|
color="primary"
|
||||||
className="p-1"
|
className="p-1"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -605,6 +605,12 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
|
|||||||
|
|
||||||
setIsCleaning(true);
|
setIsCleaning(true);
|
||||||
|
|
||||||
|
open?.({
|
||||||
|
type: "progress",
|
||||||
|
message: "正在清洗数据,请稍候...",
|
||||||
|
undoableTimeout: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const startTime = dayjs(cleanStartTime).toISOString();
|
const startTime = dayjs(cleanStartTime).toISOString();
|
||||||
const endTime = dayjs(cleanEndTime).toISOString();
|
const endTime = dayjs(cleanEndTime).toISOString();
|
||||||
|
|||||||
Reference in New Issue
Block a user