使用web worker优化计算
This commit is contained in:
		
							parent
							
								
									dd56a5b202
								
							
						
					
					
						commit
						346ed68356
					
				
							
								
								
									
										147
									
								
								src/views/GroupEvolution/components/graphWorker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/views/GroupEvolution/components/graphWorker.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,147 @@
 | 
				
			||||||
 | 
					// 图表计算Web Worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 处理力导向布局计算
 | 
				
			||||||
 | 
					self.onmessage = function(e) {
 | 
				
			||||||
 | 
					  const { type, data } = e.data;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  let result;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  switch(type) {
 | 
				
			||||||
 | 
					    case 'CLUSTER_ANALYZE':
 | 
				
			||||||
 | 
					      result = processClusterAnalyze(data);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'HIGHLIGHT_NODES':
 | 
				
			||||||
 | 
					      result = processHighlightNodes(data);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'HIGHLIGHT_LINKS':
 | 
				
			||||||
 | 
					      result = processHighlightLinks(data);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'ANOMALOUS_GROUP_PROCESS':
 | 
				
			||||||
 | 
					      result = processAnomalousGroup(data);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      self.postMessage({
 | 
				
			||||||
 | 
					        success: false,
 | 
				
			||||||
 | 
					        type,
 | 
				
			||||||
 | 
					        error: `Unknown task type: ${type}`
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  self.postMessage({
 | 
				
			||||||
 | 
					    success: true,
 | 
				
			||||||
 | 
					    type,
 | 
				
			||||||
 | 
					    result
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 处理聚类分析
 | 
				
			||||||
 | 
					function processClusterAnalyze(data) {
 | 
				
			||||||
 | 
					  const { nodes, storeId } = data;
 | 
				
			||||||
 | 
					  const clusterNodesMap = new Map();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 创建cluster到nodes的映射
 | 
				
			||||||
 | 
					  nodes.forEach(node => {
 | 
				
			||||||
 | 
					    const cluster = parseInt(node.type);
 | 
				
			||||||
 | 
					    if (!clusterNodesMap.has(cluster)) {
 | 
				
			||||||
 | 
					      clusterNodesMap.set(cluster, []);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    clusterNodesMap.get(cluster).push(node);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  let colorMap = {
 | 
				
			||||||
 | 
					    0: "75,241,184", // 绿色
 | 
				
			||||||
 | 
					    1: "250,222,37", // 黄色
 | 
				
			||||||
 | 
					    6: "69,192,242" // 蓝色
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const processedNodes = [];
 | 
				
			||||||
 | 
					  const groups = [];
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 处理节点颜色和分组
 | 
				
			||||||
 | 
					  clusterNodesMap.forEach((nodes, cluster) => {
 | 
				
			||||||
 | 
					    const color = colorMap[cluster];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    nodes.forEach(node => {
 | 
				
			||||||
 | 
					      const processedNode = { ...node };
 | 
				
			||||||
 | 
					      processedNode.fillColor = color;
 | 
				
			||||||
 | 
					      processedNode.color = color;
 | 
				
			||||||
 | 
					      processedNodes.push(processedNode);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 非异常群体模块需要添加分组
 | 
				
			||||||
 | 
					    if (storeId !== "anomalousGroup") {
 | 
				
			||||||
 | 
					      groups.push({
 | 
				
			||||||
 | 
					        nodes: nodes.map(n => n.id),
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          shape: "polygon",
 | 
				
			||||||
 | 
					          color: color,
 | 
				
			||||||
 | 
					          alpha: 0.2
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return { nodes: processedNodes, groups };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 处理节点高亮
 | 
				
			||||||
 | 
					function processHighlightNodes(data) {
 | 
				
			||||||
 | 
					  const { nodes, newNodes } = data;
 | 
				
			||||||
 | 
					  const newNodeIds = new Set(newNodes.map(node => node.name));
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return nodes.map(node => ({
 | 
				
			||||||
 | 
					    ...node,
 | 
				
			||||||
 | 
					    selected: newNodeIds.has(node.id)
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 处理连线高亮
 | 
				
			||||||
 | 
					function processHighlightLinks(data) {
 | 
				
			||||||
 | 
					  const { links, newLinks } = data;
 | 
				
			||||||
 | 
					  const newLinkSet = new Set(newLinks.map(link => `${link.source}-${link.target}`));
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return links.map(link => ({
 | 
				
			||||||
 | 
					    ...link,
 | 
				
			||||||
 | 
					    selected: newLinkSet.has(`${link.source.id}-${link.target.id}`)
 | 
				
			||||||
 | 
					  }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 处理异常群体分析
 | 
				
			||||||
 | 
					function processAnomalousGroup(data) {
 | 
				
			||||||
 | 
					  const { nodes, currentUtc, abnormalData } = data;
 | 
				
			||||||
 | 
					  const abnormalGroupConfigs = [
 | 
				
			||||||
 | 
					    { timeThreshold: "2024-06-19T08:57:55Z", groupKey: "groupA", color: "85, 125, 15" }, // 绿色
 | 
				
			||||||
 | 
					    { timeThreshold: "2024-06-19T10:58:03Z", groupKey: "groupB", color: "125, 114, 15" }, // 黄色
 | 
				
			||||||
 | 
					    { timeThreshold: "2024-06-19T12:58:04Z", groupKey: "groupC", color: "15, 106, 125" } // 蓝色
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 计算当前应激活的异常组配置
 | 
				
			||||||
 | 
					  const activeConfigs = abnormalGroupConfigs.filter(config => currentUtc >= config.timeThreshold);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 优化异常节点检测 - 预处理异常ID集合
 | 
				
			||||||
 | 
					  const abnormalMap = new Map();
 | 
				
			||||||
 | 
					  activeConfigs.forEach(config => {
 | 
				
			||||||
 | 
					    const abnormalIds = abnormalData[config.groupKey];
 | 
				
			||||||
 | 
					    if (abnormalIds) {
 | 
				
			||||||
 | 
					      abnormalMap.set(config.groupKey, new Set(abnormalIds));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // 单次遍历处理所有异常节点
 | 
				
			||||||
 | 
					  return nodes.map(node => {
 | 
				
			||||||
 | 
					    const processedNode = { ...node };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 检查节点是否属于任何激活的异常组
 | 
				
			||||||
 | 
					    for (const config of activeConfigs) {
 | 
				
			||||||
 | 
					      const abnormalIdsSet = abnormalMap.get(config.groupKey);
 | 
				
			||||||
 | 
					      // 如果节点在异常组中,则设置对应的颜色
 | 
				
			||||||
 | 
					      if (abnormalIdsSet && abnormalIdsSet.has(node.id)) {
 | 
				
			||||||
 | 
					        processedNode.fillColor = config.color;
 | 
				
			||||||
 | 
					        break; // 一旦找到匹配的异常组,就不再检查其他组
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return processedNode;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,74 @@ import group2Leg from "@/assets/images/groupEvolution/legends-group2.png"
 | 
				
			||||||
import group3Leg from "@/assets/images/groupEvolution/legends-group3.png"
 | 
					import group3Leg from "@/assets/images/groupEvolution/legends-group3.png"
 | 
				
			||||||
import abnormalLeg from "@/assets/images/groupEvolution/legends-abnormal-group.png"
 | 
					import abnormalLeg from "@/assets/images/groupEvolution/legends-abnormal-group.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 初始化Web Worker
 | 
				
			||||||
 | 
					let graphWorker = null
 | 
				
			||||||
 | 
					let workerCallbacks = new Map()
 | 
				
			||||||
 | 
					let workerTaskId = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initWorker = () => {
 | 
				
			||||||
 | 
					  if (!graphWorker) {
 | 
				
			||||||
 | 
					    // 创建Web Worker实例
 | 
				
			||||||
 | 
					    graphWorker = new Worker(new URL("./graphWorker.js", import.meta.url))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 设置Worker消息处理
 | 
				
			||||||
 | 
					    graphWorker.onmessage = (e) => {
 | 
				
			||||||
 | 
					      const { success, type, result, error, taskId } = e.data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 查找对应的回调函数
 | 
				
			||||||
 | 
					      const callback = workerCallbacks.get(taskId)
 | 
				
			||||||
 | 
					      if (callback) {
 | 
				
			||||||
 | 
					        if (success) {
 | 
				
			||||||
 | 
					          callback(null, result)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          callback(new Error(`Worker error: ${error}`))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        workerCallbacks.delete(taskId)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 设置Worker错误处理
 | 
				
			||||||
 | 
					    graphWorker.onerror = (error) => {
 | 
				
			||||||
 | 
					      console.error("Graph Worker error:", error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 向Worker发送任务
 | 
				
			||||||
 | 
					const postWorkerTask = (type, data) => {
 | 
				
			||||||
 | 
					  return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					    if (!graphWorker) {
 | 
				
			||||||
 | 
					      initWorker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 如果Worker初始化失败,使用同步处理
 | 
				
			||||||
 | 
					      if (!graphWorker) {
 | 
				
			||||||
 | 
					        reject(new Error("Web Worker not available"))
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const taskId = ++workerTaskId
 | 
				
			||||||
 | 
					    workerCallbacks.set(taskId, (error, result) => {
 | 
				
			||||||
 | 
					      if (error) {
 | 
				
			||||||
 | 
					        reject(error)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        resolve(result)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    graphWorker.postMessage({ type, data, taskId })
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 终止Worker
 | 
				
			||||||
 | 
					const terminateWorker = () => {
 | 
				
			||||||
 | 
					  if (graphWorker) {
 | 
				
			||||||
 | 
					    graphWorker.terminate()
 | 
				
			||||||
 | 
					    graphWorker = null
 | 
				
			||||||
 | 
					    workerCallbacks.clear()
 | 
				
			||||||
 | 
					    workerTaskId = 0
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
  store: {
 | 
					  store: {
 | 
				
			||||||
    required: true
 | 
					    required: true
 | 
				
			||||||
| 
						 | 
					@ -194,34 +262,57 @@ const clearEvents = () => {
 | 
				
			||||||
      if (typeof graphVis.unregistEventListener === "function") {
 | 
					      if (typeof graphVis.unregistEventListener === "function") {
 | 
				
			||||||
        graphVis.unregistEventListener(type, event, handler)
 | 
					        graphVis.unregistEventListener(type, event, handler)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // 如果没有专门的移除方法,尝试其他方式(根据GraphVis实际API调整)
 | 
					 | 
				
			||||||
        console.warn("GraphVis instance does not have unregistEventListener method")
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    eventListeners = []
 | 
					    eventListeners = []
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//公用连线或节点高亮函数
 | 
					// 公用连线或节点高亮函数(使用Web Worker)
 | 
				
			||||||
const highLightAboutNodesOrLinks = (type) => {
 | 
					const highLightAboutNodesOrLinks = async (type) => {
 | 
				
			||||||
  graphVis.cancelAllSelected()
 | 
					  graphVis.cancelAllSelected()
 | 
				
			||||||
  const { newNodes, newLinks } = graph.value
 | 
					  const { newNodes, newLinks } = graph.value
 | 
				
			||||||
  if (type == "nodes") {
 | 
					
 | 
				
			||||||
    const newNodeIds = new Set(newNodes.map((node) => node.name))
 | 
					  try {
 | 
				
			||||||
    graphVis.nodes.forEach((node) => {
 | 
					    let processedItems
 | 
				
			||||||
      if (newNodeIds.has(node.id)) {
 | 
					
 | 
				
			||||||
        node.selected = true
 | 
					    if (type == "nodes") {
 | 
				
			||||||
      }
 | 
					      // 使用Web Worker处理节点高亮
 | 
				
			||||||
    })
 | 
					      processedItems = await postWorkerTask("HIGHLIGHT_NODES", {
 | 
				
			||||||
  } else if (type == "links") {
 | 
					        nodes: graphVis.nodes,
 | 
				
			||||||
    const newLinkSet = new Set(newLinks.map((link) => `${link.source}-${link.target}`))
 | 
					        newNodes
 | 
				
			||||||
    graphVis.links.forEach((link) => {
 | 
					      })
 | 
				
			||||||
      if (newLinkSet.has(`${link.source.id}-${link.target.id}`)) {
 | 
					      graphVis.nodes = processedItems
 | 
				
			||||||
        link.selected = true
 | 
					    } else if (type == "links") {
 | 
				
			||||||
      }
 | 
					      // 使用Web Worker处理连线高亮
 | 
				
			||||||
    })
 | 
					      processedItems = await postWorkerTask("HIGHLIGHT_LINKS", {
 | 
				
			||||||
  } else {
 | 
					        links: graphVis.links,
 | 
				
			||||||
    return
 | 
					        newLinks
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      graphVis.links = processedItems
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 触发重绘
 | 
				
			||||||
 | 
					    graphVis.render()
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    // 降级处理:使用同步方法
 | 
				
			||||||
 | 
					    if (type == "nodes") {
 | 
				
			||||||
 | 
					      const newNodeIds = new Set(newNodes.map((node) => node.name))
 | 
				
			||||||
 | 
					      graphVis.nodes.forEach((node) => {
 | 
				
			||||||
 | 
					        if (newNodeIds.has(node.id)) {
 | 
				
			||||||
 | 
					          node.selected = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    } else if (type == "links") {
 | 
				
			||||||
 | 
					      const newLinkSet = new Set(newLinks.map((link) => `${link.source}-${link.target}`))
 | 
				
			||||||
 | 
					      graphVis.links.forEach((link) => {
 | 
				
			||||||
 | 
					        if (newLinkSet.has(`${link.source.id}-${link.target.id}`)) {
 | 
				
			||||||
 | 
					          link.selected = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
					const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
				
			||||||
| 
						 | 
					@ -239,40 +330,55 @@ const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
				
			||||||
      highLightAboutNodesOrLinks("nodes")
 | 
					      highLightAboutNodesOrLinks("nodes")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const handleAnomalousGroup = () => {
 | 
					    const handleAnomalousGroup = async () => {
 | 
				
			||||||
      // 当前时间从 store 取
 | 
					      try {
 | 
				
			||||||
      const now = props.store.currentUtc
 | 
					        // 使用Web Worker处理异常群体分析
 | 
				
			||||||
      const abnormalData = props.store.graphAbnormalData
 | 
					        const processedNodes = await postWorkerTask("ANOMALOUS_GROUP_PROCESS", {
 | 
				
			||||||
      const abnormalGroupConfigs = [
 | 
					          nodes: graphVis.nodes,
 | 
				
			||||||
        { timeThreshold: "2024-06-19T08:57:55Z", groupKey: "groupA", color: "85, 125, 15" }, // 绿色
 | 
					          currentUtc: props.store.currentUtc,
 | 
				
			||||||
        { timeThreshold: "2024-06-19T10:58:03Z", groupKey: "groupB", color: "125, 114, 15" }, // 黄色
 | 
					          abnormalData: props.store.graphAbnormalData
 | 
				
			||||||
        { timeThreshold: "2024-06-19T12:58:04Z", groupKey: "groupC", color: "15, 106, 125" } // 蓝色
 | 
					        })
 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
      // 计算当前应激活的异常组配置
 | 
					 | 
				
			||||||
      const activeConfigs = abnormalGroupConfigs.filter((config) => now >= config.timeThreshold)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // 优化异常节点检测 - 预处理异常ID集合
 | 
					        graphVis.nodes = processedNodes
 | 
				
			||||||
      const abnormalMap = new Map()
 | 
					        graphVis.render()
 | 
				
			||||||
      activeConfigs.forEach((config) => {
 | 
					      } catch (error) {
 | 
				
			||||||
        const abnormalIds = abnormalData[config.groupKey]
 | 
					        console.error("Error processing anomalous group:", error)
 | 
				
			||||||
        if (abnormalIds) {
 | 
					 | 
				
			||||||
          abnormalMap.set(config.groupKey, new Set(abnormalIds))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // 单次遍历处理所有异常节点
 | 
					        // 降级处理:使用同步方法
 | 
				
			||||||
      graphVis.nodes = graphVis.nodes.map((node) => {
 | 
					        // 当前时间从 store 取
 | 
				
			||||||
        // 检查节点是否属于任何激活的异常组
 | 
					        const now = props.store.currentUtc
 | 
				
			||||||
        for (const config of activeConfigs) {
 | 
					        const abnormalData = props.store.graphAbnormalData
 | 
				
			||||||
          const abnormalIdsSet = abnormalMap.get(config.groupKey)
 | 
					        const abnormalGroupConfigs = [
 | 
				
			||||||
          // 如果节点在异常组中,则设置对应的颜色
 | 
					          { timeThreshold: "2024-06-19T08:57:55Z", groupKey: "groupA", color: "85, 125, 15" }, // 绿色
 | 
				
			||||||
          if (abnormalIdsSet && abnormalIdsSet.has(node.id)) {
 | 
					          { timeThreshold: "2024-06-19T10:58:03Z", groupKey: "groupB", color: "125, 114, 15" }, // 黄色
 | 
				
			||||||
            node.fillColor = config.color
 | 
					          { timeThreshold: "2024-06-19T12:58:04Z", groupKey: "groupC", color: "15, 106, 125" } // 蓝色
 | 
				
			||||||
            break // 一旦找到匹配的异常组,就不再检查其他组
 | 
					        ]
 | 
				
			||||||
 | 
					        // 计算当前应激活的异常组配置
 | 
				
			||||||
 | 
					        const activeConfigs = abnormalGroupConfigs.filter((config) => now >= config.timeThreshold)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 优化异常节点检测 - 预处理异常ID集合
 | 
				
			||||||
 | 
					        const abnormalMap = new Map()
 | 
				
			||||||
 | 
					        activeConfigs.forEach((config) => {
 | 
				
			||||||
 | 
					          const abnormalIds = abnormalData[config.groupKey]
 | 
				
			||||||
 | 
					          if (abnormalIds) {
 | 
				
			||||||
 | 
					            abnormalMap.set(config.groupKey, new Set(abnormalIds))
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        })
 | 
				
			||||||
        return node
 | 
					
 | 
				
			||||||
      })
 | 
					        // 单次遍历处理所有异常节点
 | 
				
			||||||
 | 
					        graphVis.nodes = graphVis.nodes.map((node) => {
 | 
				
			||||||
 | 
					          // 检查节点是否属于任何激活的异常组
 | 
				
			||||||
 | 
					          for (const config of activeConfigs) {
 | 
				
			||||||
 | 
					            const abnormalIdsSet = abnormalMap.get(config.groupKey)
 | 
				
			||||||
 | 
					            // 如果节点在异常组中,则设置对应的颜色
 | 
				
			||||||
 | 
					            if (abnormalIdsSet && abnormalIdsSet.has(node.id)) {
 | 
				
			||||||
 | 
					              node.fillColor = config.color
 | 
				
			||||||
 | 
					              break // 一旦找到匹配的异常组,就不再检查其他组
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return node
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    new Map([
 | 
					    new Map([
 | 
				
			||||||
| 
						 | 
					@ -286,38 +392,60 @@ const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  graphVis.excuteLocalLayout(layoutType, layoutConfig, isAsync, handleLayoutSuccess())
 | 
					  graphVis.excuteLocalLayout(layoutType, layoutConfig, isAsync, handleLayoutSuccess())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// 根据节点的cluster属性进行分组
 | 
					// 根据节点的cluster属性进行分组(使用Web Worker)
 | 
				
			||||||
const clusterAnalyze = () => {
 | 
					const clusterAnalyze = async () => {
 | 
				
			||||||
  graphVis.removeAllGroup() // 清除原有分组
 | 
					  try {
 | 
				
			||||||
  // 创建cluster到nodes的映射
 | 
					    // 使用Web Worker处理聚类分析
 | 
				
			||||||
  const clusterNodesMap = new Map()
 | 
					    const { nodes: processedNodes, groups } = await postWorkerTask("CLUSTER_ANALYZE", {
 | 
				
			||||||
  graphVis.nodes.forEach((node) => {
 | 
					      nodes: graphVis.nodes,
 | 
				
			||||||
    const cluster = parseInt(node.type)
 | 
					      storeId
 | 
				
			||||||
    if (!clusterNodesMap.has(cluster)) {
 | 
					 | 
				
			||||||
      clusterNodesMap.set(cluster, [])
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    clusterNodesMap.get(cluster).push(node)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  let colorMap = {
 | 
					 | 
				
			||||||
    0: "75,241,184", // 绿色
 | 
					 | 
				
			||||||
    1: "250,222,37", // 黄色
 | 
					 | 
				
			||||||
    6: "69,192,242" // 蓝色
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  clusterNodesMap.forEach((nodes, cluster) => {
 | 
					 | 
				
			||||||
    const color = colorMap[cluster]
 | 
					 | 
				
			||||||
    nodes.forEach((node) => {
 | 
					 | 
				
			||||||
      node.fillColor = color
 | 
					 | 
				
			||||||
      node.color = color
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    if (storeId !== "anomalousGroup") {
 | 
					
 | 
				
			||||||
      graphVis.addNodesInGroup(nodes, {
 | 
					    graphVis.removeAllGroup() // 清除原有分组
 | 
				
			||||||
        shape: "polygon", //circle|rect|polygon|bubbleset
 | 
					    graphVis.nodes = processedNodes
 | 
				
			||||||
        color: color,
 | 
					    // 添加分组
 | 
				
			||||||
        alpha: 0.2
 | 
					    if (storeId !== "anomalousGroup" && groups && groups.length) {
 | 
				
			||||||
 | 
					      groups.forEach((group) => {
 | 
				
			||||||
 | 
					        const groupNodes = processedNodes.filter((node) => group.nodes.includes(node.id))
 | 
				
			||||||
 | 
					        if (groupNodes.length) {
 | 
				
			||||||
 | 
					          graphVis.addNodesInGroup(groupNodes, group.options)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					
 | 
				
			||||||
  // graphVis.autoGroupLayout(graphVis.nodes)
 | 
					    graphVis.render()
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    // 降级处理:使用同步方法
 | 
				
			||||||
 | 
					    graphVis.removeAllGroup() // 清除原有分组
 | 
				
			||||||
 | 
					    // 创建cluster到nodes的映射
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    let colorMap = {
 | 
				
			||||||
 | 
					      0: "75,241,184", // 绿色
 | 
				
			||||||
 | 
					      1: "250,222,37", // 黄色
 | 
				
			||||||
 | 
					      6: "69,192,242" // 蓝色
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    clusterNodesMap.forEach((nodes, cluster) => {
 | 
				
			||||||
 | 
					      const color = colorMap[cluster]
 | 
				
			||||||
 | 
					      nodes.forEach((node) => {
 | 
				
			||||||
 | 
					        node.fillColor = color
 | 
				
			||||||
 | 
					        node.color = color
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (storeId !== "anomalousGroup") {
 | 
				
			||||||
 | 
					        graphVis.addNodesInGroup(nodes, {
 | 
				
			||||||
 | 
					          shape: "polygon", //circle|rect|polygon|bubbleset
 | 
				
			||||||
 | 
					          color: color,
 | 
				
			||||||
 | 
					          alpha: 0.2
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 仅对“异常群体模块”生效:时间变化时强制重绘一次
 | 
					// 仅对“异常群体模块”生效:时间变化时强制重绘一次
 | 
				
			||||||
| 
						 | 
					@ -370,7 +498,7 @@ onMounted(() => {
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 添加更新图表的函数(优化版本:使用requestAnimationFrame和节流)
 | 
					// 添加更新图表的函数(优化版本:使用requestAnimationFrame和节流)
 | 
				
			||||||
const updateChart = (newGraphData) => {
 | 
					const updateChart = async (newGraphData) => {
 | 
				
			||||||
  if (!graphVis) {
 | 
					  if (!graphVis) {
 | 
				
			||||||
    initChart()
 | 
					    initChart()
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
| 
						 | 
					@ -379,8 +507,12 @@ const updateChart = (newGraphData) => {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    graphVis.clearAll()
 | 
					    graphVis.clearAll()
 | 
				
			||||||
    graphVis.addGraph({ ...toRaw(newGraphData) })
 | 
					    graphVis.addGraph({ ...toRaw(newGraphData) })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 初始化Web Worker
 | 
				
			||||||
 | 
					    initWorker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 重新运行力导向布局
 | 
					    // 重新运行力导向布局
 | 
				
			||||||
    clusterAnalyze()
 | 
					    await clusterAnalyze()
 | 
				
			||||||
    runDiffForceLayout({ strength: -300, ajustCluster: true }, "simulation", true)
 | 
					    runDiffForceLayout({ strength: -300, ajustCluster: true }, "simulation", true)
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error("Error updating chart:", error)
 | 
					    console.error("Error updating chart:", error)
 | 
				
			||||||
| 
						 | 
					@ -445,6 +577,9 @@ onUnmounted(() => {
 | 
				
			||||||
    forceSimulator = null
 | 
					    forceSimulator = null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 终止Web Worker
 | 
				
			||||||
 | 
					  terminateWorker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 清除引用,帮助垃圾回收
 | 
					  // 清除引用,帮助垃圾回收
 | 
				
			||||||
  currentSelectNode.value = null
 | 
					  currentSelectNode.value = null
 | 
				
			||||||
  isPlay.value = false
 | 
					  isPlay.value = false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,10 +101,9 @@ const startAutomaticPlay = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  isAutoPlaying.value = true
 | 
					  isAutoPlaying.value = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 每300ms移动2%
 | 
					 | 
				
			||||||
  animationTimer = setInterval(() => {
 | 
					  animationTimer = setInterval(() => {
 | 
				
			||||||
    // 计算新位置
 | 
					    // 计算新位置
 | 
				
			||||||
    const newPosition = clickedPosition.value + 2
 | 
					    const newPosition = clickedPosition.value + 0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 检查是否到达终点
 | 
					    // 检查是否到达终点
 | 
				
			||||||
    if (newPosition >= 100) {
 | 
					    if (newPosition >= 100) {
 | 
				
			||||||
| 
						 | 
					@ -118,7 +117,7 @@ const startAutomaticPlay = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 根据当前位置更新activeTimePoint
 | 
					    // 根据当前位置更新activeTimePoint
 | 
				
			||||||
    updateActiveTimePointByPosition(newPosition)
 | 
					    updateActiveTimePointByPosition(newPosition)
 | 
				
			||||||
  }, 1000)
 | 
					  }, 100)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据位置更新activeTimePoint
 | 
					// 根据位置更新activeTimePoint
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user