SocialNetworks_duan/src/views/KeyNodeDiscern/bridgeCommunication/components/GraphPanel.vue

316 lines
9.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="right-panel">
<div class="key-node-recognition">
<div class="background-svg-wrapper">
<svg
width="100%"
height="100%"
viewBox="0 0 800 540"
preserveAspectRatio="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient
id="paint0_linear_bg"
x1="0"
y1="167.1"
x2="800"
y2="167.1"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#063D71" stop-opacity="0.2" />
<stop offset="1" stop-color="#081E38" stop-opacity="0.8" />
</linearGradient>
<linearGradient
id="paint1_linear_border"
x1="400"
y1="0"
x2="400"
y2="540"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#3AA1F8" />
<stop offset="1" stop-color="#3AA1F8" stop-opacity="0.2" />
</linearGradient>
</defs>
<path
d="M798 0.5H2C1.17159 0.5 0.500003 1.17158 0.5 2V538C0.5 538.828 1.17159 539.5 2 539.5H798C798.828 539.5 799.5 538.828 799.5 538V2C799.5 1.17157 798.828 0.5 798 0.5Z"
fill="url(#paint0_linear_bg)"
fill-opacity="0.48"
stroke="url(#paint1_linear_border)"
/>
</svg>
</div>
<div class="content-wrapper">
<img src="@/assets/images/chuanboGraphTitle.png" alt="" />
<div class="chart-container">
<DynamicGraph
ref="leaderGraphRef"
v-show="false"
:timestamp="store.activeTimePoint"
:allLeaderData="store.allLeaderData"
@handle:openDialog="handleGraphNodeClick"
/>
<BridgeCommunityGraph ref="bridgeGraphRef" v-if="currentComponent == 'BridgeCommunityNode'" :timestamp="store.activeTimePoint" @click:navigateToCommunityDetail="handleClickCommunity"/>
<DetailCommunityGraph v-else ref="detailCommunityGraphRef"
:communityId="currentSelectedCommunityId"
@click:goback="handleClickGoBack"
/>
</div>
<div class="timeline-container">
<span class="time-label">2023.10.07 00:00:00</span>
<div class="timeline-track" :style="trackStyle">
<div
v-for="point in store.timePoints"
:key="point.id"
class="timeline-point-wrapper"
:style="{ left: `${pointPositions[point.id]}%` }"
@click="store.setActiveTimePoint(point.id)"
>
<el-tooltip
class="timePoint-box-item"
effect="light"
:content="point.timestamp"
placement="bottom"
>
<div class="timeline-point" :class="{ active: store.activeTimePoint === point.id }">
<el-popover
v-if="store.activeTimePoint === point.id"
effect="dark"
placement="top"
:title="point.leaderId"
:width="50"
trigger="click"
content="发布贴文"
>
<template #reference>
<div class="active-pin"></div>
</template>
</el-popover>
</div>
</el-tooltip>
</div>
</div>
<span class="time-label">2023.10.15 00:00:00</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, defineExpose, watch, computed } from 'vue';
import { useKeyNodeStore2 } from '@/store/keyNodeStore2';
import DynamicGraph from "./graph/dynamicGraph.vue";
import BridgeCommunityGraph from './graph/bridgeCommunityGraph.vue';
import DetailCommunityGraph from './graph/detailCommunityGraph.vue';
const store = useKeyNodeStore2();
const leaderGraphRef = ref(null);
// 添加对BridgeCommunityGraph的引用
const bridgeGraphRef = ref(null);
// 设置当前激活的组件--默认是BridgeCommunityGraph
const currentComponent = ref("BridgeCommunityNode");
const currentSelectedCommunityId = ref(null);
const handleGraphNodeClick = (leaderData) => {
store.openLeaderDetail(leaderData);
};
const handleClickCommunity = (communityId) => {
if(communityId) {
currentComponent.value = "DetailCommunityNode";
currentSelectedCommunityId.value = communityId;
}
}
const handleClickGoBack = (currentComponentName) => {
currentComponent.value = currentComponentName;
};
const highlightNode = (leaderId) => {
if (leaderGraphRef.value) {
leaderGraphRef.value.highlightNode(leaderId);
}
// 调用BridgeCommunityGraph的highlightNode方法
if (bridgeGraphRef.value) {
bridgeGraphRef.value.highlightNode(leaderId);
}
};
// 计算时间点在时间轴上的位置百分比
const calculatePositions = (timestamp) => {
// 时间范围: 2023-10-07T00:00:00 到 2023-10-15T00:00:00
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 sortedPoints = [...store.timePoints].sort((a, b) => {
return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
});
// 计算每个点的原始位置
const pointPositions = {};
sortedPoints.forEach(point => {
const pointTime = new Date(point.timestamp).getTime();
const position = ((pointTime - startTime) / timeRange) * 100;
pointPositions[point.id] = Math.max(0, Math.min(100, position));
});
// 处理重叠 (假设每个点宽度约为20px时间轴总宽度为可用宽度)
const pointWidthPercentage = 5; // 估算的点宽度百分比
const minSpacing = pointWidthPercentage; // 最小间距百分比
// 调整重叠的点
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];
// 检查是否重叠
if (currPosition - prevPosition < minSpacing) {
// 调整当前点位置
pointPositions[currPoint.id] = prevPosition + minSpacing;
// 确保不超出范围
if (pointPositions[currPoint.id] > 100) {
pointPositions[currPoint.id] = 100;
}
}
}
return pointPositions;
}
// 响应式引用存储位置信息
const pointPositions = ref({});
// 监听时间点变化,重新计算位置
watch(
() => store.timePoints,
() => {
pointPositions.value = calculatePositions();
},
{ immediate: true, deep: true }
);
// 计算时间轴轨道样式
const trackStyle = computed(() => {
if (!store.activeTimePoint) return {};
const activePosition = pointPositions.value[store.activeTimePoint] || 0;
return {
background: `linear-gradient(90deg, #3B7699 0%, #00F3FF ${activePosition}%, #3B7699 ${activePosition}%, #3B7699 100%)`
};
});
defineExpose({ highlightNode });
</script>
<style scoped>
.right-panel {
flex-grow: 1;
position: relative;
}
.key-node-recognition {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.background-svg-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.content-wrapper {
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
height: 100%;
padding: 15px 20px;
box-sizing: border-box;
}
.chart-container {
flex-grow: 1;
width: 100%;
height: calc(100% - 100px);
}
.timeline-container {
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
box-sizing: border-box;
background-color: rgba(4, 67, 92, 0.6);
border-radius: 5px;
z-index: 1;
}
.time-label {
font-size: 12px;
color: #a9c2e0;
}
.timeline-track {
flex-grow: 1;
height: 4px;
/* background: linear-gradient(90deg, #1b62a9, #3aa1f8, #1b62a9); */
margin: 0 15px;
position: relative;
display: flex;
/* justify-content: space-between; */
align-items: center;
}
.timeline-point-wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 20px;
cursor: pointer;
position: absolute;
transform: translateX(-50%);
}
.timeline-point {
width: 18px;
height: 18px;
background-color: transparent;
border-radius: 50%;
border: 1.6px solid #FFE5A4;
transition: transform 0.3s ease;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.timeline-point::after {
content: '';
width: 10px;
height: 10px;
background-color: #F9BD25;
border-radius: 50%;
position: absolute;
}
.timeline-point-wrapper:hover .timeline-point {
transform: scale(1.5);
}
.timeline-point.active {
background-color: transparent;
border-color: #FFE5A4;
transform: scale(1.3);
}
.active-pin {
width: 30px;
height: 34px;
background-image: url("@/assets/images/point.png");
background-size: cover;
bottom:6px;
position: absolute;
}
</style>