人物互动隐关系预测图一级界面,二级界面数据处理完成
This commit is contained in:
parent
924830e18d
commit
159966c580
BIN
src/assets/images/linkPrediction/icon/community-count-prefix.png
Normal file
BIN
src/assets/images/linkPrediction/icon/community-count-prefix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 347 B |
BIN
src/assets/images/linkPrediction/icon/hidden-count-prefix.png
Normal file
BIN
src/assets/images/linkPrediction/icon/hidden-count-prefix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 318 B |
BIN
src/assets/images/linkPrediction/icon/legend-icon.png
Normal file
BIN
src/assets/images/linkPrediction/icon/legend-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 147 B |
BIN
src/assets/images/linkPrediction/icon/node-count-prefix.png
Normal file
BIN
src/assets/images/linkPrediction/icon/node-count-prefix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
|
|
@ -14,6 +14,11 @@ export function getInteractionCommunityNodes() {
|
||||||
export function getInteractionCommunityDetailNodes(ids) {
|
export function getInteractionCommunityDetailNodes(ids) {
|
||||||
return http.get(`linkPrediction/interaction/community_detail?groupIds=${ids}`)
|
return http.get(`linkPrediction/interaction/community_detail?groupIds=${ids}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//人物互动隐关系预测的社团统计
|
||||||
|
export function getInteractionCommunityStatistics() {
|
||||||
|
return http.get(`linkPrediction/interaction/community_statistics`)
|
||||||
|
}
|
||||||
//社交紧密团体识别的用户组列表
|
//社交紧密团体识别的用户组列表
|
||||||
export function getGroupUserListFromTriangle() {
|
export function getGroupUserListFromTriangle() {
|
||||||
return http.get("/linkPrediction/triangle/group_list")
|
return http.get("/linkPrediction/triangle/group_list")
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,7 @@ export const useKeyNodeRecognitionStore = defineStore("keyNodeRecognition", {
|
||||||
backimg: high3Img,
|
backimg: high3Img,
|
||||||
riskType: "低风险",
|
riskType: "低风险",
|
||||||
chart: {
|
chart: {
|
||||||
predictedData: [12000, 12690, 12108, 10790, 9004, 8006, 7890, 11878],
|
predictedData: [12000, 12690, 12108, 10790, 9004, 5000, 4890, 3300],
|
||||||
realityData: [8959, 7460, 8334, 7902, 5753, 3070, "-", "-"]
|
realityData: [8959, 7460, 8334, 7902, 5753, 3070, "-", "-"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,14 @@ import {
|
||||||
getSocialPostList,
|
getSocialPostList,
|
||||||
getInteractionHiddenPostList,
|
getInteractionHiddenPostList,
|
||||||
getInteractionCommunityNodes,
|
getInteractionCommunityNodes,
|
||||||
getInteractionCommunityDetailNodes
|
getInteractionCommunityDetailNodes,
|
||||||
|
getInteractionCommunityStatistics
|
||||||
} from "@/service/api/linkPrediction"
|
} from "@/service/api/linkPrediction"
|
||||||
|
|
||||||
import defaultAvatar from "@/assets/images/avatar/default.png"
|
import defaultAvatar from "@/assets/images/avatar/default.png"
|
||||||
|
import nodePrefix from "@/assets/images/linkPrediction/icon/node-count-prefix.png"
|
||||||
|
import communityPrefix from "@/assets/images/linkPrediction/icon/community-count-prefix.png"
|
||||||
|
import hiddenPrefix from "@/assets/images/linkPrediction/icon/hidden-count-prefix.png"
|
||||||
|
|
||||||
export const useCharacterInteractionStore = defineStore("characterInteraction", {
|
export const useCharacterInteractionStore = defineStore("characterInteraction", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
|
|
@ -128,6 +132,12 @@ export const useCharacterInteractionStore = defineStore("characterInteraction",
|
||||||
{ text: "领土", top: 57.5, left: 72.5, width: 49, height: 19, fontSize: 12, opacity: 0.6 },
|
{ text: "领土", top: 57.5, left: 72.5, width: 49, height: 19, fontSize: 12, opacity: 0.6 },
|
||||||
{ text: "原则", top: 77.5, left: 264.5, width: 49, height: 19, fontSize: 12, opacity: 0.7 },
|
{ text: "原则", top: 77.5, left: 264.5, width: 49, height: 19, fontSize: 12, opacity: 0.7 },
|
||||||
{ text: "台湾", top: 195.5, left: 287.5, width: 49, height: 19, fontSize: 12, opacity: 0.8 }
|
{ text: "台湾", top: 195.5, left: 287.5, width: 49, height: 19, fontSize: 12, opacity: 0.8 }
|
||||||
|
],
|
||||||
|
|
||||||
|
statisticsList: [
|
||||||
|
{ id: 1, icon: nodePrefix, name: "节点数", key: "nodesCount" },
|
||||||
|
{ id: 2, icon: communityPrefix, name: "社团数", key: "groupCount" },
|
||||||
|
{ id: 3, icon: hiddenPrefix, name: "隐关系数", key: "hiddenInteractionCount" }
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -158,7 +168,15 @@ export const useCharacterInteractionStore = defineStore("characterInteraction",
|
||||||
},
|
},
|
||||||
async initGraphCommunityDetailNode(ids) {
|
async initGraphCommunityDetailNode(ids) {
|
||||||
const res = await getInteractionCommunityDetailNodes(ids)
|
const res = await getInteractionCommunityDetailNodes(ids)
|
||||||
console.log(res)
|
if (res.code != 200) return
|
||||||
|
return res.data
|
||||||
|
},
|
||||||
|
async initGraphStatistics() {
|
||||||
|
const res = await getInteractionCommunityStatistics()
|
||||||
|
this.statisticsList = this.statisticsList.map((item) => ({
|
||||||
|
...item,
|
||||||
|
count: res.data[item.key]
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
persist: true // 开启持久化
|
persist: true // 开启持久化
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,10 @@ onMounted(() => {
|
||||||
interactionStore.initGroupList()
|
interactionStore.initGroupList()
|
||||||
interactionStore.initInteractionPostList(1)
|
interactionStore.initInteractionPostList(1)
|
||||||
interactionStore.initGraphCommunityNode()
|
interactionStore.initGraphCommunityNode()
|
||||||
|
interactionStore.initGraphStatistics()
|
||||||
})
|
})
|
||||||
provide("communityNodeList", interactionStore.communityNodeList) // 提供数据
|
provide("communityNodeList", interactionStore.communityNodeList) // 提供数据
|
||||||
|
provide("statisticsList", interactionStore.statisticsList)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="communityNode-component">
|
<div class="communityNode-component">
|
||||||
<div class="graph-container" id="container"></div>
|
<div class="graph-container" id="container"></div>
|
||||||
|
<div class="statistic-container">
|
||||||
|
<div class="statistics-item" v-for="item in statisticsList" :key="item.id">
|
||||||
|
<img :src="item.icon" class="icon" />
|
||||||
|
<div class="name">{{ item.name }}: </div>
|
||||||
|
<div class="count">{{ item.count }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -10,6 +17,7 @@ import * as echarts from "echarts"
|
||||||
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
||||||
let chart = null
|
let chart = null
|
||||||
const emit = defineEmits(["click:node", "click:edge"])
|
const emit = defineEmits(["click:node", "click:edge"])
|
||||||
|
const statisticsList = inject("statisticsList")
|
||||||
|
|
||||||
const initChart = async () => {
|
const initChart = async () => {
|
||||||
chart = echarts.init(document.getElementById("container"))
|
chart = echarts.init(document.getElementById("container"))
|
||||||
|
|
@ -21,7 +29,10 @@ const initChart = async () => {
|
||||||
isIncludePredictNodes: item.isIncludePredictNodes,
|
isIncludePredictNodes: item.isIncludePredictNodes,
|
||||||
nodesNum: item.nodesNum,
|
nodesNum: item.nodesNum,
|
||||||
neighbors: item.neighbors.map((item) => ({ ...item, name: parseInt(item.id) })),
|
neighbors: item.neighbors.map((item) => ({ ...item, name: parseInt(item.id) })),
|
||||||
category: item.isIncludePredictNodes ? 1 : 0
|
category: item.isIncludePredictNodes ? 1 : 0,
|
||||||
|
selfIncludeImplicitRelationship:
|
||||||
|
item.neighbors.map((nei) => nei.id).includes(item.id) && //若该社团的id在该社团邻居中可以找到,说明自己这个社团中有互动隐关系
|
||||||
|
item.neighbors.find((nei) => nei.id == item.id).isHidden
|
||||||
}))
|
}))
|
||||||
|
|
||||||
//处理连边
|
//处理连边
|
||||||
|
|
@ -80,8 +91,8 @@ const initChart = async () => {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
right: 21,
|
right: 15,
|
||||||
bottom: 70,
|
bottom: 10,
|
||||||
icon: "circle",
|
icon: "circle",
|
||||||
orient: "vertical",
|
orient: "vertical",
|
||||||
itemWidth: 16,
|
itemWidth: 16,
|
||||||
|
|
@ -158,7 +169,6 @@ const initChart = async () => {
|
||||||
roam: true,
|
roam: true,
|
||||||
zoom: 0.3,
|
zoom: 0.3,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
|
|
||||||
force: {
|
force: {
|
||||||
edgeLength: 2500,
|
edgeLength: 2500,
|
||||||
repulsion: 4000,
|
repulsion: 4000,
|
||||||
|
|
@ -187,8 +197,8 @@ const initChart = async () => {
|
||||||
]),
|
]),
|
||||||
|
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
borderColor: "#46C6AD",
|
borderColor: node.selfIncludeImplicitRelationship ? "#f8bf38" : "#46C6AD",
|
||||||
borderWidth: 1,
|
borderWidth: node.selfIncludeImplicitRelationship ? 3 : 1,
|
||||||
shadowBlur: 4,
|
shadowBlur: 4,
|
||||||
borderType: "dashed",
|
borderType: "dashed",
|
||||||
shadowColor: "rgba(19, 27, 114, 0.25)"
|
shadowColor: "rgba(19, 27, 114, 0.25)"
|
||||||
|
|
@ -250,5 +260,49 @@ onMounted(async () => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 93%;
|
height: 93%;
|
||||||
}
|
}
|
||||||
|
.statistic-container {
|
||||||
|
width: 378px;
|
||||||
|
height: 42px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #3aa1f8;
|
||||||
|
background: linear-gradient(270deg, rgba(0, 82, 125, 0.48) 0%, rgba(0, 200, 255, 0.23) 100%);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 48px;
|
||||||
|
left: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.statistics-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
color: rgba(255, 255, 255, 0.76);
|
||||||
|
text-align: center;
|
||||||
|
font-family: OPPOSans;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
.count {
|
||||||
|
color: #fff;
|
||||||
|
font-family: D-DIN;
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: normal;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,232 @@
|
||||||
<div class="detailNode-component">
|
<div class="detailNode-component">
|
||||||
<img src="@/assets/images/icon/goback.png" alt="" class="goback" @click="handleGoback" />
|
<img src="@/assets/images/icon/goback.png" alt="" class="goback" @click="handleGoback" />
|
||||||
<div class="graph-container" id="container"></div>
|
<div class="graph-container" id="container"></div>
|
||||||
|
<div class="statistic-container">
|
||||||
|
<div class="statistics-item" v-for="item in statisticsList" :key="item.id">
|
||||||
|
<img :src="item.icon" class="icon" />
|
||||||
|
<div class="name">{{ item.name }}: </div>
|
||||||
|
<div class="count">{{ item.count }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="time-axis"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from "vue"
|
import nodePrefix from "@/assets/images/linkPrediction/icon/node-count-prefix.png"
|
||||||
|
import communityPrefix from "@/assets/images/linkPrediction/icon/community-count-prefix.png"
|
||||||
|
import hiddenPrefix from "@/assets/images/linkPrediction/icon/hidden-count-prefix.png"
|
||||||
|
import { defineProps, defineEmits, onMounted, ref } from "vue"
|
||||||
|
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
||||||
|
import * as echarts from "echarts"
|
||||||
|
const statisticsList = [
|
||||||
|
{ id: 1, icon: nodePrefix, name: "节点数", key: "nodesCount", count: 3000 },
|
||||||
|
{ id: 2, icon: communityPrefix, name: "社团数", key: "groupCount", count: 1000 },
|
||||||
|
{ id: 3, icon: hiddenPrefix, name: "隐关系数", key: "hiddenInteractionCount", count: 1000 }
|
||||||
|
]
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
detailNode: {
|
detailNode: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const curDetailNode = ref(props.detailNode)
|
||||||
|
|
||||||
const emit = defineEmits(["click:goback"])
|
const emit = defineEmits(["click:goback"])
|
||||||
const handleGoback = () => {
|
const handleGoback = () => {
|
||||||
emit("click:goback", "CommunityNode")
|
emit("click:goback", "CommunityNode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let chart = null
|
||||||
|
|
||||||
|
const initChart = async () => {
|
||||||
|
chart = echarts.init(document.getElementById("container"))
|
||||||
|
|
||||||
|
const links = []
|
||||||
|
const nodes = []
|
||||||
|
if (!Object.keys(curDetailNode.value).length) return
|
||||||
|
Object.entries(curDetailNode.value).forEach(([parentId, children]) => {
|
||||||
|
nodes.push({
|
||||||
|
id: `parent_${parentId}`,
|
||||||
|
name: parentId
|
||||||
|
})
|
||||||
|
children.forEach((child) => {
|
||||||
|
if (!nodes.some((n) => n.id === child.id)) {
|
||||||
|
nodes.push(child)
|
||||||
|
}
|
||||||
|
links.push({
|
||||||
|
source: `parent_${parentId}`,
|
||||||
|
target: child.id,
|
||||||
|
edge: child.isHidden ? 1 : 0,
|
||||||
|
interactionTimes: child.interactionTime
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const data = { links, nodes }
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
{ name: "事件活跃者", category: 0 },
|
||||||
|
{ name: "信息发布者", category: 1 },
|
||||||
|
{ name: "互动关系", category: 2 },
|
||||||
|
{ name: "互动隐关系", category: 3 }
|
||||||
|
]
|
||||||
|
const option = {
|
||||||
|
//图例配置
|
||||||
|
legend: [
|
||||||
|
{
|
||||||
|
data: categories.map((c) => ({
|
||||||
|
name: c.name,
|
||||||
|
itemStyle: {
|
||||||
|
color:
|
||||||
|
c.category === 0
|
||||||
|
? new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||||
|
{ offset: 1, color: "#1a3860" }, // 深蓝渐变
|
||||||
|
{ offset: 0.5, color: "#38546b" },
|
||||||
|
{ offset: 0, color: "#5fb3b3" }
|
||||||
|
])
|
||||||
|
: c.category === 1
|
||||||
|
? new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||||
|
{ offset: 0, color: "#9eec9c" }, // 绿色渐变
|
||||||
|
{ offset: 0.37, color: "#aef295" },
|
||||||
|
{ offset: 1, color: "#c2f989" }
|
||||||
|
])
|
||||||
|
: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||||
|
{ offset: 0, color: "#ff9a9e" }, // 红色渐变(用于 category === 2)
|
||||||
|
{ offset: 0.5, color: "#fad0c4" },
|
||||||
|
{ offset: 1, color: "#fbc2eb" }
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
right: 21,
|
||||||
|
bottom: 70,
|
||||||
|
icon: "circle",
|
||||||
|
orient: "vertical",
|
||||||
|
itemWidth: 16,
|
||||||
|
itemHeight: 16,
|
||||||
|
itemGap: 12,
|
||||||
|
backgroundColor: "rgba(0,67,125,0.56)", // 半透明深蓝
|
||||||
|
borderRadius: 8, // 圆角
|
||||||
|
borderColor: "#c2f2ff", // 淡蓝色边框
|
||||||
|
borderWidth: 0.3,
|
||||||
|
padding: [12, 20, 12, 20], // 上右下左
|
||||||
|
textStyle: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "normal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
//hover上去的窗口
|
||||||
|
tooltip: {
|
||||||
|
trigger: "item",
|
||||||
|
backgroundColor: "rgba(0,0,0,0)", // 透明背景
|
||||||
|
borderColor: "rgba(0,0,0,0)", // 透明边框
|
||||||
|
borderWidth: 0,
|
||||||
|
extraCssText: "box-shadow:none;padding:0;",
|
||||||
|
formatter: function (params) {
|
||||||
|
if (params.dataType === "node") {
|
||||||
|
return `<div
|
||||||
|
style="
|
||||||
|
padding:10px 15px;
|
||||||
|
height: 68px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: url('${nodeHoverImg}');
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
">
|
||||||
|
<div style="color:#fff;letter-spacing: 0.14px;">
|
||||||
|
<div >用户ID:${params.data.id}</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edgeLabel: {
|
||||||
|
show: false,
|
||||||
|
position: "middle",
|
||||||
|
formatter: function (params) {
|
||||||
|
return params.data.edge
|
||||||
|
},
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
edgeLabel: {
|
||||||
|
show: true,
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 18,
|
||||||
|
textShadowColor: "#fff",
|
||||||
|
textShadowBlur: 0,
|
||||||
|
textShadowOffsetX: 0,
|
||||||
|
textShadowOffsetY: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "graph",
|
||||||
|
layout: "force",
|
||||||
|
animation: false,
|
||||||
|
draggable: true,
|
||||||
|
roam: true,
|
||||||
|
zoom: 0.3,
|
||||||
|
categories: categories,
|
||||||
|
|
||||||
|
force: {
|
||||||
|
edgeLength: 2500,
|
||||||
|
repulsion: 4000,
|
||||||
|
gravity: 0.4,
|
||||||
|
friction: 0.02,
|
||||||
|
coolingFactor: 0.1
|
||||||
|
},
|
||||||
|
|
||||||
|
animationDurationUpdate: 3500, // 节点移动更平滑
|
||||||
|
data: data.nodes.map((node) => ({
|
||||||
|
...node,
|
||||||
|
symbolSize: 40,
|
||||||
|
itemStyle: {
|
||||||
|
color: new echarts.graphic.RadialGradient(0.98, 0.38, 0.9, [
|
||||||
|
{ offset: 1, color: "#1a3860" }, // 最左侧
|
||||||
|
{ offset: 0.5, color: "#38546b" }, // 中间
|
||||||
|
{ offset: 0, color: "#5fb3b3" } // 最右侧
|
||||||
|
]),
|
||||||
|
|
||||||
|
opacity: 1,
|
||||||
|
borderColor: "#46C6AD",
|
||||||
|
borderWidth: 1,
|
||||||
|
shadowBlur: 4,
|
||||||
|
borderType: "dashed",
|
||||||
|
shadowColor: "rgba(19, 27, 114, 0.25)"
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
shadowBlur: 20,
|
||||||
|
shadowColor: "#c4a651",
|
||||||
|
borderColor: "#fcd267",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderType: "solid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
links: data.links,
|
||||||
|
lineStyle: {
|
||||||
|
color: "#37ACD7",
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
chart.setOption(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initChart()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
@ -27,7 +237,7 @@ const handleGoback = () => {
|
||||||
position: relative;
|
position: relative;
|
||||||
.goback {
|
.goback {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: -25px;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
@ -35,5 +245,60 @@ const handleGoback = () => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 93%;
|
height: 93%;
|
||||||
}
|
}
|
||||||
|
.statistic-container {
|
||||||
|
width: 378px;
|
||||||
|
height: 42px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #3aa1f8;
|
||||||
|
background: linear-gradient(270deg, rgba(0, 82, 125, 0.48) 0%, rgba(0, 200, 255, 0.23) 100%);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 105px;
|
||||||
|
left: 21px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.statistics-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
color: rgba(255, 255, 255, 0.76);
|
||||||
|
text-align: center;
|
||||||
|
font-family: OPPOSans;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
.count {
|
||||||
|
color: #fff;
|
||||||
|
font-family: D-DIN;
|
||||||
|
font-size: 15px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: normal;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.time-axis {
|
||||||
|
width: 95%;
|
||||||
|
height: 42px;
|
||||||
|
border: 1px solid #3aa1f8;
|
||||||
|
background: linear-gradient(270deg, rgba(0, 82, 125, 0.48) 0%, rgba(0, 200, 255, 0.23) 100%);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
bottom: 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,22 @@
|
||||||
@click:node="handleClickNode"
|
@click:node="handleClickNode"
|
||||||
@click:edge="handleClickEdge"
|
@click:edge="handleClickEdge"
|
||||||
></CommunityNode>
|
></CommunityNode>
|
||||||
<DetailNode v-else @click:goback="handleClickGoBack"></DetailNode>
|
<DetailNode
|
||||||
|
v-else
|
||||||
<div class="time-axis"></div>
|
@click:goback="handleClickGoBack"
|
||||||
|
:detailNode="curSelectedGroup"
|
||||||
|
></DetailNode>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, onMounted, ref } from "vue"
|
import { defineProps, ref } from "vue"
|
||||||
import CommunityNode from "./communityNode.vue"
|
import CommunityNode from "./communityNode.vue"
|
||||||
import DetailNode from "./detailNode.vue"
|
import DetailNode from "./detailNode.vue"
|
||||||
import { useCharacterInteractionStore } from "@/store/llinkPrediction/index"
|
import { useCharacterInteractionStore } from "@/store/llinkPrediction/index"
|
||||||
const interactionStore = useCharacterInteractionStore()
|
const interactionStore = useCharacterInteractionStore()
|
||||||
const curComponent = ref("CommunityNode")
|
const curComponent = ref("CommunityNode")
|
||||||
|
const curSelectedGroup = ref(null)
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -27,19 +30,24 @@ const props = defineProps({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleClickNode = (nodeInfo) => {
|
const handleClickNode = async (nodeInfo) => {
|
||||||
interactionStore.initGraphCommunityDetailNode([nodeInfo.id])
|
const data = await interactionStore.initGraphCommunityDetailNode([nodeInfo.id])
|
||||||
console.log(nodeInfo)
|
curSelectedGroup.value = data
|
||||||
|
curComponent.value = "detailNode"
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickEdge = (edgeInfo) => {
|
const handleClickEdge = async (edgeInfo) => {
|
||||||
curComponent.value = "detailNode"
|
curComponent.value = "detailNode"
|
||||||
const ids = [edgeInfo.source, edgeInfo.target]
|
const data = await interactionStore.initGraphCommunityDetailNode([
|
||||||
console.log(ids)
|
edgeInfo.source,
|
||||||
|
edgeInfo.target
|
||||||
|
])
|
||||||
|
curSelectedGroup.value = data
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickGoBack = (currentComponentName) => {
|
const handleClickGoBack = (currentComponentName) => {
|
||||||
curComponent.value = currentComponentName
|
curComponent.value = currentComponentName
|
||||||
|
console.log(currentComponentName)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -55,18 +63,5 @@ const handleClickGoBack = (currentComponentName) => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 93%;
|
height: 93%;
|
||||||
}
|
}
|
||||||
.time-axis {
|
|
||||||
width: 95%;
|
|
||||||
height: 42px;
|
|
||||||
border: 1px solid #3aa1f8;
|
|
||||||
background: linear-gradient(270deg, rgba(0, 82, 125, 0.48) 0%, rgba(0, 200, 255, 0.23) 100%);
|
|
||||||
backdrop-filter: blur(3px);
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
border-radius: 4px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -203,111 +203,111 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch, nextTick } from "vue";
|
import { computed, ref, watch, nextTick } from "vue"
|
||||||
import UserPanel from "./components/userPanel.vue";
|
import UserPanel from "./components/userPanel.vue"
|
||||||
import UserChart from "./components/userChart.vue";
|
import UserChart from "./components/userChart.vue"
|
||||||
import AnchorGraph from "./components/anchorGraph.vue";
|
import AnchorGraph from "./components/anchorGraph.vue"
|
||||||
import AnchorPost from "./components/anchorPost.vue";
|
import AnchorPost from "./components/anchorPost.vue"
|
||||||
import AttentionTopic from "./components/attentionTopic.vue";
|
import AttentionTopic from "./components/attentionTopic.vue"
|
||||||
import Monitoring from "./components/monitoring.vue";
|
import Monitoring from "./components/monitoring.vue"
|
||||||
import { Icon } from "@iconify/vue";
|
import { Icon } from "@iconify/vue"
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts"
|
||||||
|
|
||||||
import { useKeyNodeRecognitionStore } from "@/store/keyNodeRecognition/index";
|
import { useKeyNodeRecognitionStore } from "@/store/keyNodeRecognition/index"
|
||||||
const KeyNodeOneStore = useKeyNodeRecognitionStore();
|
const KeyNodeOneStore = useKeyNodeRecognitionStore()
|
||||||
|
|
||||||
//控制弹窗
|
//控制弹窗
|
||||||
const postDialog = ref(false);
|
const postDialog = ref(false)
|
||||||
const topicDialog = ref(false);
|
const topicDialog = ref(false)
|
||||||
const anchorDialog = ref(false);
|
const anchorDialog = ref(false)
|
||||||
|
|
||||||
//当前选中的贴文数据
|
//当前选中的贴文数据
|
||||||
const currentPostPost = ref(null);
|
const currentPostPost = ref(null)
|
||||||
|
|
||||||
//当前选中的热度事件
|
//当前选中的热度事件
|
||||||
const currentTopic = ref(null);
|
const currentTopic = ref(null)
|
||||||
|
|
||||||
//当前选中的锚点数据
|
//当前选中的锚点数据
|
||||||
const currentSelectedAnchorItem = ref(null);
|
const currentSelectedAnchorItem = ref(null)
|
||||||
//筛选tabs展示条件
|
//筛选tabs展示条件
|
||||||
const filterShowUserList = computed(() => {
|
const filterShowUserList = computed(() => {
|
||||||
if (KeyNodeOneStore.currentTabType == "全部") {
|
if (KeyNodeOneStore.currentTabType == "全部") {
|
||||||
return KeyNodeOneStore.mediaData;
|
return KeyNodeOneStore.mediaData
|
||||||
} else {
|
} else {
|
||||||
return KeyNodeOneStore.mediaData.filter((item) => item.type === KeyNodeOneStore.currentTabType);
|
return KeyNodeOneStore.mediaData.filter((item) => item.type === KeyNodeOneStore.currentTabType)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const contentType = computed(() => {
|
const contentType = computed(() => {
|
||||||
return (content) => {
|
return (content) => {
|
||||||
if (content.startsWith("//@")) return "转发";
|
if (content.startsWith("//@")) return "转发"
|
||||||
return "原发";
|
return "原发"
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
//直接截取前两个关键用户
|
//直接截取前两个关键用户
|
||||||
const cutOutTheFirstTwo = computed(() => {
|
const cutOutTheFirstTwo = computed(() => {
|
||||||
return (keyUserString) => {
|
return (keyUserString) => {
|
||||||
return keyUserString.split(",").slice(0, 2).join(",");
|
return keyUserString.split(",").slice(0, 2).join(",")
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(filterShowUserList, (newList) => {
|
watch(filterShowUserList, (newList) => {
|
||||||
if (!newList?.length) return;
|
if (!newList?.length) return
|
||||||
|
|
||||||
const { anchorChartInfoList } = KeyNodeOneStore;
|
const { anchorChartInfoList } = KeyNodeOneStore
|
||||||
const find = (name) => anchorChartInfoList.find((item) => item.name === name);
|
const find = (name) => anchorChartInfoList.find((item) => item.name === name)
|
||||||
|
|
||||||
// 安全数值转换函数
|
// 安全数值转换函数
|
||||||
const safeNumber = (val) => (isNaN(+val) ? 0 : +val);
|
const safeNumber = (val) => (isNaN(+val) ? 0 : +val)
|
||||||
|
|
||||||
// 更新各项数据
|
// 更新各项数据
|
||||||
find("锚点数量").number = newList.length;
|
find("锚点数量").number = newList.length
|
||||||
|
|
||||||
find("平均粉丝数量").number = `${(
|
find("平均粉丝数量").number = `${(
|
||||||
newList.reduce((acc, cur) => acc + safeNumber(cur.number?.replace("w", "")), 0) / newList.length
|
newList.reduce((acc, cur) => acc + safeNumber(cur.number?.replace("w", "")), 0) / newList.length
|
||||||
).toFixed(1)}w`;
|
).toFixed(1)}w`
|
||||||
|
|
||||||
find("平均发帖频率").number = `${Math.round(
|
find("平均发帖频率").number = `${Math.round(
|
||||||
newList.reduce((acc, cur) => acc + safeNumber(cur.transmit?.replace("h/1次", "")), 0) /
|
newList.reduce((acc, cur) => acc + safeNumber(cur.transmit?.replace("h/1次", "")), 0) /
|
||||||
newList.length
|
newList.length
|
||||||
)}h/1次`;
|
)}h/1次`
|
||||||
|
|
||||||
find("平均参与互动次数").number = Math.floor(
|
find("平均参与互动次数").number = Math.floor(
|
||||||
newList.reduce((acc, cur) => acc + safeNumber(cur.interaction), 0) / newList.length
|
newList.reduce((acc, cur) => acc + safeNumber(cur.interaction), 0) / newList.length
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
const handleSwitchTab = (tabName) => {
|
const handleSwitchTab = (tabName) => {
|
||||||
KeyNodeOneStore.currentTabType = tabName;
|
KeyNodeOneStore.currentTabType = tabName
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSwitchChartTab = (tabName) => {
|
const handleSwitchChartTab = (tabName) => {
|
||||||
KeyNodeOneStore.currentChartTabType = tabName;
|
KeyNodeOneStore.currentChartTabType = tabName
|
||||||
};
|
}
|
||||||
|
|
||||||
const handlePostDialog = (post) => {
|
const handlePostDialog = (post) => {
|
||||||
postDialog.value = true;
|
postDialog.value = true
|
||||||
currentPostPost.value = post;
|
currentPostPost.value = post
|
||||||
console.log(post);
|
console.log(post)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleGotTopicDialog = (topic) => {
|
const handleGotTopicDialog = (topic) => {
|
||||||
topicDialog.value = true;
|
topicDialog.value = true
|
||||||
currentTopic.value = topic;
|
currentTopic.value = topic
|
||||||
console.log(topic);
|
console.log(topic)
|
||||||
};
|
}
|
||||||
|
|
||||||
let hotChartInstance = null;
|
let hotChartInstance = null
|
||||||
|
|
||||||
const renderHotChart = () => {
|
const renderHotChart = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const chartDom = document.getElementById("hotChart");
|
const chartDom = document.getElementById("hotChart")
|
||||||
if (!chartDom) return;
|
if (!chartDom) return
|
||||||
if (hotChartInstance) {
|
if (hotChartInstance) {
|
||||||
hotChartInstance.dispose();
|
hotChartInstance.dispose()
|
||||||
}
|
}
|
||||||
hotChartInstance = echarts.init(chartDom);
|
hotChartInstance = echarts.init(chartDom)
|
||||||
const option = {
|
const option = {
|
||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
tooltip: { trigger: "axis" },
|
tooltip: { trigger: "axis" },
|
||||||
|
|
@ -324,7 +324,7 @@ const renderHotChart = () => {
|
||||||
axisLine: { show: false },
|
axisLine: { show: false },
|
||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
axisLabel: { color: "#B6D6F7", fontSize: 13 },
|
axisLabel: { color: "#B6D6F7", fontSize: 13 },
|
||||||
data: ["7.18", "7.19", "7.20", "7.21", "7.22", "7.23", "7.24", "7.25"]
|
data: ["6.25", "6.26", "6.27", "6.28", "6.29", "6.30", "7.1", "7.2"]
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
|
|
@ -370,26 +370,26 @@ const renderHotChart = () => {
|
||||||
padding: [0, 0, 0, 30]
|
padding: [0, 0, 0, 30]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
}
|
||||||
hotChartInstance.setOption(option);
|
hotChartInstance.setOption(option)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOpenAnchorDialog = (params) => {
|
const handleOpenAnchorDialog = (params) => {
|
||||||
anchorDialog.value = params.anchorDialog;
|
anchorDialog.value = params.anchorDialog
|
||||||
currentSelectedAnchorItem.value = params.currentSelectAnchorNode;
|
currentSelectedAnchorItem.value = params.currentSelectAnchorNode
|
||||||
console.log(currentSelectedAnchorItem.value);
|
console.log(currentSelectedAnchorItem.value)
|
||||||
};
|
}
|
||||||
watch(topicDialog, (val) => {
|
watch(topicDialog, (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
renderHotChart();
|
renderHotChart()
|
||||||
} else {
|
} else {
|
||||||
if (hotChartInstance) {
|
if (hotChartInstance) {
|
||||||
hotChartInstance.dispose();
|
hotChartInstance.dispose()
|
||||||
hotChartInstance = null;
|
hotChartInstance = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user