Files
eaiadmin 90031b75f3 docs: 重构仓库文档目录并迁移训练素材
按当前架构重组 docs 目录,统一中文命名与目录分层,并将训练原材料迁移到独立目录以保持架构文档边界清晰。
2026-09-22 23:23:16 +08:00

23 KiB
Raw Permalink Blame History

工作流画布交互设计调研报告

版本:V2.0 | 最后更新:2026-08-17 目标产品:eai_agentplatform-app · 数字员工平台 · 工坊(Studio)


目录

  1. 调研范围与背景
  2. 行业产品全景对比
  3. 节点体系设计
    • 3.1 三节点 vs 五节点体系
    • 3.2 子类型(SubType)架构
    • 3.3 连接器作为被引用资源
  4. 画布交互模型
    • 4.1 拖入创建(Drag & Drop)
    • 4.2 右键菜单体系
    • 4.3 键盘快捷键体系
    • 4.4 选中与视觉反馈
    • 4.5 Edge 交互(连线)
  5. 布局与导航
  6. 技术实现(VueFlow)
    • 6.1 核心事件
    • 6.2 自定义节点
    • 6.3 自定义边
    • 6.4 CSS 主题与样式
  7. 当前实现状态
  8. 待完成项
  9. 参考资料

1. 调研范围与背景

1.1 调研目标

为数字员工平台的 工坊(Studio) 模块设计一套符合行业标准、贴近业务用户心智的工作流画布交互模型。核心需求:

  • 业务用户能通过 拖拽 + 配置 的方式定义数字员工的工作流程
  • 画布交互对标 Dify / n8n / Make / Zapier / Coze 等主流产品
  • 节点体系从 SY18(信源/动作/结果/权限)简化为 3 节点体系(开始/动作/结束)

1.2 调研的产品

产品 定位 节点体系 画布引擎 参考版本
Dify AI 工作流编排 开始/LLM/工具/结束等 React Flow v1.14.0
n8n 自动化工作流 触发器/动作 自研 latest
Make 可视化自动化 触发器/动作/模块 自研 latest
Zapier 轻量自动化 Trigger/Action/Search 自研 latest
Coze AI Bot 构建 开始/LLM/插件/知识库/结束 自研 latest
Langflow LLM 流程编排 Input/LLM/Output React Flow latest
VueFlow 画布引擎库 N/A Vue Flow v1.44+

2. 行业产品全景对比

2.1 产品定位对比

维度 Dify n8n Make Zapier Coze 本产品
目标用户 开发者/AI 工程师 技术运营 业务用户 业务用户 业务用户+开发者 HR/销售/业务用户
复杂度 中高 高 中 低 中 中
拖入创建 ✅ ✅ ✅ ❌(点击添加) ✅ ✅
右侧检查器 ✅ ✅ ✅ ✅(弹窗) ✅ ✅
节点右键菜单 ✅ ✅ ✅ ❌ ✅ ✅
Edge 右键菜单 ✅(PR#33391) ✅ ✅ ❌ ❌ ✅
Delete 键删除 ✅ ✅ ✅ ✅ ✅ ✅
Ctrl+D 复制 ✅ ✅ ❌ ❌ ✅ ❌
Ctrl+Z 撤销 ✅ ✅ ✅ ✅ ✅ ❌
连线 "+" 按钮 ✅ ❌ ❌ ❌ ❌ ❌

2.2 画布布局对比

产品 左侧面板 右侧面板 工具栏 布局风格
Dify 节点选择(可折叠) 检查器(选中后出现) 顶部薄栏 左-中-右
n8n 节点库 编辑面板 顶部 左-中-右
Make 模块库(浮层) 配置面板 顶部 中-右(浮层)
Zapier 无(步骤式) 弹窗配置 顶部 步骤向导
Coze 节点/插件/知识库 配置区(底部弹出) 顶部 左-中-右
本产品 节点库+工具区 检查器面板 顶部 48px 左-中-右

3. 节点体系设计

3.1 三节点 vs 五节点体系

行业共识:主流产品均采用 3~4 种核心节点类型。 Dify: Start → LLM/Tool → End n8n: Trigger → Action → No explicit end Make: Trigger → Module → No explicit end Zapier: Trigger → Action/Search → No explicit end Coze: Start → LLM/Plugin/Knowledge → End

本产品的演进:

  • 旧(SY18 模型):5 种节点(input / connector / action / result / permission)
  • 新(Zapier/Dify 对齐):3 种节点(start / action / end)
    • start(开始节点):取代旧的 input + connector
    • action(动作节点):保留旧的 action
    • end(结束节点):取代旧的 result + permission

3.2 子类型(SubType)架构

三节点通过 子类型(SubType) 实现具体的行为分化,避免了节点类型爆炸:

start (开始节点)
  ├── schedule   (⏰ 定时触发)
  ├── file       (📎 文件触发)
  ├── connector  (🔌 连接器触发)
  └── manual     (✍️ 手动触发)

action (动作节点)
  ├── ai         (🤖 AI 处理)
  ├── code       (💻 代码处理)
  ├── condition  (🔀 条件分支)
  └── connector  (🔌 连接器操作)

end (结束节点)
  ├── data       (📊 返回数据)
  ├── file       (📁 文件输出)
  └── connector  (🔌 连接器输出)

核心设计原则:

  • 每种节点只有一个 子类型下拉选择,用户在检查器中切换
  • subType 改变时自动清除不兼容的配置(如从 connector 切到 ai 时清除 connectorKey)
  • 子类型的标签包含 emoji 前缀,便于视觉识别(参考 Dify 的节点 icon + label)

3.3 连接器作为被引用资源

关键架构决策:连接器不是画布上的独立节点类型。

在旧的 SY18 模型中,"信源"被理解为连接器。经过对 Zapier/Make.com/Coze/Dify 的调研发现:

产品 连接器(Connector)的角色
Zapier Trigger 和 Action 通过下拉选择 APP + 事件/操作,APP 就是连接器
Make Module 可以选择不同的 Service,Service 就是连接器
Dify 工具(Tools)是独立节点,但连接器/数据源是 LLM 或知识库的属性
Coze 插件(Plugin)是独立节点,连接器是 Bot 的配置属性

本产品的方案:

  • 连接器不是画布节点,而是 start/action/end 节点通过 subType='connector' 引用的资源
  • 连接器列表在 工具区 中管理,由工具栏 ☑ 🔌 连接器 按钮控制显示/隐藏
  • 用户可以从工具区 点击添加 一个 start 节点并自动绑定到选中的连接器
  • 在检查器中,当 subType='connector' 时,显示连接器绑定下拉

4. 画布交互模型

4.1 拖入创建(Drag & Drop)

Dify 标准流程:

  1. 从左侧节点列表拖出一个节点类型
  2. 拖到画布上释放
  3. 节点出现在释放位置,自动选中并打开右侧配置面板

本产品实现:

<!-- 工具箱节点 -->
<div
  v-for="item in toolboxItems"
  draggable="true"
  @dragstart="handleToolboxDragStart($event, item.key)"
  @dragend="handleToolboxDragEnd"
>
  ...
</div>

<!-- 画布接收 drop -->
<div
  class="canvas-shell"
  @dragover.prevent="handleCanvasDragOver"
  @drop.prevent="handleCanvasDrop"
  @dragleave="handleCanvasDragLeave"
>
  ...
</div>

拖放数据流:

dragstart → setData('application/dw-node-kind', kind)
dragover  → 显示 drop 提示(drag-over class)
drop      → screenToFlowCoordinate → createCanvasNode
dragend   → 清理状态

4.2 右键菜单体系

Dify 标准(PR #34138 + PR #33391):

  • 节点右键菜单:编辑节点 / 复制节点 / 删除节点 / 断开所有连线
    • 新版本还支持多选节点时的对齐操作
  • Edge 右键菜单:删除连线(PR #33391, 2026-03 合并)
    • 右键点击连线弹出删除选项
    • 菜单互斥:同一时间只有一个菜单可见
  • 画布空白右键菜单:粘贴 / 适配视图 / 重置

本产品实现:

节点右键菜单:

<div v-if="contextMenu.visible" class="node-context-menu"
  :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }" @click.stop>
  <div class="context-menu-item" @click="editNodeFromContext">✏️ 编辑节点</div>
  <div class="context-menu-item" @click="duplicateNodeFromContext">📋 复制节点</div>
  <div class="context-menu-divider"></div>
  <div class="context-menu-item" @click="deleteNodeFromContext">🗑️ 删除节点</div>
  <div class="context-menu-item" @click="disconnectNodeFromContext">🔗 断开所有连线</div>
</div>

<!-- Edge 右键菜单 -->
<div v-if="edgeContextMenu.visible" class="node-context-menu edge-context-menu"
  :style="{ left: edgeContextMenu.x + 'px', top: edgeContextMenu.y + 'px' }" @click.stop>
  <div class="context-menu-item" @click="deleteEdgeFromContext">🗑️ 删除连线</div>
</div>

菜单互斥逻辑:

function handleEdgeContextMenu({ edge, event }) {
  event.preventDefault()
  closeNodeContextMenu()   // 关闭节点菜单
  closeEdgeContextMenu()   // 关闭已有边菜单
  edgeContextMenu.edgeId = edge.id
  edgeContextMenu.x = event.clientX
  edgeContextMenu.y = event.clientY
  edgeContextMenu.visible = true
}

function handlePaneClick() {
  selectedNodeId.value = ''
  closeNodeContextMenu()
  closeEdgeContextMenu()
}

4.3 键盘快捷键体系

Dify 快捷键标准:

快捷键 功能
Delete 删除选中的节点或边
Backspace 删除选中节点或边(有争议,用户易误触)
Ctrl+D 复制选中节点
Ctrl+Z 撤销
Ctrl+Shift+Z / Ctrl+Y 重做
Shift + 点击 多选节点
Arrow keys 移动选中节点
Ctrl+A 全选

本产品实现:

function handleKeyDown(event) {
  const tag = event.target.tagName
  // 输入框中不触发
  if (tag === 'INPUT' || tag === 'TEXTAREA' || event.target.isContentEditable) return

  if (event.key === 'Delete' || event.key === 'Backspace') {
    // 1. 先关菜单(如果有)
    if (contextMenu.visible || edgeContextMenu.visible) {
      closeNodeContextMenu()
      closeEdgeContextMenu()
      event.preventDefault()
      return
    }
    // 2. 删选中的边
    const selectedEdges = edges.value.filter(e => e.selected)
    if (selectedEdges.length > 0) {
      edges.value = edges.value.filter(e => !e.selected)
      event.preventDefault()
      return
    }
    // 3. 删选中的节点
    const selectedNodes = nodes.value.filter(n => n.selected)
    if (selectedNodes.length > 0) {
      const selectedIds = new Set(selectedNodes.map(n => n.id))
      nodes.value = nodes.value.filter(n => !selectedIds.has(n.id))
      edges.value = edges.value.filter(e => !selectedIds.has(e.source) && !selectedIds.has(e.target))
      event.preventDefault()
    }
  }
}

4.4 选中与视觉反馈

节点选中状态:

  • VueFlow 原生支持 node.selected 属性
  • 点击节点 → 节点获得 .selected class → 边框变色 + 阴影增强
  • 点击画布空白 → 取消全部选中

本产品的颜色体系:

节点类型 边框色 选中色
start #409eff (蓝) 实线蓝色 + 蓝光阴影
action #e6a23c (橙) 实线橙色 + 橙光阴影
end #67c23a (绿) 实线绿色 + 绿光阴影

4.5 Edge 交互(连线)

Dify 标准:

  1. 连接操作:从一个节点的右侧 handle 拖到另一个节点的左侧 handle
  2. 选中:点击连线 → 连线高亮(变蓝变粗)
  3. 右键菜单:右键点击连线 → 弹出删除选项
  4. Delete 键删除:选中连线后按 Delete 键
  5. Hover "+" 按钮:鼠标悬停在连线上 → 中点出现 "+" 按钮 → 点击后插入一个新节点(串行)
  6. 断开连接:从 handle 拖出新连线覆盖已有连线,自动替换

本产品实现:

Edge 视觉反馈(CSS):

/* 点击区域加宽 */
:deep(.vue-flow__edge-path) {
  stroke-linecap: round;
  transition: stroke 0.15s, stroke-width 0.15s;
}

/* hover 高亮 */
:deep(.vue-flow__edge:hover .vue-flow__edge-path) {
  stroke: #409eff;
  stroke-width: 2.5;
}

/* 选中高亮 */
:deep(.vue-flow__edge.selected .vue-flow__edge-path) {
  stroke: #409eff;
  stroke-width: 3;
}

Handle hover:

:deep(.vue-flow__handle) {
  transition: transform 0.12s, background 0.12s;
}
:deep(.vue-flow__handle:hover) {
  transform: scale(1.3);
  background: #409eff;
}

5. 布局与导航

5.1 整体布局结构

┌─────────────────────────────────────────────────────────────┐
│ [头部 Header · 48px]                                        │
├────────┬────────────────────────────────────┬───────────────┤
│ 左侧   │         画布区域                    │ 右侧检查器    │
│ 节点库  │   Canvas (VueFlow)                 │ Inspector     │
│ +工具区 │                                    │ (选中后出现)   │
│        │    ┌────┐   ┌────┐   ┌────┐        │               │
│        │    │ S  │──→│ A  │──→│ E  │        │               │
│        │    └────┘   └────┘   └────┘        │               │
│        │                                    │               │
├────────┴────────────────────────────────────┴───────────────┤
│ [工具栏] ← ☑ 🔌 连接器  |  ⚙ 设置  |  [保存]              │
└─────────────────────────────────────────────────────────────┘

5.2 左侧节点库(Toolbox)

区域 内容 可折叠
头部 "节点库" + ◀ 折叠按钮 ✅
节点列表 开始节点/动作节点/结束节点(可拖拽) 跟随折叠
分隔线 - -
工具区标题 "工具区" -
连接器工具 连接器列表(由工具栏按钮控制可见性) ✅(点击文字折叠)

展开状态宽度:200px 折叠状态宽度:52px(只显示图标)

5.3 右侧检查器(Inspector)

区域 内容
头部 "节点检查器"
当前节点 显示节点标题(只读)
节点类型 显示类型标签(只读)
子类型选择 el-select 下拉(start: 定时/文件/连接器/手动; action: AI/代码/条件/连接器; end: 数据/文件/连接器)
连接器绑定 仅 subType='connector' 时显示,el-select 选择连接器
节点标题 el-input 可编辑
节点说明 el-input textarea 可编辑
节点位置 X/Y 坐标(只读)
上下游 上游节点 / 下游节点(只读)

6. 技术实现(VueFlow)

6.1 核心事件

VueFlow 事件 用途 当前状态
@node-click 点击节点 → 选中 + 显示检查器 ✅
@node-context-menu 右键节点 → 弹出菜单 ✅
@edge-click 点击连线 → 选中高亮 ✅
@edge-context-menu 右键连线 → 删除菜单 ✅
@connect 建立连线 ✅
@pane-click 点击画布空白 → 取消选中 + 关菜单 ✅
@pane-ready 画布初始化 → 绑定键盘事件 ✅
@nodes-change 节点变化 ❌(未用)
@edges-change 边变化 ❌(未用)
@node-drag-stop 拖拽停止 ❌(未用)

6.2 自定义节点

// 节点定义
const newNode = {
  id: `${kind}-${nodeCounter}`,
  type: 'dw',         // 自定义节点类型,对应 <template #node-dw>
  position: { x, y },
  draggable: true,
  connectable: true,
  selectable: true,
  data: {
    kind,             // 'start' | 'action' | 'end'
    typeLabel,        // '▶ 开始' | '⚡ 动作' | '📤 结束'
    subType,          // 子类型,默认取第一个
    title,            // 节点标题
    detail,           // 节点说明
    connectorKey,     // 仅 subType='connector' 时有值
  },
}
<!-- 自定义节点渲染模板 -->
<template #node-dw="{ data, selected }">
  <div class="dw-node" :class="[data.kind, { selected }]">
    <Handle type="target" :position="Position.Left" />
    <div class="dw-node-type">{{ data.typeLabel }}</div>
    <div v-if="data.subType" class="dw-node-subtype">
      {{ nodeKindConfig[data.kind]?.subTypes?.find(s => s.value === data.subType)?.label || '' }}
    </div>
    <div class="dw-node-title">{{ data.title }}</div>
    <div class="dw-node-detail">{{ data.detail }}</div>
    <Handle type="source" :position="Position.Right" />
  </div>
</template>

6.3 自定义边

目前使用 VueFlow 内置边类型:

  • type: 'smoothstep' — 圆角折线
  • markerEnd: MarkerType.ArrowClosed — 终点箭头

待实现的自定义边功能:

  • 边中点悬停 "+" 按钮(需自定义 edgeTypes)
  • 边标签显示(显示连线条件或数据映射)

6.4 CSS 主题与样式

必须导入的样式:

import '@vue-flow/core/dist/style.css'        // 基础样式
import '@vue-flow/core/dist/theme-default.css'  // 可选默认主题

关键 CSS 变量覆盖:

:root {
  --vf-node-bg: #fff;
  --vf-node-text: #1f2d3d;
  --vf-connection-path: #b1b1b7;
  --vf-handle: #555;
}

自定义节点类名:

类名 用途
.dw-node 所有自定义节点
.dw-node.start 开始节点(蓝色边框)
.dw-node.action 动作节点(橙色边框)
.dw-node.end 结束节点(绿色边框)
.dw-node.selected 选中态(边框变色 + 阴影)

7. 当前实现状态

7.1 已完成功能

功能 状态 实现位置
3 节点体系(start/action/end) ✅ toolboxItems + nodeKindConfig
子类型下拉选择 ✅ 检查器面板 el-select
连接器绑定(subType=connector) ✅ 检查器面板条件渲染
拖入创建(drag & drop) ✅ handleToolboxDragStart → handleCanvasDrop
节点右键菜单 ✅ handleNodeContextMenu → 4 个菜单项
Edge 右键菜单 ✅ handleEdgeContextMenu → 删除连线
Delete 键删除 ✅ handleKeyDown(全局 document 监听)
Edge hover 高亮 ✅ CSS :deep(.vue-flow__edge:hover)
Edge 选中高亮 ✅ CSS :deep(.vue-flow__edge.selected)
Handle hover 放大 ✅ CSS :deep(.vue-flow__handle:hover)
工具栏连接器开关 ✅ connectorToolEnabled + ☑/☐ 按钮
工具区折叠 ✅ connectorListCollapsed + 点击"连接器"文字
连接器创建弹窗 ✅ connectorDialogVisible + createConnectorFromForm
右键菜单互斥 ✅ closeNodeContextMenu() / closeEdgeContextMenu()
键盘监听生命周期 ✅ onMounted 添加 / onUnmounted 移除
输入保护(不误删) ✅ tagName === 'INPUT' 等判断

7.2 当前代码架构

StudioPage.vue
├── Template
│   ├── 工具栏 (.canvas-toolbar)
│   │   ├── 返回按钮 + 标题 + 节点/边计数
│   │   └── ☑ 连接器 + ⚙ 设置 + 保存
│   ├── 新手引导 (无模式时)
│   ├── 模式选择弹窗 (模板/复制)
│   ├── 设置弹窗
│   ├── 连接器创建弹窗
│   ├── 主布局 (.studio-grid)
│   │   ├── 左侧节点库+工具区 (.toolbox-panel)
│   │   ├── 中间画布 (.canvas-shell > VueFlow)
│   │   └── 右侧检查器 (.inspector-panel)
│   ├── 节点右键菜单 (.node-context-menu)
│   └── Edge 右键菜单 (.edge-context-menu)
├── Script
│   ├── 状态定义 (ref/reactive)
│   ├── 计算属性 (computed)
│   ├── 模板/配置数据 (templateCards, toolboxItems, nodeKindConfig)
│   ├── 业务函数 (createDraft, rebootCanvas, saveDw, ...)
│   ├── 交互函数 (handleNodeClick, handleNodeContextMenu, handleKeyDown, ...)
│   └── 生命周期 (onMounted, onUnmounted, watch)
└── Style (scoped)
    ├── 工具栏 (.canvas-toolbar)
    ├── 新手引导 (.canvas-landing)
    ├── 主布局 (.studio-grid)
    ├── 节点库 (.toolbox-panel / .toolbox-item)
    ├── 工具区 (.toolbox-tool-area)
    ├── 画布 (.canvas-shell / .dw-flow)
    ├── DW 节点 (.dw-node / .dw-node.start/.action/.end)
    ├── 检查器 (.inspector-panel)
    ├── 弹窗公用 (.dialog-template-grid)
    ├── 连接器 (.toolbox-connector-item)
    ├── 右键菜单 (.node-context-menu)
    ├── Edge 交互 (vue-flow__edge hover/selected)
    └── 响应式 (@media)

8. 待完成项

8.1 高优先级

功能 说明 Dify 参考
Ctrl+D 复制节点 选中节点后按 Ctrl+D 快速复制 ✅
Ctrl+Z 撤销/重做 历史记录栈,支持撤销和重做操作 ✅
Edge "+" 按钮 hover 在连线中点显示 "+",点击后插入新节点(串行) ✅ (最新版本)
编译面板清理 compileCanvas() 中仍有 input/connector/result/permission 旧 kind 引用 -

8.2 中优先级

功能 说明
画布右键菜单 右键点击画布空白 → 粘贴/适配视图/重置
多选操作 Shift+点击 多选,批量删除/复制/对齐
对齐辅助线 拖拽节点时的参考线
MiniMap 缩略图导航
Edge 标签 在连线上显示条件标签(如"是/否")

8.3 低优先级

功能 说明
自动保存 画布变化后自动保存草稿
全屏模式 画布全屏编辑
节点注释/便签 在画布上添加文字注释
模板片段 选中部分节点另存为模板片段
导入/导出 JSON 导入导出画布定义

9. 参考资料

9.1 Dify 相关

链接 内容
Dify Edge Context Menu PR #33391 2026-03 合并的 Edge 右键菜单实现
Dify Multi Node Context Menu PR #34138 2026-03 合并的多选节点菜单
Dify Backspace Issue #23354 Backspace 误删节点问题讨论
Dify Backspace Discussion #20140 用户要求移除 Backspace 快捷键

9.2 VueFlow 相关

链接 内容
VueFlow 官方文档 核心 API、自定义节点/边、主题
VueFlow Edge Guide 边的增删改查
VueFlow Theming Guide CSS 变量、自定义主题

9.3 本产品内部文档

文档 内容
SY18 - 专员最小定义模型 信源/动作/结果/权限模型
SY17 - UI 线框图 工作台 UI 框架
DR03 - 交互规范 全局交互规范

文档维护者:AI 工程团队 下次评审:2026-08-24