init: 数字员工平台初始代码
包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。 - 工作台画布:节点拖放、连线模式、右键菜单、AI 助手 - 后端:连接器 API、专员种子数据 - 导航:左侧导航、工坊、市场、控制台
This commit is contained in:
@@ -0,0 +1,625 @@
|
||||
# 工作流画布交互设计调研报告
|
||||
|
||||
> **版本:V2.0 | 最后更新:2026-08-17**
|
||||
> **目标产品:eaisalestrain-app · 数字员工平台 · 工坊(Studio)**
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [调研范围与背景](#1-调研范围与背景)
|
||||
2. [行业产品全景对比](#2-行业产品全景对比)
|
||||
3. [节点体系设计](#3-节点体系设计)
|
||||
- 3.1 三节点 vs 五节点体系
|
||||
- 3.2 子类型(SubType)架构
|
||||
- 3.3 连接器作为被引用资源
|
||||
4. [画布交互模型](#4-画布交互模型)
|
||||
- 4.1 拖入创建(Drag & Drop)
|
||||
- 4.2 右键菜单体系
|
||||
- 4.3 键盘快捷键体系
|
||||
- 4.4 选中与视觉反馈
|
||||
- 4.5 Edge 交互(连线)
|
||||
5. [布局与导航](#5-布局与导航)
|
||||
6. [技术实现(VueFlow)](#6-技术实现vueflow)
|
||||
- 6.1 核心事件
|
||||
- 6.2 自定义节点
|
||||
- 6.3 自定义边
|
||||
- 6.4 CSS 主题与样式
|
||||
7. [当前实现状态](#7-当前实现状态)
|
||||
8. [待完成项](#8-待完成项)
|
||||
9. [参考资料](#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. 节点出现在释放位置,自动选中并打开右侧配置面板
|
||||
|
||||
**本产品实现:**
|
||||
```vue
|
||||
<!-- 工具箱节点 -->
|
||||
<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>
|
||||
```
|
||||
|
||||
**拖放数据流:**
|
||||
```javascript
|
||||
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 合并)
|
||||
- 右键点击连线弹出删除选项
|
||||
- 菜单互斥:同一时间只有一个菜单可见
|
||||
- **画布空白右键菜单**:粘贴 / 适配视图 / 重置
|
||||
|
||||
**本产品实现:**
|
||||
|
||||
**节点右键菜单:**
|
||||
```vue
|
||||
<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>
|
||||
```
|
||||
|
||||
**菜单互斥逻辑:**
|
||||
```javascript
|
||||
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 | 全选 |
|
||||
|
||||
**本产品实现:**
|
||||
```javascript
|
||||
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):**
|
||||
```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:**
|
||||
```css
|
||||
: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 自定义节点
|
||||
|
||||
```javascript
|
||||
// 节点定义
|
||||
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' 时有值
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<!-- 自定义节点渲染模板 -->
|
||||
<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 主题与样式
|
||||
|
||||
**必须导入的样式:**
|
||||
```javascript
|
||||
import '@vue-flow/core/dist/style.css' // 基础样式
|
||||
import '@vue-flow/core/dist/theme-default.css' // 可选默认主题
|
||||
```
|
||||
|
||||
**关键 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](https://github.com/langgenius/dify/pull/33391) | 2026-03 合并的 Edge 右键菜单实现 |
|
||||
| [Dify Multi Node Context Menu PR #34138](https://github.com/langgenius/dify/pull/34138) | 2026-03 合并的多选节点菜单 |
|
||||
| [Dify Backspace Issue #23354](https://github.com/langgenius/dify/issues/23354) | Backspace 误删节点问题讨论 |
|
||||
| [Dify Backspace Discussion #20140](https://github.com/langgenius/dify/discussions/20140) | 用户要求移除 Backspace 快捷键 |
|
||||
|
||||
### 9.2 VueFlow 相关
|
||||
|
||||
| 链接 | 内容 |
|
||||
|------|------|
|
||||
| [VueFlow 官方文档](https://vueflow.dev/) | 核心 API、自定义节点/边、主题 |
|
||||
| [VueFlow Edge Guide](https://vueflow.dev/guide/edge.html) | 边的增删改查 |
|
||||
| [VueFlow Theming Guide](https://vueflow.dev/guide/theming.html) | CSS 变量、自定义主题 |
|
||||
|
||||
### 9.3 本产品内部文档
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| [SY18 - 专员最小定义模型](../01_System_Overall/SY18_Specialist_Minimal_Definition_Model.md) | 信源/动作/结果/权限模型 |
|
||||
| [SY17 - UI 线框图](../01_System_Overall/SY17_Workbench_UI_Wireframes.md) | 工作台 UI 框架 |
|
||||
| [DR03 - 交互规范](../08_Design_Rules/DR03_Interaction_Rules.md) | 全局交互规范 |
|
||||
|
||||
---
|
||||
|
||||
> **文档维护者:AI 工程团队**
|
||||
> **下次评审:2026-08-24**
|
||||
@@ -0,0 +1,19 @@
|
||||
# 工业AI观察员
|
||||
|
||||
微信公众号文章存档。
|
||||
|
||||
- **公众号名称**:工业AI观察员
|
||||
- **原始 ID(user_name)**:`gh_c70db3f788a6`
|
||||
- **biz**:`MzIxNjcyMzI5MA==`
|
||||
|
||||
## 文章列表
|
||||
|
||||
| # | 标题 | 发布时间 | 原文链接 |
|
||||
|---|------|----------|----------|
|
||||
| 1 | [钨合金烧结全流程数字化改造:一个硬核项目怎么帮客户省下真金白银](钨合金烧结全流程数字化改造:一个硬核项目怎么帮客户省下真金白银.md) | 2026年7月28日 11:29 | https://mp.weixin.qq.com/s/9TidaZKDZzsGWmbAc2xYlA |
|
||||
|
||||
## 目录结构
|
||||
|
||||
- `*.md` — 每篇文章的 Markdown 正文(含图片引用)
|
||||
- `raw/*.html` — 每篇文章的原始 HTML
|
||||
- `assets/` — 文章内嵌图片
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
# 钨合金烧结全流程数字化改造:一个硬核项目怎么帮客户省下真金白银
|
||||
|
||||
> **公众号**:工业AI观察员
|
||||
> **发布时间**:2026年7月28日 11:29
|
||||
> **原文链接**:https://mp.weixin.qq.com/s/9TidaZKDZzsGWmbAc2xYlA
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
💡 宏观大势
|
||||
|
||||
钨合金烧结,这个听起来冷门的领域,其实藏着工业自动化的硬骨头。过去三年,我亲眼看着这个行业从“老师傅靠手感”向“数据驱动”转型,但真正跑通全流程数字化的,不超过两成。
|
||||
|
||||
2026年工信部发布的《工业AI工艺优化示范项目》名单里,32个标杆项目平均良品率提升6.2%、能耗降低12.8%。钨合金烧结作为难熔金属加工的关键环节,能耗占整个生产成本的**40%以上**,良品率波动常超过**5%**。政策红利来了,但企业主们最关心的还是:投下去的钱,多久能回本?
|
||||
|
||||
更关键的是,宁德时代宜宾工厂的“AI工艺大脑”已经证明,AI闭环控制能让电芯一致性提升40%。但钨合金烧结的难度不在精度,而在温度、压力、气氛的耦合控制——一个参数跳变,整炉料就报废。这不是锦上添花,是雪中送炭。
|
||||
|
||||

|
||||
|
||||
🔧 科技加持
|
||||
|
||||
当年在西门子做V90伺服系统时,我就意识到:工艺参数优化不能靠PID硬扛,得用模型预测。钨合金烧结的难点在于,烧结温度高达**2500℃**,炉内气氛(氢气或真空)对致密度影响极大,传统做法是操作员盯着仪表手动调。
|
||||
|
||||
我们用的方案是OPC UA实时采集300+个传感器数据,包括炉膛温度、压力、氢气流量、推杆速度等。数据流通过边缘网关清洗后,送入部署在地平线征程J6 Edge芯片上的强化学习模型。这个芯片功耗仅12W,支持实时推理100+参数,适配西门子S7-1200和罗克韦尔ControlLogix两大主流PLC。
|
||||
|
||||
核心逻辑是:模型根据当前炉况(温度梯度、压力波动),自动推荐下一段烧结曲线(升温速率、保温时间、冷却速率)。如果检测到温度偏差超过**5℃**,模型会在**200ms**内给出补偿指令,比人工响应快**50倍**。这不是炫技,是让“老师傅”变成“AI搭档”。
|
||||
|
||||

|
||||
|
||||
🏭 案例实战
|
||||
|
||||
去年我带队做的湖南某钨业公司项目,堪称“从坑里爬出来的样板”。客户是硬质合金龙头,年产能**8000吨**,烧结工序能耗占全厂**35%**,良品率长期卡在**93%**。领导拍桌子说:“再提不上去,明年订单全丢给江西那帮人。”
|
||||
|
||||
开干后发现,真正的坑不在技术,而在数据。客户现场有**12台**老式烧结炉,PLC是西门子S7-200,连以太网口都没有。我们被迫加装OPC UA网关,用Modbus RTU转OPC UA,调试过程那叫一个酸爽——信号干扰导致数据丢包率一度高达**15%**。博世那个项目上我吃过亏,这次专门带了屏蔽双绞线和隔离器,总算把丢包率压到**0.3%**。
|
||||
|
||||
上线后第一个月,良品率从**93%**爬升到**96.2%**,但能耗反而涨了**2%**。团队一查,发现模型为了保良品率,把保温时间拉长了。我们紧急加入能耗惩罚因子,重新训练模型。第二月,良品率稳定在**97.1%**,能耗下降**11.5%**。客户算了一笔账:每年省下返工成本**1800万**,电费省**320万**。项目投资**500万**,**9个月**回本。
|
||||
|
||||

|
||||
|
||||
⚡ 痛点直击
|
||||
|
||||
你肯定遇到过这种情况:烧结炉刚升温到**1800℃**,操作员一看温度曲线有点抖,立马手动降功率。结果整炉料致密度不够,报废。技术主管开会骂娘,操作员委屈:“我干了20年,还能错?”
|
||||
|
||||
这就是钨合金烧结最扎心的痛点——老师傅的经验不可复制,年轻工程师又不敢乱动。我见过一个厂,**6台**炉子,每台配**2个**操作员,三班倒,光人工成本一年就**300多万**。更崩溃的是,同一批料,不同炉子烧出来性能差**5%**。客户吐槽:“我们不是在造硬质合金,是在养祖宗。”
|
||||
|
||||
还有更隐蔽的坑:氢气泄露。烧结炉的氢气流量一旦失控,轻则炉管爆裂,重则爆炸。传统方案靠人巡检,但人总有走神的时候。AI方案的好处是,通过压力传感器+流量计联动,一旦检测到流量异常,**1秒内**自动切断氢气并报警。这不是省钱的问题,是救命。
|
||||
|
||||

|
||||
|
||||
🎯 解决方案
|
||||
|
||||
别急着上AI,先做好这三步,否则投钱就打水漂。
|
||||
|
||||
第一步:数据清洗是地基。当年西门子V20变频器项目上,我吃过数据脏的亏。钨合金烧结的数据,温度、压力、流量采样频率不同,必须统一时间戳。建议用OPC UA采集,采样频率**1Hz**,数据存时序数据库如InfluxDB。清洗规则:剔除**±3σ**之外的异常值,用线性插值填补空缺。这一步花不了多少钱,但能省掉后面80%的调试时间。
|
||||
|
||||
第二步:选对硬件。别迷信大厂,国产工业AI芯片地平线征程J6 Edge足够用。功耗**12W**,支持**100+**参数实时推理,适配主流PLC协议。如果炉子老旧(像S7-200),加个OPC UA网关,成本**2000元**一台。边缘计算设备预算控制在**5万元**以内,多了就是浪费。
|
||||
|
||||
第三步:模型要轻量。别搞深度学习那套花架子,用强化学习(DQN或PPO)就行。核心逻辑:定义状态(当前温度、压力、流量)、动作(升温速率、保温时间)、奖励(良品率+能耗加权)。训练数据用历史**3个月**数据,模拟**5000炉**次,收敛后部署。注意加入安全约束:温度不准超过**2600℃**,压力不准低于**0.5MPa**。
|
||||
|
||||
最后,一定要做A/B测试。选**2台**炉子跑AI,**2台**跑传统方案,对比**1个月**。客户看到数据才肯签字。我那个项目,就是靠A/B测试数据,让客户技术总监心服口服。
|
||||
|
||||
📝 写在最后
|
||||
|
||||
钨合金烧结的数字化改造,不是技术问题,是信任问题。一旦跑通,节省的真金白银会自动说话。你们厂里目前烧结良品率多少?卡在哪个参数上?评论区聊聊,我帮你把把关。关注后回复「烧结」,免费领取《钨合金烧结AI改造自检清单》和《边缘AI选型成本核算表》。建议转给你公司的生产负责人和采购主管,他们一定也需要这份实战分析。
|
||||
|
||||
✦
|
||||
|
||||
关注「工业AI观察员」,回复「改造」获取《数字化改造方案框架》。
|
||||
|
||||
---
|
||||
|
||||
*本文由 WeChat 公众号「工业AI观察员」发布,抓取自原文。*
|
||||
Reference in New Issue
Block a user