Vue Flow 交互式流程图和图形世界的桥梁

Vue Flow

Vue Flow 是通往交互式流程图和图形世界的桥梁,使您能够为流程图和图形表示带来动态性和交互性。无论是制作个人图表、生成动态编辑器,还是您想象出的任何其他事情,Vue Flow 都是您的创意伙伴。

Vue Flow 允许集成您自己的定制节点和边缘,从而可以毫不费力地定制和扩展基本功能。背景、小地图和控件等其他组件进一步丰富了界面,将您的作品转变为引人入胜的平台。

主要特点

  • 无缝设置
    Vue Flow 让你快速进入行动。凭借元素拖动、缩放和平移以及选择等内置功能,Vue Flow 开箱即用。

  • 定制
    Vue Flow 是你来塑造的。从自定义节点和边缘到连接线,您可以扩展 Vue Flow 的功能以满足您的创意需求。

  • 高效且响应迅速
    Vue Flow 会做出反应性地跟踪更改,确保只重新渲染必要的元素。

  • 实用程序和可组合性
    Vue Flow 专为复杂用途而设计,具有内置的图形助手和状态可组合函数

安装

vite 版本最低 3.0.9 node 版本 18.2.0

pnpm install @vue-flow/core@1.29.2
pnpm install @vue-flow/controls@1.1.0
pnpm install  @vue-flow/background@1.2.0

注意事项为了确保 Vue Flow 的显示正确,请确保包含必要的样式。

/* these are necessary styles for vue flow */
@import '@vue-flow/core/dist/style.css';

/* this contains the default theme, these are optional styles */
@import '@vue-flow/core/dist/theme-default.css';

Basic–官方基本使用

<script setup lang="ts">
  import { ref } from 'vue';
  import type { Node, Edge } from '@vue-flow/core';
  import { VueFlow } from '@vue-flow/core';

  // these components are only shown as examples of how to use a custom node or edge
  // you can find many examples of how to create these custom components in the examples page of the docs
  import SpecialNode from './components/SpecialNode.vue';
  import SpecialEdge from './components/SpecialEdge.vue';

  // these are our nodes
  const nodes = ref<Node[]>([
    // an input node, specified by using `type: 'input'`
    {
      id: '1',
      type: 'input',
      position: { x: 250, y: 5 },
      // all nodes can have a data object containing any data you want to pass to the node
      // a label can property can be used for default nodes
      data: { label: 'Node 1' },
    },

    // default node, you can omit `type: 'default'` as it's the fallback type
    {
      id: '2',
      position: { x: 100, y: 100 },
      data: { label: 'Node 2' },
    },

    // An output node, specified by using `type: 'output'`
    {
      id: '3',
      type: 'output',
      position: { x: 400, y: 200 },
      data: { label: 'Node 3' },
    },

    // this is a custom node
    // we set it by using a custom type name we choose, in this example `special`
    // the name can be freely chosen, there are no restrictions as long as it's a string
    {
      id: '4',
      type: 'special', // <-- this is the custom node type name
      position: { x: 400, y: 200 },
      data: {
        label: 'Node 4',
        hello: 'world',
      },
    },
  ]);

  // these are our edges
  const edges = ref<Edge[]>([
    // default bezier edge
    // consists of an edge id, source node id and target node id
    {
      id: 'e1->2',
      source: '1',
      target: '2',
    },

    // set `animated: true` to create an animated edge path
    {
      id: 'e2->3',
      source: '2',
      target: '3',
      animated: true,
    },

    // a custom edge, specified by using a custom type name
    // we choose `type: 'special'` for this example
    {
      id: 'e3->4',
      type: 'special',
      source: '3',
      target: '4',

      // all edges can have a data object containing any data you want to pass to the edge
      data: {
        hello: 'world',
      },
    },
  ]);
</script>

<template>
  <VueFlow :nodes="nodes" :edges="edges">
    <!-- bind your custom node type to a component by using slots, slot names are always `node-<type>` -->
    <template #node-special="specialNodeProps">
      <SpecialNode v-bind="specialNodeProps" />
    </template>

    <!-- bind your custom edge type to a component by using slots, slot names are always `edge-<type>` -->
    <template #edge-special="specialEdgeProps">
      <SpecialEdge v-bind="specialEdgeProps" />
    </template>
  </VueFlow>
</template>

<style>
  /* import the necessary styles for Vue Flow to work */
  @import '@vue-flow/core/dist/style.css';

  /* import the default theme, this is optional but generally recommended */
  @import '@vue-flow/core/dist/theme-default.css';

  @import '@vue-flow/controls/dist/style.css';
</style>

Custom–一些自定义用法

默认需要拖拽的内容,因为是动态数据,有数据的时候才让其进行操作 Main.vue


  <CustomVueFlow :nodeData="vueFlowData.nodes" :edgeData="vueFlowData.edges" />
  <div
    :style="`cursor:${!!isEmptyBankAccountData ? 'not-allowed' : 'grab'}`"
    class="basic-flow-node"
    :draggable="!isEmptyBankAccountData"
    @dragstart="handleOnDragStart($event, 'allot')"
  >
    <PlusOutlined
      class="basic-flow-add"
      :style="`cursor:${!!isEmptyBankAccountData ? 'not-allowed' : 'grab'}`"
    />
    <Descriptions
      title="银行账户信息"
      :style="`cursor:${!!isEmptyBankAccountData ? 'not-allowed' : 'grab'}`"
    >
      <DescriptionsItem label="公司名称" :span="3">{{ bankAccountData.orgName }}</DescriptionsItem>
    </Descriptions>
  </div>

  // 开始拖拽
  function handleOnDragStart(event: DragEvent, nodeType: any) {
    if (event.dataTransfer) {
      event.dataTransfer.setData(
        'application/vueflow',
        JSON.stringify({
          nodeType,
          nodeData: bankAccountData.value,
        }),
      );
      event.dataTransfer.effectAllowed = 'move';
    }
  }

内部使用 vue-flow 的组件 CustomVueFlow.vue


<script setup lang="ts">
  import { markRaw, nextTick, ref, watch } from 'vue';
  import { VueFlow, useVueFlow, Panel } from '@vue-flow/core';
  import { Background } from '@vue-flow/background';
  import { Controls } from '@vue-flow/controls';
  import type { Dimensions, Elements } from '@vue-flow/core';

  import CustomFlowNode from './CustomFlowNode.vue';
  import CustomFlowEdge from './CustomFlowEdge.vue';
  import CustomFlowLine from './CustomFlowLine.vue';

  // CustomFlow动态父节点数据
  const propsFlow = defineProps({
    nodeData: {
      type: Array,
      required: true,
    },
    edgeData: {
      type: Array,
      required: true,
    },
  });

  // 需要自定义固定内容时可以使用
  const elements = ref<Elements>();
  // 自定义节点的类型
  const nodeTypes = {
    allot: markRaw(AllotFlowNode),// 自定义节点
  };
  // vue-flow提供的hook函数
  const { findNode, nodes, edges, addNodes, addEdges, project, vueFlowRef, onConnect } =
    useVueFlow();

  // 两个节点连接时进行校验
  onConnect((params) => {
    (params.type = 'custom'), (params.animated = false);
    addEdges(params);
  });
  // 当拖拽至Background背景中时解析数据添加节点
  function handleOnDrop(event: DragEvent) {
    const nodeJsonObj = event.dataTransfer?.getData('application/vueflow');
    const { nodeType, nodeData } = JSON.parse(nodeJsonObj);
    const { left, top } = vueFlowRef.value!.getBoundingClientRect();
    const position = project({ x: event.clientX - left, y: event.clientY - top });
    const newNode = {
      id: ,
      type: nodeType,
      position,
      data: { ...nodeData, edgeShow: true }, // edgeShow默认展示
    };

    addNodes([newNode]);
  }
  // 拖拽结束
  function handleOnDragOver(event: DragEvent) {
    event.preventDefault();
    if (event.dataTransfer) {
      event.dataTransfer.dropEffect = 'move';
    }
  }
</script>

<template>
  <div class="relative h-full" id="main-canvas" @drop="handleOnDrop" @dragover="handleOnDragOver">
    <VueFlow :nodes="propsFlow.nodeData" :edges="propsFlow.edgeData" :node-types="nodeTypes">
      <Controls />
      <Background />
      <template #connection-line="{ sourceX, sourceY, targetX, targetY }">
        <CustomFlowLine
          :source-x="sourceX"
          :source-y="sourceY"
          :target-x="targetX"
          :target-y="targetY"
        />
      </template>
      <template #edge-custom="props">
        <CustomFlowEdge v-bind="props" />
      </template>
    </VueFlow>
  </div>
</template>
<style>
  @import '@vue-flow/core/dist/style.css';
  @import '@vue-flow/core/dist/theme-default.css';
  @import '@vue-flow/controls/dist/style.css';

  .vue-flow__handle {
    width: 18px;
    height: 18px;
    background-color: rgb(37, 99, 235);
  }
</style>

自定义 CustomFlowLine.vue

<script setup>
  defineProps({
    sourceX: {
      type: Number,
      required: true,
    },
    sourceY: {
      type: Number,
      required: true,
    },
    targetX: {
      type: Number,
      required: true,
    },
    targetY: {
      type: Number,
      required: true,
    },
  });
</script>

<template>
  <g>
    <path
      class="vue-flow__connection animated"
      fill="none"
      stroke="#6F3381"
      :stroke-width="2.5"
      :d="`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`"
    />

    <circle :cx="targetX" :cy="targetY" fill="#fff" :r="4" stroke="#6F3381" :stroke-width="1.5" />
  </g>
</template>

自定义 CustomFlowEdge.vue

<script lang="ts" setup>
import type { EdgeProps, Position } from '@vue-flow/core'
import { EdgeLabelRenderer, getBezierPath, useVueFlow } from '@vue-flow/core'
import type { CSSProperties } from 'vue'

interface CustomEdgeProps<T = any> extends EdgeProps<T> {
  id: string
  sourceX: number
  sourceY: number
  targetX: number
  targetY: number
  sourcePosition: Position
  targetPosition: Position
  data: T
  markerEnd: string
  style?: CSSProperties
}

const props = defineProps<CustomEdgeProps>()

const { removeEdges } = useVueFlow()

const path = computed(() => getBezierPath(props))
</script>

<script lang="ts">
export default {
  inheritAttrs: false,
}
</script>

<template>
  <path :id="id" :style="style" class="vue-flow__edge-path" :d="path[0]" :marker-end="markerEnd" />

  <EdgeLabelRenderer>
    <div
      :style="{
        pointerEvents: 'all',
        position: 'absolute',
        transform: `translate(-50%, -50%) translate(${path[1]}px,${path[2]}px)`,
      }"
      class="nodrag nopan"
    >
      <button class="edgebutton" @click="removeEdges(id)">×</button>
    </div>
  </EdgeLabelRenderer>
</template>

<style>
.edgebutton {
  border-radius: 999px;
  cursor: pointer;
}
.edgebutton:hover {
  box-shadow: 0 0 0 2px pink, 0 0 0 4px #f05f75;
}
</style>

使用体会

最主要的 **findNode, nodes, edges, addNodes, addEdges,**这几个 hook 函数的使用在自定义 edge 中还可以加入表单使用,数据只需要加入节点中的 data 使用就可以了因为 vue-flow 内部的数据就处理的可以了,进行数据交互就没有使用 vuex 或者 pinia,通过 props 进行数据交互就可以了。

要使用 pinia 也可以,插件有使用现有的存储,通过 pinia 来存储我们的元素,更新它们的位置并切换类 😊Pinia Store 用例 😀 vue-flow 官方文档

扩展

还有一些插件,像echart、d3、logicflow、relation-graph/vue3、g、g6、x6、reactflow都去看过,因为使用的技术栈是vue,需要自定义线节点,感觉vue-flow开发体验比较好一些,虽然文档是英文,有例子使用起来还是很方便。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值