703 lines
22 KiB
TypeScript
703 lines
22 KiB
TypeScript
import React, { useState, useEffect, useCallback } from "react";
|
||
import { useData, useMap } from "../MapComponent";
|
||
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"; // 引入绘图面板组件
|
||
|
||
import VectorSource from "ol/source/Vector";
|
||
import VectorLayer from "ol/layer/Vector";
|
||
import { Style, Stroke, Fill, Circle } from "ol/style";
|
||
import { FeatureLike } from "ol/Feature";
|
||
import Feature from "ol/Feature";
|
||
import StyleEditorPanel from "./StyleEditorPanel";
|
||
import StyleLegend from "./StyleLegend"; // 引入图例组件
|
||
import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService";
|
||
|
||
import { config } from "@/config/config";
|
||
const backendUrl = config.BACKEND_URL;
|
||
|
||
import { LayerStyleState } from "./StyleEditorPanel";
|
||
|
||
// 添加接口定义隐藏按钮的props
|
||
interface ToolbarProps {
|
||
hiddenButtons?: string[]; // 可选的隐藏按钮列表,例如 ['info', 'draw', 'style']
|
||
queryType?: string; // 可选的查询类型参数
|
||
}
|
||
const Toolbar: React.FC<ToolbarProps> = ({ hiddenButtons, queryType }) => {
|
||
const map = useMap();
|
||
const data = useData();
|
||
if (!data) return null;
|
||
const { currentTime, selectedDate, schemeName } = data;
|
||
const [activeTools, setActiveTools] = useState<string[]>([]);
|
||
const [highlightFeature, setHighlightFeature] = useState<FeatureLike | null>(
|
||
null
|
||
);
|
||
const [showPropertyPanel, setShowPropertyPanel] = useState<boolean>(false);
|
||
const [showDrawPanel, setShowDrawPanel] = useState<boolean>(false);
|
||
const [showStyleEditor, setShowStyleEditor] = useState<boolean>(false);
|
||
const [highlightLayer, setHighlightLayer] =
|
||
useState<VectorLayer<VectorSource> | null>(null);
|
||
|
||
// 样式状态管理 - 在 Toolbar 中管理,带有默认样式
|
||
const [layerStyleStates, setLayerStyleStates] = useState<LayerStyleState[]>([
|
||
{
|
||
isActive: false, // 默认不激活,不显示图例
|
||
layerId: "junctions",
|
||
layerName: "节点图层",
|
||
styleConfig: {
|
||
property: "pressure",
|
||
classificationMethod: "pretty_breaks",
|
||
segments: 6,
|
||
minSize: 4,
|
||
maxSize: 12,
|
||
minStrokeWidth: 2,
|
||
maxStrokeWidth: 8,
|
||
fixedStrokeWidth: 3,
|
||
colorType: "gradient",
|
||
singlePaletteIndex: 0,
|
||
gradientPaletteIndex: 0,
|
||
rainbowPaletteIndex: 0,
|
||
showLabels: false,
|
||
opacity: 0.9,
|
||
adjustWidthByProperty: true,
|
||
},
|
||
legendConfig: {
|
||
layerId: "junctions",
|
||
layerName: "节点图层",
|
||
property: "压力", // 暂时为空,等计算后更新
|
||
colors: [],
|
||
type: "point",
|
||
dimensions: [],
|
||
breaks: [],
|
||
},
|
||
},
|
||
{
|
||
isActive: false, // 默认不激活,不显示图例
|
||
layerId: "pipes",
|
||
layerName: "管道图层",
|
||
styleConfig: {
|
||
property: "flow",
|
||
classificationMethod: "pretty_breaks",
|
||
segments: 6,
|
||
minSize: 4,
|
||
maxSize: 12,
|
||
minStrokeWidth: 2,
|
||
maxStrokeWidth: 8,
|
||
fixedStrokeWidth: 3,
|
||
colorType: "gradient",
|
||
singlePaletteIndex: 0,
|
||
gradientPaletteIndex: 0,
|
||
rainbowPaletteIndex: 0,
|
||
showLabels: false,
|
||
opacity: 0.9,
|
||
adjustWidthByProperty: true,
|
||
},
|
||
legendConfig: {
|
||
layerId: "pipes",
|
||
layerName: "管道图层",
|
||
property: "流量", // 暂时为空,等计算后更新
|
||
colors: [],
|
||
type: "linestring",
|
||
dimensions: [],
|
||
breaks: [],
|
||
},
|
||
},
|
||
]);
|
||
|
||
// 计算激活的图例配置
|
||
const activeLegendConfigs = layerStyleStates
|
||
.filter((state) => state.isActive && state.legendConfig.property)
|
||
.map((state) => ({
|
||
...state.legendConfig,
|
||
layerName: state.layerName,
|
||
layerId: state.layerId,
|
||
}));
|
||
// 创建高亮图层
|
||
useEffect(() => {
|
||
if (!map) return;
|
||
|
||
const highLightSource = new VectorSource();
|
||
const highLightLayer = new VectorLayer({
|
||
source: highLightSource,
|
||
style: new Style({
|
||
stroke: new Stroke({
|
||
color: `rgba(255, 0, 0, 1)`,
|
||
width: 5,
|
||
}),
|
||
fill: new Fill({
|
||
color: `rgba(255, 0, 0, 0.2)`,
|
||
}),
|
||
image: new Circle({
|
||
radius: 7,
|
||
stroke: new Stroke({
|
||
color: `rgba(255, 0, 0, 1)`,
|
||
width: 3,
|
||
}),
|
||
fill: new Fill({
|
||
color: `rgba(255, 0, 0, 0.2)`,
|
||
}),
|
||
}),
|
||
}),
|
||
properties: {
|
||
name: "属性查询高亮图层", // 设置图层名称
|
||
value: "info_highlight_layer",
|
||
type: "multigeometry",
|
||
properties: [],
|
||
},
|
||
});
|
||
|
||
map.addLayer(highLightLayer);
|
||
setHighlightLayer(highLightLayer);
|
||
|
||
return () => {
|
||
map.removeLayer(highLightLayer);
|
||
};
|
||
}, [map]);
|
||
// 高亮要素的函数
|
||
useEffect(() => {
|
||
if (!highlightLayer) {
|
||
return;
|
||
}
|
||
const source = highlightLayer.getSource();
|
||
if (!source) {
|
||
return;
|
||
}
|
||
// 清除之前的高亮
|
||
source.clear();
|
||
// 添加新的高亮要素
|
||
if (highlightFeature instanceof Feature) {
|
||
source.addFeature(highlightFeature);
|
||
}
|
||
}, [highlightFeature]);
|
||
// 地图点击选择要素事件处理函数
|
||
const handleMapClickSelectFeatures = useCallback(
|
||
async (event: { coordinate: number[] }) => {
|
||
if (!map) return;
|
||
const feature = await mapClickSelectFeatures(event, map); // 调用导入的函数
|
||
setHighlightFeature(feature);
|
||
},
|
||
[map, setHighlightFeature]
|
||
);
|
||
// 添加矢量属性查询事件监听器
|
||
useEffect(() => {
|
||
if (!activeTools.includes("info") || !map) return;
|
||
map.on("click", handleMapClickSelectFeatures);
|
||
|
||
return () => {
|
||
map.un("click", handleMapClickSelectFeatures);
|
||
};
|
||
}, [activeTools, map, handleMapClickSelectFeatures]);
|
||
|
||
// 处理工具栏按钮点击事件
|
||
const handleToolClick = (tool: string) => {
|
||
// 样式工具的特殊处理 - 只有再次点击时才会取消激活和关闭
|
||
if (tool === "style") {
|
||
if (activeTools.includes("style")) {
|
||
// 如果样式工具已激活,点击时关闭
|
||
setShowStyleEditor(false);
|
||
setActiveTools((prev) => prev.filter((t) => t !== "style"));
|
||
} else {
|
||
// 激活样式工具,打开样式面板
|
||
setActiveTools((prev) => [...prev, "style"]);
|
||
setShowStyleEditor(true);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 其他工具的处理逻辑
|
||
if (activeTools.includes(tool)) {
|
||
// 如果当前工具已激活,再次点击时取消激活并关闭面板
|
||
deactivateTool(tool);
|
||
setActiveTools((prev) => prev.filter((t) => t !== tool));
|
||
} else {
|
||
// 如果当前工具未激活,先关闭所有其他工具,然后激活当前工具
|
||
// 关闭所有面板(但保持样式编辑器状态)
|
||
closeAllPanelsExceptStyle();
|
||
|
||
// 取消激活所有非样式工具
|
||
setActiveTools((prev) => {
|
||
const styleActive = prev.includes("style");
|
||
return styleActive ? ["style", tool] : [tool];
|
||
});
|
||
|
||
// 激活当前工具并打开对应面板
|
||
activateTool(tool);
|
||
}
|
||
};
|
||
|
||
// 取消激活指定工具并关闭对应面板
|
||
const deactivateTool = (tool: string) => {
|
||
switch (tool) {
|
||
case "info":
|
||
setShowPropertyPanel(false);
|
||
setHighlightFeature(null);
|
||
break;
|
||
case "draw":
|
||
setShowDrawPanel(false);
|
||
break;
|
||
case "history":
|
||
// 取消历史查询激活时的清理(目前不保留额外状态)
|
||
break;
|
||
}
|
||
};
|
||
|
||
// 激活指定工具并打开对应面板
|
||
const activateTool = (tool: string) => {
|
||
switch (tool) {
|
||
case "info":
|
||
setShowPropertyPanel(true);
|
||
break;
|
||
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);
|
||
}
|
||
};
|
||
|
||
// 关闭所有面板(除了样式编辑器)
|
||
const closeAllPanelsExceptStyle = () => {
|
||
setShowPropertyPanel(false);
|
||
setHighlightFeature(null);
|
||
setShowDrawPanel(false);
|
||
// 样式编辑器保持其当前状态,不自动关闭
|
||
};
|
||
const [computedProperties, setComputedProperties] = useState<
|
||
Record<string, any>
|
||
>({});
|
||
// 添加 useEffect 来查询计算属性
|
||
useEffect(() => {
|
||
if (!highlightFeature || !selectedDate) {
|
||
setComputedProperties({});
|
||
return;
|
||
}
|
||
|
||
const id = highlightFeature.getProperties().id;
|
||
if (!id) {
|
||
setComputedProperties({});
|
||
return;
|
||
}
|
||
|
||
const queryComputedProperties = async () => {
|
||
try {
|
||
const properties = highlightFeature?.getProperties?.() || {};
|
||
const type =
|
||
properties.geometry?.getType?.() === "LineString" ? "link" : "node";
|
||
// selectedDate 格式化为 YYYY-MM-DD
|
||
let dateObj: Date;
|
||
if (selectedDate instanceof Date) {
|
||
dateObj = new Date(selectedDate);
|
||
} else {
|
||
dateObj = new Date(selectedDate);
|
||
}
|
||
const minutes = Number(currentTime) || 0;
|
||
dateObj.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0);
|
||
// 转为 UTC ISO 字符串
|
||
const querytime = dateObj.toISOString(); // 例如 "2025-09-16T16:30:00.000Z"
|
||
let response;
|
||
if (queryType === "scheme") {
|
||
response = await fetch(
|
||
`${backendUrl}/queryschemesimulationrecordsbyidtime/?scheme_name=${schemeName}&id=${id}&querytime=${querytime}&type=${type}`
|
||
);
|
||
} else {
|
||
response = await fetch(
|
||
`${backendUrl}/querysimulationrecordsbyidtime/?id=${id}&querytime=${querytime}&type=${type}`
|
||
);
|
||
}
|
||
if (!response.ok) {
|
||
throw new Error("API request failed");
|
||
}
|
||
const data = await response.json();
|
||
setComputedProperties(data.results[0] || {});
|
||
} catch (error) {
|
||
console.error("Error querying computed properties:", error);
|
||
setComputedProperties({});
|
||
}
|
||
};
|
||
// 仅当 currentTime 有效时查询
|
||
if (currentTime !== -1 && queryType) queryComputedProperties();
|
||
}, [highlightFeature, currentTime, selectedDate]);
|
||
|
||
// 从要素属性中提取属性面板需要的数据
|
||
const getFeatureProperties = useCallback(() => {
|
||
if (!highlightFeature) return {};
|
||
const layer = highlightFeature?.getId()?.toString().split(".")[0];
|
||
const properties = highlightFeature.getProperties();
|
||
// 计算属性字段,增加 key 字段
|
||
const pipeComputedFields = [
|
||
{ key: "flow", label: "流量", unit: "m³/s" },
|
||
{ key: "friction", label: "摩阻", unit: "" },
|
||
{ key: "headloss", label: "水头损失", unit: "m" },
|
||
{ key: "quality", label: "水质", unit: "mg/L" },
|
||
{ key: "reaction", label: "反应", unit: "1/s" },
|
||
{ key: "setting", label: "设置", unit: "" },
|
||
{ key: "status", label: "状态", unit: "" },
|
||
{ key: "velocity", label: "流速", unit: "m/s" },
|
||
];
|
||
const nodeComputedFields = [
|
||
{ key: "actualdemand", label: "实际需水量", unit: "m³/s" },
|
||
{ key: "head", label: "水头", unit: "m" },
|
||
{ key: "pressure", label: "压力", unit: "m" },
|
||
{ key: "quality", label: "水质", unit: "mg/L" },
|
||
];
|
||
|
||
if (layer === "geo_pipes_mat" || layer === "geo_pipes") {
|
||
let result = {
|
||
id: properties.id,
|
||
type: "管道",
|
||
properties: [
|
||
{ label: "起始节点ID", value: properties.node1 },
|
||
{ label: "终点节点ID", value: properties.node2 },
|
||
{ label: "长度", value: properties.length?.toFixed?.(1), unit: "m" },
|
||
{
|
||
label: "管径",
|
||
value: properties.diameter?.toFixed?.(1),
|
||
unit: "mm",
|
||
},
|
||
{ label: "粗糙度", value: properties.roughness },
|
||
{ label: "局部损失", value: properties.minor_loss },
|
||
{ label: "初始状态", value: "开" },
|
||
],
|
||
};
|
||
// 追加计算属性
|
||
if (computedProperties) {
|
||
pipeComputedFields.forEach(({ key, label, unit }) => {
|
||
if (computedProperties[key] !== undefined) {
|
||
result.properties.push({
|
||
label,
|
||
value:
|
||
computedProperties[key].toFixed?.(3) || computedProperties[key],
|
||
unit,
|
||
});
|
||
}
|
||
});
|
||
}
|
||
return result;
|
||
}
|
||
if (layer === "geo_junctions_mat" || layer === "geo_junctions") {
|
||
let result = {
|
||
id: properties.id,
|
||
type: "节点",
|
||
properties: [
|
||
{
|
||
label: "海拔",
|
||
value: properties.elevation?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
// 将 demand1~demand5 与 pattern1~pattern5 作为二级表格展示
|
||
{
|
||
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,
|
||
],
|
||
};
|
||
// 追加计算属性
|
||
if (computedProperties) {
|
||
nodeComputedFields.forEach(({ key, label, unit }) => {
|
||
if (computedProperties[key] !== undefined) {
|
||
result.properties.push({
|
||
label,
|
||
value:
|
||
computedProperties[key].toFixed?.(3) || computedProperties[key],
|
||
unit,
|
||
});
|
||
}
|
||
});
|
||
}
|
||
return result;
|
||
}
|
||
if (layer === "geo_tanks_mat" || layer === "geo_tanks") {
|
||
return {
|
||
id: properties.id,
|
||
type: "水池",
|
||
properties: [
|
||
{
|
||
label: "海拔",
|
||
value: properties.elevation?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
{
|
||
label: "初始水位",
|
||
value: properties.init_level?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
{
|
||
label: "最低水位",
|
||
value: properties.min_level?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
{
|
||
label: "最高水位",
|
||
value: properties.max_level?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
{
|
||
label: "直径",
|
||
value: properties.diameter?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
{
|
||
label: "最小容积",
|
||
value: properties.min_vol?.toFixed?.(1),
|
||
unit: "m³",
|
||
},
|
||
// {
|
||
// label: "容积曲线",
|
||
// value: properties.vol_curve,
|
||
// },
|
||
{
|
||
label: "溢出",
|
||
value: properties.overflow ? "是" : "否",
|
||
},
|
||
],
|
||
};
|
||
}
|
||
if (layer === "geo_reservoirs_mat" || layer === "geo_reservoirs") {
|
||
return {
|
||
id: properties.id,
|
||
type: "水库",
|
||
properties: [
|
||
{
|
||
label: "水头",
|
||
value: properties.head?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
// {
|
||
// label: "模式",
|
||
// value: properties.pattern,
|
||
// },
|
||
],
|
||
};
|
||
}
|
||
if (layer === "geo_pumps_mat" || layer === "geo_pumps") {
|
||
return {
|
||
id: properties.id,
|
||
type: "水泵",
|
||
properties: [
|
||
{ label: "起始节点 ID", value: properties.node1 },
|
||
{ label: "终点节点 ID", value: properties.node2 },
|
||
{
|
||
label: "功率",
|
||
value: properties.power?.toFixed?.(1),
|
||
unit: "kW",
|
||
},
|
||
{
|
||
label: "扬程",
|
||
value: properties.head?.toFixed?.(1),
|
||
unit: "m",
|
||
},
|
||
{
|
||
label: "转速",
|
||
value: properties.speed?.toFixed?.(1),
|
||
unit: "rpm",
|
||
},
|
||
{
|
||
label: "模式",
|
||
value: properties.pattern,
|
||
},
|
||
],
|
||
};
|
||
}
|
||
if (layer === "geo_valves_mat" || layer === "geo_valves") {
|
||
return {
|
||
id: properties.id,
|
||
type: "阀门",
|
||
properties: [
|
||
{ label: "起始节点 ID", value: properties.node1 },
|
||
{ label: "终点节点 ID", value: properties.node2 },
|
||
{
|
||
label: "直径",
|
||
value: properties.diameter?.toFixed?.(1),
|
||
unit: "mm",
|
||
},
|
||
{
|
||
label: "阀门类型",
|
||
value: properties.v_type,
|
||
},
|
||
// {
|
||
// label: "设置",
|
||
// value: properties.setting?.toFixed?.(2),
|
||
// },
|
||
{
|
||
label: "局部损失",
|
||
value: properties.minor_loss?.toFixed?.(2),
|
||
},
|
||
],
|
||
};
|
||
}
|
||
// 传输频率文字对应
|
||
const getTransmissionFrequency = (transmission_frequency: string) => {
|
||
// 传输频率文本:00:01:00,00:05:00,00:10:00,00:30:00,01: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 getReliability = (reliability: number) => {
|
||
switch (reliability) {
|
||
case 1:
|
||
return "高";
|
||
case 2:
|
||
return "中";
|
||
case 3:
|
||
return "低";
|
||
default:
|
||
return "未知";
|
||
}
|
||
};
|
||
if (layer === "geo_scada_mat" || layer === "geo_scada") {
|
||
let result = {
|
||
id: properties.id,
|
||
type: "SCADA设备",
|
||
properties: [
|
||
{
|
||
label: "类型",
|
||
value:
|
||
properties.type === "pipe_flow" ? "流量传感器" : "压力传感器",
|
||
},
|
||
{
|
||
label: "关联节点 ID",
|
||
value: properties.associated_element_id,
|
||
},
|
||
{
|
||
label: "传输模式",
|
||
value:
|
||
properties.transmission_mode === "non_realtime"
|
||
? "定时传输"
|
||
: "实时传输",
|
||
},
|
||
{
|
||
label: "传输频率",
|
||
value: getTransmissionFrequency(properties.transmission_frequency),
|
||
unit: "分钟",
|
||
},
|
||
{
|
||
label: "可靠性",
|
||
value: getReliability(properties.reliability),
|
||
},
|
||
],
|
||
};
|
||
return result;
|
||
}
|
||
return {};
|
||
}, [highlightFeature, computedProperties]);
|
||
|
||
return (
|
||
<>
|
||
<div className="absolute top-4 left-4 bg-white p-1 rounded-xl shadow-lg flex opacity-85 hover:opacity-100 transition-opacity">
|
||
{!hiddenButtons?.includes("info") && (
|
||
<ToolbarButton
|
||
icon={<InfoOutlinedIcon />}
|
||
name="查看属性"
|
||
isActive={activeTools.includes("info")}
|
||
onClick={() => handleToolClick("info")}
|
||
/>
|
||
)}
|
||
{!hiddenButtons?.includes("history") && (
|
||
<ToolbarButton
|
||
icon={<QueryStatsOutlinedIcon />}
|
||
name="查询历史数据"
|
||
isActive={activeTools.includes("history")}
|
||
onClick={() => handleToolClick("history")}
|
||
/>
|
||
)}
|
||
{!hiddenButtons?.includes("draw") && (
|
||
<ToolbarButton
|
||
icon={<EditOutlinedIcon />}
|
||
name="标记绘制"
|
||
isActive={activeTools.includes("draw")}
|
||
onClick={() => handleToolClick("draw")}
|
||
/>
|
||
)}
|
||
{!hiddenButtons?.includes("style") && (
|
||
<ToolbarButton
|
||
icon={<PaletteOutlinedIcon />}
|
||
name="图层样式"
|
||
isActive={activeTools.includes("style")}
|
||
onClick={() => handleToolClick("style")}
|
||
/>
|
||
)}
|
||
</div>
|
||
{showPropertyPanel && <PropertyPanel {...getFeatureProperties()} />}
|
||
{showDrawPanel && map && <DrawPanel />}
|
||
{showStyleEditor && (
|
||
<StyleEditorPanel
|
||
layerStyleStates={layerStyleStates}
|
||
setLayerStyleStates={setLayerStyleStates}
|
||
/>
|
||
)}
|
||
|
||
{/* 图例显示 */}
|
||
{activeLegendConfigs.length > 0 && (
|
||
<div className="absolute bottom-40 right-4 drop-shadow-xl flex flex-row items-end max-w-screen-lg overflow-x-auto z-10">
|
||
<div className="flex flex-row gap-3">
|
||
{activeLegendConfigs.map((config, index) => (
|
||
<StyleLegend key={`${config.layerId}-${index}`} {...config} />
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default Toolbar;
|