|
"getOption": "const series = context.panel.data.series.map((s) => {\n const sData = s.fields.find((f) => f.type === 'number').values.buffer || s.fields.find((f) => f.type === 'number').values;\n const sTime = s.fields.find((f) => f.type === 'time').values.buffer || s.fields.find((f) => f.type === 'time').values;\n \n return {\n name: s.refId,\n type: 'line',\n showSymbol: false,\n areaStyle: {\n opacity: 0.1,\n },\n lineStyle: {\n width: 1,\n },\n data: sData.map((d, i) => [sTime[i], d.toFixed(2)]),\n };\n});\n\n/**\n * Enable Data Zoom by default\n */\nsetTimeout(() => context.panel.chart.dispatchAction({\n type: 'takeGlobalCursor',\n key: 'dataZoomSelect',\n dataZoomSelectActive: true,\n}), 500);\n\n/**\n * Update Time Range on Zoom\n */\ncontext.panel.chart.on('datazoom', function (params) {\n const startValue = params.batch[0]?.startValue;\n const endValue = params.batch[0]?.endValue;\n locationService.partial({ from: startValue, to: endValue });\n});\n\nreturn {\n backgroundColor: 'transparent',\n tooltip: {\n trigger: 'axis',\n },\n legend: {\n left: '0',\n bottom: '0',\n data: context.panel.data.series.map((s) => s.refId),\n textStyle: {\n color: 'rgba(128, 128, 128, .9)',\n },\n },\n toolbox: {\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n icon: {\n zoom: 'path://',\n back: 'path://',\n },\n },\n saveAsImage: {},\n }\n },\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n grid: {\n left: '2%',\n right: '2%',\n top: '2%',\n bottom: 24,\n containLabel: true,\n },\n series,\n};",
|
|
"code": "const data = context.panel.data;\nlet graph = {\n nodes: [],\n links: [],\n categories: []\n};\n\nif (data.series.length > 0) {\n data.series[0].fields.forEach((s) => {\n console.log(`Processing field: ${s.name}`, JSON.parse(s.values));\n if (s.name === 'links') graph.links = JSON.parse(s.values);\n else if (s.name === 'nodes') graph.nodes = JSON.parse(s.values);\n else if (s.name === 'categories') graph.categories = JSON.parse(s.values);\n });\n}\n\ngraph.nodes.forEach(node => {\n if (node.category == 0) node.category = 1;\n else if (node.category == 1) node.category = 0;\n else if (node.category == 3) node.category = 4;\n else if (node.category == 4) node.category = 3;\n});\n\ngraph.categories.forEach(categorie => {\n if (categorie.name === 'IP 1') {\n categorie.name = '設備ID-1';\n } else if (categorie.name === 'IP 2') {\n categorie.name = '設備ID-2';\n } else if (categorie.name === 'UDID 1') {\n categorie.name = '位址-1';\n } else if (categorie.name === 'UDID 2') {\n categorie.name = '位址-2';\n } else if (categorie.name === 'Account 1') {\n categorie.name = '帳號-1';\n } else if (categorie.name === 'Account 2') {\n categorie.name = '帳號-2';\n }\n});\n\nconst categoryColors = {\n 3: \"#81D4FA\", 4: \"#AED581\", 5: \"#FFB74D\",\n 0: \"#0288D1\", 1: \"#388E3C\", 2: \"#F57C00\"\n};\n\nconst triangleHeight = 1; // 頂部與底部之間的垂直距離\nconst triangleSpacing = 480; // 左右兩個三角形之間的水平距離\nconst nodeSpacing = 36; // 節點垂直間距(由水平改成垂直)\n\nconst categoryGroups = {\n 0: { baseX: -triangleSpacing, baseY: triangleHeight }, // 左頂\n 1: { baseX: -triangleSpacing - 360, baseY: triangleHeight + 180 }, // 左底左\n 2: { baseX: -triangleSpacing + 360, baseY: triangleHeight + 180 }, // 左底右\n 3: { baseX: triangleSpacing, baseY: triangleHeight }, // 右頂\n 4: { baseX: triangleSpacing - 360, baseY: triangleHeight + 180 }, // 右底左\n 5: { baseX: triangleSpacing + 360, baseY: triangleHeight + 180 } // 右底右\n};\n\n// 初始化每類別的偏移量為 0\nconst categoryOffsets = {};\nObject.keys(categoryGroups).forEach(k => categoryOffsets[k] = 0);\n\ngraph.nodes.forEach(node => {\n const cat = node.category;\n const group = categoryGroups[cat];\n const offset = categoryOffsets[cat];\n node.symbol = \"circle\"; //circle,rect,roundRect,triangle,diamond\n node.symbolSize = [12, 12];\n node.label = { show: true, position: \"inside\" };\n\n // 改為「固定 X、Y 軸上下堆疊」\n node.x = group.baseX;\n node.y = group.baseY + offset;\n categoryOffsets[cat] += nodeSpacing;\n console.log(cat + ':' + categoryOffsets[cat]);\n // 節點顏色\n node.itemStyle = {\n color: categoryColors[cat] || \"#cccccc\"\n };\n\n //categoryOffsets[cat] = categoryOffsets[cat] + 10;\n\n});\n\nconst option = {\n tooltip: {},\n legend: [\n {\n icon: 'circle',\n data: graph.categories.map(cat => cat.name),\n textStyle: {\n rich: graph.categories.reduce((acc, cat, index) => {\n acc[cat.name] = { color: categoryColors[index] };\n return acc;\n }, {})\n }\n }\n ],\n animation: false,\n series: [\n {\n name: \"Graph\",\n type: \"graph\",\n layout: \"force\",\n zoom: 0.8,\n force: {\n repulsion: [100, 1200],\n gravity: 0.1,\n edgeLength: [100, 1200],\n friction: 0.2,\n preventOverlap: true\n },\n draggable: true,\n data: graph.nodes.map(node => ({\n ...node,\n symbol: 'circle',\n symbolSize: [12, 12],\n itemStyle: {\n color: categoryColors[node.category] || \"#cccccc\"\n },\n label: {\n show: true,\n position: \"right\",\n padding: [0, 0, 0, 5],\n formatter: params => params.name.substring(0, 16),\n },\n labelLayout: {\n hideOverlap: false\n }\n })),\n links: graph.links,\n focusNodeAdjacency: false,\n categories: graph.categories.map((cat, index) => ({\n ...cat,\n itemStyle: { color: categoryColors[index] }\n })),\n roam: true,\n lineStyle: { color: \"source\", curveness: 0.3 },\n }\n ],\n graphic: graph.nodes.length === 0 ? [\n {\n type: \"text\",\n left: \"center\",\n top: \"middle\",\n style: {\n text: \"查無資訊\",\n fill: \"#999\",\n font: \"bold 20px sans-serif\"\n }\n }\n ] : []\n};\n\nreturn option;\n\n\n\n",
|