调整数据小数点位数显示;更换 valve 图标,上调 valvesLayer 的显示层级;特殊处理流量的样式显示
This commit is contained in:
@@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761789564862" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="70077" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M513.640525 513.911813m-510.088186 0a510.088187 510.088187 0 1 0 1020.176373 0 510.088187 510.088187 0 1 0-1020.176373 0Z" fill="#F1C339" p-id="70078"></path><path d="M483.307444 389.380334v-87.682364H351.164541c-17.997151 0-32.651827-14.204236-32.651827-31.628099 0-17.428981 14.670032-31.633217 32.651827-31.633217h320.186511c17.986913 0 32.64159 14.204236 32.64159 31.633217 0 17.613252-14.654677 31.628099-32.631353 31.628099h-131.948393v87.677246h10.349897c12.315456 0 22.286574 9.65888 22.286574 21.590438v51.590809l-121.393751-0.194509v-51.391181c0-11.931558 9.965999-21.590439 22.286574-21.590439h10.365254z m-217.450216 336.248814v36.362844c0 5.302915-4.463457 9.663999-10.078609 9.663999h-25.792845c-5.620271 0-10.083728-4.361084-10.083728-9.853389V529.226795c0-5.487186 4.463457-9.84827 10.083728-9.84827h25.792845c5.620271 0 10.078609 4.361084 10.078609 9.84827v47.352571h85.757754c3.572813-48.719249 45.141318-87.124432 95.918262-87.124432h118.471005c50.776944 0 92.345448 38.405183 95.928499 87.124432h85.942025v-47.736469c0-5.287559 4.268949-9.464372 9.704948-9.464372h26.366133c5.425762 0 9.689592 4.161457 9.689593 9.464372v233.333366c0.194508 5.313152-4.26383 9.479728-9.694711 9.479728h-26.371252c-5.425762 0-9.694711-4.161457-9.694711-9.479728v-36.547115h-92.304499c-14.009727 34.924505-48.821622 59.652672-89.566025 59.652672H447.533244c-40.739284 0-75.551179-24.71793-89.571144-59.652672H265.857228z" fill="#FFFFFF" p-id="70079"></path></svg>
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763523822720" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21241" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M513.640525 513.911813m-510.088186 0a510.088187 510.088187 0 1 0 1020.176373 0 510.088187 510.088187 0 1 0-1020.176373 0Z" fill="#8a8a8a" p-id="21242"></path><path d="M483.307444 389.380334v-87.682364H351.164541c-17.997151 0-32.651827-14.204236-32.651827-31.628099 0-17.428981 14.670032-31.633217 32.651827-31.633217h320.186511c17.986913 0 32.64159 14.204236 32.64159 31.633217 0 17.613252-14.654677 31.628099-32.631353 31.628099h-131.948393v87.677246h10.349897c12.315456 0 22.286574 9.65888 22.286574 21.590438v51.590809l-121.393751-0.194509v-51.391181c0-11.931558 9.965999-21.590439 22.286574-21.590439h10.365254z m-217.450216 336.248814v36.362844c0 5.302915-4.463457 9.663999-10.078609 9.663999h-25.792845c-5.620271 0-10.083728-4.361084-10.083728-9.853389V529.226795c0-5.487186 4.463457-9.84827 10.083728-9.84827h25.792845c5.620271 0 10.078609 4.361084 10.078609 9.84827v47.352571h85.757754c3.572813-48.719249 45.141318-87.124432 95.918262-87.124432h118.471005c50.776944 0 92.345448 38.405183 95.928499 87.124432h85.942025v-47.736469c0-5.287559 4.268949-9.464372 9.704948-9.464372h26.366133c5.425762 0 9.689592 4.161457 9.689593 9.464372v233.333366c0.194508 5.313152-4.26383 9.479728-9.694711 9.479728h-26.371252c-5.425762 0-9.694711-4.161457-9.694711-9.479728v-36.547115h-92.304499c-14.009727 34.924505-48.821622 59.652672-89.566025 59.652672H447.533244c-40.739284 0-75.551179-24.71793-89.571144-59.652672H265.857228z" fill="#FFFFFF" p-id="21243"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -70,7 +70,6 @@ export interface HistoryDataPanelProps {
|
||||
defaultTab?: "chart" | "table";
|
||||
/** Y 轴数值的小数位数 */
|
||||
fractionDigits?: number;
|
||||
// 清洗功能已移除,相关参数请通过外部面板/服务清洗后再传入
|
||||
}
|
||||
|
||||
type PanelTab = "chart" | "table";
|
||||
@@ -235,7 +234,7 @@ const HistoryDataPanel: React.FC<HistoryDataPanelProps> = ({
|
||||
fetchTimeSeriesData = defaultFetcher,
|
||||
visible = true,
|
||||
defaultTab = "chart",
|
||||
fractionDigits = 2,
|
||||
fractionDigits = 3,
|
||||
}) => {
|
||||
// 从 devices 中提取 id 列表用于渲染与查询
|
||||
const deviceIds = devices?.map((d) => d.id) ?? [];
|
||||
|
||||
@@ -301,7 +301,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
if (layerId !== undefined && property !== undefined) {
|
||||
// 验证自定义断点设置
|
||||
if (styleConfig.classificationMethod === "custom_breaks") {
|
||||
const expected = styleConfig.segments + 1;
|
||||
const expected = styleConfig.segments;
|
||||
const custom = styleConfig.customBreaks || [];
|
||||
if (
|
||||
custom.length !== expected ||
|
||||
@@ -609,7 +609,6 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
// 更新当前 VectorTileSource 中的所有缓冲要素属性
|
||||
const updateVectorTileSource = (property: string, data: any[]) => {
|
||||
if (!map) return;
|
||||
|
||||
const vectorTileSources = map
|
||||
.getAllLayers()
|
||||
.filter((layer) => layer instanceof WebGLVectorTileLayer)
|
||||
@@ -636,7 +635,12 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
const featureId = renderFeature.get("id");
|
||||
const value = dataMap.get(featureId);
|
||||
if (value !== undefined) {
|
||||
(renderFeature as any).properties_[property] = value;
|
||||
if (property === "flow") {
|
||||
// 特殊处理流量属性,取绝对值
|
||||
(renderFeature as any).properties_[property] = Math.abs(value);
|
||||
} else {
|
||||
(renderFeature as any).properties_[property] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -677,7 +681,12 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
const featureId = renderFeature.get("id");
|
||||
const value = dataMap.get(featureId);
|
||||
if (value !== undefined) {
|
||||
(renderFeature as any).properties_[property] = value;
|
||||
if (property === "flow") {
|
||||
// 特殊处理流量属性,取绝对值
|
||||
(renderFeature as any).properties_[property] = Math.abs(value);
|
||||
} else {
|
||||
(renderFeature as any).properties_[property] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -845,7 +854,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
useEffect(() => {
|
||||
if (styleConfig.classificationMethod !== "custom_breaks") return;
|
||||
|
||||
const numBreaks = styleConfig.segments + 1;
|
||||
const numBreaks = styleConfig.segments;
|
||||
setStyleConfig((prev) => {
|
||||
const prevBreaks = prev.customBreaks || [];
|
||||
if (prevBreaks.length === numBreaks) return prev;
|
||||
@@ -1372,46 +1381,44 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
className="flex flex-col gap-2"
|
||||
sx={{ maxHeight: "240px", overflowY: "auto", paddingTop: "12px" }}
|
||||
>
|
||||
{Array.from({ length: styleConfig.segments + 1 }).map(
|
||||
(_, idx) => (
|
||||
<TextField
|
||||
key={idx}
|
||||
label={`阈值 ${idx + 1}`}
|
||||
type="number"
|
||||
size="small"
|
||||
slotProps={{ input: { inputProps: { min: 0, step: 0.1 } } }}
|
||||
value={
|
||||
(styleConfig.customBreaks &&
|
||||
styleConfig.customBreaks[idx]) ??
|
||||
""
|
||||
}
|
||||
onChange={(e) => {
|
||||
const v = parseFloat(e.target.value);
|
||||
setStyleConfig((prev) => {
|
||||
const prevBreaks = prev.customBreaks
|
||||
? [...prev.customBreaks]
|
||||
: [];
|
||||
// 保证长度
|
||||
while (prevBreaks.length < styleConfig.segments + 1)
|
||||
prevBreaks.push(0);
|
||||
prevBreaks[idx] = isNaN(v) ? 0 : Math.max(0, v);
|
||||
return { ...prev, customBreaks: prevBreaks };
|
||||
});
|
||||
}}
|
||||
onBlur={() => {
|
||||
// on blur 保证升序
|
||||
setStyleConfig((prev) => {
|
||||
const prevBreaks = (prev.customBreaks || []).slice(
|
||||
0,
|
||||
styleConfig.segments + 1
|
||||
);
|
||||
prevBreaks.sort((a, b) => a - b);
|
||||
return { ...prev, customBreaks: prevBreaks };
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{Array.from({ length: styleConfig.segments }).map((_, idx) => (
|
||||
<TextField
|
||||
key={idx}
|
||||
label={`阈值 ${idx + 1}`}
|
||||
type="number"
|
||||
size="small"
|
||||
slotProps={{ input: { inputProps: { min: 0, step: 0.1 } } }}
|
||||
value={
|
||||
(styleConfig.customBreaks &&
|
||||
styleConfig.customBreaks[idx]) ??
|
||||
""
|
||||
}
|
||||
onChange={(e) => {
|
||||
const v = parseFloat(e.target.value);
|
||||
setStyleConfig((prev) => {
|
||||
const prevBreaks = prev.customBreaks
|
||||
? [...prev.customBreaks]
|
||||
: [];
|
||||
// 保证长度
|
||||
while (prevBreaks.length < styleConfig.segments + 1)
|
||||
prevBreaks.push(0);
|
||||
prevBreaks[idx] = isNaN(v) ? 0 : Math.max(0, v);
|
||||
return { ...prev, customBreaks: prevBreaks };
|
||||
});
|
||||
}}
|
||||
onBlur={() => {
|
||||
// on blur 保证升序
|
||||
setStyleConfig((prev) => {
|
||||
const prevBreaks = (prev.customBreaks || []).slice(
|
||||
0,
|
||||
styleConfig.segments + 1
|
||||
);
|
||||
prevBreaks.sort((a, b) => a - b);
|
||||
return { ...prev, customBreaks: prevBreaks };
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
@@ -1473,7 +1480,7 @@ const StyleEditorPanel: React.FC<StyleEditorPanelProps> = ({
|
||||
}
|
||||
/>
|
||||
}
|
||||
label="显示属性(放大后显示)"
|
||||
label="显示属性(缩放 >=15 级时显示)"
|
||||
/>
|
||||
<div className="my-3"></div>
|
||||
{/* 操作按钮 */}
|
||||
|
||||
@@ -403,7 +403,7 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
result.properties.push({
|
||||
label,
|
||||
value:
|
||||
computedProperties[key].toFixed?.(2) || computedProperties[key],
|
||||
computedProperties[key].toFixed?.(3) || computedProperties[key],
|
||||
unit,
|
||||
});
|
||||
}
|
||||
@@ -446,7 +446,7 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
result.properties.push({
|
||||
label,
|
||||
value:
|
||||
computedProperties[key].toFixed?.(2) || computedProperties[key],
|
||||
computedProperties[key].toFixed?.(3) || computedProperties[key],
|
||||
unit,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
// 缓存 junction、pipe 数据,提供给 deck.gl 显示标签使用
|
||||
// 缓存 junction、pipe 数据,提供给 deck.gl 提供坐标供标签显示
|
||||
junctionSource.on("tileloadend", (event) => {
|
||||
try {
|
||||
if (event.tile instanceof VectorTile) {
|
||||
@@ -555,14 +555,14 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
});
|
||||
// 重新排列图层顺序,确保顺序 点>线>面
|
||||
availableLayers.sort((a, b) => {
|
||||
// 明确顺序(点类优先)
|
||||
// 明确顺序(点类优先),这里 valves 特殊处理
|
||||
const order = [
|
||||
"valves",
|
||||
"junctions",
|
||||
"scada",
|
||||
"reservoirs",
|
||||
"pumps",
|
||||
"tanks",
|
||||
"valves",
|
||||
"pipes",
|
||||
].reverse();
|
||||
// 取值时做安全检查,兼容不同写法(properties.value 或 直接 value)
|
||||
@@ -850,7 +850,9 @@ const MapComponent: React.FC<MapComponentProps> = ({ children }) => {
|
||||
pipeProperties === "flow" && record.value < 0 && p.flowFlag > 0
|
||||
? [...p.path].reverse()
|
||||
: p.path,
|
||||
[pipeProperties]: record.value,
|
||||
// 流量数值
|
||||
[pipeProperties]:
|
||||
pipeProperties === "flow" ? Math.abs(record.value) : record.value,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -14,8 +14,11 @@ import {
|
||||
Chip,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
} from '@mui/material';
|
||||
import { LocationOn as LocationIcon, Visibility as VisibilityIcon } from '@mui/icons-material';
|
||||
} from "@mui/material";
|
||||
import {
|
||||
LocationOn as LocationIcon,
|
||||
Visibility as VisibilityIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
interface LocationResult {
|
||||
id: number;
|
||||
@@ -24,7 +27,7 @@ interface LocationResult {
|
||||
pressure: number;
|
||||
waterLevel: number;
|
||||
flow: number;
|
||||
status: 'normal' | 'warning' | 'danger';
|
||||
status: "normal" | "warning" | "danger";
|
||||
coordinates: [number, number];
|
||||
}
|
||||
|
||||
@@ -33,7 +36,10 @@ interface LocationResultsProps {
|
||||
onViewDetail?: (id: number) => void;
|
||||
}
|
||||
|
||||
const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetail }) => {
|
||||
const LocationResults: React.FC<LocationResultsProps> = ({
|
||||
onLocate,
|
||||
onViewDetail,
|
||||
}) => {
|
||||
const [results, setResults] = useState<LocationResult[]>([
|
||||
// 示例数据
|
||||
// {
|
||||
@@ -50,27 +56,27 @@ const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetai
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'normal':
|
||||
return 'success';
|
||||
case 'warning':
|
||||
return 'warning';
|
||||
case 'danger':
|
||||
return 'error';
|
||||
case "normal":
|
||||
return "success";
|
||||
case "warning":
|
||||
return "warning";
|
||||
case "danger":
|
||||
return "error";
|
||||
default:
|
||||
return 'default';
|
||||
return "default";
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'normal':
|
||||
return '正常';
|
||||
case 'warning':
|
||||
return '预警';
|
||||
case 'danger':
|
||||
return '危险';
|
||||
case "normal":
|
||||
return "正常";
|
||||
case "warning":
|
||||
return "预警";
|
||||
case "danger":
|
||||
return "危险";
|
||||
default:
|
||||
return '未知';
|
||||
return "未知";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,7 +101,7 @@ const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetai
|
||||
正常
|
||||
</Typography>
|
||||
<Typography variant="h6" className="font-bold text-green-600">
|
||||
{results.filter((r) => r.status === 'normal').length}
|
||||
{results.filter((r) => r.status === "normal").length}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
@@ -103,7 +109,7 @@ const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetai
|
||||
预警
|
||||
</Typography>
|
||||
<Typography variant="h6" className="font-bold text-orange-600">
|
||||
{results.filter((r) => r.status === 'warning').length}
|
||||
{results.filter((r) => r.status === "warning").length}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
@@ -111,7 +117,7 @@ const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetai
|
||||
危险
|
||||
</Typography>
|
||||
<Typography variant="h6" className="font-bold text-red-600">
|
||||
{results.filter((r) => r.status === 'danger').length}
|
||||
{results.filter((r) => r.status === "danger").length}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -129,12 +135,46 @@ const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetai
|
||||
fill="none"
|
||||
className="opacity-40"
|
||||
>
|
||||
<circle cx="40" cy="40" r="25" stroke="currentColor" strokeWidth="2" />
|
||||
<circle
|
||||
cx="40"
|
||||
cy="40"
|
||||
r="25"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<circle cx="40" cy="40" r="5" fill="currentColor" />
|
||||
<line x1="40" y1="15" x2="40" y2="25" stroke="currentColor" strokeWidth="2" />
|
||||
<line x1="40" y1="55" x2="40" y2="65" stroke="currentColor" strokeWidth="2" />
|
||||
<line x1="15" y1="40" x2="25" y2="40" stroke="currentColor" strokeWidth="2" />
|
||||
<line x1="55" y1="40" x2="65" y2="40" stroke="currentColor" strokeWidth="2" />
|
||||
<line
|
||||
x1="40"
|
||||
y1="15"
|
||||
x2="40"
|
||||
y2="25"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<line
|
||||
x1="40"
|
||||
y1="55"
|
||||
x2="40"
|
||||
y2="65"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<line
|
||||
x1="15"
|
||||
y1="40"
|
||||
x2="25"
|
||||
y2="40"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<line
|
||||
x1="55"
|
||||
y1="40"
|
||||
x2="65"
|
||||
y2="40"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
</svg>
|
||||
</Box>
|
||||
<Typography variant="body2">暂无定位结果</Typography>
|
||||
@@ -161,9 +201,15 @@ const LocationResults: React.FC<LocationResultsProps> = ({ onLocate, onViewDetai
|
||||
<TableRow key={result.id} hover>
|
||||
<TableCell>{result.nodeName}</TableCell>
|
||||
<TableCell>{result.nodeId}</TableCell>
|
||||
<TableCell align="right">{result.pressure.toFixed(2)}</TableCell>
|
||||
<TableCell align="right">{result.waterLevel.toFixed(2)}</TableCell>
|
||||
<TableCell align="right">{result.flow.toFixed(1)}</TableCell>
|
||||
<TableCell align="right">
|
||||
{result.pressure.toFixed(3)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{result.waterLevel.toFixed(3)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{result.flow.toFixed(1)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Chip
|
||||
label={getStatusText(result.status)}
|
||||
|
||||
Reference in New Issue
Block a user