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

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

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

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

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

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

📢 特别提醒:

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

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

相关推荐
萧行之1 小时前
可视化技术复盘:从图形语法看 D3、Vega-Lite、ECharts、Observable Plot
前端·学习·数据可视化
尾善爱看海9 小时前
Vue 面试收官篇:SSR、性能优化落地、30 道高频面试题精讲(附标准答案)
前端·javascript·vue.js·面试·vue
weixin_BYSJ19879 小时前
【计算机毕设】基于SpringBoot与Vue的文物保护档案管理系统08621
vue.js·spring boot·spring cloud·微服务·架构·django·课程设计
Bs_MoneyMagnet10 小时前
基于springboot+vue的个人健康管理系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·vue3·springboot3·计算机毕业设计
驳是10 小时前
一个组件,提升 react-router 开发的幸福感
前端·preact
GreenTea10 小时前
OpenAI Agents API 上手实测:一次调用把整个 agent loop 甩给 OpenAI
前端·后端·算法
星云API技术支持10 小时前
企业微信二次开发:群权限设置、成员管理与群资料维护的接口组合实践
java·前端·企业微信
京东云开发者10 小时前
81.8 秒的视频,我们改了 15 个版本:一次纯 Codex 驱动的 AI-native 视频实践
前端·aigc
Csvn11 小时前
TypeScript 大型项目架构:从单体 tsconfig 到分层可扩展的工程
前端
Bs_MoneyMagnet12 小时前
基于springboot+vue的心理咨询预约与随访平台的设计与实现 源码+文档
vue.js·spring boot·后端·spring·毕业设计·旅游·计算机毕业设计