JavaScript is required

AI模型工作流关系图

这个示例构建了一个固定位置的工作流面板,它看起来更像 AI 生成流水线,而不是标准的节点图。每个节点都被渲染为深色圆角卡片,包含标题、左右两列参数以及堆叠的选项行。其中一些选项行会渲染为特殊区块,包括多行文本输入框和图片预览。

AI 模型工作流图

这个示例构建了什么

这个示例构建了一个固定位置的工作流面板,它看起来更像 AI 生成流水线,而不是标准的节点图。每个节点都被渲染为深色圆角卡片,包含标题、左右两列参数以及堆叠的选项行。其中一些选项行会渲染为特殊区块,包括多行文本输入框和图片预览。

可见连线并不是附着在整个节点框上,而是附着在具体的输入行和输出行上,因此整个图更像一个参数依赖看板。用户可以平移和缩放画布,使用缩略图在宽布局中导航,并在点击节点时只高亮属于该步骤的依赖关系。

数据是如何组织的

源数据从 MyGraph-data.ts 中的 myJsonData 开始,包含 rootIdnodes 数组,以及空的 linesfakeLines 数组。每个节点都带有一个 data 对象,其中分为 inputoutputoptions 三个部分。自定义节点渲染器正是基于这个结构生成卡片布局。

依赖模型单独存放在 modelRelations 中。每条关系都会同时描述上游模型 id 和输出参数,以及下游模型 id 和输入参数。在运行 setJsonData() 之前,示例会把这些记录转换成 relation-graph 的 fakeLines,把已连接输出的类型复制到对应的输入记录上,并从预定义的位置表中恢复保存的 xy 坐标;如果没有命中,则回退到基于索引的默认坐标。

在真实应用中,这种结构同样可以表示 ML 流水线步骤、ETL 任务、工作流作业,或者任何一个节点暴露命名输入、命名输出和配置字段的过程。

relation-graph 是如何使用的

这个示例把 relation-graph 用作查看器,而不是编辑器。RGProvider 提供图上下文,RGHooks.useGraphInstance() 获取图实例,用于清理旧状态、加载 JSON 数据、将视口移动到中心、缩放以适配内容,以及在点击处理时检查现有 fakeLines

图被配置为 fixed 布局,使用 RGJunctionPoint.border、半透明默认节点颜色,并去掉默认节点边框。这样一来,节点的可视表面就完全交给通过 RGSlotOnNode 渲染的自定义卡片组件。在这张卡片内部,每一行输入和输出都包裹在 RGConnectTarget 中,从而为 relation-graph 提供一个可供连线附着的具体 DOM 端点。RGSlotOnView 添加了 RGMiniView,因此这个示例无需额外 UI 就保留了内置的总览导航能力。

样式层对最终效果同样重要。my-relation-graph.scss 把画布变成深色工作区,移除了默认节点外壳的视觉样式,并把选中态发光替换为围绕插槽内容的自定义描边。MyAIModelNode.scss 负责卡片布局样式,并让生成出来的 I_O_ 端点元素使用相对定位,以便基于 DOM 目标的连线能够正确解析。

关键交互

  • 在挂载时,示例会准备 fake lines、恢复保存的坐标、加载图,并让工作流适配到视口中。
  • 点击节点时,只会高亮引用该节点的 fake lines,这让图从静态图片变成了依赖追踪器。
  • 缩略图帮助用户在异常宽的固定画布中导航而不丢失上下文。
  • 因为每一行参数都是独立端点,用户可以直接在画布上检查端口到端口的依赖关系,而不必从节点级边上自行推断。

关键代码片段

这段代码展示了该图被有意配置为固定布局查看器,并尽量弱化默认节点外壳。

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.border,
    defaultNodeColor: 'rgba(100, 100, 100, 0.5)',
    defaultNodeBorderWidth: 0,
    layout: {
        layoutName: 'fixed'
    },
};

这段代码展示了如何把独立的工作流关系列表转换成 fake lines,以及代码如何在渲染前统一已连接输入的类型。

myJsonData.fakeLines = modelRelations.map((relation, index) => {
    const outputModel = getModel(relation.output_model);
    const inputModel = getModel(relation.input_model);

    if (!outputModel || !inputModel) return null;

    const outputParam = getModelOutputParam(outputModel, relation.output_param_name);
    const inputParam = getModelInputParam(inputModel, relation.input_param_name);

    if (inputParam && outputParam) {
        inputParam.type = outputParam.type; // fix data bug
    }

这段代码展示了合成出来的连线会指向节点内容内部的具体参数行,而不是整个节点的锚点。

return {
    id: `line-${index}`,
    from: `O_${relation.output_model}-${relation.output_param_name}`,
    fromJunctionPoint: RGJunctionPoint.right,
    to: `I_${relation.input_model}-${relation.input_param_name}`,
    toJunctionPoint: RGJunctionPoint.left,
    text: '',
    lineShape: RGLineShape.StandardCurve,
    lineWidth: 3,
    color: outputParam ? dataTypeColorMap[outputParam.type] : '#ccc',
    isFakeLine: true
};

这段代码说明每一行输入在自定义节点卡片中都会暴露为一个 RGConnectTarget

{node.data?.input?.map((input: any) => (
    <RGConnectTarget key={input.name} targetId={`I_${node.id}-${input.name}`} junctionPoint={RGJunctionPoint.left}>
        <span className="c-dot" style={{ backgroundColor: dataTypeColorMap[input.type] || '#ccc' }}/>
        {input.name}
    </RGConnectTarget>
))}

这段代码展示了示例如何在首次加载时恢复保存的坐标,并让准备好的工作流进入合适视图。

myJsonData.nodes.forEach((model, index) => {
    const position = nodesPosition.find(n => n.id === model.id);
    if (position) {
        model.x = position.x;
        model.y = position.y;
    } else {
        model.x = index * 50;
        model.y = index * 50;
    }
});
graphInstance.clearGraph();
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

这段代码展示了基于点击的依赖检查行为。

const onNodeClick = (node: RGNode, $event: RGUserEvent) => {
    console.log('onNodeClick:', node.text);
    const elLines = graphInstance.getFakeLines();
    elLines.forEach((line: any) => {
        const keywords = `_${node.id}-`;
        const refed = line.from.includes(keywords) || line.to.includes(keywords);
        line.animation = refed ? 2 : undefined;
    });
};

这个示例的独特之处

对比数据表明,这个示例远不只是一个通用节点样式示例。它最突出的差异点在于:自定义卡片节点、针对每一行的 RGConnectTarget 端点、基于独立 modelRelations 列表合成的 fake lines,以及点击后只高亮已连接依赖的交互。这一组合在预先整理的对比数据中被标记为较少见。

node-content-lines 相比,这个示例会在运行时根据工作流元数据生成可见依赖连线,并把参数类型转化为端口和连线共享的颜色语言。与 table-relationship 相比,它把同样的 DOM 端点技术用于 AI 工作流面板而不是 schema 查看器,并额外加入了类型传播和依赖高亮。与 node 相比,它使用节点插槽和缩略图来服务一个聚焦的工作流检查模式,而不是更宽泛的节点样式试验场。

因此,它更适合作为固定、只读流程图的起点,适用于用户需要检查精确上下游参数流的场景。

这种模式还适用于哪些地方

  • 需要保留精心布置的布局而不是使用自动布局的只读 AI 或自动化工作流快照。
  • 需要对字段而不只是表或任务进行明确源到目标连线的数据血缘或 ETL 界面。
  • 每个步骤都暴露命名输入、输出和配置参数的审批流或规则引擎视图。
  • 需要通过点击聚焦依赖关系、但不需要完整图编辑能力的制造、媒体处理或编排面板。