节点

节点通过 FlowNodeEntity 定义

节点数据

通过 node.toJSON() 可以获取

基本结构:
  • id: string 节点唯一标识, 必须保证唯一
  • meta: object 节点的 ui 配置信息,如自由布局的 position 信息放这里
  • type: string | number 节点类型,会和 nodeRegistries 中的 type 对应
  • data: object 节点表单数据, 业务可自定义
  • blocks: array 节点的分支, 采用 block 更贴近 Gramming
const nodeData: FlowNodeJSON = {
  id: 'xxxx',
  type: 'condition',
  data: {
    title: 'MyCondition',
    desc: 'xxxxx'
  },
}

节点定义

声明节点可以用于确定节点的类型及渲染方式

import { FlowNodeRegistry, ValidateTrigger } from '@flowgram.ai/fixed-layout-editor';

/**
 * 自定义节点注册
 */
export const nodeRegistries: FlowNodeRegistry[] = [
  {
    /**
     * 自定义节点类型
     */
    type: 'condition',
    /**
     * 自定义节点扩展:
     *  - loop: 扩展为循环节点
     *  - start: 扩展为开始节点
     *  - dynamicSplit: 扩展为分支节点
     *  - end: 扩展为结束节点
     *  - tryCatch: 扩展为 tryCatch 节点
     *  - default: 扩展为普通节点 (默认)
     */
    extend: 'dynamicSplit',
    /**
     * 节点配置信息
     */
    meta: {
      // isStart: false, // 是否为开始节点
      // isNodeEnd: false, // 是否为结束节点,结束节点后边无法再添加节点
      // draggable: false, // 是否可拖拽,如开始节点和结束节点无法拖拽
      // selectable: false, // 触发器等开始节点不能被框选
      // deleteDisable: true, // 禁止删除
      // copyDisable: true, // 禁止copy
      // addDisable: true, // 禁止添加
    },
    /**
     * 配置节点表单的校验及渲染,
     * 注:validate 采用数据和渲染分离,保证节点即使不渲染也能对数据做校验
     */
    formMeta: {
      validateTrigger: ValidateTrigger.onChange,
      validate: {
        title: ({ value }) => (value ? undefined : 'Title is required'),
      },
      /**
       * Render form
       */
      render: () => (
       <>
          <Field name="title">
            {({ field }) => <div className="demo-free-node-title">{field.value}</div>}
          </Field>
          <Field name="content">
            {({ field }) => <input onChange={field.onChange} value={field.value}/>}
          </Field>
        </>
      )
    },
  },
];

当前渲染节点获取

通过 useNodeRender 获取节点相关方法

function BaseNode() {
  const { id, type, data, updateData, node } = useNodeRender()
}

创建节点

通过 FlowOperationService 创建

  • 添加节点
const ctx = useClientContext()

ctx.operation.addNode({
  id: 'xxx', // 要保证画布内唯一
  type: 'custom',
  meta: {},
  data: {}, // 表单相关数据
  blocks: [], // 子节点
  parent: someParent // 父亲节点,分支会用到
})
  • 在指定节点之后添加
const ctx = useClientContext()

ctx.operation.addFromNode(targetNode, {
  id: 'xxx', // 要保证画布内唯一
  type: 'custom',
  meta: {},
  data: {}, // 表单相关数据
  blocks: [], // 子节点
})
  • 添加分支节点 (用于条件分支)
const ctx = useClientContext()

ctx.operation.addBlock(parentNode, {
  id: 'xxx', // 要保证画布内唯一
  type: 'block',
  meta: {},
  data: {}, // 表单相关数据
  blocks: [], // 子节点
})

删除节点

function BaseNode({ node }) {
  const ctx = useClientContext()
  function onClick() {
    ctx.operation.deleteNode(node)
  }
  return (
    <button onClick={onClick}>Delete</button>
  )
}

更新节点 data 数据

function BaseNode() {
  const { data, updateData } = useNodeRender();
  // 对应节点的 data 数据
  console.log(data)

  function onChange(e) {
    updateData({
      ...data,
      title: e.target.value
    })
  }
  return <input value={data.title} onChange={onChange}/>
}

function FormRender() {
  return (
    <Field name="title">
      <Input />
    </Field>
  )
}

更新节点的 extInfo 数据

extInfo 用于存储 一些 ui 状态, 如果未开启节点引擎,节点的 data 数据会默认存到 extInfo 里

function BaseNode({ node }) {
  const times = node.getExtInfo()?.times || 0
  function onClick() {
    node.updateExtInfo({ times: times ++ })
  }
  return (
    <div>
      <span>Click Times: {times}</span>
      <button onClick={onClick}>Click</button>
    </div>
  )
}