Vue 3 + jsPlumb 打造可视化流程图组件(支持图标与颜色配置)

文章目录

一、项目简介

本文基于 Vue 3 + jsPlumb 实现一个完整的流程图组件,具备以下功能:

  • 支持节点拖拽添加
  • 支持节点之间连接与删除
  • 节点支持自定义图标与颜色选择
  • 支持导入导出 JSON 数据
  • 支持视图缩放与自动居中
  • 支持快捷键删除、右键菜单操作
  • 支持自动布局拓展
  • 具备组件封装能力,可复用

二、流程图基础结构搭建

2.1 安装依赖

bash 复制代码
npm install jsplumb

2.2 基础组件结构

javascript 复制代码
<template>
  <div class="flow-container" ref="container">
    <div
      v-for="node in nodes"
      :key="node.id"
      class="flow-node"
      :id="node.id"
      :style="getNodeStyle(node)"
      @contextmenu.prevent="showContextMenu(node, $event)"
    >
      <div class="icon">{{ node.icon }}</div>
      <div class="label">{{ node.label }}</div>
    </div>

    <context-menu
      v-if="context.visible"
      :node="context.node"
      :x="context.x"
      :y="context.y"
      @update="updateNode"
      @delete="deleteNode"
    />
  </div>
</template>

三、节点拖拽与连接逻辑

3.1 拖拽添加节点

javascript 复制代码
function addNode(label = '新节点') {
  const id = 'node-' + Date.now();
  nodes.value.push({
    id,
    label,
    icon: '📦',
    color: '#409EFF',
    left: 200,
    top: 150
  });
  nextTick(() => {
    instance.value.draggable(id, { containment: 'parent' });
    initEndpoints(id);
  });
}

3.2 jsPlumb 初始化与连接规则

javascript 复制代码
onMounted(() => {
  instance.value = jsPlumb.getInstance();

  instance.value.bind('connection', info => {
    connections.value.push({
      source: info.sourceId,
      target: info.targetId
    });
  });

  nodes.value.forEach(node => {
    instance.value.draggable(node.id, { containment: 'parent' });
    initEndpoints(node.id);
  });
});

function initEndpoints(id) {
  instance.value.addEndpoint(id, {
    anchor: 'Right',
    isSource: true,
    maxConnections: -1
  }, commonStyle);

  instance.value.addEndpoint(id, {
    anchor: 'Left',
    isTarget: true,
    maxConnections: -1
  }, commonStyle);
}

四、节点图标与颜色选择功能

4.1 节点数据结构

javascript 复制代码
{
  id: 'node-1',
  label: '开始',
  icon: '🟢',
  color: '#67C23A',
  left: 100,
  top: 100
}

4.2 图标与颜色右键菜单

javascript 复制代码
<!-- ContextMenu.vue -->
<template>
  <div class="context-menu" :style="{ left: x + 'px', top: y + 'px' }">
    <label>名称:
      <input v-model="node.label" />
    </label>
    <label>图标:
      <select v-model="node.icon">
        <option>📦</option>
        <option>🟢</option>
        <option>📄</option>
        <option>🔴</option>
      </select>
    </label>
    <label>颜色:
      <input type="color" v-model="node.color" />
    </label>
    <button @click="$emit('delete', node)">删除节点</button>
  </div>
</template>

4.3 渲染节点样式

javascript 复制代码
function getNodeStyle(node) {
  return {
    left: node.left + 'px',
    top: node.top + 'px',
    backgroundColor: node.color
  };
}

五、节点与连线删除功能

5.1 快捷键删除

javascript 复制代码
window.addEventListener('keydown', e => {
  if (e.key === 'Delete' && selectedNode.value) {
    deleteNode(selectedNode.value);
  }
});

5.2 删除连线

javascript 复制代码
instance.value.bind('click', conn => {
  instance.value.deleteConnection(conn);
  connections.value = connections.value.filter(
    c => !(c.source === conn.sourceId && c.target === conn.targetId)
  );
});

六、导入导出 JSON 数据

6.1 导出结构

javascript 复制代码
function exportFlow() {
  const data = {
    nodes: nodes.value,
    connections: connections.value
  };
  const json = JSON.stringify(data, null, 2);
  console.log(json);
}

6.2 导入结构

javascript 复制代码
function importFlow(json) {
  const data = JSON.parse(json);
  nodes.value = data.nodes;
  connections.value = data.connections;

  nextTick(() => {
    nodes.value.forEach(n => {
      instance.value.draggable(n.id, { containment: 'parent' });
      initEndpoints(n.id);
    });

    connections.value.forEach(conn => {
      instance.value.connect({
        source: conn.source,
        target: conn.target
      });
    });
  });
}

七、视图缩放与自动居中

javascript 复制代码
function zoomView(scale) {
  const container = document.querySelector('.flow-container');
  container.style.transform = `scale(${scale})`;
  container.style.transformOrigin = '0 0';
}

function centerView() {
  const container = document.querySelector('.flow-container');
  container.scrollIntoView({ behavior: 'smooth', block: 'center' });
}

八、自动布局拓展方向

可借助 dagre.js 或 elkjs 实现自动布局:

  • 自动计算节点位置
  • 优化连接路径
  • 支持有向图/流程图布局模式
bash 复制代码
npm install dagre

九、组件封装建议

  • 封装为 <FlowEditor /> 组件
  • 支持 v-model:data 传入节点与连线数据
  • 提供 @save@connect@delete 等事件钩子
  • 可组合使用多个视图与工具栏

到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~

创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:

点个赞❤️ 让更多人看到优质内容

关注「前端极客探险家」🚀 每周解锁新技巧

收藏文章⭐️ 方便随时查阅

📢 特别提醒:

转载请注明原文链接,商业合作请私信联系

感谢你的阅读!我们下篇文章再见~ 💕

相关推荐
CodeCraft Studio1 分钟前
PDF处理控件Aspose.PDF指南:使用 C# 从 PDF 文档中删除页面
前端·pdf·c#
好_快13 分钟前
Lodash源码阅读-baseFill
前端·javascript·源码阅读
好_快13 分钟前
Lodash源码阅读-fill
前端·javascript·源码阅读
excel1 小时前
webpack 模块图 第 三 节
前端
徐_三岁1 小时前
Vue 3中的 setup
前端
excel1 小时前
webpack 模块图 第 二 节
前端
CodeJourney.3 小时前
从PPT到DeepSeek开启信息可视化的全新之旅
数据库·人工智能·算法·excel·流程图
计算机学姐6 小时前
基于SpringBoo的地方美食分享网站
java·vue.js·mysql·tomcat·mybatis·springboot·美食
—Qeyser7 小时前
用 Deepseek 写的uniapp血型遗传查询工具
前端·javascript·ai·chatgpt·uni-app·deepseek
web_Hsir7 小时前
Uniapp Vue 实现当前日期到给定日期的倒计时组件开发
vue.js