diff --git a/src/service/api/groupEvolution.js b/src/service/api/groupEvolution.js index d3938f3..fe842e4 100644 --- a/src/service/api/groupEvolution.js +++ b/src/service/api/groupEvolution.js @@ -95,7 +95,6 @@ export function getAbnormalGroupInteractionChart() { } // 4.4异常行为分析 export function getAbnormalGroupBehaviorPosts(time) { - console.log(time) return http.get(`/groupEvolution/groupException/posts?date=${time}`) } diff --git a/src/store/groupEvolution/index.js b/src/store/groupEvolution/index.js index a05c379..b17b014 100644 --- a/src/store/groupEvolution/index.js +++ b/src/store/groupEvolution/index.js @@ -712,11 +712,18 @@ export const useGroupMemberStore = defineStore("groupMember", { type: node.groupId } }) + console.log(this.graph); } }, persist: true // 开启持久化 }) +// —— 时间门槛 —— +const T0 = "2024-06-19T07:57:46Z"; // 默认渲染出全部节点 +const TA = "2024-06-19T08:57:55Z"; // A 组异常开始 +const TB = "2024-06-19T10:58:03Z"; // B 组异常开始 +const TC = "2024-06-19T12:58:04Z"; // C 组异常开始 + export const useAnomalousGroup = defineStore("anomalousGroup", { state: () => ({ groupList: [], @@ -856,7 +863,145 @@ export const useAnomalousGroup = defineStore("anomalousGroup", { CUSUM: 5.0, KS: 0.02 } - ] + ], + currentUtc: T0, + graphData: { + nodes: [ + { id: "G01_A", type: "0", isAnomaly: false, label: "G01" }, + { id: "G02_A", type: "0", isAnomaly: true, label: "G02" }, + { id: "G03_A", type: "0", isAnomaly: false, label: "G03" }, + { id: "G04_A", type: "0", isAnomaly: false, label: "G04" }, + { id: "G05_A", type: "0", isAnomaly: false, label: "G05" }, + { id: "G06_A", type: "0", isAnomaly: false, label: "G06" }, + { id: "G07_A", type: "0", isAnomaly: true, label: "G07" }, + { id: "G08_A", type: "0", isAnomaly: false, label: "G08" }, + { id: "G09_A", type: "0", isAnomaly: false, label: "G09" }, + { id: "G10_A", type: "0", isAnomaly: false, label: "G10" }, + { id: "G11_A", type: "0", isAnomaly: false, label: "G11" }, + { id: "G12_A", type: "0", isAnomaly: false, label: "G12" }, + + { id: "G01_B", type: "1", isAnomaly: false, label: "G01" }, + { id: "G02_B", type: "1", isAnomaly: false, label: "G02" }, + { id: "G03_B", type: "1", isAnomaly: false, label: "G03" }, + { id: "G04_B", type: "1", isAnomaly: true, label: "G04" }, + { id: "G05_B", type: "1", isAnomaly: false, label: "G05" }, + { id: "G06_B", type: "1", isAnomaly: false, label: "G06" }, + { id: "G07_B", type: "1", isAnomaly: false, label: "G07" }, + { id: "G08_B", type: "1", isAnomaly: true, label: "G08" }, + { id: "G09_B", type: "1", isAnomaly: false, label: "G09" }, + { id: "G10_B", type: "1", isAnomaly: false, label: "G10" }, + { id: "G11_B", type: "1", isAnomaly: false, label: "G11" }, + { id: "G12_B", type: "1", isAnomaly: false, label: "G12" }, + + { id: "G01_C", type: "6", isAnomaly: false, label: "G01" }, + { id: "G02_C", type: "6", isAnomaly: false, label: "G02" }, + { id: "G03_C", type: "6", isAnomaly: true, label: "G03" }, + { id: "G04_C", type: "6", isAnomaly: false, label: "G04" }, + { id: "G05_C", type: "6", isAnomaly: false, label: "G05" }, + { id: "G06_C", type: "6", isAnomaly: true, label: "G06" }, + { id: "G07_C", type: "6", isAnomaly: false, label: "G07" }, + { id: "G08_C", type: "6", isAnomaly: false, label: "G08" }, + { id: "G09_C", type: "6", isAnomaly: false, label: "G09" }, + { id: "G10_C", type: "6", isAnomaly: false, label: "G10" }, + { id: "G11_C", type: "6", isAnomaly: false, label: "G11" }, + { id: "G12_C", type: "6", isAnomaly: false, label: "G12" } + ], + links: [ + /* —— 组内:A 环 —— */ + { source: "G01_A", target: "G02_A", type: "0" }, + { source: "G02_A", target: "G03_A", type: "0" }, + { source: "G03_A", target: "G04_A", type: "0" }, + { source: "G04_A", target: "G05_A", type: "0" }, + { source: "G05_A", target: "G06_A", type: "0" }, + { source: "G06_A", target: "G07_A", type: "0" }, + { source: "G07_A", target: "G08_A", type: "0" }, + { source: "G08_A", target: "G09_A", type: "0" }, + { source: "G09_A", target: "G10_A", type: "0" }, + { source: "G10_A", target: "G11_A", type: "0" }, + { source: "G11_A", target: "G12_A", type: "0" }, + { source: "G12_A", target: "G01_A", type: "0" }, + /* —— A 组:异常节点额外连边(突出)—— */ + { source: "G02_A", target: "G05_A", type: "0" }, + { source: "G02_A", target: "G08_A", type: "0" }, + { source: "G02_A", target: "G11_A", type: "0" }, + { source: "G07_A", target: "G10_A", type: "0" }, + { source: "G07_A", target: "G01_A", type: "0" }, + { source: "G07_A", target: "G04_A", type: "0" }, + + /* —— 组内:B 环 —— */ + { source: "G01_B", target: "G02_B", type: "1" }, + { source: "G02_B", target: "G03_B", type: "1" }, + { source: "G03_B", target: "G04_B", type: "1" }, + { source: "G04_B", target: "G05_B", type: "1" }, + { source: "G05_B", target: "G06_B", type: "1" }, + { source: "G06_B", target: "G07_B", type: "1" }, + { source: "G07_B", target: "G08_B", type: "1" }, + { source: "G08_B", target: "G09_B", type: "1" }, + { source: "G09_B", target: "G10_B", type: "1" }, + { source: "G10_B", target: "G11_B", type: "1" }, + { source: "G11_B", target: "G12_B", type: "1" }, + { source: "G12_B", target: "G01_B", type: "1" }, + /* —— B 组:异常节点额外连边 —— */ + { source: "G04_B", target: "G07_B", type: "1" }, + { source: "G04_B", target: "G10_B", type: "1" }, + { source: "G04_B", target: "G01_B", type: "1" }, + { source: "G08_B", target: "G11_B", type: "1" }, + { source: "G08_B", target: "G02_B", type: "1" }, + { source: "G08_B", target: "G05_B", type: "1" }, + + /* —— 组内:C 环 —— */ + { source: "G01_C", target: "G02_C", type: "6" }, + { source: "G02_C", target: "G03_C", type: "6" }, + { source: "G03_C", target: "G04_C", type: "6" }, + { source: "G04_C", target: "G05_C", type: "6" }, + { source: "G05_C", target: "G06_C", type: "6" }, + { source: "G06_C", target: "G07_C", type: "6" }, + { source: "G07_C", target: "G08_C", type: "6" }, + { source: "G08_C", target: "G09_C", type: "6" }, + { source: "G09_C", target: "G10_C", type: "6" }, + { source: "G10_C", target: "G11_C", type: "6" }, + { source: "G11_C", target: "G12_C", type: "6" }, + { source: "G12_C", target: "G01_C", type: "6" }, + /* —— C 组:异常节点额外连边 —— */ + { source: "G03_C", target: "G06_C", type: "6" }, + { source: "G03_C", target: "G09_C", type: "6" }, + { source: "G03_C", target: "G12_C", type: "6" }, + { source: "G06_C", target: "G09_C", type: "6" }, + { source: "G06_C", target: "G12_C", type: "6" }, + { source: "G06_C", target: "G03_C", type: "6" }, + + /* —— 跨组连边 —— */ + { source: "G02_A", target: "G04_B", type: "0" }, + { source: "G07_A", target: "G08_B", type: "0" }, + { source: "G01_A", target: "G05_B", type: "0" }, + { source: "G05_A", target: "G10_B", type: "0" }, + + { source: "G04_B", target: "G03_C", type: "1" }, + { source: "G08_B", target: "G06_C", type: "1" }, + { source: "G02_B", target: "G09_C", type: "1" }, + { source: "G11_B", target: "G01_C", type: "1" }, + + { source: "G03_C", target: "G02_A", type: "6" }, + { source: "G06_C", target: "G07_A", type: "6" }, + { source: "G04_C", target: "G09_A", type: "6" }, + { source: "G12_C", target: "G06_A", type: "6" } + ], + meta: { + groups: [ + { id: "A", anomalies: ["G02_A", "G07_A"] }, + { id: "B", anomalies: ["G04_B", "G08_B"] }, + { id: "C", anomalies: ["G03_C", "G06_C"] } + ] + } + }, + graphAbnormalData: { + groupA: ["G02_A", "G07_A"], + groupB: ["G04_B", "G08_B"], + groupC: ["G03_C", "G06_C"], + abnormalNodesT1: ["G02_A", "G07_A"], + abnormalNodesT2: ["G04_B", "G08_B"], + abnormalNodesT3: ["G02_A", "G07_A", "G04_B", "G08_B", "G03_C", "G06_C"] + } }), actions: { // 获取时间轴数据-中间时间轴 @@ -919,6 +1064,38 @@ export const useAnomalousGroup = defineStore("anomalousGroup", { if (res.code === 200) { this.abnormalContentList = res.data } + }, + // 用于画中间组件的圆 + async initialGraphByUtcTime(utcIso) { + this.currentUtc = utcIso; + // 1) 复制 nodes 并上色 + const nodes = this.graphData.nodes.map(n => { + return { + id: n.id, + label: n.label, + type: n.type, // 0=A, 1=B, 6=C(组件里 colorMap 已按这三个上色/分组) + isAnomaly: n.isAnomaly + }; + }); + + this.graph["nodes"] = nodes + + const setColor = (type) => { + const colorMap = { + 0: "50,141,120", + 1: "133,129,48", + 6: "12,112,144" + } + return colorMap[type] + } + + const links = this.graphData.links.map(l => ({ + source: l.source, + target: l.target, + color: setColor(l.type), + })) + this.graph["links"] = links + } }, persist: true // 开启持久化 diff --git a/src/views/GroupEvolution/abnormalGroup/index.vue b/src/views/GroupEvolution/abnormalGroup/index.vue index 0a07912..847686c 100644 --- a/src/views/GroupEvolution/abnormalGroup/index.vue +++ b/src/views/GroupEvolution/abnormalGroup/index.vue @@ -90,6 +90,7 @@ const handleChangeXAxis = (utcTime) => { // 根据时间轴变化更改群体结构演化分析的群体演化信息-中下-随时间轴变化 anomalousGroupStore.initializeAbnormalGroupPosts(utcTime) anomalousGroupStore.initializeAbnormalGroupInteractionDetail(utcTime) + anomalousGroupStore.currentUtc = utcTime; // ★ 关键:触发上面的 watcher } //控制弹窗 @@ -109,6 +110,7 @@ onMounted(async () => { await anomalousGroupStore.initializeAbnormalGroupPosts() await anomalousGroupStore.initializeAbnormalGroupInteractionDetail() await anomalousGroupStore.initializeAbnormalGroupInteractionChart() + await anomalousGroupStore.initialGraphByUtcTime("2024-06-19T07:57:46Z") }) diff --git a/src/views/GroupEvolution/component/groupGraph.vue b/src/views/GroupEvolution/component/groupGraph.vue index 8cfa707..c7db095 100644 --- a/src/views/GroupEvolution/component/groupGraph.vue +++ b/src/views/GroupEvolution/component/groupGraph.vue @@ -23,6 +23,7 @@ import { convertToUtcIsoString } from "@/utils/transform" import { paintNodeFunction, paintLineFunction } from "@/utils/customePaint" import TimeAxis from "@/components/timeAxis.vue" import GraphVis from "@/assets/package/graphvis.esm.min.js" + const props = defineProps({ store: { required: true @@ -155,7 +156,7 @@ const clusterAnalyze = () => { } clusterNodesMap.get(cluster).push(node) }) - const colorMap = { + let colorMap = { 0: "50,141,120", // 绿色 1: "133,129,48", // 黄色 6: "12,112,144" // 蓝色 @@ -189,6 +190,72 @@ const clusterAnalyze = () => { function handleAnomalousGroup() { console.log(storeId) + + // 当前时间从 store 取;没有就用 T0 + const now = (props.store.currentUtc); + + const TA = "2024-06-19T08:57:55Z"; // A + const TB = "2024-06-19T10:58:03Z"; // B + const TC = "2024-06-19T12:58:04Z"; // C + + // 组色(与你现有 colorMap 一致) + const GROUP_COLOR = { 0: "12,112,144", 1: "180,150,20", 6: "50,141,120" }; + const GROUP_ALPHA = 0.30; + + const RED = "220,50,60"; + + // 覆盖画三大组大圆(蓝/黄/绿透明) +/* const buckets = new Map(); + graphVis.nodes.forEach(n => { + const t = parseInt(n.type); // "0"|"1"|"6" → 0/1/6 + if (!buckets.has(t)) buckets.set(t, []); + buckets.get(t).push(n); + }); + buckets.forEach((nodes, t) => { + const color = GROUP_COLOR[t] || "120,120,120"; + const g = graphVis.addNodesInGroup(nodes, { shape: "circle", color, alpha: GROUP_ALPHA }); + g.smoothPath = false; + }); */ + + // 时间门控:达到阈值就把各组异常节点染红 + 加红圈 + const shouldA = now >= TA; + const shouldB = now >= TB; + const shouldC = now >= TC; + + if(shouldA) { + console.log("A异常") + + graphVis.nodes = graphVis.nodes.map(n => { + if(props.store.graphAbnormalData.groupA.includes(n.id)) { + n.fillColor = RED; + } + return n; + }) + } + if(shouldB) { + console.log("B异常") + graphVis.nodes = graphVis.nodes.map(n => { + if(props.store.graphAbnormalData.groupB.includes(n.id)) { + n.fillColor = RED; + } + return n; + }) + } + if(shouldC) { + console.log("C异常") + + graphVis.nodes = graphVis.nodes.map(n => { + if(props.store.graphAbnormalData.groupC.includes(n.id)) { + n.fillColor = RED; + } + return n; + }) + } + + + + graphVis.autoGroupLayout(graphVis.nodes); + graphVis.zoomFit(); // 没有 refresh;这两个就够 } new Map([ @@ -200,6 +267,20 @@ const clusterAnalyze = () => { // graphVis.selectedEdge(graphVis.links[0]) } +// 仅对“异常群体模块”生效:时间变化时强制重绘一次 +if (storeId === 'anomalousGroup') { + watch( + () => props.store.currentUtc, + () => { + // 不改 graph 数据结构,直接用当前 graph 强制走一遍:addGraph → clusterAnalyze → runForceLayout + if (graphVis) { + updateChart(toRaw(graph.value)); + } + } + ); +} + +// 实例化 GraphVis、注册自定义绘制/事件 const createGraph = () => { if (!graphVis) { graphVis = new GraphVis({