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 等事件钩子
  • 可组合使用多个视图与工具栏

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

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

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

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

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

📢 特别提醒:

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

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

相关推荐
小兵张健5 小时前
价值1000的 AI 工作流:Codex 通用前端协作模式
前端·aigc·ai编程
sunny_5 小时前
面试踩大坑!同一段 Node.js 代码,CJS 和 ESM 的执行顺序居然是反的?!99% 的人都答错了
前端·面试·node.js
拉不动的猪5 小时前
移动端调试工具VConsole初始化时的加载阻塞问题
前端·javascript·微信小程序
ayqy贾杰7 小时前
Agent First Engineering
前端·vue.js·面试
IT_陈寒7 小时前
SpringBoot实战:5个让你的API性能翻倍的隐藏技巧
前端·人工智能·后端
iceiceiceice8 小时前
iOS PDF阅读器段评实现:如何从 PDFSelection 精准还原一个自然段
前端·人工智能·ios
大金乄8 小时前
封装一个vue2的elementUI 表格组件(包含表格编辑以及多级表头)
前端·javascript
葡萄城技术团队9 小时前
【性能优化篇】面对万行数据也不卡顿?揭秘协同服务器的“片段机制 (Fragments)”
前端
程序员阿峰9 小时前
2026前端必备:TensorFlow.js,浏览器里的AI引擎,不写Python也能玩转智能
前端
Jans9 小时前
Shipfe — Rust 写的前端静态部署工具:一条命令上线 + 零停机 + 可回滚 + 自动清理
前端