Discover: cytoscape map && switch data type done.
This commit is contained in:
88
package-lock.json
generated
88
package-lock.json
generated
@@ -11,7 +11,9 @@
|
|||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.2.2",
|
||||||
"cytoscape": "^3.23.0",
|
"cytoscape": "^3.23.0",
|
||||||
|
"cytoscape-dagre": "^2.5.0",
|
||||||
"cytoscape-klay": "^3.1.4",
|
"cytoscape-klay": "^3.1.4",
|
||||||
|
"cytoscape-popper": "^2.0.0",
|
||||||
"javascript-color-gradient": "^2.4.4",
|
"javascript-color-gradient": "^2.4.4",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
@@ -629,6 +631,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||||
|
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rushstack/eslint-patch": {
|
"node_modules/@rushstack/eslint-patch": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||||
@@ -2057,6 +2068,17 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cytoscape-dagre": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cytoscape-dagre/-/cytoscape-dagre-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-VG2Knemmshop4kh5fpLO27rYcyUaaDkRw+6PiX4bstpB+QFt0p2oauMrsjVbUamGWQ6YNavh7x2em2uZlzV44g==",
|
||||||
|
"dependencies": {
|
||||||
|
"dagre": "^0.8.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"cytoscape": "^3.2.22"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cytoscape-klay": {
|
"node_modules/cytoscape-klay": {
|
||||||
"version": "3.1.4",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz",
|
||||||
@@ -2068,6 +2090,26 @@
|
|||||||
"cytoscape": "^3.2.0"
|
"cytoscape": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cytoscape-popper": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cytoscape-popper/-/cytoscape-popper-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-b7WSOn8qXHWtdIXFNmrgc8qkaOs16tMY0EwtRXlxzvn8X+al6TAFrUwZoYATkYSlotfd/36ZMoeKMEoUck6feA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"cytoscape": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dagre": {
|
||||||
|
"version": "0.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
|
||||||
|
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
|
||||||
|
"dependencies": {
|
||||||
|
"graphlib": "^2.1.8",
|
||||||
|
"lodash": "^4.17.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dashdash": {
|
"node_modules/dashdash": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||||
@@ -3243,6 +3285,14 @@
|
|||||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
|
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/graphlib": {
|
||||||
|
"version": "2.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
|
||||||
|
"integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has": {
|
"node_modules/has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
@@ -6606,6 +6656,11 @@
|
|||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@popperjs/core": {
|
||||||
|
"version": "2.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||||
|
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
|
||||||
|
},
|
||||||
"@rushstack/eslint-patch": {
|
"@rushstack/eslint-patch": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||||
@@ -7743,6 +7798,14 @@
|
|||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cytoscape-dagre": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cytoscape-dagre/-/cytoscape-dagre-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-VG2Knemmshop4kh5fpLO27rYcyUaaDkRw+6PiX4bstpB+QFt0p2oauMrsjVbUamGWQ6YNavh7x2em2uZlzV44g==",
|
||||||
|
"requires": {
|
||||||
|
"dagre": "^0.8.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cytoscape-klay": {
|
"cytoscape-klay": {
|
||||||
"version": "3.1.4",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape-klay/-/cytoscape-klay-3.1.4.tgz",
|
||||||
@@ -7751,6 +7814,23 @@
|
|||||||
"klayjs": "^0.4.1"
|
"klayjs": "^0.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cytoscape-popper": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cytoscape-popper/-/cytoscape-popper-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-b7WSOn8qXHWtdIXFNmrgc8qkaOs16tMY0EwtRXlxzvn8X+al6TAFrUwZoYATkYSlotfd/36ZMoeKMEoUck6feA==",
|
||||||
|
"requires": {
|
||||||
|
"@popperjs/core": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dagre": {
|
||||||
|
"version": "0.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
|
||||||
|
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
|
||||||
|
"requires": {
|
||||||
|
"graphlib": "^2.1.8",
|
||||||
|
"lodash": "^4.17.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dashdash": {
|
"dashdash": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||||
@@ -8632,6 +8712,14 @@
|
|||||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
|
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"graphlib": {
|
||||||
|
"version": "2.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
|
||||||
|
"integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.17.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"has": {
|
"has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.2.2",
|
||||||
"cytoscape": "^3.23.0",
|
"cytoscape": "^3.23.0",
|
||||||
"cytoscape-klay": "^3.1.4",
|
"cytoscape-dagre": "^2.5.0",
|
||||||
"javascript-color-gradient": "^2.4.4",
|
"javascript-color-gradient": "^2.4.4",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import moment from 'moment';
|
|||||||
import mitt from 'mitt';
|
import mitt from 'mitt';
|
||||||
import ToastPlugin from 'vue-toast-notification';
|
import ToastPlugin from 'vue-toast-notification';
|
||||||
import cytoscape from 'cytoscape';
|
import cytoscape from 'cytoscape';
|
||||||
import klay from 'cytoscape-klay';
|
import dagre from 'cytoscape-dagre';
|
||||||
|
|
||||||
import "./assets/main.css";
|
import "./assets/main.css";
|
||||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||||
@@ -29,7 +29,7 @@ app.config.globalProperties.$moment = moment;
|
|||||||
app.config.globalProperties.emitter = emitter;
|
app.config.globalProperties.emitter = emitter;
|
||||||
app.config.globalProperties.$cytoscape = cytoscape;
|
app.config.globalProperties.$cytoscape = cytoscape;
|
||||||
|
|
||||||
cytoscape.use( klay );
|
cytoscape.use( dagre );
|
||||||
|
|
||||||
app.use(pinia);
|
app.use(pinia);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|||||||
197
src/module/cytoscapeMap.js
Normal file
197
src/module/cytoscapeMap.js
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import cytoscape from 'cytoscape';
|
||||||
|
import dagre from 'cytoscape-dagre';
|
||||||
|
import Gradient from 'javascript-color-gradient'; // 多個色階產生器
|
||||||
|
import TimeLabel from '@/module/timeLabel.js'; // 時間格式轉換器
|
||||||
|
|
||||||
|
cytoscape.use( dagre );
|
||||||
|
|
||||||
|
export default function cytoscapeMap(mapData, mapType, dataLayerType, dataLayerOption, curveStyle, rank, graphId) {
|
||||||
|
// console.log(mapData);
|
||||||
|
// console.log(mapType);
|
||||||
|
// console.log(dataLayerType);
|
||||||
|
// console.log(dataLayerOption);
|
||||||
|
// console.log(curveStyle);
|
||||||
|
// console.log(rank);
|
||||||
|
// create color Gradient
|
||||||
|
// 設定每個 node, edges 的顏色與樣式
|
||||||
|
let gradientArray = [];
|
||||||
|
let activityArray = [];
|
||||||
|
let edgeArray = [];
|
||||||
|
let nodeOption = [];
|
||||||
|
let edgeOption = [];
|
||||||
|
let nodes = mapData.nodes;
|
||||||
|
let edges = mapData.edges;
|
||||||
|
|
||||||
|
// 設定除了 start, end 的 node 顏色
|
||||||
|
// 找出 type activity's node
|
||||||
|
activityArray = nodes.filter(i => i.data.type === 'activity');
|
||||||
|
|
||||||
|
// 找出除了 start, end 以外所有的 node 的 option value
|
||||||
|
activityArray.map(node => nodeOption.push(node.data[dataLayerType][dataLayerOption]));
|
||||||
|
|
||||||
|
// 刪掉重複的元素,小到大排序(映對色階淺到深)
|
||||||
|
nodeOption = [...new Set(nodeOption)].sort((a, b) => a - b);
|
||||||
|
|
||||||
|
// 產生 node 色階
|
||||||
|
gradientArray = new Gradient()
|
||||||
|
.setColorGradient("#CCE5FF", "#66b2ff")
|
||||||
|
.setMidpoint(nodeOption.length)
|
||||||
|
.getColors();
|
||||||
|
|
||||||
|
// 設定每個 node 的背景色
|
||||||
|
activityArray.forEach(node => {
|
||||||
|
// 透過 index 找對應位置
|
||||||
|
let gradientIndex = nodeOption.indexOf(node.data[dataLayerType][dataLayerOption]);
|
||||||
|
node.data.backgroundColor = gradientArray[gradientIndex];
|
||||||
|
})
|
||||||
|
|
||||||
|
// 設定除了 start, end 的 edges 粗細
|
||||||
|
// 找出除了 start, end 以外所有的 edge
|
||||||
|
edgeArray = edges.filter(i => i.data.source !== mapData.startId && i.data.target !== mapData.endId);
|
||||||
|
|
||||||
|
// 找出所有 edge 的 option value
|
||||||
|
edgeArray.map(edge => edgeOption.push(edge.data[dataLayerType][dataLayerOption]));
|
||||||
|
|
||||||
|
// 刪掉重複的元素,小到大排序(映對色階淺到深)
|
||||||
|
edgeOption = [...new Set(edgeOption)].sort((a, b) => a - b);
|
||||||
|
|
||||||
|
// 設定每個 edge 的粗細
|
||||||
|
edgeArray.forEach(edge => {
|
||||||
|
let edgeIndex = edgeOption.indexOf(edge.data[dataLayerType][dataLayerOption])
|
||||||
|
edge.data.lineWidth = (parseInt(edgeIndex) + 1) * 0.15
|
||||||
|
edge.data.style = 'solid'
|
||||||
|
})
|
||||||
|
|
||||||
|
// create Cytoscape
|
||||||
|
let cy = cytoscape({
|
||||||
|
container: graphId,
|
||||||
|
elements: {
|
||||||
|
nodes: nodes, //nodes, // 節點的資料
|
||||||
|
edges: edges, //edges, // 關係線的資料
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
name: 'dagre',
|
||||||
|
rankDir: rank, // 直向 TB | 橫向 LR, 'cytoscape-dagre' 裡的變數
|
||||||
|
|
||||||
|
},
|
||||||
|
style: [
|
||||||
|
// 點擊 node 後改變的樣式
|
||||||
|
{
|
||||||
|
selector:'node:selected',
|
||||||
|
style:{
|
||||||
|
'border-color':'red',
|
||||||
|
'border-width':'3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// node 節點的樣式
|
||||||
|
{
|
||||||
|
selector: 'node',
|
||||||
|
style: {
|
||||||
|
'label':
|
||||||
|
function(node) { // 節點要顯示的文字
|
||||||
|
// node.data(this.dataLayerType+"."+this.dataLayerOption) 為原先陣列 node.data.key.value
|
||||||
|
let optionValue = node.data(`${dataLayerType}.${dataLayerOption}`);
|
||||||
|
let text = '';
|
||||||
|
|
||||||
|
// 文字超過 10 字尾巴要加「...」,style 要換兩行(\n 換行符號)
|
||||||
|
// 使用 data() 是因為在 cytoscape 中從陣列轉為 function
|
||||||
|
text = node.data('label').length > 10 ? `${node.data('label').substr(0, 10)}...\n\n` : `${node.data('label')}\n\n`;
|
||||||
|
|
||||||
|
// 在 element 中 activity 歸類在 default,所以要先判斷 node 是否為 activeity 才裝入文字。
|
||||||
|
// 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字
|
||||||
|
// Relative 要轉為百分比 %
|
||||||
|
if(node.data('type') === 'activity') {
|
||||||
|
switch(dataLayerType) {
|
||||||
|
case 'freq': // Frequency
|
||||||
|
let textInt = dataLayerOption === 'rel_freq' ? text + optionValue * 100 + "%" : text + optionValue;
|
||||||
|
let textFloat = dataLayerOption === 'rel_freq'? text + (optionValue * 100).toFixed(2) + "%" : text + optionValue.toFixed(2);
|
||||||
|
|
||||||
|
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
||||||
|
text = Math.trunc(optionValue) === optionValue ? textInt : textFloat;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
|
||||||
|
// Relative %
|
||||||
|
let textDurRel = text + (optionValue * 100).toFixed(2) + "%";
|
||||||
|
// Timelabel
|
||||||
|
let timeLabelInt = text + TimeLabel(optionValue);
|
||||||
|
let timeLabelFloat = text + TimeLabel(optionValue.toFixed(2));
|
||||||
|
|
||||||
|
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
||||||
|
let textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
|
||||||
|
|
||||||
|
text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
},
|
||||||
|
'text-opacity':0.7,
|
||||||
|
'background-color': 'data(backgroundColor)',
|
||||||
|
'border-color':'data(bordercolor)',
|
||||||
|
'border-width':
|
||||||
|
function(node) {
|
||||||
|
return node.data('type') === 'activity' ? '1' : '2';
|
||||||
|
},
|
||||||
|
//'border-radius': '5',
|
||||||
|
'shape':'data(shape)',
|
||||||
|
'text-wrap': 'wrap',
|
||||||
|
'text-max-width':75,
|
||||||
|
'text-halign': 'center',
|
||||||
|
'text-valign': 'center',
|
||||||
|
'height': 'data(height)',
|
||||||
|
'width': 'data(width)',
|
||||||
|
'color': '#001933',
|
||||||
|
'font-size':
|
||||||
|
function(node) {
|
||||||
|
return node.data('type') === 'activity' ? 14 : 22;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// edge 關係線的樣式
|
||||||
|
{
|
||||||
|
selector: 'edge',
|
||||||
|
style: {
|
||||||
|
'content': function(edge) { // 關係線顯示的文字
|
||||||
|
let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`);
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
if(optionValue === '') return optionValue;
|
||||||
|
|
||||||
|
switch(dataLayerType) {
|
||||||
|
case 'freq':
|
||||||
|
let edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue;
|
||||||
|
let edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2);
|
||||||
|
|
||||||
|
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
||||||
|
result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
|
||||||
|
// Relative %
|
||||||
|
let edgeDurRel = (optionValue * 100).toFixed(2) + "%";
|
||||||
|
// Timelabel
|
||||||
|
let timeLabelInt = TimeLabel(optionValue);
|
||||||
|
let timeLabelFloat = TimeLabel(optionValue.toFixed(2));
|
||||||
|
let edgeTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
|
||||||
|
|
||||||
|
result = dataLayerOption === 'rel_duration' ? edgeDurRel : edgeTimeLabel;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
'curve-style': curveStyle, // unbundled-bezier | taxi
|
||||||
|
'target-arrow-shape': 'triangle', // 指向目標的箭頭形狀: 三角形
|
||||||
|
'color': 'gray', //#0066cc
|
||||||
|
//'control-point-step-size':100, // 從點到點的垂直線,指定貝茲取線邊緣間的距離
|
||||||
|
'width':'data(lineWidth)',
|
||||||
|
'line-style':'data(style)',
|
||||||
|
"text-margin-y": "15rem",
|
||||||
|
//"text-rotation": "autorotate",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,11 +18,9 @@ export default defineStore('allMapDataStore', {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.$axios.get(api);
|
const response = await this.$axios.get(api);
|
||||||
// console.log(response);
|
|
||||||
this.processMap = response.data.process_map;
|
this.processMap = response.data.process_map;
|
||||||
this.bpmn = response.data.bpmn;
|
this.bpmn = response.data.bpmn;
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
// console.dir(error);
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,43 +3,44 @@
|
|||||||
<div>
|
<div>
|
||||||
<!-- 選擇 bpmn / processmap button -->
|
<!-- 選擇 bpmn / processmap button -->
|
||||||
<ul class="m-3 flex">
|
<ul class="m-3 flex">
|
||||||
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="mapType = 'processMap'">Process map</li>
|
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchMapType('processMap')">Process map</li>
|
||||||
<li class="rounded-xl p-2 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="mapType = 'bpmn'">BPMN model</li>
|
<li class="rounded-xl p-2 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchMapType('bpmn')">BPMN model</li>
|
||||||
<li class="p-2">map type: {{ mapType }}</li>
|
<li class="p-2">map type: {{ mapType }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- 選擇繪畫樣式 bezier / unbundled-bezier button-->
|
<!-- 選擇繪畫樣式 bezier / unbundled-bezier button-->
|
||||||
<ul class="m-3 flex">
|
<ul class="m-3 flex">
|
||||||
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="curveStyle = 'bezier'">bezier</li>
|
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchCurveStyles('bezier')">bezier</li>
|
||||||
<li class="rounded-xl p-2 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="curveStyle = 'unbundled-bezier'">unbundled-bezier</li>
|
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchCurveStyles('unbundled-bezier')">unbundled-bezier</li>
|
||||||
|
<li class="rounded-xl p-2 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchCurveStyles('taxi')">taxi</li>
|
||||||
<li class="p-2">curve style: {{ curveStyle }}</li>
|
<li class="p-2">curve style: {{ curveStyle }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- 直向 TB | 橫向 LR -->
|
<!-- 直向 TB | 橫向 LR -->
|
||||||
<ul class="m-3 flex">
|
<ul class="m-3 flex">
|
||||||
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="rank = 'LR'">LR</li>
|
<li class="rounded-xl p-2 mr-3 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchRank('LR')">LR</li>
|
||||||
<li class="rounded-xl p-2 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="rank = 'TB'">TB</li>
|
<li class="rounded-xl p-2 cursor-pointer border border-[#F0EFF4] hover:bg-primary" @click="switchRank('TB')">TB</li>
|
||||||
<li class="p-2">rank: {{ rank }}</li>
|
<li class="p-2">rank: {{ rank }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- Data Layer type -->
|
<!-- Data Layer type -->
|
||||||
<ul class="m-3" @change="switchDataLayerType($event)">
|
<ul class="m-3">
|
||||||
<li class="flex mb-3">
|
<li class="flex mb-3" @change="switchDataLayerType($event, 'freq')">
|
||||||
<div class="mr-5">
|
<div class="mr-5">
|
||||||
<input type="radio" id="freq" value="freq" name="dataLayer" />
|
<input type="radio" id="freq" value="freq" name="dataLayer" checked/>
|
||||||
<label for="freq">Frequency</label>
|
<label for="freq">Frequency</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<select class="border-b">
|
<select class="border-b" :disabled="dataLayerType === 'duration'">
|
||||||
<option v-for="(freq, index) in selectFrequency" :key="index" :value="freq.value" :disabled="freq.disabled">{{ freq.label }}</option>
|
<option v-for="(freq, index) in selectFrequency" :key="index" :value="freq.value" :disabled="freq.disabled" :selected="freq.value === selectedFreq">{{ freq.label }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex mb-3">
|
<li class="flex mb-3" @change="switchDataLayerType($event, 'duration')">
|
||||||
<div class="mr-5">
|
<div class="mr-5">
|
||||||
<input type="radio" id="duration" value="duration" name="dataLayer" />
|
<input type="radio" id="duration" value="duration" name="dataLayer" />
|
||||||
<label for="duration">Duration</label>
|
<label for="duration">Duration</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<select class="border-b">
|
<select class="border-b" :disabled="dataLayerType === 'freq'">
|
||||||
<option v-for="(duration, index) in selectDuration" :key="index" :value="duration.value" :disabled="duration.disabled">{{ duration.label }}</option>
|
<option v-for="(duration, index) in selectDuration" :key="index" :value="duration.value" :disabled="duration.disabled" :selected="duration.value === selectedDuration">{{ duration.label }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -56,8 +57,7 @@
|
|||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import LoadingStore from '@/stores/loading.js';
|
import LoadingStore from '@/stores/loading.js';
|
||||||
import AllMapDataStore from '@/stores/allMapData.js';
|
import AllMapDataStore from '@/stores/allMapData.js';
|
||||||
import Gradient from 'javascript-color-gradient'; // 多個色階產生器
|
import cytoscapeMap from '@/module/cytoscapeMap.js' //
|
||||||
import TimeLabel from '@/module/timeLabel.js'; // 時間格式轉換器
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
@@ -104,79 +104,71 @@ export default {
|
|||||||
{ value:"max", label:"Max", disabled:false, },
|
{ value:"max", label:"Max", disabled:false, },
|
||||||
{ value:"min", label:"Min", disabled:false, },
|
{ value:"min", label:"Min", disabled:false, },
|
||||||
],
|
],
|
||||||
curveStyle:'unbundled-bezier', // 貝茲曲線 bezier | 直角 unbundled-bezier
|
curveStyle:'unbundled-bezier', // unbundled-bezier | taxi
|
||||||
mapType: 'processMap', // processMap | bpmn
|
mapType: 'processMap', // processMap | bpmn
|
||||||
|
mapData: {},
|
||||||
dataLayerType: 'freq', // freq | duration
|
dataLayerType: 'freq', // freq | duration
|
||||||
dataLayerOption: 'total',
|
dataLayerOption: 'total',
|
||||||
|
selectedFreq: '',
|
||||||
|
selectedDuration: '',
|
||||||
rank: 'LR' // 直向 TB | 橫向 LR
|
rank: 'LR' // 直向 TB | 橫向 LR
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* switch Data Layoer Type or Option.
|
* switch map type
|
||||||
* @param {string} e
|
* @param {string} type processMap | bpmn
|
||||||
*/
|
*/
|
||||||
switchDataLayerType(e){
|
switchMapType(type) {
|
||||||
if(e.target.type === 'radio') this.dataLayerType = e.target.value;
|
this.mapType = type;
|
||||||
else if(e.target.type === 'select-one') this.dataLayerOption = e.target.value;
|
this.executeApi(type);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 設定每個 node, edges 的顏色與樣式
|
* switch curve style
|
||||||
|
* @param {string} style 直角 unbundled-bezier | taxi
|
||||||
*/
|
*/
|
||||||
createColorGradient() {
|
switchCurveStyles(style) {
|
||||||
let gradientArray = [];
|
this.curveStyle = style;
|
||||||
let activityArray = [];
|
this.executeApi(this.mapType);
|
||||||
let edgeArray = [];
|
},
|
||||||
let nodeOption = [];
|
/**
|
||||||
let edgeOption = [];
|
* switch rank
|
||||||
let nodes = this.processMapData.nodes;
|
* @param {string} rank 直向 TB | 橫向 LR
|
||||||
let edges = this.processMapData.edges;
|
*/
|
||||||
|
switchRank(rank) {
|
||||||
|
this.rank = rank;
|
||||||
|
this.executeApi(this.mapType);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* switch Data Layoer Type or Option.
|
||||||
|
* @param {string} e
|
||||||
|
* @param {string} type freq | duration
|
||||||
|
*/
|
||||||
|
switchDataLayerType(e, type){
|
||||||
|
let value = e.target.type === 'select-one'? e.target.value: 'total';
|
||||||
|
|
||||||
// 設定除了 start, end 的 node 顏色
|
switch (type) {
|
||||||
// 找出 type activity's node
|
case 'freq':
|
||||||
activityArray = nodes.filter(i => i.data.type === 'activity');
|
this.dataLayerType = type;
|
||||||
|
this.dataLayerOption = value;
|
||||||
|
this.selectedFreq = value;
|
||||||
|
break;
|
||||||
|
|
||||||
// 找出除了 start, end 以外所有的 node 的 option value
|
case 'duration':
|
||||||
activityArray.map(node => nodeOption.push(node.data[this.dataLayerType][this.dataLayerOption]));
|
this.dataLayerType = type;
|
||||||
|
this.dataLayerOption = value;
|
||||||
|
this.selectedDuration = value;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
// 刪掉重複的元素,小到大排序(映對色階淺到深)
|
this.executeApi(this.mapType);
|
||||||
nodeOption = [...new Set(nodeOption)].sort((a, b) => a - b);
|
|
||||||
|
|
||||||
// 產生 node 色階
|
|
||||||
gradientArray = new Gradient()
|
|
||||||
.setColorGradient("#CCE5FF", "#66b2ff")
|
|
||||||
.setMidpoint(nodeOption.length)
|
|
||||||
.getColors();
|
|
||||||
|
|
||||||
// 設定每個 node 的背景色
|
|
||||||
activityArray.forEach(node => {
|
|
||||||
// 透過 index 找對應位置
|
|
||||||
let gradientIndex = nodeOption.indexOf(node.data[this.dataLayerType][this.dataLayerOption]);
|
|
||||||
node.data.backgroundColor = gradientArray[gradientIndex];
|
|
||||||
})
|
|
||||||
|
|
||||||
// 設定除了 start, end 的 edges 粗細
|
|
||||||
// 找出除了 start, end 以外所有的 edge
|
|
||||||
edgeArray = edges.filter(i => i.data.source !== this.processMapData.startId && i.data.target !== this.processMapData.endId);
|
|
||||||
|
|
||||||
// 找出所有 edge 的 option value
|
|
||||||
edgeArray.map(edge => edgeOption.push(edge.data[this.dataLayerType][this.dataLayerOption]));
|
|
||||||
|
|
||||||
// 刪掉重複的元素,小到大排序(映對色階淺到深)
|
|
||||||
edgeOption = [...new Set(edgeOption)].sort((a, b) => a - b);
|
|
||||||
|
|
||||||
// 設定每個 edge 的粗細
|
|
||||||
edgeArray.forEach(edge => {
|
|
||||||
let edgeIndex = edgeOption.indexOf(edge.data[this.dataLayerType][this.dataLayerOption])
|
|
||||||
edge.data.lineWidth = (parseInt(edgeIndex) + 1) * 0.15
|
|
||||||
edge.data.style = 'solid'
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 將 element nodes 資料彙整
|
* 將 element nodes 資料彙整
|
||||||
* @param {string} type ProcessMap | BPMN
|
* @param {object} type processMapData | bpmnData
|
||||||
*/
|
*/
|
||||||
setNodesData(type) {
|
setNodesData(mapData) {
|
||||||
|
let mapType = this.mapType;
|
||||||
const logFreq = {
|
const logFreq = {
|
||||||
"total": "",
|
"total": "",
|
||||||
"rel_freq": "",
|
"rel_freq": "",
|
||||||
@@ -201,12 +193,14 @@ export default {
|
|||||||
inclusive: "o",
|
inclusive: "o",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 避免每次渲染都重複累加
|
||||||
|
mapData.nodes = [];
|
||||||
// 將 api call 回來的資料帶進 node
|
// 將 api call 回來的資料帶進 node
|
||||||
this.processMap.vertices.forEach(node => {
|
this[mapType].vertices.forEach(node => {
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
// add type of 'bpmn gateway' node
|
// add type of 'bpmn gateway' node
|
||||||
case 'gateway':
|
case 'gateway':
|
||||||
this.processMapData.nodes.push({
|
mapData.nodes.push({
|
||||||
data:{
|
data:{
|
||||||
id:node.id,
|
id:node.id,
|
||||||
type:node.type,
|
type:node.type,
|
||||||
@@ -223,10 +217,10 @@ export default {
|
|||||||
break;
|
break;
|
||||||
// add type of 'event' node
|
// add type of 'event' node
|
||||||
case 'event':
|
case 'event':
|
||||||
if(node.event_type === 'start') this.processMapData.startId = node.id;
|
if(node.event_type === 'start') mapData.startId = node.id;
|
||||||
else if(node.event_type === 'end') this.processMapData.endId = node.id;
|
else if(node.event_type === 'end') mapData.endId = node.id;
|
||||||
|
|
||||||
this.processMapData.nodes.push({
|
mapData.nodes.push({
|
||||||
data:{
|
data:{
|
||||||
id:node.id,
|
id:node.id,
|
||||||
type:node.type,
|
type:node.type,
|
||||||
@@ -243,7 +237,7 @@ export default {
|
|||||||
break;
|
break;
|
||||||
// add type of 'activity' node
|
// add type of 'activity' node
|
||||||
default:
|
default:
|
||||||
this.processMapData.nodes.push({
|
mapData.nodes.push({
|
||||||
data:{
|
data:{
|
||||||
id:node.id,
|
id:node.id,
|
||||||
type:node.type,
|
type:node.type,
|
||||||
@@ -263,9 +257,10 @@ export default {
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 將 element edges 資料彙整
|
* 將 element edges 資料彙整
|
||||||
* @param {string} type ProcessMap/BPMN
|
* @param {object} type processMapData | bpmnData
|
||||||
*/
|
*/
|
||||||
setEdgesData(type) {
|
setEdgesData(mapData) {
|
||||||
|
let mapType = this.mapType;
|
||||||
//add event duration is empty
|
//add event duration is empty
|
||||||
const logDuration = {
|
const logDuration = {
|
||||||
"total": "",
|
"total": "",
|
||||||
@@ -276,8 +271,10 @@ export default {
|
|||||||
"min": "",
|
"min": "",
|
||||||
"cases": ""
|
"cases": ""
|
||||||
};
|
};
|
||||||
this.processMap.edges.forEach(edge => {
|
|
||||||
this.processMapData.edges.push({
|
mapData.edges = [];
|
||||||
|
this[mapType].edges.map(edge => {
|
||||||
|
mapData.edges.push({
|
||||||
data: {
|
data: {
|
||||||
source:edge.tail,
|
source:edge.tail,
|
||||||
target:edge.head,
|
target:edge.head,
|
||||||
@@ -290,178 +287,27 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 繪圖
|
* create cytoscape map
|
||||||
|
* @param {string} type this.mapType processMap | bpmn
|
||||||
*/
|
*/
|
||||||
// drawMap(){
|
async executeApi(type) {
|
||||||
// if(this.mapType === 'processMap' && this.processMap.vertices.length !== 0){
|
|
||||||
// let graphId = this.mapType;
|
|
||||||
// ProcessMap(this.ProcessMap.nodes,this.ProcessMap.edges,ProcessMapID,this.Optval.value,this.ProcessMap.start_end,this.Optval.key,this.Curvetype)
|
|
||||||
// }
|
|
||||||
// else if(this.mapType === 'bpmn' && this.bpmn.vertices.length !== 0){
|
|
||||||
// let graphId = this.mapType;
|
|
||||||
// ProcessMap(this.BPMN.nodes,this.BPMN.edges,BPMNID,this.Optval.value,this.BPMN.start_end,this.Optval.key,this.Curvetype)
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
/**
|
|
||||||
* create and setting cytoscape
|
|
||||||
*/
|
|
||||||
createCytoscape() {
|
|
||||||
let graphId = document.getElementById(`${this.mapType}`);
|
|
||||||
let dataLayerType = this.dataLayerType;
|
|
||||||
let dataLayerOption = this.dataLayerOption;
|
|
||||||
let nodesData = this.processMapData.nodes;
|
|
||||||
let edgesData = this.processMapData.edges;
|
|
||||||
let rank = this.rank;
|
|
||||||
console.log(nodesData);
|
|
||||||
console.log(edgesData);
|
|
||||||
console.log(rank);
|
|
||||||
|
|
||||||
let cy = this.$cytoscape({
|
|
||||||
container: graphId,
|
|
||||||
elements: {
|
|
||||||
nodes: nodesData, //nodes, // 節點的資料
|
|
||||||
edges: edgesData, //edges, // 關係線的資料
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
name: 'klay',
|
|
||||||
rankDir: rank, // 直向 TB | 橫向 LR
|
|
||||||
},
|
|
||||||
style: [
|
|
||||||
// 點擊 node 後改變的樣式
|
|
||||||
{
|
|
||||||
selector:'node:selected',
|
|
||||||
style:{
|
|
||||||
'border-color':'red',
|
|
||||||
'border-width':'3',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// node 節點的樣式
|
|
||||||
{
|
|
||||||
selector: 'node',
|
|
||||||
style: {
|
|
||||||
'label':
|
|
||||||
function(node) { // 節點要顯示的文字
|
|
||||||
// node.data(this.dataLayerType+"."+this.dataLayerOption) 為原先陣列 node.data.key.value
|
|
||||||
let optionValue = node.data(`${dataLayerType}.${dataLayerOption}`);
|
|
||||||
let text = '';
|
|
||||||
|
|
||||||
// 文字超過 10 字尾巴要加「...」,style 要換兩行(\n 換行符號)
|
|
||||||
// 使用 data() 是因為在 cytoscape 中從陣列轉為 function
|
|
||||||
text = node.data('label').length > 10 ? `${node.data('label').substr(0, 10)}...\n\n` : `${node.data('label')}\n\n`;
|
|
||||||
|
|
||||||
// 在 element 中 activity 歸類在 default,所以要先判斷 node 是否為 activeity 才裝入文字。
|
|
||||||
// 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字
|
|
||||||
// Relative 要轉為百分比 %
|
|
||||||
if(node.data('type') === 'activity') {
|
|
||||||
switch(dataLayerType) {
|
|
||||||
case 'freq': // Frequency
|
|
||||||
let textInt = dataLayerOption === 'rel_freq' ? text + optionValue * 100 + "%" : text + optionValue;
|
|
||||||
let textFloat = dataLayerOption === 'rel_freq'? text + (optionValue * 100).toFixed(2) + "%" : text + optionValue.toFixed(2);
|
|
||||||
|
|
||||||
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
|
||||||
text = Math.trunc(optionValue) === optionValue ? textInt : textFloat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
|
|
||||||
// Relative %
|
|
||||||
let textDurRel = text + (optionValue * 100).toFixed(2) + "%";
|
|
||||||
// Timelabel
|
|
||||||
let timeLabelInt = text + TimeLabel(optionValue);
|
|
||||||
let timeLabelFloat = text + TimeLabel(optionValue).toFixed(2);
|
|
||||||
let textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
|
|
||||||
|
|
||||||
text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return text
|
|
||||||
},
|
|
||||||
'text-opacity':0.7,
|
|
||||||
'background-color': 'data(backgroundColor)',
|
|
||||||
'border-color':'data(bordercolor)',
|
|
||||||
'border-width':
|
|
||||||
function(node) {
|
|
||||||
return node.data('type') === 'activity' ? '1' : '2';
|
|
||||||
},
|
|
||||||
//'border-radius': '5',
|
|
||||||
'shape':'data(shape)',
|
|
||||||
'text-wrap': 'wrap',
|
|
||||||
'text-max-width':75,
|
|
||||||
'text-halign': 'center',
|
|
||||||
'text-valign': 'center',
|
|
||||||
'height': 'data(height)',
|
|
||||||
'width': 'data(width)',
|
|
||||||
'color': '#001933',
|
|
||||||
'font-size':
|
|
||||||
function(node) {
|
|
||||||
return node.data('type') === 'activity' ? 14 : 22;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// edge 關係線的樣式
|
|
||||||
{
|
|
||||||
selector: 'edge',
|
|
||||||
style: {
|
|
||||||
'content': function(edge) { // 關係線顯示的文字
|
|
||||||
let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`);
|
|
||||||
let result = '';
|
|
||||||
|
|
||||||
if(optionValue === '') return optionValue;
|
|
||||||
|
|
||||||
switch(dataLayerType) {
|
|
||||||
case 'freq':
|
|
||||||
let edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue;
|
|
||||||
let edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2);
|
|
||||||
|
|
||||||
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
|
||||||
result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
|
|
||||||
// Relative %
|
|
||||||
let edgeDurRel = (optionValue * 100).toFixed(2) + "%";
|
|
||||||
// Timelabel
|
|
||||||
let timeLabelInt = TimeLabel(optionValue);
|
|
||||||
let timeLabelFloat = TimeLabel(optionValue).toFixed(2);
|
|
||||||
let edgeTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
|
|
||||||
|
|
||||||
result = dataLayerOption === 'rel_duration' ? edgeDurRel : edgeTimeLabel;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
'curve-style': 'unbundled-bezier', // 貝茲曲線 bezier | 直角 unbundled-bezier
|
|
||||||
'target-arrow-shape': 'triangle', // 指向目標的箭頭形狀: 三角形
|
|
||||||
'color': 'gray', //#0066cc
|
|
||||||
//'control-point-step-size':100, // 從點到點的垂直線,指定貝茲取線邊緣間的距離
|
|
||||||
'width':'data(lineWidth)',
|
|
||||||
'line-style':'data(style)',
|
|
||||||
"text-margin-y": "15rem",
|
|
||||||
//"text-rotation": "autorotate",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
// this.$cytoscape.warnings(false); // false
|
|
||||||
},
|
|
||||||
async executeApi() {
|
|
||||||
await this.allMapDataStore.getAllMapData();
|
await this.allMapDataStore.getAllMapData();
|
||||||
this.setNodesData();
|
|
||||||
this.setEdgesData();
|
let graphId = document.getElementById(type);
|
||||||
this.createColorGradient();
|
let mapData = type === 'processMap'? this.processMapData: this.bpmnData;
|
||||||
this.createCytoscape();
|
|
||||||
// this.drawMap();
|
this.setNodesData(mapData);
|
||||||
|
this.setEdgesData(mapData);
|
||||||
|
|
||||||
|
if(this[type].vertices.length !== 0) cytoscapeMap(mapData, this.mapType, this.dataLayerType, this.dataLayerOption, this.curveStyle, this.rank, graphId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.allMapDataStore.logId = this.$route.params.logId;
|
this.allMapDataStore.logId = this.$route.params.logId;
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.isLoading = false
|
this.isLoading = false;
|
||||||
// this.createCytoscape();
|
this.executeApi(this.mapType);
|
||||||
this.executeApi();
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user