人物互动隐关系预测二级界面跳转,一级界面互动隐关系已标识
This commit is contained in:
parent
49b4f2edd0
commit
d79cb47cea
|
|
@ -1 +1 @@
|
|||
VITE_APP_BASE_API = "http://127.0.0.1:3000/api"
|
||||
VITE_APP_BASE_API = "http://10.7.1.183:8080/api"
|
||||
|
|
@ -5,12 +5,11 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, onMounted, ref, defineEmits } from "vue"
|
||||
import { onMounted, defineEmits, inject } from "vue"
|
||||
import * as echarts from "echarts"
|
||||
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
||||
import { inject } from "vue"
|
||||
let chart = null
|
||||
const emit = defineEmits(["click:node"])
|
||||
const emit = defineEmits(["click:node", "click:edge"])
|
||||
|
||||
const initChart = async () => {
|
||||
chart = echarts.init(document.getElementById("container"))
|
||||
|
|
@ -20,10 +19,32 @@ const initChart = async () => {
|
|||
id: parseInt(item.id),
|
||||
name: parseInt(item.id),
|
||||
isIncludePredictNodes: item.isIncludePredictNodes,
|
||||
nodesNum: item.nodesNum
|
||||
nodesNum: item.nodesNum,
|
||||
neighbors: item.neighbors.map((item) => ({ ...item, name: parseInt(item.id) })),
|
||||
category: item.isIncludePredictNodes ? 1 : 0
|
||||
}))
|
||||
|
||||
const links = [] //待处理
|
||||
//处理连边
|
||||
const links = []
|
||||
const edgeSet = new Set() //去除重复边
|
||||
nodes.forEach((communityNode) => {
|
||||
communityNode.neighbors.forEach((communityNei) => {
|
||||
const key = [communityNode.name, communityNei.name].sort().join("-")
|
||||
if (edgeSet.has(key)) return
|
||||
links.push({
|
||||
source: communityNode.name,
|
||||
target: communityNei.name,
|
||||
edge: communityNei.isHidden ? 1 : 0, //该边存在互动隐关系则权值为1,否则为0
|
||||
lineStyle: {
|
||||
width: communityNei.isHidden ? 4 : 1, // 无互动=细线,有互动=加粗
|
||||
color: communityNei.isHidden ? "#f8bf38" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
||||
type: communityNei.isHidden ? "dashed" : "solid", // 无互动=实线,有互动=虚线
|
||||
dashArray: [2, 1] // 2像素实线,1像素空白
|
||||
}
|
||||
})
|
||||
edgeSet.add(key)
|
||||
})
|
||||
})
|
||||
|
||||
const data = { nodes, links }
|
||||
|
||||
|
|
@ -84,7 +105,6 @@ const initChart = async () => {
|
|||
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") {
|
||||
|
|
@ -110,15 +130,14 @@ const initChart = async () => {
|
|||
return ""
|
||||
}
|
||||
},
|
||||
//权值设置样式
|
||||
// edgeLabel: {
|
||||
// show: false,
|
||||
// position: "middle",
|
||||
// formatter: function (params) {
|
||||
// return params.data.edge
|
||||
// },
|
||||
// fontSize: 14
|
||||
// },
|
||||
edgeLabel: {
|
||||
show: false,
|
||||
position: "middle",
|
||||
formatter: function (params) {
|
||||
return params.data.edge
|
||||
},
|
||||
fontSize: 14
|
||||
},
|
||||
emphasis: {
|
||||
edgeLabel: {
|
||||
show: true,
|
||||
|
|
@ -137,10 +156,11 @@ const initChart = async () => {
|
|||
animation: false,
|
||||
draggable: true,
|
||||
roam: true,
|
||||
zoom: 0.4,
|
||||
zoom: 0.3,
|
||||
categories: categories,
|
||||
|
||||
force: {
|
||||
edgeLength: 2000,
|
||||
edgeLength: 2500,
|
||||
repulsion: 4000,
|
||||
gravity: 0.4,
|
||||
friction: 0.02,
|
||||
|
|
@ -201,14 +221,20 @@ const initChart = async () => {
|
|||
}
|
||||
chart.setOption(option)
|
||||
}
|
||||
|
||||
const handleClickNode = () => {
|
||||
chart.on("click", function (params) {
|
||||
//params.data.name才是社团id
|
||||
if (params.dataType === "node") {
|
||||
emit("click:node", params.data)
|
||||
} else if (params.dataType == "edge") {
|
||||
const { data } = params
|
||||
if (data.edge) {
|
||||
emit("click:edge", data)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await initChart()
|
||||
handleClickNode()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<div class="detailNode-component">
|
||||
<img src="@/assets/images/icon/goback.png" alt="" class="goback" @click="handleGoback" />
|
||||
<div class="graph-container" id="container"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue"
|
||||
import { defineProps, defineEmits } from "vue"
|
||||
|
||||
const props = defineProps({
|
||||
detailNode: {
|
||||
|
|
@ -13,6 +14,10 @@ const props = defineProps({
|
|||
default: () => []
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(["click:goback"])
|
||||
const handleGoback = () => {
|
||||
emit("click:goback", "CommunityNode")
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
@ -20,6 +25,12 @@ const props = defineProps({
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.goback {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.graph-container {
|
||||
width: 100%;
|
||||
height: 93%;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
<div class="graph-component">
|
||||
<img :src="title" alt="" class="title" />
|
||||
|
||||
<CommunityNode v-if="curComponent == '社团节点'" @click:node="handleClickNode"></CommunityNode>
|
||||
<DetailNode v-else></DetailNode>
|
||||
<CommunityNode
|
||||
v-if="curComponent == 'CommunityNode'"
|
||||
@click:node="handleClickNode"
|
||||
@click:edge="handleClickEdge"
|
||||
></CommunityNode>
|
||||
<DetailNode v-else @click:goback="handleClickGoBack"></DetailNode>
|
||||
|
||||
<div class="time-axis"></div>
|
||||
</div>
|
||||
|
|
@ -15,7 +19,7 @@ import CommunityNode from "./communityNode.vue"
|
|||
import DetailNode from "./detailNode.vue"
|
||||
import { useCharacterInteractionStore } from "@/store/llinkPrediction/index"
|
||||
const interactionStore = useCharacterInteractionStore()
|
||||
const curComponent = ref("社团节点")
|
||||
const curComponent = ref("CommunityNode")
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
|
|
@ -27,6 +31,16 @@ const handleClickNode = (nodeInfo) => {
|
|||
interactionStore.initGraphCommunityDetailNode([nodeInfo.id])
|
||||
console.log(nodeInfo)
|
||||
}
|
||||
|
||||
const handleClickEdge = (edgeInfo) => {
|
||||
curComponent.value = "detailNode"
|
||||
const ids = [edgeInfo.source, edgeInfo.target]
|
||||
console.log(ids)
|
||||
}
|
||||
|
||||
const handleClickGoBack = (currentComponentName) => {
|
||||
curComponent.value = currentComponentName
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@ watch(riskEventIndex, (newIndex) => {
|
|||
const tooltipList = keyNodeStore.tooltipList
|
||||
|
||||
const highRiskCount = tooltipList[newIndex].filter((item) => item.riskType == "高风险").length
|
||||
console.log(tooltipList[newIndex])
|
||||
|
||||
keyNodeStore.statisticsList[5].number = highRiskCount
|
||||
keyNodeStore.statisticsList[4].number = tooltipList[newIndex].length
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,48 +6,48 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, onMounted, ref } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import { cropToCircleAsync } from "@/utils/transform";
|
||||
import anchorNeighbors from "@/assets/json/anchor_neighbors.json";
|
||||
import nodeHoverImg from "@/assets/images/nodeHover.png";
|
||||
import { useKeyNodeRecognitionStore } from "@/store/keyNodeRecognition/index";
|
||||
const keyNodeStore = useKeyNodeRecognitionStore();
|
||||
import { defineProps, onMounted, ref } from "vue"
|
||||
import * as echarts from "echarts"
|
||||
import { cropToCircleAsync } from "@/utils/transform"
|
||||
import anchorNeighbors from "@/assets/json/anchor_neighbors.json"
|
||||
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
||||
import { useKeyNodeRecognitionStore } from "@/store/keyNodeRecognition/index"
|
||||
const keyNodeStore = useKeyNodeRecognitionStore()
|
||||
const props = defineProps({
|
||||
communityNode: {
|
||||
type: Object,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(["click:openDialog", "click:goback"]);
|
||||
const detailContainer = ref(null);
|
||||
const currentSelectedCommunity = ref({});
|
||||
let chart = null;
|
||||
})
|
||||
const emit = defineEmits(["click:openDialog", "click:goback"])
|
||||
const detailContainer = ref(null)
|
||||
const currentSelectedCommunity = ref({})
|
||||
let chart = null
|
||||
|
||||
// 根据节点数量计算缩放比例
|
||||
const calculateInitialZoom = (nodes) => {
|
||||
const NODE_COUNT = nodes.length;
|
||||
if (NODE_COUNT > 1000) return 0.1;
|
||||
if (NODE_COUNT > 200) return 0.2;
|
||||
if (NODE_COUNT > 100) return 0.3;
|
||||
if (NODE_COUNT > 50) return 0.4;
|
||||
return 0.8; // 默认值
|
||||
};
|
||||
const NODE_COUNT = nodes.length
|
||||
if (NODE_COUNT > 1000) return 0.1
|
||||
if (NODE_COUNT > 200) return 0.2
|
||||
if (NODE_COUNT > 100) return 0.3
|
||||
if (NODE_COUNT > 50) return 0.4
|
||||
return 0.8 // 默认值
|
||||
}
|
||||
|
||||
const initChart = async () => {
|
||||
currentSelectedCommunity.value = props.communityNode;
|
||||
currentSelectedCommunity.value = props.communityNode
|
||||
|
||||
chart = echarts.init(document.getElementById("container"));
|
||||
chart = echarts.init(document.getElementById("container"))
|
||||
//处理节点
|
||||
//从锚点邻居数据集中查找出该锚点所有的邻居节点
|
||||
let nodes = [];
|
||||
let nodes = []
|
||||
|
||||
//批量将头像转换成base64
|
||||
const extraInfo = keyNodeStore.anchorExtraInfos;
|
||||
const keys = Object.keys(extraInfo);
|
||||
const extraInfo = keyNodeStore.anchorExtraInfos
|
||||
const keys = Object.keys(extraInfo)
|
||||
for (const key of keys) {
|
||||
const userInfo = extraInfo[key];
|
||||
userInfo.avatar = await cropToCircleAsync(userInfo.avatar);
|
||||
const userInfo = extraInfo[key]
|
||||
userInfo.avatar = await cropToCircleAsync(userInfo.avatar)
|
||||
}
|
||||
|
||||
//筛选出本社团中所有锚点的邻居节点并重新将需要补充的数据映射到该列表中
|
||||
|
|
@ -57,40 +57,40 @@ const initChart = async () => {
|
|||
...extraInfo[filteredList[0]],
|
||||
anchor: filteredList[0],
|
||||
neighbors: filteredList[1].map((neighNode) => ({ name: neighNode, avatar: "" }))
|
||||
}));
|
||||
}))
|
||||
|
||||
//处理连线
|
||||
let links = [];
|
||||
let links = []
|
||||
filterResult.forEach(({ anchor, neighbors }) => {
|
||||
(neighbors ?? []).forEach((neigh) => {
|
||||
links.push({ source: anchor, target: neigh.name });
|
||||
});
|
||||
});
|
||||
;(neighbors ?? []).forEach((neigh) => {
|
||||
links.push({ source: anchor, target: neigh.name })
|
||||
})
|
||||
})
|
||||
|
||||
// 合并所有锚点和邻居节点到一个数组并去重
|
||||
let nodeSet = new Set();
|
||||
let nodeSet = new Set()
|
||||
filterResult.forEach((item) => {
|
||||
//添加锚点自己
|
||||
if (!nodeSet.has(item.anchor)) {
|
||||
nodes.push({ name: item?.anchor, value: item?.anchor, category: 1, ...item });
|
||||
nodeSet.add(item?.anchor);
|
||||
nodes.push({ name: item?.anchor, value: item?.anchor, category: 1, ...item })
|
||||
nodeSet.add(item?.anchor)
|
||||
}
|
||||
//添加该锚点的邻居
|
||||
(item.neighbors || []).forEach((n) => {
|
||||
;(item.neighbors || []).forEach((n) => {
|
||||
if (!nodeSet.has(n?.name)) {
|
||||
nodes.push({ name: n.name, value: n.name, category: 0, avatar: n.avatar ?? "" });
|
||||
nodeSet.add(n.name);
|
||||
nodes.push({ name: n.name, value: n.name, category: 0, avatar: n.avatar ?? "" })
|
||||
nodeSet.add(n.name)
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
const data = { nodes, links };
|
||||
console.log(data);
|
||||
const data = { nodes, links }
|
||||
console.log(data)
|
||||
|
||||
const categories = [
|
||||
{ name: "邻居账号", category: 0 },
|
||||
{ name: "锚点账号", category: 1 }
|
||||
];
|
||||
]
|
||||
|
||||
const option = {
|
||||
//图例配置
|
||||
|
|
@ -178,7 +178,7 @@ const initChart = async () => {
|
|||
<div style="margin-left: 20px">粉丝数: ${params.data.fancy}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`
|
||||
} else if (params.dataType === "node" && !params.data.category) {
|
||||
return `<div
|
||||
style="
|
||||
|
|
@ -211,7 +211,7 @@ const initChart = async () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -273,29 +273,37 @@ const initChart = async () => {
|
|||
lineStyle: {
|
||||
color: "#37ACD7",
|
||||
width: 1
|
||||
},
|
||||
emphasis: {
|
||||
// 高亮配置
|
||||
focus: "adjacency", // 高亮相邻节点
|
||||
lineStyle: {
|
||||
// 高亮时线条样式
|
||||
width: 10 // 线条宽度(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
chart.setOption(option);
|
||||
};
|
||||
chart.setOption(option)
|
||||
}
|
||||
|
||||
const handleClickNode = () => {
|
||||
chart.on("click", function (params) {
|
||||
if (params.dataType == "node" && params.data.category == 1) {
|
||||
emit("click:openDialog", params.data);
|
||||
emit("click:openDialog", params.data)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const handleGoback = () => {
|
||||
emit("click:goback", "CommunityNode");
|
||||
};
|
||||
emit("click:goback", "CommunityNode")
|
||||
}
|
||||
onMounted(async () => {
|
||||
await initChart();
|
||||
handleClickNode();
|
||||
});
|
||||
await initChart()
|
||||
handleClickNode()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user