diff --git a/src/views/monitor/topology-build/graph.ts b/src/views/monitor/topology-build/graph.ts
deleted file mode 100644
index 99f295b2..00000000
--- a/src/views/monitor/topology-build/graph.ts
+++ /dev/null
@@ -1,1742 +0,0 @@
-import {
- Algorithm,
- Util,
- registerNode,
- registerEdge,
- Layout,
- Menu,
- Graph,
- Tooltip,
-} from '@antv/g6';
-
-const { labelPropagation, louvain, findShortestPath } = Algorithm;
-const { uniqueId } = Util;
-
-const NODESIZEMAPPING = 'degree';
-const SMALLGRAPHLABELMAXLENGTH = 12;
-let labelMaxLength = SMALLGRAPHLABELMAXLENGTH;
-const DEFAULTNODESIZE = 30;
-const DEFAULTAGGREGATEDNODESIZE = 56;
-const NODE_LIMIT = 40; // TODO: find a proper number for maximum node number on the canvas
-
-let graph: any = null;
-let currentUnproccessedData = { nodes: [], edges: [] };
-let nodeMap: any = {};
-let aggregatedNodeMap: any = {};
-let hiddenItemIds: any[] = []; // 隐藏的元素 id 数组
-let largeGraphMode = true;
-let cachePositions: any = {};
-let manipulatePosition: any = undefined;
-let descreteNodeCenter: any;
-let layout: any = {
- type: '',
- instance: null,
- destroyed: true,
-};
-let expandArray: any = [];
-let collapseArray: any = [];
-let shiftKeydown = false;
-let CANVAS_WIDTH = 800,
- CANVAS_HEIGHT = 800;
-
-const duration = 2000;
-const animateOpacity = 0.6;
-const animateBackOpacity = 0.1;
-const virtualEdgeOpacity = 0.1;
-const realEdgeOpacity = 0.2;
-
-const darkBackColor = 'rgb(43, 47, 51)';
-const disableColor = '#777';
-const theme = 'dark'; // 'dark';
-const subjectColors = [
- '#5F95FF', // blue
- '#61DDAA',
- '#65789B',
- '#F6BD16',
- '#7262FD',
- '#78D3F8',
- '#9661BC',
- '#F6903D',
- '#008685',
- '#F08BB4',
-];
-
-const colorSets = Util.getColorSetsBySubjectColors(
- subjectColors,
- darkBackColor,
- theme,
- disableColor
-);
-
-const globalStyle = {
- node: {
- style: {
- fill: '#2B384E',
- },
- labelCfg: {
- style: {
- fill: '#acaeaf',
- stroke: '#191b1c',
- },
- },
- stateStyles: {
- focus: {
- fill: '#2B384E',
- },
- },
- },
- edge: {
- style: {
- stroke: '#acaeaf',
- realEdgeStroke: '#acaeaf', //'#f00',
- realEdgeOpacity,
- strokeOpacity: realEdgeOpacity,
- },
- labelCfg: {
- style: {
- fill: '#acaeaf',
- realEdgeStroke: '#acaeaf', //'#f00',
- realEdgeOpacity: 0.5,
- stroke: '#191b1c',
- },
- },
- stateStyles: {
- focus: {
- stroke: '#fff', // '#3C9AE8',
- },
- },
- },
-};
-
-// Custom super node
-registerNode(
- 'aggregated-node',
- {
- draw(cfg, group) {
- let width = 53,
- height = 27;
- const style = cfg.style || {};
- const colorSet = cfg.colorSet || colorSets[0];
-
- // halo for hover
- group.addShape('rect', {
- attrs: {
- x: -width * 0.55,
- y: -height * 0.6,
- width: width * 1.1,
- height: height * 1.2,
- fill: colorSet.mainFill,
- opacity: 0.9,
- lineWidth: 0,
- radius: (height / 2 || 13) * 1.2,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'halo-shape',
- visible: false,
- });
-
- // focus stroke for hover
- group.addShape('rect', {
- attrs: {
- x: -width * 0.55,
- y: -height * 0.6,
- width: width * 1.1,
- height: height * 1.2,
- fill: colorSet.mainFill, // '#3B4043',
- stroke: '#AAB7C4',
- lineWidth: 1,
- lineOpacty: 0.85,
- radius: (height / 2 || 13) * 1.2,
- },
- name: 'stroke-shape',
- visible: false,
- });
-
- const keyShape = group.addShape('rect', {
- attrs: {
- x: -width / 2,
- y: -height / 2,
- width,
- height,
- fill: colorSet.mainFill, // || '#3B4043',
- stroke: colorSet.mainStroke,
- lineWidth: 2,
- cursor: 'pointer',
- radius: height / 2 || 13,
- lineDash: [2, 2],
- ...style,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'aggregated-node-keyShape',
- });
-
- let labelStyle = {};
- if (cfg.labelCfg) {
- labelStyle = Object.assign(labelStyle, cfg.labelCfg.style);
- }
- group.addShape('text', {
- attrs: {
- text: `${cfg.count}`,
- x: 0,
- y: 0,
- textAlign: 'center',
- textBaseline: 'middle',
- cursor: 'pointer',
- fontSize: 12,
- fill: '#fff',
- opacity: 0.85,
- fontWeight: 400,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'count-shape',
- className: 'count-shape',
- draggable: true,
- });
-
- // tag for new node
- // if (cfg.new) {
- // group.addShape('circle', {
- // attrs: {
- // x: width / 2 - 3,
- // y: -height / 2 + 3,
- // r: 4,
- // fill: '#6DD400',
- // lineWidth: 0.5,
- // stroke: '#FFFFFF',
- // },
- // // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- // name: 'typeNode-tag-circle',
- // });
- // }
- return keyShape;
- },
- setState: (name, value, item: any) => {
- const group = item.get('group');
- if (name === 'layoutEnd' && value) {
- const labelShape = group.find(
- (e: any) => e.get('name') === 'text-shape'
- );
- if (labelShape) labelShape.set('visible', true);
- } else if (name === 'hover') {
- if (item.hasState('focus')) {
- return;
- }
- const halo = group.find((e: any) => e.get('name') === 'halo-shape');
- const keyShape = item.getKeyShape();
- const colorSet = item.getModel().colorSet || colorSets[0];
- if (value) {
- halo && halo.show();
- keyShape.attr('fill', colorSet.activeFill);
- } else {
- halo && halo.hide();
- keyShape.attr('fill', colorSet.mainFill);
- }
- } else if (name === 'focus') {
- const stroke = group.find((e: any) => e.get('name') === 'stroke-shape');
- const keyShape = item.getKeyShape();
- const colorSet = item.getModel().colorSet || colorSets[0];
- if (value) {
- stroke && stroke.show();
- keyShape.attr('fill', colorSet.selectedFill);
- } else {
- stroke && stroke.hide();
- keyShape.attr('fill', colorSet.mainFill);
- }
- }
- },
- update: undefined,
- },
- 'single-node'
-);
-
-// Custom real node
-registerNode(
- 'real-node',
- {
- draw(cfg, group) {
- let r = 30;
- if (typeof cfg.size === 'number') {
- r = cfg.size / 2;
- } else if (Array.isArray(cfg.size)) {
- r = cfg.size[0] / 2;
- }
- const style = cfg.style || {};
- const colorSet = cfg.colorSet || colorSets[0];
- console.log('=== ', cfg);
-
- // halo for hover
- group.addShape('circle', {
- attrs: {
- x: 0,
- y: 0,
- r: r + 5,
- fill: style.fill || colorSet.mainFill || '#2B384E',
- opacity: 0.9,
- lineWidth: 0,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'halo-shape',
- visible: false,
- });
-
- // focus stroke for hover
- group.addShape('circle', {
- attrs: {
- x: 0,
- y: 0,
- r: r + 5,
- fill: style.fill || colorSet.mainFill || '#2B384E',
- stroke: '#fff',
- strokeOpacity: 0.85,
- lineWidth: 1,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'stroke-shape',
- visible: false,
- });
-
- // 默认形状
- const keyShape = group.addShape('circle', {
- attrs: {
- x: 0,
- y: 0,
- r,
- fill: colorSet.mainFill,
- stroke: colorSet.mainStroke,
- lineWidth: 2,
- cursor: 'pointer',
- ...style,
- },
- // 设置 draggable 以允许响应鼠标的图拽事件
- draggable: true,
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'aggregated-node-keyShape',
- });
-
- // 网元状态标识
- if (cfg.info) {
- const neInfo: any = cfg.info;
- const hasNeState = neInfo.serverState.neId;
- const neStateShape = group.addShape('circle', {
- attrs: {
- x: r - 3,
- y: -r + 3,
- r: 5,
- fill: hasNeState ? '#6DD400' : 'red',
- lineWidth: 0.5,
- stroke: '#FFFFFF',
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'ne-state',
- });
-
- // 添加动画
- neStateShape.animate(
- {
- // Magnifying and disappearing
- r: 5,
- opacity: 0.3,
- },
- {
- duration: 1000,
- easing: 'easeCubic',
- delay: 0,
- repeat: true, // repeat
- }
- );
- }
-
- if (cfg.label) {
- const text = cfg.label;
- let labelStyle: any = {
- textAlign: 'center',
- textBaseLine: 'alphabetic',
- cursor: 'pointer',
- fontSize: 8,
- fill: '#fff',
- opacity: 0.85,
- fontWeight: 400,
- };
- let refY = 0;
- if (cfg.labelCfg) {
- labelStyle = Object.assign(labelStyle, cfg.labelCfg.style);
- refY += cfg.labelCfg.offset || 0;
- }
- labelStyle.fontSize = labelStyle.fontSize < 8 ? 8 : labelStyle.fontSize;
-
- group.addShape('text', {
- attrs: {
- text,
- x: 0,
- y: r + refY + 15,
- ...labelStyle,
- },
- // 设置 draggable 以允许响应鼠标的图拽事件
- draggable: true,
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'text-shape',
- className: 'text-shape',
- });
- }
-
- if (cfg.icon) {
- // left icon
- group.addShape('image', {
- attrs: {
- x: 0,
- cursor: 'pointer',
- ...cfg.icon,
- },
- // 设置 draggable 以允许响应鼠标的图拽事件
- draggable: true,
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'icon-shape',
- });
- }
-
- return keyShape;
- },
- setState: (name, value, item: any) => {
- const group = item.get('group');
- if (name === 'layoutEnd' && value) {
- const labelShape = group.find(
- (e: any) => e.get('name') === 'text-shape'
- );
- if (labelShape) labelShape.set('visible', true);
- } else if (name === 'neState') {
- // 设置网元状态
- const neState = group.find((e: any) => e.get('name') === 'ne-state');
- if (value) {
- neState.attr('fill', '#52c41a');
- } else {
- neState.attr('fill', '#f5222d');
- }
- } else if (name === 'hover') {
- if (item.hasState('focus')) {
- return;
- }
- const halo = group.find((e: any) => e.get('name') === 'halo-shape');
- const keyShape = item.getKeyShape();
- const colorSet = item.getModel().colorSet || colorSets[0];
- if (value) {
- halo && halo.show();
- keyShape.attr('fill', colorSet.activeFill);
- } else {
- halo && halo.hide();
- keyShape.attr('fill', colorSet.mainFill);
- }
- } else if (name === 'focus') {
- const stroke = group.find((e: any) => e.get('name') === 'stroke-shape');
- const label = group.find((e: any) => e.get('name') === 'text-shape');
- const keyShape = item.getKeyShape();
- const colorSet = item.getModel().colorSet || colorSets[0];
- if (value) {
- stroke && stroke.show();
- keyShape.attr('fill', colorSet.selectedFill);
- label && label.attr('fontWeight', 800);
- } else {
- stroke && stroke.hide();
- keyShape.attr('fill', colorSet.mainFill); // '#2B384E'
- label && label.attr('fontWeight', 400);
- }
- }
- // const model = item.getModel();
- // const neState = group.find(
- // (e: any) => e.get('name') === 'ne-state-circle'
- // );
- // if (neState) {
- // console.log('neState', neState);
- // item.getKeyShape().attr('fill', '#6DD400');
- // }
- },
- update: undefined,
- },
- 'aggregated-node'
-); // 这样可以继承 aggregated-node 的 setState
-
-// Custom the quadratic edge for multiple edges between one node pair
-registerEdge(
- 'custom-quadratic',
- {
- setState: (name, value, item: any) => {
- const group = item.get('group');
- const model = item.getModel();
- if (name === 'focus') {
- const back = group.find((ele: any) => ele.get('name') === 'back-line');
- if (back) {
- back.stopAnimate();
- back.remove();
- back.destroy();
- }
- const keyShape = group.find(
- (ele: any) => ele.get('name') === 'edge-shape'
- );
- const arrow = model.style.endArrow;
- if (value) {
- if (keyShape.cfg.animation) {
- keyShape.stopAnimate(true);
- }
- keyShape.attr({
- strokeOpacity: animateOpacity,
- opacity: animateOpacity,
- stroke: '#fff',
- endArrow: {
- ...arrow,
- stroke: '#fff',
- fill: '#fff',
- },
- });
- if (model.isReal) {
- const { lineWidth, path, endArrow, stroke } = keyShape.attr();
- const back = group.addShape('path', {
- attrs: {
- lineWidth,
- path,
- stroke,
- endArrow,
- opacity: animateBackOpacity,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'back-line',
- });
- back.toBack();
- const length = keyShape.getTotalLength();
- keyShape.animate(
- (ratio: any) => {
- // the operations in each frame. Ratio ranges from 0 to 1 indicating the prograss of the animation. Returns the modified configurations
- const startLen = ratio * length;
- // Calculate the lineDash
- const cfg = {
- lineDash: [startLen, length - startLen],
- };
- return cfg;
- },
- {
- repeat: true, // Whether executes the animation repeatly
- duration, // the duration for executing once
- }
- );
- } else {
- let index = 0;
- const lineDash = keyShape.attr('lineDash');
- const totalLength = lineDash[0] + lineDash[1];
- keyShape.animate(
- () => {
- index++;
- if (index > totalLength) {
- index = 0;
- }
- const res = {
- lineDash,
- lineDashOffset: -index,
- };
- // returns the modified configurations here, lineDash and lineDashOffset here
- return res;
- },
- {
- repeat: true, // whether executes the animation repeatly
- duration, // the duration for executing once
- }
- );
- }
- } else {
- keyShape.stopAnimate();
- const stroke = '#acaeaf';
- const opacity = model.isReal ? realEdgeOpacity : virtualEdgeOpacity;
- keyShape.attr({
- stroke,
- strokeOpacity: opacity,
- opacity,
- endArrow: {
- ...arrow,
- stroke,
- fill: stroke,
- },
- });
- }
- }
- },
- },
- 'quadratic'
-);
-
-// Custom the line edge for single edge between one node pair
-registerEdge(
- 'custom-line',
- {
- setState: (name, value, item: any) => {
- const group = item.get('group');
- const model = item.getModel();
- if (name === 'focus') {
- const keyShape = group.find(
- (ele: any) => ele.get('name') === 'edge-shape'
- );
- const back = group.find((ele: any) => ele.get('name') === 'back-line');
- if (back) {
- back.stopAnimate();
- back.remove();
- back.destroy();
- }
- const arrow = model.style.endArrow;
- if (value) {
- if (keyShape.cfg.animation) {
- keyShape.stopAnimate(true);
- }
- keyShape.attr({
- strokeOpacity: animateOpacity,
- opacity: animateOpacity,
- stroke: '#fff',
- endArrow: {
- ...arrow,
- stroke: '#fff',
- fill: '#fff',
- },
- });
- if (model.isReal) {
- const { path, stroke, lineWidth } = keyShape.attr();
- const back = group.addShape('path', {
- attrs: {
- path,
- stroke,
- lineWidth,
- opacity: animateBackOpacity,
- },
- // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
- name: 'back-line',
- });
- back.toBack();
- const length = keyShape.getTotalLength();
- keyShape.animate(
- (ratio: any) => {
- // the operations in each frame. Ratio ranges from 0 to 1 indicating the prograss of the animation. Returns the modified configurations
- const startLen = ratio * length;
- // Calculate the lineDash
- const cfg = {
- lineDash: [startLen, length - startLen],
- };
- return cfg;
- },
- {
- repeat: true, // Whether executes the animation repeatly
- duration, // the duration for executing once
- }
- );
- } else {
- const lineDash = keyShape.attr('lineDash');
- const totalLength = lineDash[0] + lineDash[1];
- let index = 0;
- keyShape.animate(
- () => {
- index++;
- if (index > totalLength) {
- index = 0;
- }
- const res = {
- lineDash,
- lineDashOffset: -index,
- };
- // returns the modified configurations here, lineDash and lineDashOffset here
- return res;
- },
- {
- repeat: true, // whether executes the animation repeatly
- duration, // the duration for executing once
- }
- );
- }
- } else {
- keyShape.stopAnimate();
- const stroke = '#acaeaf';
- const opacity = model.isReal ? realEdgeOpacity : virtualEdgeOpacity;
- keyShape.attr({
- stroke,
- strokeOpacity: opacity,
- opacity: opacity,
- endArrow: {
- ...arrow,
- stroke,
- fill: stroke,
- },
- });
- }
- }
- },
- },
- 'single-edge'
-);
-
-const descendCompare = (p: any) => {
- // 这是比较函数
- return function (m: any, n: any) {
- const a = m[p];
- const b = n[p];
- return b - a; // 降序
- };
-};
-
-const clearFocusItemState = (graph: any) => {
- if (!graph) return;
- clearFocusNodeState(graph);
- clearFocusEdgeState(graph);
-};
-
-// 清除图上所有节点的 focus 状态及相应样式
-const clearFocusNodeState = (graph: any) => {
- const focusNodes = graph.findAllByState('node', 'focus');
- focusNodes.forEach((fnode: any) => {
- graph.setItemState(fnode, 'focus', false); // false
- });
-};
-
-// 清除图上所有边的 focus 状态及相应样式
-const clearFocusEdgeState = (graph: any) => {
- const focusEdges = graph.findAllByState('edge', 'focus');
- focusEdges.forEach((fedge: any) => {
- graph.setItemState(fedge, 'focus', false);
- });
-};
-
-// 截断长文本。length 为文本截断后长度,elipsis 是后缀
-const formatText = (text: any, length = 5, elipsis = '...') => {
- if (!text) return '';
- if (text.length > length) {
- return `${text.substr(0, length)}${elipsis}`;
- }
- return text;
-};
-
-const labelFormatter = (text: any, minLength = 10) => {
- if (text && text.split('').length > minLength)
- return `${text.substr(0, minLength)}...`;
- return text;
-};
-
-const processNodesEdges = (
- nodes: any,
- edges: any,
- width: any,
- height: any,
- largeGraphMode: any,
- edgeLabelVisible: any
-) => {
- if (!nodes || nodes.length === 0) return {};
- const currentNodeMap: any = {};
- let maxNodeCount = -Infinity;
- const paddingRatio = 0.3;
- const paddingLeft = paddingRatio * width;
- const paddingTop = paddingRatio * height;
- nodes.forEach((node: any) => {
- node.type = node.level === 0 ? 'real-node' : 'aggregated-node';
- node.isReal = node.level === 0 ? true : false;
- node.label = formatText(node.label, labelMaxLength, '...');
- node.degree = 0;
- node.inDegree = 0;
- node.outDegree = 0;
- if (currentNodeMap[node.id]) {
- console.warn('node exists already!', node.id);
- node.id = `${node.id}${Math.random()}`;
- }
- currentNodeMap[node.id] = node;
- if (node.count > maxNodeCount) maxNodeCount = node.count;
- const cachePosition = cachePositions ? cachePositions[node.id] : undefined;
- if (cachePosition) {
- node.x = cachePosition.x;
- node.y = cachePosition.y;
- } else {
- if (manipulatePosition && !node.x && !node.y) {
- node.x =
- manipulatePosition.x + 30 * Math.cos(Math.random() * Math.PI * 2);
- node.y =
- manipulatePosition.y + 30 * Math.sin(Math.random() * Math.PI * 2);
- }
- }
- });
-
- let maxCount = -Infinity;
- let minCount = Infinity;
- // let maxCount = 0;
- edges.forEach((edge: any) => {
- // to avoid the dulplicated id to nodes
- if (!edge.id) edge.id = uniqueId('edge');
- else if (edge.id.split('-')[0] !== 'edge') edge.id = `edge-${edge.id}`;
- // TODO: delete the following line after the queried data is correct
- if (!currentNodeMap[edge.source] || !currentNodeMap[edge.target]) {
- console.warn(
- 'edge source target does not exist',
- edge.source,
- edge.target,
- edge.id
- );
- return;
- }
- const sourceNode = currentNodeMap[edge.source];
- const targetNode = currentNodeMap[edge.target];
-
- if (!sourceNode || !targetNode)
- console.warn(
- 'source or target is not defined!!!',
- edge,
- sourceNode,
- targetNode
- );
-
- // calculate the degree
- sourceNode.degree++;
- targetNode.degree++;
- sourceNode.outDegree++;
- targetNode.inDegree++;
-
- if (edge.count > maxCount) maxCount = edge.count;
- if (edge.count < minCount) minCount = edge.count;
- });
-
- nodes.sort(descendCompare(NODESIZEMAPPING));
- const maxDegree = nodes[0].degree || 1;
-
- const descreteNodes: any = [];
- nodes.forEach((node: any, i: any) => {
- // assign the size mapping to the outDegree
- const countRatio = node.count / maxNodeCount;
- const isRealNode = node.level === 0;
- node.size = isRealNode
- ? node.size || DEFAULTNODESIZE
- : DEFAULTAGGREGATEDNODESIZE;
- node.isReal = isRealNode;
- node.labelCfg = node.labelCfg || {
- position: 'bottom',
- offset: 5,
- style: {
- fill: globalStyle.node.labelCfg.style.fill,
- fontSize: 6 + countRatio * 6 || 12,
- stroke: globalStyle.node.labelCfg.style.stroke,
- lineWidth: 3,
- },
- };
-
- if (!node.degree) {
- descreteNodes.push(node);
- }
- });
-
- const countRange = maxCount - minCount;
- const minEdgeSize = 1;
- const maxEdgeSize = 12;
- const edgeSizeRange = maxEdgeSize - minEdgeSize;
- edges.forEach((edge: any) => {
- // set edges' style
- const targetNode = currentNodeMap[edge.target];
-
- const size =
- ((edge.count - minCount) / countRange) * edgeSizeRange + minEdgeSize || 1;
- edge.size = size;
-
- const arrowWidth = Math.max(size / 2 + 2, 3);
- const arrowLength = 10;
- const arrowBeging = targetNode.size + arrowLength;
- let arrowPath: any = `M ${arrowBeging},0 L ${
- arrowBeging + arrowLength
- },-${arrowWidth} L ${arrowBeging + arrowLength},${arrowWidth} Z`;
- let d = targetNode.size / 2 + arrowLength;
- if (edge.source === edge.target) {
- edge.type = 'loop';
- arrowPath = undefined;
- }
- const sourceNode = currentNodeMap[edge.source];
- const isRealEdge = targetNode.isReal && sourceNode.isReal;
- edge.isReal = isRealEdge;
- const stroke = isRealEdge
- ? globalStyle.edge.style.realEdgeStroke
- : globalStyle.edge.style.stroke;
- const opacity = isRealEdge
- ? globalStyle.edge.style.realEdgeOpacity
- : globalStyle.edge.style.strokeOpacity;
- const dash = Math.max(size, 2);
- const lineDash = isRealEdge ? undefined : [dash, dash];
- edge.style = {
- stroke,
- strokeOpacity: opacity,
- cursor: 'pointer',
- lineAppendWidth: Math.max(edge.size || 5, 5),
- fillOpacity: 1,
- lineDash,
- endArrow: arrowPath
- ? {
- path: arrowPath,
- d,
- fill: stroke,
- strokeOpacity: 0,
- }
- : false,
- };
- edge.labelCfg = {
- autoRotate: true,
- style: {
- stroke: globalStyle.edge.labelCfg.style.stroke,
- fill: globalStyle.edge.labelCfg.style.fill,
- lineWidth: 4,
- fontSize: 12,
- lineAppendWidth: 10,
- opacity: 1,
- },
- };
- // 标签名称
- if (!edge.oriLabel) edge.oriLabel = edge.label;
- if (largeGraphMode || !edgeLabelVisible) edge.label = '';
- else {
- edge.label = labelFormatter(edge.label, labelMaxLength);
- }
-
- // arrange the other nodes around the hub
- const sourceDis = sourceNode.size / 2 + 20;
- const targetDis = targetNode.size / 2 + 20;
- if (sourceNode.x && !targetNode.x) {
- targetNode.x =
- sourceNode.x + sourceDis * Math.cos(Math.random() * Math.PI * 2);
- }
- if (sourceNode.y && !targetNode.y) {
- targetNode.y =
- sourceNode.y + sourceDis * Math.sin(Math.random() * Math.PI * 2);
- }
- if (targetNode.x && !sourceNode.x) {
- sourceNode.x =
- targetNode.x + targetDis * Math.cos(Math.random() * Math.PI * 2);
- }
- if (targetNode.y && !sourceNode.y) {
- sourceNode.y =
- targetNode.y + targetDis * Math.sin(Math.random() * Math.PI * 2);
- }
-
- if (!sourceNode.x && !sourceNode.y && manipulatePosition) {
- sourceNode.x =
- manipulatePosition.x + 30 * Math.cos(Math.random() * Math.PI * 2);
- sourceNode.y =
- manipulatePosition.y + 30 * Math.sin(Math.random() * Math.PI * 2);
- }
- if (!targetNode.x && !targetNode.y && manipulatePosition) {
- targetNode.x =
- manipulatePosition.x + 30 * Math.cos(Math.random() * Math.PI * 2);
- targetNode.y =
- manipulatePosition.y + 30 * Math.sin(Math.random() * Math.PI * 2);
- }
- });
-
- descreteNodeCenter = {
- x: width - paddingLeft,
- y: height - paddingTop,
- };
- descreteNodes.forEach((node: any) => {
- if (!node.x && !node.y) {
- node.x =
- descreteNodeCenter.x + 30 * Math.cos(Math.random() * Math.PI * 2);
- node.y =
- descreteNodeCenter.y + 30 * Math.sin(Math.random() * Math.PI * 2);
- }
- });
-
- Util.processParallelEdges(edges, 12.5, 'custom-quadratic', 'custom-line');
- return {
- maxDegree,
- edges,
- };
-};
-
-const getForceLayoutConfig = (
- graph: any,
- largeGraphMode: any,
- configSettings?: any
-) => {
- let {
- linkDistance,
- edgeStrength,
- nodeStrength,
- nodeSpacing,
- preventOverlap,
- nodeSize,
- collideStrength,
- alpha,
- alphaDecay,
- alphaMin,
- } = configSettings || { preventOverlap: true };
-
- if (!linkDistance && linkDistance !== 0) linkDistance = 525;
- if (!edgeStrength && edgeStrength !== 0) edgeStrength = 50;
- if (!nodeStrength && nodeStrength !== 0) nodeStrength = 200;
- if (!nodeSpacing && nodeSpacing !== 0) nodeSpacing = 5;
-
- const config: any = {
- type: 'gForce',
- minMovement: 0.01,
- maxIteration: 5000,
- preventOverlap,
- damping: 0.99,
- linkDistance: (d: any) => {
- let dist = linkDistance;
- const sourceNode = nodeMap[d.source] || aggregatedNodeMap[d.source];
- const targetNode = nodeMap[d.target] || aggregatedNodeMap[d.target];
- // // 两端都是聚合点
- // if (sourceNode.level && targetNode.level) dist = linkDistance * 3;
- // // 一端是聚合点,一端是真实节点
- // else if (sourceNode.level || targetNode.level) dist = linkDistance * 1.5;
- if (!sourceNode?.level && !targetNode?.level) dist = linkDistance * 0.3;
- return dist;
- },
- edgeStrength: (d: any) => {
- const sourceNode = nodeMap[d.source] || aggregatedNodeMap[d.source];
- const targetNode = nodeMap[d.target] || aggregatedNodeMap[d.target];
- // 聚合节点之间的引力小
- if (sourceNode?.level && targetNode?.level) return edgeStrength / 2;
- // 聚合节点与真实节点之间引力大
- if (sourceNode?.level || targetNode?.level) return edgeStrength;
- return edgeStrength;
- },
- nodeStrength: (d: any) => {
- // 给离散点引力,让它们聚集
- if (d.degree === 0) return -10;
- // 聚合点的斥力大
- if (d.level) return nodeStrength * 2;
- return nodeStrength;
- },
- nodeSize: (d: any) => {
- if (!nodeSize && d.size) return d.size;
- return 50;
- },
- nodeSpacing: (d: any) => {
- if (d.degree === 0) return nodeSpacing * 2;
- if (d.level) return nodeSpacing;
- return nodeSpacing;
- },
- onLayoutEnd: () => {
- if (largeGraphMode) {
- graph.getEdges().forEach((edge: any) => {
- console.log('onLayoutEnd', edge.oriLabel);
- if (!edge.oriLabel) return;
- edge.update({
- label: labelFormatter(edge.oriLabel, labelMaxLength),
- });
- });
- }
- },
- tick: () => {
- graph.refreshPositions();
- },
- };
-
- if (nodeSize) config['nodeSize'] = nodeSize;
- if (collideStrength) config['collideStrength'] = collideStrength;
- if (alpha) config['alpha'] = alpha;
- if (alphaDecay) config['alphaDecay'] = alphaDecay;
- if (alphaMin) config['alphaMin'] = alphaMin;
-
- return config;
-};
-
-const hideItems = (graph: any) => {
- hiddenItemIds.forEach(id => {
- graph.hideItem(id);
- });
-};
-
-const showItems = (graph: any) => {
- graph.getNodes().forEach((node: any) => {
- if (!node.isVisible()) graph.showItem(node);
- });
- graph.getEdges().forEach((edge: any) => {
- if (!edge.isVisible()) edge.showItem(edge);
- });
- hiddenItemIds = [];
-};
-
-const handleRefreshGraph = (
- graph: any,
- graphData: any,
- width: any,
- height: any,
- largeGraphMode: any,
- edgeLabelVisible: any
-) => {
- if (!graphData || !graph) return;
- clearFocusItemState(graph);
- // reset the filtering
- graph.getNodes().forEach((node: any) => {
- if (!node.isVisible()) node.show();
- });
- graph.getEdges().forEach((edge: any) => {
- if (!edge.isVisible()) edge.show();
- });
-
- let nodes = [],
- edges = [];
-
- nodes = graphData.nodes;
- const processRes = processNodesEdges(
- nodes,
- graphData.edges || [],
- width,
- height,
- largeGraphMode,
- edgeLabelVisible
- );
-
- edges = processRes.edges;
-
- graph.changeData({ nodes, edges });
-
- hideItems(graph);
- graph.getNodes().forEach((node: any) => {
- node.toFront();
- });
-
- // layout.instance.stop();
- // force 需要使用不同 id 的对象才能进行全新的布局,否则会使用原来的引用。因此复制一份节点和边作为 force 的布局数据
- layout.instance.init({
- nodes: graphData.nodes,
- edges,
- });
-
- layout.instance.minMovement = 0.0001;
- // layout.instance.getCenter = d => {
- // const cachePosition = cachePositions[d.id];
- // if (!cachePosition && (d.x || d.y)) return [d.x, d.y, 10];
- // else if (cachePosition) return [cachePosition.x, cachePosition.y, 10];
- // return [width / 2, height / 2, 10];
- // }
- layout.instance.getMass = (d: any) => {
- const cachePosition = cachePositions[d.id];
- if (cachePosition) return 5;
- return 1;
- };
- layout.instance.execute();
- return { nodes, edges };
-};
-
-const getMixedGraph = (
- aggregatedData: any,
- originData: any,
- nodeMap: any,
- aggregatedNodeMap: any,
- expandArray: any,
- collapseArray: any
-) => {
- let nodes: any = [],
- edges: any = [];
-
- const expandMap: any = {},
- collapseMap: any = {};
- expandArray.forEach((expandModel: any) => {
- expandMap[expandModel.id] = true;
- });
- collapseArray.forEach((collapseModel: any) => {
- collapseMap[collapseModel.id] = true;
- });
-
- aggregatedData.clusters.forEach((cluster: any, i: any) => {
- if (expandMap[cluster.id]) {
- nodes = nodes.concat(cluster.nodes);
- aggregatedNodeMap[cluster.id].expanded = true;
- } else {
- nodes.push(aggregatedNodeMap[cluster.id]);
- aggregatedNodeMap[cluster.id].expanded = false;
- }
- });
-
- originData.edges.forEach((edge: any) => {
- const isSourceInExpandArray = expandMap[nodeMap[edge.source].clusterId];
- const isTargetInExpandArray = expandMap[nodeMap[edge.target].clusterId];
- if (isSourceInExpandArray && isTargetInExpandArray) {
- edges.push(edge);
- } else if (isSourceInExpandArray) {
- const targetClusterId = nodeMap[edge.target].clusterId;
- const vedge = {
- source: edge.source,
- target: targetClusterId,
- id: uniqueId('edge'),
- label: '',
- };
- edges.push(vedge);
- } else if (isTargetInExpandArray) {
- const sourceClusterId = nodeMap[edge.source].clusterId;
- const vedge = {
- target: edge.target,
- source: sourceClusterId,
- id: uniqueId('edge'),
- label: '',
- };
- edges.push(vedge);
- }
- });
- aggregatedData.clusterEdges.forEach((edge: any) => {
- if (expandMap[edge.source] || expandMap[edge.target]) return;
- else edges.push(edge);
- });
- return { nodes, edges };
-};
-
-const examAncestors = (
- model: any,
- expandedArray: any,
- length: any,
- keepTags: any
-) => {
- for (let i = 0; i < length; i++) {
- const expandedNode = expandedArray[i];
- if (!keepTags[i] && model.parentId === expandedNode.id) {
- keepTags[i] = true; // 需要被保留
- examAncestors(expandedNode, expandedArray, length, keepTags);
- break;
- }
- }
-};
-
-// 展开节点数据控制
-const manageExpandCollapseArray = (
- nodeNumber: any,
- model: any,
- collapseArray: any,
- expandArray: any
-) => {
- manipulatePosition = { x: model.x, y: model.y };
-
- // 维护 expandArray,若当前画布节点数高于上限,移出 expandedArray 中非 model 祖先的节点)
- if (nodeNumber > NODE_LIMIT) {
- // 若 keepTags[i] 为 true,则 expandedArray 的第 i 个节点需要被保留
- const keepTags: any = {};
- const expandLen = expandArray.length;
- // 检查 X 的所有祖先并标记 keepTags
- examAncestors(model, expandArray, expandLen, keepTags);
- // 寻找 expandedArray 中第一个 keepTags 不为 true 的点
- let shiftNodeIdx = -1;
- for (let i = 0; i < expandLen; i++) {
- if (!keepTags[i]) {
- shiftNodeIdx = i;
- break;
- }
- }
- // 如果有符合条件的节点,将其从 expandedArray 中移除
- if (shiftNodeIdx !== -1) {
- let foundNode = expandArray[shiftNodeIdx];
- if (foundNode.level === 2) {
- let foundLevel1 = false;
- // 找到 expandedArray 中 parentId = foundNode.id 且 level = 1 的第一个节点
- for (let i = 0; i < expandLen; i++) {
- const eNode = expandArray[i];
- if (eNode.parentId === foundNode.id && eNode.level === 1) {
- foundLevel1 = true;
- foundNode = eNode;
- expandArray.splice(i, 1);
- break;
- }
- }
- // 若未找到,则 foundNode 不变, 直接删去 foundNode
- if (!foundLevel1) expandArray.splice(shiftNodeIdx, 1);
- } else {
- // 直接删去 foundNode
- expandArray.splice(shiftNodeIdx, 1);
- }
- // const removedNode = expandedArray.splice(shiftNodeIdx, 1); // splice returns an array
- const idSplits = foundNode.id.split('-');
- let collapseNodeId;
- // 去掉最后一个后缀
- for (let i = 0; i < idSplits.length - 1; i++) {
- const str = idSplits[i];
- if (collapseNodeId) collapseNodeId = `${collapseNodeId}-${str}`;
- else collapseNodeId = str;
- }
- const collapseNode = {
- id: collapseNodeId,
- parentId: foundNode.id,
- level: foundNode.level - 1,
- };
- collapseArray.push(collapseNode);
- }
- }
-
- const currentNode = {
- id: model.id,
- level: model.level,
- parentId: model.parentId,
- };
-
- // 加入当前需要展开的节点
- expandArray.push(currentNode);
-
- graph.get('canvas').setCursor('default');
- return { expandArray, collapseArray };
-};
-
-const cacheNodePositions = (nodes: any) => {
- const positionMap: any = {};
- const nodeLength = nodes.length;
- for (let i = 0; i < nodeLength; i++) {
- const node = nodes[i].getModel();
- positionMap[node.id] = {
- x: node.x,
- y: node.y,
- level: node.level,
- };
- }
- return positionMap;
-};
-
-// 布局停止动画
-const stopLayout = () => {
- layout.instance.stop();
-};
-
-// 图监听事件
-const bindListener = (graph: any) => {
- graph.on('node:mouseenter', (evt: any) => {
- const { item } = evt;
- graph.setItemState(item, 'hover', true);
- item.toFront();
- });
-
- graph.on('node:mouseleave', (evt: any) => {
- const { item } = evt;
- graph.setItemState(item, 'hover', false);
- });
-
- graph.on('edge:mouseenter', (evt: any) => {
- const { item } = evt;
- const model = item.getModel();
- const currentLabel = model.label;
- item.update({
- label: model.oriLabel,
- });
- model.oriLabel = currentLabel;
- item.toFront();
- item.getSource().toFront();
- item.getTarget().toFront();
- });
-
- graph.on('edge:mouseleave', (evt: any) => {
- const { item } = evt;
- const model = item.getModel();
- const currentLabel = model.label;
- item.update({
- label: model.oriLabel,
- });
- model.oriLabel = currentLabel;
- });
- // click node to show the detail drawer
- graph.on('node:click', (evt: any) => {
- stopLayout();
- if (!shiftKeydown) clearFocusItemState(graph);
- else clearFocusEdgeState(graph);
- const { item } = evt;
-
- // highlight the clicked node, it is down by click-select
- graph.setItemState(item, 'focus', true);
-
- if (!shiftKeydown) {
- // 将相关边也高亮
- const relatedEdges = item.getEdges();
- relatedEdges.forEach((edge: any) => {
- graph.setItemState(edge, 'focus', true);
- });
- }
- });
-
- // click edge to show the detail of integrated edge drawer
- graph.on('edge:click', (evt: any) => {
- stopLayout();
- if (!shiftKeydown) clearFocusItemState(graph);
- const { item } = evt;
- console.log(item);
- // highlight the clicked edge
- graph.setItemState(item, 'focus', true);
- });
-
- // click canvas to cancel all the focus state
- graph.on('canvas:click', (evt: any) => {
- clearFocusItemState(graph);
- console.log(
- graph.getGroup(),
- graph.getGroup().getBBox(),
- graph.getGroup().getCanvasBBox()
- );
- });
-};
-
-// 导出渲染函数
-export function randerGroph(graphG6Dom: HTMLElement | undefined, data: any) {
- if (!graphG6Dom) return;
-
- // graphG6Dom.value.style.backgroundColor = '#2b2f33';
-
- CANVAS_WIDTH = graphG6Dom.scrollWidth;
- CANVAS_HEIGHT = (graphG6Dom.scrollHeight || 500) - 30;
-
- const nodeMap: any = {};
- const clusteredData = louvain(data, false, 'weight');
- const aggregatedData: any = { nodes: [], edges: [] };
- clusteredData.clusters.forEach((cluster, i) => {
- cluster.nodes.forEach(node => {
- const readNode = data.nodes.find((item: any) => item.id === node.id);
- node = Object.assign(node, readNode);
- node.level = 0;
- // node.label = node.id;
- node.type = '';
- node.colorSet = colorSets[i];
- nodeMap[node.id] = node;
- });
- const cnode = {
- id: cluster.id,
- type: 'aggregated-node',
- count: cluster.nodes.length,
- level: 1,
- label: cluster.id,
- colorSet: colorSets[i],
- idx: i,
- };
- aggregatedNodeMap[cluster.id] = cnode;
- aggregatedData.nodes.push(cnode);
- });
- clusteredData.clusterEdges.forEach(clusterEdge => {
- const cedge: any = {
- ...clusterEdge,
- size: Math.log(clusterEdge.count),
- // label: '',
- id: uniqueId('edge'),
- };
- // 自旋
- if (cedge.source === cedge.target) {
- cedge.type = 'loop';
- cedge.loopCfg = {
- dist: 20,
- };
- } else {
- cedge.type = 'line';
- }
- aggregatedData.edges.push(cedge);
- });
-
- // data.edges.forEach((edge: any) => {
- // edge.label = `${edge.source}-${edge.target}`;
- // edge.id = uniqueId('edge');
- // });
-
- currentUnproccessedData = aggregatedData;
-
- // 节点上下文菜单
- const contextMenu = new Menu({
- shouldBegin(evt: any) {
- if (evt.target && evt.target.isCanvas && evt.target.isCanvas())
- return true;
- if (evt.item) return true;
- return false;
- },
- getContent(evt: any) {
- const { item } = evt;
- if (evt.target && evt.target.isCanvas && evt.target.isCanvas()) {
- return `
-
-
- 1. 显示所有隐藏项
-
-
- 2. 折叠所有集群
-
-
`;
- }
- if (!item) return '';
- const itemType = item.getType();
- const model = item.getModel();
- if (itemType && model) {
- if (itemType === 'node') {
- if (model.level !== 0) {
- return `
-
-
- 1. 展开集群
-
-
- 2. 隐藏节点
-
-
- `;
- } else {
- return `
-
-
- 1. 折叠集群
-
-
- 2. 隐藏节点
-
-
- `;
- }
- } else {
- return `
-
- `;
- }
- }
- return '';
- },
- handleMenuClick: (target, item) => {
- const model: any = item && item.getModel();
- const liIdStrs = target.id.split('-');
- let mixedGraphData;
- switch (liIdStrs[0]) {
- case 'hide':
- graph.hideItem(item);
- hiddenItemIds.push(model.id);
- break;
- case 'expand':
- const newArray = manageExpandCollapseArray(
- graph.getNodes().length,
- model,
- collapseArray,
- expandArray
- );
- expandArray = newArray.expandArray;
- collapseArray = newArray.collapseArray;
- mixedGraphData = getMixedGraph(
- clusteredData,
- data,
- nodeMap,
- aggregatedNodeMap,
- expandArray,
- collapseArray
- );
- break;
- case 'collapse':
- const aggregatedNode = aggregatedNodeMap[model.clusterId];
- manipulatePosition = { x: aggregatedNode.x, y: aggregatedNode.y };
- collapseArray.push(aggregatedNode);
- for (let i = 0; i < expandArray.length; i++) {
- if (expandArray[i].id === model.clusterId) {
- expandArray.splice(i, 1);
- break;
- }
- }
- mixedGraphData = getMixedGraph(
- clusteredData,
- data,
- nodeMap,
- aggregatedNodeMap,
- expandArray,
- collapseArray
- );
- break;
- case 'collapseAll':
- expandArray = [];
- collapseArray = [];
- mixedGraphData = getMixedGraph(
- clusteredData,
- data,
- nodeMap,
- aggregatedNodeMap,
- expandArray,
- collapseArray
- );
- break;
- case 'restart':
- console.log('restart');
- break;
- case 'show':
- showItems(graph);
- break;
- default:
- break;
- }
- if (mixedGraphData) {
- cachePositions = cacheNodePositions(graph.getNodes());
- currentUnproccessedData = mixedGraphData;
- handleRefreshGraph(
- graph,
- currentUnproccessedData,
- CANVAS_WIDTH,
- CANVAS_HEIGHT,
- largeGraphMode,
- true
- );
- }
- },
- // offsetX and offsetY include the padding of the parent container
- // 需要加上父级容器的 padding-left 16 与自身偏移量 10
- offsetX: 16 + 10,
- // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
- offsetY: 0,
- // the types of items that allow the menu show up
- // 在哪些类型的元素上响应
- itemTypes: ['node', 'edge', 'canvas'],
- });
-
- // 节点提示菜单
- const tooltip = new Tooltip({
- offsetX: 20,
- offsetY: 20,
- getContent(e: any) {
- const node = e.item.getModel();
- const neInfo = node.info;
- if (!neInfo) {
- return `ID:${node.id}
`;
- }
- const serverState = neInfo.serverState;
- console.log(neInfo);
- const hasNeID = serverState.neId;
- if (!hasNeID) {
- return '状态:异常
';
- }
- return `
-
-
状态:
- ${hasNeID ? '正常' : '异常'}
-
-
刷新时间:
- ${serverState.refreshTime ?? '--'}
-
-
ID:${hasNeID}
-
名称:${serverState.neName ?? '--'}
-
版本:
- ${serverState.version ?? '--'}
-
-
SN:${serverState.sn ?? '--'}
-
有效期:
- ${serverState.expire ?? '--'}
-
-
- `;
- },
- itemTypes: ['node'],
- });
-
- const { edges: processedEdges } = processNodesEdges(
- currentUnproccessedData.nodes,
- currentUnproccessedData.edges,
- CANVAS_WIDTH,
- CANVAS_HEIGHT,
- true,
- true
- );
-
- graph = new Graph({
- container: graphG6Dom,
- width: graphG6Dom?.clientWidth,
- height: graphG6Dom?.clientHeight,
- linkCenter: true,
- minZoom: 0.1,
- groupByTypes: false,
- modes: {
- default: [
- {
- type: 'drag-canvas',
- enableOptimize: true,
- },
- {
- type: 'zoom-canvas',
- enableOptimize: true,
- optimizeZoom: 0.01,
- },
- 'drag-node',
- ],
- lassoSelect: [
- {
- type: 'zoom-canvas',
- enableOptimize: true,
- optimizeZoom: 0.01,
- },
- {
- type: 'lasso-select',
- selectedState: 'focus',
- trigger: 'drag',
- },
- ],
- fisheyeMode: [],
- },
- defaultNode: {
- type: 'aggregated-node',
- size: DEFAULTNODESIZE,
- },
- plugins: [contextMenu, tooltip],
- });
-
- graph.get('canvas').set('localRefresh', false);
-
- const layoutConfig = getForceLayoutConfig(graph, largeGraphMode);
- layoutConfig.center = [CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2];
- layout.instance = new Layout['gForce'](layoutConfig);
- layout.instance.init({
- nodes: aggregatedData.nodes,
- edges: processedEdges,
- });
- layout.instance.execute();
- // console.log(processedEdges)
- bindListener(graph);
- // graph.data({ nodes: aggregatedData.nodes, edges: processedEdges });
- graph.data({ nodes: aggregatedData.nodes, edges: processedEdges });
- graph.render();
-
- // 节点展开
- for (const model of aggregatedData.nodes) {
- const newArray = manageExpandCollapseArray(
- graph.getNodes().length,
- model,
- collapseArray,
- expandArray
- );
- expandArray = newArray.expandArray;
- collapseArray = newArray.collapseArray;
- let mixedGraphData = getMixedGraph(
- clusteredData,
- data,
- nodeMap,
- aggregatedNodeMap,
- expandArray,
- collapseArray
- );
- if (mixedGraphData) {
- cachePositions = cacheNodePositions(graph.getNodes());
- let currentUnproccessedData = mixedGraphData;
- handleRefreshGraph(
- graph,
- currentUnproccessedData,
- CANVAS_WIDTH,
- CANVAS_HEIGHT,
- largeGraphMode,
- true
- );
- }
- }
-
- return graph;
-}
diff --git a/src/views/monitor/topology-build/hooks/useEdge.ts b/src/views/monitor/topology-build/hooks/useEdge.ts
new file mode 100644
index 00000000..1929525a
--- /dev/null
+++ b/src/views/monitor/topology-build/hooks/useEdge.ts
@@ -0,0 +1,151 @@
+import { message, Form } from 'ant-design-vue/lib';
+import { reactive, watch } from 'vue';
+import { graphG6 } from './useGraph';
+
+/**图边内置边类型 */
+export const edgeTypeOptions = [
+ {
+ value: 'line',
+ label: '直线,连接两个节点的直线',
+ },
+ {
+ value: 'polyline',
+ label: '折线,多段线段构成的折线,连接两个端点',
+ },
+ {
+ value: 'arc',
+ label: '圆弧线,连接两个节点的一段圆弧',
+ },
+ {
+ value: 'quadratic',
+ label: '二阶贝塞尔曲线,只有一个控制点的曲线',
+ },
+ {
+ value: 'cubic',
+ label: '三阶贝塞尔曲线,有两个控制点的曲线',
+ },
+ {
+ value: 'cubic-vertical',
+ label: '垂直方向的三阶贝塞尔曲线',
+ },
+ {
+ value: 'cubic-horizontal',
+ label: '水平方向的三阶贝塞尔曲线',
+ },
+ {
+ value: 'loop',
+ label: '自环',
+ },
+];
+
+/**图边标签文本位置 */
+export const edgePositionOptions = [
+ {
+ value: 'start',
+ label: '开头',
+ },
+ {
+ value: 'middle',
+ label: '中间',
+ },
+ {
+ value: 'end',
+ label: '末尾',
+ },
+];
+
+/**边信息状态类型 */
+type EdgeStateType = {
+ /**图边原始数据 */
+ origin: Record;
+ /**图边表单数据 */
+ form: Record;
+};
+
+/**边信息状态 */
+export let edgeState: EdgeStateType = reactive({
+ origin: {},
+ form: {
+ id: '',
+ source: '',
+ target: '',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+});
+
+/**图边对话框内表单属性和校验规则 */
+export const edgeStateForm = Form.useForm(
+ edgeState.form,
+ reactive({
+ id: [{ required: true, message: '边唯一 ID' }],
+ source: [{ required: true, message: '起始点 id' }],
+ target: [{ required: true, message: '结束点 id' }],
+ type: [{ required: true, message: 'line' }],
+ })
+);
+
+/**图边编辑监听更新视图 */
+watch(edgeState.form, edge => {
+ const info = JSON.parse(JSON.stringify(edge));
+ const edgeId = info.id;
+ if (edgeId) {
+ graphG6.value.clearItemStates(edgeId, 'selected');
+ graphG6.value.updateItem(edgeId, info);
+ }
+});
+
+/**图边新增或更新 */
+export function handleOkEdge() {
+ const edge = JSON.parse(JSON.stringify(edgeState.form));
+ if (!edge.id) {
+ message.warn({
+ content: `边元素ID错误`,
+ duration: 2,
+ });
+ return false;
+ }
+ // graphG6.value.removeItem(edge.id);
+ // edge.id = `${edge.source}~${Date.now()}~${edge.target}`;
+ // graphG6.value.addItem('edge', edge);
+ const item = graphG6.value.findById(edge.id);
+ if (item) {
+ graphG6.value.updateItem(item, edge);
+ } else {
+ edge.id = `${edge.source}~${Date.now()}~${edge.target}`;
+ graphG6.value.addItem('edge', edge);
+ }
+ edgeStateForm.resetFields();
+ edgeState.origin = {};
+ return true;
+}
+
+/**图边取消还原 */
+export function handleCancelEdge() {
+ const origin = JSON.parse(JSON.stringify(edgeState.origin));
+ if (origin.id) {
+ graphG6.value.updateItem(origin.id, origin);
+ // graphG6.value.removeItem(edgeOrigin.id);
+ // graphG6.value.addItem('edge', edgeOrigin);
+ edgeStateForm.resetFields();
+ edgeState.origin = {};
+ }
+}
diff --git a/src/views/monitor/topology-build/hooks/useGraph.ts b/src/views/monitor/topology-build/hooks/useGraph.ts
new file mode 100644
index 00000000..a3f86d89
--- /dev/null
+++ b/src/views/monitor/topology-build/hooks/useGraph.ts
@@ -0,0 +1,422 @@
+import {
+ Graph,
+ GraphData,
+ ICanvas,
+ IShapeBase,
+ Item,
+ Menu,
+ Tooltip,
+} from '@antv/g6';
+import { ref } from 'vue';
+
+/**图实例对象 */
+export const graphG6 = ref(null);
+
+/**图事件变更 */
+export const graphEvent = ref<{
+ type: string;
+ target: HTMLElement | (IShapeBase & ICanvas);
+ item: Item | null;
+}>();
+
+/**图画布右击菜单 */
+const graphCanvasMenu = new Menu({
+ offsetX: 6,
+ offseY: 10,
+ itemTypes: ['canvas'],
+ getContent(evt) {
+ return `
+ `;
+ },
+ handleMenuClick(target, item) {
+ console.log(target, item);
+ const targetId = target.id;
+ switch (targetId) {
+ case 'show':
+ // 显示节点
+ graphG6.value.getNodes().forEach((node: any) => {
+ if (!node.isVisible()) {
+ graphG6.value.showItem(node);
+ graphG6.value.refreshItem(node);
+ }
+ });
+ // 显示边
+ graphG6.value.getEdges().forEach((edge: any) => {
+ if (!edge.isVisible()) {
+ graphG6.value.showItem(edge);
+ graphG6.value.refreshItem(edge);
+ }
+ });
+ break;
+ }
+ },
+});
+
+/**图节点右击菜单 */
+const graphNodeMenu = new Menu({
+ offsetX: 6,
+ offseY: 10,
+ itemTypes: ['node'],
+ getContent(evt) {
+ console.log(evt);
+ return `
+
+
+ 1. 编辑
+
+
+ 2. 隐藏
+
+
+ `;
+ },
+ handleMenuClick(target, item) {
+ console.log(target, item);
+ const targetId = target.id;
+ switch (targetId) {
+ case 'edit':
+ graphEvent.value = { type: `nodeMenu-${targetId}`, target, item };
+ break;
+ case 'hide':
+ graphG6.value.hideItem(item);
+ break;
+ }
+ },
+});
+
+/**图节点展示 */
+const graphNodeTooltip = new Tooltip({
+ offsetX: 10,
+ offsetY: 20,
+ getContent(e: any) {
+ const outDiv = document.createElement('div');
+ outDiv.style.width = '180px';
+ outDiv.innerHTML = `
+ 自定义tooltip
+
+ - Label: ${e.item.getModel().label || e.item.getModel().id}
+
`;
+ return outDiv;
+ },
+ itemTypes: ['node'],
+});
+
+/**图边右击菜单 */
+const graphEdgeMenu = new Menu({
+ offsetX: 6,
+ offseY: 10,
+ itemTypes: ['edge'],
+ getContent(evt) {
+ console.log(evt);
+ return `
+
+
+ 1. 编辑
+
+
+ 2. 隐藏
+
+
+ `;
+ },
+ handleMenuClick(target, item) {
+ console.log(target, item);
+ const targetId = target.id;
+ switch (targetId) {
+ case 'edit':
+ graphEvent.value = { type: `edgeMenu-${targetId}`, target, item };
+ break;
+ case 'hide':
+ graphG6.value.hideItem(item);
+ break;
+ }
+ },
+});
+
+/**图边展示 */
+const graphEdgeTooltip = new Tooltip({
+ offsetX: 10,
+ offsetY: 20,
+ getContent(e: any) {
+ const outDiv = document.createElement('div');
+ outDiv.style.width = '180px';
+ outDiv.innerHTML = `
+ graphEdgeTooltip
+
+ - Label: ${e.item.getModel().label || e.item.getModel().id}
+
`;
+ return outDiv;
+ },
+ itemTypes: ['edge'],
+});
+
+/**图绑定事件 */
+function fnGraphEvent(graph: Graph) {
+ // 调用 graph.add / graph.addItem 方法之后触发
+ graph.on('afteradditem', evt => {
+ fnSelectSourceTargetOptionsData();
+ });
+
+ // 鼠标进入节点事件
+ graph.on('edge:mouseenter', (ev: any) => {
+ // 获得鼠标当前目标边
+ const edge = ev.item;
+ // 该边的起始点
+ const source = edge.getSource();
+ // 该边的结束点
+ const target = edge.getTarget();
+ // 先将边提前,再将端点提前。这样该边两个端点还是在该边上层,较符合常规。
+ // edge.toFront();
+ // source.toFront();
+ // target.toFront();
+ });
+
+ graph.on('edge:mouseleave', (ev: any) => {
+ // 获得图上所有边实例
+ const edges = graph.getEdges();
+ // 遍历边,将所有边的层级放置在后方,以恢复原样
+ // edges.forEach(edge => {
+ // edge.toBack();
+ // });
+ });
+
+ graph.on('node:mouseenter', evt => {
+ // 获得鼠标当前目标节点
+ const node = evt.item;
+ // 获取该节点的所有相关边
+ const edges = node && graph.getEdges();
+ // 遍历相关边,将所有相关边提前,再将相关边的两个端点提前,以保证相关边的端点在边的上方常规效果
+ // edges.forEach((edge: any) => {
+ // edge.toFront();
+ // edge.getSource().toFront();
+ // edge.getTarget().toFront();
+ // });
+ // graphEvent.value = {
+ // type: 'node:mouseenter',
+ // target: evt.target,
+ // item: evt.item,
+ // };
+ });
+
+ graph.on('node:mouseleave', (ev: any) => {
+ // 获得图上所有边实例
+ const edges = graph.getEdges();
+ // 遍历边,将所有边的层级放置在后方,以恢复原样
+ // edges.forEach(edge => {
+ // edge.toBack();
+ // });
+ });
+}
+
+/**图元素选择开始结束点 */
+export const selectSourceTargetOptions = ref[]>([]);
+
+/**
+ * 图元素选择开始结束点数据获取
+ */
+function fnSelectSourceTargetOptionsData() {
+ // 节点
+ graphG6.value.getNodes().forEach((node: any) => {
+ const info = JSON.parse(JSON.stringify(node.getModel()));
+ selectSourceTargetOptions.value.push({
+ value: info.id,
+ label: info.label,
+ info,
+ });
+ });
+ // 框
+ graphG6.value.getCombos().forEach((combo1: any) => {
+ const info = JSON.parse(JSON.stringify(combo1.getModel()));
+ selectSourceTargetOptions.value.push({
+ value: info.id,
+ label: info.label,
+ info,
+ });
+ });
+}
+
+/**图数据渲染 */
+export function handleRanderGraph(container: HTMLElement, data: GraphData) {
+ if (!container) return;
+ const { clientHeight, clientWidth } = container;
+
+ const graph = new Graph({
+ container: container,
+ width: clientWidth,
+ height: clientHeight,
+ animate: true,
+ fitCenter: true,
+ modes: {
+ // default: [
+ // // 允许拖拽画布、放缩画布、拖拽节点
+ // 'drag-canvas',
+ // 'zoom-canvas',
+ // 'drag-node',
+ // ],
+ default: [
+ {
+ type: 'click-select',
+ selectEdge: true,
+ },
+ 'drag-combo',
+ {
+ type: 'drag-node',
+ onlyChangeComboSize: true,
+ },
+ 'drag-canvas',
+ 'zoom-canvas',
+ 'collapse-expand-combo',
+ ],
+ edit: [
+ {
+ type: 'click-select',
+ selectEdge: true,
+ },
+ {
+ type: 'drag-node',
+ shouldEnd: (e: any) => {
+ return true;
+ },
+ },
+ { type: 'drag-combo' },
+ 'drag-canvas',
+ 'zoom-canvas',
+ { type: 'create-edge', key: 'alt' },
+ ],
+ },
+ groupByTypes: false,
+ // layout: {
+ // type: 'dagre',
+ // sortByCombo: false,
+ // ranksep: 10,
+ // nodesep: 10,
+ // },
+ // 全局节点 矩形
+ defaultNode: {
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ radius: 8,
+ // fill: '#ffffff',
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20, // triangle 特有
+ },
+ direction: 'up', // triangle 三角形的方向
+ },
+ // 全局边 三次贝塞尔曲线
+ defaultEdge: {
+ type: 'polyline',
+ style: {
+ offset: 20, // 拐弯处距离节点最小距离
+ radius: 2, // 拐弯处的圆角弧度,若不设置则为直角
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ // defaultEdge: {
+ // type: 'line',
+ // },
+ // 全局框节点 矩形
+ defaultCombo: {
+ type: 'rect', // Combo 类型
+ size: [40, 40],
+ style: {
+ fillOpacity: 0.1,
+ },
+ },
+ plugins: [
+ graphCanvasMenu,
+ graphNodeMenu,
+ graphNodeTooltip,
+ graphEdgeMenu,
+ graphEdgeTooltip,
+ ],
+ });
+ graph.data(data);
+ graph.render();
+
+ // 图绑定事件
+ fnGraphEvent(graph);
+
+ graphG6.value = graph;
+
+ // 图元素选择开始结束点数据
+ fnSelectSourceTargetOptionsData();
+
+ return graph;
+}
+
+/**图模式选择项 */
+export const graphModeOptions = [
+ {
+ value: 'default',
+ label: '默认',
+ },
+ {
+ value: 'edit',
+ label: '编辑',
+ },
+];
+
+/**图模式选择项 */
+export const graphMode = ref('default');
+
+/**图模式改变 default | edit */
+export function handleChangeMode(value: any) {
+ console.log(value, JSON.parse(JSON.stringify(graphG6.value.save())));
+ graphG6.value.setMode(value);
+ graphMode.value = graphG6.value.getCurrentMode();
+}
diff --git a/src/views/monitor/topology-build/hooks/useNode.ts b/src/views/monitor/topology-build/hooks/useNode.ts
new file mode 100644
index 00000000..ee4d1085
--- /dev/null
+++ b/src/views/monitor/topology-build/hooks/useNode.ts
@@ -0,0 +1,294 @@
+import { message, Form } from 'ant-design-vue/lib';
+import { reactive, watch } from 'vue';
+import { graphG6 } from './useGraph';
+
+/**图节点内置边类型 */
+export const nodeTypeOptions = [
+ {
+ value: 'circle',
+ label: '圆形',
+ },
+ {
+ value: 'rect',
+ label: '矩形',
+ },
+ {
+ value: 'ellipse',
+ label: '椭圆',
+ },
+ {
+ value: 'diamond',
+ label: '菱形',
+ },
+ {
+ value: 'triangle',
+ label: '三角形',
+ },
+ {
+ value: 'star',
+ label: '星形',
+ },
+ {
+ value: 'image',
+ label: '图片',
+ },
+ {
+ value: 'donut',
+ label: '面包圈',
+ },
+];
+
+/**图节点标签文本位置 */
+export const nodePositionOptions = [
+ {
+ value: 'top',
+ label: '上',
+ },
+ {
+ value: 'left',
+ label: '左',
+ },
+ {
+ value: 'right',
+ label: '右',
+ },
+ {
+ value: 'bottom',
+ label: '下',
+ },
+ {
+ value: 'center',
+ label: '居中',
+ },
+];
+
+/**图节点三角形方向 */
+export const nodeDirectionOptions = [
+ { value: 'up', label: '向上' },
+ { value: 'down', label: '向下' },
+ { value: 'left', label: '向左' },
+ { value: 'right', label: '向右' },
+];
+
+/**图节点图片裁剪的形状 */
+export const nodeImageClipCfgOptions = [
+ { value: 'circle', label: '圆形' },
+ { value: 'rect', label: '矩形' },
+ { value: 'ellipse', label: '椭圆' },
+];
+
+/**图节点图片来源 */
+export const nodeImageOptions = [
+ { value: '/svg/base.svg', label: '基站' },
+ { value: '/svg/cloud.svg', label: '云' },
+ { value: '/svg/service.svg', label: '服务器' },
+ { value: '/svg/service_db.svg', label: '数据服务器' },
+];
+
+/**图节点信息状态类型 */
+type NodeStateType = {
+ /**图节点原始数据 */
+ origin: Record;
+ /**图节点表单数据 */
+ form: Record;
+};
+
+/**图节点信息状态 */
+export let nodeState: NodeStateType = reactive({
+ origin: {},
+ form: {
+ id: '',
+ x: 0,
+ y: 0,
+ type: 'circle',
+ size: [0],
+ anchorPoints: false,
+ style: {
+ fill: '#ffffff',
+ stroke: '#ffffff',
+ lineWidth: 1,
+ },
+ label: '',
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+});
+
+/**图节点对话框内表单属性和校验规则 */
+export const nodeStateForm = Form.useForm(
+ nodeState.form,
+ reactive({
+ id: [{ required: true, message: '节点唯一 ID' }],
+ type: [{ required: true, message: 'line' }],
+ })
+);
+
+/**图节点编辑监听更新视图 */
+watch(nodeState.form, node => {
+ const info = JSON.parse(JSON.stringify(node));
+ const nodeId = info.id;
+ if (nodeId) {
+ // 图片类型需要移除style属性,避免填充
+ if (info.type === 'image') {
+ Reflect.deleteProperty(info, 'style');
+ }
+ graphG6.value.clearItemStates(nodeId, 'selected');
+ graphG6.value.updateItem(nodeId, info);
+ // 三角和图片的样式变更需要重绘才生效
+ if (info.type === 'triangle' || info.type === 'image') {
+ graphG6.value.read(graphG6.value.save());
+ }
+ }
+});
+
+/**图节点类型输入限制 */
+export function handleTypeChange(type: any) {
+ // 设置图标属性
+ if (['circle', 'ellipse', 'diamond', 'star', 'donut'].includes(type)) {
+ const origin = nodeState.origin;
+ if (origin.icon) {
+ nodeState.form = Object.assign(nodeState.form, {
+ icon: origin.icon,
+ });
+ } else {
+ nodeState.form = Object.assign(nodeState.form, {
+ icon: {
+ show: false,
+ img: '',
+ width: 25,
+ height: 25,
+ },
+ });
+ }
+ } else if (type === 'triangle') {
+ // 三角
+ const origin = nodeState.origin;
+ if (origin.icon) {
+ nodeState.form = Object.assign(nodeState.form, {
+ direction: origin.direction || 'up', // triangle 三角形的方向
+ icon: Object.assign({ offset: 20 }, origin.icon),
+ });
+ } else {
+ nodeState.form = Object.assign(nodeState.form, {
+ direction: 'up', // triangle 三角形的方向
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20, // triangle 特有
+ },
+ });
+ }
+ }
+ // 设置图片属性
+ if (type === 'image') {
+ const origin = nodeState.origin;
+ if (origin.img) {
+ nodeState.form = Object.assign(nodeState.form, {
+ img: origin.img,
+ clipCfg: origin.clipCfg,
+ });
+ } else {
+ nodeState.form = Object.assign(nodeState.form, {
+ img: '/svg/service.svg',
+ clipCfg: {
+ show: false,
+ width: 0,
+ height: 0,
+ type: 'circle',
+ },
+ });
+ }
+ Reflect.deleteProperty(nodeState.form, 'style');
+ } else {
+ // 当切换非图片时补充style属性
+ if (!Reflect.has(nodeState.form, 'style')) {
+ nodeState.form = Object.assign(nodeState.form, {
+ style: {
+ fill: '#ffffff',
+ stroke: '#ffffff',
+ lineWidth: 1,
+ },
+ });
+ }
+ }
+}
+
+/**图节点大小输入限制 */
+export function handleSizeChange(value: any) {
+ // 处理格式
+ let intArr: number[] = [];
+ for (const v of value) {
+ const intV = parseInt(v);
+ if (!isNaN(intV)) {
+ intArr.push(intV);
+ }
+ }
+ // 节点类型限制size
+ const nodeType = nodeState.form.type;
+ switch (nodeType) {
+ case 'circle':
+ case 'star':
+ case 'donut':
+ intArr = intArr.slice(0, 1);
+ break;
+ case 'rect':
+ case 'ellipse':
+ case 'diamond':
+ case 'triangle':
+ case 'image':
+ intArr = intArr.slice(0, 2);
+ break;
+ }
+ nodeState.form.size = intArr;
+}
+
+/**图节点新增或更新 */
+export function handleOkNode() {
+ const node = JSON.parse(JSON.stringify(nodeState.form));
+ if (!node.id) {
+ message.warn({
+ content: `节点元素ID错误`,
+ duration: 2,
+ });
+ return false;
+ }
+ // graphG6.value.removeItem(node.id);
+ // graphG6.value.addItem('node', node);
+ const item = graphG6.value.findById(node.id);
+ if (item) {
+ graphG6.value.updateItem(item, node);
+ } else {
+ graphG6.value.addItem('node', node);
+ }
+ // 三角和图片的样式变更需要重绘才生效
+ if (node.type === 'triangle' || node.type === 'image') {
+ graphG6.value.read(graphG6.value.save());
+ }
+ nodeStateForm.resetFields();
+ nodeState.origin = {};
+ return true;
+}
+
+/**图节点取消还原 */
+export function handleCancelNode() {
+ const origin = JSON.parse(JSON.stringify(nodeState.origin));
+ if (origin.id) {
+ graphG6.value.updateItem(origin.id, origin);
+ // 三角和图片的样式变更需要重绘才生效
+ if (origin.type === 'triangle' || origin.type === 'image') {
+ graphG6.value.read(graphG6.value.save());
+ }
+ console.log(JSON.parse(JSON.stringify(nodeState.form)));
+ nodeStateForm.resetFields();
+ nodeState.origin = {};
+ }
+}
diff --git a/src/views/monitor/topology-build/index.vue b/src/views/monitor/topology-build/index.vue
index 43a514d5..78797adf 100644
--- a/src/views/monitor/topology-build/index.vue
+++ b/src/views/monitor/topology-build/index.vue
@@ -2,28 +2,68 @@
import { reactive, onMounted, ref, watch } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import useI18n from '@/hooks/useI18n';
-import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
-import { listNe, stateNe } from '@/api/ne/ne';
-import { parseDateToStr } from '@/utils/date-utils';
-import { Graph, Menu, Tooltip } from '@antv/g6';
-import { message, Modal, Form, notification } from 'ant-design-vue/lib';
+import {
+ handleRanderGraph,
+ handleChangeMode,
+ graphEvent,
+ graphG6,
+ selectSourceTargetOptions,
+ graphMode,
+ graphModeOptions,
+} from './hooks/useGraph';
+import {
+ edgeTypeOptions,
+ edgePositionOptions,
+ handleOkEdge,
+ handleCancelEdge,
+ edgeState,
+ edgeStateForm,
+} from './hooks/useEdge';
+import {
+ nodeTypeOptions,
+ nodePositionOptions,
+ nodeDirectionOptions,
+ nodeImageClipCfgOptions,
+ nodeImageOptions,
+ handleTypeChange,
+ handleSizeChange,
+ handleOkNode,
+ handleCancelNode,
+ nodeState,
+ nodeStateForm,
+} from './hooks/useNode';
const { t } = useI18n();
/**图DOM节点实例对象 */
const graphG6Dom = ref(undefined);
-/**图实例对象 */
-const graphG6 = ref(null);
+/**图监听事件变更 */
+watch(graphEvent, v => {
+ if (!v) return;
+ const { type, target, item } = v;
+ console.log(type, target, item);
-/**图实例状态 */
-const graphG6State = reactive>({
- mode: 'default',
- editEdge: {},
+ if (type === 'edgeMenu-edit' && item) {
+ const edge = item.getModel();
+ edgeState.origin = JSON.parse(JSON.stringify(edge));
+ edgeState.form = Object.assign(edgeState.form, edge);
+ modalState.title = '边信息编辑';
+ modalState.formType = 'edge';
+ modalState.visible = true;
+ }
+ if (type === 'nodeMenu-edit' && item) {
+ const node = item.getModel();
+ nodeState.origin = JSON.parse(JSON.stringify(node));
+ nodeState.form = Object.assign(nodeState.form, node);
+ modalState.title = '节点信息编辑';
+ modalState.formType = 'node';
+ modalState.visible = true;
+ }
});
/**图数据 */
-const graphG6Data = reactive>({
+const graphG6Data2 = reactive>({
nodes: [
// 0 基站
{
@@ -320,288 +360,186 @@ const graphG6Data = reactive>({
],
});
-/**图绑定事件 */
-function graphEvent(graph: Graph) {
- // 调用 graph.add / graph.addItem 方法之后触发
- graph.on('afteradditem', evt => {
- fnSelectSourceTargetOptionsData();
- });
-
- // 鼠标进入节点事件
- graph.on('edge:mouseenter', (ev: any) => {
- // 获得鼠标当前目标边
- const edge = ev.item;
- // 该边的起始点
- const source = edge.getSource();
- // 该边的结束点
- const target = edge.getTarget();
- // 先将边提前,再将端点提前。这样该边两个端点还是在该边上层,较符合常规。
- // edge.toFront();
- // source.toFront();
- // target.toFront();
- });
-
- graph.on('edge:mouseleave', (ev: any) => {
- // 获得图上所有边实例
- const edges = graph.getEdges();
- // 遍历边,将所有边的层级放置在后方,以恢复原样
- // edges.forEach(edge => {
- // edge.toBack();
- // });
- });
-
- graph.on('node:mouseenter', (ev: any) => {
- // 获得鼠标当前目标节点
- const node = ev.item;
- // 获取该节点的所有相关边
- const edges = node.getEdges();
- // 遍历相关边,将所有相关边提前,再将相关边的两个端点提前,以保证相关边的端点在边的上方常规效果
- // edges.forEach((edge: any) => {
- // edge.toFront();
- // edge.getSource().toFront();
- // edge.getTarget().toFront();
- // });
- });
-
- graph.on('node:mouseleave', (ev: any) => {
- // 获得图上所有边实例
- const edges = graph.getEdges();
- // 遍历边,将所有边的层级放置在后方,以恢复原样
- // edges.forEach(edge => {
- // edge.toBack();
- // });
- });
-}
-
-/**图画布右击菜单 */
-const graphCanvasMenu = new Menu({
- offsetX: 6,
- offseY: 10,
- itemTypes: ['canvas'],
- getContent(evt) {
- return `
- `;
- },
- handleMenuClick(target, item) {
- console.log(target, item);
- const targetId = target.id;
- switch (targetId) {
- case 'show':
- // 显示节点
- graphG6.value.getNodes().forEach((node: any) => {
- if (!node.isVisible()) {
- graphG6.value.showItem(node);
- graphG6.value.refreshItem(node);
- }
- });
- // 显示边
- graphG6.value.getEdges().forEach((edge: any) => {
- if (!edge.isVisible()) {
- graphG6.value.showItem(edge);
- graphG6.value.refreshItem(edge);
- }
- });
- break;
- }
- },
-});
-
-/**图节点右击菜单 */
-const graphNodeMenu = new Menu({
- offsetX: 6,
- offseY: 10,
- itemTypes: ['node'],
- getContent(evt) {
- console.log(evt);
- return `
-
-
- 1. 编辑
-
-
- 2. 隐藏
-
-
- `;
- },
- handleMenuClick(target, item) {
- console.log(target, item);
- const targetId = target.id;
- switch (targetId) {
- case 'edit':
- const node = item.getModel();
- modalState.title = '节点信息编辑';
- modalState.formNodeOrigin = JSON.parse(JSON.stringify(node));
- modalState.formNode = Object.assign(modalState.formNode, node);
- modalState.formType = 'node';
- modalState.visible = true;
- console.log(JSON.parse(JSON.stringify(modalState.formNode)));
- break;
- case 'hide':
- graphG6.value.hideItem(item);
- break;
- }
- },
-});
-
-/**图节点展示 */
-const graphNodeTooltip = new Tooltip({
- offsetX: 10,
- offsetY: 20,
- getContent(e: any) {
- const outDiv = document.createElement('div');
- outDiv.style.width = '180px';
- outDiv.innerHTML = `
- 自定义tooltip
-
- - Label: ${e.item.getModel().label || e.item.getModel().id}
-
`;
- return outDiv;
- },
- itemTypes: ['node'],
-});
-
-/**图边右击菜单 */
-const graphEdgeMenu = new Menu({
- offsetX: 6,
- offseY: 10,
- itemTypes: ['edge'],
- getContent(evt) {
- console.log(evt);
- return `
-
-
- 1. 编辑
-
-
- 2. 隐藏
-
-
- `;
- },
- handleMenuClick(target, item) {
- console.log(target, item);
- const targetId = target.id;
- switch (targetId) {
- case 'edit':
- const edge = item.getModel();
- modalState.title = '边信息编辑';
- modalState.formEdgeOrigin = JSON.parse(JSON.stringify(edge));
- modalState.formEdge = Object.assign(modalState.formEdge, edge);
- modalState.formType = 'edge';
- modalState.visible = true;
- break;
- case 'hide':
- graphG6.value.hideItem(item);
- break;
- }
- },
-});
-
-/**图边展示 */
-const graphEdgeTooltip = new Tooltip({
- offsetX: 10,
- offsetY: 20,
- getContent(e: any) {
- const outDiv = document.createElement('div');
- outDiv.style.width = '180px';
- outDiv.innerHTML = `
- graphEdgeTooltip
-
- - Label: ${e.item.getModel().label || e.item.getModel().id}
-
`;
- return outDiv;
- },
- itemTypes: ['edge'],
-});
-
-/**查询全部网元数据列表 */
-function fnRanderGraph() {
- if (!graphG6Dom.value) return;
- const { clientHeight, clientWidth } = graphG6Dom.value;
-
- const graph = new Graph({
- container: graphG6Dom.value,
- width: clientWidth,
- height: clientHeight,
- animate: true,
- fitCenter: true,
- modes: {
- // default: [
- // // 允许拖拽画布、放缩画布、拖拽节点
- // 'drag-canvas',
- // 'zoom-canvas',
- // 'drag-node',
- // ],
- default: [
- {
- type: 'click-select',
- selectEdge: true,
+const graphG6Data = reactive>({
+ nodes: [
+ {
+ id: '0',
+ x: 415,
+ y: 482,
+ size: 48,
+ type: 'circle',
+ label: '基站',
+ labelCfg: {
+ position: 'bottom',
+ offset: 10,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
},
- 'drag-combo',
- {
- type: 'drag-node',
- onlyChangeComboSize: true,
+ },
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
},
- 'drag-canvas',
- 'zoom-canvas',
- 'collapse-expand-combo',
- ],
- edit: [
- {
- type: 'click-select',
- selectEdge: true,
- },
- {
- type: 'drag-node',
- shouldEnd: (e: any) => {
- return true;
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
},
},
- { type: 'drag-combo' },
- 'drag-canvas',
- 'zoom-canvas',
- { type: 'create-edge', key: 'alt' },
- ],
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#5B8FF9',
+ lineWidth: 2,
+ cursor: 'pointer',
+ fill: '#9EC9FF',
+ },
+ icon: {
+ show: true,
+ img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
+ width: 24,
+ height: 24,
+ offset: 20,
+ },
+ direction: 'up',
},
- groupByTypes: false,
- // layout: {
- // type: 'dagre',
- // sortByCombo: false,
- // ranksep: 10,
- // nodesep: 10,
- // },
- // 全局节点 矩形
- defaultNode: {
+ {
+ id: '1',
+ x: 132,
+ y: 42,
+ label: 'DN',
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#00b050',
+ lineWidth: 1,
+ cursor: 'pointer',
+ fill: '#00b050',
+ },
+ type: 'rect',
+ size: [80, 40],
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '2',
+ x: 747,
+ y: 431,
+ label: 'O&M',
type: 'rect',
size: [80, 40],
style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
radius: 8,
- // fill: '#ffffff',
stroke: '#ffffff',
lineWidth: 1,
cursor: 'pointer',
@@ -620,20 +558,683 @@ function fnRanderGraph() {
img: '/svg/service.svg',
width: 25,
height: 25,
- offset: 20, // triangle 特有
+ offset: 20,
},
- direction: 'up', // triangle 三角形的方向
+ direction: 'up',
},
- // 全局边 三次贝塞尔曲线
- defaultEdge: {
- type: 'polyline',
+ {
+ id: '100',
+ label: 'EMS',
+ comboId: 'combo-ems',
+ x: 106,
+ y: 450,
+ type: 'rect',
+ size: [80, 40],
style: {
- offset: 20, // 拐弯处距离节点最小距离
- radius: 2, // 拐弯处的圆角弧度,若不设置则为直角
+ radius: 8,
stroke: '#ffffff',
lineWidth: 1,
cursor: 'pointer',
},
+ depth: 50,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '190',
+ comboId: 'combo-upf',
+ x: 119,
+ y: 338,
+ label: 'UPF',
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#d580ff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ fill: '#d580ff',
+ },
+ type: 'rect',
+ size: [80, 40],
+ depth: 28,
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '110',
+ comboId: 'combo-ims',
+ x: 600,
+ y: 350,
+ label: 'IMS',
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ed7d31',
+ lineWidth: 1,
+ cursor: 'pointer',
+ fill: '#ed7d31',
+ },
+ type: 'rect',
+ size: [80, 40],
+ depth: 39,
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '170',
+ label: 'NSSF',
+ comboId: 'combo-5gc',
+ x: 300,
+ y: 50,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 17,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '130',
+ label: 'AUSF',
+ comboId: 'combo-5gc',
+ x: 450,
+ y: 50,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 16,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '140',
+ label: 'UDM',
+ comboId: 'combo-5gc',
+ x: 600,
+ y: 50,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 15,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '120',
+ label: 'AMF',
+ comboId: 'combo-5gc',
+ x: 300,
+ y: 150,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 14,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '180',
+ label: 'NRF',
+ comboId: 'combo-5gc',
+ x: 450,
+ y: 150,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 13,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '150',
+ label: 'SMF',
+ comboId: 'combo-5gc',
+ x: 300,
+ y: 250,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 12,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ {
+ id: '160',
+ label: 'PCF',
+ comboId: 'combo-5gc',
+ x: 700,
+ y: 250,
+ type: 'rect',
+ size: [80, 40],
+ style: {
+ active: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ },
+ selected: {
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 4,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ fill: 'rgb(223, 234, 255)',
+ stroke: '#4572d9',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ fill: 'rgb(247, 250, 255)',
+ stroke: 'rgb(191, 213, 255)',
+ lineWidth: 1,
+ },
+ disable: {
+ fill: 'rgb(250, 250, 250)',
+ stroke: 'rgb(224, 224, 224)',
+ lineWidth: 1,
+ },
+ radius: 8,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ depth: 11,
+ labelCfg: {
+ position: 'center',
+ offset: 0,
+ style: {
+ fill: '#000000',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ icon: {
+ show: false,
+ img: '/svg/service.svg',
+ width: 25,
+ height: 25,
+ offset: 20,
+ },
+ direction: 'up',
+ },
+ ],
+ edges: [
+ {
+ id: '170-120',
+ source: '170',
+ target: '120',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 259.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '259.5|||50',
+ },
+ endPoint: {
+ x: 259.5,
+ y: 150,
+ anchorIndex: 0,
+ id: '259.5|||150',
+ },
labelCfg: {
refX: 0,
refY: 0,
@@ -646,169 +1247,1482 @@ function fnRanderGraph() {
},
},
},
- // defaultEdge: {
- // type: 'line',
- // },
- // 全局框节点 矩形
- defaultCombo: {
- type: 'rect', // Combo 类型
- size: [40, 40],
+ {
+ id: '140-120',
+ source: '140',
+ target: '120',
+ type: 'polyline',
style: {
- fillOpacity: 0.1,
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 559.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '559.5|||50',
+ },
+ endPoint: {
+ x: 340.5,
+ y: 150,
+ anchorIndex: 1,
+ id: '340.5|||150',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
},
},
- plugins: [
- graphCanvasMenu,
- graphNodeMenu,
- graphNodeTooltip,
- graphEdgeMenu,
- graphEdgeTooltip,
- ],
- });
- graph.data(graphG6Data);
- graph.render();
+ {
+ id: '120-180',
+ source: '120',
+ target: '180',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 340.5,
+ y: 150,
+ anchorIndex: 1,
+ id: '340.5|||150',
+ },
+ endPoint: {
+ x: 409.5,
+ y: 150,
+ anchorIndex: 0,
+ id: '409.5|||150',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '140-110',
+ source: '140',
+ target: '110',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 559.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '559.5|||50',
+ },
+ endPoint: {
+ x: 559.5,
+ y: 350,
+ anchorIndex: 0,
+ id: '559.5|||350',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '120-150',
+ source: '120',
+ target: '150',
+ data: {},
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 259.5,
+ y: 150,
+ anchorIndex: 0,
+ id: '259.5|||150',
+ },
+ endPoint: {
+ x: 259.5,
+ y: 250,
+ anchorIndex: 0,
+ id: '259.5|||250',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '150-180',
+ source: '150',
+ target: '180',
+ data: {},
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 340.5,
+ y: 250,
+ anchorIndex: 1,
+ id: '340.5|||250',
+ },
+ endPoint: {
+ x: 409.5,
+ y: 150,
+ anchorIndex: 0,
+ id: '409.5|||150',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '160-120',
+ source: '160',
+ target: '120',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 659.5,
+ y: 250,
+ anchorIndex: 0,
+ id: '659.5|||250',
+ },
+ endPoint: {
+ x: 340.5,
+ y: 150,
+ anchorIndex: 1,
+ id: '340.5|||150',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '160-180',
+ source: '160',
+ target: '180',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 659.5,
+ y: 250,
+ anchorIndex: 0,
+ id: '659.5|||250',
+ },
+ endPoint: {
+ x: 490.5,
+ y: 150,
+ anchorIndex: 1,
+ id: '490.5|||150',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '160-110',
+ source: '160',
+ target: '110',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 659.5,
+ y: 250,
+ anchorIndex: 0,
+ id: '659.5|||250',
+ },
+ endPoint: {
+ x: 640.5,
+ y: 350,
+ anchorIndex: 1,
+ id: '640.5|||350',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '150-190',
+ source: '150',
+ target: '190',
+ type: 'polyline',
+ style: {
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ startPoint: {
+ x: 259.5,
+ y: 250,
+ anchorIndex: 0,
+ id: '259.5|||250',
+ },
+ endPoint: {
+ x: 159.5,
+ y: 338,
+ anchorIndex: 1,
+ id: '159.5|||338',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: 'ems-upf',
+ source: 'combo-ems',
+ target: 'combo-upf',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 167,
+ y: 445,
+ anchorIndex: 1,
+ id: '167|||445',
+ },
+ endPoint: {
+ x: 58,
+ y: 333,
+ anchorIndex: 0,
+ id: '58|||333',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: 'ems-ims',
+ source: 'combo-ems',
+ target: 'combo-ims',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 167,
+ y: 445,
+ anchorIndex: 1,
+ id: '167|||445',
+ },
+ endPoint: {
+ x: 539,
+ y: 345,
+ anchorIndex: 0,
+ id: '539|||345',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: '0~1703575912428~combo-upf',
+ source: '0',
+ target: 'combo-upf',
+ type: 'cubic-vertical',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#eeff00',
+ lineWidth: 13,
+ cursor: 'pointer',
+ startArrow: true,
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 415,
+ y: 457,
+ },
+ endPoint: {
+ x: 58,
+ y: 333,
+ anchorIndex: 0,
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: 'combo-upf~1703575945004~1',
+ source: 'combo-upf',
+ target: '1',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#4202f2',
+ lineWidth: 12,
+ cursor: 'pointer',
+ endArrow: true,
+ startArrow: true,
+ },
+ label: '3分3发',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'end',
+ autoRotate: true,
+ style: {
+ fill: '#fa9e00',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 180,
+ y: 333,
+ anchorIndex: 1,
+ id: '180|||333',
+ },
+ endPoint: {
+ x: 91.5,
+ y: 42,
+ anchorIndex: 0,
+ id: '91.5|||42',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: 'combo-ems~1703575966436~2',
+ source: 'combo-ems',
+ target: '2',
+ type: 'quadratic',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#20cb81',
+ lineWidth: 12,
+ cursor: 'pointer',
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 167,
+ y: 445,
+ anchorIndex: 1,
+ },
+ endPoint: {
+ x: 706.5,
+ y: 431,
+ anchorIndex: 0,
+ },
+ curvePosition: 0.5,
+ minCurveOffset: [0, 0],
+ curveOffset: -20,
+ },
+ {
+ id: 'combo-ems~1703575974507~combo-5gc',
+ source: 'combo-ems',
+ target: 'combo-5gc',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#cc1e1e',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 167,
+ y: 445,
+ anchorIndex: 1,
+ id: '167|||445',
+ },
+ endPoint: {
+ x: 239,
+ y: 145,
+ anchorIndex: 0,
+ id: '239|||145',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: '150~1703575985251~160',
+ source: '150',
+ target: '160',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#5eab4f',
+ lineWidth: 5,
+ cursor: 'pointer',
+ endArrow: true,
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 340.5,
+ y: 250,
+ anchorIndex: 1,
+ id: '340.5|||250',
+ },
+ endPoint: {
+ x: 659.5,
+ y: 250,
+ anchorIndex: 0,
+ id: '659.5|||250',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: '0-5gc',
+ source: '0',
+ target: 'combo-5gc',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#ffffff',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 421.20060794856374,
+ y: 457.78115483619814,
+ id: '421.20060794856374|||457.78115483619814',
+ },
+ endPoint: {
+ x: 239,
+ y: 145,
+ anchorIndex: 0,
+ id: '239|||145',
+ },
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 12,
+ fontWeight: 500,
+ },
+ },
+ },
+ {
+ id: 'combo-upf~1703576027131~combo-ims',
+ source: 'combo-upf',
+ target: 'combo-ims',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#7f5757',
+ lineWidth: 5,
+ cursor: 'pointer',
+ },
+ label: '3分3发',
+ labelCfg: {
+ refX: 2,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 180,
+ y: 333,
+ anchorIndex: 1,
+ id: '180|||333',
+ },
+ endPoint: {
+ x: 539,
+ y: 345,
+ anchorIndex: 0,
+ id: '539|||345',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: '130~1703576145468~120',
+ source: '130',
+ target: '120',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#492727',
+ lineWidth: 1,
+ cursor: 'pointer',
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 409.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '409.5|||50',
+ },
+ endPoint: {
+ x: 340.5,
+ y: 150,
+ anchorIndex: 1,
+ id: '340.5|||150',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: '130~1703576154675~180',
+ source: '130',
+ target: '180',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#9a0e0e',
+ lineWidth: 7,
+ cursor: 'pointer',
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 409.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '409.5|||50',
+ },
+ endPoint: {
+ x: 409.5,
+ y: 150,
+ anchorIndex: 0,
+ id: '409.5|||150',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: '140~1703576175859~150',
+ source: '140',
+ target: '150',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#a63636',
+ lineWidth: 3,
+ cursor: 'pointer',
+ },
+ label: '3分3发',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 559.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '559.5|||50',
+ },
+ endPoint: {
+ x: 340.5,
+ y: 250,
+ anchorIndex: 1,
+ id: '340.5|||250',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ {
+ id: '140~1703575996915~180',
+ source: '140',
+ target: '180',
+ type: 'polyline',
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(234, 234, 234)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(245, 245, 245)',
+ lineWidth: 1,
+ },
+ offset: 20,
+ radius: 2,
+ stroke: '#3de6e3',
+ lineWidth: 4,
+ cursor: 'pointer',
+ },
+ label: '',
+ labelCfg: {
+ refX: 0,
+ refY: 0,
+ position: 'middle',
+ autoRotate: false,
+ style: {
+ fill: '#ffffff',
+ fontSize: 14,
+ fontWeight: 400,
+ },
+ },
+ isComboEdge: true,
+ startPoint: {
+ x: 559.5,
+ y: 50,
+ anchorIndex: 0,
+ id: '559.5|||50',
+ },
+ endPoint: {
+ x: 490.5,
+ y: 150,
+ anchorIndex: 1,
+ id: '490.5|||150',
+ },
+ curvePosition: [0.5, 0.5],
+ minCurveOffset: [0, 0],
+ },
+ ],
+ combos: [
+ {
+ id: 'combo-5gc',
+ label: 'combo 5GC控制面',
+ children: [
+ {
+ id: '170',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ {
+ id: '130',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ {
+ id: '140',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ {
+ id: '120',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ {
+ id: '180',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ {
+ id: '150',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ {
+ id: '160',
+ comboId: 'combo-5gc',
+ itemType: 'node',
+ depth: 12,
+ },
+ ],
+ depth: 10,
+ type: 'rect',
+ size: [40, 40],
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ fill: 'rgb(247, 250, 255)',
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: '#4572d9',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(224, 224, 224)',
+ fill: 'rgb(253, 253, 253)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(234, 234, 234)',
+ fill: 'rgb(250, 250, 250)',
+ lineWidth: 1,
+ },
+ fillOpacity: 0.1,
+ r: 240.5,
+ width: 481,
+ height: 241,
+ },
+ x: 500,
+ y: 150,
+ },
+ {
+ id: 'combo-upf',
+ label: 'combo upf',
+ children: [
+ {
+ id: '190',
+ comboId: 'combo-upf',
+ itemType: 'node',
+ depth: 29,
+ },
+ ],
+ depth: 27,
+ type: 'rect',
+ size: [40, 40],
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ fill: 'rgb(247, 250, 255)',
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: '#4572d9',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(224, 224, 224)',
+ fill: 'rgb(253, 253, 253)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(234, 234, 234)',
+ fill: 'rgb(250, 250, 250)',
+ lineWidth: 1,
+ },
+ fillOpacity: 0.1,
+ r: 40.5,
+ width: 81,
+ height: 41,
+ },
+ x: 119,
+ y: 338,
+ },
+ {
+ id: 'combo-ims',
+ label: 'combo ims',
+ children: [
+ {
+ id: '110',
+ comboId: 'combo-ims',
+ itemType: 'node',
+ depth: 40,
+ },
+ ],
+ depth: 38,
+ type: 'rect',
+ size: [40, 40],
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ fill: 'rgb(247, 250, 255)',
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: '#4572d9',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(224, 224, 224)',
+ fill: 'rgb(253, 253, 253)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(234, 234, 234)',
+ fill: 'rgb(250, 250, 250)',
+ lineWidth: 1,
+ },
+ fillOpacity: 0.1,
+ r: 40.5,
+ width: 81,
+ height: 41,
+ },
+ x: 600,
+ y: 350,
+ },
+ {
+ id: 'combo-ems',
+ label: 'Combo ems',
+ children: [
+ {
+ id: '100',
+ comboId: 'combo-ems',
+ itemType: 'node',
+ depth: 51,
+ },
+ ],
+ depth: 49,
+ type: 'rect',
+ size: [40, 40],
+ style: {
+ active: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 1,
+ fill: 'rgb(247, 250, 255)',
+ },
+ selected: {
+ stroke: 'rgb(95, 149, 255)',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ shadowColor: 'rgb(95, 149, 255)',
+ shadowBlur: 10,
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ highlight: {
+ stroke: '#4572d9',
+ lineWidth: 2,
+ fill: 'rgb(253, 253, 253)',
+ 'text-shape': {
+ fontWeight: 500,
+ },
+ },
+ inactive: {
+ stroke: 'rgb(224, 224, 224)',
+ fill: 'rgb(253, 253, 253)',
+ lineWidth: 1,
+ },
+ disable: {
+ stroke: 'rgb(234, 234, 234)',
+ fill: 'rgb(250, 250, 250)',
+ lineWidth: 1,
+ },
+ fillOpacity: 0.1,
+ r: 40.5,
+ width: 81,
+ height: 41,
+ },
+ x: 106,
+ y: 450,
+ },
+ ],
+});
- // 图绑定事件
- graphEvent(graph);
-
- graphG6.value = graph;
-
- //
- fnSelectSourceTargetOptionsData();
-}
-
-/**查询网元状态 */
-async function fnGetState() {
- for (const node of graphG6Data.nodes) {
- const ne = node.info;
- if (ne.neType === 'OMC') continue;
- const result = await stateNe(ne.neType, ne.neId);
- if (result.code === RESULT_CODE_SUCCESS) {
- ne.serverState = result.data;
- ne.serverState.refreshTime = parseDateToStr(
- ne.serverState.refreshTime,
- 'HH:mm:ss'
- );
- const node = graphG6.value.findById(ne.neName);
- console.log('查询网元状态', node);
- graphG6.value.setItemState(node, 'neState', true);
- }
- }
-}
-
-/**查询全部网元数据列表 */
-function fnGetList(refresh: boolean = false) {
- listNe({
- bandStatus: false,
- })
- .then(res => {
- if (
- res.code === RESULT_CODE_SUCCESS &&
- Array.isArray(res.data) &&
- res.data.length > 0
- ) {
- let rootNode = 'OMC';
- const nodes = [];
- const edges = [];
- for (const item of res.data) {
- item.serverState = {};
- const nodeIndex = nodes.findIndex(v => v.id === item.neName);
- if (nodeIndex === -1) {
- // 根网管
- if (item.neType === 'OMC') {
- rootNode = item.neName;
- item.serverState = {
- neId: item.neId,
- neName: item.neName,
- neType: item.neType,
- expire: '2024-03-31',
- refreshTime: '10:31:47',
- sn: '13770707',
- version: '2.2312.8',
- };
- nodes.push({
- id: item.neName,
- label: item.neName,
- info: item,
- labelCfg: {
- position: 'bottom',
- offset: 8,
- style: {
- fill: '#fff',
- fontSize: 14,
- },
- },
- size: 20,
- icon: {
- x: -30,
- y: -30,
- // 可更换为其他图片地址
- img: '/svg/service_db.svg',
- width: 60,
- height: 60,
- },
- });
- } else {
- nodes.push({
- id: item.neName,
- label: item.neName,
- info: item,
- size: 20,
- icon: {
- x: -24,
- y: -24,
- img: '/svg/service.svg',
- width: 48,
- height: 48,
- },
- });
- }
- }
-
- if (item.neType !== 'OMC') {
- const edgeIndex = edges.findIndex(v => v.source === item.neName);
- if (edgeIndex === -1) {
- edges.push({
- source: item.neName,
- target: rootNode,
- label: `${item.neType}-${rootNode}`,
- });
- }
- }
- }
- graphG6Data.nodes = nodes;
- graphG6Data.edges = edges;
-
- return true;
- } else {
- message.warning({
- content: t('common.noData'),
- duration: 2,
- });
- return false;
- }
- })
- .then(hasNeList => {
- if (!hasNeList) return;
- if (refresh) {
- graphG6.value.destroy();
- }
- fnGetState();
- });
+// 将数据渲染到画布
+function fnRanderGraph(data: Record) {
+ // 获取DOM渲染数据
+ if (!graphG6Dom.value) return;
+ handleRanderGraph(graphG6Dom.value, data);
}
onMounted(() => {
- // 获取网元列表
- // fnGetList();
- fnRanderGraph();
+ fnRanderGraph(graphG6Data);
});
-/**改变图模式 */
-function fnChangeMode(value: any) {
- console.log(value, JSON.parse(JSON.stringify(graphG6.value.save())));
- graphG6.value.setMode(value);
-}
-
/**对话框对象信息状态类型 */
type ModalStateType = {
/**对话框是否显示 */
@@ -817,12 +2731,6 @@ type ModalStateType = {
title: string;
/**图元素表单类型 */
formType: 'edge' | 'node';
- /**图节点表单数据 */
- formNodeOrigin: Record;
- formNode: Record;
- /**图边表单数据 */
- formEdgeOrigin: Record;
- formEdge: Record;
/**确定按钮 loading */
confirmLoading: boolean;
};
@@ -832,432 +2740,29 @@ let modalState: ModalStateType = reactive({
visible: false,
title: '图信息',
formType: 'edge',
- formNodeOrigin: {},
- formNode: {
- id: '',
- x: 0,
- y: 0,
- type: 'circle',
- size: [0],
- anchorPoints: false,
- style: {
- fill: '#ffffff',
- stroke: '#ffffff',
- lineWidth: 1,
- },
- label: '',
- labelCfg: {
- position: 'center',
- offset: 0,
- style: {
- fill: '#000000',
- fontSize: 12,
- fontWeight: 500,
- },
- },
- },
- formEdgeOrigin: {},
- formEdge: {
- id: '',
- source: '',
- target: '',
- type: 'polyline',
- style: {
- offset: 20,
- radius: 2,
- stroke: '#ffffff',
- lineWidth: 1,
- cursor: 'pointer',
- },
- label: '',
- labelCfg: {
- refX: 0,
- refY: 0,
- position: 'middle',
- autoRotate: false,
- style: {
- fill: '#ffffff',
- fontSize: 12,
- fontWeight: 500,
- },
- },
- },
confirmLoading: false,
});
-/**图元素选择开始结束点 */
-let selectSourceTargetOptions = ref[]>([]);
-
-/**
- * 图元素选择开始结束点数据获取
- */
-function fnSelectSourceTargetOptionsData() {
- // 节点
- graphG6.value.getNodes().forEach((node: any) => {
- const info = JSON.parse(JSON.stringify(node.getModel()));
- selectSourceTargetOptions.value.push({
- value: info.id,
- label: info.label,
- info,
- });
- });
- // 框
- graphG6.value.getCombos().forEach((combo1: any) => {
- const info = JSON.parse(JSON.stringify(combo1.getModel()));
- selectSourceTargetOptions.value.push({
- value: info.id,
- label: info.label,
- info,
- });
- });
-}
-
-/**图边对话框内表单属性和校验规则 */
-const modalStateFormEdge = Form.useForm(
- modalState.formEdge,
- reactive({
- id: [{ required: true, message: '边唯一 ID' }],
- source: [{ required: true, message: '起始点 id' }],
- target: [{ required: true, message: '结束点 id' }],
- type: [{ required: true, message: 'line' }],
- })
-);
-
-/**图边内置边类型 */
-const edgeTypeOptions = [
- {
- value: 'line',
- label: '直线,连接两个节点的直线',
- },
- {
- value: 'polyline',
- label: '折线,多段线段构成的折线,连接两个端点',
- },
- {
- value: 'arc',
- label: '圆弧线,连接两个节点的一段圆弧',
- },
- {
- value: 'quadratic',
- label: '二阶贝塞尔曲线,只有一个控制点的曲线',
- },
- {
- value: 'cubic',
- label: '三阶贝塞尔曲线,有两个控制点的曲线',
- },
- {
- value: 'cubic-vertical',
- label: '垂直方向的三阶贝塞尔曲线',
- },
- {
- value: 'cubic-horizontal',
- label: '水平方向的三阶贝塞尔曲线',
- },
- {
- value: 'loop',
- label: '自环',
- },
-];
-
-/**图边标签文本位置 */
-const edgePositionOptions = [
- {
- value: 'start',
- label: '开头',
- },
- {
- value: 'middle',
- label: '中间',
- },
- {
- value: 'end',
- label: '末尾',
- },
-];
-
-/**图边编辑监听更新视图 */
-watch(modalState.formEdge, edge => {
- const info = JSON.parse(JSON.stringify(edge));
- const edgeId = info.id;
- if (edgeId) {
- graphG6.value.clearItemStates(edgeId, 'selected');
- graphG6.value.updateItem(edgeId, info);
- }
-});
-
-/**图边新增 */
-function fnModalOkEdge(edge: any) {
- console.log(JSON.parse(JSON.stringify(graphG6.value.save())));
- if (!edge.id) {
- message.warn({
- content: `边元素ID错误`,
- duration: 2,
- });
- return;
- }
- // graphG6.value.removeItem(edge.id);
- // edge.id = `${edge.source}~${Date.now()}~${edge.target}`;
- // graphG6.value.addItem('edge', edge);
- const item = graphG6.value.findById(edge.id);
- if (item) {
- graphG6.value.updateItem(item, edge);
- } else {
- edge.id = `${edge.source}~${Date.now()}~${edge.target}`;
- graphG6.value.addItem('edge', edge);
- }
- modalState.visible = false;
- modalStateFormEdge.resetFields();
- modalState.formEdgeOrigin = {};
-}
-
-/**图节点对话框内表单属性和校验规则 */
-const modalStateFormNode = Form.useForm(
- modalState.formNode,
- reactive({
- id: [{ required: true, message: '边唯一 ID' }],
- source: [{ required: true, message: '起始点 id' }],
- target: [{ required: true, message: '结束点 id' }],
- type: [{ required: true, message: 'line' }],
- })
-);
-
-/**图节点内置边类型 */
-const nodeTypeOptions = [
- {
- value: 'circle',
- label: '圆形',
- },
- {
- value: 'rect',
- label: '矩形',
- },
- {
- value: 'ellipse',
- label: '椭圆',
- },
- {
- value: 'diamond',
- label: '菱形',
- },
- {
- value: 'triangle',
- label: '三角形',
- },
- {
- value: 'star',
- label: '星形',
- },
- {
- value: 'image',
- label: '图片',
- },
- {
- value: 'donut',
- label: '面包圈',
- },
-];
-
-/**图节点标签文本位置 */
-const nodePositionOptions = [
- {
- value: 'top',
- label: '上',
- },
- {
- value: 'left',
- label: '左',
- },
- {
- value: 'right',
- label: '右',
- },
- {
- value: 'bottom',
- label: '下',
- },
- {
- value: 'center',
- label: '居中',
- },
-];
-
-/**图节点三角形方向 */
-const nodeDirectionOptions = [
- { value: 'up', label: '向上' },
- { value: 'down', label: '向下' },
- { value: 'left', label: '向左' },
- { value: 'right', label: '向右' },
-];
-
-/**图节点图片裁剪的形状 */
-const nodeImageClipCfgOptions = [
- { value: 'circle', label: '圆形' },
- { value: 'rect', label: '矩形' },
- { value: 'ellipse', label: '椭圆' },
-];
-
-/**图节点类型输入限制 */
-function fnNodeTypeChange(type: any) {
- console.log(type);
-
- // 设置图标属性
- if (['circle', 'ellipse', 'diamond', 'star', 'donut'].includes(type)) {
- const nodeOrigin = modalState.formNodeOrigin;
- if (nodeOrigin.icon) {
- modalState.formNode = Object.assign(modalState.formNode, {
- icon: nodeOrigin.icon,
- });
- } else {
- modalState.formNode = Object.assign(modalState.formNode, {
- icon: {
- show: false,
- img: '',
- width: 25,
- height: 25,
- },
- });
- }
- } else if (type === 'triangle') {
- // 三角
- const nodeOrigin = modalState.formNodeOrigin;
- if (nodeOrigin.icon) {
- modalState.formNode = Object.assign(modalState.formNode, {
- direction: nodeOrigin.direction || 'up', // triangle 三角形的方向
- icon: Object.assign({ offset: 20 }, nodeOrigin.icon),
- });
- } else {
- modalState.formNode = Object.assign(modalState.formNode, {
- direction: 'up', // triangle 三角形的方向
- icon: {
- show: false,
- img: '/svg/service.svg',
- width: 25,
- height: 25,
- offset: 20, // triangle 特有
- },
- });
- }
- }
- // 设置图片属性
- if (type === 'image') {
- const nodeOrigin = modalState.formNodeOrigin;
- if (nodeOrigin.img) {
- modalState.formNode = Object.assign(modalState.formNode, {
- img: nodeOrigin.img,
- clipCfg: nodeOrigin.clipCfg,
- });
- } else {
- modalState.formNode = Object.assign(modalState.formNode, {
- img: '/svg/service.svg',
- clipCfg: {
- show: false,
- width: 0,
- height: 0,
- type: 'circle',
- },
- });
- }
- Reflect.deleteProperty(modalState.formNode, 'style');
- } else {
- // 当切换非图片时补充style属性
- if (!Reflect.has(modalState.formNode, 'style')) {
- modalState.formNode = Object.assign(modalState.formNode, {
- style: {
- fill: '#ffffff',
- stroke: '#ffffff',
- lineWidth: 1,
- },
- });
- }
- }
-}
-
-/**图节点大小输入限制 */
-function fnNodeSizeChange(value: any) {
- // 处理格式
- let intArr: number[] = [];
- for (const v of value) {
- const intV = parseInt(v);
- if (!isNaN(intV)) {
- intArr.push(intV);
- }
- }
- // 节点类型限制size
- const nodeType = modalState.formNode.type;
- switch (nodeType) {
- case 'circle':
- case 'star':
- case 'donut':
- intArr = intArr.slice(0, 1);
- break;
- case 'rect':
- case 'ellipse':
- case 'diamond':
- case 'triangle':
- case 'image':
- intArr = intArr.slice(0, 2);
- break;
- }
- modalState.formNode.size = intArr;
-}
-
-/**图节点编辑监听更新视图 */
-watch(modalState.formNode, node => {
- const info = JSON.parse(JSON.stringify(node));
- const nodeId = info.id;
- console.log(info);
- if (nodeId) {
- if (info.type === 'image') {
- Reflect.deleteProperty(info, 'style');
- }
- graphG6.value.clearItemStates(nodeId, 'selected');
- graphG6.value.updateItem(nodeId, info);
- if (info.type === 'triangle' || info.type === 'image') {
- graphG6.value.read(graphG6.value.save());
- }
- }
-});
-
-/**图节点新增 */
-function fnModalOkNode(node: any) {
- console.log(JSON.parse(JSON.stringify(graphG6.value.save())));
- if (!node.id) {
- message.warn({
- content: `节点元素ID错误`,
- duration: 2,
- });
- return;
- }
- // graphG6.value.removeItem(node.id);
- // graphG6.value.addItem('node', node);
- const item = graphG6.value.findById(node.id);
- if (item) {
- graphG6.value.updateItem(item, node);
- } else {
- graphG6.value.addItem('node', node);
- }
- if (node.type === 'triangle' || node.type === 'image') {
- graphG6.value.read(graphG6.value.save());
- }
- modalState.visible = false;
- modalStateFormNode.resetFields();
- modalState.formNodeOrigin = {};
-}
-
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
+ modalState.confirmLoading = true;
const type = modalState.formType;
- console.log(type);
+ // 边编辑确认
if (type === 'edge') {
- const edge = JSON.parse(JSON.stringify(modalState.formEdge));
- fnModalOkEdge(edge);
+ const result = handleOkEdge();
+ console.log(type, result);
+ modalState.visible = !result;
+ modalState.confirmLoading = !result;
}
+ // 节点编辑确认
if (type === 'node') {
- const node = JSON.parse(JSON.stringify(modalState.formNode));
- fnModalOkNode(node);
+ const result = handleOkNode();
+ console.log(type, result);
+ modalState.visible = !result;
+ modalState.confirmLoading = !result;
}
}
@@ -1269,23 +2774,12 @@ function fnModalCancel() {
modalState.visible = false;
const type = modalState.formType;
// 边编辑还原
- const edgeOrigin = JSON.parse(JSON.stringify(modalState.formEdgeOrigin));
- if (type === 'edge' && edgeOrigin.id) {
- graphG6.value.updateItem(edgeOrigin.id, edgeOrigin);
- // graphG6.value.removeItem(edgeOrigin.id);
- // graphG6.value.addItem('edge', edgeOrigin);
- modalStateFormEdge.resetFields();
- modalState.formEdgeOrigin = {};
+ if (type === 'edge') {
+ handleCancelEdge();
}
// 节点编辑还原
- const nodeOrigin = JSON.parse(JSON.stringify(modalState.formNodeOrigin));
- if (type === 'node' && nodeOrigin.id) {
- graphG6.value.updateItem(nodeOrigin.id, nodeOrigin);
- if (nodeOrigin.type === 'triangle' || nodeOrigin.type === 'image') {
- graphG6.value.read(graphG6.value.save());
- }
- modalStateFormNode.resetFields();
- modalState.formNodeOrigin = {};
+ if (type === 'node') {
+ handleCancelNode();
}
}
@@ -1316,13 +2810,10 @@ function fnGraphLoad() {