更改 SCADA 设备列表条目关键词;样式设置中新增彩虹色带;强制计算后清除计算范围内的缓存;

This commit is contained in:
JIANG
2025-10-31 15:24:23 +08:00
parent fe797c1bf3
commit b332a6437d
6 changed files with 331 additions and 29 deletions

View File

@@ -104,6 +104,40 @@ const GRADIENT_PALETTES = [
end: "rgba(148, 103, 189, 1)", end: "rgba(148, 103, 189, 1)",
}, },
]; ];
// 离散彩虹色系 - 提供高区分度的颜色
const RAINBOW_PALETTES = [
{
name: "正向彩虹",
colors: [
"rgba(255, 0, 0, 1)", // 红 #FF0000
"rgba(255, 127, 0, 1)", // 橙 #FF7F00
"rgba(255, 215, 0, 1)", // 金黄 #FFD700
"rgba(199, 224, 0, 1)", // 黄绿 #C7E000
"rgba(76, 175, 80, 1)", // 中绿 #4CAF50
"rgba(0, 158, 115, 1)", // 青绿/翡翠 #009E73
"rgba(0, 188, 212, 1)", // 青/青色 #00BCD4
"rgba(33, 150, 243, 1)", // 天蓝 #2196F3
"rgba(63, 81, 181, 1)", // 靛青 #3F51B5
"rgba(142, 68, 173, 1)", // 紫 #8E44AD
],
},
{
name: "反向彩虹",
colors: [
"rgba(142, 68, 173, 1)", // 紫 #8E44AD
"rgba(63, 81, 181, 1)", // 靛青 #3F51B5
"rgba(33, 150, 243, 1)", // 天蓝 #2196F3
"rgba(0, 188, 212, 1)", // 青/青色 #00BCD4
"rgba(0, 158, 115, 1)", // 青绿/翡翠 #009E73
"rgba(76, 175, 80, 1)", // 中绿 #4CAF50
"rgba(199, 224, 0, 1)", // 黄绿 #C7E000
"rgba(255, 215, 0, 1)", // 金黄 #FFD700
"rgba(255, 127, 0, 1)", // 橙 #FF7F00
"rgba(255, 0, 0, 1)", // 红 #FF0000
],
},
];
// 预设分类方法 // 预设分类方法
const CLASSIFICATION_METHODS = [ const CLASSIFICATION_METHODS = [
{ name: "优雅分段", value: "pretty_breaks" }, { name: "优雅分段", value: "pretty_breaks" },
@@ -164,6 +198,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
// 颜色方案选择 // 颜色方案选择
const [singlePaletteIndex, setSinglePaletteIndex] = useState(0); const [singlePaletteIndex, setSinglePaletteIndex] = useState(0);
const [gradientPaletteIndex, setGradientPaletteIndex] = useState(0); const [gradientPaletteIndex, setGradientPaletteIndex] = useState(0);
const [rainbowPaletteIndex, setRainbowPaletteIndex] = useState(0);
// 根据分段数生成相应数量的渐进颜色 // 根据分段数生成相应数量的渐进颜色
const generateGradientColors = useCallback( const generateGradientColors = useCallback(
(segments: number): string[] => { (segments: number): string[] => {
@@ -189,6 +224,29 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
}, },
[gradientPaletteIndex, parseColor] [gradientPaletteIndex, parseColor]
); );
// 根据分段数生成彩虹色
const generateRainbowColors = useCallback(
(segments: number): string[] => {
const baseColors = RAINBOW_PALETTES[rainbowPaletteIndex].colors;
if (segments <= baseColors.length) {
// 如果分段数小于等于基础颜色数,均匀选取
const step = baseColors.length / segments;
return Array.from(
{ length: segments },
(_, i) => baseColors[Math.floor(i * step)]
);
} else {
// 如果分段数大于基础颜色数,重复使用
return Array.from(
{ length: segments },
(_, i) => baseColors[i % baseColors.length]
);
}
},
[rainbowPaletteIndex]
);
// 保存当前图层的样式状态 // 保存当前图层的样式状态
const saveLayerStyle = useCallback( const saveLayerStyle = useCallback(
(layerId?: string, newLegendConfig?: LegendStyleConfig) => { (layerId?: string, newLegendConfig?: LegendStyleConfig) => {
@@ -350,7 +408,9 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
Array.from({ length: breaksLength }, () => { Array.from({ length: breaksLength }, () => {
return SINGLE_COLOR_PALETTES[singlePaletteIndex].color; return SINGLE_COLOR_PALETTES[singlePaletteIndex].color;
}) })
: generateGradientColors(breaksLength); : styleConfig.colorType === "gradient"
? generateGradientColors(breaksLength)
: generateRainbowColors(breaksLength);
// 计算每个分段的线条粗细和点大小 // 计算每个分段的线条粗细和点大小
const dimensions: number[] = const dimensions: number[] =
layerType === "linestring" layerType === "linestring"
@@ -803,6 +863,73 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
</FormControl> </FormControl>
); );
} }
if (styleConfig.colorType === "rainbow") {
return (
<FormControl
variant="standard"
fullWidth
margin="dense"
className="mt-3"
>
<InputLabel></InputLabel>
<Select
value={rainbowPaletteIndex}
onChange={(e) => setRainbowPaletteIndex(Number(e.target.value))}
>
{RAINBOW_PALETTES.map((p, idx) => {
// 根据当前分段数生成该方案的预览颜色
const baseColors = p.colors;
const segments = styleConfig.segments;
let previewColors: string[];
if (segments <= baseColors.length) {
const step = baseColors.length / segments;
previewColors = Array.from(
{ length: segments },
(_, i) => baseColors[Math.floor(i * step)]
);
} else {
previewColors = Array.from(
{ length: segments },
(_, i) => baseColors[i % baseColors.length]
);
}
return (
<MenuItem key={idx} value={idx}>
<Box
width="100%"
sx={{ display: "flex", alignItems: "center" }}
>
<Typography sx={{ marginRight: 1 }}>{p.name}</Typography>
<Box
sx={{
width: "60%",
height: 16,
borderRadius: 2,
display: "flex",
border: "1px solid #ccc",
overflow: "hidden",
}}
>
{previewColors.map((color, colorIdx) => (
<Box
key={colorIdx}
sx={{
flex: 1,
backgroundColor: color,
}}
/>
))}
</Box>
</Box>
</MenuItem>
);
})}
</Select>
</FormControl>
);
}
}; };
// 根据不同图层的类型和颜色分类方案显示不同的大小设置 // 根据不同图层的类型和颜色分类方案显示不同的大小设置
const getSizeSetting = () => { const getSizeSetting = () => {
@@ -813,7 +940,9 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
} else if (styleConfig.colorType === "gradient") { } else if (styleConfig.colorType === "gradient") {
const { start, end } = GRADIENT_PALETTES[gradientPaletteIndex]; const { start, end } = GRADIENT_PALETTES[gradientPaletteIndex];
colors = [start, end]; colors = [start, end];
} else if (styleConfig.colorType === "categorical") { } else if (styleConfig.colorType === "rainbow") {
const rainbowColors = RAINBOW_PALETTES[rainbowPaletteIndex].colors;
colors = [rainbowColors[0], rainbowColors[rainbowColors.length - 1]];
} }
if (selectedRenderLayer?.get("type") === "point") { if (selectedRenderLayer?.get("type") === "point") {
@@ -1024,10 +1153,20 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
} }
onChange={(e) => { onChange={(e) => {
const index = e.target.value as number; const index = e.target.value as number;
setSelectedRenderLayer( const newLayer = index >= 0 ? renderLayers[index] : undefined;
index >= 0 ? renderLayers[index] : undefined setSelectedRenderLayer(newLayer);
// 检查新图层是否有缓存的样式,没有才清空
if (newLayer) {
const layerId = newLayer.get("value");
const cachedStyleState = layerStyleStates.find(
(state) => state.layerId === layerId
); );
// 只有在没有缓存时才清空属性
if (!cachedStyleState) {
setStyleConfig((prev) => ({ ...prev, property: "" })); setStyleConfig((prev) => ({ ...prev, property: "" }));
}
}
}} }}
> >
{renderLayers.map((layer, index) => { {renderLayers.map((layer, index) => {
@@ -1102,13 +1241,13 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
onChange={(e) => onChange={(e) =>
setStyleConfig((prev) => ({ setStyleConfig((prev) => ({
...prev, ...prev,
colorType: e.target.value as "gradient" | "categorical", colorType: e.target.value as "single" | "gradient" | "rainbow",
})) }))
} }
> >
<MenuItem value="single"></MenuItem> <MenuItem value="single"></MenuItem>
<MenuItem value="gradient"></MenuItem> <MenuItem value="gradient"></MenuItem>
{/* <MenuItem value="categorical">分类色</MenuItem> */} <MenuItem value="rainbow"></MenuItem>
</Select> </Select>
{getColorSetting()} {getColorSetting()}
</FormControl> </FormControl>

View File

@@ -27,7 +27,6 @@ import { FiSkipBack, FiSkipForward } from "react-icons/fi";
import { useData } from "../MapComponent"; import { useData } from "../MapComponent";
import { config, NETWORK_NAME } from "@/config/config"; import { config, NETWORK_NAME } from "@/config/config";
import { useMap } from "../MapComponent"; import { useMap } from "../MapComponent";
import { Network } from "inspector/promises";
const backendUrl = config.backendUrl; const backendUrl = config.backendUrl;
interface TimelineProps { interface TimelineProps {
@@ -139,10 +138,6 @@ const Timeline: React.FC<TimelineProps> = ({
} }
} }
// console.log(
// "Query Time:",
// queryTime.toLocaleDateString() + " " + queryTime.toLocaleTimeString()
// );
// 等待所有有效请求 // 等待所有有效请求
const responses = await Promise.all(requests); const responses = await Promise.all(requests);
@@ -211,7 +206,6 @@ const Timeline: React.FC<TimelineProps> = ({
// 播放时间间隔选项 // 播放时间间隔选项
const intervalOptions = [ const intervalOptions = [
// { value: 1000, label: "1秒" },
{ value: 2000, label: "2秒" }, { value: 2000, label: "2秒" },
{ value: 5000, label: "5秒" }, { value: 5000, label: "5秒" },
{ value: 10000, label: "10秒" }, { value: 10000, label: "10秒" },
@@ -239,7 +233,7 @@ const Timeline: React.FC<TimelineProps> = ({
} }
debounceRef.current = setTimeout(() => { debounceRef.current = setTimeout(() => {
setCurrentTime(value); setCurrentTime(value);
}, 300); // 300ms 防抖延迟 }, 500); // 500ms 防抖延迟
}, },
[timeRange, minTime, maxTime] [timeRange, minTime, maxTime]
); );
@@ -441,6 +435,11 @@ const Timeline: React.FC<TimelineProps> = ({
return; return;
} }
// 提前提取日期和时间值,避免异步操作期间被时间轴拖动改变
const calculationDate = selectedDate;
const calculationTime = currentTime;
const calculationDateStr = calculationDate.toISOString().split("T")[0];
setIsCalculating(true); setIsCalculating(true);
// 显示处理中的通知 // 显示处理中的通知
open?.({ open?.({
@@ -451,8 +450,8 @@ const Timeline: React.FC<TimelineProps> = ({
try { try {
const body = { const body = {
name: NETWORK_NAME, name: NETWORK_NAME,
simulation_date: selectedDate.toISOString().split("T")[0], // YYYY-MM-DD simulation_date: calculationDateStr, // YYYY-MM-DD
start_time: `${formatTime(currentTime)}:00`, // HH:MM:00 start_time: `${formatTime(calculationTime)}:00`, // HH:MM:00
duration: calculatedInterval, duration: calculatedInterval,
}; };
@@ -468,6 +467,44 @@ const Timeline: React.FC<TimelineProps> = ({
); );
if (response.ok) { if (response.ok) {
// 清空当天当前时刻及之后的缓存
const currentDateStr = calculationDateStr;
const currentTimeInMinutes = calculationTime;
// 清空node缓存
const nodeCacheKeys = Array.from(nodeCacheRef.current.keys());
nodeCacheKeys.forEach((key) => {
const keyParts = key.split("_");
const cacheDate = keyParts[0].split("T")[0];
const cacheTimeStr = keyParts[0].split("T")[1];
if (cacheDate === currentDateStr && cacheTimeStr) {
const [hours, minutes] = cacheTimeStr.split(":");
const cacheTimeInMinutes = parseInt(hours) * 60 + parseInt(minutes);
if (cacheTimeInMinutes >= currentTimeInMinutes) {
nodeCacheRef.current.delete(key);
}
}
});
// 清空link缓存
const linkCacheKeys = Array.from(linkCacheRef.current.keys());
linkCacheKeys.forEach((key) => {
const keyParts = key.split("_");
const cacheDate = keyParts[0].split("T")[0];
const cacheTimeStr = keyParts[0].split("T")[1];
if (cacheDate === currentDateStr && cacheTimeStr) {
const [hours, minutes] = cacheTimeStr.split(":");
const cacheTimeInMinutes = parseInt(hours) * 60 + parseInt(minutes);
if (cacheTimeInMinutes >= currentTimeInMinutes) {
linkCacheRef.current.delete(key);
}
}
});
open?.({ open?.({
type: "success", type: "success",
message: "重新计算成功", message: "重新计算成功",

View File

@@ -286,7 +286,7 @@ const SchemeQuery: React.FC<SchemeQueryProps> = ({
} }
return styles; return styles;
}; };
// 创建高亮图层 - 爆管管段标识样式 // 创建高亮图层
const highlightLayer = new VectorLayer({ const highlightLayer = new VectorLayer({
source: new VectorSource(), source: new VectorSource(),
style: burstPipeStyle, style: burstPipeStyle,

View File

@@ -103,7 +103,7 @@ const SchemeQuery: React.FC<SchemeQueryProps> = ({
anchor: [0.5, 1], anchor: [0.5, 1],
}), }),
}); });
// 创建高亮图层 - 爆管管段标识样式 // 创建高亮图层
const highlightLayer = new VectorLayer({ const highlightLayer = new VectorLayer({
source: new VectorSource(), source: new VectorSource(),
style: sensorStyle, style: sensorStyle,

View File

@@ -336,7 +336,7 @@ const SCADADataPanel: React.FC<SCADADataPanelProps> = ({
} else { } else {
setTimeSeries([]); setTimeSeries([]);
} }
}, [deviceIds.join(","), hasDevices]); }, [deviceIds.join(",")]); // 移除 hasDevices因为它由 deviceIds 决定,避免潜在的依赖循环
const columns: GridColDef[] = useMemo(() => { const columns: GridColDef[] = useMemo(() => {
const base: GridColDef[] = [ const base: GridColDef[] = [

View File

@@ -42,8 +42,13 @@ import { FixedSizeList } from "react-window";
import { useMap } from "@app/OlMap/MapComponent"; import { useMap } from "@app/OlMap/MapComponent";
import { GeoJSON } from "ol/format"; import { GeoJSON } from "ol/format";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { Stroke, Style, Circle } from "ol/style";
import Feature from "ol/Feature";
import { Point } from "ol/geom"; import { Point } from "ol/geom";
import config from "@/config/config"; import config from "@/config/config";
import { get } from "http";
const STATUS_OPTIONS: { const STATUS_OPTIONS: {
value: "online" | "offline" | "warning" | "error"; value: "online" | "offline" | "warning" | "error";
@@ -57,12 +62,14 @@ const STATUS_OPTIONS: {
interface SCADADevice { interface SCADADevice {
id: string; id: string;
name: string; name: string;
transmission_frequency: string;
reliability: number;
type: string; type: string;
coordinates: [number, number];
status: { status: {
value: "online" | "offline" | "warning" | "error"; value: "online" | "offline" | "warning" | "error";
name: "在线" | "离线" | "警告" | "错误"; name: "在线" | "离线" | "警告" | "错误";
}; };
coordinates: [number, number];
properties?: Record<string, any>; properties?: Record<string, any>;
} }
@@ -85,6 +92,7 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
const [searchQuery, setSearchQuery] = useState<string>(""); const [searchQuery, setSearchQuery] = useState<string>("");
const [selectedType, setSelectedType] = useState<string>("all"); const [selectedType, setSelectedType] = useState<string>("all");
const [selectedStatus, setSelectedStatus] = useState<string>("all"); const [selectedStatus, setSelectedStatus] = useState<string>("all");
const [selectedReliability, setSelectedReliability] = useState<string>("all");
const [isExpanded, setIsExpanded] = useState<boolean>(true); const [isExpanded, setIsExpanded] = useState<boolean>(true);
const [internalSelection, setInternalSelection] = useState<string[]>([]); const [internalSelection, setInternalSelection] = useState<string[]>([]);
const [pendingSelection, setPendingSelection] = useState<string[] | null>( const [pendingSelection, setPendingSelection] = useState<string[] | null>(
@@ -94,6 +102,10 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [inputValue, setInputValue] = useState<string>(""); const [inputValue, setInputValue] = useState<string>("");
const [highlightLayer, setHighlightLayer] =
useState<VectorLayer<VectorSource> | null>(null);
const [highlightFeatures, setHighlightFeatures] = useState<Feature[]>([]);
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null); const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
// 防抖更新搜索查询 // 防抖更新搜索查询
@@ -139,6 +151,8 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
const data = features.map((feature) => ({ const data = features.map((feature) => ({
id: feature.get("id") || feature.getId(), id: feature.get("id") || feature.getId(),
name: feature.get("id") || feature.getId(), name: feature.get("id") || feature.getId(),
transmission_frequency: feature.get("transmission_frequency"),
reliability: feature.get("reliability"),
type: feature.get("type") === "pipe_flow" ? "流量" : "压力", type: feature.get("type") === "pipe_flow" ? "流量" : "压力",
status: STATUS_OPTIONS[Math.floor(Math.random() * 4)], status: STATUS_OPTIONS[Math.floor(Math.random() * 4)],
coordinates: (feature.getGeometry() as Point)?.getCoordinates() as [ coordinates: (feature.getGeometry() as Point)?.getCoordinates() as [
@@ -170,6 +184,20 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
// 获取设备状态列表 // 获取设备状态列表
const deviceStatuses = STATUS_OPTIONS; const deviceStatuses = STATUS_OPTIONS;
// 可靠度文字映射
const getReliability = (reliability: number) => {
switch (reliability) {
case 1:
return "高";
case 2:
return "中";
case 3:
return "低";
default:
return "未知";
}
};
// 过滤设备列表 // 过滤设备列表
const filteredDevices = useMemo(() => { const filteredDevices = useMemo(() => {
return effectiveDevices.filter((device) => { return effectiveDevices.filter((device) => {
@@ -186,10 +214,21 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
selectedType === "all" || device.type === selectedType; selectedType === "all" || device.type === selectedType;
const matchesStatus = const matchesStatus =
selectedStatus === "all" || device.status.value === selectedStatus; selectedStatus === "all" || device.status.value === selectedStatus;
const matchesReliability =
selectedReliability === "all" ||
getReliability(device.reliability) === selectedReliability;
return matchesSearch && matchesType && matchesStatus; return (
matchesSearch && matchesType && matchesStatus && matchesReliability
);
}); });
}, [effectiveDevices, searchQuery, selectedType, selectedStatus]); }, [
effectiveDevices,
searchQuery,
selectedType,
selectedStatus,
selectedReliability,
]);
// 状态颜色映射 // 状态颜色映射
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
@@ -222,6 +261,17 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
return "●"; return "●";
} }
}; };
// 传输频率文字对应
const getTransmissionFrequency = (transmission_frequency: string) => {
// 传输频率文本00:01:0000:05:0000:10:0000:30:0001:00:00转换为分钟数
const parts = transmission_frequency.split(":");
if (parts.length !== 3) return transmission_frequency;
const hours = parseInt(parts[0], 10);
const minutes = parseInt(parts[1], 10);
const seconds = parseInt(parts[2], 10);
const totalMinutes = hours * 60 + minutes + (seconds >= 30 ? 1 : 0);
return totalMinutes;
};
// 处理设备点击 // 处理设备点击
const handleDeviceClick = (device: SCADADevice, event?: React.MouseEvent) => { const handleDeviceClick = (device: SCADADevice, event?: React.MouseEvent) => {
@@ -264,6 +314,7 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
setSearchQuery(""); setSearchQuery("");
setSelectedType("all"); setSelectedType("all");
setSelectedStatus("all"); setSelectedStatus("all");
setSelectedReliability("all");
}); });
}, []); }, []);
@@ -273,6 +324,56 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
setPendingSelection([]); setPendingSelection([]);
}, []); }, []);
// 初始化管道图层和高亮图层
useEffect(() => {
if (!map) return;
// 获取地图的目标容器
const SCADASelectedStyle = () => {
return new Style({
image: new Circle({
stroke: new Stroke({ color: "yellow", width: 2 }),
radius: 15,
}),
});
};
// 创建高亮图层
const highlightLayer = new VectorLayer({
source: new VectorSource(),
style: SCADASelectedStyle,
maxZoom: 24,
minZoom: 12,
properties: {
name: "SCADA 选中高亮",
value: "scada_selected_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);
}
});
}, [selectedDeviceIds, highlightFeatures]);
// 清理定时器 // 清理定时器
useEffect(() => { useEffect(() => {
return () => { return () => {
@@ -364,14 +465,14 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
{/* 筛选器 */} {/* 筛选器 */}
<Stack direction="row" spacing={2}> <Stack direction="row" spacing={2}>
<FormControl size="small" sx={{ minWidth: 120 }}> <FormControl size="small" sx={{ minWidth: 80 }}>
<InputLabel></InputLabel> <InputLabel></InputLabel>
<Select <Select
value={selectedType} value={selectedType}
label="设备类型" label="设备类型"
onChange={(e) => setSelectedType(e.target.value)} onChange={(e) => setSelectedType(e.target.value)}
> >
<MenuItem value="all"></MenuItem> <MenuItem value="all"></MenuItem>
{deviceTypes.map((type) => ( {deviceTypes.map((type) => (
<MenuItem key={type} value={type}> <MenuItem key={type} value={type}>
{type} {type}
@@ -380,14 +481,14 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
</Select> </Select>
</FormControl> </FormControl>
<FormControl size="small" sx={{ minWidth: 100 }}> <FormControl size="small" sx={{ minWidth: 80 }}>
<InputLabel></InputLabel> <InputLabel></InputLabel>
<Select <Select
value={selectedStatus} value={selectedStatus}
label="状态" label="状态"
onChange={(e) => setSelectedStatus(e.target.value)} onChange={(e) => setSelectedStatus(e.target.value)}
> >
<MenuItem value="all"></MenuItem> <MenuItem value="all"></MenuItem>
{deviceStatuses.map((status) => ( {deviceStatuses.map((status) => (
<MenuItem key={status.value} value={status.value}> <MenuItem key={status.value} value={status.value}>
{status.name} {status.name}
@@ -396,6 +497,20 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
</Select> </Select>
</FormControl> </FormControl>
<FormControl size="small" sx={{ minWidth: 80 }}>
<InputLabel></InputLabel>
<Select
value={selectedReliability}
label="可靠度"
onChange={(e) => setSelectedReliability(e.target.value)}
>
<MenuItem value="all"></MenuItem>
<MenuItem value="高"></MenuItem>
<MenuItem value="中"></MenuItem>
<MenuItem value="低"></MenuItem>
</Select>
</FormControl>
<Tooltip title="重置筛选条件"> <Tooltip title="重置筛选条件">
<IconButton onClick={handleResetFilters}> <IconButton onClick={handleResetFilters}>
<FilterList /> <FilterList />
@@ -523,6 +638,14 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
variant="outlined" variant="outlined"
sx={{ fontSize: "0.7rem", height: 20 }} sx={{ fontSize: "0.7rem", height: 20 }}
/> />
<Chip
label={
"可靠度" + getReliability(device.reliability)
}
size="small"
variant="outlined"
sx={{ fontSize: "0.7rem", height: 20 }}
/>
</Stack> </Stack>
} }
secondary={ secondary={
@@ -537,8 +660,11 @@ const SCADADeviceList: React.FC<SCADADeviceListProps> = ({
variant="caption" variant="caption"
color="text.secondary" color="text.secondary"
> >
: {device.coordinates[0].toFixed(6)},{" "} :{" "}
{device.coordinates[1].toFixed(6)} {getTransmissionFrequency(
device.transmission_frequency
)}{" "}
</Typography> </Typography>
</Stack> </Stack>
} }