重新设计属性面板,为 demand 提供二级表格显示更多信息
This commit is contained in:
@@ -7,10 +7,20 @@ interface BaseProperty {
|
||||
formatter?: (value: string | number) => string;
|
||||
}
|
||||
|
||||
// 新增:表格型属性(用于二级数据)
|
||||
interface TableProperty {
|
||||
type: "table";
|
||||
label: string;
|
||||
columns: string[]; // 表头
|
||||
rows: (string | number)[][]; // 每行的数据
|
||||
}
|
||||
|
||||
type PropertyItem = BaseProperty | TableProperty;
|
||||
|
||||
interface PropertyPanelProps {
|
||||
id?: string;
|
||||
type?: string;
|
||||
properties?: BaseProperty[];
|
||||
properties?: PropertyItem[];
|
||||
}
|
||||
|
||||
const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||
@@ -30,8 +40,17 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||
|
||||
const isImportantKeys = ["ID", "类型", "Name", "面积", "长度"];
|
||||
|
||||
// 统计属性数量(表格型按行数计入)
|
||||
const totalProps = id
|
||||
? 2 +
|
||||
properties.reduce((sum, p) => {
|
||||
if ("type" in p && p.type === "table") return sum + p.rows.length;
|
||||
return sum + 1;
|
||||
}, 0)
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<div className="absolute top-4 right-4 bg-white shadow-2xl rounded-xl overflow-hidden w-96 z-10 max-h-[850px] flex flex-col backdrop-blur-sm opacity-95 hover:opacity-100 transition-all duration-300">
|
||||
<div className="absolute top-4 right-4 bg-white shadow-2xl rounded-xl overflow-hidden w-96 z-10 max-h[850px] flex flex-col backdrop-blur-sm opacity-95 hover:opacity-100 transition-all duration-300">
|
||||
{/* 头部 */}
|
||||
<div className="flex justify-between items-center px-5 py-4 bg-[#257DD4] text-white">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -98,12 +117,60 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 其他属性 */}
|
||||
{/* 其他属性(包含二级表格) */}
|
||||
{properties.map((property, index) => {
|
||||
const isImportant = isImportantKeys.includes(property.label);
|
||||
// 二级表格
|
||||
if ("type" in property && property.type === "table") {
|
||||
return (
|
||||
<div
|
||||
key={`table-${index}`}
|
||||
className="group rounded-lg p-3 transition-all duration-200 bg-gray-50 hover:bg-gray-100"
|
||||
>
|
||||
<div className="flex justify-between items-start gap-3">
|
||||
<span className="font-medium text-xs uppercase tracking-wide text-gray-600">
|
||||
{property.label}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ml-4 mt-2 border border-gray-300 rounded-md overflow-hidden shadow-sm">
|
||||
<table className="w-full text-xs">
|
||||
<thead className="bg-gray-200 text-gray-700">
|
||||
<tr>
|
||||
{property.columns.map((col, ci) => (
|
||||
<th
|
||||
key={ci}
|
||||
className="px-3 py-2 text-left font-semibold"
|
||||
>
|
||||
{col}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-300">
|
||||
{property.rows.map((row, ri) => (
|
||||
<tr key={ri} className="bg-white hover:bg-gray-50">
|
||||
{row.map((cell, cci) => (
|
||||
<td
|
||||
key={cci}
|
||||
className="px-3 py-2 text-gray-800"
|
||||
>
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 普通属性
|
||||
const base = property as BaseProperty;
|
||||
const isImportant = isImportantKeys.includes(base.label);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
key={`prop-${index}`}
|
||||
className={`group rounded-lg p-3 transition-all duration-200 ${
|
||||
isImportant
|
||||
? "bg-blue-50 hover:bg-blue-100 border-l-4 border-blue-500"
|
||||
@@ -116,14 +183,14 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||
isImportant ? "text-blue-700" : "text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{property.label}
|
||||
{base.label}
|
||||
</span>
|
||||
<span
|
||||
className={`text-sm font-semibold text-right flex-1 ${
|
||||
isImportant ? "text-blue-900" : "text-gray-800"
|
||||
}`}
|
||||
>
|
||||
{formatValue(property)}
|
||||
{formatValue(base)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,7 +217,7 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
||||
/>
|
||||
</svg>
|
||||
共 {id ? properties.length + 2 : 0} 个属性
|
||||
共 {totalProps} 个属性
|
||||
</span>
|
||||
{id && (
|
||||
<span className="text-green-600 flex items-center gap-1 font-medium">
|
||||
|
||||
@@ -68,7 +68,7 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
const { open, close } = useNotification();
|
||||
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
const [playInterval, setPlayInterval] = useState<number>(5000); // 毫秒
|
||||
const [playInterval, setPlayInterval] = useState<number>(15000); // 毫秒
|
||||
const [calculatedInterval, setCalculatedInterval] = useState<number>(15); // 分钟
|
||||
const [isCalculating, setIsCalculating] = useState<boolean>(false);
|
||||
|
||||
@@ -206,9 +206,9 @@ const Timeline: React.FC<TimelineProps> = ({
|
||||
|
||||
// 播放时间间隔选项
|
||||
const intervalOptions = [
|
||||
{ value: 2000, label: "2秒" },
|
||||
{ value: 5000, label: "5秒" },
|
||||
{ value: 10000, label: "10秒" },
|
||||
{ value: 15000, label: "15秒" },
|
||||
{ value: 20000, label: "20秒" },
|
||||
];
|
||||
// 强制计算时间段选项
|
||||
const calculatedIntervalOptions = [
|
||||
|
||||
@@ -4,6 +4,7 @@ import ToolbarButton from "@/components/olmap/common/ToolbarButton";
|
||||
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
||||
import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
|
||||
import PaletteOutlinedIcon from "@mui/icons-material/PaletteOutlined";
|
||||
import QueryStatsOutlinedIcon from "@mui/icons-material/QueryStatsOutlined";
|
||||
import PropertyPanel from "./PropertyPanel"; // 引入属性面板组件
|
||||
import DrawPanel from "./DrawPanel"; // 引入绘图面板组件
|
||||
|
||||
@@ -266,6 +267,9 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
case "draw":
|
||||
setShowDrawPanel(false);
|
||||
break;
|
||||
case "history":
|
||||
// 取消历史查询激活时的清理(目前不保留额外状态)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -278,6 +282,47 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
case "draw":
|
||||
setShowDrawPanel(true);
|
||||
break;
|
||||
case "history":
|
||||
// 激活历史查询后立即触发一次网络历史数据查询(结果暂时打印到控制台)
|
||||
queryNetworkHistory();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 查询管网历史数据的函数(激活时调用)
|
||||
const queryNetworkHistory = async () => {
|
||||
try {
|
||||
// 由当前选中日期和 currentTime 构造查询时间(UTC ISO)
|
||||
let dateObj: Date;
|
||||
if (selectedDate instanceof Date) {
|
||||
dateObj = new Date(selectedDate);
|
||||
} else {
|
||||
dateObj = new Date(selectedDate as any);
|
||||
}
|
||||
const minutes = Number(currentTime) || 0;
|
||||
dateObj.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0);
|
||||
const querytime = dateObj.toISOString();
|
||||
|
||||
let url: string;
|
||||
if (queryType === "scheme") {
|
||||
url = `${backendUrl}/queryschemesimulationrecordsbytime/?scheme_name=${schemeName}&querytime=${querytime}`;
|
||||
} else {
|
||||
url = `${backendUrl}/querysimulationrecordsbytime/?querytime=${querytime}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
console.error("查询管网历史数据失败:", response.statusText);
|
||||
return;
|
||||
}
|
||||
const result = await response.json();
|
||||
// TODO: 根据需要把结果展示到面板或状态中,目前先打印
|
||||
console.log("管网历史数据:", result);
|
||||
// 简单提示用户已查询(可改为更友好的 UI)
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("已查询管网历史数据(请查看控制台或后续面板展示)。");
|
||||
} catch (error) {
|
||||
console.error("查询管网历史数据出错:", error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -363,7 +408,7 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
const nodeComputedFields = [
|
||||
{ key: "actualdemand", label: "实际需水量", unit: "m³/s" },
|
||||
{ key: "head", label: "水头", unit: "m" },
|
||||
{ key: "pressure", label: "压力", unit: "kPa" },
|
||||
{ key: "pressure", label: "压力", unit: "m" },
|
||||
{ key: "quality", label: "水质", unit: "mg/L" },
|
||||
];
|
||||
|
||||
@@ -410,11 +455,22 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
value: properties.elevation?.toFixed?.(1),
|
||||
unit: "m",
|
||||
},
|
||||
// 将 demand1~demand5 与 pattern1~pattern5 作为二级表格展示
|
||||
{
|
||||
label: "需求量",
|
||||
value: properties.demand?.toFixed?.(1),
|
||||
unit: "m³/s",
|
||||
},
|
||||
type: "table",
|
||||
label: "基本需水量",
|
||||
columns: ["demand", "pattern"],
|
||||
rows: Array.from({ length: 5 }, (_, i) => i + 1)
|
||||
.map((idx) => {
|
||||
const d = properties?.[`demand${idx}`]?.toFixed?.(3);
|
||||
const p = properties?.[`pattern${idx}`];
|
||||
// 仅当 demand 有效时展示该行
|
||||
if (d !== undefined && d !== null && d !== "") {
|
||||
return [typeof d === "number" ? d.toFixed(3) : d, p ?? "-"];
|
||||
}
|
||||
})
|
||||
.filter(Boolean) as (string | number)[][],
|
||||
} as any,
|
||||
],
|
||||
};
|
||||
// 追加计算属性
|
||||
@@ -623,6 +679,14 @@ const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||||
onClick={() => handleToolClick("info")}
|
||||
/>
|
||||
)}
|
||||
{!hiddenButtons?.includes("history") && (
|
||||
<ToolbarButton
|
||||
icon={<QueryStatsOutlinedIcon />}
|
||||
name="查询历史数据"
|
||||
isActive={activeTools.includes("history")}
|
||||
onClick={() => handleToolClick("history")}
|
||||
/>
|
||||
)}
|
||||
{!hiddenButtons?.includes("draw") && (
|
||||
<ToolbarButton
|
||||
icon={<EditOutlinedIcon />}
|
||||
|
||||
Reference in New Issue
Block a user