echarts关系图参考 https://echarts.apache.org/examples/zh/editor.html?c=graph-circular-layout
image.png

  1. import React, { useEffect, useState } from 'react';
  2. import { array, bool, number } from 'prop-types';
  3. import { TooltipComponent, LegendComponent } from 'echarts/components';
  4. import { GraphChart } from 'echarts/charts';
  5. import Echarts, { chartHeight, sanKeyColor } from '@/components/Echarts';
  6. import nodeObj from './nodes.json';
  7. function nodeSource({ nodes, calls }) {
  8. const nodeTypes = nodes.map(it => it.type);
  9. // [{name: "tcp"}, {name: "http"}]
  10. const legendData = [...new Set(nodeTypes)].map(name => ({ name }));
  11. const links = (calls || []).reduce((prev, next, index) => {
  12. const { source, target } = next;
  13. const item = { index, source: source.id, target: target.id };
  14. prev.push(item);
  15. return prev;
  16. }, []);
  17. const _nodes = (nodes || []).reduce((prev, next, index) => {
  18. const { id, name, type } = next;
  19. const item = { id, name, category: type, label: { show: true } };
  20. prev.push(item);
  21. return prev;
  22. }, []);
  23. return { nodes: _nodes, links, legendData };
  24. }
  25. Chart.propTypes = {
  26. dataSource: array,
  27. height: number,
  28. loading: bool,
  29. };
  30. Chart.defaultProps = {
  31. dataSource: [],
  32. height: chartHeight,
  33. loading: false,
  34. };
  35. function Chart({ dataSource, height, loading }) {
  36. const [options, setOptions] = useState({});
  37. useEffect(update, [dataSource]);
  38. function update() {
  39. const OPTIONS = getOptions();
  40. setOptions(OPTIONS);
  41. }
  42. function getOptions() {
  43. const { nodes, links, legendData } = nodeSource(nodeObj);
  44. return {
  45. 'tooltip': {},
  46. 'color': [
  47. '#6be6c1',
  48. '#a0a7e6',
  49. '#96dee8',
  50. '#3f96e3',
  51. '#3fb1e3',
  52. '#6be6c1',
  53. '#626c91',
  54. '#a0a7e6',
  55. '#c4ebad',
  56. '#96dee8',
  57. ],
  58. 'legend': {
  59. 'top': 24,
  60. 'textStyle': {
  61. 'color': '#ddd',
  62. },
  63. 'data': legendData,
  64. },
  65. 'series': [
  66. {
  67. 'top': '20%',
  68. 'height': '60%',
  69. 'name': '服务',
  70. 'type': 'graph',
  71. 'layout': 'circular',
  72. 'circular': {
  73. 'rotateLabel': true,
  74. },
  75. data: nodes,
  76. links,
  77. 'categories': legendData,
  78. 'roam': true,
  79. 'label': {
  80. 'position': 'right',
  81. 'formatter': '{b}',
  82. },
  83. 'lineStyle': {
  84. 'color': 'source',
  85. 'curveness': 0.3,
  86. },
  87. },
  88. ],
  89. };
  90. }
  91. const attrs = {
  92. options,
  93. height: 640,
  94. loading,
  95. components: [
  96. TooltipComponent,
  97. LegendComponent,
  98. GraphChart,
  99. ],
  100. renderType: 'svg'
  101. };
  102. return (
  103. <Echarts {...attrs} />
  104. );
  105. }
  106. export default Chart;

click

image.png

  1. function onClick() {
  2. const myChart = this.$refs.radial.myChart;
  3. myChart.on('click', (params) => {
  4. const currentNode = nodes.find(it => it.id === params.data.id);
  5. const payload = currentNode || {};
  6. this.$store.commit('rocketTopo/SET_NODE', payload);
  7. });
  8. }

image.png

关系图数据

  1. const links = [
  2. {
  3. 'index': 0,
  4. 'source': 'Ym9va2luZm86OnByb2R1Y3RwYWdl.1',
  5. 'target': 'Ym9va2luZm86OmRldGFpbHM=.1',
  6. },
  7. {
  8. 'index': 1,
  9. 'source': 'Ym9va2luZm86OnByb2R1Y3RwYWdl.1',
  10. 'target': 'Ym9va2luZm86OnJldmlld3M=.1',
  11. },
  12. {
  13. 'index': 2,
  14. 'source': 'Ym9va2luZm86OnJhdGluZ3M=.1',
  15. 'target': 'Ym9va2luZm86Om1vbmdvZGI=.1',
  16. },
  17. {
  18. 'index': 3,
  19. 'source': 'Ym9va2luZm86OnJldmlld3M=.1',
  20. 'target': 'Ym9va2luZm86OnJhdGluZ3M=.1',
  21. },
  22. {
  23. 'index': 4,
  24. 'source': 'Ym9va2luZm86Om1vbmdvZGI=.1',
  25. 'target': 'Ym9va2luZm86Om1vbmdvZGI=.1',
  26. },
  27. {
  28. 'index': 5,
  29. 'source': 'Ym9va2luZm86OnJldmlld3M=.1',
  30. 'target': 'Ym9va2luZm86Om1vbmdvZGI=.1',
  31. },
  32. {
  33. 'index': 6,
  34. 'source': 'Ym9va2luZm86OnJhdGluZ3M=.1',
  35. 'target': 'a3ViZS1zeXN0ZW06Omt1YmUtZG5z.1',
  36. },
  37. {
  38. 'index': 7,
  39. 'source': 'Ym9va2luZm86OnRyYWZmaWNnZW5lcmF0b3I=.1',
  40. 'target': 'Ym9va2luZm86OnByb2R1Y3RwYWdl.1',
  41. },
  42. {
  43. 'index': 8,
  44. 'source': 'Ym91dGlxdWU6OmVtYWlsc2VydmljZQ==.1',
  45. 'target': 'Ym9va2luZm86OmRldGFpbHM=.1',
  46. },
  47. {
  48. 'index': 9,
  49. 'source': 'Ym91dGlxdWU6OmVtYWlsc2VydmljZQ==.1',
  50. 'target': 'Ym9va2luZm86OnJldmlld3M=.1',
  51. },
  52. {
  53. 'index': 10,
  54. 'source': 'Ym91dGlxdWU6OmZyb250ZW5kLWV4dGVybmFs.1',
  55. 'target': 'Ym9va2luZm86OnJldmlld3M=.1',
  56. },
  57. {
  58. 'index': 11,
  59. 'source': 'Y2VydC1tYW5hZ2VyOjpjZXJ0LW1hbmFnZXItd2ViaG9vaw==.1',
  60. 'target': 'Ym9va2luZm86OnByb2R1Y3RwYWdl.1',
  61. },
  62. ]
  63. const nodes = [
  64. {
  65. 'id': 'Ym9va2luZm86OnRyYWZmaWNnZW5lcmF0b3I=.1',
  66. 'name': 'bookinfo::trafficgenerator',
  67. 'label': {
  68. 'show': true,
  69. },
  70. },
  71. {
  72. 'id': 'a3ViZS1zeXN0ZW06Omt1YmUtZG5z.1',
  73. 'name': 'kube-system::kube-dns',
  74. 'label': {
  75. 'show': true,
  76. },
  77. 'category': 'tcp',
  78. },
  79. {
  80. 'id': 'Ym9va2luZm86OmRldGFpbHM=.1',
  81. 'name': 'bookinfo::details',
  82. 'label': {
  83. 'show': true,
  84. },
  85. 'category': 'http',
  86. },
  87. {
  88. 'id': 'Y2VydC1tYW5hZ2VyOjpjZXJ0LW1hbmFnZXItd2ViaG9vaw==.1',
  89. 'name': 'cert-manager::cert-manager-webhook',
  90. 'label': {
  91. 'show': true,
  92. },
  93. },
  94. {
  95. 'id': 'Ym9va2luZm86OnByb2R1Y3RwYWdl.1',
  96. 'name': 'bookinfo::productpage',
  97. 'label': {
  98. 'show': true,
  99. },
  100. 'category': 'http',
  101. },
  102. {
  103. 'id': 'Ym9va2luZm86OnJldmlld3M=.1',
  104. 'name': 'bookinfo::reviews',
  105. 'label': {
  106. 'show': true,
  107. },
  108. 'category': 'http',
  109. },
  110. {
  111. 'id': 'Ym91dGlxdWU6OmVtYWlsc2VydmljZQ==.1',
  112. 'name': 'boutique::emailservice',
  113. 'label': {
  114. 'show': true,
  115. },
  116. },
  117. {
  118. 'id': 'Ym91dGlxdWU6OmZyb250ZW5kLWV4dGVybmFs.1',
  119. 'name': 'boutique::frontend-external',
  120. 'label': {
  121. 'show': true,
  122. },
  123. },
  124. {
  125. 'id': 'Ym9va2luZm86OnJhdGluZ3M=.1',
  126. 'name': 'bookinfo::ratings',
  127. 'label': {
  128. 'show': true,
  129. },
  130. 'category': 'http',
  131. },
  132. {
  133. 'id': 'Ym9va2luZm86Om1vbmdvZGI=.1',
  134. 'name': 'bookinfo::mongodb',
  135. 'label': {
  136. 'show': true,
  137. },
  138. 'category': 'http',
  139. },
  140. ]