Преглед изворни кода

fix: 优化节点详情展示和入出边查询

- build_persona_graph: 移除节点的sources字段
- PostTreeView: 隐藏children/id等不需要显示的字段
- PostTreeView: 匹配层人设节点获取完整节点数据(包含detail)
- PostTreeView: 入边/出边同时查询帖子图谱和人设图谱

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
yangxiaohui пре 14 часа
родитељ
комит
16af8c2e3b

+ 6 - 26
script/data_processing/build_persona_graph.py

@@ -207,17 +207,6 @@ def extract_category_nodes_from_pattern(
             if isinstance(value, dict):
                 current_path = parent_path + [key]
 
-                # 构建节点来源(只收集当前节点的特征)
-                node_sources = []
-                if "特征列表" in value:
-                    for feature in value["特征列表"]:
-                        source = {
-                            "pointName": feature.get("所属点", ""),
-                            "pointDesc": feature.get("点描述", ""),
-                            "postId": feature.get("帖子id", "")
-                        }
-                        node_sources.append(source)
-
                 # 收集帖子ID列表(递归收集当前节点及所有子节点的帖子ID,去重)
                 all_sources = collect_sources_recursively(value)
                 unique_post_ids = list(set(s.get("postId", "") for s in all_sources if s.get("postId")))
@@ -232,8 +221,7 @@ def extract_category_nodes_from_pattern(
                     detail={
                         "parentPath": parent_path.copy(),
                         "postIds": unique_post_ids,
-                        "postCount": len(unique_post_ids),
-                        "sources": node_sources
+                        "postCount": len(unique_post_ids)
                     }
                 )
 
@@ -258,7 +246,7 @@ def extract_tag_nodes_from_pattern(
         { nodeId: nodeData }
     """
     nodes = {}
-    tag_map = {}  # 用于合并同名标签: tagId -> { sources, postIds, parentPath }
+    tag_map = {}  # 用于合并同名标签: tagId -> { postIds, parentPath }
 
     if dimension_key not in pattern_data:
         return nodes
@@ -272,25 +260,18 @@ def extract_tag_nodes_from_pattern(
                 if not tag_name:
                     continue
 
-                source = {
-                    "pointName": feature.get("所属点", ""),
-                    "pointDesc": feature.get("点描述", ""),
-                    "postId": feature.get("帖子id", "")
-                }
-
+                post_id = feature.get("帖子id", "")
                 tag_id = build_node_id("人设", dimension_name, "标签", tag_name)
 
                 if tag_id not in tag_map:
                     tag_map[tag_id] = {
                         "name": tag_name,
-                        "sources": [],
                         "postIds": set(),
                         "parentPath": parent_path.copy()
                     }
 
-                tag_map[tag_id]["sources"].append(source)
-                if source["postId"]:
-                    tag_map[tag_id]["postIds"].add(source["postId"])
+                if post_id:
+                    tag_map[tag_id]["postIds"].add(post_id)
 
         # 递归处理子节点
         for key, value in node.items():
@@ -313,8 +294,7 @@ def extract_tag_nodes_from_pattern(
             detail={
                 "parentPath": tag_info["parentPath"],
                 "postIds": list(tag_info["postIds"]),
-                "postCount": len(tag_info["postIds"]),
-                "sources": tag_info["sources"]
+                "postCount": len(tag_info["postIds"])
             }
         )
 

+ 66 - 26
script/visualization/src/components/PostTreeView.vue

@@ -373,8 +373,8 @@ const props = defineProps({
 
 const emit = defineEmits(['update:matchListCollapsed'])
 
-// 不需要显示的节点字段
-const hiddenNodeFields = ['index', 'x', 'y', 'vx', 'vy', 'fx', 'fy']
+// 不需要显示的节点字段(D3相关字段和children)
+const hiddenNodeFields = ['index', 'x', 'y', 'vx', 'vy', 'fx', 'fy', 'children', 'id', 'data', 'parent', 'depth', 'height']
 
 const store = useGraphStore()
 
@@ -562,32 +562,62 @@ function getNodeColor(node) {
   return '#888'
 }
 
-// 节点的入边列表(按分数降序)
+// 节点的入边列表(按分数降序,同时查找帖子图谱和人设图谱
 const nodeInEdges = computed(() => {
   if (!displayNode.value) return []
   const nodeId = displayNode.value.id || displayNode.value.data?.id
   if (!nodeId) return []
 
+  const edges = []
+
+  // 从帖子图谱查找
   const postGraph = store.currentPostGraph
-  if (!postGraph?.edges) return []
+  if (postGraph?.edges) {
+    for (const e of Object.values(postGraph.edges)) {
+      if (e.target === nodeId) edges.push(e)
+    }
+  }
 
-  return Object.values(postGraph.edges)
-    .filter(e => e.target === nodeId)
-    .sort((a, b) => (b.score || 0) - (a.score || 0))
+  // 从人设图谱查找(如果是人设节点)
+  if (nodeId.startsWith('人设:')) {
+    const personaEdges = store.graphData?.edges
+    if (personaEdges) {
+      for (const e of Object.values(personaEdges)) {
+        if (e.target === nodeId) edges.push(e)
+      }
+    }
+  }
+
+  return edges.sort((a, b) => (b.score || 0) - (a.score || 0))
 })
 
-// 节点的出边列表(按分数降序)
+// 节点的出边列表(按分数降序,同时查找帖子图谱和人设图谱
 const nodeOutEdges = computed(() => {
   if (!displayNode.value) return []
   const nodeId = displayNode.value.id || displayNode.value.data?.id
   if (!nodeId) return []
 
+  const edges = []
+
+  // 从帖子图谱查找
   const postGraph = store.currentPostGraph
-  if (!postGraph?.edges) return []
+  if (postGraph?.edges) {
+    for (const e of Object.values(postGraph.edges)) {
+      if (e.source === nodeId) edges.push(e)
+    }
+  }
+
+  // 从人设图谱查找(如果是人设节点)
+  if (nodeId.startsWith('人设:')) {
+    const personaEdges = store.graphData?.edges
+    if (personaEdges) {
+      for (const e of Object.values(personaEdges)) {
+        if (e.source === nodeId) edges.push(e)
+      }
+    }
+  }
 
-  return Object.values(postGraph.edges)
-    .filter(e => e.source === nodeId)
-    .sort((a, b) => (b.score || 0) - (a.score || 0))
+  return edges.sort((a, b) => (b.score || 0) - (a.score || 0))
 })
 
 // 获取节点名称(根据节点ID)
@@ -819,23 +849,33 @@ function renderMatchLayer(contentG, root, baseTreeHeight) {
 
   if (matchEdges.length === 0) return
 
-  // 收集匹配的人设节点(去重)
+  // 收集匹配的人设节点(去重),获取完整节点数据
   const matchedPersonaMap = new Map()
   for (const edge of matchEdges) {
     if (!matchedPersonaMap.has(edge.target)) {
-      // 从人设节点ID提取信息: "人设:目的点:标签:进行产品种草"
-      const parts = edge.target.split(':')
-      const name = parts[parts.length - 1]
-      const dimension = parts[1] // 灵感点/目的点/关键点
-      const type = parts[2] // 标签/分类/点
-      matchedPersonaMap.set(edge.target, {
-        id: edge.target,
-        name: name,
-        dimension: dimension,
-        type: type,
-        domain: '人设', // 人设节点:实心
-        sourceEdges: [] // 连接的帖子节点
-      })
+      // 从 store 获取完整的人设节点数据
+      const fullNode = store.getNode(edge.target)
+      if (fullNode) {
+        matchedPersonaMap.set(edge.target, {
+          ...fullNode,
+          id: edge.target,
+          sourceEdges: [] // 连接的帖子节点
+        })
+      } else {
+        // 回退:从节点ID提取基本信息
+        const parts = edge.target.split(':')
+        const name = parts[parts.length - 1]
+        const dimension = parts[1]
+        const type = parts[2]
+        matchedPersonaMap.set(edge.target, {
+          id: edge.target,
+          name: name,
+          dimension: dimension,
+          type: type,
+          domain: '人设',
+          sourceEdges: []
+        })
+      }
     }
     matchedPersonaMap.get(edge.target).sourceEdges.push({
       sourceId: edge.source,