Vue2 usa mariposa para dibujar resumen de experiencia de gráfico acíclico dirigido

Tabla de contenido

1. Cómo usar

1.1 Cómo configurar la configuración del lienzo 

1.2 Cómo inicializar el componente butterfly-vue

1.3 La estructura de datos del componente butterfly-vue

1.4 Cómo escribir componentes personalizados de nodo vue

2. Personaliza la posición del nodo

3. Cuando se vuelve a dibujar el dag, cómo restaurar el zoom del lienzo

4. Cuando se vuelve a dibujar el dag, cómo restaurar el desplazamiento del lienzo

5. Cómo controlar la visibilidad del nodo Nodo

6. Cómo implementar el reordenamiento


1. Cómo usar

La posición del nodo se puede calcular mediante el método canvas.layout, solo necesita definir el tipo de diseño y el espacio horizontal entre nodos y el espacio vertical entre filas.

1.1 Cómo configurar la configuración del lienzo 

canvasConfig: {
        disLinkable: false, // 可删除连线
        linkable: false, // 可连线
        draggable: true, // 可拖动
        zoomable: true, // 可放大
        moveable: true, // 可平移
        theme: {
          edge: {
            shapeType: 'AdvancedBezier',
            isExpandWidth: false,
          },
        },
        layout: {
          type: 'dagreLayout',
          options: {
            rankdir: 'TB',
            nodesep: 70,
            ranksep: 40,
            controlPoints: false,
          },
        },
 }

1.2 Cómo inicializar el componente butterfly-vue

Consulte el método de escritura de la plantilla en el archivo index.vue

<template>
  <div class="canvas-box">
    <ButterflyVue ref="butterflyVue" :canvasData="renderData" :canvasConf="canvasConfig" @onLoaded="handleLoaded" />
  </div>
</template>

1.3 La estructura de datos del componente butterfly-vue

Consulte el método de escritura de mockData



import Node from '../Node.vue'

const renderData = {
    "nodes": [
        {
            "id": "200606",
            "label": "节点 1",
            "iconType": "icon-code",
            "status": "success",
            "render": Node
        },
        {
            "id": "200599",
            "label": "节点 2",
            "iconType": "icon-code",
            "status": "failed",
            "render": Node
        },
        {
            "id": "200609",
            "label": "节点 3",
            "iconType": "icon-code",
            "status": "init",
            "render": Node
        }
    ],
    "edges": [
        {
            "id": "0",
            "source": "200609",
            "target": "200606",
            "arrow": true,
            "type": "node"
        },
        {
            "id": "1",
            "source": "200609",
            "target": "200599",
            "arrow": true,
            "type": "node"
        },
        {
            "id": "2",
            "source": "200599",
            "target": "200606",
            "arrow": true,
            "type": "node"
        }
    ]
}

export default {
    renderData
}

1.4 Cómo escribir componentes personalizados de nodo vue

Consulte el siguiente archivo node.vue 

<template>
  <div
    :class="['dagre-node', hovered && 'hover']"
    :id="itemData.id"
    @mouseenter="onFocus"
    @mouseleave="onBlur"
  >
      <div :class="`icon-box ${itemData.className}`">
        <a-icon v-if="itemData.status === 'running'" class="iconfont" type="loading" />
        <i v-else :class="`iconfont ${itemData.iconType}`"></i>
      </div>
    <span class="text-box" :title="itemData.label">{
   
   { itemData.label }}</span>
  </div>
</template>

<script>

export default {
  props: {
    itemData: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {
      hovered: false,
    }
  },
  mounted() {},
  methods: {
    onFocus() {
      this.hovered = true
    },
    onBlur() {
      this.hovered = false
    },
  },
}
</script>

<style lang="less" scoped>
.dagre-node {
  width: fit-content;

  .icon-box {
    width: 46px;
    height: 46px;
    display: flex;
    justify-content: center;
    align-items: center;
    line-height: 46px;
    box-sizing: content-box;
    margin: 0 auto;
    text-align: center;
    background: #438efd;
    border-radius: 50%;

    .iconfont {
      font-size: 28px;
      color: #fff;
    }
  }

  .text-box {
    height: 30px;
    width: 200px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    display: inline-block;
    padding: 0 10px;
    line-height: 30px;
    text-align: center;
    font-size: 14px;
  }
}

</style>

2. Personaliza la posición del nodo

Conocimiento de los negocios:

La empresa requiere que guardemos la posición del nodo en la memoria caché del navegador después de que el usuario arrastre y suelte la posición del nodo móvil y calcule automáticamente el nodo recién agregado.

Hay un problema:

Cuando se proporciona canvasData, los atributos izquierdo y superior de los nodos directamente definidos no son válidos. Después de representar el dag, mover el nodo a las posiciones izquierda y superior no tiene efecto.

Solución:

primer paso:

Cuando se mueva la posición del nodo, escuche el evento del lienzo y escriba la posición y la identificación de todos los nodos en el lienzo actual en el caché local del navegador. El código es el siguiente:

canvas.on('system.drag.end', (data) => {
    if(data.dragType === 'node:drag') {
        const graphData = _.cloneDeep(this.canvas.getDataMap().nodes.map((node) => {
            return {
              id: node.id,
              left: node.left,
              top: node.top
            }
        }))
        this.setLocalDagData(graphData)
    }
})

Segundo paso:

El código para mover la posición del nodo debe escribirse en $nextTick para garantizar que el gráfico dag se haya cargado y representado, y después del diseño con la posición del nodo calculada automáticamente, muévase hacia la izquierda y las posiciones superiores guardadas en el caché.

Por favor, consulte el siguiente código

watch: {
    data(newValue) {
    // 通过watch组件的props属性data的变化,实现重绘
    // 第一步,重新计算dag图的nodes和edges属性
      const _nodes = newValue.nodes.map((item) => {
        return {
          id: item.id.toString(),
          label: item.name,
          iconType: 'icon-code', // 或者 'icon-yinzi'
          factor_id: item.factor_id,
          schedule_id: item.schedule_id,
          status: item.status,
          render: item.render,
        }
      })
      const _edges = newValue.edges.map((item, index) => {
        return {
          id: index.toString(),
          source: item.source.toString(),
          target: item.dest.toString(),
          arrow: true,
          type: 'node',
        }
      })
      // 深拷贝nodes和edges,重新复制给renderData也就是butterfly-vue的数据canvasData属性
      // 只有这样,vue组件才能监听到canvasData属性的数据变化,引发重绘
      this.renderData = {
        nodes: _.cloneDeep(_nodes),
        edges: _.cloneDeep(_edges),
      }
      // 引用两次vue组件的redraw()方法,保证及时重绘更新
      this.$refs.butterflyVue.redraw()
      this.$nextTick(() => {
        this.$refs.butterflyVue.redraw()
        this.$nextTick(() => {
          // 在第二重绘redraw()完成之后,再移动节点到缓存里的位置
          this.canvas.nodes.forEach((node, index) => {
            const graphData = this.getLocalDagData()
            const target = graphData ? graphData.find((item) => { return item.id === node.id }) : null
            if(target) {
              this.canvas.nodes[index].moveTo(target.left, target.top)
            }
          })
        })
      })
    },
  },
methods: {
    getLocalDagData() {
      const dashboard_id = this.$route.params.id
      if(dashboard_id && localStorage.getItem(`dagData-${dashboard_id}`)) {
        return JSON.parse(localStorage.getItem(`dagData-${dashboard_id}`))
      }
      return null
    },
}

3. Cuando se vuelve a dibujar el dag, cómo restaurar el zoom del lienzo

Conocimiento de los negocios:

El diagrama dag muestra el registro de programación del estado de ejecución de la tarea, porque las actualizaciones de datos pueden ocurrir cada pocos segundos. Al volver a dibujar el gráfico, es necesario restaurar el zoom del usuario en el lienzo para optimizar la experiencia del usuario.

Idea de implementación:

Aquí debe usar dos métodos de mariposa-dag, consulte la documentación oficial

lienzo.getZoom ()

Función : Obtener el zoom del lienzo.

devolver

  • {float} - la escala del lienzo (0-1)
getZoom = () => {}

lienzo.zoom (escala)

Función : configurar manualmente el zoom del lienzo

parámetro

  • {float} scale - valor de escala entre 0-1
  • {function} callback - Devolución de llamada después de escalar
zoom = (scale) => {}

Guarde el valor de zoom del lienzo en el estado global this antes de volver a dibujar. Después de completar el redibujado, lea el valor de zoom del lienzo del estado global this y restaure el estado de zoom del lienzo. Puede consultar el siguiente código

this.zoom = this.canvas.getZoom()
this.$refs.butterflyVue.redraw()
this.$nextTick(() => {
    this.canvas.zoom(this.zoom)
})

4. Cuando se vuelve a dibujar el dag, cómo restaurar el desplazamiento del lienzo

 Conocimiento de los negocios:

El diagrama dag muestra el registro de programación del estado de ejecución de la tarea, porque las actualizaciones de datos pueden ocurrir cada pocos segundos. Al volver a dibujar el gráfico, es necesario restaurar el desplazamiento generado por la acción de arrastrar y soltar del usuario en el lienzo para optimizar la experiencia del usuario. Este problema también se puede extender, es decir, esperamos guardar el desplazamiento del lienzo, el zoom y la posición del nodo después de moverse en el caché del navegador, entonces necesitamos usar el punto de conocimiento del caché local del navegador, localStorage.

Idea de implementación:

La solución a este problema es muy similar al tercer punto. Debe usar dos métodos de mariposa-dag. Consulte la documentación oficial.

canvas.move (posición)

Función : configurar manualmente el desplazamiento del lienzo

parámetro

  • {[x, y]} array - coordenadas x,y
move = (postion) => {}

lienzo.getOffset ()

Función : obtenga el valor de compensación del movimiento general del lienzo

devolver

  • {[x, y]} - el valor de compensación del lienzo
getOffset = () => {}

5. Cómo controlar la visibilidad del nodo Nodo

Conocimiento de los negocios:

Cuando hay suficientes nodos en el gráfico, mostrarlos todos será redundante, la estructura del gráfico se volverá desordenada o los nodos se volverán demasiado pequeños para distinguirlos. Por lo tanto, se espera poder controlar la visualización/ocultación de algunos nodos según el estado del nodo.

Solución:

Después de obtener los nodos del nodo y la información del borde, filtre inmediatamente los nodos que cumplen las condiciones. Cabe señalar que los bordes también deben filtrar los bordes conectados a los nodos que cumplen las condiciones, de lo contrario, butterfly-vue informará un error de "el nodo de destino o el nodo de origen no existe " .

Si necesita controlar el estado de éxito del nodo, defina una variable global con este atributo hideSuccess, el usuario puede controlar su valor para cambiar entre verdadero/falso, después de obtener los nodos de origen de datos y los bordes del gráfico dag, filtre los nodos según hideSuccess y node.status, y verifique si el nodo de destino y el nodo de origen del borde existen en los nodos filtrados. Luego organícelo en datos y entrégueselo a butterfly-vue para renderizar

Código de muestra:

getSchedule(this.schedule_id).then((res) => {
    this.task_history = res
    const _nodes = this.task_history.nodes.filter((node) => {
        return !this.hideSuccess || node.status !== 'success'
    })
    const _edges = this.task_history.edges.filter((edge) => {
        const sourceNodeExist = !!_nodes.find((node) => { return node.id === edge.source })
        const destNodeExist = !!_nodes.find((node) => { return node.id === edge.dest })
        return sourceNodeExist && destNodeExist
    })
    this.dagData = {
        nodes: _nodes.map((node) => {
            node.render = Node
            node.factor_id = node.id
            node.schedule_id = this.task_history.id
            return node
        }),
        edges: _edges
    }
})

6. Cómo implementar el reordenamiento

Volver a diseñar significa eliminar los cambios del usuario en el zoom y el desplazamiento del lienzo, así como el movimiento de la posición del nodo. Escribí el siguiente código de manera intuitiva:

initLayout() {
    this.canvas.move([0,0])
    this.canvas.zoom(1)
    // 重新计算节点位置,布局layout
    this.redraw(this.data)
}

pregunta:

El código anterior no logra el efecto de escalar el lienzo a su estado inicial.

razón:

Después de leer el código fuente de canvas.zoom (escala) en butterfly-dag/src/canvas/baseCanvas.js, descubrí que este método también acepta una función de devolución de llamada como segundo parámetro, lo que significa que el método se ejecuta de forma asíncrona. Si llama al método getZoom() inmediatamente después de llamar al método de zoom, encontrará que el valor de zoom aún no ha vuelto al valor inicial dado de 1.

Solución:

Escriba el código para volver a calcular el diseño de la posición del nodo después de canvas.zoom() y canvas.move(), y envuélvalo en la función de devolución de llamada de $nextTick().

Código de muestra:

initLayout() {
    this.canvas.move([0,0])
    this.canvas.zoom(1)
    this.$nextTick(() => {
        // 待画布缩放和偏移量设置完,重新计算节点位置,布局layout
        this.redraw(this.data)
    })
}

 

Supongo que te gusta

Origin blog.csdn.net/valsedefleurs/article/details/130580071
Recomendado
Clasificación