# dynamic-node **Repository Path**: HeQingHua2018/dynamic-node ## Basic Information - **Project Name**: dynamic-node - **Description**: 一个基于元数据驱动的动态节点配置系统,允许开发者通过配置而非编码的方式快速定义和注入各种类型的节点,实现灵活可扩展的节点配置功能。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-08-14 - **Last Updated**: 2025-08-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Dynamic Node Injector ## 项目介绍 Dynamic Node Injector 是一个基于元数据驱动的动态节点配置系统,允许开发者通过配置而非编码的方式快速定义和注入各种类型的节点,实现灵活可扩展的节点配置功能。 ## 技术栈 - **前端框架**:React + UmiJS - **UI 组件库**:Ant Design - **样式处理**:Tailwind CSS - **编程语言**:TypeScript - **构建工具**:npm/pnpm - **包管理**:pnpm (推荐) ## 安装说明 1. 克隆项目 ```bash git clone https://github.com/yourusername/dynamic-node.git cd dynamic-node ``` 2. 安装依赖 ```bash npm install # 或使用 pnpm pnpm install ``` 3. 启动开发服务器 ```bash npm run dev ``` ## 实现思路概述 不同节点拥有不同配置项的功能,核心采用**元数据驱动(Metadata-driven)** 结合**组件动态映射**的设计模式。该模式能够灵活支持节点配置的多样性,同时保证系统的可扩展性和代码复用性,避免冗余开发 ## 关键实现机制 1. 节点类型与元数据(Schema)定义 每种节点类型(如 “Prompt 节点”“工具调用节点”“条件判断节点” 等)均通过**元数据(Schema)** 定义其专属配置项结构。元数据是描述配置项的 “说明书”,包含以下核心信息: - **基础属性**:字段名(如 `prompt_template`、`tool_id`)、字段类型(文本、下拉选择、开关等); - **UI 表现**:对应的渲染组件(如输入框、代码编辑器、键值对编辑器等); - **验证规则**:是否必填、格式校验(如邮箱格式)、长度限制等; - **辅助信息**:默认值、描述文案、占位符; - **依赖关系**:字段间的显示 / 隐藏逻辑(如 “选择某工具后才显示参数配置”)。 2. 节点类型与配置组件的映射关系 - 大多数简单节点通过**内置配置组件**处理(基于元数据自动生成表单); - 复杂节点使用**自定义配置组件**,动态注入。 3. 动态表单生成器(核心组件) 动态表单生成器是实现配置项差异化渲染的核心工具,其作用是根据元数据自动生成对应的 UI 表单。工作流程如下: - **接收输入**:当前节点的元数据(Schema)和当前配置值; - **解析元数据**:遍历配置项,根据 `type` 和 `widget` 字段匹配对应的表单控件; - **处理依赖逻辑**:根据字段间的依赖关系(如 `dependsOn` 配置),动态显示 / 隐藏控件; - **实时交互**:用户输入时,同步更新配置值并触发校验; - **输出结果**:将最终配置值同步至全局状态。 ## 快速开始 ### 1. 初始化注入器 ```typescript import { initializeInjector } from '@/plugin/injector'; import { nodeSchemas } from '@/data/schema'; import { nodeTypes } from '@/data/node'; // 初始化注入器 const injector = initializeInjector(nodeSchemas, nodeTypes); ``` ### 2. 定义节点元数据 ```typescript // 在src/data/schema目录下定义节点元数据 import { Schema } from "@/widget/type"; const nodeSchemas: Record = { // Prompt节点元数据 prompt: { type: "prompt", label: "Prompt节点", config: [ { field: "prompt_template", label: "提示词模板", type: "text", widget: "monaco-editor", defaultValue: "", widgetProps: { placeholder: "请输入提示词模板", }, formItemProps: { rules: [ { required: true, message: "请输入提示词模板", }, { min: 10, message: "提示词模板长度不能少于10个字符", }, ], }, }, // 更多配置项... ], }, }; export { nodeSchemas }; ``` ### 3. 注入动态节点 ```typescript import { injectNodeSchema, injectNodeType, injectWidget } from '@/plugin/injector'; import { nodeConfigs } from './example/schema'; import { message } from 'antd'; /** * 注入动态节点 */ const initInjectDynamicNode = (): boolean => { try { // 遍历所有节点配置并注入 nodeConfigs.forEach(config => { // 从schema中获取类型和标签 const { type, label } = config.schema; // 注入节点类型 injectNodeType(type, label); // 注入节点元数据 injectNodeSchema(type, config.schema); // 注入相关控件 Object.entries(config?.widgets || {}).forEach(([widgetType, widgetComponent]) => { injectWidget(widgetType, widgetComponent); }); }); message.success('所有动态节点已成功注入!'); return true; } catch (error) { message.error('注入节点时发生错误:' + (error as Error).message); console.error('注入节点错误:', error); return false; } } export { initInjectDynamicNode, }; ``` ### 4. 使用动态表单 ```tsx import React, { useState, useEffect } from 'react'; import { Card, Select, Divider, Typography, Space, Button } from 'antd'; import DynamicConfigForm from '@/widget/index'; import type { Field } from '@/widget/type.d'; import { getNodeSchemas, getNodeTypes, getInjector } from '@/plugin/injector'; import { initInjectDynamicNode } from '@/example/index'; const NodeConfigDemo: React.FC = () => { const [selectedNodeType, setSelectedNodeType] = useState(); const [nodeConfig, setNodeConfig] = useState>({}); const [nodeSchemas, setNodeSchemas] = useState>(getNodeSchemas()); const [nodeTypes, setNodeTypes] = useState<{ value: string; label: string }[]>(getNodeTypes()); // 监听节点元数据和类型变化 useEffect(() => { const updateData = () => { setNodeSchemas(getNodeSchemas()); setNodeTypes(getNodeTypes()); }; // 初始化数据 updateData(); // 使用订阅机制监听变化 const unsubscribe = getInjector().subscribe(updateData); return () => unsubscribe(); }, []); const currentSchema = nodeSchemas[selectedNodeType as keyof typeof nodeSchemas]; // 当节点类型改变时重置配置 useEffect(() => { // 初始化默认值 const initialConfig: Record = {}; if (currentSchema && currentSchema.config) { currentSchema.config.forEach((field: Field) => { if (field.defaultValue !== undefined) { initialConfig[field.field] = field.defaultValue; } }); } setNodeConfig(initialConfig); }, [selectedNodeType, currentSchema]); return (
节点配置差异化演示 {currentSchema && ( <> {currentSchema && ( )} )}
); }; export default NodeConfigDemo; ``` ## 项目结构 ``` dynamic-node/ ├── .gitignore ├── .npmrc ├── .umirc.ts ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src/ │ ├── assets/ │ │ └── yay.jpg │ ├── data/ │ │ ├── index.ts │ │ ├── node/ │ │ └── schema/ │ ├── example/ │ │ ├── index.ts │ │ ├── schema.ts │ │ └── widget/ │ ├── pages/ │ │ └── index.tsx │ ├── plugin/ │ │ └── injector.ts │ └── widget/ │ ├── components/ │ ├── index.tsx │ └── type.d.ts ├── tailwind.config.js ├── tailwind.css ├── tsconfig.json └── typings.d.ts ``` ## API 文档 ### 注入器 API - `initializeInjector(initialSchemas, initialTypes, initialWidgets)`: 初始化注入器 - `getInjector()`: 获取注入器实例 - `injectNodeSchema(type, schema)`: 注入节点元数据 - `injectNodeType(type, label)`: 注入节点类型 - `injectWidget(type, widget)`: 注入自定义控件 - `getNodeSchemas()`: 获取所有节点元数据 - `getNodeTypes()`: 获取所有节点类型 - `getWidgets()`: 获取所有控件 ### 动态表单 API - `DynamicConfigForm`: 动态表单组件 - `schema`: 节点元数据 - `value`: 当前配置值 - `onChange`: 配置值变化回调 ## 示例 项目提供了完整的示例代码,展示如何使用动态节点配置系统: 1. **基础示例**:`src/example/schema.ts` - 定义了不同类型节点的元数据 2. **自定义控件示例**:`src/example/widget/` - 包含自定义控件实现 3. **使用示例**:`src/example/index.ts` - 展示如何注入动态节点 4. **完整应用示例**:`src/pages/index.tsx` - 完整的节点配置演示应用 运行项目后,访问 http://localhost:8000 可以查看实际效果。