zh-aiot-workflow-canvas

AIoT 工作流画布组件库:基于 Vue Flow 的 Vue3 可视化编排画布,可独立调试并发布到 npm,供 zh-aiot-webui 等项目引用
2,702
Install
npm install zh-aiot-workflow-canvas
Latest Version:0.7.2
Vue:3
License:MIT
Last Updated:Sep 20, 2026
Links: npm
Maintainer: shihh

zh-aiot-workflow-canvas

AIoT 工作流画布组件库 —— 基于 Vue 3 + TypeScript + Vue Flow(@vue-flow/core) 的可视化编排画布,对标 Dify Workflow Builder 的交互层。

作为独立包开发调试,可发布到 npm 公共仓库后供 zh-aiot-webui(Vue 3 + Vite + pnpm)等项目引用。

引擎只做节点/连线/交互;业务节点种类、工具列表、导出至 LiteFlow graphJson 均由本库在画布之上按需扩展。


一、目录结构

zh-aiot-workflow-canvas/
├── package.json          # 包名 zh-aiot-workflow-canvas
├── vite.config.ts        # Vite 库模式构建(ESM + UMD + d.ts + style.css)
├── tsconfig.json
├── index.html            # 本地调试入口(开发服务器根页面)
├── src/                  # 库源码(打包只含此目录)
│   ├── index.ts          # 统一出口 + Vue 插件 install + 全局样式引入
│   ├── types.ts          # 业务类型:WorkflowNode / WorkflowNodeData / Tool 等
│   ├── presets.ts        # defaultNodeTypes + buildWorkflowNode() 便捷工厂
│   ├── kinds.ts          # 节点种类元数据(开始/条件分支/循环/迭代/工具调用/结束/注释)
│   ├── tools.ts          # 工具列表 mock(HTTP / Redis / MQ / 设备能力等)
│   ├── params.ts         # 节点参数与变量引用模型(输出/上游/visibleWhen)
│   ├── liteflow.ts       # 扁平画布 → LiteFlow graphJson 导出器
│   ├── history.ts        # 撤销/重做历史栈(past/future + 连续编辑合并)
│   ├── api.ts            # provide/inject key(WorkflowCanvasApi)
│   └── components/
│       ├── WorkflowCanvas.vue   # 画布封装:v-model / 模式 / 快捷键 / 导出
│       ├── WorkflowNode.vue     # Dify 式节点卡片(图标+标题+chevron+变量行)
│       ├── NodeDrawer.vue       # Dify 式右侧编辑面板(400px,点节点打开)
│       ├── VarChip.vue          # 变量引用 chip({x} 图标 + 节点名/字段名,隐藏 {{}} 代码)
│       ├── AnnotationNode.vue   # 注释节点(无端口,双击编辑)
│       └── WorkflowToolbar.vue  # Dify 式左侧操作栏(节点/注释/指针/手型)
└── playground/           # 独立调试沙盒(不参与打包)
    ├── main.ts
    └── App.vue           # 示例 AIoT 链路 + 导出验证

二、本地调试

pnpm install        # 安装依赖
pnpm dev            # 启动 playground(默认 http://localhost:5174)
pnpm type-check     # vue-tsc 类型检查
pnpm build          # 库构建,产物输出 dist/

调试要点:

  • playground 从 ../src 直接引源码,改动即时热更新,与「消费方引用构建产物」隔离;
  • 新增节点组件 → 在 src/presets.ts 的 defaultNodeTypes 注册 → playground 直接使用验证;
  • 节点 data 遵循 WorkflowNodeData(kind / label / sublabel / liteflow / tool 等),运行态通过改 data.status 即可回显「待运行/运行中/成功/失败」。

三、构建产物(dist/)

文件 说明
index.js ESM,供 Vite 工程按 import 引入
index.umd.cjs UMD,浏览器 <script> 直引场景
index.d.ts 类型声明,随包发布
style.css 已内联 @vue-flow/core 基础样式 + 组件样式,消费方只引这一份 CSS

vue、@vue-flow/core、@iconify/vue 均作为外部依赖不打进产物,由消费方自行安装,保证单一实例。

图标说明:工具 / 节点的 icon 形如 ri:router-line 时走 Iconify 渲染。库本身不内置图标数据, 消费方需保证图标可用 —— 外网环境可直接用 @iconify/vue 的在线 API;内网环境请用 addIcon() / addCollection() 离线注册(如配合已安装的 @iconify-icons/ri 按需导入), 否则图标位置为空白(不影响颜色与其余渲染)。非 Iconify 形态(emoji / 单字)按文字渲染。

四、消费方用法(以 zh-aiot-webui 为例)

安装

# 在 zh-aiot-webui 下添加依赖(需 Vue 3.5+)
pnpm add zh-aiot-workflow-canvas
# peerDependency:图标组件(^4.1.0 || ^5.0.0)
pnpm add @iconify/vue
# 可选:如需背景网格 / 缩放控制 / 小地图等装饰组件(独立子包,按需装)
pnpm add @vue-flow/background @vue-flow/controls @vue-flow/minimap

基础画布

<script setup>
import { ref } from 'vue'
import { WorkflowCanvas } from 'zh-aiot-workflow-canvas'
import 'zh-aiot-workflow-canvas/style.css'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'

const nodes = ref([])
const edges = ref([])
</script>

<template>
  <WorkflowCanvas v-model:nodes="nodes" v-model:edges="edges" height="640px">
    <Background />
    <Controls />
  </WorkflowCanvas>
</template>

受控工具 + 自定义工具加载

<script setup>
import { ref } from 'vue'
import { WorkflowCanvas } from 'zh-aiot-workflow-canvas'
import 'zh-aiot-workflow-canvas/style.css'

const tool = ref('select') // 'select' | 'hand'

async function loadTools() {
  const res = await fetch('/api/zh-aiot/workflow/tools')
  return await res.json()
}
</script>

<template>
  <WorkflowCanvas
    v-model:nodes="nodes"
    v-model:edges="edges"
    v-model:tool="tool"
    :tool-loader="loadTools"
    height="640px"
  />
</template>

导出 LiteFlow graphJson(zh-aiot-server 可解析)

<script setup>
import { ref } from 'vue'
import { WorkflowCanvas, serializeLiteflowGraph } from 'zh-aiot-workflow-canvas'

const canvasRef = ref()

function handleExport() {
  const result = canvasRef.value.exportLiteflow()
  if (result.ok) {
    console.log(serializeLiteflowGraph(result.graph))
    // 或 POST 到后端
  } else {
    console.error(result.message)
  }
}
</script>

<template>
  <WorkflowCanvas ref="canvasRef" v-model:nodes="nodes" v-model:edges="edges" />
  <button @click="handleExport">导出 LiteFlow</button>
</template>

也可以全量注册:

import ZhWorkflowCanvas from 'zh-aiot-workflow-canvas'
app.use(ZhWorkflowCanvas) // 全局可用 <WorkflowCanvas />

五、组件 API

<WorkflowCanvas> Props

属性 类型 默认值 说明
nodes Node[] [] 受控节点列表(支持 v-model:nodes)
edges Edge[] [] 受控连线列表(支持 v-model:edges)
nodeTypes NodeTypesObject {} 扩展/覆盖节点类型
height number | string '100%' 画布高度
fitView boolean true 初始自适应视图
id string 自动生成 多实例时区分 store
tool 'select' | 'hand' 'select' 指针/手型(受控)
toolLoader ToolListLoader defaultToolLoader 工具列表加载器;默认提供 HTTP、Redis、MQTT,可由内部接口替换

<WorkflowCanvas> Slots

插槽 作用域参数 说明
default — Vue Flow 的背景、控制器、缩略图等装饰内容
panel { node, close } 接管当前节点的右侧编辑面板;未传时仍渲染内置 NodePanel

panel 只接管渲染,面板状态仍由画布管理:节点单击和新增节点会打开面板,Esc、画布空白单击及 close() 会关闭面板,切换节点会更新 node。宿主可对业务节点使用自定义面板,并让其余节点继续使用包内导出的 NodePanel:

<script setup lang="ts">
import {
  NodePanel,
  WorkflowCanvas,
} from 'zh-aiot-workflow-canvas'
</script>

<template>
  <WorkflowCanvas v-model:nodes="nodes" v-model:edges="edges">
    <template #panel="{ node, close }">
      <DeviceNodePanel
        v-if="isDeviceNode(node)"
        :node="node"
        @close="close"
      />
      <NodePanel v-else :node="node" @close="close" />
    </template>
  </WorkflowCanvas>
</template>

插槽组件位于 WorkflowCanvas 的 provide 上下文内,可以在 DeviceNodePanel 自身的 setup 中直接 inject(WORKFLOW_CANVAS_API),再调用 updateNodeData(node.id, patch) 写回;因此 NodePanel 在插槽中也仍可使用工具选择、变量插入和输出变量等完整功能。

<WorkflowCanvas> 注入 API(内部组件通过 inject(WORKFLOW_CANVAS_API) 使用)

方法 说明
getTool() 当前指针/手型
setTool(tool) 切换模式
updateNodeData(id, patch) 更新节点 data:只覆盖 patch 出现的顶层键,对象/数组整段替换;纳入 undo/redo,并触发受控 update:nodes 回传
removeNode(id) 删除节点(连同挂载连线一并清理)
loadTools() 拉取工具列表
getNodeOutputs(id) 某节点的输出参数(供下游引用)
getUpstreamOutputs(id) 某节点可引用的上游节点输出分组(变量引用下拉数据源)
exportLiteflow() 导出 LiteFlow graphJson

<WorkflowCanvas> Expose

方法 说明
snapshot(): { nodes, edges } 清掉运行时 key 的节点/连线快照
exportLiteflow(): WorkflowLiteflowExport 导出 LiteFlow graphJson(含校验)
addNodeByKind({ kind, tool? }) 编程式添加节点
addAnnotation() 编程式添加注释节点
setTool(tool) 切换模式
removeNode(id) 删除节点 + 挂载连线
undo() / redo() 撤销 / 重做(增删节点、连线、拖拽、删除、抽屉内编辑均可回退)

自定义面板样式约定

  • 定位容器是画布根 .wf-canvas-root(position: relative)。
  • 面板建议使用 position: absolute; top: 0; right: 0; bottom: 0; width: min(400px, 100%); z-index: 35;内置工具栏 .wf-toolbar 的 z-index 为 30。
  • 画布外层保留 wf-panel-slide 进出场动画(transform + opacity,0.18s ease),单根节点的自定义面板可直接复用;宿主也可以在面板内部实现自己的动画。

快捷键(全局)

快捷键 功能
V 切换到指针模式
H 切换到手型模式
N 打开/收起「添加节点」面板
A 添加注释节点
Esc 收起节点面板
Delete / Backspace 删除选中节点/连线(可撤销)
⌘/Ctrl+Z 撤销(焦点在输入框时也可回退文本编辑)
⌘/Ctrl+⇧+Z(或 Ctrl+Y) 重做

字母/删除快捷键在焦点位于输入框时不拦截;撤销/重做任意焦点下均生效。抽屉内连续击键自动合并为一个撤销步。

内置节点类型(defaultNodeTypes)

type 组件 说明
workflow WorkflowNode.vue 通用工作流节点,自动按 data.kind 渲染不同形态与端口
annotation AnnotationNode.vue 注释节点(无端口、双击编辑)

六、节点种类(画布与导出)

kind 中文 端口特征 LiteFlow 后端类型
start 用户输入 仅 source 不导出(触发锚点,输入字段即 request.*)
timer 定时触发器 仅 source timer(config.cron)
condition 条件分支 source×2(真/假) if
loop 循环 source×2(body/done)、target×1(back) for(CONST)
iteration 迭代 source×2(body/done)、target×1(back) for(LIST)
tool 工具调用 有 source + target 不导出(类型由选定工具决定)
end 结束返回 仅 target return
annotation 注释 无端口 不导出

触发入口:两种触发方式互斥(同一流程二选一),由注册表 excludes 声明:

  • 用户输入(start):用户填写输入字段后触发,输入字段即下游可引用的 request.* 输出;不落库,仅作主链锚点;同画布区域最多一个;
  • 定时触发器(timer):按 Cron 表达式到点触发,导出为后端 timer 节点(config.cron 必填);同一流程可定义多个 —— 多个 timer 指向同一下游即可共用流程体,导出时各 timer 声明(含 cron)统一置于链首;可配置触发变量(名称 / 类型 / 值)。变量结构(名 + 类型)全流程定时器共用一份:任一定时器增删变量、改名、改类型会同步到所有定时器(改名联动重写全图引用);各定时器只为这套结构填写各自的值,新建定时器自动继承当前结构。导出为 config.payload.<变量>(值按类型转换),运行时服务端注入节点输出 data.payload,下游引用路径 context.node.<id>.data.payload.<变量>。

工具调用节点可在节点内展开选择工具,或在「添加节点」面板中点击「工具调用」后在右侧侧边列选择工具。

节点卡片与编辑面板(Dify 式)

画布节点卡片(WorkflowNode.vue):白底圆角卡片,仅做展示——

  • 头部:彩色图标块 + 节点标题 + 运行状态点 + › chevron;
  • 描述行:data.desc,最多 2 行截断;
  • 变量行:灰底圆角,label → value;含 {{nodeId.field}} 的引用不显示模板代码,而是渲染为变量 chip({x} 图标 + 节点名/字段名,主题蓝),普通文本与 chip 混排;
  • 条件分支节点:卡片上按 IF [变量chip] 运算符 [值chip] 逐行展示结构化条件;
  • 源口内嵌 +,hover 放大。

右侧编辑面板(NodeDrawer.vue,点击节点打开,400px):

  • 头部:图标 + 直接可编辑的节点标题输入框 + 删除节点 / 关闭按钮;
  • 描述区:备注输入,实时回显到节点卡片描述行;
  • 工具调用节点:工具分组下拉切换;
  • 用户输入节点(start):输入字段编辑(变量名 + 类型下拉 string/number/boolean/object/array/json + 删除 + 添加);
  • 定时触发器(timer):Cron 配置控件 —— 常用预设一键填充、秒/分/时/日/月/周六段分框编辑、表达式实时校验、下次执行时间预览(本地时区);导出时同样做 Cron 格式校验;另有触发变量编辑(名称 / 类型 / 值,值在流程内定义,导出为 config.payload,改名联动重写下游引用);
  • 条件分支节点(Dify 式 IF 结构化编辑器):每条条件 = 左值变量选择(上游输出 chip)+ 运算符下拉(等于/不等于/大于/包含/为空…12 种)+ 右值(字面量输入或 {x} 插入变量,失焦态显示 chip、聚焦态才编辑文本);支持「添加条件」、删除单条、多条件 AND/OR 逻辑切换。数据写入 config.condition.items 并同步生成 config.expression;
  • 参数区:label + 输入框 + {x} 变量按钮(内嵌输入框右上角),点击展开上游节点输出下拉,插入 {{nodeId.field}} 引用 token;
  • 底部输出变量折叠区:计数徽章 + 旋转 chevron,展开显示 name/type 行与引用语法提示。

节点参数与变量引用

  • 变量引用以 chip 呈现:画布卡片和面板里所有 {{nodeId.field}} 都经 toValueSegments() 解析为「节点名 / 字段名」chip(VarChip.vue),用户看不到模板代码;原始语法仅存于数据与悬浮提示;
  • 开始节点(用户输入):可编辑输入参数(名称 / 类型),输入参数即该节点的输出,供下游引用;
  • 工具节点:按工具 params 自动渲染参数编辑区(select / textarea / number / text),支持 visibleWhen 按取值动态显隐(如 redis 按数据类型展开不同输入);
  • 条件节点:结构化 IF 条件(ConditionItem { variable, op, value }),非旧版裸表达式文本框;导出时自动过滤未完成(左值为空)的条件项;
  • 所有节点:编辑面板参数输入框内嵌 {x} 按钮插入上游输出引用;
  • 输出参数:编辑面板底部输出变量折叠区(计数徽章 toggle),展开提示下游可引用字段。

可复用默认工具

HTTP、Redis、MQTT 是组件库的正式项目级定义,可由引用方单独选用,也可将完整加载器直接传给画布:

import {
  defaultToolList,
  defaultToolLoader,
  httpRequestTool,
  redisOperationTool,
  mqttPublishTool,
} from 'zh-aiot-workflow-canvas'

const selectedTools = [httpRequestTool, mqttPublishTool]
<WorkflowCanvas :tool-loader="defaultToolLoader" />

mockToolList 仍用于 Playground,内部复用 defaultToolList,不会维护第二份定义。

工具 说明 动态输入
HTTP 请求 GET/POST/PUT/DELETE;Body 支持 none、form-data、x-www-form-urlencoded、JSON、raw、binary method / url / headers / query / bodyType / bodyParams / bodyTemplate / timeoutMs
Redis 读取 按数据类型读取键值 dataType → 展开 key / field / start/stop / min/max / member
Redis 写入 按数据类型写入键值 dataType → 展开 key / value / field / fieldValue / pushMode / listValue / score / zsetValue / ttlSeconds
MQ 消息生成 向消息队列投递消息 topic / tag / payloadType / payload / delayMs
查询设备状态 设备在线与属性 deviceId
设备开关 下发开/关 deviceId / action
查询历史数据 按时间范围查询 deviceId / start / end
查询天气 按经纬度查天气 longitude / latitude
AI 规则判定 大模型判定 prompt

七、LiteFlow 导出器

import { buildLiteflowGraph, serializeLiteflowGraph, LiteflowExportError } from 'zh-aiot-workflow-canvas'
  • buildLiteflowGraph(nodes, edges):将扁平 nodes/edges 编译为 { root: { children: [...] } } 嵌套树;
  • 支持条件分支链、循环/迭代 closure、注释与开始节点自动跳过;触发节点(用户输入 start / 定时触发器 timer)作为流程入口,多个定时触发器的声明统一置于链首、共享流程体只编译一次;
  • 开始节点直接显示和导出 request.<变量名>;定时触发器变量(含值)导出为 config.payload.<变量>(按类型转换),下游引用路径为 context.node.<id>.data.payload.<变量>;其他节点使用固定的 code / message / data / success 输出结构,业务字段位于 data;
  • 画布内部变量引用使用 {{nodeId.field}};导出时自动转换为服务端路径:开始输入为 request.field,其他节点输出为 context.node.nodeId.*,循环局部变量为 context.loop.field,普通模板转换为 ${...};
  • 条件项导出为服务端读取的 leftPath/operator/rightType/rightValue;结束节点的单变量引用导出为 returnPath,字面量或混合模板导出为 returnValue,并附带 returnLabel(当前仅支持一个返回值);定时触发器导出前校验 Cron 格式(6 段 Quartz/Spring 风格,core/cron 提供 parseCron / validateCron / nextCronExecutions 可独立复用);
  • 节点 name 和输出别名 exports 一并写入 graphJson,节点类型限定为服务端内置类型及消费方通过声明合并 + registerWorkflowTools 注册的自定义类型;
  • 若存在游离节点或端口未连接,会抛出 LiteflowExportError(含中文错误描述)。

八、发布到 npm

# 1) 发布前按需调整 package.json 中的 "version"(遵循 semver)

# 2) 构建(发布内容为 dist/,sourcemap 已排除)
pnpm build

# 3) 发布(需 npmjs 账号,首次先 npm login;publishConfig 已指向 registry.npmjs.org)
npm publish

提示:zh-aiot-webui 本地联调阶段可用 pnpm link(在 webui 目录 pnpm link zh-aiot-workflow-canvas)或直接 "zh-aiot-workflow-canvas": "file:../zh-aiot-workflow-canvas" 指向本目录,无需发布即可联调。

九、路线图(建议)

  • Dify 式左侧操作栏 + 节点种类面板 + 指针/手型模式 + 快捷键
  • 多形态业务节点(用户输入/定时触发器/条件分支/循环/迭代/工具调用/结束/注释)
  • 工具调用:支持注入 loader 获取内部接口工具列表(默认 mock)
  • 一键导出 zh-aiot-server LiteFlow 可解析的 graphJson
  • 节点参数编辑 + 变量引用({{nodeId.field}}):所有节点可引用上游输出;开始节点可编辑输入参数
  • 双触发入口互斥(二选一):用户输入(start)/ 定时触发器(timer,Cron,可定义多个)
  • 工具调用:支持注入 loader 获取内部接口工具列表(默认 mock,含 HTTP 请求、Redis 操作、MQTT 消息推送)
  • 工具展开改为侧边列(节点面板右侧新开一列)
  • 节点输出参数 chips(提示下游可引用字段)
  • Dify 式节点卡片(图标+标题+chevron+描述行+变量行,含 {x} 引用高亮)
  • Dify 式右侧编辑面板(可编辑标题 + 删除节点 + 描述/工具/输入字段/参数/输出变量折叠区)
  • removeNode API:删除节点并清理挂载连线
  • 撤销/重做(history 栈 + ⌘/Ctrl+Z、⇧+Z 快捷键 + 浮动按钮,覆盖增删/连线/拖拽/编辑)
  • 条件分支 Dify 式 IF 结构化编辑器(变量 chip + 运算符 + 值,AND/OR,多条件)
  • 参数引用全部以变量 chip 呈现(VarChip,卡片/面板隐藏 {{}} 代码)
  • 接入后端 DSL:从工作流 JSON/YAML 还原 nodes/edges(反向导入)
  • 运行态回显:SSE/WebSocket 驱动 data.status
  • 撤销/重做(可引入 zundo/自定义 history)
  • 自动布局 / 校验 / 缩略图美化

自定义节点类型扩展

liteflow.type 使用 string,可直接填写后端支持的类型,无需声明合并或通过注册获得类型许可。画布不检查类型白名单。

1. 创建自定义工具描述符

import type { ToolDescriptor } from 'zh-aiot-workflow-canvas'

export const customDeviceTool: ToolDescriptor = {
  id: 'custom-device',
  name: 'Custom Device',
  description: 'A custom device tool',
  category: 'Custom',
  color: '#10b981',
  icon: 'ri:device-line',
  params: [
    { key: 'deviceId', label: 'Device ID', type: 'text', required: true, allowVariable: true },
    { key: 'command', label: 'Command', type: 'textarea', required: true, allowVariable: true },
  ],
  outputs: [
    { name: 'success', label: 'Success', type: 'boolean' },
    { name: 'data', label: 'Response Data', type: 'json' },
  ],
  liteflow: {
    type: 'custom_device', // 自定义节点类型
    config: { deviceId: '', command: '' }
  }
}

2. 注册自定义工具

import { registerWorkflowTools } from 'zh-aiot-workflow-canvas'
import { customDeviceTool } from './custom-tools'

registerWorkflowTools([customDeviceTool])

注册用于导入时恢复工具描述;通过 toolLoader 提供工具选择列表。节点自身携带 liteflow.type/config 时,无需注册即可导出。

内置节点类型

以下节点类型是内置的,无需注册即可使用:

  • timer - 定时触发器
  • http - HTTP 请求
  • mqtt - MQTT 消息
  • redis - Redis 操作
  • if - 条件分支
  • switch - 多条件分支
  • for - 循环
  • while - 条件循环
  • workflow - 子流程调用
  • return - 流程返回

验证机制

画布仍会检查节点是否配置 liteflow.type,并校验流程结构、必填参数和内置节点配置。具体类型是否被后端支持,由后端决定。

Related Packages

@marui_2026/workflow-canvas

Dify workflow canvas editor as a Vue 3 component library. UI-only: all backend A...

4,159
element-less-aicanvas

Vue 3 AI 无限画布编辑器组件,开箱即用 —— 传入 projectId 与 adapter 即可加载完整...

8,894
@stsdti/approvable-workflow-editor

Visual workflow editor for laravel-approvable, built on Vue Flow.

636
@xyflow/vue

Vue Flow - A highly customizable Vue library for building node-based editors, wo...

5,302 38,757
@thihaeung/flowchart-lib

A lightweight flowchart library for creating interactive flowcharts

5,977 0

Version History

Version Released License
0.7.2 MIT
0.7.1 MIT
0.7.0 MIT
0.6.9 MIT
0.6.8 MIT