|
@@ -201,22 +201,23 @@ function renderGraph() {
|
|
|
svg.attr('viewBox', `0 0 ${width} ${height}`)
|
|
svg.attr('viewBox', `0 0 ${width} ${height}`)
|
|
|
|
|
|
|
|
// 中心节点(选中节点,或边的第一个端点)
|
|
// 中心节点(选中节点,或边的第一个端点)
|
|
|
- const centerNodeId = store.selectedNodeId || Array.from(store.highlightedNodeIds)[0]
|
|
|
|
|
|
|
+ const centerNodeId = store.selectedNodeId || Array.from(store.mergedHighlightNodes)[0]
|
|
|
|
|
|
|
|
- // 准备节点和边数据
|
|
|
|
|
|
|
+ // 准备节点和边数据(使用 store 合并后的高亮状态)
|
|
|
const nodes = []
|
|
const nodes = []
|
|
|
const links = []
|
|
const links = []
|
|
|
const nodeSet = new Set()
|
|
const nodeSet = new Set()
|
|
|
|
|
+ const edgeSet = new Set()
|
|
|
|
|
|
|
|
- // 显示所有高亮节点
|
|
|
|
|
- for (const nodeId of store.highlightedNodeIds) {
|
|
|
|
|
|
|
+ // 显示所有合并后的高亮节点
|
|
|
|
|
+ for (const nodeId of store.mergedHighlightNodes) {
|
|
|
const nodeData = store.getNode(nodeId)
|
|
const nodeData = store.getNode(nodeId)
|
|
|
- if (nodeData) {
|
|
|
|
|
|
|
+ if (nodeData && !nodeSet.has(nodeId)) {
|
|
|
nodes.push({
|
|
nodes.push({
|
|
|
id: nodeId,
|
|
id: nodeId,
|
|
|
...nodeData,
|
|
...nodeData,
|
|
|
isCenter: nodeId === centerNodeId,
|
|
isCenter: nodeId === centerNodeId,
|
|
|
- isHighlighted: store.highlightedNodeIds.size > 1
|
|
|
|
|
|
|
+ isHighlighted: true
|
|
|
})
|
|
})
|
|
|
nodeSet.add(nodeId)
|
|
nodeSet.add(nodeId)
|
|
|
}
|
|
}
|
|
@@ -225,12 +226,14 @@ function renderGraph() {
|
|
|
// 如果没有节点,不渲染
|
|
// 如果没有节点,不渲染
|
|
|
if (nodes.length === 0) return
|
|
if (nodes.length === 0) return
|
|
|
|
|
|
|
|
- // 使用游走时记录的边(只显示两端节点都存在的边)
|
|
|
|
|
- // 优先使用 postWalkedEdges(帖子游走),否则用 walkedEdges(人设游走)
|
|
|
|
|
- const edges = store.postWalkedEdges.length > 0 ? store.postWalkedEdges : store.walkedEdges
|
|
|
|
|
- for (const edge of edges) {
|
|
|
|
|
|
|
+ // 使用合并后的高亮边(只显示两端节点都存在的边)
|
|
|
|
|
+ for (const edge of store.mergedHighlightEdges) {
|
|
|
if (nodeSet.has(edge.source) && nodeSet.has(edge.target)) {
|
|
if (nodeSet.has(edge.source) && nodeSet.has(edge.target)) {
|
|
|
- links.push({ ...edge })
|
|
|
|
|
|
|
+ const edgeKey = `${edge.source}->${edge.target}`
|
|
|
|
|
+ if (!edgeSet.has(edgeKey)) {
|
|
|
|
|
+ links.push({ ...edge })
|
|
|
|
|
+ edgeSet.add(edgeKey)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -530,10 +533,14 @@ function handleSvgClick(event) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 统一高亮更新
|
|
|
|
|
|
|
+// 统一高亮更新(使用合并后的高亮状态)
|
|
|
function updateHighlight() {
|
|
function updateHighlight() {
|
|
|
- const edgeSet = store.walkedEdgeSet.size > 0 ? store.walkedEdgeSet : store.postWalkedEdgeSet
|
|
|
|
|
- applyHighlight(svgRef.value, store.highlightedNodeIds, edgeSet, store.selectedNodeId)
|
|
|
|
|
|
|
+ // 构建合并后的边集合
|
|
|
|
|
+ const mergedEdgeSet = new Set()
|
|
|
|
|
+ for (const edge of store.mergedHighlightEdges) {
|
|
|
|
|
+ mergedEdgeSet.add(`${edge.source}->${edge.target}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ applyHighlight(svgRef.value, store.mergedHighlightNodes, mergedEdgeSet, store.selectedNodeId)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 恢复锁定的 hover 状态(重新渲染后调用)
|
|
// 恢复锁定的 hover 状态(重新渲染后调用)
|
|
@@ -568,35 +575,53 @@ watch(() => store.selectedEdgeId, () => {
|
|
|
// 监听高亮节点集合变化
|
|
// 监听高亮节点集合变化
|
|
|
watch(() => store.highlightedNodeIds.size, updateHighlight)
|
|
watch(() => store.highlightedNodeIds.size, updateHighlight)
|
|
|
|
|
|
|
|
|
|
+// 监听合并高亮变化(各图高亮变化时自动重新渲染)
|
|
|
|
|
+watch([() => store.mergedHighlightNodes.size, () => store.mergedHighlightEdges.length], () => {
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ renderGraph()
|
|
|
|
|
+ // 渲染后缩放到所有高亮节点
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ if (store.mergedHighlightNodes.size > 0) {
|
|
|
|
|
+ zoomToPathNodes(store.mergedHighlightNodes)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
// 监听 hover 状态变化(用于左右联动)
|
|
// 监听 hover 状态变化(用于左右联动)
|
|
|
watch([() => store.hoverPathNodes.size, () => store.hoverNodeId], () => {
|
|
watch([() => store.hoverPathNodes.size, () => store.hoverNodeId], () => {
|
|
|
if (!graphNodeSelection || !graphLinkSelection) return
|
|
if (!graphNodeSelection || !graphLinkSelection) return
|
|
|
|
|
|
|
|
- if (store.hoverPathNodes.size > 0) {
|
|
|
|
|
- // 应用 hover 高亮(支持嵌套:传入锁定路径和选中节点)
|
|
|
|
|
- const lockedPath = store.lockedHoverPathNodes.size > 0 ? store.lockedHoverPathNodes : null
|
|
|
|
|
- applyHoverHighlight(graphNodeSelection, graphLinkSelection, graphLinkLabelSelection, store.hoverPathNodes, lockedPath, store.selectedNodeId)
|
|
|
|
|
-
|
|
|
|
|
- // 如果是从 PostTreeView 触发的,缩放到显示完整路径,并显示锁定按钮
|
|
|
|
|
- if (store.hoverSource === 'post-tree') {
|
|
|
|
|
- zoomToPathNodes(store.hoverPathNodes)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 在对应节点上显示锁定按钮(无论来源)
|
|
|
|
|
- if (store.hoverNodeId) {
|
|
|
|
|
- graphNodeSelection.each(function(d) {
|
|
|
|
|
- if (d.id === store.hoverNodeId) {
|
|
|
|
|
- showLockButton(this)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 清除 hover,恢复原有高亮(包括选中节点的样式)
|
|
|
|
|
|
|
+ // hover 结束时,无论来源是什么,都恢复合并后的高亮
|
|
|
|
|
+ if (store.hoverPathNodes.size === 0) {
|
|
|
updateHighlight()
|
|
updateHighlight()
|
|
|
|
|
+ // 缩放回合并后的高亮节点
|
|
|
|
|
+ if (store.mergedHighlightNodes.size > 0) {
|
|
|
|
|
+ zoomToPathNodes(store.mergedHighlightNodes)
|
|
|
|
|
+ }
|
|
|
// 如果没有锁定,隐藏按钮
|
|
// 如果没有锁定,隐藏按钮
|
|
|
if (!store.lockedHoverNodeId) {
|
|
if (!store.lockedHoverNodeId) {
|
|
|
hideLockButton()
|
|
hideLockButton()
|
|
|
}
|
|
}
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 应用 hover 高亮(支持嵌套:传入锁定路径和选中节点)
|
|
|
|
|
+ const lockedPath = store.lockedHoverPathNodes.size > 0 ? store.lockedHoverPathNodes : null
|
|
|
|
|
+ applyHoverHighlight(graphNodeSelection, graphLinkSelection, graphLinkLabelSelection, store.hoverPathNodes, lockedPath, store.selectedNodeId)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是从其他视图触发的 hover,缩放到显示完整路径
|
|
|
|
|
+ if (store.hoverSource === 'post-tree' || store.hoverSource === 'derivation') {
|
|
|
|
|
+ zoomToPathNodes(store.hoverPathNodes)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 在对应节点上显示锁定按钮(无论来源)
|
|
|
|
|
+ if (store.hoverNodeId) {
|
|
|
|
|
+ graphNodeSelection.each(function(d) {
|
|
|
|
|
+ if (d.id === store.hoverNodeId) {
|
|
|
|
|
+ showLockButton(this)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|