Code前端首页关于Code前端联系我们

Vue3项目怎么实现专业流程图?从0到1选工具、搭组件、做交互全指南

terry 1小时前 阅读数 5 #Vue

最近公司要上线一个供应链节点追踪的后台,产品经理明确指定用Vue3做,还要加拖拽式的流程图功能——能拖拽节点、连边、改属性、导出不同格式,看起来要求不低,一开始我也犯懵,查了不少资料踩了几个小坑才做出来,刚好整理成问答,给同样需要Vue3做流程图的朋友参考。

为什么Vue3比Vue2更适合做流程图?

很多人可能没太在意框架版本对这类可视化组件的影响,其实Vue3的几个核心特性刚好踩中了流程图的需求点:

响应式系统的优化更适配高频交互

流程图最常见的就是拖拽、缩放、连边时的实时反馈,Vue3用Proxy重写了响应式系统,比Vue2的Object.defineProperty快得多——尤其是节点和连线数据量大的时候(比如几百个节点的复杂架构图),Vue2的深层监听经常会导致页面卡顿,Vue3可以直接监听整个画布的对象或数组,不需要递归遍历,性能提升肉眼可见。

Composition API更灵活组织复杂逻辑

做流程图往往要写一堆逻辑:节点注册、事件监听、状态管理、导出功能……如果用Vue2的Options API,同一个功能的代码会被拆到data、methods、computed里,后期维护特别头疼,Vue3的Composition API可以用ref、reactive、computed这些组合式函数,把“画布缩放”“节点拖拽”“连边规则”这些功能封装成独立的hooks,比如useCanvasZoom、useNodeDrag,代码复用性和可读性都提升了不止一个档次。

Teleport组件解决了弹窗定位问题

专业的流程图一般会有节点属性配置弹窗,弹窗要么要浮在画布上不被遮挡,要么要挂载到body或者某个全局容器上,避免受父组件的overflow、z-index限制,Vue2实现这个要写额外的DOM操作代码,或者用第三方插件,Vue3自带的Teleport组件直接就能解决,只需要给弹窗加个to="body"属性就行,简单又安全。

Vue3做流程图应该选原生开发还是第三方组件库?

这是个非常关键的选择,直接影响开发周期和最终效果,我整理了两种方式的优劣势,大家可以根据自己的项目情况选:

原生开发的优劣势

原生开发就是自己用Canvas或者SVG写,不依赖第三方组件库。

  • 优势:完全自由,想做什么功能就能做什么功能,比如可以定制独特的节点样式、特殊的连边算法、甚至是自定义的导出格式;没有额外的依赖包,项目体积会更小;性能完全可控,可以根据自己的需求优化渲染逻辑。
  • 劣势:开发周期长,至少要花2-3周(如果是新手可能更长)才能做出一个基础的拖拽式流程图;技术门槛高,需要精通Canvas/SVG的API,还要懂一些计算机图形学的知识(比如碰撞检测、贝塞尔曲线绘制);后期维护成本高,每加一个功能都要自己写代码,没有现成的文档和社区支持。
  • 适用场景:项目对流程图的功能、样式有非常特殊的要求,第三方组件库完全满足不了;团队有技术储备,有人精通Canvas/SVG和计算机图形学;项目周期充足。

第三方组件库的优劣势

现在市面上有很多成熟的Vue3流程图组件库,不用自己写底层逻辑,直接调用API就能实现大部分功能。

  • 优势:开发周期短,基础功能(拖拽、缩放、连边、撤销重做、导出)1-2天就能搞定;技术门槛低,不需要精通Canvas/SVG的底层API,只需要看文档写配置;有现成的文档和社区支持,遇到问题可以查文档或者在GitHub/Stack Overflow上提问;后期维护成本低,组件库会持续更新bug和功能。
  • 劣势:样式和功能有一定的限制,虽然可以自定义,但自由度不如原生开发;会增加项目的依赖包体积,一般成熟的流程图组件库体积在100KB-500KB之间;性能可能不如原生开发优化得好,但对于大部分项目(比如几百个节点以内的流程图)来说完全够用。
  • 适用场景:项目对流程图的功能、样式没有非常特殊的要求;团队技术储备一般,没有精通Canvas/SVG的人;项目周期紧张。

市面上有哪些好用的Vue3流程图组件库?该怎么选?

我查了GitHub、npm和一些技术博客,选了4个比较火的Vue3流程图组件库,给大家做个对比:

LogicFlow

  • 特点:蚂蚁金服开源的流程图组件库,主打“扩展性强”,核心代码用TypeScript写,对Vue3的支持非常好(有官方的@logicflow/vue3-adapter适配器);节点和连线都可以完全自定义,甚至可以用Vue组件当节点;内置了很多实用的功能,比如网格对齐、分组、节点/连线的右键菜单、撤销重做、导出SVG/PNG/JPEG/JSON;社区活跃,文档详细,有很多示例代码。
  • 适用场景:企业级后台项目,比如业务流程图、架构图、ER图;需要用Vue组件自定义节点的项目;扩展性要求比较高的项目。

AntV X6

  • 特点:阿里巴巴达摩院开源的图编辑引擎,不是专门的流程图组件库,但可以轻松实现流程图功能;同样用TypeScript写,对Vue3有官方适配器;核心功能比LogicFlow更强大,比如支持节点缩放、旋转、多画布、协同编辑(需要配合AntV XFlow);扩展性也很强,可以用React/Vue/Angular组件当节点;社区更活跃,用户量更大,文档更详细,示例代码更多。
  • 适用场景:除了流程图,还需要做思维导图、拓扑图、BPMN图、UML图的项目;需要协同编辑功能的项目;对核心功能要求更高的项目。

Vue Flow

  • 特点:专门为Vue3打造的轻量级流程图组件库,代码体积小(核心包只有几十KB);上手非常快,只需要几行代码就能实现一个基础的拖拽式流程图;节点和连线也可以自定义,但扩展性不如LogicFlow和X6;内置了网格对齐、缩放、撤销重做、导出JSON等基础功能;社区活跃,文档比较详细,有一些示例代码。
  • 适用场景:个人项目或者小型企业项目,只需要基础流程图功能的项目;对项目体积要求比较高的项目。

GoJS

  • 特点:老牌的商业图编辑组件库,不是开源的,需要付费使用;功能极其强大,几乎可以做任何类型的图;对Vue3有官方适配器;性能非常好,即使是几千个节点的复杂图也不会卡顿;文档极其详细,有几千个示例代码;有专业的技术支持。
  • 适用场景:大型企业项目,对功能和性能要求极高的项目;预算充足的项目。

我最后选的是LogicFlow,因为公司项目只需要做业务流程图,不需要做其他类型的图,扩展性要求比较高,预算也有限,而且蚂蚁金服的开源项目质量一般都有保障。

用LogicFlow在Vue3项目中实现流程图的全流程步骤

下面我以LogicFlow为例,给大家详细讲一下从0到1实现一个供应链节点追踪流程图的步骤:

第一步:创建Vue3项目

如果你还没有Vue3项目,可以用Vite或者Vue CLI创建一个,我这里用Vite,因为Vite比Vue CLI快得多:

  1. 打开终端,输入npm create vite@latest vue3-logicflow-demo -- --template vue-ts(如果不用TypeScript,可以把-- --template vue-ts去掉)
  2. 进入项目目录,输入cd vue3-logicflow-demo
  3. 安装依赖,输入npm install

第二步:安装LogicFlow的相关依赖

LogicFlow有核心包、Vue3适配器、主题包(可选),还有一些插件(比如右键菜单插件、分组插件、导出插件),我这里安装核心包、Vue3适配器、默认主题包、右键菜单插件、导出插件: 输入npm install @logicflow/core @logicflow/vue3-adapter @logicflow/theme @logicflow/extension --save

第三步:初始化LogicFlow画布

  1. 在src/components目录下创建一个新的组件,比如LogicFlowCanvas.vue
  2. 导入相关的依赖:
    import { LogicFlow, type LFOptions } from '@logicflow/core'
    import { VueNodeView } from '@logicflow/vue3-adapter'
    import '@logicflow/theme'
    import { DndPanel, SelectionSelect, Menu, Snapshot } from '@logicflow/extension'
    import '@logicflow/extension/lib/style/index.css'
  3. 在template中添加画布容器和拖拽面板容器:
    <template>
    <div class="logic-flow-container">
     <!-- 拖拽面板容器 -->
     <div class="dnd-panel" ref="dndPanelRef"></div>
     <!-- 画布容器 -->
     <div class="lf-container" ref="lfContainerRef"></div>
    </div>
    </template>
  4. 在script setup中初始化LogicFlow画布:
    import { onMounted, ref } from 'vue'

// 定义画布容器和拖拽面板容器的引用 const lfContainerRef = ref<HTMLElement | null>(null) const dndPanelRef = ref<HTMLElement | null>(null) // 定义LogicFlow实例 let lf: LogicFlow | null = null

// 初始化LogicFlow const initLogicFlow = () => { if (!lfContainerRef.value) return

// 配置LogicFlow const options: LFOptions = { container: lfContainerRef.value, width: '100%', height: '100%', grid: { size: 20, // 网格大小 visible: true, // 是否显示网格 type: 'dot' // 网格类型(dot: 点, line: 线) }, background: { color: '#f5f5f5' // 背景颜色 }, plugins: [DndPanel, SelectionSelect, Menu, Snapshot] // 注册插件 }

// 创建LogicFlow实例 lf = new LogicFlow(options)

// 注册拖拽面板 const dndPanel = lf?.getPluginInstance('dnd-panel') as DndPanel dndPanel?.register({ type: 'rect', // 节点类型 label: '供应商', // 节点标签 icon: '📦' // 节点图标 }) dndPanel?.register({ type: 'circle', label: '仓库', icon: '🏭' }) dndPanel?.register({ type: 'diamond', label: '分拣中心', icon: '🚚' }) dndPanel?.register({ type: 'ellipse', label: '客户', icon: '👤' })

// 渲染画布 lf?.render() }

// 在组件挂载后初始化LogicFlow onMounted(() => { initLogicFlow() })

在style中添加样式:
```css
.logic-flow-container {
  width: 100vw;
  height: 100vh;
  display: flex;
}
.dnd-panel {
  width: 200px;
  height: 100%;
  background-color: #fff;
  border-right: 1px solid #eee;
  padding: 20px;
}
.lf-container {
  flex: 1;
}

第四步:自定义节点和连线

现在我们已经有了一个基础的拖拽式流程图,但默认的节点和连线样式可能不符合产品经理的要求,我们可以自定义节点和连线的样式:

  1. 在src/components目录下创建一个新的文件夹,比如CustomNodes
  2. 在CustomNodes目录下创建一个新的组件,比如CustomRectNode.vue(自定义矩形节点):
    <template>
    <div class="custom-rect-node" :style="{ backgroundColor: properties.color || '#409EFF' }">
     <div class="custom-rect-node-icon">{{ properties.icon || '📦' }}</div>
     <div class="custom-rect-node-label">{{ properties.label || '节点' }}</div>
    </div>
    </template>
``` 3. 在LogicFlowCanvas.vue中注册自定义节点: ```typescript // 导入自定义节点 import CustomRectNode from './CustomNodes/CustomRectNode.vue'

// 注册自定义节点 lf?.register({ type: 'custom-rect', // 自定义节点类型 view: VueNodeView, // 使用VueNodeView渲染 model: { props: { color: '#409EFF', // 默认颜色 icon: '📦', // 默认图标 label: '节点' // 默认标签 } }, component: CustomRectNode // 自定义Vue组件 })

在拖拽面板中使用自定义节点:
```typescript
dndPanel?.register({
  type: 'custom-rect', // 自定义节点类型
  label: '供应商',
  icon: '' // 不需要默认图标了,因为自定义节点里已经有了
})
  1. 自定义连线的样式可以通过LF的style属性配置,比如把连线改成贝塞尔曲线,颜色改成#67C23A,宽度改成2px:
    const options: LFOptions = {
    // 其他配置
    style: {
     baseEdge: {
       stroke: '#67C23A',
       strokeWidth: 2,
       // 贝塞尔曲线
       edgeType: 'bezier',
       // 贝塞尔曲线的控制参数
       offset: 30
     }
    },
    // 其他配置
    }

第五步:添加交互功能

现在我们的流程图已经有了自定义的节点和连线,但还需要一些实用的交互功能,比如节点属性配置、导出功能、撤销重做功能:

  1. 节点属性配置功能:可以用右键菜单插件和Vue的弹窗组件(比如Element Plus的ElDialog)实现,先在项目中安装Element Plus: 输入npm install element-plus --save 然后在main.ts中导入Element Plus:
    import { createApp } from 'vue'
    import './style.css'
    import App from './App.vue'
    import ElementPlus from 'element-plus'
    import 'element-plus/dist/index.css'

const app = createApp(App) app.use(ElementPlus) app.mount('#app')

接着在LogicFlowCanvas.vue中添加右键菜单和节点属性配置弹窗:
```typescript
// 导入Element Plus的弹窗组件
import { ElDialog, ElForm, ElFormItem, ElInput, ElButton } from 'element-plus'
import { ref, reactive } from 'vue'
// 定义右键菜单配置
const menuConfig = {
  nodeMenu: [
    {
      text: '配置属性',
      callback: (node: any) => {
        // 打开属性配置弹窗
        currentNodeId.value = node.id
        currentNodeProperties.value = { ...node.properties }
        dialogVisible.value = true
      }
    },
    {
      text: '删除节点',
      callback: (node: any) => {
        lf?.deleteNode(node.id)
      }
    }
  ]
}
// 配置右键菜单
lf?.getPluginInstance('menu')?.setMenuConfig(menuConfig)
// 定义属性配置弹窗的状态
const dialogVisible = ref(false)
const currentNodeId = ref('')
const currentNodeProperties = reactive({
  color: '#409EFF',
  icon: '📦',
  label: '节点'
})
// 保存节点属性
const saveNodeProperties = () => {
  lf?.setProperties(currentNodeId.value, currentNodeProperties)
  dialogVisible.value = false
}

然后在template中添加属性配置弹窗:

<template>
  <!-- 其他代码 -->
  <ElDialog v-model="dialogVisible" title="配置节点属性" width="400px">
    <ElForm :model="currentNodeProperties" label-width="80px">
      <ElFormItem label="节点颜色">
        <ElInput v-model="currentNodeProperties.color" placeholder="请输入节点颜色(409EFF)" />
      </ElFormItem>
      <ElFormItem label="节点图标">
        <ElInput v-model="currentNodeProperties.icon" placeholder="请输入节点图标()" />
      </ElFormItem>
      <ElFormItem label="节点标签">
        <ElInput v-model="currentNodeProperties.label" placeholder="请输入节点标签" />
      </ElFormItem>
    </ElForm>
    <template #footer>
      <ElButton @click="dialogVisible = false">取消</ElButton>
      <ElButton type="primary" @click="saveNodeProperties">保存</ElButton>
    </template>
  </ElDialog>
</template>
  1. 导出功能:可以用Snapshot插件实现,先在template中添加导出按钮:
    <template>
    <!-- 其他代码 -->
    <div class="toolbar">
     <ElButton type="primary" @click="exportSVG">导出SVG</ElButton>
     <ElButton type="success" @click="exportPNG">导出PNG</ElButton>
     <ElButton type="warning" @click="exportJSON">导出JSON</ElButton>
     <ElButton type="danger" @click="importJSON">导入JSON</ElButton>
    </div>
    </template>

    然后在style中添加toolbar的样式:

    .toolbar {
    position: absolute;
    top: 20px;
    right: 20px;
    display: flex;
    gap: 10px;
    z-index: 999;
    }

    接着在script setup中添加导出和导入的方法:

    // 导入Element Plus的上传组件
    import { ElUpload, type UploadFile, type UploadProps } from 'element-plus'

// 导出SVG const exportSVG = () => { const snapshot = lf?.getPluginInstance('snapshot') as Snapshot snapshot?.getSnapshot({ backgroundColor: '#f5f5f5', fileType: 'svg', fileName: '供应链节点追踪流程图' }) }

// 导出PNG const exportPNG = () => { const snapshot = lf?.getPluginInstance('snapshot') as Snapshot snapshot?.getSnapshot({ backgroundColor: '#f5f5f5', fileType: 'png', fileName: '供应链节点追踪流程图' }) }

// 导出JSON const exportJSON = () => { const data = lf?.getGraphData() const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = '供应链节点追踪流程图.json' a.click() URL.revokeObjectURL(url) }

// 导入JSON的文件读取方法 const handleFileChange: UploadProps['onChange'] = (file: UploadFile) => { const reader = new FileReader() reader.onload = (e) => { const data = JSON.parse(e.target?.result as string) lf?.render(data) } if (file.raw) { reader.readAsText(file.raw) } }

最后在template中添加上传组件:
```html
<template>
  <!-- 其他代码 -->
  <div class="toolbar">
    <!-- 其他按钮 -->
    <ElUpload
      accept=".json"
      :show-file-list="false"
      :auto-upload="false"
      :on-change="handleFileChange"
    >
      <ElButton type="info">导入JSON</ElButton>
    </ElUpload>
  </div>
</template>
  1. 撤销重做功能:Snapshot插件已经内置了撤销重做功能,只需要添加按钮调用就行,先在template中添加按钮:
    <template>
    <!-- 其他代码 -->
    <div class="toolbar">
     <!-- 其他按钮 -->
     <ElButton type="primary" @click="undo">撤销</ElButton>
     <ElButton type="success" @click="redo">重做</ElButton>
    </div>
    </template>

    然后在script setup中添加撤销和重做的方法:

    // 撤销
    const undo = () => {
    const snapshot = lf?.getPluginInstance('snapshot') as Snapshot
    snapshot?.undo()
    }

// 重做 const redo = () => { const snapshot = lf?.getPluginInstance('snapshot') as Snapshot snapshot?.redo() }


### 第六步:测试和优化
现在我们的流程图已经基本完成了,接下来需要测试一下所有功能是否正常,比如拖拽节点、连边、配置属性、导出、导入、撤销重做,如果有问题可以查LogicFlow的官方文档或者在GitHub上提问,优化方面可以根据自己的项目情况调整,比如调整网格大小、背景颜色、节点样式、连线样式、工具栏的位置等等。
## 
Vue3做流程图其实并不难,关键是要选对方式和工具——如果项目对功能、样式有非常特殊的要求,团队有技术储备,项目周期充足,可以选原生开发;否则建议选第三方组件库,比如LogicFlow、AntV X6、Vue Flow,其中LogicFlow比较适合企业级后台项目,上手也比较快。
今天的分享就到这里了,希望对大家有帮助,如果有什么问题可以在评论区留言,我会尽量回复大家。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门