From a90b683a441baae622d493c0c880fdaf47914c13 Mon Sep 17 00:00:00 2001 From: 666ghj <670939375@qq.com> Date: Wed, 10 Dec 2025 19:26:30 +0800 Subject: [PATCH] Enhance graph data retrieval and detail display in Process.vue and graph_builder.py - Updated the `get_graph_data` method in `graph_builder.py` to include additional attributes such as creation time, validity periods, and episodes for nodes and edges, improving the richness of the graph data. - Modified the detail panel in `Process.vue` to present new attributes, including properties, episodes, and timestamps, enhancing user interaction and data visibility. - Improved styling for the detail panel to better organize and present the comprehensive information for selected nodes and edges. --- backend/app/services/graph_builder.py | 39 ++++++++- frontend/src/views/Process.vue | 114 ++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/backend/app/services/graph_builder.py b/backend/app/services/graph_builder.py index 488476b..bfa8a2e 100644 --- a/backend/app/services/graph_builder.py +++ b/backend/app/services/graph_builder.py @@ -418,36 +418,71 @@ class GraphBuilderService: def get_graph_data(self, graph_id: str) -> Dict[str, Any]: """ - 获取完整图谱数据 + 获取完整图谱数据(包含详细信息) Args: graph_id: 图谱ID Returns: - 包含nodes和edges的字典 + 包含nodes和edges的字典,包括时间信息、属性等详细数据 """ nodes = self.client.graph.node.get_by_graph_id(graph_id=graph_id) edges = self.client.graph.edge.get_by_graph_id(graph_id=graph_id) + # 创建节点映射用于获取节点名称 + node_map = {} + for node in nodes: + node_map[node.uuid_] = node.name or "" + nodes_data = [] for node in nodes: + # 获取创建时间 + created_at = getattr(node, 'created_at', None) + if created_at: + created_at = str(created_at) + nodes_data.append({ "uuid": node.uuid_, "name": node.name, "labels": node.labels or [], "summary": node.summary or "", "attributes": node.attributes or {}, + "created_at": created_at, }) edges_data = [] for edge in edges: + # 获取时间信息 + created_at = getattr(edge, 'created_at', None) + valid_at = getattr(edge, 'valid_at', None) + invalid_at = getattr(edge, 'invalid_at', None) + expired_at = getattr(edge, 'expired_at', None) + + # 获取 episodes + episodes = getattr(edge, 'episodes', None) or getattr(edge, 'episode_ids', None) + if episodes and not isinstance(episodes, list): + episodes = [str(episodes)] + elif episodes: + episodes = [str(e) for e in episodes] + + # 获取 fact_type + fact_type = getattr(edge, 'fact_type', None) or edge.name or "" + edges_data.append({ "uuid": edge.uuid_, "name": edge.name or "", "fact": edge.fact or "", + "fact_type": fact_type, "source_node_uuid": edge.source_node_uuid, "target_node_uuid": edge.target_node_uuid, + "source_node_name": node_map.get(edge.source_node_uuid, ""), + "target_node_name": node_map.get(edge.target_node_uuid, ""), "attributes": edge.attributes or {}, + "created_at": str(created_at) if created_at else None, + "valid_at": str(valid_at) if valid_at else None, + "invalid_at": str(invalid_at) if invalid_at else None, + "expired_at": str(expired_at) if expired_at else None, + "episodes": episodes or [], }) return { diff --git a/frontend/src/views/Process.vue b/frontend/src/views/Process.vue index 5ee5673..4a10100 100644 --- a/frontend/src/views/Process.vue +++ b/frontend/src/views/Process.vue @@ -48,7 +48,7 @@
- {{ selectedItem.type === 'node' ? '节点详情' : '关系详情' }} + {{ selectedItem.type === 'node' ? 'Node Details' : 'Relationship' }} {{ selectedItem.entityType }} @@ -59,7 +59,7 @@
Name: - {{ selectedItem.data.name }} + {{ selectedItem.data.name }}
UUID: @@ -69,10 +69,25 @@ Created: {{ formatDate(selectedItem.data.created_at) }}
+ + +
+ Properties: +
+
+ {{ key }}: + {{ value }} +
+
+
+ +
Summary:

{{ selectedItem.data.summary }}

+ +
Labels:
@@ -83,13 +98,17 @@
+
- {{ selectedItem.data.source_name }} + {{ selectedItem.data.source_name || selectedItem.data.source_node_name }} - {{ selectedItem.data.name || selectedItem.data.fact_type }} + {{ selectedItem.data.name || selectedItem.data.fact_type || 'RELATED_TO' }} - {{ selectedItem.data.target_name }} + {{ selectedItem.data.target_name || selectedItem.data.target_node_name }}
+ +
Relationship
+
UUID: {{ selectedItem.data.uuid }} @@ -98,10 +117,25 @@ Label: {{ selectedItem.data.name || selectedItem.data.fact_type || 'RELATED_TO' }}
+
+ Type: + {{ selectedItem.data.fact_type }} +
+ +
Fact:

{{ selectedItem.data.fact }}

+ + +
+ Episodes: +
+ {{ ep }} +
+
+
Created: {{ formatDate(selectedItem.data.created_at) }} @@ -110,6 +144,14 @@ Valid From: {{ formatDate(selectedItem.data.valid_at) }}
+
+ Invalid At: + {{ formatDate(selectedItem.data.invalid_at) }} +
+
+ Expired At: + {{ formatDate(selectedItem.data.expired_at) }} +
@@ -1477,6 +1519,68 @@ onUnmounted(() => { color: #fff; } +.detail-value.highlight { + font-weight: 600; + color: #000; +} + +.detail-subtitle { + font-size: 0.9rem; + font-weight: 600; + color: #333; + margin: 16px 0 12px 0; + padding-bottom: 8px; + border-bottom: 1px solid #E0E0E0; +} + +/* Properties 属性列表 */ +.properties-list { + margin-top: 8px; + padding: 10px; + background: #F9F9F9; + border: 1px solid #E0E0E0; +} + +.property-item { + display: flex; + margin-bottom: 6px; + font-size: 0.85rem; +} + +.property-item:last-child { + margin-bottom: 0; +} + +.property-key { + color: #666; + margin-right: 8px; + font-family: 'JetBrains Mono', monospace; +} + +.property-value { + color: #333; + word-break: break-word; +} + +/* Episodes 列表 */ +.episodes-list { + margin-top: 8px; + display: flex; + flex-direction: column; + gap: 6px; +} + +.episode-tag { + display: block; + padding: 6px 10px; + font-size: 0.75rem; + font-family: 'JetBrains Mono', monospace; + background: #F0F0F0; + border: 1px solid #E0E0E0; + color: #666; + word-break: break-all; +} + .error-icon { font-size: 2rem; display: block;