Merge branch 'master' of http://172.16.20.1:3000/duanhao/SocialNetworks_duan
This commit is contained in:
		
						commit
						3b024cb6cd
					
				
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 21 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/linkPrediction/title/graph2-title.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/linkPrediction/title/graph2-title.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/linkPrediction/title/graph3-title.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/linkPrediction/title/graph3-title.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/time-point.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/time-point.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 864 B  | 
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
    () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
        </div>
 | 
			
		||||
        <div class="timeline-container">
 | 
			
		||||
          <span class="time-label">2023.10.07 00:00:00</span>
 | 
			
		||||
          <div class="timeline-track" :style="trackStyle">
 | 
			
		||||
          <div class="timeline-track" :style="trackStyle" @click="handleTrackClick">
 | 
			
		||||
            <div
 | 
			
		||||
              v-for="point in store.timePoints"
 | 
			
		||||
              :key="point.id"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,13 +43,11 @@
 | 
			
		|||
                    trigger="click"
 | 
			
		||||
                    content="发布贴文"
 | 
			
		||||
                  >
 | 
			
		||||
                    <template #reference>
 | 
			
		||||
                      <div class="active-pin"></div>
 | 
			
		||||
                    </template>
 | 
			
		||||
                  </el-popover>
 | 
			
		||||
                </div>
 | 
			
		||||
              </el-tooltip>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="active-pin" :style="{ left: `${clickedPosition}%` }"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <span class="time-label">2023.10.15 00:00:00</span>
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -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 })
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
| 
						 | 
				
			
			@ -250,6 +422,7 @@ const handleTimePointClick = (pointId) => {
 | 
			
		|||
  position: relative;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
.timeline-point-wrapper {
 | 
			
		||||
  display: flex;
 | 
			
		||||
| 
						 | 
				
			
			@ -261,11 +434,10 @@ const handleTimePointClick = (pointId) => {
 | 
			
		|||
  transform: translateX(-50%);
 | 
			
		||||
}
 | 
			
		||||
.timeline-point {
 | 
			
		||||
  width: vw(18);
 | 
			
		||||
  width: 4px;
 | 
			
		||||
  height: vh(18);
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  border: 1.6px solid #ffe5a4;
 | 
			
		||||
  background: linear-gradient(180deg, #fee39e 0%, #f9bd25 100%);
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  transition: transform 0.3s ease;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  display: flex;
 | 
			
		||||
| 
						 | 
				
			
			@ -273,29 +445,24 @@ const handleTimePointClick = (pointId) => {
 | 
			
		|||
  justify-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除 ::after 伪元素,因为不再需要内部圆点
 | 
			
		||||
.timeline-point::after {
 | 
			
		||||
  content: "";
 | 
			
		||||
  width: vw(10);
 | 
			
		||||
  height: vh(10);
 | 
			
		||||
  background-color: #f9bd25;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.timeline-point-wrapper:hover .timeline-point {
 | 
			
		||||
  transform: scale(1.5);
 | 
			
		||||
}
 | 
			
		||||
// 调整 active 状态样式
 | 
			
		||||
.timeline-point.active {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  border-color: #ffe5a4;
 | 
			
		||||
  transform: scale(1.3);
 | 
			
		||||
  background: linear-gradient(180deg, #fee39e 0%, #f9bd25 100%);
 | 
			
		||||
  transform: scaleX(1.5);
 | 
			
		||||
}
 | 
			
		||||
.active-pin {
 | 
			
		||||
  width: vw(30);
 | 
			
		||||
  height: vh(34);
 | 
			
		||||
  background-image: url("@/assets/images/point.png");
 | 
			
		||||
  width: vw(14);
 | 
			
		||||
  height: vh(14);
 | 
			
		||||
  background-image: url("@/assets/images/time-point.png");
 | 
			
		||||
  background-size: cover;
 | 
			
		||||
  bottom: vh(6);
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  bottom: vh(-3);
 | 
			
		||||
  transform: translateX(-50%); // 居中对齐
 | 
			
		||||
  z-index: 10; // 确保显示在时间点上方
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user