From abd124d630f1013544e69f15454e474e54e268cb Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 10 Aug 2026 16:54:57 +0800 Subject: [PATCH] =?UTF-8?q?node=20=E8=87=AA=E9=80=82=E5=BA=94=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Adminive/DataTopology.tsx | 46 +++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Adminive/DataTopology.tsx b/src/Adminive/DataTopology.tsx index e12d3ef..ea4b17b 100644 --- a/src/Adminive/DataTopology.tsx +++ b/src/Adminive/DataTopology.tsx @@ -451,8 +451,7 @@ function create_flow_nodes(data: Topology_Data, view: Topology_View, layout: Rec positions.set(node.id, {x: source.x + index * 270, y: source.y + node_size(source_node)[1] + 92}) } return data.nodes.map(node => { - const [width] = node_size(node) - return {id: node.id, type: "topology", position: positions.get(node.id)!, draggable: true, selectable: true, sourcePosition: Position.Bottom, targetPosition: Position.Top, style: {width}, data: {topology_node: node, kind: kinds.get(node.kind), status, status_expansion}} + return {id: node.id, type: "topology", position: positions.get(node.id)!, draggable: true, selectable: true, sourcePosition: Position.Bottom, targetPosition: Position.Top, data: {topology_node: node, kind: kinds.get(node.kind), status, status_expansion}} }) } function create_flow_edges(data: Topology_Data, schema: Data_Topology_Schema, status_expansion: MutableRefObject>, on_context_menu: (event: React_Mouse_Event, edge_id: string) => void): Flow_Edge[] { @@ -513,22 +512,47 @@ function status_children(value: unknown): [string, unknown][] { return value.map((item, index) => [String(index), item]) return value !== null && typeof value === "object" ? Object.entries(value as Record) : [] } +function status_text_width(value: string): number { + return Array.from(value).reduce((width, character) => width + (character.charCodeAt(0) > 255 ? 11 : 6.4), 0) +} +function status_tree_width(value: Record, minimum: number): number { + let required = minimum + const visit = (name: string, child: unknown, depth: number) => { + const children = status_children(child) + const display = children.length > 0 ? `${Array.isArray(child) ? "[" : "{"}${children.length}${Array.isArray(child) ? "]" : "}"}` : status_text(child) + required = Math.max(required, depth * 20 + 20 + status_text_width(name) + 18 + status_text_width(display) + 16) + for(const [key, nested] of children) + visit(key, nested, depth + 1) + } + for(const [name, child] of Object.entries(value)) + visit(name, child, 0) + return Math.min(920, Math.ceil(required)) +} +function use_maximum_status_width(value: Record, minimum: number): number { + const required = useMemo(() => status_tree_width(value, minimum), [minimum, value]) + const [maximum, set_maximum] = useState(required) + useEffect(() => { + if(required > maximum) + set_maximum(required) + }, [maximum, required]) + return Math.max(maximum, required) +} function Status_Tree_Node({name, value, depth}: {name: string, value: unknown, depth: number}) { const children = status_children(value) const branch = children.length > 0 const [expanded, set_expanded] = useState(depth === 0) - return
-
+ return
+
{branch ?
{branch && expanded &&
{children.map(([key, child]) => )}
}
} function Status_Tree({value}: {value: Record}) { const entries = Object.entries(value) - return entries.length > 0 ?
{entries.map(([key, child]) => )}
: 暂无状态 + return entries.length > 0 ?
{entries.map(([key, child]) => )}
: 暂无状态 } function use_polling_status(api: string | undefined, expanded: boolean, poll_interval_ms: number) { const [status, set_status] = useState | null>(null) @@ -583,9 +607,10 @@ function Node_Status({node, config, expansion}: {node: Topology_Node, config: To const [expanded, set_expanded] = use_status_expansion(`node:${node.id}`, expansion) const {status, loading, error, updated_at} = use_polling_status(node.status_api, expanded, config.poll_interval_ms) const current_status = node.status_api ? status ?? {} : Object.fromEntries(node.details.map(detail => [detail.label, detail.value])) + const status_width = use_maximum_status_width(current_status, node_size(node)[0] - 28) return
event.stopPropagation()} onClick={event => event.stopPropagation()} style={{marginTop: 7, borderTop: "1px solid rgba(0,0,0,.08)", paddingTop: 5}}>
{expanded && }
- {expanded &&
+ {expanded &&
{loading &&
} {error && {error}} {!loading && !error && } @@ -599,11 +624,12 @@ function Topology_Flow_Edge({id, sourceX, sourceY, targetX, targetY, sourcePosit const [expanded, set_expanded] = use_status_expansion(`edge:${edge.id}`, data!.status_expansion) const {status, loading, error, updated_at} = use_polling_status(edge.status_api, expanded, data!.status.poll_interval_ms) const current_status = edge.status_api ? status ?? {} : Object.fromEntries((edge.details ?? []).map(detail => [detail.label, detail.value])) + const status_width = use_maximum_status_width(current_status, 280) return <>
data!.on_context_menu(event)} style={{position: "absolute", transform: `translate(-50%, -50%) translate(${label_x}px, ${label_y}px)`, pointerEvents: "all", zIndex: expanded ? 8 : 2}}> {!expanded &&
{loading &&
} {error && {error}} @@ -618,7 +644,7 @@ function Topology_Flow_Node({data}: NodeProps) { const [width] = node_size(node) return <> {node.kind !== "data_source" && } -
+
}/>