群体演化分析关系图组件封装完毕

This commit is contained in:
qumeng039@126.com 2025-08-07 19:11:52 +08:00
parent f24cce9202
commit 3c6a07f45c
4 changed files with 109 additions and 107 deletions

View File

@ -12,10 +12,7 @@ import {
getGroupEvolutionGroupScaleChart,
getGroupEvolutionTimeLine,
getPostByUtcTime,
getStructuralEvolutionChart,
getGroupMemberInfoList,
getGroupMemberTimeLine,
getGroupMemberChart,
@ -240,8 +237,7 @@ export const useGroupDiscoveryStore = defineStore("groupDiscovery", {
return {
id: node.name,
label: node.name,
color: setColor(node.groupId),
cluster: parseInt(node.groupId)
type: node.groupId
}
})
},
@ -265,21 +261,21 @@ export const useGroupStructureStore = defineStore("groupStructure", {
type: "群体一",
focusedTopic: "#中国海警首次登检菲律宾运补船只",
innerNum: 20,
outerNum: 100,
outerNum: 100
},
{
id: 2,
type: "群体二",
focusedTopic: "#外交部回应中国海警缴获菲土乓枪支",
innerNum: 20,
outerNum: 100,
outerNum: 100
},
{
id: 3,
type: "群体三",
focusedTopic: "#社会群体对中国海警缴获菲土乓枪支",
innerNum: 20,
outerNum: 100,
outerNum: 100
}
],
chartData: {
@ -290,7 +286,7 @@ export const useGroupStructureStore = defineStore("groupStructure", {
data: [0.3055, 0.3939, 0.5813, 0.6094, 0.6289],
name: "",
themeColor: "#2AB8FD"
},
}
]
},
chartsData: {
@ -541,7 +537,7 @@ export const useGroupStructureStore = defineStore("groupStructure", {
async initializeStructuralEvolutionChart() {
const res = await getStructuralEvolutionChart()
this.chartData = {
xAxisData: res.data.xaxisData.map(item => utcStringToHHMMSS(item)),
xAxisData: res.data.xaxisData.map((item) => utcStringToHHMMSS(item)),
seriesList: [
{
name: "",
@ -549,7 +545,7 @@ export const useGroupStructureStore = defineStore("groupStructure", {
themeColor: "#2AB8FD"
}
],
yAxisRange: {min: 0, max: 1, interval: 0.2},
yAxisRange: { min: 0, max: 1, interval: 0.2 }
}
}
},

View File

@ -54,18 +54,18 @@ export const paintLineFunction = function (ctx, needHideText) {
this.drawOriginalLine(ctx, needHideText) //内置默认的绘制方法
//指定路径,用于鼠标检测选中
// this.path = [
// { x: this.source.cx, y: this.source.cy },
// { x: this.target.cx, y: this.target.cy }
// ];
this.path = [
{ x: this.source.cx, y: this.source.cy },
{ x: this.target.cx, y: this.target.cy }
]
// //绘制路径
// ctx.beginPath();
// ctx.moveTo(this.source.cx,this.source.cy);
// ctx.lineTo(this.target.cx,this.target.cy);
// this.setLineStyle(ctx);
// ctx.stroke();
ctx.beginPath()
ctx.moveTo(this.source.cx, this.source.cy)
ctx.lineTo(this.target.cx, this.target.cy)
this.setLineStyle(ctx)
ctx.stroke()
// //绘制连线上文字(内部方法)
// this.paintTextOnLineWithAngle(ctx, this.source, this.target);
this.paintTextOnLineWithAngle(ctx, this.source, this.target)
}

View File

@ -6,7 +6,7 @@
<TimeAxis
v-if="timeList.length"
:time-list="timeList"
:is-auto-play="false"
:is-auto-play="true"
:start-time="new Date(timeList[0])"
:end-time="new Date(timeList[timeList.length - 1])"
@click:pointerDown="handlePointerDown"
@ -21,7 +21,6 @@ import { defineProps, defineEmits, onUnmounted, ref, toRaw, watch } from "vue"
import { storeToRefs } from "pinia"
import { convertToUtcIsoString } from "@/utils/transform"
import { paintNodeFunction, paintLineFunction } from "@/utils/customePaint"
import { demoData } from "@/assets/customeGraph/data"
import TimeAxis from "@/components/timeAxis.vue"
import GraphVis from "@/assets/package/graphvis.esm.min.js"
const props = defineProps({
@ -45,7 +44,7 @@ const defaultConfig = {
},
shape: "circle",
size: 30,
color: "120,120,240",
color: (node) => colorMap[node.cluster],
borderColor: "200,50,50",
borderWidth: 0,
selected: {
@ -129,73 +128,85 @@ const registEvents = () => {
})
}
const runForceLayout = () => {
let viewCenter = graphVis.getViewCenter()
let simulation = graphVis.getSimulationLayout()
let curForceSimulator = simulation.forceSimulation()
curForceSimulator
.nodes(graphVis.nodes)
.force("center", simulation.forceCenter(viewCenter.x, viewCenter.y))
.force("charge", simulation.forceManyBody().strength(-550).theta(0.85)) // manyBodyReuse|forceManyBody .distanceMin(300).distanceMax(400).theta(0.9)
.force("link", simulation.forceLink(graphVis.links).distance(120).strength(0.35)) //.distance(120).strength(0.15)
.force(
"collide",
simulation.forceCollide().radius((d) => d.radius + 5)
) //.iterations(5)
.force("x", simulation.forceX())
.force("y", simulation.forceY())
.alphaDecay(0.02) // alpha .1500.0228
//.alphaMin(0.005) // [0, 1] min alpha 0.001. 仿 alpha alpha alpha
.velocityDecay(0.15) // 0.4,使使
curForceSimulator.alpha(1).restart()
curForceSimulator.on("tick", () => {
if (graphVis) {
graphVis.refreshView() //
const layoutConfig = { strength: -500, ajustCluster: true }
//
graphVis.excuteWorkerLayout(
graphVis.getGraphData(),
"simulation",
layoutConfig,
false,
function () {
graphVis.zoomFit() //
}
})
curForceSimulator.on("end", () => {
curForceSimulator.alpha(0)
curForceSimulator.stop()
})
forceSimulator = curForceSimulator
)
}
// cluster
const clusterAnalyze = () => {
graphVis.removeAllGroup() //
// clusternodes
const clusterNodesMap = new Map()
graphVis.nodes.forEach((node) => {
const cluster = parseInt(node.type)
if (!clusterNodesMap.has(cluster)) {
clusterNodesMap.set(cluster, [])
}
clusterNodesMap.get(cluster).push(node)
})
const colorMap = {
0: "50,141,120", // 绿
1: "133,129,48", //
6: "12,112,144" //
}
clusterNodesMap.forEach((nodes, cluster) => {
const color = colorMap[cluster]
nodes.forEach((node) => {
node.fillColor = color
node.color = color
})
let group = graphVis.addNodesInGroup(nodes, {
shape: "polygon", //circle|rect|polygon|bubbleset
color: color,
alpha: 0.3
})
group.smoothPath = false //线
})
graphVis.autoGroupLayout(graphVis.nodes)
}
const createGraph = () => {
if (!graphVis) {
graphVis = new GraphVis({
container: document.getElementById("container"),
licenseKey: "hbsy",
config: defaultConfig
})
}
graphVis.setDragHideLine(false) //线
graphVis.setShowDetailScale(0.1) //
graphVis.setZoomRange(0.1, 5) //
registCustomePaintFunc() //
registEvents()
}
const initChart = () => {
createGraph()
graphVis.addGraph({ ...toRaw(graph.value) })
runForceLayout()
clusterAnalyze()
}
//
const updateChart = (newGraphData) => {
if (!graphVis) {
initChart()
return
}
//
graphVis.clearAll()
//
graphVis.addGraph({ ...toRaw(newGraphData) })
graphVis.autoGroupLayout(toRaw(newGraphData).nodes)
graphVis.zoomFit()
//
runForceLayout()
}
const initChart = () => {
const createGraph = () => {
if (!graphVis) {
graphVis = new GraphVis({
container: document.getElementById("container"),
licenseKey: "hbsy",
config: defaultConfig
})
}
graphVis.setDragHideLine(false) //线
graphVis.setShowDetailScale(0.1) //
graphVis.setZoomRange(0.05, 5) //
registCustomePaintFunc() //
registEvents()
}
createGraph()
graphVis.addGraph({ ...toRaw(graph.value) })
runForceLayout()
clusterAnalyze()
}
let lastLength = 0 //

View File

@ -20,10 +20,10 @@
</div>
<div class="middle-container">
<div class="graph">
<!-- <GroupGraph
<GroupGraph
:store="groupStructureStore"
@click:pointerDownAndSLide="handleChangeXAxis"
></GroupGraph> -->
></GroupGraph>
</div>
<!-- 事件脉络分析 -->
<div class="postList">
@ -74,45 +74,40 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import GroupPanel from "./components/groupPanel.vue";
import GroupGraph from "../component/groupGraph.vue";
import GroupChart from "../component/groupChart.vue";
import GroupPost from "../component/groupPost.vue";
import GroupAnalysis from "./components/groupAnalysis.vue";
import WordsCloud from "../component/wordsCloud.vue";
import userPanelTitleImg from "@/assets/images/groupEvolution/structure-panel-title.png";
import userChartTitleImg from "@/assets/images/groupEvolution/connectivity-title.png";
import groupAnalsisTitleImg from "@/assets/images/groupEvolution/structure-analysis-title.png";
import { Icon } from "@iconify/vue";
import { useGroupStructureStore } from "@/store/groupEvolution/index";
import { ref, onMounted } from "vue"
import GroupPanel from "./components/groupPanel.vue"
import GroupGraph from "../component/groupGraph.vue"
import GroupChart from "../component/groupChart.vue"
import GroupPost from "../component/groupPost.vue"
import GroupAnalysis from "./components/groupAnalysis.vue"
import WordsCloud from "../component/wordsCloud.vue"
import userPanelTitleImg from "@/assets/images/groupEvolution/structure-panel-title.png"
import userChartTitleImg from "@/assets/images/groupEvolution/connectivity-title.png"
import groupAnalsisTitleImg from "@/assets/images/groupEvolution/structure-analysis-title.png"
import { Icon } from "@iconify/vue"
import { useGroupStructureStore } from "@/store/groupEvolution/index"
const groupStructureStore = useGroupStructureStore();
const groupStructureStore = useGroupStructureStore()
const moduleName = '群体结构演化分析';
const moduleName = "群体结构演化分析"
//
const postDialog = ref(false);
const postDialog = ref(false)
//
const currentPostPost = ref(null);
const handleChangeXAxis = (utcTime) => {
}
const currentPostPost = ref(null)
const handleChangeXAxis = (utcTime) => {}
const handleOpenPostDialog = (post) => {
postDialog.value = true;
currentPostPost.value = post;
console.log(currentPostPost.value);
};
postDialog.value = true
currentPostPost.value = post
console.log(currentPostPost.value)
}
onMounted(async () => {
await groupStructureStore.initializeStructuralEvolutionChart()
})
</script>
<style scoped lang="less">