vue2 antv x6 注册vue组件 人工dag图 vue组件传值监听
实现效果~,主要使用,注意 结构解析值 !!! ,当前节点没有node属性
如果: node.on('change:data', ({ node }) => {}) 错!!! ,为了少掉几个头发。。。。。。
打印看看~
this.curNode.on('change:data', (current) => {})
正确: node.on('change:data', ({ current }) => {})
官网 人工dag图
人工dag图,节点使用vue组件 ,在节点组件 change:data 事件,监听节点变化
父组件官网代码:
<template> <div id="container" style="width: 800px; border: 1px solid #ccc; height: 500px" > </div> </template> <script> import { Graph, Path, Cell } from '@antv/x6' import { Selection } from '@antv/x6-plugin-selection' import { register, getTeleport } from '@antv/x6-vue-shape' import dataJSON from './dag.json' import AlgoNode from './node1.vue' export default { components: { AlgoNode }, name: 'dagView', provide() { return { getNode: () => this.getNode } }, data() { return { grpah: null, curNodes: null, // 当前-节点信息包括对象 imglist: { logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ', success: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ', failed: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ', running: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*t8fURKfgSOgAAAAAAAAAAAAAARQnAQ' } } }, mounted() { this.init() }, methods: { init() { let _this = this const graph = new Graph({ container: document.getElementById('container'), grid: true, panning: { enabled: true, eventTypes: ['leftMouseDown', 'mouseWheel'] }, mousewheel: { enabled: true, modifiers: 'ctrl', factor: 1.1, maxScale: 1.5, minScale: 0.5 }, highlighting: { magnetAdsorbed: { name: 'stroke', args: { attrs: { fill: '#fff', stroke: '#31d0c6', strokeWidth: 4 } } } }, connecting: { snap: true, allowBlank: false, allowLoop: false, highlight: true, connector: 'algo-connector', connectionPoint: 'anchor', anchor: 'center', validateMagnet({ magnet }) { return magnet.getAttribute('port-group') !== 'top' }, createEdge() { return graph.createEdge({ shape: 'dag-edge', attrs: { line: { strokeDasharray: '5 5' } }, zIndex: -1 }) } } }) register({ shape: 'dag-node', width: 180, height: 36, // component: AlgoNode, component: { // 使用vue3的render渲染组件,并添加自定义事件 render(h) { return h(AlgoNode, { props: { curNodes: _this.curNodes, imgs: _this.imglist } }) } }, ports: { groups: { top: { position: 'top', attrs: { circle: { r: 4, magnet: true, stroke: '#C2C8D5', strokeWidth: 1, fill: '#fff' } } }, bottom: { position: 'bottom', attrs: { circle: { r: 4, magnet: true, stroke: '#C2C8D5', strokeWidth: 1, fill: '#fff' } } } } } }) Graph.registerEdge( 'dag-edge', { inherit: 'edge', attrs: { line: { stroke: '#C2C8D5', strokeWidth: 1, targetMarker: null } } }, true ) Graph.registerConnector( 'algo-connector', (s, e) => { const offset = 4 const deltaY = Math.abs(e.y - s.y) const control = Math.floor((deltaY / 3) * 2) const v1 = { x: s.x, y: s.y + offset + control } const v2 = { x: e.x, y: e.y - offset - control } return Path.normalize( `M ${s.x} ${s.y} L ${s.x} ${s.y + offset} C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset} L ${e.x} ${e.y} ` ) }, true ) const nodeStatusList = [ [ { id: '1', status: 'running' }, { id: '2', status: 'default' }, { id: '3', status: 'default' }, { id: '4', status: 'default' } ], [ { id: '1', status: 'success' }, { id: '2', status: 'running' }, { id: '3', status: 'default' }, { id: '4', status: 'default' } ], [ { id: '1', status: 'success' }, { id: '2', status: 'success' }, { id: '3', status: 'running' }, { id: '4', status: 'running' } ], [ { id: '1', status: 'success' }, { id: '2', status: 'success' }, { id: '3', status: 'success' }, { id: '4', status: 'failed' } ] ] graph.use( new Selection({ multiple: true, rubberEdge: true, rubberNode: true, modifiers: 'shift', rubberband: true }) ) graph.on('edge:connected', ({ edge }) => { edge.attr({ line: { strokeDasharray: '' } }) }) graph.on('node:change:data', ({ node }) => { const edges = graph.getIncomingEdges(node) const { status } = node.getData() edges?.forEach((edge) => { if (status === 'running') { edge.attr('line/strokeDasharray', 5) edge.attr( 'line/style/animation', 'running-line 30s infinite linear' ) } else { edge.attr('line/strokeDasharray', '') edge.attr('line/style/animation', '') } }) }) // 初始化节点 / 边 const init = (data) => { const cells = [] data.forEach((item) => { if (item.shape === 'dag-node') { cells.push(graph.createNode(item)) } else { cells.push(graph.createEdge(item)) } }) graph.resetCells(cells) } // TODO // 显示节点状态 let _time = null const showNodeStatus = async (statusList) => { const status = statusList.shift() if (!status) { clearTimeout(_time) return } // 当前要渲染---->节点信息 status?.forEach((item) => { const { id, status } = item // 根据状态id 获取当前node节点 const node = graph.getCellById(id) const data = node.getData() node.setData({ ...data, status }) console.log('node=====', node) // _this.curNodes = node.getData() }) _time = setTimeout(() => { showNodeStatus(statusList) }, 3000) } console.log('dataJSON', dataJSON) init(dataJSON) showNodeStatus(nodeStatusList) graph.centerContent() } } } </script> <style lang="scss"> .node { display: flex; align-items: center; width: 100%; height: 100%; background-color: #fff; border: 1px solid #c2c8d5; border-left: 4px solid #5f95ff; border-radius: 4px; box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06); } .node img { width: 20px; height: 20px; flex-shrink: 0; margin-left: 8px; } .node .label { display: inline-block; flex-shrink: 0; width: 104px; margin-left: 8px; color: #666; font-size: 12px; } .node .status { flex-shrink: 0; } .node.success { border-left: 4px solid #52c41a; } .node.failed { border-left: 4px solid #ff4d4f; } .node.running .status img { animation: spin 1s linear infinite; } .x6-node-selected .node { border-color: #1890ff; border-radius: 2px; box-shadow: 0 0 0 4px #d4e8fe; } .x6-node-selected .node.success { border-color: #52c41a; border-radius: 2px; box-shadow: 0 0 0 4px #ccecc0; } .x6-node-selected .node.failed { border-color: #ff4d4f; border-radius: 2px; box-shadow: 0 0 0 4px #fedcdc; } .x6-edge:hover path:nth-child(2) { stroke: #1890ff; stroke-width: 1px; } .x6-edge-selected path:nth-child(2) { stroke: #1890ff; stroke-width: 1.5px !important; } @keyframes running-line { to { stroke-dashoffset: -1000; } } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style>
子组件 node1.vue
<template> <div class="node" :class="[status]"> <img :src="imgs.logo" alt="logo" /> <span class="label">{{ nodeData?.label }}</span> <span class="status"> <img v-if="status === 'success'" :src="imgs.success" alt="success" /> <img v-if="status === 'failed'" :src="imgs.failed" alt="failed" /> <img v-if="status === 'running'" :src="imgs.running" alt="running" /> </span> </div> </template> <script> export default { name: 'nodeView', props: { imgs: { type: Object }, curNodes: { type: Object | Array } }, inject: ['getNode'], data() { return { status: '', curNode: null, nodeData: { label: '' } } }, mounted() { this.curNode = this.getNode() this.changeNode()
//注意~ current结构解析值 不确定情况下把返回值打印,不需要解构 this.curNode.on('change:data', ({ current }) => { // 监听节点数据变化~~~~~~~ this.changeNode() }) }, methods: { changeNode() { this.curNode = this.getNode() this.nodeData = this.curNode?.data ?? {} this.status = this.curNode.data.status } } } </script>