diff --git a/src/assets/images/linkPrediction/title/analysis-title.png b/src/assets/images/linkPrediction/title/analysis-title.png
index 12b296a..383f320 100644
Binary files a/src/assets/images/linkPrediction/title/analysis-title.png and b/src/assets/images/linkPrediction/title/analysis-title.png differ
diff --git a/src/assets/images/linkPrediction/title/characters-hidden-interaction-analysis-title.png b/src/assets/images/linkPrediction/title/characters-hidden-interaction-analysis-title.png
new file mode 100644
index 0000000..e2cc7cd
Binary files /dev/null and b/src/assets/images/linkPrediction/title/characters-hidden-interaction-analysis-title.png differ
diff --git a/src/assets/images/linkPrediction/title/graph2-title.png b/src/assets/images/linkPrediction/title/graph2-title.png
new file mode 100644
index 0000000..800e8d0
Binary files /dev/null and b/src/assets/images/linkPrediction/title/graph2-title.png differ
diff --git a/src/assets/images/linkPrediction/title/graph3-title.png b/src/assets/images/linkPrediction/title/graph3-title.png
new file mode 100644
index 0000000..8fa2db5
Binary files /dev/null and b/src/assets/images/linkPrediction/title/graph3-title.png differ
diff --git a/src/assets/images/time-point.png b/src/assets/images/time-point.png
new file mode 100644
index 0000000..5e37dfd
Binary files /dev/null and b/src/assets/images/time-point.png differ
diff --git a/src/store/keyNodeStore2.js b/src/store/keyNodeStore2.js
index 4f59ebc..2b9e417 100644
--- a/src/store/keyNodeStore2.js
+++ b/src/store/keyNodeStore2.js
@@ -250,7 +250,7 @@ export const useKeyNodeStore2 = defineStore("keyNode2", () => {
const posts = ref([
{
id: 1,
- timestamp: "2023-10-07 15:08:27",
+ timestamp: "2023-10-07 08:08:27",
author: "President Biden Archived",
influence: 69304.0,
highlighted: false,
@@ -352,7 +352,7 @@ export const useKeyNodeStore2 = defineStore("keyNode2", () => {
},
{
id: 10,
- timestamp: "2023-10-15 00:00:00",
+ timestamp: "2023-10-14 15:00:00",
author: "Jackson Hinkle 🇺🇸",
influence: 22546,
highlighted: false,
@@ -468,7 +468,7 @@ export const useKeyNodeStore2 = defineStore("keyNode2", () => {
])
// 当前激活的时间点
- const activeTimePoint = ref(1)
+ const activeTimePoint = ref(0)
// 所有时间点数据
const timePoints = ref([])
const activeLeader = ref(null)
diff --git a/src/views/GroupEvolution/components/groupGraph.vue b/src/views/GroupEvolution/components/groupGraph.vue
index 05f50f9..708a5f8 100644
--- a/src/views/GroupEvolution/components/groupGraph.vue
+++ b/src/views/GroupEvolution/components/groupGraph.vue
@@ -119,6 +119,7 @@ const handlePointerDown = (time) => {
const registEvents = () => {
const simulation = graphVis.getSimulationLayout()
forceSimulator = simulation.forceSimulation()
+
//全局记录包裹层元素
const containerDom = document.getElementById("container")
graphVis.registEventListener("node", "mouseOver", function (event, node) {
@@ -166,10 +167,10 @@ const highLightAboutNodesOrLinks = (type) => {
graphVis.cancelAllSelected()
const { newNodes, newLinks } = graph.value
console.log(graphVis.nodes)
-
+ console.log("进来highLightAboutNodesOrLinks")
if (type == "nodes") {
//实现高亮节点逻辑
-
+ console.log("进来if")
graphVis.nodes.forEach((node) =>
newNodes.forEach((newNode) => {
if (node.id === newNode.name) {
@@ -194,14 +195,17 @@ const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
function handleLayoutSuccess() {
//处理四个关系图的差异函数
const handleGroupDiscoveryDiff = () => {
+ console.log("进来handleGroupDiscoveryDiff")
return
}
const handleGroupStructureDiff = () => {
+ console.log("进来handleGroupStructureDiff")
highLightAboutNodesOrLinks("links")
}
const handleGroupMemberDiff = () => {
+ console.log("进来handleGroupMemberDiff")
highLightAboutNodesOrLinks("nodes")
}
@@ -296,7 +300,7 @@ const clusterAnalyze = () => {
}
// 仅对“异常群体模块”生效:时间变化时强制重绘一次
-if (storeId === "anomalousGroup") {
+if (storeId == "anomalousGroup") {
watch(
() => props.store.currentUtc,
() => {
diff --git a/src/views/KeyNodeDiscern/bridgeCommunication/components/GraphPanel.vue b/src/views/KeyNodeDiscern/bridgeCommunication/components/GraphPanel.vue
index 63e91a7..bb66225 100644
--- a/src/views/KeyNodeDiscern/bridgeCommunication/components/GraphPanel.vue
+++ b/src/views/KeyNodeDiscern/bridgeCommunication/components/GraphPanel.vue
@@ -19,7 +19,7 @@
2023.10.07 00:00:00
-
2023.10.15 00:00:00
@@ -78,6 +76,148 @@ const bridgeGraphRef = ref(null)
const currentComponent = ref("BridgeCommunityNode")
const currentSelectedCommunityId = ref(null)
+// 用于存储点击位置
+const clickedPosition = ref(0)
+
+// 添加定时器相关变量
+let animationTimer = null
+const isAutoPlaying = ref(false)
+
+// 开始自动播放方法
+const startAutomaticPlay = () => {
+ // 如果已经在播放,先清除
+ if (animationTimer) {
+ clearInterval(animationTimer)
+ }
+
+ isAutoPlaying.value = true
+
+ // 每300ms移动2%
+ animationTimer = setInterval(() => {
+ // 计算新位置
+ const newPosition = clickedPosition.value + 2
+
+ // 检查是否到达终点
+ if (newPosition >= 100) {
+ clickedPosition.value = 100
+ props.stopAutomaticPlay()
+ return
+ }
+
+ // 更新位置
+ clickedPosition.value = newPosition
+
+ // 根据当前位置更新activeTimePoint
+ updateActiveTimePointByPosition(newPosition)
+ }, 300)
+}
+
+// 根据位置更新activeTimePoint
+const updateActiveTimePointByPosition = (position) => {
+ // 获取排序后的时间点
+ const sortedPoints = [...store.timePoints].sort((a, b) => {
+ return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
+ })
+
+ // 计算每个时间点的原始位置百分比(不考虑重叠调整)
+ const startTime = new Date("2023-10-07T00:00:00").getTime()
+ const endTime = new Date("2023-10-15T00:00:00").getTime()
+ const timeRange = endTime - startTime
+
+ const originalPositions = {}
+ sortedPoints.forEach((point) => {
+ const pointTime = new Date(point.timestamp).getTime()
+ const pointPos = ((pointTime - startTime) / timeRange) * 100
+ originalPositions[point.id] = pointPos
+ })
+ // 找到当前位置对应的时间点
+ let targetPointId = null
+ if (sortedPoints.length > 0 && position < originalPositions[sortedPoints[0].id]) {
+ store.setActiveTimePoint(0)
+ } else {
+ for (let i = 0; i < sortedPoints.length; i++) {
+ const point = sortedPoints[i]
+ const pointPosition = originalPositions[point.id]
+ // 如果位置大于等于当前时间点位置,则选中当前时间点
+ if (position >= pointPosition) {
+ targetPointId = point.id
+ } else {
+ break
+ }
+ }
+ }
+
+ // 如果找到了对应的时间点且与当前activeTimePoint不同,则更新
+ if (targetPointId && targetPointId !== store.activeTimePoint) {
+ store.setActiveTimePoint(targetPointId)
+ }
+}
+
+const handleTrackClick = (event) => {
+ // 停止自动播放
+ props.stopAutomaticPlay()
+
+ // 清除内部定时器
+ if (animationTimer) {
+ clearInterval(animationTimer)
+ animationTimer = null
+ isAutoPlaying.value = false
+ }
+
+ // 获取时间轴轨道的DOM元素和尺寸信息
+ const trackElement = event.currentTarget
+ const rect = trackElement.getBoundingClientRect()
+
+ // 计算点击位置在时间轴上的百分比
+ const clickPosition = ((event.clientX - rect.left) / rect.width) * 100
+ // 保存点击位置
+ clickedPosition.value = clickPosition
+
+ // 获取排序后的时间点
+ const sortedPoints = [...store.timePoints].sort((a, b) => {
+ return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
+ })
+
+ // 计算每个时间点的原始位置百分比(不考虑重叠调整)
+ const startTime = new Date("2023-10-07T00:00:00").getTime()
+ const endTime = new Date("2023-10-15T00:00:00").getTime()
+ const timeRange = endTime - startTime
+
+ const originalPositions = {};
+ sortedPoints.forEach((point) => {
+ const pointTime = new Date(point.timestamp).getTime();
+ const position = ((pointTime - startTime) / timeRange) * 100;
+ originalPositions[point.id] = position;
+ });
+
+ // 特殊情况:如果点击位置小于所有时间点的位置,则设置activeTimePoint为0
+ if (sortedPoints.length > 0 && clickPosition < originalPositions[sortedPoints[0].id]) {
+ store.setActiveTimePoint(0);
+ return; // 直接返回,不再执行后续逻辑
+ }
+
+ // 找到点击位置对应的时间点
+ let targetPointId = null;
+ for (let i = 0; i < sortedPoints.length; i++) {
+ const point = sortedPoints[i];
+ const pointPosition = originalPositions[point.id];
+
+ // 如果点击位置大于等于当前时间点位置,且小于下一个时间点位置,则选中当前时间点
+ if (
+ clickPosition >= pointPosition &&
+ (i === sortedPoints.length - 1 || clickPosition < originalPositions[sortedPoints[i + 1].id])
+ ) {
+ targetPointId = point.id;
+ break;
+ }
+ }
+
+ // 如果找到了对应的时间点,则更新activeTimePoint
+ if (targetPointId) {
+ store.setActiveTimePoint(targetPointId);
+ }
+};
+
const handleGraphNodeClick = (leaderData) => {
store.openLeaderDetail(leaderData)
}
@@ -120,29 +260,29 @@ const calculatePositions = (timestamp) => {
const position = ((pointTime - startTime) / timeRange) * 100
pointPositions[point.id] = Math.max(0, Math.min(100, position))
})
- // 处理重叠 (假设每个点宽度约为20px,时间轴总宽度为可用宽度)
- const pointWidthPercentage = 5 // 估算的点宽度百分比
- const minSpacing = pointWidthPercentage // 最小间距百分比
+ // // 处理重叠 (假设每个点宽度约为20px,时间轴总宽度为可用宽度)
+ // const pointWidthPercentage = 5 // 估算的点宽度百分比
+ // const minSpacing = pointWidthPercentage // 最小间距百分比
- // 调整重叠的点
- for (let i = 1; i < sortedPoints.length; i++) {
- const prevPoint = sortedPoints[i - 1]
- const currPoint = sortedPoints[i]
+ // // 调整重叠的点
+ // for (let i = 1; i < sortedPoints.length; i++) {
+ // const prevPoint = sortedPoints[i - 1]
+ // const currPoint = sortedPoints[i]
- const prevPosition = pointPositions[prevPoint.id]
- const currPosition = pointPositions[currPoint.id]
+ // const prevPosition = pointPositions[prevPoint.id]
+ // const currPosition = pointPositions[currPoint.id]
- // 检查是否重叠
- if (currPosition - prevPosition < minSpacing) {
- // 调整当前点位置
- pointPositions[currPoint.id] = prevPosition + minSpacing
+ // // 检查是否重叠
+ // if (currPosition - prevPosition < minSpacing) {
+ // // 调整当前点位置
+ // pointPositions[currPoint.id] = prevPosition + minSpacing
- // 确保不超出范围
- if (pointPositions[currPoint.id] > 100) {
- pointPositions[currPoint.id] = 100
- }
- }
- }
+ // // 确保不超出范围
+ // if (pointPositions[currPoint.id] > 100) {
+ // pointPositions[currPoint.id] = 100
+ // }
+ // }
+ // }
return pointPositions
}
@@ -161,21 +301,53 @@ watch(
// 计算时间轴轨道样式
const trackStyle = computed(() => {
- if (!store.activeTimePoint) return {}
- const activePosition = pointPositions.value[store.activeTimePoint] || 0
+ // 优先使用clickedPosition,如果没有则使用activeTimePoint对应的位置
+ const activePosition =
+ clickedPosition.value !== null
+ ? clickedPosition.value
+ : pointPositions.value[store.activeTimePoint] || 0
+
return {
background: `linear-gradient(90deg, #3B7699 0%, #00F3FF ${activePosition}%, #3B7699 ${activePosition}%, #3B7699 100%)`
}
})
-defineExpose({ highlightNode })
-// 添加时间点点击事件处理函数
+// 为了保证点击时间点也能更新clickedPosition,修改handleTimePointClick方法
const handleTimePointClick = (pointId) => {
// 停止自动播放
props.stopAutomaticPlay()
+
+ // 清除内部定时器
+ if (animationTimer) {
+ clearInterval(animationTimer)
+ animationTimer = null
+ isAutoPlaying.value = false
+ }
+
// 设置当前激活的时间点
store.setActiveTimePoint(pointId)
+
+ // 查找该时间点的原始位置
+ const point = store.timePoints.find((p) => p.id === pointId)
+ if (point) {
+ const startTime = new Date("2023-10-07T00:00:00").getTime()
+ const endTime = new Date("2023-10-15T00:00:00").getTime()
+ const timeRange = endTime - startTime
+ const pointTime = new Date(point.timestamp).getTime()
+ const position = ((pointTime - startTime) / timeRange) * 100
+ clickedPosition.value = position
+ }
}
+
+// 在组件卸载时清除定时器
+import { onUnmounted } from "vue"
+onUnmounted(() => {
+ if (animationTimer) {
+ clearInterval(animationTimer)
+ }
+})
+
+defineExpose({ highlightNode, startAutomaticPlay })
diff --git a/src/views/KeyNodeDiscern/bridgeCommunication/components/graph/bridgeCommunityGraph.vue b/src/views/KeyNodeDiscern/bridgeCommunication/components/graph/bridgeCommunityGraph.vue
index 39d4331..eafdebc 100644
--- a/src/views/KeyNodeDiscern/bridgeCommunication/components/graph/bridgeCommunityGraph.vue
+++ b/src/views/KeyNodeDiscern/bridgeCommunication/components/graph/bridgeCommunityGraph.vue
@@ -43,6 +43,13 @@ const processData = async () => {
// 批量将头像转换成base64
const bridgeNodes = keyNodeStore2.bridgeNodes
+ if (props.timestamp === 0) {
+ const result = {
+ nodes: [],
+ links: []
+ }
+ return result
+ }
// 根据timestamp过滤桥梁节点
const filteredBridgeNodes = bridgeNodes.filter((node) => node.postsId <= props.timestamp)
for (const node of filteredBridgeNodes) {
@@ -543,13 +550,14 @@ onUnmounted(() => {
onMounted(() => {
setTimeout(async () => {
await initChart()
- }, 100)
+ }, 500)
resizeChart()
})
watch(
() => props.timestamp,
async (newValue) => {
+ console.log("newValue:", newValue)
await initChart()
}
)
diff --git a/src/views/KeyNodeDiscern/bridgeCommunication/index.vue b/src/views/KeyNodeDiscern/bridgeCommunication/index.vue
index e2a3dec..73581f2 100644
--- a/src/views/KeyNodeDiscern/bridgeCommunication/index.vue
+++ b/src/views/KeyNodeDiscern/bridgeCommunication/index.vue
@@ -89,6 +89,11 @@ const stopAutomaticPlay = () => {
clearInterval(timer)
timer = null
}
+ // 调用GraphPanel组件的内部方法停止自动播放
+ if (graphPanelRef.value) {
+ // 通过直接清除定时器的方式停止,因为我们在GraphPanel内部也实现了停止逻辑
+ // 这里不需要额外调用,因为GraphPanel中的handleTrackClick和handleTimePointClick已经会停止自动播放
+ }
}
let timer = null
@@ -96,34 +101,19 @@ let timer = null
// 根据不同的时间间隔生成对应的延时数据
const intervalTimes = [500, 500, 1000, 1000, 1000, 1000, 1000, 1000, 4000, 2000]
const automaticPlay = () => {
- let index = 1
- if (timer) clearInterval(timer)
-
- // 创建一个函数来处理定时逻辑
- const playNext = () => {
- store.setActiveTimePoint(index)
-
- if (index >= store.allLeaderData.length) {
- clearInterval(timer)
- timer = null
- return
- }
-
- // 根据当前index获取对应的时间间隔,如果超出数组长度则使用默认值
- const interval = intervalTimes[index] || 1000
- timer = setTimeout(playNext, interval)
- index++
+ // 不再使用原来的定时器逻辑,而是使用GraphPanel组件内部的定时器
+ if (graphPanelRef.value) {
+ graphPanelRef.value.startAutomaticPlay()
}
-
- // 启动自动播放,使用第一个时间间隔
- const initialInterval = intervalTimes[0] || 1000
- timer = setTimeout(playNext, initialInterval)
}
// 调用 store.initializeTimePoints() 初始化时间点
onMounted(() => {
store.initializeTimePoints()
- automaticPlay()
+ // 延迟调用,确保graphPanelRef已经挂载
+ setTimeout(() => {
+ automaticPlay()
+ }, 1000)
})
onUnmounted(() => {
diff --git a/src/views/LinkPrediction/charactersHiddenInteraction/index.vue b/src/views/LinkPrediction/charactersHiddenInteraction/index.vue
index 083ff48..f8723fc 100644
--- a/src/views/LinkPrediction/charactersHiddenInteraction/index.vue
+++ b/src/views/LinkPrediction/charactersHiddenInteraction/index.vue
@@ -123,8 +123,8 @@ import Graph from "../components/graph.vue"
import WordsCloud from "../components/cloudWords.vue"
import { useCharacterHiddenStore } from "@/store/linkPrediction/index"
import userPanelTitleImg from "@/assets/images/linkPrediction/title/characters-hidden-interaction-user-title.png"
-import graphTitleImg from "@/assets/images/linkPrediction/title/graph1-title.png"
-import analysisTitleImg from "@/assets/images/linkPrediction/title/analysis-title.png"
+import graphTitleImg from "@/assets/images/linkPrediction/title/graph3-title.png"
+import analysisTitleImg from "@/assets/images/linkPrediction/title/characters-hidden-interaction-analysis-title.png"
import { storeToRefs } from "pinia"
const characterHiddenStore = useCharacterHiddenStore()
diff --git a/src/views/LinkPrediction/socialGroups/index.vue b/src/views/LinkPrediction/socialGroups/index.vue
index 43ac36b..711bdfb 100644
--- a/src/views/LinkPrediction/socialGroups/index.vue
+++ b/src/views/LinkPrediction/socialGroups/index.vue
@@ -146,7 +146,7 @@ import { Icon } from "@iconify/vue"
import { useSocialGroupsStore } from "@/store/linkPrediction/index"
import userPanelTitleImg from "@/assets/images/linkPrediction/title/social-group-user-title.png"
import userChartTitleImg from "@/assets/images/linkPrediction/title/interaction-strenth-title.png"
-import graphTitleImg from "@/assets/images/linkPrediction/title/graph1-title.png"
+import graphTitleImg from "@/assets/images/linkPrediction/title/graph2-title.png"
import analysisTitleImg from "@/assets/images/linkPrediction/title/social-group-analysis-title.png"
import { getAvatarUrl } from "@/utils/transform"
import { storeToRefs } from "pinia"