logologo
Get Started
Guide
Development
Plugins
API
English
简体中文
Get Started
Guide
Development
Plugins
API
English
简体中文
logologo

Introduction

What is FlowEngine?
FlowEngine and Plugins
Quick Start
Learning Roadmap

Guides

Register FlowModel
Create FlowModel
Render FlowModel
FlowModel Event Flow and Configuration
FlowModel Persistence
FlowModel Lifecycle
FlowModel Context System
Reactive Mechanism: Observable
FlowModel vs React.Component

Definitions

ModelDefinition
FlowDefinition
EventDefinition
ActionDefinition
StepDefinition
Previous PageActionDefinition

#StepDefinition

StepDefinition defines a single step in a flow. Each step can be an action, event handling, or other operation. A step is the basic execution unit of a flow.

#Type Definition

interface StepDefinition<TModel extends FlowModel = FlowModel>
  extends Partial<Omit<ActionDefinition<TModel, FlowRuntimeContext<TModel>>, 'name'>> {
  key?: string;
  isAwait?: boolean;
  use?: string;
  sort?: number;
  preset?: boolean;
  paramsRequired?: boolean;
  hideInSettings?: boolean;
  uiMode?: StepUIMode | ((ctx: FlowRuntimeContext<TModel>) => StepUIMode | Promise<StepUIMode>);
}

#Usage

class MyModel extends FlowModel {}

MyModel.registerFlow({
  key: 'pageSettings',
  steps: {
    step1: {
      use: 'actionName',
      title: 'First Step',
      sort: 0,
      preset: true
    },
    step2: {
      handler: async (ctx, params) => {
        // Custom processing logic
        return { result: 'success' };
      },
      title: 'Second Step',
      sort: 1
    }
  }
});

#Property Descriptions

#key

Type: string
Required: No
Description: The unique identifier for the step within the flow.

If not provided, the key name of the step in the steps object will be used.

Example:

steps: {
  loadData: {  // key is 'loadData'
    use: 'loadDataAction'
  }
}

#use

Type: string
Required: No
Description: The name of a registered ActionDefinition to use.

The use property allows you to reference a registered action, avoiding duplicate definitions.

Example:

// Register the action first
MyModel.registerAction({
  name: 'loadDataAction',
  handler: async (ctx, params) => {
    // Data loading logic
  }
});

// Use it in a step
steps: {
  step1: {
    use: 'loadDataAction',  // Reference the registered action
    title: 'Load Data'
  }
}

#title

Type: string
Required: No
Description: The display title of the step.

Used for UI display and debugging.

Example:

title: 'Load Data'
title: 'Process Information'
title: 'Save Results'

#sort

Type: number
Required: No
Description: The execution order of the step. The smaller the value, the earlier it executes.

Used to control the execution order of multiple steps in the same flow.

Example:

steps: {
  step1: { sort: 0 },  // Executes first
  step2: { sort: 1 },  // Executes next
  step3: { sort: 2 }   // Executes last
}

#handler

Type: (ctx: FlowRuntimeContext<TModel>, params: any) => Promise<any> | any
Required: No
Description: The handler function for the step.

When the use property is not used, you can define the handler function directly.

Example:

handler: async (ctx, params) => {
  // Get context information
  const { model, flowEngine } = ctx;
  
  // Processing logic
  const result = await processData(params);
  
  // Return result
  return { success: true, data: result };
}

#defaultParams

Type: Record<string, any> | ((ctx: FlowRuntimeContext<TModel>) => Record<string, any> | Promise<Record<string, any>>)
Required: No
Description: The default parameters for the step.

Fills parameters with default values before the step is executed.

Example:

// Static default parameters
defaultParams: {
  timeout: 5000,
  retries: 3,
  format: 'json'
}

// Dynamic default parameters
defaultParams: (ctx) => {
  return {
    userId: ctx.model.uid,
    timestamp: Date.now()
  }
}

// Asynchronous default parameters
defaultParams: async (ctx) => {
  const config = await loadConfig();
  return {
    apiUrl: config.apiUrl,
    apiKey: config.apiKey
  }
}

#uiSchema

Type: Record<string, ISchema> | ((ctx: FlowRuntimeContext<TModel>) => Record<string, ISchema> | Promise<Record<string, ISchema>>)
Required: No
Description: The UI configuration schema for the step.

Defines how the step is displayed in the interface and its form configuration.

Example:

uiSchema: {
  'x-component': 'Form',
  'x-component-props': {
    layout: 'vertical'
  },
  properties: {
    name: {
      type: 'string',
      title: 'Name',
      'x-component': 'Input'
    },
    age: {
      type: 'number',
      title: 'Age',
      'x-component': 'InputNumber'
    }
  }
}

#beforeParamsSave

Type: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>
Required: No
Description: A hook function that runs before the parameters are saved.

Executes before the step parameters are saved, and can be used for parameter validation or transformation.

Example:

beforeParamsSave: (ctx, params, previousParams) => {
  // Parameter validation
  if (!params.name) {
    throw new Error('Name is required');
  }
  
  // Parameter transformation
  params.name = params.name.trim().toLowerCase();
}

#afterParamsSave

Type: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>
Required: No
Description: A hook function that runs after the parameters are saved.

Executes after the step parameters are saved, and can be used to trigger other operations.

Example:

afterParamsSave: (ctx, params, previousParams) => {
  // Record logs
  console.log('Step params saved:', params);
  
  // Trigger other operations
  ctx.model.emitter.emit('paramsChanged', params);
}

#uiMode

Type: StepUIMode | ((ctx: FlowRuntimeContext<TModel>) => StepUIMode | Promise<StepUIMode>)
Required: No
Description: The UI display mode for the step.

Controls how the step is displayed in the interface.

Supported modes:

  • 'dialog' - Dialog mode
  • 'drawer' - Drawer mode
  • 'embed' - Embed mode
  • Or a custom configuration object

Example:

// Simple mode
uiMode: 'dialog'

// Custom configuration
uiMode: {
  type: 'dialog',
  props: {
    width: 800,
    title: 'Step Configuration'
  }
}

// Dynamic mode
uiMode: (ctx) => {
  return ctx.model.isMobile ? 'drawer' : 'dialog';
}

#preset

Type: boolean
Required: No
Description: Whether it is a preset step.

Parameters for steps with preset: true need to be filled in at creation time. Those without this flag can be filled in after the model is created.

Example:

steps: {
  step1: {
    preset: true,  // Parameters must be filled in at creation time
    use: 'requiredAction'
  },
  step2: {
    preset: false, // Parameters can be filled in later
    use: 'optionalAction'
  }
}

#paramsRequired

Type: boolean
Required: No
Description: Whether the step parameters are required.

If true, a configuration dialog will open before adding the model.

Example:

paramsRequired: true  // Parameters must be configured before adding the model
paramsRequired: false // Parameters can be configured later

#hideInSettings

Type: boolean
Required: No
Description: Whether to hide the step in the settings menu.

Example:

hideInSettings: true  // Hide in settings
hideInSettings: false // Show in settings (default)

#isAwait

Type: boolean
Required: No
Default: true
Description: Whether to wait for the handler function to complete.

Example:

isAwait: true  // Wait for the handler function to complete (default)
isAwait: false // Do not wait, execute asynchronously

#Complete Example

class DataProcessingModel extends FlowModel {}

DataProcessingModel.registerFlow({
  key: 'dataProcessing',
  title: 'Data Processing',
  steps: {
    loadData: {
      use: 'loadDataAction',
      title: 'Load Data',
      sort: 0,
      preset: true,
      paramsRequired: true,
      defaultParams: {
        source: 'api',
        timeout: 5000
      },
      uiMode: 'dialog'
    },
    processData: {
      handler: async (ctx, params) => {
        const data = await ctx.model.getData();
        return processData(data, params);
      },
      title: 'Process Data',
      sort: 1,
      defaultParams: (ctx) => ({
        userId: ctx.model.uid,
        timestamp: Date.now()
      }),
      beforeParamsSave: (ctx, params) => {
        if (!params.processor) {
          throw new Error('Processor is required');
        }
      },
      afterParamsSave: (ctx, params) => {
        ctx.model.emitter.emit('dataProcessed', params);
      }
    },
    saveData: {
      use: 'saveDataAction',
      title: 'Save Data',
      sort: 2,
      hideInSettings: false,
      uiMode: {
        type: 'drawer',
        props: {
          width: 600,
          title: 'Save Configuration'
        }
      }
    }
  }
});