This commit is contained in:
qumeng039@126.com 2025-07-08 16:32:13 +08:00
parent 908bcfc5b1
commit 99acd814eb
11 changed files with 437 additions and 394 deletions

View File

@ -249,6 +249,7 @@ const showToggleJianjie = ref(false);
<style scoped> <style scoped>
.app-container { .app-container {
width: 100vw; width: 100vw;
padding-bottom: 20px;
background-image: url("./assets/images/bci.png"); background-image: url("./assets/images/bci.png");
background-size:cover; background-size:cover;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -272,7 +273,7 @@ const showToggleJianjie = ref(false);
} }
.aside { .aside {
width: 320px; width: 320px;
height: 100%; height: 954px;
border: 2px; border: 2px;
padding: 16px; padding: 16px;
left: 16px; left: 16px;

View File

@ -47,14 +47,14 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onUnmounted } from 'vue'; import { ref, onMounted, onUnmounted } from "vue";
import { useKeyNodeStore } from '@/store/keyNodeStore1'; import { useKeyNodeStore } from "@/store/keyNodeStore1";
import LeaderList from './KeyNodeRecognition1/components/LeaderList.vue'; import LeaderList from "./KeyNodeRecognition1/components/LeaderList.vue";
import GraphPanel from './KeyNodeRecognition1/components/GraphPanel.vue'; import GraphPanel from "./KeyNodeRecognition1/components/GraphPanel.vue";
import DetailsModal from './KeyNodeRecognition1/components/DetailsModal.vue'; import DetailsModal from "./KeyNodeRecognition1/components/DetailsModal.vue";
import LeaderDetailDialog from './KeyNodeRecognition1/components/LeaderDetailDialog.vue'; import LeaderDetailDialog from "./KeyNodeRecognition1/components/LeaderDetailDialog.vue";
import PostDetailDialog from './KeyNodeRecognition1/components/PostDetailDialog.vue'; import PostDetailDialog from "./KeyNodeRecognition1/components/PostDetailDialog.vue";
import LeaderAnalysis from "./KeyNodeRecognition1/components/LeaderAnalysis.vue"; import LeaderAnalysis from "./KeyNodeRecognition1/components/LeaderAnalysis.vue";
import EventHeatmap from "../components/weight/EventHeatmap.vue"; import EventHeatmap from "../components/weight/EventHeatmap.vue";
@ -224,4 +224,38 @@ const wordCloudData = ref([
.leader-containner2 { .leader-containner2 {
margin-top: 10px; margin-top: 10px;
} }
</style>
:deep(.custom-leader-dialog) {
width: 640px;
height: 680px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
}
:deep(.custom-leader-dialog) .dialog-content {
width: 100%;
height: 100%;
padding: 23px 23px;
}
:deep(.custom-leader-post-dialog) {
width: 640px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
z-index: 1;
}
:deep(.custom-leader-post-dialog) .dialog-content {
width: 100%;
padding: 25px 25px;
}
</style>

View File

@ -48,7 +48,7 @@
<div class="content"> <div class="content">
<!-- Title Image: using a placeholder as the local asset is not available --> <!-- Title Image: using a placeholder as the local asset is not available -->
<img <img
src="../../assets/images/chuanbofenxititle.png" src="@/assets/images/chuanbofenxititle.png"
alt="传播意见领袖分析" alt="传播意见领袖分析"
class="title-img" class="title-img"
style="margin-top: -22px; margin-left: -24px" style="margin-top: -22px; margin-left: -24px"

View File

@ -1,83 +1,77 @@
<template> <template>
<el-dialog v-model="isVisible" width="640" align-center class="custom-dialog"> <el-dialog v-model="isVisible" width="640" align-center class="custom-leader-dialog">
<template v-if="store.activeLeader"> <img src="@/assets/images/leaderDialogTitle.png" alt="" class="dialogTitleImg" />
<img src="@/assets/images/leaderDialogTitle.png" alt="" class="dialogTitleImg" /> <div class="dialog-content">
<div class="dialog-content"> <div class="dialog-content-leaderInfo">
<div class="dialog-content-leaderInfo"> <img class="leaderInfo-avatar" :src="store.activeLeader.leaderOriginInfo.avatar" alt="" />
<img class="leaderInfo-avatar" :src="store.activeLeader.leaderOriginInfo.avatar" alt="" /> <div class="leaderInfo-message">
<div class="leaderInfo-message"> <div class="leader-name">{{ store.activeLeader.name }}</div>
<div class="leader-name">{{ store.activeLeader.name }}</div> <div class="leader-heat">
<div class="leader-heat"> <div class="fancy">粉丝量:  {{ store.activeLeader.leaderOriginInfo.followers }}</div>
<div class="fancy"> <div class="post-number">发帖数:  {{ store.activeLeader.leaderOriginInfo.posts }}</div>
粉丝量:  {{ store.activeLeader.leaderOriginInfo.followers }}
</div>
<div class="post-number">
发帖数:  {{ store.activeLeader.leaderOriginInfo.posts }}
</div>
</div>
</div> </div>
</div> </div>
<div class="dialog-content-post"> </div>
<div class="leader-post-detail-content"> <div class="dialog-content-post">
<div <div class="leader-post-detail-content">
class="content-item" <div
v-for="item in store.activeLeader.leaderOriginInfo.labelling" class="content-item"
:key="item.id" v-for="item in store.activeLeader.leaderOriginInfo.labelling"
> :key="item.id"
<div class="item-type">{{ item.type }}</div> >
<div class="item-content">{{ item.content }}</div> <div class="item-type">{{ item.type }}</div>
<div class="item-heat"> <div class="item-content">{{ item.content }}</div>
<div class="item-time">{{ item.time }}</div> <div class="item-heat">
<div class="item-heat-detail"> <div class="item-time">{{ item.time }}</div>
<div class="item-heat-like"> <div class="item-heat-detail">
<Icon icon="ei:like" width="25" height="25" /> {{ item.like }} <div class="item-heat-like">
</div> <Icon icon="ei:like" width="25" height="25" /> {{ item.like }}
<div class="item-heat-comment"> </div>
<Icon icon="la:comment-dots" width="25" height="25" /> {{ item.comment }} <div class="item-heat-comment">
</div> <Icon icon="la:comment-dots" width="25" height="25" /> {{ item.comment }}
<div class="item-heat-transmit"> </div>
<Icon icon="mdi:share-outline" width="25" height="25" /> {{ item.transmit }} <div class="item-heat-transmit">
</div> <Icon icon="mdi:share-outline" width="25" height="25" /> {{ item.transmit }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="dialog-content-heat-degree"> </div>
<div class="heat-item"> <div class="dialog-content-heat-degree">
<p class="diamond"></p> <div class="heat-item">
粉丝数量:   {{ store.activeLeader.leaderOriginInfo.followers }} <p class="diamond"></p>
</div> 粉丝数量:   {{ store.activeLeader.leaderOriginInfo.followers }}
<div class="heat-item"> </div>
<p class="diamond"></p> <div class="heat-item">
关注数量:   1329 <p class="diamond"></p>
</div> 关注数量:   1329
<div class="heat-item"> </div>
<p class="diamond"></p> <div class="heat-item">
发帖总数:   {{ store.activeLeader.leaderOriginInfo.posts }} <p class="diamond"></p>
</div> 发帖总数:   {{ store.activeLeader.leaderOriginInfo.posts }}
<div class="heat-item"> </div>
<p class="diamond"></p> <div class="heat-item">
贴文被转总数:   1329 <p class="diamond"></p>
</div> 贴文被转总数:   1329
<div class="heat-item"> </div>
<p class="diamond"></p> <div class="heat-item">
参与互动次数:   30 <p class="diamond"></p>
</div> 参与互动次数:   30
<div class="heat-item"> </div>
<p class="diamond"></p> <div class="heat-item">
首次活跃事件:   2022.7.31 00:14 <p class="diamond"></p>
</div> 首次活跃事件:   2022.7.31 00:14
</div> </div>
</div> </div>
</template> </div>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from "vue";
import { useKeyNodeStore } from '@/store/keyNodeStore1'; import { useKeyNodeStore } from "@/store/keyNodeStore1";
import { Icon } from '@iconify/vue'; import { Icon } from "@iconify/vue";
const store = useKeyNodeStore(); const store = useKeyNodeStore();
@ -92,25 +86,10 @@ const isVisible = computed({
</script> </script>
<style scoped> <style scoped>
:deep(.custom-dialog) {
width: 640px;
height: 680px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
}
.dialogTitleImg { .dialogTitleImg {
margin-top: -23px; margin-top: -23px;
} }
:deep(.custom-dialog) .dialog-content {
width: 100%;
height: 100%;
padding: 23px 23px;
}
.dialog-content-leaderInfo { .dialog-content-leaderInfo {
width: 100%; width: 100%;
height: 70px; height: 70px;
@ -209,4 +188,4 @@ const isVisible = computed({
margin-right: 10px; margin-right: 10px;
box-shadow: 0 4px 8px rgb(0, 123, 255); box-shadow: 0 4px 8px rgb(0, 123, 255);
} }
</style> </style>

View File

@ -1,31 +1,33 @@
<template> <template>
<el-dialog v-model="isVisible" width="640" align-center class="custom-leader-post-dialog"> <el-dialog v-model="isVisible" width="640" align-center class="custom-leader-post-dialog">
<template v-if="store.currentPost"> <template v-if="store.currentPost">
<img src="@/assets/images/head/post-dialog-title.png" alt="" class="postTitleImage" /> <img src="@/assets/images/head/post-dialog-title.png" alt="" class="postTitleImage" />
<div class="dialog-content"> <div class="dialog-content">
<div class="content">{{ store.currentPost.content }}</div> <div class="content">{{ store.currentPost.content }}</div>
<div class="heat"> <div class="heat">
<div class="item-heat-detail"> <div class="item-heat-detail">
<div class="item-heat-like"> <div class="item-heat-like">
<Icon icon="ei:like" width="25" height="25" /> {{ store.currentPost.like }} <Icon icon="ei:like" width="25" height="25" /> {{ store.currentPost.like }}
</div> </div>
<div class="item-heat-comment"> <div class="item-heat-comment">
<Icon icon="la:comment-dots" width="25" height="25" /> {{ store.currentPost.comment }} <Icon icon="la:comment-dots" width="25" height="25" /> {{ store.currentPost.comment }}
</div> </div>
<div class="item-heat-transmit"> <div class="item-heat-transmit">
<Icon icon="mdi:share-outline" width="25" height="25" /> {{ store.currentPost.transmit }} <Icon icon="mdi:share-outline" width="25" height="25" /> {{
</div> store.currentPost.transmit
}}
</div> </div>
</div> </div>
</div> </div>
</template> </div>
</template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from "vue";
import { useKeyNodeStore } from '@/store/keyNodeStore1'; import { useKeyNodeStore } from "@/store/keyNodeStore1";
import { Icon } from '@iconify/vue'; import { Icon } from "@iconify/vue";
const store = useKeyNodeStore(); const store = useKeyNodeStore();
@ -39,22 +41,7 @@ const isVisible = computed({
}); });
</script> </script>
<style scoped> <style>
:deep(.custom-leader-post-dialog) {
width: 640px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
z-index: 1;
}
:deep(.custom-leader-post-dialog) .dialog-content {
width: 100%;
padding: 25px 25px;
}
.content { .content {
color: #fff; color: #fff;
opacity: 0.7; opacity: 0.7;
@ -64,6 +51,9 @@ const isVisible = computed({
justify-content: flex-end; justify-content: flex-end;
margin-top: 20px; margin-top: 20px;
} }
.heat .item-heat-detail {
display: flex;
}
.heat .item-heat-detail div { .heat .item-heat-detail div {
width: 70px; width: 70px;
color: #fff; color: #fff;
@ -73,4 +63,4 @@ const isVisible = computed({
margin-top: -24px; margin-top: -24px;
margin-left: -2px; margin-left: -2px;
} }
</style> </style>

View File

@ -21,7 +21,7 @@ import Cache from "@/utils/cache";
// --- 10 10images/leader--- // --- 10 10images/leader---
import node1_1 from "@/assets/images/leader/The Spectator Index.png"; import node1_1 from "@/assets/images/leader/The Spectator Index.png";
import node1_2 from "@/assets/images/leader/自由時報_ziyoushibao.png"; import node1_2 from "@/assets/images/leader/自由時報_ziyoushibao.png";
import node1_3 from "@/assets/images/qiaoliang/Andy Ngo.png";// import node1_3 from "@/assets/images/qiaoliang/Andy Ngo.png"; //
import node1_4 from "@/assets/images/leader/Viscosity🧩Solutions.png"; import node1_4 from "@/assets/images/leader/Viscosity🧩Solutions.png";
import node1_5 from "@/assets/images/leader/Insider Paper.png"; import node1_5 from "@/assets/images/leader/Insider Paper.png";
import node1_6 from "@/assets/images/leader/रV Sinघ.png"; import node1_6 from "@/assets/images/leader/रV Sinघ.png";
@ -75,7 +75,7 @@ import node5_4 from "@/assets/images/leader/Radio.png";
import node5_5 from "@/assets/images/leader/Rafael Fontana.png"; import node5_5 from "@/assets/images/leader/Rafael Fontana.png";
import node5_6 from "@/assets/images/leader/Indo-Pacific News - Watching the CCP-China Threat.png"; import node5_6 from "@/assets/images/leader/Indo-Pacific News - Watching the CCP-China Threat.png";
import node5_7 from "@/assets/images/leader/🍁RASTA MAN™🍁.png"; import node5_7 from "@/assets/images/leader/🍁RASTA MAN™🍁.png";
import node5_8 from "@/assets/images/qiaoliang/THEE((L'EtatC'estMoi))🎗️.png";// import node5_8 from "@/assets/images/qiaoliang/THEE((L'EtatC'estMoi))🎗️.png"; //
import node5_9 from "@/assets/images/leader/Mundo News.png"; import node5_9 from "@/assets/images/leader/Mundo News.png";
import node5_10 from "@/assets/images/leader/迷人的小红🇨🇳.png"; import node5_10 from "@/assets/images/leader/迷人的小红🇨🇳.png";
@ -107,7 +107,7 @@ import node8_5 from "@/assets/images/leader/911news.png";
import node8_6 from "@/assets/images/leader/Navar 🇧🇷🇷🇺.png"; import node8_6 from "@/assets/images/leader/Navar 🇧🇷🇷🇺.png";
import node8_7 from "@/assets/images/leader/Kingmoj.png"; import node8_7 from "@/assets/images/leader/Kingmoj.png";
import node8_8 from "@/assets/images/leader/Hosanna Revivals.png"; import node8_8 from "@/assets/images/leader/Hosanna Revivals.png";
import node8_9 from "@/assets/images/qiaoliang/SilbernagelSimos.png";// import node8_9 from "@/assets/images/qiaoliang/SilbernagelSimos.png"; //
// --- 9 --- // --- 9 ---
import node9_1 from "@/assets/images/leader/Go22.png"; import node9_1 from "@/assets/images/leader/Go22.png";
@ -250,7 +250,6 @@ const node9Followers = [
{ name: "从头再来_congtouzailai", avatar: node9_9 } { name: "从头再来_congtouzailai", avatar: node9_9 }
]; ];
const allLeaderData = ref(props.allLeaderData); const allLeaderData = ref(props.allLeaderData);
const chart = ref(null); const chart = ref(null);
const allGraphData = ref({ nodes: [], edges: [] }); const allGraphData = ref({ nodes: [], edges: [] });
@ -270,15 +269,30 @@ const getCircleAvatar = async (avatarUrl) => {
const initAllGraphData = async () => { const initAllGraphData = async () => {
const nodes = []; const nodes = [];
const edges = []; const edges = [];
const node2Avatars = [ const node2Avatars = [
node2_1, node2_2, node2_3, node2_4, node2_5, node2_1,
node2_6, node2_7, node2_8, node2_9, node2_10 node2_2,
node2_3,
node2_4,
node2_5,
node2_6,
node2_7,
node2_8,
node2_9,
node2_10
]; ];
const node2Names = [ const node2Names = [
'Andy Ngo', 'Andy Ngo', 'Ian Miles Cheong', '简爱_jianai', "Andy Ngo",
'唯有基督拯救人类!', 'wethenorth - 1984', 'Lomt Lea🅰', "Andy Ngo",
'Chad Felix Greene 🇮🇱', 'AlinaSalazarGongora', 'Gary Du' "Ian Miles Cheong",
"简爱_jianai",
"唯有基督拯救人类!",
"wethenorth - 1984",
"Lomt Lea🅰",
"Chad Felix Greene 🇮🇱",
"AlinaSalazarGongora",
"Gary Du"
]; ];
for (const [leaderIndex, leader] of allLeaderData.value.entries()) { for (const [leaderIndex, leader] of allLeaderData.value.entries()) {
@ -305,7 +319,7 @@ const initAllGraphData = async () => {
if (leaderIndex === 0 && i < node1Followers.length) { if (leaderIndex === 0 && i < node1Followers.length) {
const followerData = node1Followers[i]; const followerData = node1Followers[i];
const currentAvatar = followerData.avatar; const currentAvatar = followerData.avatar;
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
@ -321,11 +335,11 @@ const initAllGraphData = async () => {
// 1 // 1
else if (leaderIndex === 1 && i < 10) { else if (leaderIndex === 1 && i < 10) {
const currentAvatar = node2Avatars[i]; const currentAvatar = node2Avatars[i];
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: node2Names[i], name: node2Names[i],
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
@ -341,13 +355,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -358,13 +372,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -375,13 +389,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -392,13 +406,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -486,68 +500,66 @@ const initAllGraphData = async () => {
label: { show: false } label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} } else {
else {
// 使 // 使
nodes.push({ // nodes.push({
id: userId, // id: userId,
name: `user ${i}`, // name: `user ${i}`,
symbol: "circle", // symbol: "circle",
symbolSize: 25, // symbolSize: 25,
category: 1, // category: 1,
value: "", // value: "",
label: { show: false }, // label: { show: false },
itemStyle: { // itemStyle: {
color: { // color: {
type: "linear", // type: "linear",
colorStops: [ // colorStops: [
{ offset: 0, color: "#035e96" }, // { offset: 0, color: "#035e96" },
{ offset: 1, color: "#34a7b0" } // { offset: 1, color: "#34a7b0" }
] // ]
} // }
} // }
}); // });
nodeAdded = true; nodeAdded = true;
} }
// //
if (nodeAdded) { if (nodeAdded) {
edges.push({ edges.push({
source: leader.id, source: leader.id,
target: userId target: userId
}); });
} }
} }
} }
// //
const splatteringCount = Math.floor(Math.random() * 11) + 10; // 10~20 // const splatteringCount = Math.floor(Math.random() * 11) + 10; // 10~20
for (let i = 0; i < splatteringCount; i++) { // for (let i = 0; i < splatteringCount; i++) {
const userId = `user_splattering_${i}`; // const userId = `user_splattering_${i}`;
nodes.push({ // nodes.push({
id: userId, // id: userId,
name: `user ${i}`, // name: `user ${i}`,
symbol: "circle", // symbol: "circle",
symbolSize: 25, // symbolSize: 25,
category: 1, // category: 1,
value: "", // value: "",
label: { show: false }, // label: { show: false },
itemStyle: { // itemStyle: {
color: { // color: {
type: "linear", // type: "linear",
colorStops: [ // colorStops: [
{ offset: 0, color: "#035e96" }, // { offset: 0, color: "#035e96" },
{ offset: 1, color: "#34a7b0" } // { offset: 1, color: "#34a7b0" }
] // ]
} // }
} // }
}); // });
} // }
allGraphData.value = { nodes, edges }; allGraphData.value = { nodes, edges };
console.log(allGraphData.value); console.log(allGraphData.value);
}; };
// timestamp // timestamp
const getVisibleGraphData = () => { const getVisibleGraphData = () => {
const leaders = allLeaderData.value.slice(0, props.timestamp); const leaders = allLeaderData.value.slice(0, props.timestamp);
@ -690,4 +702,4 @@ defineExpose({
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>

View File

@ -16,7 +16,17 @@
:panelTrendData="[40105, 24845, 27917, 717, 444]" :panelTrendData="[40105, 24845, 27917, 717, 444]"
:panelXAxisLabels="['2023.10.11', '2023.10.14', '2023.10.18', '2023.10.22', '2023.10.25']" :panelXAxisLabels="['2023.10.11', '2023.10.14', '2023.10.18', '2023.10.22', '2023.10.25']"
:granularTrendData="[28545, 13922, 13719, 16651, 18240, 2205, 274, 157, 315]" :granularTrendData="[28545, 13922, 13719, 16651, 18240, 2205, 274, 157, 315]"
:granularXAxisLabels="['10.9 13h', '10.11 14h', '10.13 16h', '10.15 17h', '10.17 18h', '10.19 20h', '10.21 21h', '10.23 22h', '10.25 23h']" :granularXAxisLabels="[
'10.9 13h',
'10.11 14h',
'10.13 16h',
'10.15 17h',
'10.17 18h',
'10.19 20h',
'10.21 21h',
'10.23 22h',
'10.25 23h'
]"
/> />
<PostDynamics :posts="store.posts" @handle:openDialog="handleOpenPostDialog" /> <PostDynamics :posts="store.posts" @handle:openDialog="handleOpenPostDialog" />
<WordCloud :wordsCloudList="wordCloudData" /> <WordCloud :wordsCloudList="wordCloudData" />
@ -29,14 +39,14 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onUnmounted } from 'vue'; import { ref, onMounted, onUnmounted } from "vue";
import { useKeyNodeStore } from '@/store/keyNodeStore2'; import { useKeyNodeStore } from "@/store/keyNodeStore2";
import LeaderList from './KeyNodeRecognition2/components/LeaderList.vue'; import LeaderList from "./KeyNodeRecognition2/components/LeaderList.vue";
import GraphPanel from './KeyNodeRecognition2/components/GraphPanel.vue'; import GraphPanel from "./KeyNodeRecognition2/components/GraphPanel.vue";
import DetailsModal from './KeyNodeRecognition2/components/DetailsModal.vue'; import DetailsModal from "./KeyNodeRecognition2/components/DetailsModal.vue";
import LeaderDetailDialog from './KeyNodeRecognition2/components/LeaderDetailDialog.vue'; import LeaderDetailDialog from "./KeyNodeRecognition2/components/LeaderDetailDialog.vue";
import PostDetailDialog from './KeyNodeRecognition2/components/PostDetailDialog.vue'; import PostDetailDialog from "./KeyNodeRecognition2/components/PostDetailDialog.vue";
import LeaderAnalysis from "./KeyNodeRecognition2/components/LeaderAnalysis.vue"; import LeaderAnalysis from "./KeyNodeRecognition2/components/LeaderAnalysis.vue";
import EventHeatmap from "../components/weight/EventHeatmap.vue"; import EventHeatmap from "../components/weight/EventHeatmap.vue";
@ -267,4 +277,44 @@ const wordCloudData = ref([
.leader-containner2 { .leader-containner2 {
margin-top: 10px; margin-top: 10px;
} }
</style>
:deep(.leader-custom-dialog) {
width: 640px;
height: 680px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1) !important;
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
}
:deep(.leader-custom-dialog) .dialog-content {
width: 100%;
height: 100%;
padding: 23px 23px;
background-color: rgba(6, 45, 90, 1) !important;
}
:deep(.custom-leader-post-dialog) {
width: 640px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
z-index: 1;
}
:deep(.custom-leader-post-dialog) .dialog-content {
width: 100%;
padding: 25px 25px;
}
:deep(.custom-leader-post-dialog) .dialog-content .content {
color: #fff;
opacity: 0.7;
}
</style>

View File

@ -48,8 +48,7 @@
<div class="content"> <div class="content">
<!-- Title Image: using a placeholder as the local asset is not available --> <!-- Title Image: using a placeholder as the local asset is not available -->
<img <img
src="../../assets/images/chuanbofenxititle.png" src="../../../assets/images/chuanbo-show-title.png"
alt="传播意见领袖分析"
class="title-img" class="title-img"
style="margin-top: -22px; margin-left: -24px" style="margin-top: -22px; margin-left: -24px"
/> />
@ -89,7 +88,7 @@
</div> </div>
<!-- X-Axis Labels --> <!-- X-Axis Labels -->
<div class="x-axis-labels"> <div class="x-axis-labels">
<span v-for="n in 6" :key="n">{{ (n - 1) * 2 }}</span> <span v-for="n in 6" :key="n">{{ ((chart.max / 5) * (n - 1)).toFixed(0) }}</span>
<span class="unit">{{ chart.unit }}</span> <span class="unit">{{ chart.unit }}</span>
</div> </div>
</div> </div>
@ -108,30 +107,30 @@ const chartData = ref([
id: 1, id: 1,
title: "平均发帖数", title: "平均发帖数",
unit: "数量", unit: "数量",
max: 10, max: 20,
rows: [ rows: [
{ label: "领袖", value: 6.4, type: "leader" }, { label: "桥梁节点", value: 14.4, type: "leader" },
{ label: "所有用户", value: 0.46, type: "user" } { label: "其他节点", value: 5.2, type: "user" }
] ]
}, },
{ {
id: 2, id: 2,
title: "帖子平均生存周期", title: "帖子平均生存周期",
unit: "天数", unit: "分钟",
max: 10, max: 40,
rows: [ rows: [
{ label: "领袖", value: 2.73, type: "leader" }, { label: "桥梁节点", value: (2245.87 / 60).toFixed(2), type: "leader" },
{ label: "所有用户", value: 2.32, type: "user" } { label: "其他节点", value: (67.43 / 60).toFixed(2), type: "user" }
] ]
}, },
{ {
id: 3, id: 3,
title: "平均粉丝数", title: "平均粉丝数",
unit: "数", unit: "",
max: 10, max: 1200,
rows: [ rows: [
{ label: "领袖", value: 290.4, type: "leader", highlight: false }, { label: "桥梁节点", value: 1033, type: "leader", highlight: false },
{ label: "所有用户", value: 1.31, type: "user" } { label: "其他节点", value: 120, type: "user" }
] ]
} }
]); ]);

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog v-model="isVisible" width="640" align-center class="custom-dialog"> <el-dialog v-model="isVisible" width="640" align-center class="leader-custom-dialog">
<template v-if="store.activeLeader"> <template v-if="store.activeLeader">
<img src="@/assets/images/leaderDialogTitle.png" alt="" class="dialogTitleImg" /> <img src="@/assets/images/leaderDialogTitle.png" alt="" class="dialogTitleImg" />
<div class="dialog-content"> <div class="dialog-content">
@ -8,9 +8,7 @@
<div class="leaderInfo-message"> <div class="leaderInfo-message">
<div class="leader-name">{{ store.activeLeader.name }}</div> <div class="leader-name">{{ store.activeLeader.name }}</div>
<div class="leader-heat"> <div class="leader-heat">
<div class="fancy"> <div class="fancy">粉丝量:  {{ store.activeLeader.leaderOriginInfo.followers }}</div>
粉丝量:  {{ store.activeLeader.leaderOriginInfo.followers }}
</div>
<div class="post-number"> <div class="post-number">
发帖数:  {{ store.activeLeader.leaderOriginInfo.posts }} 发帖数:  {{ store.activeLeader.leaderOriginInfo.posts }}
</div> </div>
@ -75,9 +73,9 @@
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from "vue";
import { useKeyNodeStore } from '@/store/keyNodeStore2'; import { useKeyNodeStore } from "@/store/keyNodeStore2";
import { Icon } from '@iconify/vue'; import { Icon } from "@iconify/vue";
const store = useKeyNodeStore(); const store = useKeyNodeStore();
@ -92,25 +90,10 @@ const isVisible = computed({
</script> </script>
<style scoped> <style scoped>
:deep(.custom-dialog) {
width: 640px;
height: 680px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
}
.dialogTitleImg { .dialogTitleImg {
margin-top: -23px; margin-top: -23px;
} }
:deep(.custom-dialog) .dialog-content {
width: 100%;
height: 100%;
padding: 23px 23px;
}
.dialog-content-leaderInfo { .dialog-content-leaderInfo {
width: 100%; width: 100%;
height: 70px; height: 70px;
@ -209,4 +192,4 @@ const isVisible = computed({
margin-right: 10px; margin-right: 10px;
box-shadow: 0 4px 8px rgb(0, 123, 255); box-shadow: 0 4px 8px rgb(0, 123, 255);
} }
</style> </style>

View File

@ -1,31 +1,31 @@
<template> <template>
<el-dialog v-model="isVisible" width="640" align-center class="custom-leader-post-dialog"> <el-dialog v-model="isVisible" width="640" align-center class="custom-leader-post-dialog">
<template v-if="store.currentPost"> <img src="@/assets/images/head/post-dialog-title.png" alt="" class="postTitleImage" />
<img src="@/assets/images/head/post-dialog-title.png" alt="" class="postTitleImage" /> <div class="dialog-content">
<div class="dialog-content"> <div class="content">{{ store.currentPost.content }}</div>
<div class="content">{{ store.currentPost.content }}</div> <div class="heat">
<div class="heat"> <div class="item-heat-detail">
<div class="item-heat-detail"> <div class="item-heat-like">
<div class="item-heat-like"> <Icon icon="ei:like" width="25" height="25" /> {{ store.currentPost.like }}
<Icon icon="ei:like" width="25" height="25" /> {{ store.currentPost.like }} </div>
</div> <div class="item-heat-comment">
<div class="item-heat-comment"> <Icon icon="la:comment-dots" width="25" height="25" /> {{ store.currentPost.comment }}
<Icon icon="la:comment-dots" width="25" height="25" /> {{ store.currentPost.comment }} </div>
</div> <div class="item-heat-transmit">
<div class="item-heat-transmit"> <Icon icon="mdi:share-outline" width="25" height="25" /> {{
<Icon icon="mdi:share-outline" width="25" height="25" /> {{ store.currentPost.transmit }} store.currentPost.transmit
</div> }}
</div>
</div> </div>
</div> </div>
</template> </div>
</div>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from "vue";
import { useKeyNodeStore } from '@/store/keyNodeStore2'; import { useKeyNodeStore } from "@/store/keyNodeStore2";
import { Icon } from '@iconify/vue'; import { Icon } from "@iconify/vue";
const store = useKeyNodeStore(); const store = useKeyNodeStore();
@ -40,30 +40,14 @@ const isVisible = computed({
</script> </script>
<style scoped> <style scoped>
:deep(.custom-leader-post-dialog) {
width: 640px;
border-width: 0px, 0px, 0px, 0px;
border-style: solid;
border-image-source: linear-gradient(180deg, #3aa1f8 0%, rgba(58, 161, 248, 0.2) 100%);
background-color: rgba(6, 45, 90, 1);
border: 1px solid #1a8bff;
border-radius: 2px;
padding: 0 0;
z-index: 1;
}
:deep(.custom-leader-post-dialog) .dialog-content {
width: 100%;
padding: 25px 25px;
}
.content {
color: #fff;
opacity: 0.7;
}
.heat { .heat {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
margin-top: 20px; margin-top: 20px;
} }
.heat .item-heat-detail {
display: flex;
}
.heat .item-heat-detail div { .heat .item-heat-detail div {
width: 70px; width: 70px;
color: #fff; color: #fff;
@ -73,4 +57,4 @@ const isVisible = computed({
margin-top: -24px; margin-top: -24px;
margin-left: -2px; margin-left: -2px;
} }
</style> </style>

View File

@ -118,7 +118,6 @@ import node9_7 from "@/assets/images/qiaoliang/Change Zhang🇨🇦.png";
import node9_8 from "@/assets/images/qiaoliang/Andy C.png"; import node9_8 from "@/assets/images/qiaoliang/Andy C.png";
import node9_9 from "@/assets/images/qiaoliang/从头再来_congtouzailai.png.png"; import node9_9 from "@/assets/images/qiaoliang/从头再来_congtouzailai.png.png";
const emit = defineEmits(["handle:openDialog"]); const emit = defineEmits(["handle:openDialog"]);
const props = defineProps({ const props = defineProps({
timestamp: { timestamp: {
@ -249,7 +248,6 @@ const node9Followers = [
{ name: "从头再来_congtouzailai", avatar: node9_9 } { name: "从头再来_congtouzailai", avatar: node9_9 }
]; ];
const allLeaderData = ref(props.allLeaderData); const allLeaderData = ref(props.allLeaderData);
const chart = ref(null); const chart = ref(null);
const allGraphData = ref({ nodes: [], edges: [] }); const allGraphData = ref({ nodes: [], edges: [] });
@ -269,15 +267,30 @@ const getCircleAvatar = async (avatarUrl) => {
const initAllGraphData = async () => { const initAllGraphData = async () => {
const nodes = []; const nodes = [];
const edges = []; const edges = [];
const node2Avatars = [ const node2Avatars = [
node2_1, node2_2, node2_3, node2_4, node2_5, node2_1,
node2_6, node2_7, node2_8, node2_9, node2_10 node2_2,
node2_3,
node2_4,
node2_5,
node2_6,
node2_7,
node2_8,
node2_9,
node2_10
]; ];
const node2Names = [ const node2Names = [
'Andy Ngo', 'Andy Ngo', 'Ian Miles Cheong', '简爱_jianai', "Andy Ngo",
'唯有基督拯救人类!', 'wethenorth - 1984', 'Lomt Lea🅰', "Andy Ngo",
'Chad Felix Greene 🇮🇱', 'AlinaSalazarGongora', 'Gary Du' "Ian Miles Cheong",
"简爱_jianai",
"唯有基督拯救人类!",
"wethenorth - 1984",
"Lomt Lea🅰",
"Chad Felix Greene 🇮🇱",
"AlinaSalazarGongora",
"Gary Du"
]; ];
for (const [leaderIndex, leader] of allLeaderData.value.entries()) { for (const [leaderIndex, leader] of allLeaderData.value.entries()) {
@ -304,7 +317,7 @@ const initAllGraphData = async () => {
if (leaderIndex === 0 && i < node1Followers.length) { if (leaderIndex === 0 && i < node1Followers.length) {
const followerData = node1Followers[i]; const followerData = node1Followers[i];
const currentAvatar = followerData.avatar; const currentAvatar = followerData.avatar;
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
@ -320,11 +333,11 @@ const initAllGraphData = async () => {
// 1 // 1
else if (leaderIndex === 1 && i < 10) { else if (leaderIndex === 1 && i < 10) {
const currentAvatar = node2Avatars[i]; const currentAvatar = node2Avatars[i];
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: node2Names[i], name: node2Names[i],
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
@ -340,13 +353,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -357,13 +370,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -374,13 +387,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -391,13 +404,13 @@ const initAllGraphData = async () => {
const followerAvatar = await getCircleAvatar(currentAvatar); const followerAvatar = await getCircleAvatar(currentAvatar);
nodes.push({ nodes.push({
id: userId, id: userId,
name: followerData.name, name: followerData.name,
symbol: `image://${followerAvatar}`, symbol: `image://${followerAvatar}`,
symbolSize: 25, symbolSize: 25,
category: 1, category: 1,
value: "", value: "",
label: { show: false }, label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} }
@ -485,68 +498,66 @@ const initAllGraphData = async () => {
label: { show: false } label: { show: false }
}); });
nodeAdded = true; nodeAdded = true;
} } else {
else {
// 使 // 使
nodes.push({ // nodes.push({
id: userId, // id: userId,
name: `user ${i}`, // name: `user ${i}`,
symbol: "circle", // symbol: "circle",
symbolSize: 25, // symbolSize: 25,
category: 1, // category: 1,
value: "", // value: "",
label: { show: false }, // label: { show: false },
itemStyle: { // itemStyle: {
color: { // color: {
type: "linear", // type: "linear",
colorStops: [ // colorStops: [
{ offset: 0, color: "#035e96" }, // { offset: 0, color: "#035e96" },
{ offset: 1, color: "#34a7b0" } // { offset: 1, color: "#34a7b0" }
] // ]
} // }
} // }
}); // });
nodeAdded = true; nodeAdded = true;
} }
// //
if (nodeAdded) { if (nodeAdded) {
edges.push({ edges.push({
source: leader.id, source: leader.id,
target: userId target: userId
}); });
} }
} }
} }
// //
const splatteringCount = Math.floor(Math.random() * 11) + 10; // 10~20 // const splatteringCount = Math.floor(Math.random() * 11) + 10; // 10~20
for (let i = 0; i < splatteringCount; i++) { // for (let i = 0; i < splatteringCount; i++) {
const userId = `user_splattering_${i}`; // const userId = `user_splattering_${i}`;
nodes.push({ // nodes.push({
id: userId, // id: userId,
name: `user ${i}`, // name: `user ${i}`,
symbol: "circle", // symbol: "circle",
symbolSize: 25, // symbolSize: 25,
category: 1, // category: 1,
value: "", // value: "",
label: { show: false }, // label: { show: false },
itemStyle: { // itemStyle: {
color: { // color: {
type: "linear", // type: "linear",
colorStops: [ // colorStops: [
{ offset: 0, color: "#035e96" }, // { offset: 0, color: "#035e96" },
{ offset: 1, color: "#34a7b0" } // { offset: 1, color: "#34a7b0" }
] // ]
} // }
} // }
}); // });
} // }
allGraphData.value = { nodes, edges }; allGraphData.value = { nodes, edges };
console.log(allGraphData.value); console.log(allGraphData.value);
}; };
// timestamp // timestamp
const getVisibleGraphData = () => { const getVisibleGraphData = () => {
const leaders = allLeaderData.value.slice(0, props.timestamp); const leaders = allLeaderData.value.slice(0, props.timestamp);
@ -689,4 +700,4 @@ defineExpose({
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>