From 5b3c0050b9ad7ebde063d81d424a72d14c9af0a5 Mon Sep 17 00:00:00 2001 From: Cindy Chang Date: Thu, 5 Sep 2024 17:49:01 +0800 Subject: [PATCH] fix: dotted solid edge style bug --- src/module/cytoscapeMap.js | 2 +- src/stores/mapPathStore.ts | 529 ++++++++++++++++----------------- src/views/Discover/Map/Map.vue | 192 ++++++------ 3 files changed, 370 insertions(+), 353 deletions(-) diff --git a/src/module/cytoscapeMap.js b/src/module/cytoscapeMap.js index 34c1443..22a9939 100644 --- a/src/module/cytoscapeMap.js +++ b/src/module/cytoscapeMap.js @@ -192,7 +192,7 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu 'color': 'gray', //#0066cc //'control-point-step-size':100, // 從點到點的垂直線,指定貝茲取線邊緣間的距離 'width': 'data(lineWidth)', - 'line-style': 'data(style)', + 'line-style': 'data(edgeStyle)', "text-margin-y": "0.7rem", //"text-rotation": "autorotate", } diff --git a/src/stores/mapPathStore.ts b/src/stores/mapPathStore.ts index 15fea82..4229537 100644 --- a/src/stores/mapPathStore.ts +++ b/src/stores/mapPathStore.ts @@ -14,288 +14,287 @@ const ImgCapsulesGlow = [ImgCapsuleGlow1, ImgCapsuleGlow2, ImgCapsuleGlow3, ImgC const ImgCapsules = [ImgCapsule1, ImgCapsule2, ImgCapsule3, ImgCapsule4]; export default defineStore('useMapPathStore', { - state: () => ({ - clickedPath: [], - insights: {}, - insightWithPath: {}, - cytoscape: { - process: - { - curved:{ - horizontal:null, - vertical:null, - }, - elbow:{ - horizontal:null, - vertical:null, - } - }, - bpmn: { - curved:{ - horizontal:null, - vertical:null, - }, - elbow:{ - horizontal:null, - vertical:null, - } - } + state: () => ({ + clickedPath: [], + insights: {}, + insightWithPath: {}, + cytoscape: { + process: + { + curved: { + horizontal: null, + vertical: null, }, - processOrBPMN: 'process', - curveType: 'curved', - directionType: 'horizontal', - allPaths: [], - allPathsByEdge: [], - startNode: null, - mapGraphPathToInsight: {}, - activeTrace: 0, - activeListIndex: 0, - lastClickedNode: null, - isBPMNOn: false, - }), - actions: { - async setCytoscape(cytoscape, processOrBPMN = 'process', curveType = 'curved', directionType = 'horizontal') { - this.processOrBPMN = processOrBPMN; - this.curveType = curveType; - this.directionType = directionType; - this.cytoscape[processOrBPMN][curveType][directionType] = cytoscape; - await this.createInsightWithPath(); - if(processOrBPMN === 'process') { - await this.highlightMostFrequentPath(); - } + elbow: { + horizontal: null, + vertical: null, + } }, - async createInsightWithPath() { - const { insights } = AllMapData(); - this.insights = {...insights}; - this.startNode = this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes() - .filter(function(elem) { + bpmn: { + curved: { + horizontal: null, + vertical: null, + }, + elbow: { + horizontal: null, + vertical: null, + } + } + }, + processOrBPMN: 'process', + curveType: 'curved', + directionType: 'horizontal', + allPaths: [], + allPathsByEdge: [], + startNode: null, + mapGraphPathToInsight: {}, + activeTrace: 0, + activeListIndex: 0, + lastClickedNode: null, + isBPMNOn: false, + }), + actions: { + async setCytoscape(cytoscape, processOrBPMN = 'process', curveType = 'curved', directionType = 'horizontal') { + this.processOrBPMN = processOrBPMN; + this.curveType = curveType; + this.directionType = directionType; + this.cytoscape[processOrBPMN][curveType][directionType] = cytoscape; + await this.createInsightWithPath(); + if (processOrBPMN === 'process') { + await this.highlightMostFrequentPath(); + } + }, + async createInsightWithPath() { + const { insights } = AllMapData(); + this.insights = { ...insights }; + this.startNode = this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes() + .filter(function (elem) { return elem.data('label').toLocaleLowerCase() === 'start'; }); - for(let i = 0; i < INSIGHTS_FIELDS_AND_LABELS.length; i++) { - const curButton = this.insights[INSIGHTS_FIELDS_AND_LABELS[i][0]]; - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]] = {}; // first layer index + for (let i = 0; i < INSIGHTS_FIELDS_AND_LABELS.length; i++) { + const curButton = this.insights[INSIGHTS_FIELDS_AND_LABELS[i][0]]; + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]] = {}; // first layer index - for(let listIndex = 0; listIndex < curButton.length; listIndex++) { - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex] = { - edges: [], - nodes: [], - }; // second layer index + for (let listIndex = 0; listIndex < curButton.length; listIndex++) { + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex] = { + edges: [], + nodes: [], + }; // second layer index - let curGraphNode, prevGraphNode, curEdge; // 配對 curGraphNode 與 nodeIndex 指向的 node - for(let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++){ - if(nodeIndex === 0) { // special case, initialize curGraphNode - curGraphNode = this.startNode.outgoers('node').filter(neighborOfStart => - neighborOfStart.data('label') === curButton[listIndex][nodeIndex] + let curGraphNode, prevGraphNode, curEdge; // 配對 curGraphNode 與 nodeIndex 指向的 node + for (let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++) { + if (nodeIndex === 0) { // special case, initialize curGraphNode + curGraphNode = this.startNode.outgoers('node').filter(neighborOfStart => + neighborOfStart.data('label') === curButton[listIndex][nodeIndex] + ); + curEdge = this.startNode.edgesTo(curGraphNode); + } else { + if (prevGraphNode) { + curGraphNode = prevGraphNode.outgoers('node').filter(neighbor => + neighbor.data('label') === curButton[listIndex][nodeIndex] ); - curEdge = this.startNode.edgesTo(curGraphNode); - } else { - if(prevGraphNode){ - curGraphNode = prevGraphNode.outgoers('node').filter(neighbor => - neighbor.data('label') === curButton[listIndex][nodeIndex] - ); - curEdge = prevGraphNode.edgesWith(curGraphNode); - } + curEdge = prevGraphNode.edgesWith(curGraphNode); } - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].nodes.push(curGraphNode); - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].edges.push(curEdge); - // 特殊狀況,在for迴圈之外額外插入最後一條線段 - if(nodeIndex === curButton[listIndex].length - 1){ - const endNode = curGraphNode.outgoers('node').filter(neighbor => - neighbor.data('label').toLocaleLowerCase() === 'end' - ); - const lastEdge = curGraphNode.edgesWith(endNode); - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].edges.push(lastEdge); - } - prevGraphNode = curGraphNode; } + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].nodes.push(curGraphNode); + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].edges.push(curEdge); + // 特殊狀況,在for迴圈之外額外插入最後一條線段 + if (nodeIndex === curButton[listIndex].length - 1) { + const endNode = curGraphNode.outgoers('node').filter(neighbor => + neighbor.data('label').toLocaleLowerCase() === 'end' + ); + const lastEdge = curGraphNode.edgesWith(endNode); + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[i][0]][listIndex].edges.push(lastEdge); + } + prevGraphNode = curGraphNode; } } - }, - async createPaths() { - this.startNode = this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes() - .filter(function(elem) { + } + }, + async createPaths() { + this.startNode = this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes() + .filter(function (elem) { return elem.data('label').toLocaleLowerCase() === 'start'; }); - // Depth First Search from the starting node - await this.depthFirstSearchCreatePath(this.startNode, [this.startNode], []); - const { insights } = AllMapData(); - this.insights = {...insights}; - await this.matchGraphPathWithInsightsPath(); - }, - /** 從start節點開始建立所有的path - * 於第二個參數逐漸推入節點,於第三個參數逐漸推入線段 - */ - depthFirstSearchCreatePath(node, currentPathByNode, curPathByEdge){ - const outgoingEdges = node.outgoers('edge'); - if (outgoingEdges.length === 0) { - // 表示已經遇到尾聲 - this.allPaths.push([...currentPathByNode]); - this.allPathsByEdge.push([...curPathByEdge]) - } else { - outgoingEdges.targets().forEach((targetNode) => { - if (!currentPathByNode.includes(targetNode)) { - const connectingEdge = targetNode.edgesWith(currentPathByNode[currentPathByNode.length - 1]); - // 避免loop,只有當目標節點不在當前路徑中之時才繼續 - this.depthFirstSearchCreatePath(targetNode, [...currentPathByNode, targetNode], - [...curPathByEdge, connectingEdge] - ); - } - }); - } - }, - /** - * 比對兩條Paths是否相等。 - * 第一條path是透過depthFirstSearchCreatePath所建立, - * 而第二條path是從後端給的insights物件而來,其資料結構是簡單的array而已。 - * 在每條path沿路據節點上的label之 - * 字串來匹配這個path是屬於insights物件的哪一條path, - * 其中用curButton去記憶住insights[INSIGHTS_FIELDS_AND_LABELS[i][0]]內文 - * 而curButton[listIndex][nodeIndex]是用來確認是否跟depthFirstSearchCreatePath內的 - * node.data('label')字串完全相等,也就是 activity 節點的文字 - */ - matchGraphPathWithInsightsPath(){ - for(let whichPath = 0; whichPath < this.allPaths.length; whichPath++) { - const curPath = this.allPaths[whichPath]; - const curPathByEdge = this.allPathsByEdge[whichPath]; - // 針對這個path的第一個節點,找到它在insights中是對應到哪一個起點 - for(let i = 0; i < INSIGHTS_FIELDS_AND_LABELS.length; i++) { - const curButton = this.insights[INSIGHTS_FIELDS_AND_LABELS[i][0]]; - for(let listIndex = 0; listIndex < curButton.length; listIndex++) { - for(let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++){ - if(curPath[1].data('label') === curButton[listIndex][nodeIndex]){ - // 從 1 開始而不是從 0 開始是因為 0 的label是start字串 - const matchResult = this.depthFirstSearchMatchTwoPaths(curPath, 1, curButton, listIndex, nodeIndex) - if(matchResult){ - this.mapGraphPathToInsight[i] = { - [listIndex] : { - pathByNode: [...curPath], - pathByEdge: [...curPathByEdge], - pathType: INSIGHTS_FIELDS_AND_LABELS[i][0], - } + // Depth First Search from the starting node + await this.depthFirstSearchCreatePath(this.startNode, [this.startNode], []); + const { insights } = AllMapData(); + this.insights = { ...insights }; + await this.matchGraphPathWithInsightsPath(); + }, + /** 從start節點開始建立所有的path + * 於第二個參數逐漸推入節點,於第三個參數逐漸推入線段 + */ + depthFirstSearchCreatePath(node, currentPathByNode, curPathByEdge) { + const outgoingEdges = node.outgoers('edge'); + if (outgoingEdges.length === 0) { + // 表示已經遇到尾聲 + this.allPaths.push([...currentPathByNode]); + this.allPathsByEdge.push([...curPathByEdge]) + } else { + outgoingEdges.targets().forEach((targetNode) => { + if (!currentPathByNode.includes(targetNode)) { + const connectingEdge = targetNode.edgesWith(currentPathByNode[currentPathByNode.length - 1]); + // 避免loop,只有當目標節點不在當前路徑中之時才繼續 + this.depthFirstSearchCreatePath(targetNode, [...currentPathByNode, targetNode], + [...curPathByEdge, connectingEdge] + ); + } + }); + } + }, + /** + * 比對兩條Paths是否相等。 + * 第一條path是透過depthFirstSearchCreatePath所建立, + * 而第二條path是從後端給的insights物件而來,其資料結構是簡單的array而已。 + * 在每條path沿路據節點上的label之 + * 字串來匹配這個path是屬於insights物件的哪一條path, + * 其中用curButton去記憶住insights[INSIGHTS_FIELDS_AND_LABELS[i][0]]內文 + * 而curButton[listIndex][nodeIndex]是用來確認是否跟depthFirstSearchCreatePath內的 + * node.data('label')字串完全相等,也就是 activity 節點的文字 + */ + matchGraphPathWithInsightsPath() { + for (let whichPath = 0; whichPath < this.allPaths.length; whichPath++) { + const curPath = this.allPaths[whichPath]; + const curPathByEdge = this.allPathsByEdge[whichPath]; + // 針對這個path的第一個節點,找到它在insights中是對應到哪一個起點 + for (let i = 0; i < INSIGHTS_FIELDS_AND_LABELS.length; i++) { + const curButton = this.insights[INSIGHTS_FIELDS_AND_LABELS[i][0]]; + for (let listIndex = 0; listIndex < curButton.length; listIndex++) { + for (let nodeIndex = 0; nodeIndex < curButton[listIndex].length; nodeIndex++) { + if (curPath[1].data('label') === curButton[listIndex][nodeIndex]) { + // 從 1 開始而不是從 0 開始是因為 0 的label是start字串 + const matchResult = this.depthFirstSearchMatchTwoPaths(curPath, 1, curButton, listIndex, nodeIndex) + if (matchResult) { + this.mapGraphPathToInsight[i] = { + [listIndex]: { + pathByNode: [...curPath], + pathByEdge: [...curPathByEdge], + pathType: INSIGHTS_FIELDS_AND_LABELS[i][0], } - } // end if - } - } // end fourth for - } // end third for - } // end second for - } // end first for - }, - depthFirstSearchMatchTwoPaths(curPath, curPathIndex, curButton, listIndex, nodeIndex){ - if(listIndex >= curButton.length) { // 邊界條件檢查,防止超出範圍 - return; // nodeIndex表示是當選訂了五顆按鈕之一之後,清單上的第幾個path + } + } // end if + } + } // end fourth for + } // end third for + } // end second for + } // end first for + }, + depthFirstSearchMatchTwoPaths(curPath, curPathIndex, curButton, listIndex, nodeIndex) { + if (listIndex >= curButton.length) { // 邊界條件檢查,防止超出範圍 + return; // nodeIndex表示是當選訂了五顆按鈕之一之後,清單上的第幾個path + } + if (nodeIndex >= curButton[listIndex]) { // 邊界條件檢查,防止超出範圍 + return; // 表示清單上這個path上的第幾個節點 + } + // 如果 `curPath` 和 `curButton[listIndex]` 完全匹配 + if (curPathIndex === curPath.length || nodeIndex === curButton[listIndex].length) { + return true; + } + + // 邊界條件檢查,防止超出範圍 + if (curPathIndex >= curPath.length || nodeIndex >= curButton[listIndex].length) { + return; + } + + const nodeLabel = curPath[curPathIndex].data('label'); + // 如果當前節點匹配 + if (nodeLabel === curButton[listIndex][nodeIndex]) { + if (nodeIndex === curButton[listIndex].length - 1) { + return true; // Reach } - if(nodeIndex >= curButton[listIndex]) { // 邊界條件檢查,防止超出範圍 - return; // 表示清單上這個path上的第幾個節點 - } - // 如果 `curPath` 和 `curButton[listIndex]` 完全匹配 - if (curPathIndex === curPath.length || nodeIndex === curButton[listIndex].length) { + //從以下兩個選項選出答案可能是true的。但也可能答案都是false + // 選項一是遞增insights的第一層的指標。這裡必須遞增path的指標 + if (this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex + 1, nodeIndex)) { return true; } - - // 邊界條件檢查,防止超出範圍 - if (curPathIndex >= curPath.length || nodeIndex >= curButton[listIndex].length) { - return; + // 選項二是遞增insights的第一層的指標。這裡必須遞增path的指標 + if (this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex, nodeIndex + 1)) { + return true; } - - const nodeLabel = curPath[curPathIndex].data('label'); - // 如果當前節點匹配 - if (nodeLabel === curButton[listIndex][nodeIndex]) { - if(nodeIndex === curButton[listIndex].length - 1) { - return true; // Reach - } - //從以下兩個選項選出答案可能是true的。但也可能答案都是false - // 選項一是遞增insights的第一層的指標。這裡必須遞增path的指標 - if(this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex + 1, nodeIndex)) { - return true; - } - // 選項二是遞增insights的第一層的指標。這裡必須遞增path的指標 - if(this.depthFirstSearchMatchTwoPaths(curPath, curPathIndex + 1, curButton, listIndex, nodeIndex + 1)) { - return true; - } - } - return false; // 當前節點不匹配時返回 false - }, - highlightClickedPath(clickedActiveTraceIndex: number, clickedPathListIndex: number){ - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[clickedActiveTraceIndex][0]][clickedPathListIndex].edges.forEach(edgeToHighlight => { - edgeToHighlight.addClass('highlight-edge'); - }); - this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[clickedActiveTraceIndex][0]][clickedPathListIndex].nodes.forEach(nodeToHighlight => { - nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); - }); - }, - highlightClickedPathUnused(clickedActiveTraceIndex: number, clickedPathListIndex: number) { - this.activeTrace = clickedActiveTraceIndex; - this.activeListIndex = clickedPathListIndex; - this.mapGraphPathToInsight[clickedActiveTraceIndex][clickedPathListIndex].pathByEdge - .forEach(pathToHighlight => { - pathToHighlight.addClass('highlight-edge'); - }); - this.mapGraphPathToInsight[clickedActiveTraceIndex][clickedPathListIndex].pathByNode - .forEach(nodeToHighlight => { - nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); - }) - }, - clearAllHighlight() { - this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.edges().removeClass('highlight-edge'); - this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes().removeClass('highlight-node'); - this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes().forEach(nodeToReset => { - nodeToReset.data('nodeImageUrl', ImgCapsules[nodeToReset.data('level')]) - }); - }, - onNodeClickHighlightEdges(clickedNode) { - this.clearAllHighlight(); - clickedNode.addClass('highlight-node'); - clickedNode.data('nodeImageUrl', ImgCapsulesGlow[clickedNode.data('level')]); - clickedNode.outgoers('edge').forEach(edgeToHighlight => edgeToHighlight.addClass('highlight-edge')); - clickedNode.incomers('edge').forEach(edgeToHighlight => edgeToHighlight.addClass('highlight-edge')); - this.lastClickedNode = clickedNode; - }, - onEdgeClickHighlightNodes(clickedEdge) { - this.clearAllHighlight(); - - const sourceNode = clickedEdge.source(); - const targetNode = clickedEdge.target(); - - sourceNode.addClass('highlight-node'); - targetNode.addClass('highlight-node'); - - sourceNode.data('nodeImageUrl', ImgCapsulesGlow[sourceNode.data('level')]); - targetNode.data('nodeImageUrl', ImgCapsulesGlow[targetNode.data('level')]); - - clickedEdge.addClass('highlight-edge'); - }, - async highlightMostFrequentPath() { - console.log('highlightMostFrequentPath', this.insightWithPath['most_freq_traces'][0]); - const LIST_INDEX = 0; - this.insightWithPath['most_freq_traces'][LIST_INDEX].nodes.map(nodeToHighlight => { - nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); - }); - this.insightWithPath['most_freq_traces'][LIST_INDEX].edges.map(edgeToHighlight => - edgeToHighlight.addClass('highlight-edge')); - }, - async highlightMostFrequentPathUnused() { - for(let buttonIter = 0; buttonIter < INSIGHTS_FIELDS_AND_LABELS.length; buttonIter++) { - // 有可能遇到兩個以上的most frequent paths,然而我們只取一個path點亮 - if (this.mapGraphPathToInsight[buttonIter]) { - const keyLength = Object.keys(this.mapGraphPathToInsight[buttonIter]).length; - for(let i = 0; i < keyLength; i++) { - if(this.mapGraphPathToInsight[buttonIter][i]?.pathType === 'most_freq_traces'){ - await this.mapGraphPathToInsight[buttonIter][i].pathByNode.map(async(nodeToHighlight) => { - await nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); - }); - await this.mapGraphPathToInsight[buttonIter][i].pathByEdge.map( - async(edgeToHighlight) => { - await edgeToHighlight.addClass('highlight-edge'); - }); - return; // 之所以要此時就立刻return是因為要避免第二個以上的most freq path也被點亮 - } - break; - } - } - } - }, - setIsBPMNOn(isOn: boolean) { - this.isBPMNOn = isOn; - }, + } + return false; // 當前節點不匹配時返回 false }, + highlightClickedPath(clickedActiveTraceIndex: number, clickedPathListIndex: number) { + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[clickedActiveTraceIndex][0]][clickedPathListIndex].edges.forEach(edgeToHighlight => { + edgeToHighlight.addClass('highlight-edge'); + }); + this.insightWithPath[INSIGHTS_FIELDS_AND_LABELS[clickedActiveTraceIndex][0]][clickedPathListIndex].nodes.forEach(nodeToHighlight => { + nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); + }); + }, + highlightClickedPathUnused(clickedActiveTraceIndex: number, clickedPathListIndex: number) { + this.activeTrace = clickedActiveTraceIndex; + this.activeListIndex = clickedPathListIndex; + this.mapGraphPathToInsight[clickedActiveTraceIndex][clickedPathListIndex].pathByEdge + .forEach(pathToHighlight => { + pathToHighlight.addClass('highlight-edge'); + }); + this.mapGraphPathToInsight[clickedActiveTraceIndex][clickedPathListIndex].pathByNode + .forEach(nodeToHighlight => { + nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); + }) + }, + clearAllHighlight() { + this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.edges().removeClass('highlight-edge'); + this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes().removeClass('highlight-node'); + this.cytoscape[this.processOrBPMN][this.curveType][this.directionType]?.nodes().forEach(nodeToReset => { + nodeToReset.data('nodeImageUrl', ImgCapsules[nodeToReset.data('level')]) + }); + }, + onNodeClickHighlightEdges(clickedNode) { + this.clearAllHighlight(); + clickedNode.addClass('highlight-node'); + clickedNode.data('nodeImageUrl', ImgCapsulesGlow[clickedNode.data('level')]); + clickedNode.outgoers('edge').forEach(edgeToHighlight => edgeToHighlight.addClass('highlight-edge')); + clickedNode.incomers('edge').forEach(edgeToHighlight => edgeToHighlight.addClass('highlight-edge')); + this.lastClickedNode = clickedNode; + }, + onEdgeClickHighlightNodes(clickedEdge) { + this.clearAllHighlight(); + + const sourceNode = clickedEdge.source(); + const targetNode = clickedEdge.target(); + + sourceNode.addClass('highlight-node'); + targetNode.addClass('highlight-node'); + + sourceNode.data('nodeImageUrl', ImgCapsulesGlow[sourceNode.data('level')]); + targetNode.data('nodeImageUrl', ImgCapsulesGlow[targetNode.data('level')]); + + clickedEdge.addClass('highlight-edge'); + }, + async highlightMostFrequentPath() { + const LIST_INDEX = 0; + this.insightWithPath['most_freq_traces'][LIST_INDEX].nodes.map(nodeToHighlight => { + nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); + }); + this.insightWithPath['most_freq_traces'][LIST_INDEX].edges.map(edgeToHighlight => + edgeToHighlight.addClass('highlight-edge')); + }, + async highlightMostFrequentPathUnused() { + for (let buttonIter = 0; buttonIter < INSIGHTS_FIELDS_AND_LABELS.length; buttonIter++) { + // 有可能遇到兩個以上的most frequent paths,然而我們只取一個path點亮 + if (this.mapGraphPathToInsight[buttonIter]) { + const keyLength = Object.keys(this.mapGraphPathToInsight[buttonIter]).length; + for (let i = 0; i < keyLength; i++) { + if (this.mapGraphPathToInsight[buttonIter][i]?.pathType === 'most_freq_traces') { + await this.mapGraphPathToInsight[buttonIter][i].pathByNode.map(async (nodeToHighlight) => { + await nodeToHighlight.data('nodeImageUrl', ImgCapsulesGlow[nodeToHighlight.data('level')]); + }); + await this.mapGraphPathToInsight[buttonIter][i].pathByEdge.map( + async (edgeToHighlight) => { + await edgeToHighlight.addClass('highlight-edge'); + }); + return; // 之所以要此時就立刻return是因為要避免第二個以上的most freq path也被點亮 + } + break; + } + } + } + }, + setIsBPMNOn(isOn: boolean) { + this.isBPMNOn = isOn; + }, + }, }); \ No newline at end of file diff --git a/src/views/Discover/Map/Map.vue b/src/views/Discover/Map/Map.vue index 1888c7e..470d1d6 100644 --- a/src/views/Discover/Map/Map.vue +++ b/src/views/Discover/Map/Map.vue @@ -1,22 +1,29 @@