# 任务编排界面 · Workflow 编排设计说明

> 设计日期：2026-06-01  
> 范围：任务编排页 → **Workflow 编排** Tab  
> 状态：MVP 已实现，本文档与当前控件库参数定义同步

---

## 1. 设计目标

为调度人员提供可视化的任务流程编排能力：通过拖拽控件、连线、配置参数，完成 AGV 调度流程的设计，无需编写脚本。

设计原则：

- **所见即所得**：画布即流程，节点即步骤
- **分组清晰**：控件按业务语义分类，左侧一行简介即可理解用途
- **分支可辨**：涉及「成功/失败」「正常/超时」的节点，出口必须在视觉上可区分
- **参数克制**：只展示当前步骤需要的字段，随选项变化动态显隐

---

## 2. 页面结构

```
┌──────────────────────────────────────────────────────────────────────────┐
│ 顶栏：流程名称 · 校验 · [删除选中][加载示例][导入][导出][清除画布] │
├────────────┬─────────────────────────────────────────┬───────────────────┤
│ 流程库     │              流程画布                     │   参数配置         │
│ 控件库     │                                         │                   │
│  搜索      │     节点 · 连线 · 缩放 · 平移             │  选中节点的属性     │
│  分组列表  │                                         │                   │
└────────────┴─────────────────────────────────────────┴───────────────────┘
```

| 区域 | 宽度建议 | 职责 |
|------|----------|------|
| 流程库 | 约 220px（与控件库同列） | 管理多个已保存流程，新建 / 保存 / 切换 / 复制 / 删除 |
| 控件库 | 约 220px（流程库下方） | 浏览、搜索、拖拽控件到画布 |
| 流程画布 | 自适应 | 编排主工作区 |
| 参数配置 | 约 280px | 编辑当前选中节点或连线的属性 |

顶栏 **校验状态** 对流程完整性做即时提示（如缺少开始节点、分支未连接等），不打断编辑。

---

## 3. 控件库

### 3.1 分组与配色

| 分组 | 主色 | 控件 |
|------|------|------|
| 流程入口 | 绿色 | 开始、结束 |
| 调度动作 | 橙色 | 前往站点、执行动作、分配车辆 |
| 流程逻辑 | 紫色 | 条件分支、延时事件 |
| 事件与信号 | 蓝色 | 等待事件、通知事件 |
| 协议控制 | 青色 | 设备控制 |

### 3.2 控件卡片信息层级

每个控件卡片包含：

1. **名称**（主标题，一行）
2. **简介**（副标题，**仅一行**，超出省略）

不在控件库中展示长说明或技术协议细节；详细配置留给右侧参数面板。

### 3.3 控件简介一览

| 控件 | 类型编码 | 简介 |
|------|----------|------|
| 开始 | `start` | 流程起点（全局唯一） |
| 结束 | `end` | 流程终点 |
| 前往站点 | `gotoSite` | AGV 移动到目标站点 |
| 执行动作 | `executeAction` | 下发 VDA5050 单条动作指令 |
| 分配车辆 | `assignCar` | 为任务指定车辆或车型 |
| 条件分支 | `branch` | 条件成立与否走不同分支 |
| 延时事件 | `delay` | 等待指定秒数后继续 |
| 等待事件 | `waitEvent` | 等待事件触发，超时走另一路 |
| 通知事件 | `notify` | 调用接口推送 JSON 通知 |
| 设备控制 | `deviceControl` | Modbus 读等待或写输出 |

---

## 4. 画布节点

### 4.1 通用节点样式

- 白底圆角卡片，边框颜色与所属分组一致
- 左侧：一个输入连接点
- 右侧：一个输出连接点（结束节点无输出）
- 内容区：控件类型（小字）+ 用户自定义显示名（加粗）

选中态：蓝色外发光，与未选中节点明确区分。

### 4.2 双出口节点

**适用控件**：条件分支、等待事件

| 元素 | 规范 |
|------|------|
| 节点高度 | 高于普通节点，预留双出口空间 |
| 出口数量 | 右侧上下两个连接点 |
| 出口布局 | 标签与连接点同一行（flex 行布局），文字在左、Handle 在右，保证对齐 |
| 出口标签 | 节点内右侧展示文字标签 |
| 连线颜色 | 上方出口路径偏绿，下方出口路径偏红 |
| 连线标签 | 连线上标注出口含义 |

**出口标识（sourceHandle）**：

| 控件 | 上方出口 | 下方出口 |
|------|----------|----------|
| 条件分支 | `success`（成立） | `fail`（不成立） |
| 等待事件 | `triggered`（正常执行） | `timeout`（超时执行） |

---

## 5. 控件参数设计

### 5.1 前往站点

**用途**：指令 AGV 移动到指定站点。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 目标站点 | 文本 | 是 | 站点编号，如 S001 |

---

### 5.2 执行动作

**用途**：下发 VDA5050 协议中的单条 Action 指令（`actionId` 由运行时生成，不在编排界面配置）。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 动作类型 | 下拉 | 是 | 取货 / 放货 / 充电，默认「取货」 |
| 动作参数 | 多行文本 | — | JSON 数组，可选 |

**动作类型与导出值对照**：

| 界面选项 | 导出值 `actionType` |
|----------|---------------------|
| 取货 | `pick` |
| 放货 | `drop` |
| 充电 | `startCharging` |

**动作参数示例**：

```json
[{"key":"loadType","value":"pallet"}]
```

---

### 5.3 分配车辆

**用途**：为任务指定执行车辆或车辆类型。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 分配策略 | 单选 | 是 | 指定车辆 / 指定类型 |
| 车辆编号 | 文本 | 是* | 选择「指定车辆」时显示，如 C01 |
| 车辆类型 | 文本 | 是* | 选择「指定类型」时显示，如车型类名 |

\* 随分配策略切换，只展示对应字段。

**分配策略与导出值对照**：

| 界面选项 | 导出值 `strategy` |
|----------|-------------------|
| 指定车辆 | `fixed` |
| 指定类型 | `byType` |

---

### 5.4 等待事件

**用途**：阻塞流程，直到指定事件发生或超时。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 事件名 | 文本 | 是 | 如 site.arrived |
| 超时(秒) | 数字 | — | 默认 60；填 0 表示不超时 |

**双出口语义**：

| 出口位置 | 标签 | 含义 |
|----------|------|------|
| 上方（绿） | 正常执行 | 在超时前收到事件，沿此路径继续 |
| 下方（红） | 超时执行 | 超过设定秒数仍未收到事件，沿此路径处理 |

**设计建议用法**：

- 正常执行 → 后续业务步骤（如送料完成通知）
- 超时执行 → 告警、重试、人工介入或结束流程

**校验提示**：设置了超时时间但未连接「超时执行」出口时，顶栏给出警告。

---

### 5.5 通知事件

**用途**：向外部系统推送通知，不阻塞主流程。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 接口地址 | 文本 | 是 | HTTP 接口 URL |
| 数据配置 | 多行文本 | 是 | JSON 格式请求体 |
| 消息 | 多行文本 | 否 | 运维界面可见的提示文案 |
| 级别 | 单选 | — | 信息 / 警告 / 错误，默认信息 |

**数据配置示例**：

```json
{
  "event": "delivery.complete",
  "payload": {
    "missionId": "M01",
    "status": "ok"
  }
}
```

---

### 5.6 条件分支

**用途**：根据条件表达式决定流程走向。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 条件表达式 | 多行文本 | 是 | 如 batterySoc > 0.2 |

**双出口语义**：

| 出口位置 | 标签 | 含义 |
|----------|------|------|
| 上方（绿） | 成立 | 表达式结果为 true |
| 下方（红） | 不成立 | 表达式结果为 false |

---

### 5.7 延时事件

**用途**：阻塞流程指定时长后继续执行，单出口。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 延时(秒) | 数字 | 是 | 默认 5 |

---

### 5.8 设备控制

**用途**：通过 Modbus 协议读取设备信号（等待触发）或向设备写入输出值。

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 读写模式 | 下拉 | 是 | 读信号（接收）/ 写信号（输出），默认读 |
| 协议类型 | 下拉 | 是 | Modbus TCP / Modbus RTU |
| 连接地址 | 文本 | 是 | TCP 如 `10.0.2.30:502`；RTU 如 `COM3,9600,8,N,1` |
| 寄存器地址 | 数字 | 是 | 如 40001 或 0（视驱动约定） |
| 数据类型 | 下拉 | 是 | 布尔 / Int16 / UInt16 / Int32 / Float32 |

**读模式额外字段**（选择「读信号」时显示）：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 触发条件 | 下拉 | 是 | 等于 / 不等于 / 大于 / 大于等于 / 小于 / 小于等于 / 值发生变化 |
| 轮询间隔 (ms) | 数字 | — | 默认 500 |

**写模式额外字段**（选择「写信号」时显示）：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 写入值 | 文本 | 是 | 如 `1` / `true` / `100` |
| 写入模式 | 下拉 | — | 持续写入 / 脉冲（写入后复位），默认持续写入 |
| 脉冲宽度 (ms) | 数字 | — | 写入模式为脉冲时有效，默认 500 |
| 写入后回读校验 | 开关 | — | 默认开启 |

---

## 6. 参数面板交互规范

| 规则 | 说明 |
|------|------|
| 标签置顶 | 字段名在输入框上方，必填项标红星 |
| 条件显隐 | 仅展示与当前选项相关的字段；切换选项时清理已隐藏字段的值 |
| 布尔控件 | 使用开关，显示「是 / 否」，禁止白字白底不可见 |
| 空状态 | 未选中节点时提示「选中画布上的节点以编辑参数」 |
| 显示名称 | 所有节点均可编辑显示名，独立于控件类型 |
| 无参节点 | 开始、结束节点无可配置业务参数，仅可改显示名 |
| 节点删除 | 参数面板底部提供「删除节点」按钮，二次确认后删除 |
| 连线选中 | 点击画布上的连线，右侧面板切换为连线详情（起点、终点、出口标签） |

---

## 6.1 删除节点与连线

### 选中方式

| 对象 | 操作 |
|------|------|
| 节点 | 点击节点，右侧显示参数面板 |
| 连线 | 点击连线，右侧显示连线详情；选中态连线加粗高亮 |

点击画布空白处取消选中。

### 删除方式

| 方式 | 节点 | 连线 | 说明 |
|------|------|------|------|
| 快捷键 | ✓ | ✓ | 选中后按 **Delete** 或 **Backspace**，立即删除，无需确认 |
| 顶栏「删除选中」 | ✓ | ✓ | 删除节点需二次确认；仅删连线时直接删除 |
| 参数面板「删除节点」 | ✓ | — | 二次确认后删除，关联连线一并移除 |
| 连线面板「删除连线」 | — | ✓ | 直接删除，无需确认 |

**规则**：

- 删除节点时，与该节点相连的所有连线自动移除
- 删除连线不影响节点本身
- 删除后右侧面板回到空状态；草稿自动保存

---

## 7. 顶栏操作与校验

### 7.1 顶栏操作

| 操作 | 行为 |
|------|------|
| 流程名称 | 可编辑，标识当前流程 |
| 删除选中 | 删除当前选中的节点或连线；无选中时按钮禁用 |
| 加载示例 | 将内置「A区送料示例」加入流程库并切换编辑（不覆盖其他已保存流程） |
| 导入 | 从 JSON 文件导入流程，追加到**当前会话**流程库（支持单流程与流程库合集，见 11.5） |
| 导出 | 下载当前会话流程库内**全部流程**的合集 JSON（见 11.4 节） |
| 清除画布 | 二次确认后清空**当前流程**画布上的节点与连线 |

流程库仅保存在**当前浏览器会话内存**中（刷新页面会恢复为内置示例，除非通过「导入」重新载入）。编辑过程中会自动同步到内存流程库（约 400ms 防抖），便于切换多个流程；**持久化请使用顶栏「导出」下载 JSON 文件**。

### 7.1.1 流程库（多流程管理）

左侧面板顶部为 **流程库** 列表：

| 操作 | 说明 |
|------|------|
| + 新建 | 创建空白流程并切换为当前编辑对象 |
| 保存 | 将当前流程写入内存流程库（存在校验错误时阻止保存并提示） |
| 点击列表项 | 切换到该流程（切换前自动同步当前编辑到内存） |
| 复制 | 复制当前选中流程为「xxx（副本）」 |
| 删除 | 从库中移除该流程（至少保留 1 个） |

每个列表项显示流程名称与节点数量；当前编辑项高亮。

### 7.2 流程校验

顶栏实时展示校验结果：全部通过时显示绿色「校验通过」；存在问题则列出错误与警告，**不阻断编辑**。

| 级别 | 条件 |
|------|------|
| 错误 | 缺少「开始」节点 |
| 错误 | 存在多个「开始」节点 |
| 错误 | 「开始」节点未连接下游 |
| 警告 | 未添加「结束」节点 |
| 警告 | 「结束」节点没有上游连线 |
| 警告 | 中间节点处于孤立状态（无上游连线） |
| 警告 | 「等待事件」未连接「正常执行」出口 |
| 警告 | 「等待事件」设置了超时但未连接「超时执行」出口 |

保存时：若存在错误则阻止并提示；若仅有警告则允许保存并弹出警告条数。

---

## 8. 示例流程（A区送料）

用于演示双出口与通知节点的典型编排：

```
开始
  → 分配车辆（指定车辆 C01）
  → 前往站点（S001）
  → 执行动作（取货）
  → 等待到达（事件 site.arrived，超时 120 秒）
       ├─ 正常执行 → 送料完成通知 → 结束
       └─ 超时执行 → 等待超时告警 → 结束
```

---

## 9. 视觉与主题注意事项

任务编排编辑器内部面板（控件库、画布区、参数配置）采用 **白底 + 深色文字**，与平台全局深色/紫色主题隔离，避免：

- 表单文字不可见
- 开关关闭态「否」与背景融为一体
- 下拉、输入框在浅色卡片上显示异常

节点、连线、标签颜色以 **绿 = 正向/成功/正常**、**红 = 异常/超时/不成立** 为语义约定，全页保持一致。

---

## 10. 后续扩展方向（设计预留）

- 流程版本管理与发布
- 与 Mission 实例管理的联动（编排 → 实例化）
- 控件库随插件任务类型扩展
- 画布缩略图、对齐辅助线、撤销重做
- 运行时执行态在画布上的高亮反馈

---

## 11. 导出的 JSON 数据示例

- **导出**：下载流程库合集 `workflow-catalog.json`，内含当前会话全部流程（结构见 11.4）。

### 11.1 数据结构说明

| 顶层字段 | 含义 |
|----------|------|
| `id` | 流程唯一标识 |
| `name` | 流程名称 |
| `version` | 流程版本号 |
| `viewport` | 画布视口位置与缩放 |
| `nodes` | 节点列表 |
| `edges` | 连线列表 |

**节点（nodes）** 每条包含：

| 字段 | 含义 |
|------|------|
| `id` | 节点 ID |
| `type` | 控件类型（如 start、assignCar、waitEvent） |
| `label` | 显示名称 |
| `position` | 画布坐标 |
| `params` | 该节点的参数配置 |
| `nextId` | 下一节点 ID（单出口 / 条件成立 / 正常执行）；结束节点为 `null` |
| `failNextId` | 条件不成立时的下一节点 ID（仅条件分支） |
| `timeoutNextId` | 超时执行时的下一节点 ID（仅等待事件） |

**连线（edges）** 每条包含：

| 字段 | 含义 |
|------|------|
| `id` | 连线 ID |
| `source` | 起始节点 ID |
| `target` | 目标节点 ID |
| `sourceHandle` | 出口标识（双出口节点必填，如正常执行、超时执行） |
| `label` | 连线标签（可选） |

### 11.2 完整示例（A区送料）

对应第 8 节示例流程的导出内容：

```json
{
  "id": "wf-demo-001",
  "name": "A区送料示例",
  "version": 1,
  "viewport": { "x": 0, "y": 0, "zoom": 1 },
  "nodes": [
    {
      "id": "n1",
      "type": "start",
      "label": "开始",
      "position": { "x": 60, "y": 200 },
      "params": {},
      "nextId": "n2"
    },
    {
      "id": "n2",
      "type": "assignCar",
      "label": "分配车辆",
      "position": { "x": 240, "y": 180 },
      "params": {
        "strategy": "fixed",
        "carId": "C01"
      },
      "nextId": "n3"
    },
    {
      "id": "n3",
      "type": "gotoSite",
      "label": "前往入库点",
      "position": { "x": 460, "y": 180 },
      "params": {
        "siteId": "S001"
      },
      "nextId": "n4"
    },
    {
      "id": "n4",
      "type": "executeAction",
      "label": "取货",
      "position": { "x": 680, "y": 180 },
      "params": {
        "actionType": "pick",
        "actionParameters": "[{\"key\":\"loadType\",\"value\":\"pallet\"}]"
      },
      "nextId": "n5"
    },
    {
      "id": "n5",
      "type": "waitEvent",
      "label": "等待到达",
      "position": { "x": 900, "y": 180 },
      "params": {
        "eventName": "site.arrived",
        "timeoutSec": 120
      },
      "nextId": "n6",
      "timeoutNextId": "n8"
    },
    {
      "id": "n6",
      "type": "notify",
      "label": "送料完成通知",
      "position": { "x": 1120, "y": 180 },
      "params": {
        "apiUrl": "http://127.0.0.1:8080/api/hook/notify",
        "dataConfig": "{\"event\":\"delivery.complete\",\"message\":\"送料完成\"}",
        "message": "送料完成",
        "level": "info"
      },
      "nextId": "n7"
    },
    {
      "id": "n7",
      "type": "end",
      "label": "结束",
      "position": { "x": 1340, "y": 200 },
      "params": {},
      "nextId": null
    },
    {
      "id": "n8",
      "type": "notify",
      "label": "等待超时告警",
      "position": { "x": 1120, "y": 320 },
      "params": {
        "apiUrl": "http://127.0.0.1:8080/api/hook/notify",
        "dataConfig": "{\"event\":\"wait.timeout\",\"message\":\"等待到达超时\"}",
        "message": "等待到达超时",
        "level": "warn"
      },
      "nextId": "n7"
    }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2" },
    { "id": "e2", "source": "n2", "target": "n3" },
    { "id": "e3", "source": "n3", "target": "n4" },
    { "id": "e4", "source": "n4", "target": "n5" },
    {
      "id": "e5",
      "source": "n5",
      "target": "n6",
      "sourceHandle": "triggered",
      "label": "正常执行"
    },
    {
      "id": "e5t",
      "source": "n5",
      "target": "n8",
      "sourceHandle": "timeout",
      "label": "超时执行"
    },
    { "id": "e6", "source": "n6", "target": "n7" },
    { "id": "e8", "source": "n8", "target": "n7" }
  ]
}
```

### 11.3 阅读要点

- 每个节点导出时会根据连线自动写入 **`nextId`**，执行引擎可直接按节点跳转，无需再解析 `edges`。
- **等待事件**：`nextId` = 正常执行下游，`timeoutNextId` = 超时执行下游。
- **条件分支**：`nextId` = 条件成立下游，`failNextId` = 条件不成立下游。
- **结束节点**：`nextId` 为 `null`。
- `edges` 仍保留，便于还原画布布局；与 `nextId` 字段互为补充。
- **等待事件** 的两条出线通过 `sourceHandle` 区分：`triggered` 表示正常执行，`timeout` 表示超时执行。
- **条件分支** 的双出口同理：`success` 表示条件成立，`fail` 表示条件不成立。
- 节点的 `params` 只保存当前生效的字段；例如「分配车辆」在指定车辆策略下仅含 `strategy` 与 `carId`，不会出现无关的空字段。
- **执行动作** 的 `actionType` 存 VDA5050 枚举值（`pick` / `drop` / `startCharging`），界面下拉选项与之对应。
- `dataConfig`、`actionParameters` 等字段在 JSON 中以 **字符串** 形式存储 JSON 内容，导入时需按文本解析。

### 11.4 流程库合集导出（导出）

点击 **「导出」** 后，下载 `workflow-catalog.json`，包含流程库内每一个流程的完整定义（均已附带 `nextId`）。

```json
{
  "catalogVersion": 3,
  "exportedAt": "2026-06-01T12:00:00.000Z",
  "workflows": [
    {
      "id": "wf-demo-001",
      "name": "A区送料示例",
      "version": 1,
      "viewport": { "x": 0, "y": 0, "zoom": 1 },
      "nodes": [ "..." ],
      "edges": [ "..." ]
    },
    {
      "id": "wf-1730000000000-1",
      "name": "未命名流程",
      "version": 1,
      "nodes": [],
      "edges": []
    }
  ]
}
```

| 字段 | 含义 |
|------|------|
| `catalogVersion` | 流程库 schema 版本，与 localStorage 中一致 |
| `exportedAt` | 导出时间（ISO 8601） |
| `workflows` | 流程数组，每项结构与单流程导出相同 |

执行引擎或后端可遍历 `workflows` 加载多套模板；单流程导入可使用单流程 JSON 文件（结构见 11.2）。

### 11.5 导入 JSON

点击顶栏 **「导入」**，选择本地 `.json` 文件。系统自动识别以下格式并**追加**到流程库（不覆盖已有流程）：

| 格式 | 识别条件 | 示例来源 |
|------|----------|----------|
| 单流程 | 顶层含 `nodes`、`edges` | 单流程 JSON 文件 |
| 流程库合集 | 顶层含 `workflows` 数组 | 「导出」生成的 `workflow-catalog.json` |
| 旧版 draft | 顶层含 `workflow` 对象 | 早期单流程草稿格式 |

**导入规则**：

- 导入前会先保存当前正在编辑的流程
- 流程 `id` 与库内已有重复时，自动分配新 ID
- 节点上的 `nextId` / `failNextId` / `timeoutNextId` 会被忽略，画布连线以 `edges` 为准
- 导入完成后切换到**最后一个**导入的流程进行编辑
- 参数会按当前控件库 schema 规范化（补默认值、剔除无效字段）
- 存在校验警告时不阻断导入，顶栏会提示警告条数
