@done-coding/forge-flow
v0.3.0
Published
流程编排
Readme
@done-coding/forge-flow
流程编排组件库:基于 @antv/x6 的流程编辑器(FlowBox 集大成容器 = 画布 + 素材面板 + 操作栏 + 小地图 + 节点详情面板),内置业务节点(步骤 / 起止 / 判断)与端口语义预设(FLOW_PORT_PRESET),应用层注册节点、决定业务数据与连线逻辑。另附只读运行态族(FlowRunSteps 执行步骤树 / FlowRunIo 节点进料与输出,刻意不基于画布引擎)与通用 bridge 基座(Vue 插件 + 插件宿主 + 命名空间化存储原语)。
安装
pnpm add @done-coding/forge-flowpeer 依赖需消费方自装:vue / element-plus / @element-plus/icons-vue / lodash。运行时依赖(随包安装):@antv/x6 / @antv/x6-vue-shape。
快速开始(最小可用)
① 注册节点 → 编排画布
// 参考 demo-src/pages/Home.vue
import {
FlowBox,
FlowStep,
FlowStartEnd,
FlowDecision,
registerFlowCustomNode,
FLOW_PORT_PRESET,
type FlowInstance,
} from "@done-coding/forge-flow";
// 应用层按需注册 / 切换 type:库提供内置节点组件 + 端口语义预设。
// 开始 = entry(仅下出桩)、结束 = exit(仅上入桩):语义单向、无多余反向桩
registerFlowCustomNode({
shape: "flow-start",
component: FlowStartEnd,
width: 180,
height: 44,
ports: FLOW_PORT_PRESET.entry(),
});
registerFlowCustomNode({
shape: "flow-end",
component: FlowStartEnd,
width: 180,
height: 44,
ports: FLOW_PORT_PRESET.exit(),
});
registerFlowCustomNode({
shape: "flow-step",
component: FlowStep,
width: 280,
height: 64,
ports: FLOW_PORT_PRESET.process(),
});<FlowBox
ref="flowInstance"
style="height: 100vh"
:materialPanelProps="materialPanelProps"
@add-node-request="handleAddNodeRequest"
@graph-created="handleGraphCreated"
/>要点:
registerFlowCustomNode注册 x6-vue-shape 节点(shape 名应用层定,如"flow-step");库不预注册任何 shape——不注册则素材拖入无对应渲染- 业务数据经 x6 节点
data字段承载(内置节点契约见 components/flow-custom-cell:FlowStepCellData/FlowStartEndCellData/FlowDecisionCellData,含skin皮肤字段) @add-node-request(点节点出端「+」请求新增下游)/@graph-created(画布就绪,可绑事件 / 做自定义图操作)- 素材面板拖入 + 点击出端「+」都由应用层决定新增什么 type 并接线——库不持业务语义
- 依赖宿主主题:节点 accent 默认色走
--forge-core-* → --el-* → 死值三级回退(未传leftColor时随主题明暗自适配)
② 画布控制(受控操作)
const flowInstance = shallowRef<FlowInstance>();
// FlowInstance.mountData(data) 挂载 JSON 数据;getGraph() 拿 x6 Graph(未就绪时 undefined)
flowInstance.value?.mountData(savedJson);
const graph = flowInstance.value?.getGraph();③ 只读运行轨迹(与画布无关,可单独用)
import { FlowRunSteps, FlowRunIo, type FlowRunStepNode } from "@done-coding/forge-flow";
// 🔴 parallel 是二维:每个元素 = 一条并行支,支内是一串子步
const steps: FlowRunStepNode[] = [
{ key: "parse", label: "解析输入", status: "done", durationMs: 820 },
{
key: "fanout",
label: "并行生成",
status: "done",
parallel: [
[{ key: "gen-a", label: "文案生成", status: "done", durationMs: 4300 }],
[{ key: "gen-b", label: "配图生成", status: "failed", durationMs: 12010 }],
],
},
{ key: "merge", label: "合并产物", status: "running" },
];<FlowRunSteps v-model:activeKey="activeKey" :steps="steps" @replay="onReplay" />
<FlowRunIo :input="current?.input" :output="current?.output">
<!-- 想怎么画自己来;不传插槽则原样降级展示 -->
<template #input="{ value, missing }">…</template>
</FlowRunIo>要点:
- 只画不判断:
running时传「已执行 + 当前」,终态传全量;组件不推断哪步在跑 - 诚实缺省:
undefined= 没取到(显说明文案),null/""/[]/{}= 取到的空值;durationMs无值不显示,[MUST NOT] 兜成0ms - 不按业务 schema 分型渲染:分型是应用层语义,包侧只给
#input/#output插槽
④ bridge 基座(可选)
import { createBridge, BRIDGE_KEY } from "@done-coding/forge-flow";
const bridge = createBridge<{
base: { title: string };
theme: { dark: boolean };
}>({
namespace: "myapp", // 派生 storage / useState 的 key 前缀
config: { base: { title: "Hi" }, theme: { dark: false } },
});
app.use(bridge); // 经 provide(BRIDGE_KEY) 全局注入
bridge.update("theme", { dark: true }); // 单字段一级浅合并
bridge.use(logPlugin); // 插件宿主,按引用 / name 幂等能力边界 / 按需使用
FlowBox是唯一推荐入口:子件(FlowEditor / FlowActionBar / FlowEditorMiniMap / FlowMaterialPanel / FlowNodeDetailPanel)可独立使用,但配套装配(布局矫正 / 面板联动 / 空态引导)只在 FlowBox 内- 节点皮肤:
FlowNodeSkinEnum(plain 默认 / neon / glass / aurora / cyber),应用层经节点data.skin切换 - 端口预设:
FLOW_PORT_PRESET.entry/exit/process/branch(n)/gateway(style?)——语义角色桩布局免手拼;特殊布局用底层getFlowNodeCirclePorts - 库不读宿主 env / 构建配置:无任何
import.meta.env读取;主题色走 CSS 变量回退链而非写死值 - 数据序列化:
saveJsonData()/mountData()走 x6toJSON/fromJSON,节点业务数据随data字段往返
API 真相源
⚠️ API 以
src/源码与types/d.ts 为真相源;文档与源码冲突时以源码为准。JSDoc 注释随 d.ts 发布(构建开放:minify:false+removeComments:false,产物可读)。
组件族索引
| 族 | 组件 | 文档 | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | flow 编排族 | FlowBox(集大成容器)/ FlowEditor(画布)/ FlowActionBar(操作栏)/ FlowEditorMiniMap(小地图)/ FlowMaterialPanel(素材面板)/ FlowNodeDetailPanel(节点详情) | README | | flow-custom-cell 节点族 | 内部件:FlowStep / FlowStartEnd / FlowDecision / FlowSkinFx + useFlowCellData | README | | flow-run 运行态族 | FlowRunSteps(执行步骤树)/ FlowRunIo(节点进料 / 输出展示)——只读、非画布 | README |
节点注册工具 registerFlowCustomNode / 端口预设 FLOW_PORT_PRESET 与类型 FlowProps / FlowInstance 等均经顶层 barrel 导出(import { FlowBox } from "@done-coding/forge-flow")。
内置 bridge 基座
文档:
src/bridge/docs/README.md(含插件契约 / storage / useState 原语)
一个可作为 Vue 插件安装(app.use(bridge))、自身又是插件宿主(bridge.use(plugin))的轻量基座:只读响应式配置 + 增量 update + 命名空间化的 storage / useState 原语。无业务耦合,配置 map 由调用方泛型自定义。底层工厂 createUseState / createStorageWithNamespace 与 BRIDGE_KEY 也经 barrel 独立导出(多命名空间 / 直接消费场景)。零模块级单例:每次 createBridge 在闭包内独立构造。
开发(本仓)
双区结构:src/(库源码,唯一发布产物来源)|demo-src/(本地调试 + 单测宿主,不发布)。
| 命令 | 作用 |
| --------------------- | ---------------------------------------------------- |
| pnpm dev | demo 开发服务(调试库,直引 src 源码免实时 build) |
| pnpm build | 库构建:vue-tsc && vite build → es/ + types/ |
| pnpm test | 单测(watch);pnpm test:ci = 单测 + demo 类型检查 |
| pnpm typecheck:demo | demo 区类型检查(tsconfig.demo.json) |
反模式 / 注意
- 节点内 SVG / 光效尺寸 [MUST] 取 x6 节点模型几何(
getNode().size()+ 监听change:size),[MUST NOT] 用ResizeObserver/getBoundingClientRect观测自身宿主祖先来驱动自身渲染尺寸——被观测元素 ⊇ 受其结果驱动尺寸的元素 = 正反馈自激,必致尺寸无界暴涨 - 高亮/选中是瞬态视觉:改动 x6 cell 时透传
FLOW_HIGH_LIGHT_MUTATION_OPTION,History 插件据此跳过、undo/redo 不被选中高亮污染 - 画布数据序列化走 x6 原语(
FromJSONData),消费方持久化到自身存储——库不焊后端
