当前位置: 首页 > news >正文

antv/g6 图谱实现,自定义节点,自定义边,边动画

具体实现效果:

图谱中存在4种节点,2中边

创建节点:

1.创建上下分割节点 

 G6.registerNode('split-rect', {draw(cfg, group) {// 主矩形配置const width = 135const height = 60const fill = cfg.chg > 0 ? '#FBEAEC' : cfg.chg < 0 ? '#DFF5EB' : '#F1F2FA'const stroke =cfg.chg > 0 ? '#F8DDE0' : cfg.chg < 0 ? '#B8DDCC' : '#D7DCEA'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 1,radius: [4, 4, 4, 4],padding: 12,},name: 'main-rect',})// 添加分割线(水平中线)group.addShape('line', {attrs: {x1: -width / 2 + 12,y1: 0,x2: width / 2 - 12,y2: 0,stroke: cfg.chg > 0 ? '#E6C5C6' : cfg.chg < 0 ? '#ACD5C3' : '#D3D9E8',lineWidth: 1,},name: 'divider-line',})// 添加上半部分小矩形group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width: width,height: 30,fill: '',stroke: '#666',lineWidth: 0,radius: [4, 4, 0, 0],},name: 'top-rect',})// 添加下半部分小矩形group.addShape('rect', {attrs: {x: -width / 2,y: 0, // 从分割线下方开始width: width,height: 30,fill: '',stroke: '',lineWidth: 0,radius: [0, 0, 4, 4],},name: 'bottom-rect',})if (cfg.label) {group.addShape('text', {attrs: {text: cfg.label,x: -width / 2 + 12,y: -8,textAlign: 'left',fill: '#333',fontSize: 12,fontWeight: 600,},name: 'text1',})// 添加SVG图像group.addShape('image', {attrs: {x: width / 2 - 24,y: -22,width: 14,height: 14,img:cfg.chg > 0? getSvg(1, '#D91212'): cfg.chg < 0? getSvg(-1, '#12A96E'): getSvg(0, '#999'),},name: 'img-1',})group.addShape('text', {attrs: {text: formatNumValue(cfg.dataValue),x: -width / 2 + 12,y: 22,textAlign: 'left',fill: cfg.chg > 0 ? '#D91212' : cfg.chg < 0 ? '#12A96E' : '#666',fontSize: 12,fontWeight: 600,},name: 'text3',})group.addShape('text', {attrs: {text: (cfg.chg > 0 ? '+' : cfg.chg < 0 ? '' : '') + cfg.chg + '%',x: width / 2 - 12,y: 22,textAlign: 'right',fill: cfg.chg > 0 ? '#D91212' : cfg.chg < 0 ? '#12A96E' : '#666',fontSize: 12,fontWeight: 600,},name: 'text4',})}return mainRect},// 响应状态变化setState(name, value, item) {// console.log('name', name, value, item)let cfg = item._cfg.modelif (name === 'highlight') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('stroke',value? cfg.chg > 0? '#D12323': cfg.chg < 0? '#12A96E': '#8E92A1': cfg.chg > 0? '#F8DDE0': cfg.chg < 0? '#B8DDCC': '#D7DCEA')main_rect.attr('lineWidth', value ? 2 : 1)const top_rect = group.find((ele) => ele.get('name') === 'top-rect')// 改变背景色top_rect.attr('fill',value? cfg.chg > 0? '#D12323': cfg.chg < 0? '#12A96E': '#8E92A1': '')const bottom_rect = group.find((ele) => ele.get('name') === 'bottom-rect')// 改变背景色bottom_rect.attr('fill',value? cfg.chg > 0? '#FFF6F7': cfg.chg < 0? '#E9FBF3': '#F8F9FE': '')const text1 = group.find((ele) => ele.get('name') === 'text1')text1.attr('fill', value ? '#FFF' : '#333')const img_1 = group.find((ele) => ele.get('name') === 'img-1')img_1.attr('img',value? cfg.chg > 0? getSvg(1, '#fff'): cfg.chg < 0? getSvg(-1, '#fff'): getSvg(0, '#fff'): cfg.chg > 0? getSvg(1, '#D91212'): cfg.chg < 0? getSvg(-1, '#12A96E'): getSvg(0, '#999'))}if (name === 'hover') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('stroke',value || item.getStates().includes('highlight')? cfg.chg > 0? '#D12323': cfg.chg < 0? '#12A96E': '#8E92A1': cfg.chg > 0? '#F8DDE0': cfg.chg < 0? '#B8DDCC': '#D7DCEA')}},getAnchorPoints() {return [[0.5, 0],[1, 0.5],[0.5, 1],[0, 0.5], // 四边中点]},})

2.创建品种文本节点

 

 G6.registerNode('breed-rect', {draw(cfg, group) {// 主矩形配置const width = cfg.label == '其他' ? 134 : 120const height = 40const fill = '#FFF'const stroke = '#C3CDE8'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 1,radius: [4, 4, 4, 4],padding: 0,shadowOffsetX: 0,shadowOffsetX: 2,shadowBlur: 6,spread: 2,shadowColor: 'rgba(149,157,174,0.1)',},name: 'main-rect',})if (cfg.label) {group.addShape('text', {attrs: {text: cfg.label,x: 0,y: 7,textAlign: 'center',fill: '#333',fontSize: 14,fontWeight: 600,},name: 'text1',})if (cfg.label == '废钢') {group.addShape('text', {attrs: {text: `螺废差:${cfg.dataValue}`,x: -width / 2,y: 46,textAlign: 'left',fill: '#333',fontSize: 12,},name: 'text2',})group.addShape('text', {attrs: {text: `(${cfg.desc1})`,x: -width / 2 + (4 * 12 + (cfg.dataValue.length + '') * 6) + 8,y: 46,textAlign: 'left',fill: '#D91212',fontSize: 12,},name: 'text2-desc1',})group.addShape('text', {attrs: {text: `${cfg.desc2}:`,x: -width / 2,y: 68,textAlign: 'left',fill: '#D91212',fontSize: 12,},name: 'text2-desc2',})group.addShape('text', {attrs: {text: `${cfg.desc3}`,x: -width / 2 + cfg.desc2.length * 12 + 8,y: 68,textAlign: 'left',fill: '#333',fontSize: 12,},name: 'text2-desc3',})}}return mainRect},setState(name, value, item) {if (name === 'hover') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('shadowColor',value ? 'rgba(149,157,174,0.2)' : 'rgba(149,157,174,0.1)')}},})

3.创建文本利率节点

 G6.registerNode('rate-rect', {draw(cfg, group) {// 主矩形配置const width = cfg.codes && cfg.codes.length > 2 ? 204 : 212const height = 12 + cfg.codes.length * 22const fill = '#FFF'const stroke = '#fff'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 0,radius: [4, 4, 4, 4],padding: 0,shadowOffsetX: 0,shadowOffsetX: 2,shadowBlur: 6,spread: 2,shadowColor: 'rgba(54,78,128,0.1)',},name: 'main-rect',})if (cfg.codes) {cfg.codes.forEach((item, index) => {group.addShape('text', {attrs: {text: `${item.label}: ${item.chg}%`,x: -width / 2 + 12,y: -height / 2 + 24 + 22 * index,textAlign: 'left',fill: '#333',fontSize: 12,},name: `text-${index + 1}`,})group.addShape('text', {attrs: {text: `(${item.desc})`,x:-width / 2 +12 +((item.label.length + 1) * 12 +((item.chg + '').length + 1) * 6) +12,y: -height / 2 + 24 + 22 * index,textAlign: 'left',fill: '#D91212',fontSize: 12,},name: `text-${index + 1}-desc`,})})}return mainRect},})

4.创建产量同比节点

G6.registerNode('ratio-rect', {draw(cfg, group) {// 主矩形配置const width = 147const height = 86const fill = '#FFF'const stroke = '#C3CDE8'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 1,radius: [4, 4, 4, 4],padding: 0,shadowOffsetX: 0,shadowOffsetX: 2,shadowBlur: 6,spread: 2,shadowColor: 'rgba(149,157,174,0.1)',},name: 'main-rect',})group.addShape('text', {attrs: {text: cfg.label,x: 0,y: -height / 2 + 24,textAlign: 'center',fill: '#333',fontSize: 14,fontWeight: 600,},name: 'text1',})group.addShape('line', {attrs: {x1: -width / 2 + 12,y1: -14,x2: width / 2 - 12,y2: -14,stroke: '#DDE3F2',lineWidth: 1,},name: 'divider-line',})group.addShape('text', {attrs: {text: `${cfg.dataValue}`,x: 16,y: -height / 2 + 55,textAlign: 'right',fill: '#333',fontSize: 16,fontWeight: 600,},name: 'text2',})group.addShape('text', {attrs: {text: `${cfg.unit}`,x: 20,y: -height / 2 + 53,textAlign: 'left',fill: '#666',fontSize: 12,},name: 'text2',})group.addShape('text', {attrs: {text: `累计同比${cfg.sumYoy > 0 ? '上涨' : cfg.sumYoy < 0 ? '下降' : '持平'}`,x: -width / 2 + 12,y: -height / 2 + 75,textAlign: 'left',fill: '#666',fontSize: 12,},name: 'text3',})group.addShape('text', {attrs: {text: `${Math.abs(cfg.sumYoy).toFixed(2)}%`,x: width / 2 - 14,y: -height / 2 + 76,textAlign: 'right',fill:cfg.sumYoy > 0 ? '#D91212' : cfg.sumYoy < 0 ? '#12A96E' : '#666666',fontSize: 14,fontWeight: 600,},name: 'text3-chg',})group.addShape('text', {attrs: {text: `${cfg.desc}`,x: 0,y: -height / 2 + 85 + 22,textAlign: 'center',fill: '#D91212',fontSize: 12,},name: 'text4',})return mainRect},setState(name, value, item) {if (name === 'hover') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('shadowColor',value ? 'rgba(149,157,174,0.2)' : 'rgba(149,157,174,0.1)')}},})

1.创建边,及在边上运动的圆点

G6.registerEdge('hvh', {draw(cfg, group) {const startPoint = cfg.startPointconst endPoint = cfg.endPointconst shape = group.addShape('path', {attrs: {stroke: '#8993AA',path: [['M', startPoint.x, startPoint.y],['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一处['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二处['L', endPoint.x, endPoint.y],],lineWidth: 1,zIndex: 10, // 确保边在顶部// startArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 10,// },// endArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 5,// },// endArrow: {//   path: G6.Arrow.triangle(10, 12, 25), // 三角形箭头//   fill: "#333", // 填充颜色//   stroke: "#333", // 边框颜色// },},})// 计算箭头方向const angle = Math.atan2(endPoint.y - startPoint.y,endPoint.x - startPoint.x)// 自定义箭头路径生成方法function getArrowPath(x, y, angle) {const length = 9 // 箭头长度const width = 4 // 箭头宽度// 计算箭头三个点的坐标const x1 = x - length * Math.cos(angle)const y1 = y - length * Math.sin(angle)const x2 = x1 - width * Math.cos(angle + Math.PI / 2)const y2 = y1 - width * Math.sin(angle + Math.PI / 2)const x3 = x1 - width * Math.cos(angle - Math.PI / 2)const y3 = y1 - width * Math.sin(angle - Math.PI / 2)return [['M', x, y], ['L', x2, y2], ['L', x3, y3], ['Z']]}//是否不需要显示箭头if (!cfg.disableArrow) {// 在终点绘制自定义箭头group.addShape('path', {attrs: {path: getArrowPath(endPoint.x, endPoint.y, 0),fill: '#8993AA',stroke: '#8993AA',lineWidth: 1,},})}// //在边上画循环点const circle = group.addShape('ellipse', {attrs: {x: cfg.startPoint.x,y: cfg.startPoint.y,fill: 'l(0) 0:rgba(102,212,255,0.7) 1:#1A56FF',rx: 4,ry: 1,shadowColor: '#1A56FF',shadowBlur: 4,shadowOffsetY: 1,},name: 'circle-shape',})const time =(Math.abs(cfg.endPoint.x - cfg.startPoint.x) +Math.abs(cfg.endPoint.y - cfg.startPoint.y)) /0.05circle.show()circle.animate((ratio) => {const tmpPoint = shape.getPoint(ratio)const pos = G6.Util.getLabelPosition(shape, ratio)let matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1]matrix = G6.Util.transform(matrix, [['t', -tmpPoint.x, -tmpPoint.y],['r', pos.angle],['t', tmpPoint.x, tmpPoint.y],])return {x: tmpPoint.x,y: tmpPoint.y,matrix,}},{repeat: true,duration: time,easing: 'easeLinear',delay: 0,})return shape},})

2.创建向上的边

G6.registerEdge('hvh2', {draw(cfg, group) {const startPoint = cfg.startPointconst endPoint = cfg.endPointconst shape = group.addShape('path', {attrs: {stroke: '#8993AA',path: [['M', startPoint.x, startPoint.y],['L', startPoint.x, (2 / 3) * startPoint.y + (1 / 3) * endPoint.y], // 三分之一处['L', endPoint.x, (2 / 3) * startPoint.y + (1 / 3) * endPoint.y], // 三分之二处['L', endPoint.x, endPoint.y],],lineWidth: 1.2,// startArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 10,// },// endArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 5,// },// endArrow: {//   path: G6.Arrow.triangle(10, 12, 25), // 三角形箭头//   fill: "#333", // 填充颜色//   stroke: "#333", // 边框颜色// },},})// 计算箭头方向const angle = Math.atan2(endPoint.y - startPoint.y,endPoint.x - startPoint.x)// 自定义箭头路径生成方法function getArrowPath(x, y, angle) {const length = 9 // 箭头长度const width = 4 // 箭头宽度// 计算箭头三个点的坐标const x1 = xconst y1 = yconst x2 = x1 - widthconst y2 = y1 + lengthconst x3 = x1 + widthconst y3 = y1 + lengthreturn [['M', x, y], ['L', x2, y2], ['L', x3, y3], ['Z']]}// 在终点绘制自定义箭头group.addShape('path', {attrs: {path: getArrowPath(endPoint.x, endPoint.y, 0),fill: '#8993AA',stroke: '#8993AA',lineWidth: 1,},})return shape},})

全部代码:

<template><div class="link-map"><div class="link-title">{{ breedName + props.elementName }}</div><gl-spin :spinning="mapLoading"><div id="mountNode" :style="{ height: mapHeight + 'px' }"></div></gl-spin></div>
</template>
<script setup>
import { onMounted, getCurrentInstance } from 'vue'
import { formatNumValue } from '@/utils/index'
import G6 from '@antv/g6/dist/g6.min'
const { proxy } = getCurrentInstance()
const route = useRoute()
const props = defineProps({secCode: {type: String,default: '',},elementName: {type: String,default: '',},elementCode: {type: String,default: '',},
})
const emit = defineEmits(['breed-select'])
const nodes = ref([{id: 'block1',x: 487,y: 117,type: 'block-rect',label: '长流程',},{id: 'block2',x: 670,y: 304,type: 'block-rect',label: '短流程',},{id: 'node1',x: 78,y: 42,label: '焦煤',breedCode: 'JM',indexCode: 'FU00008663',text: '焦煤',dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node2',x: 244,y: 42,label: '焦炭',breedCode: 'J',indexCode: 'FU00010732',text: '焦炭',dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node3',x: 244,y: 115,label: '铁矿石',breedCode: 'I',indexCode: 'FU00005559',text: '铁矿石',dataValue: '8302',chg: '-0.82',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node4',x: 466,y: 81,label: '高炉炼铁',dataValue: '8302',chg: '0.16',type: 'breed-rect',anchorPoints: [[0.5, 0], // 上[1, 0.5], // 右[0.5, 1], // 下[0, 0.5], // 左],},{id: 'node5',x: 394,y: 189,label: '硅铁',breedCode: 'SF',indexCode: 'FU00000314',text: '硅铁',dataValue: '8302',chg: '0',anchorPoints: [[0, 0],[0.5, 0],],},{id: 'node6',x: 540,y: 189,label: '硅锰',breedCode: 'SM',indexCode: 'FU00000202',text: '硅锰',dataValue: '8302',chg: '-0.16',anchorPoints: [[0, 0.5],[0.5, 0],],},{id: 'node7',x: 669,y: 81,dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],codes: [{label: '产能利用率',chg: '88.54',desc: '历史低位',indexCode: 'ID00183114',text: '高炉炼铁-产能利用率',},{label: '开工率',chg: '88.54',desc: '历史低位',indexCode: 'ID00183109',text: '高炉炼铁-开工率',},{label: '钢厂盈利率',chg: '88.54',desc: '历史低位',indexCode: 'ID00183126',text: '高炉炼铁-钢厂盈利率',},],type: 'rate-rect',},{id: 'node8',x: 886,y: 81,label: '生铁产量',indexCode: 'ID00184088',text: '生铁产量',dataValue: '6669.9',chg: '2.3',type: 'ratio-rect',desc: '高于近一年平均增速',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node9',x: 466,y: 284,label: '废钢',indexCode: 'OFFRDE0554363373',text: '废钢',dataValue: '8302',chg: '0.16',type: 'breed-rect',desc1: '相对高位',desc2: '利好',desc3: '独立电炉企业',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node10',x: 660,y: 284,label: '电炉炼钢',dataValue: '8302',chg: '0.16',type: 'breed-rect',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node11',x: 854,y: 284,label: '',codes: [{label: '产能利用率',chg: '88.54',desc: '历史低位',indexCode: 'ID01302473',text: '电炉炼钢-产能利用率',},{label: '电炉炼钢利润',chg: '88.54',desc: '历史低位',indexCode: 'ID01040556',text: '电炉炼钢-电炉炼钢利率',},],type: 'rate-rect',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node12',x: 1100,y: 188,label: '粗钢',indexCode: 'ID00182958',text: '粗钢',dataValue: '6669.9',chg: '3.3',type: 'ratio-rect',desc: '高于近一年平均增速',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node13',x: 1307,y: 42,label: '螺纹钢',breedCode: 'RB',indexCode: 'FU00001454',text: '螺纹钢',dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node14',x: 1307,y: 115,label: '线材',breedCode: 'WR',indexCode: 'FU00005821',noClick: true, //是否节点可点击text: '线材',dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node15',x: 1307,y: 188,label: '热轧板卷',breedCode: 'HC',indexCode: 'FU00002440',text: '热轧板卷',dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node16',x: 1307,y: 261,label: '不锈钢',breedCode: 'SS',indexCode: 'FU00000075',text: '不锈钢',dataValue: '8302',chg: '0.16',anchorPoints: [[0, 0.5],[1, 0.5],],},{id: 'node17',x: 1307,y: 324,label: '其他',dataValue: '8302',chg: '0.16',type: 'breed-rect',anchorPoints: [[0, 0.5],[1, 0.5],],},
])
const mapLoading = ref(false)
const graph2 = ref(null)
const mapHeight = ref(366)
const breedName = ref(window.breedName)
const getSvg = (type, iconColor) => {// 动态生成SVG内容const generateSVG1 = (iconColor) => {return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1"><title>Icon/涨</title><g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Q2_5_4期限分析_供给分析_最小宽度1180" transform="translate(-388.000000, -226.000000)"><g id="价格" transform="translate(164.000000, 102.000000)"><g id="编组-13" transform="translate(46.000000, 116.000000)"><g id="产业链/期货/涨备份-3" transform="translate(70.000000, 0.000000)"><g id="Icon/涨" transform="translate(108.000000, 8.000000)"><rect id="矩形" x="0" y="0" width="14" height="14"/><path d="M13.234199,3.55970481 C13.5098844,3.54382543 13.7462444,3.75443985 13.7621238,4.03012528 C13.7633238,4.05095907 13.7632183,4.07184754 13.761808,4.09266815 L13.5387743,7.38534552 C13.5201122,7.66085657 13.2816378,7.86907379 13.0061267,7.8504117 C12.8875582,7.84238031 12.775727,7.7923795 12.6906807,7.70937277 L11.522514,6.56902231 C11.5039313,6.59161598 11.4837258,6.61329794 11.4619021,6.63390921 L7.52440214,10.3526592 C7.19748321,10.661416 6.66840549,10.5862535 6.44041171,10.1986641 L4.67251566,7.193375 L1.15091069,10.1604378 C0.879839379,10.3887084 0.486186576,10.3759255 0.230649525,10.1444346 L0.164577825,10.075895 C-0.0844446196,9.78018087 -0.0465935113,9.33858461 0.249120641,9.08956216 L4.40537064,5.58956216 C4.73689384,5.31038473 5.23987001,5.39651159 5.45961961,5.77008591 L7.20126566,8.731625 L10.5006292,5.61609079 L10.525,5.59589343 L9.5175239,4.6123147 C9.3199065,4.41943657 9.31606456,4.10287739 9.5089427,3.90525999 C9.59608394,3.81597757 9.71345538,3.76249833 9.83800849,3.75532412 L13.234199,3.55970481 Z" id="形状结合" fill="${iconColor}" fill-rule="nonzero"/></g></g></g></g></g></g></svg>`}// 下跌const generateSVG2 = (iconColor) => {return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1"><title>Icon/跌</title><g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Q2_5_4期限分析_供给分析_最小宽度1180" transform="translate(-547.000000, -226.000000)"><g id="价格" transform="translate(164.000000, 102.000000)"><g id="编组-13" transform="translate(46.000000, 116.000000)"><g id="产业链/期货/跌备份-4" transform="translate(229.000000, 0.000000)"><g id="Icon/跌" transform="translate(108.000000, 8.000000)"><rect id="矩形" x="0" y="0" width="14" height="14"/><path d="M13.234199,3.50481138 C13.5098844,3.48893201 13.7462444,3.69954643 13.7621238,3.97523186 C13.7633238,3.99606564 13.7632183,4.01695412 13.761808,4.03777472 L13.5387743,7.3304521 C13.5201122,7.60596314 13.2816378,7.81418036 13.0061267,7.79551827 C12.8875582,7.78748688 12.775727,7.73748607 12.6906807,7.65447934 L11.522514,6.51412888 C11.5039313,6.53672256 11.4837258,6.55840451 11.4619021,6.57901578 L7.52440214,10.2977658 C7.19748321,10.6065225 6.66840549,10.5313601 6.44041171,10.1437707 L4.67251566,7.13848157 L1.15091069,10.1055444 C0.879839379,10.333815 0.486186576,10.3210321 0.230649525,10.0895412 L0.164577825,10.0210016 C-0.0844446196,9.72528744 -0.0465935113,9.28369118 0.249120641,9.03466874 L4.40537064,5.53466874 C4.73689384,5.25549131 5.23987001,5.34161816 5.45961961,5.71519249 L7.20126566,8.67673157 L10.5006292,5.56119737 L10.525,5.541 L9.5175239,4.55742128 C9.3199065,4.36454314 9.31606456,4.04798397 9.5089427,3.85036657 C9.59608394,3.76108415 9.71345538,3.70760491 9.83800849,3.70043069 L13.234199,3.50481138 Z" id="形状结合" fill="${iconColor}" fill-rule="nonzero" transform="translate(6.883798, 6.994464) scale(1, -1) translate(-6.883798, -6.994464) "/></g></g></g></g></g></g></svg>`}// 持平const generateSVG3 = (iconColor) => {return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1"><title>Icon/跌</title><g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Q2_5_4期限分析_供给分析_最小宽度1180" transform="translate(-706.000000, -226.000000)"><g id="价格" transform="translate(164.000000, 102.000000)"><g id="编组-13" transform="translate(46.000000, 116.000000)"><g id="产业链/期货/跌备份-3" transform="translate(388.000000, 0.000000)"><g id="Icon/跌" transform="translate(108.000000, 8.000000)"><rect id="矩形" x="0" y="0" width="14" height="14"/><path d="M2.8,6 L11.2,6 C11.6418278,6 12,6.3581722 12,6.8 C12,7.2418278 11.6418278,7.6 11.2,7.6 L2.8,7.6 C2.3581722,7.6 2,7.2418278 2,6.8 C2,6.3581722 2.3581722,6 2.8,6 Z" id="矩形" fill="${iconColor}"/></g></g></g></g></g></g></svg>`}return ('data:image/svg+xml;charset=utf-8,' +encodeURIComponent(type > 0? generateSVG1(iconColor): type === 0? generateSVG3(iconColor): generateSVG2(iconColor)))
}
const linkData = ref({link1: {indexCode: 'FU00008663',text: '焦煤',dataValue: '',dataChg: '',desc: '',},link2: {indexCode: 'FU00010732',text: '焦炭',dataValue: '',dataChg: '',desc: '',},link3: {indexCode: 'FU00005559',text: '铁矿石',dataValue: '',dataChg: '',desc: '',},link4: {indexCode: 'FU00000314',text: '硅铁',dataValue: '',dataChg: '',desc: '',},link5: {indexCode: 'FU00000202',text: '硅锰',dataValue: '',dataChg: '',desc: '',},link6: {indexCode: 'ID00183114',text: '高炉炼铁-产能利用率',dataValue: '',dataChg: '',desc: '',},link7: {indexCode: 'ID00183109',text: '高炉炼铁-开工率',dataValue: '',dataChg: '',desc: '',},link8: {indexCode: 'ID00183126',text: '高炉炼铁-钢厂盈利率',dataValue: '',dataChg: '',desc: '',},link9: {indexCode: 'ID00184088',text: '生铁产量',dataValue: '',dataChg: '',desc: '',},link10: {indexCode: 'OFFRDE0554363373',text: '废钢',dataValue: '',dataChg: '',desc: '',},link11: {indexCode: 'ID01302473',text: '电炉炼钢-产能利用率',dataValue: '',dataChg: '',desc: '',},link12: {indexCode: 'ID01040556',text: '电炉炼钢-电炉炼钢利率',dataValue: '',dataChg: '',desc: '',},link13: {indexCode: 'ID00182958',text: '粗钢',dataValue: '',dataChg: '',desc: '',},link14: {indexCode: 'FU00001454',text: '螺纹钢',dataValue: '',dataChg: '',desc: '',},link15: {indexCode: 'FU00005821',text: '线材',dataValue: '',dataChg: '',desc: '',},link16: {indexCode: 'FU00002440',text: '热轧板卷',dataValue: '',dataChg: '',desc: '',},link17: {indexCode: 'FU00000075',text: '不锈钢',dataValue: '',dataChg: '',desc: '',},
})
const initGrah = () => {//创建模块节点G6.registerNode('block-rect', {draw(cfg, group) {// 主矩形配置const width = cfg.label == '长流程' ? 972 : 604const height = cfg.label == '长流程' ? 232 : 122const fill = '#FAFCFF'const stroke = '#4376CE'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,stroke,fill,lineDash: [2, 2], // 激活状态用实线,非激活用虚线lineWidth: 1,radius: [4, 4, 4, 4],padding: 0,zIndex: -99,},name: 'main-rect',})if (cfg.label == '长流程') {group.addShape('rect', {attrs: {x: -width / 2 + 12,y: height / 2 - 10,width: 52,height: 20,fill: '#4580D9',stroke: '',lineWidth: 0,radius: [2, 2, 2, 2],zIndex: -99,},name: 'top-rect',})group.addShape('text', {attrs: {x: -width / 2 + 37,y: height / 2 + 7,text: '长流程',textAlign: 'center',fill: '#fff',fontSize: 12,zIndex: -99,},name: 'text1',})} else {group.addShape('rect', {attrs: {x: -width / 2 - 26,y: -10,width: 52,height: 20,fill: '#4580D9',stroke: '',lineWidth: 0,radius: [2, 2, 2, 2],zIndex: -99,},name: 'top-rect',})group.addShape('text', {attrs: {x: -width / 2,y: 8,text: '短流程',textAlign: 'center',fill: '#fff',fontSize: 12,zIndex: -99,},name: 'text1',})}return mainRect},})//创建上下分割节点G6.registerNode('split-rect', {draw(cfg, group) {// 主矩形配置const width = 135const height = 60const fill = cfg.chg > 0 ? '#FBEAEC' : cfg.chg < 0 ? '#DFF5EB' : '#F1F2FA'const stroke =cfg.chg > 0 ? '#F8DDE0' : cfg.chg < 0 ? '#B8DDCC' : '#D7DCEA'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 1,radius: [4, 4, 4, 4],padding: 12,},name: 'main-rect',})// 添加分割线(水平中线)group.addShape('line', {attrs: {x1: -width / 2 + 12,y1: 0,x2: width / 2 - 12,y2: 0,stroke: cfg.chg > 0 ? '#E6C5C6' : cfg.chg < 0 ? '#ACD5C3' : '#D3D9E8',lineWidth: 1,},name: 'divider-line',})// 添加上半部分小矩形group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width: width,height: 30,fill: '',stroke: '#666',lineWidth: 0,radius: [4, 4, 0, 0],},name: 'top-rect',})// 添加下半部分小矩形group.addShape('rect', {attrs: {x: -width / 2,y: 0, // 从分割线下方开始width: width,height: 30,fill: '',stroke: '',lineWidth: 0,radius: [0, 0, 4, 4],},name: 'bottom-rect',})if (cfg.label) {group.addShape('text', {attrs: {text: cfg.label,x: -width / 2 + 12,y: -8,textAlign: 'left',fill: '#333',fontSize: 12,fontWeight: 600,},name: 'text1',})// 添加SVG图像group.addShape('image', {attrs: {x: width / 2 - 24,y: -22,width: 14,height: 14,img:cfg.chg > 0? getSvg(1, '#D91212'): cfg.chg < 0? getSvg(-1, '#12A96E'): getSvg(0, '#999'),},name: 'img-1',})group.addShape('text', {attrs: {text: formatNumValue(cfg.dataValue),x: -width / 2 + 12,y: 22,textAlign: 'left',fill: cfg.chg > 0 ? '#D91212' : cfg.chg < 0 ? '#12A96E' : '#666',fontSize: 12,fontWeight: 600,},name: 'text3',})group.addShape('text', {attrs: {text: (cfg.chg > 0 ? '+' : cfg.chg < 0 ? '' : '') + cfg.chg + '%',x: width / 2 - 12,y: 22,textAlign: 'right',fill: cfg.chg > 0 ? '#D91212' : cfg.chg < 0 ? '#12A96E' : '#666',fontSize: 12,fontWeight: 600,},name: 'text4',})}return mainRect},// 响应状态变化setState(name, value, item) {// console.log('name', name, value, item)let cfg = item._cfg.modelif (name === 'highlight') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('stroke',value? cfg.chg > 0? '#D12323': cfg.chg < 0? '#12A96E': '#8E92A1': cfg.chg > 0? '#F8DDE0': cfg.chg < 0? '#B8DDCC': '#D7DCEA')main_rect.attr('lineWidth', value ? 2 : 1)const top_rect = group.find((ele) => ele.get('name') === 'top-rect')// 改变背景色top_rect.attr('fill',value? cfg.chg > 0? '#D12323': cfg.chg < 0? '#12A96E': '#8E92A1': '')const bottom_rect = group.find((ele) => ele.get('name') === 'bottom-rect')// 改变背景色bottom_rect.attr('fill',value? cfg.chg > 0? '#FFF6F7': cfg.chg < 0? '#E9FBF3': '#F8F9FE': '')const text1 = group.find((ele) => ele.get('name') === 'text1')text1.attr('fill', value ? '#FFF' : '#333')const img_1 = group.find((ele) => ele.get('name') === 'img-1')img_1.attr('img',value? cfg.chg > 0? getSvg(1, '#fff'): cfg.chg < 0? getSvg(-1, '#fff'): getSvg(0, '#fff'): cfg.chg > 0? getSvg(1, '#D91212'): cfg.chg < 0? getSvg(-1, '#12A96E'): getSvg(0, '#999'))}if (name === 'hover') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('stroke',value || item.getStates().includes('highlight')? cfg.chg > 0? '#D12323': cfg.chg < 0? '#12A96E': '#8E92A1': cfg.chg > 0? '#F8DDE0': cfg.chg < 0? '#B8DDCC': '#D7DCEA')}},getAnchorPoints() {return [[0.5, 0],[1, 0.5],[0.5, 1],[0, 0.5], // 四边中点]},})//创建品种文本节点G6.registerNode('breed-rect', {draw(cfg, group) {// 主矩形配置const width = cfg.label == '其他' ? 134 : 120const height = 40const fill = '#FFF'const stroke = '#C3CDE8'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 1,radius: [4, 4, 4, 4],padding: 0,shadowOffsetX: 0,shadowOffsetX: 2,shadowBlur: 6,spread: 2,shadowColor: 'rgba(149,157,174,0.1)',},name: 'main-rect',})if (cfg.label) {group.addShape('text', {attrs: {text: cfg.label,x: 0,y: 7,textAlign: 'center',fill: '#333',fontSize: 14,fontWeight: 600,},name: 'text1',})if (cfg.label == '废钢') {group.addShape('text', {attrs: {text: `螺废差:${cfg.dataValue}`,x: -width / 2,y: 46,textAlign: 'left',fill: '#333',fontSize: 12,},name: 'text2',})group.addShape('text', {attrs: {text: `(${cfg.desc1})`,x: -width / 2 + (4 * 12 + (cfg.dataValue.length + '') * 6) + 8,y: 46,textAlign: 'left',fill: '#D91212',fontSize: 12,},name: 'text2-desc1',})group.addShape('text', {attrs: {text: `${cfg.desc2}:`,x: -width / 2,y: 68,textAlign: 'left',fill: '#D91212',fontSize: 12,},name: 'text2-desc2',})group.addShape('text', {attrs: {text: `${cfg.desc3}`,x: -width / 2 + cfg.desc2.length * 12 + 8,y: 68,textAlign: 'left',fill: '#333',fontSize: 12,},name: 'text2-desc3',})}}return mainRect},setState(name, value, item) {if (name === 'hover') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('shadowColor',value ? 'rgba(149,157,174,0.2)' : 'rgba(149,157,174,0.1)')}},})//创建文本利率节点G6.registerNode('rate-rect', {draw(cfg, group) {// 主矩形配置const width = cfg.codes && cfg.codes.length > 2 ? 204 : 212const height = 12 + cfg.codes.length * 22const fill = '#FFF'const stroke = '#fff'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 0,radius: [4, 4, 4, 4],padding: 0,shadowOffsetX: 0,shadowOffsetX: 2,shadowBlur: 6,spread: 2,shadowColor: 'rgba(54,78,128,0.1)',},name: 'main-rect',})if (cfg.codes) {cfg.codes.forEach((item, index) => {group.addShape('text', {attrs: {text: `${item.label}: ${item.chg}%`,x: -width / 2 + 12,y: -height / 2 + 24 + 22 * index,textAlign: 'left',fill: '#333',fontSize: 12,},name: `text-${index + 1}`,})group.addShape('text', {attrs: {text: `(${item.desc})`,x:-width / 2 +12 +((item.label.length + 1) * 12 +((item.chg + '').length + 1) * 6) +12,y: -height / 2 + 24 + 22 * index,textAlign: 'left',fill: '#D91212',fontSize: 12,},name: `text-${index + 1}-desc`,})})}return mainRect},})//创建产量同比节点G6.registerNode('ratio-rect', {draw(cfg, group) {// 主矩形配置const width = 147const height = 86const fill = '#FFF'const stroke = '#C3CDE8'// 创建主矩形const mainRect = group.addShape('rect', {attrs: {x: -width / 2,y: -height / 2,width,height,fill,stroke,lineWidth: 1,radius: [4, 4, 4, 4],padding: 0,shadowOffsetX: 0,shadowOffsetX: 2,shadowBlur: 6,spread: 2,shadowColor: 'rgba(149,157,174,0.1)',},name: 'main-rect',})group.addShape('text', {attrs: {text: cfg.label,x: 0,y: -height / 2 + 24,textAlign: 'center',fill: '#333',fontSize: 14,fontWeight: 600,},name: 'text1',})group.addShape('line', {attrs: {x1: -width / 2 + 12,y1: -14,x2: width / 2 - 12,y2: -14,stroke: '#DDE3F2',lineWidth: 1,},name: 'divider-line',})group.addShape('text', {attrs: {text: `${cfg.dataValue}`,x: 16,y: -height / 2 + 55,textAlign: 'right',fill: '#333',fontSize: 16,fontWeight: 600,},name: 'text2',})group.addShape('text', {attrs: {text: `${cfg.unit}`,x: 20,y: -height / 2 + 53,textAlign: 'left',fill: '#666',fontSize: 12,},name: 'text2',})group.addShape('text', {attrs: {text: `累计同比${cfg.sumYoy > 0 ? '上涨' : cfg.sumYoy < 0 ? '下降' : '持平'}`,x: -width / 2 + 12,y: -height / 2 + 75,textAlign: 'left',fill: '#666',fontSize: 12,},name: 'text3',})group.addShape('text', {attrs: {text: `${Math.abs(cfg.sumYoy).toFixed(2)}%`,x: width / 2 - 14,y: -height / 2 + 76,textAlign: 'right',fill:cfg.sumYoy > 0 ? '#D91212' : cfg.sumYoy < 0 ? '#12A96E' : '#666666',fontSize: 14,fontWeight: 600,},name: 'text3-chg',})group.addShape('text', {attrs: {text: `${cfg.desc}`,x: 0,y: -height / 2 + 85 + 22,textAlign: 'center',fill: '#D91212',fontSize: 12,},name: 'text4',})return mainRect},setState(name, value, item) {if (name === 'hover') {const group = item.getContainer()const main_rect = group.find((ele) => ele.get('name') === 'main-rect')main_rect.attr('shadowColor',value ? 'rgba(149,157,174,0.2)' : 'rgba(149,157,174,0.1)')}},})G6.registerEdge('hvh', {draw(cfg, group) {const startPoint = cfg.startPointconst endPoint = cfg.endPointconst shape = group.addShape('path', {attrs: {stroke: '#8993AA',path: [['M', startPoint.x, startPoint.y],['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一处['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二处['L', endPoint.x, endPoint.y],],lineWidth: 1,zIndex: 10, // 确保边在顶部// startArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 10,// },// endArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 5,// },// endArrow: {//   path: G6.Arrow.triangle(10, 12, 25), // 三角形箭头//   fill: "#333", // 填充颜色//   stroke: "#333", // 边框颜色// },},})// 计算箭头方向const angle = Math.atan2(endPoint.y - startPoint.y,endPoint.x - startPoint.x)// 自定义箭头路径生成方法function getArrowPath(x, y, angle) {const length = 9 // 箭头长度const width = 4 // 箭头宽度// 计算箭头三个点的坐标const x1 = x - length * Math.cos(angle)const y1 = y - length * Math.sin(angle)const x2 = x1 - width * Math.cos(angle + Math.PI / 2)const y2 = y1 - width * Math.sin(angle + Math.PI / 2)const x3 = x1 - width * Math.cos(angle - Math.PI / 2)const y3 = y1 - width * Math.sin(angle - Math.PI / 2)return [['M', x, y], ['L', x2, y2], ['L', x3, y3], ['Z']]}//是否不需要显示箭头if (!cfg.disableArrow) {// 在终点绘制自定义箭头group.addShape('path', {attrs: {path: getArrowPath(endPoint.x, endPoint.y, 0),fill: '#8993AA',stroke: '#8993AA',lineWidth: 1,},})}// //在边上画循环点const circle = group.addShape('ellipse', {attrs: {x: cfg.startPoint.x,y: cfg.startPoint.y,fill: 'l(0) 0:rgba(102,212,255,0.7) 1:#1A56FF',rx: 4,ry: 1,shadowColor: '#1A56FF',shadowBlur: 4,shadowOffsetY: 1,},name: 'circle-shape',})const time =(Math.abs(cfg.endPoint.x - cfg.startPoint.x) +Math.abs(cfg.endPoint.y - cfg.startPoint.y)) /0.05circle.show()circle.animate((ratio) => {const tmpPoint = shape.getPoint(ratio)const pos = G6.Util.getLabelPosition(shape, ratio)let matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1]matrix = G6.Util.transform(matrix, [['t', -tmpPoint.x, -tmpPoint.y],['r', pos.angle],['t', tmpPoint.x, tmpPoint.y],])return {x: tmpPoint.x,y: tmpPoint.y,matrix,}},{repeat: true,duration: time,easing: 'easeLinear',delay: 0,})return shape},})G6.registerEdge('hvh2', {draw(cfg, group) {const startPoint = cfg.startPointconst endPoint = cfg.endPointconst shape = group.addShape('path', {attrs: {stroke: '#8993AA',path: [['M', startPoint.x, startPoint.y],['L', startPoint.x, (2 / 3) * startPoint.y + (1 / 3) * endPoint.y], // 三分之一处['L', endPoint.x, (2 / 3) * startPoint.y + (1 / 3) * endPoint.y], // 三分之二处['L', endPoint.x, endPoint.y],],lineWidth: 1.2,// startArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 10,// },// endArrow: {//   path: "M 10,0 L -10,-10 L -10,10 Z",//   d: 5,// },// endArrow: {//   path: G6.Arrow.triangle(10, 12, 25), // 三角形箭头//   fill: "#333", // 填充颜色//   stroke: "#333", // 边框颜色// },},})// 计算箭头方向const angle = Math.atan2(endPoint.y - startPoint.y,endPoint.x - startPoint.x)// 自定义箭头路径生成方法function getArrowPath(x, y, angle) {const length = 9 // 箭头长度const width = 4 // 箭头宽度// 计算箭头三个点的坐标const x1 = xconst y1 = yconst x2 = x1 - widthconst y2 = y1 + lengthconst x3 = x1 + widthconst y3 = y1 + lengthreturn [['M', x, y], ['L', x2, y2], ['L', x3, y3], ['Z']]}// 在终点绘制自定义箭头group.addShape('path', {attrs: {path: getArrowPath(endPoint.x, endPoint.y, 0),fill: '#8993AA',stroke: '#8993AA',lineWidth: 1,},})return shape},})const graph = new G6.Graph({container: 'mountNode',width: 1380,height: 366,// v3.5.1 后支持。开启后,图将会被平移,图的中心将对齐到画布中心,但不缩放。优先级低于 fitView。//fitCenter: true,// 最小缩放比例minZoom: 0.4,// 最大缩放比例maxZoom: 1,// 开启高DPI支持// rendererCfg: {//   enableHiDPIScreen: true, // 适配高清屏//   devicePixelRatio: window.devicePixelRatio || 1, // 使用设备像素比// },// 添加以下配置解决模糊问题//pixelRatio: window.devicePixelRatio || 1, // 关键配置modes: {default: ['click-select',{ type: 'drag-canvas', enableOptimize: true },{type: 'zoom-canvas',optimizeZoom: true, // 开启优化// 完全覆盖默认行为shouldUpdate: () => false, // 阻止默认行为onWheel: (e) => {e.preventDefault()const currentZoom = graph.getZoom()// console.log('当前缩放倍数:', currentZoom, e.wheelDelta)// let ratio =//   e.wheelDelta > 0//     ? currentZoom + 0.1 > 1//       ? 1//       : currentZoom + 0.1//     : currentZoom - 0.1 < 0.4//     ? 0.4//     : currentZoom - 0.1let ratio = e.wheelDelta > 0 ? currentZoom + 0.1 : currentZoom - 0.1graph.refresh()graph.zoomTo(ratio, {x: graph.getWidth() / 2,y: graph.getHeight() / 2,})if (e.wheelDelta > 0 && currentZoom + 0.1 >= 1) {graph.moveTo(0, 0)}// if (//   e.wheelDelta < 0 &&//   currentZoom - 0.1 <= 0.4 &&//   document.getElementById('mountNode').clientWidth <= 830// ) {//   graph.moveTo(0, 0)// }},},],},defaultNode: {type: 'split-rect', // 使用自定义节点类型size: [120, 80], // 自定义尺寸style: {fill: '#FBEAEC', // 主矩形填充色stroke: '#F8DDE0', // 主矩形边框色},label: '', // 可选文本},defaultEdge: {type: 'hvh',//type: "custom-arrow-edge",zIndex: 2, // 边的层级高于节点// labelCfg: {//   style: {//     opacity: 0,//   },// },},})graph.data({edges: [{ source: 'node1', target: 'node2' },{ source: 'node2', target: 'node4' },{ source: 'node3', target: 'node4' },{ source: 'node5', target: 'node4', type: 'hvh2' },{ source: 'node6', target: 'node4', type: 'hvh2' },{ source: 'node4', target: 'node7', disableArrow: true },{ source: 'node7', target: 'node8' },{ source: 'node9', target: 'node10' },{ source: 'node10', target: 'node11', disableArrow: true },{ source: 'node8', target: 'node12' },{ source: 'node11', target: 'node12' },{ source: 'node12', target: 'node13' },{ source: 'node12', target: 'node14' },{ source: 'node12', target: 'node15' },{ source: 'node12', target: 'node16' },{ source: 'node12', target: 'node17' },],nodes: nodes.value,})graph.render()nextTick(() => {//console.log(graph.get('edgeGroup'), 11111)const edgeGroup = graph.get('edgeGroup')edgeGroup.toFront() // 将边图形分组前置graph.paint() // 必须调用以重绘})// 5. 添加点击事件处理graph.on('node:click', (evt) => {// console.log('evt', evt)const { item } = evtconst model = item.getModel()// console.log('type', item.getType())// 如果点击的是父节点// item.setState("highlight", true);if (item._cfg.currentShape === 'split-rect') {if (!item._cfg.model.noClick) {// 1. 重置所有子节点状态graph.getNodes().forEach((node) => {// console.log('node', node.getStates())if (node.getStates().includes('highlight')) {node.clearStates('highlight')}})//排除不可点的item.setState('highlight', true)emit('breed-select', item._cfg.model.breedCode)}// 2. 找到所有子节点并设置状态// const childIds = graph.getNeighbors(model.id, 'source');// childIds.forEach(childItem => {//   childItem.setState('highlight', true);// });}})// graph.on  在回调函数中使定义的交互状态 hover 生效graph.on('node:mouseenter', (ev) => {//console.log("node:mouseenter", ev);// 关闭自动重绘graph.setAutoPaint(false)// 状态变化操作// 手动触发绘制graph.paint()// 开启自动重绘const { item } = evgraph.setAutoPaint(true)if (item._cfg.currentShape === 'split-rect' ||item._cfg.currentShape === 'breed-rect' ||item._cfg.currentShape === 'ratio-rect') {graph.setItemState(item, 'hover', true)}})graph.on('node:mouseleave', (ev) => {//console.log("node:mouseleave", ev);const { item } = evif (item._cfg.currentShape === 'split-rect' ||item._cfg.currentShape === 'breed-rect' ||item._cfg.currentShape === 'ratio-rect') {graph.setItemState(item, 'hover', false)}})// 在缩放后强制刷新graph.on('viewportchange', () => {graph.refresh()graph.paint()})//默认选中品种graph.getNodes().forEach((node) => {// console.log('node', node._cfg.model.label)if (node._cfg.model.breedCode == window.breedCode) {node.setState('highlight', true)}})graph2.value = graphnextTick(() => {mapResize()})
}
const mapResize = () => {//console.log('echartResize')//graph.changeSize(1384, 366)let parentWidth = document.querySelector('.supply-analysis').clientWidthlet width = parentWidthlet autoHeight = (366 * width) / 1376mapHeight.value = autoHeightconsole.log('缩小比例', width / 1426)let ratio = document.getElementById('mountNode').clientWidth / 1376console.log('ratio', ratio)graph2.value &&graph2.value.zoomTo(ratio, {x: graph2.value.getWidth() / 2,y: graph2.value.getHeight() / 2,})graph2.value && graph2.value.moveTo(0, 0)
}
onMounted(() => {initGrah()//通用模块监听事件proxy.$bus.on('echartResize', mapResize)
})
onBeforeUnmount(() => {proxy.$bus.off('echartResize', mapResize)
})
</script>
<style lang="less" scoped>
.link-map {padding: 12px;.link-title {font-weight: 600;font-size: 16px;color: #333333;line-height: 24px;text-align: center;margin-bottom: 12px;}
}
#mountNode {width: 100%;height: 366px;// border: 1px solid #efefef;position: relative;overflow: hidden;
}
</style>

相关文章:

  • 内网穿透原理解析、使用网络场景、及如何实现公网访问步骤教程
  • Android ViewPager使用预加载机制导致出现页面穿透问题
  • 第二章 DQL查询语句
  • Vue 2 和 Vue 3 中的 `nextTick` 原理
  • openssh离线一键升级脚本分享(含安装包)
  • Kubernetes 节点摘除指南
  • 人形机器人中的实时操作系统应用研究
  • 【T2I】MIGC++: Advanced Multi-Instance GenerationController for Image Synthesis
  • AgentOps - 帮助开发者构建、评估和监控 AI Agent
  • AI Agents系列之AI代理的类型
  • 【STM32单片机】#9 DMA直接存储器存取
  • SAP ECCS标准报表在报表中不存在特征CG细分期间 消息号 GK715报错分析
  • 苍穹外卖菜品图片保存本地代码修改
  • 【多线程-第四天-NSCache Objective-C语言】
  • Jetpack Compose 实现主页面与局部页面独立刷新的最佳实践
  • 四六级听力调频广播有线传输无线覆盖系统:弥补单一发射系统安全缺陷,构建稳定可靠听力系统平台
  • Hadoop集群部署教程-P3
  • udhcpc和udhcpd的区别
  • vue3+element-plus实现省市区三级地址多选
  • 【技术派后端篇】ElasticSearch 实战指南:环境搭建、API 操作与集成实践
  • 网站备案规定/站长工具推荐
  • 苏州相城区做网站公司/精准营销系统价值
  • 咸阳兼职做网站/东营优化路网
  • 国内哪个网站做水产比较大/南宁优化网站网络服务
  • 大型门户网站建设是什么/培训机构好还是学校好
  • 色情网站是怎么建设的/学网络运营在哪里学比较好