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.
This commit is contained in:
parent
8565f04d22
commit
a90b683a44
2 changed files with 146 additions and 7 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
<!-- 节点/边详情面板 -->
|
||||
<div v-if="selectedItem" class="detail-panel">
|
||||
<div class="detail-panel-header">
|
||||
<span class="detail-title">{{ selectedItem.type === 'node' ? '节点详情' : '关系详情' }}</span>
|
||||
<span class="detail-title">{{ selectedItem.type === 'node' ? 'Node Details' : 'Relationship' }}</span>
|
||||
<span v-if="selectedItem.type === 'node'" class="detail-badge" :style="{ background: selectedItem.color }">
|
||||
{{ selectedItem.entityType }}
|
||||
</span>
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
<div v-if="selectedItem.type === 'node'" class="detail-content">
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">Name:</span>
|
||||
<span class="detail-value">{{ selectedItem.data.name }}</span>
|
||||
<span class="detail-value highlight">{{ selectedItem.data.name }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">UUID:</span>
|
||||
|
|
@ -69,10 +69,25 @@
|
|||
<span class="detail-label">Created:</span>
|
||||
<span class="detail-value">{{ formatDate(selectedItem.data.created_at) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Properties / Attributes -->
|
||||
<div class="detail-section" v-if="selectedItem.data.attributes && Object.keys(selectedItem.data.attributes).length > 0">
|
||||
<span class="detail-label">Properties:</span>
|
||||
<div class="properties-list">
|
||||
<div v-for="(value, key) in selectedItem.data.attributes" :key="key" class="property-item">
|
||||
<span class="property-key">{{ key }}:</span>
|
||||
<span class="property-value">{{ value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary -->
|
||||
<div class="detail-section" v-if="selectedItem.data.summary">
|
||||
<span class="detail-label">Summary:</span>
|
||||
<p class="detail-summary">{{ selectedItem.data.summary }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Labels -->
|
||||
<div class="detail-row" v-if="selectedItem.data.labels?.length">
|
||||
<span class="detail-label">Labels:</span>
|
||||
<div class="detail-labels">
|
||||
|
|
@ -83,13 +98,17 @@
|
|||
|
||||
<!-- 边详情 -->
|
||||
<div v-else class="detail-content">
|
||||
<!-- 关系展示 -->
|
||||
<div class="edge-relation">
|
||||
<span class="edge-source">{{ selectedItem.data.source_name }}</span>
|
||||
<span class="edge-source">{{ selectedItem.data.source_name || selectedItem.data.source_node_name }}</span>
|
||||
<span class="edge-arrow">→</span>
|
||||
<span class="edge-type">{{ selectedItem.data.name || selectedItem.data.fact_type }}</span>
|
||||
<span class="edge-type">{{ selectedItem.data.name || selectedItem.data.fact_type || 'RELATED_TO' }}</span>
|
||||
<span class="edge-arrow">→</span>
|
||||
<span class="edge-target">{{ selectedItem.data.target_name }}</span>
|
||||
<span class="edge-target">{{ selectedItem.data.target_name || selectedItem.data.target_node_name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-subtitle">Relationship</div>
|
||||
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">UUID:</span>
|
||||
<span class="detail-value uuid">{{ selectedItem.data.uuid }}</span>
|
||||
|
|
@ -98,10 +117,25 @@
|
|||
<span class="detail-label">Label:</span>
|
||||
<span class="detail-value">{{ selectedItem.data.name || selectedItem.data.fact_type || 'RELATED_TO' }}</span>
|
||||
</div>
|
||||
<div class="detail-row" v-if="selectedItem.data.fact_type">
|
||||
<span class="detail-label">Type:</span>
|
||||
<span class="detail-value">{{ selectedItem.data.fact_type }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Fact -->
|
||||
<div class="detail-section" v-if="selectedItem.data.fact">
|
||||
<span class="detail-label">Fact:</span>
|
||||
<p class="detail-summary">{{ selectedItem.data.fact }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Episodes -->
|
||||
<div class="detail-section" v-if="selectedItem.data.episodes?.length">
|
||||
<span class="detail-label">Episodes:</span>
|
||||
<div class="episodes-list">
|
||||
<span v-for="ep in selectedItem.data.episodes" :key="ep" class="episode-tag">{{ ep }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-row" v-if="selectedItem.data.created_at">
|
||||
<span class="detail-label">Created:</span>
|
||||
<span class="detail-value">{{ formatDate(selectedItem.data.created_at) }}</span>
|
||||
|
|
@ -110,6 +144,14 @@
|
|||
<span class="detail-label">Valid From:</span>
|
||||
<span class="detail-value">{{ formatDate(selectedItem.data.valid_at) }}</span>
|
||||
</div>
|
||||
<div class="detail-row" v-if="selectedItem.data.invalid_at">
|
||||
<span class="detail-label">Invalid At:</span>
|
||||
<span class="detail-value">{{ formatDate(selectedItem.data.invalid_at) }}</span>
|
||||
</div>
|
||||
<div class="detail-row" v-if="selectedItem.data.expired_at">
|
||||
<span class="detail-label">Expired At:</span>
|
||||
<span class="detail-value">{{ formatDate(selectedItem.data.expired_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue