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 PageEventDefinition
Next PageStepDefinition

#ActionDefinition

ActionDefinition defines reusable actions that can be referenced in multiple flows and steps. An action is the core execution unit in the FlowEngine, encapsulating specific business logic.

#Type Definition

interface ActionDefinition<TModel extends FlowModel = FlowModel, TCCtx extends FlowContext = FlowContext> {
  name: string;
  title?: string;
  handler: (ctx: TCtx, params: any) => Promise<any> | any;
  uiSchema?: Record<string, ISchema> | ((ctx: TCtx) => Record<string, ISchema> | Promise<Record<string, ISchema>>);
  defaultParams?: Record<string, any> | ((ctx: TCtx) => Record<string, any> | Promise<Record<string, any>>);
  beforeParamsSave?: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>;
  afterParamsSave?: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>;
  useRawParams?: boolean | ((ctx: TCtx) => boolean | Promise<boolean>);
  uiMode?: StepUIMode | ((ctx: FlowRuntimeContext<TModel>) => StepUIMode | Promise<StepUIMode>);
  scene?: ActionScene | ActionScene[];
  sort?: number;
}

#Registration Method

// Global registration (via FlowEngine)
const engine = new FlowEngine();
engine.registerAction({
  name: 'loadDataAction',
  title: 'Load Data',
  handler: async (ctx, params) => {
    // Processing logic
  }
});

// Model-level registration (via FlowModel)
class MyModel extends FlowModel {}
MyModel.registerAction({
  name: 'processDataAction',
  title: 'Process Data',
  handler: async (ctx, params) => {
    // Processing logic
  }
});

// Use in a flow
MyModel.registerFlow({
  key: 'dataFlow',
  steps: {
    step1: {
      use: 'loadDataAction',  // Reference global action
    },
    step2: {
      use: 'processDataAction', // Reference model-level action
    }
  }
});

#Property Descriptions

#name

Type: string
Required: Yes
Description: The unique identifier for the action

Used to reference the action in a step via the use property.

Example:

name: 'loadDataAction'
name: 'processDataAction'
name: 'saveDataAction'

#title

Type: string
Required: No
Description: The display title for the action

Used for UI display and debugging.

Example:

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

#handler

Type: (ctx: TCtx, params: any) => Promise<any> | any
Required: Yes
Description: The handler function for the action

The core logic of the action, which receives the context and parameters, and returns the processing result.

Example:

handler: async (ctx, params) => {
  const { model, flowEngine } = ctx;
  
  try {
    // Execute specific logic
    const result = await performAction(params);
    
    // Return result
    return {
      success: true,
      data: result,
      message: 'Action completed successfully'
    };
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

#defaultParams

Type: Record<string, any> | ((ctx: TCtx) => Record<string, any> | Promise<Record<string, any>>)
Required: No
Description: The default parameters for the action

Fills parameters with default values before the action 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(),
    version: ctx.flowEngine.version
  }
}

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

#uiSchema

Type: Record<string, ISchema> | ((ctx: TCtx) => Record<string, ISchema> | Promise<Record<string, ISchema>>)
Required: No
Description: The UI configuration schema for the action

Defines how the action is displayed in the UI and its form configuration.

Example:

uiSchema: {
  'x-component': 'Form',
  'x-component-props': {
    layout: 'vertical',
    labelCol: { span: 6 },
    wrapperCol: { span: 18 }
  },
  properties: {
    url: {
      type: 'string',
      title: 'API URL',
      'x-component': 'Input',
      'x-decorator': 'FormItem',
      required: true
    },
    method: {
      type: 'string',
      title: 'HTTP Method',
      'x-component': 'Select',
      'x-decorator': 'FormItem',
      enum: ['GET', 'POST', 'PUT', 'DELETE'],
      default: 'GET'
    },
    timeout: {
      type: 'number',
      title: 'Timeout (ms)',
      'x-component': 'InputNumber',
      'x-decorator': 'FormItem',
      default: 5000
    }
  }
}

#beforeParamsSave

Type: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>
Required: No
Description: Hook function executed before saving parameters

Executed before the action parameters are saved, can be used for parameter validation or transformation.

Example:

beforeParamsSave: (ctx, params, previousParams) => {
  // Parameter validation
  if (!params.url) {
    throw new Error('URL is required');
  }
  
  // Parameter transformation
  params.url = params.url.trim();
  if (!params.url.startsWith('http')) {
    params.url = 'https://' + params.url;
  }
  
  // Log changes
  console.log('Parameters changed:', {
    from: previousParams,
    to: params
  });
}

#afterParamsSave

Type: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>
Required: No
Description: Hook function executed after saving parameters

Executed after the action parameters are saved, can be used to trigger other operations.

Example:

afterParamsSave: (ctx, params, previousParams) => {
  // Log records
  console.log('Action params saved:', params);
  
  // Trigger event
  ctx.model.emitter.emit('actionParamsChanged', {
    actionName: 'loadDataAction',
    params,
    previousParams
  });
  
  // Update cache
  ctx.model.updateCache('actionParams', params);
}

#useRawParams

Type: boolean | ((ctx: TCtx) => boolean | Promise<boolean>)
Required: No
Description: Whether to use raw parameters

If true, the raw parameters are passed directly to the handler function without any processing.

Example:

// Static configuration
useRawParams: true

// Dynamic configuration
useRawParams: (ctx) => {
  return ctx.model.isDebugMode;
}

#uiMode

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

Controls how the action is displayed in the UI.

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: 'Action Configuration',
    maskClosable: false
  }
}

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

#scene

Type: ActionScene | ActionScene[]
Required: No
Description: The usage scenarios for the action

Restricts the action to be used only in specific scenarios.

Supported scenes:

  • 'settings' - Settings scene
  • 'runtime' - Runtime scene
  • 'design' - Design-time scene

Example:

scene: 'settings'  // Use only in the settings scene
scene: ['settings', 'runtime']  // Use in settings and runtime scenes

#sort

Type: number
Required: No
Description: The sorting weight for the action

Controls the display order of the action in a list. A smaller value means a higher position.

Example:

sort: 0  // Highest position
sort: 10 // Middle position
sort: 100 // Lower position

#Complete Example

class DataProcessingModel extends FlowModel {}

// Register data loading action
DataProcessingModel.registerAction({
  name: 'loadDataAction',
  title: 'Load Data',
  handler: async (ctx, params) => {
    const { url, method = 'GET', timeout = 5000 } = params;
    
    try {
      const response = await fetch(url, {
        method,
        timeout,
        headers: {
          'Content-Type': 'application/json'
        }
      });
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      
      const data = await response.json();
      
      return {
        success: true,
        data,
        message: 'Data loaded successfully'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  },
  defaultParams: {
    method: 'GET',
    timeout: 5000
  },
  uiSchema: {
    'x-component': 'Form',
    properties: {
      url: {
        type: 'string',
        title: 'API URL',
        'x-component': 'Input',
        'x-decorator': 'FormItem',
        required: true
      },
      method: {
        type: 'string',
        title: 'HTTP Method',
        'x-component': 'Select',
        'x-decorator': 'FormItem',
        enum: ['GET', 'POST', 'PUT', 'DELETE']
      },
      timeout: {
        type: 'number',
        title: 'Timeout (ms)',
        'x-component': 'InputNumber',
        'x-decorator': 'FormItem'
      }
    }
  },
  beforeParamsSave: (ctx, params) => {
    if (!params.url) {
      throw new Error('URL is required');
    }
    params.url = params.url.trim();
  },
  afterParamsSave: (ctx, params) => {
    ctx.model.emitter.emit('dataSourceChanged', params);
  },
  uiMode: 'dialog',
  scene: ['settings', 'runtime'],
  sort: 0
});

// Register data processing action
DataProcessingModel.registerAction({
  name: 'processDataAction',
  title: 'Process Data',
  handler: async (ctx, params) => {
    const { data, processor, options = {} } = params;
    
    try {
      const processedData = await processData(data, processor, options);
      
      return {
        success: true,
        data: processedData,
        message: 'Data processed successfully'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  },
  defaultParams: (ctx) => ({
    processor: 'default',
    options: {
      format: 'json',
      encoding: 'utf8'
    }
  }),
  uiSchema: {
    'x-component': 'Form',
    properties: {
      processor: {
        type: 'string',
        title: 'Processor',
        'x-component': 'Select',
        'x-decorator': 'FormItem',
        enum: ['default', 'advanced', 'custom']
      },
      options: {
        type: 'object',
        title: 'Options',
        'x-component': 'Form',
        'x-decorator': 'FormItem',
        properties: {
          format: {
            type: 'string',
            title: 'Format',
            'x-component': 'Select',
            enum: ['json', 'xml', 'csv']
          },
          encoding: {
            type: 'string',
            title: 'Encoding',
            'x-component': 'Select',
            enum: ['utf8', 'ascii', 'latin1']
          }
        }
      }
    }
  },
  scene: 'runtime',
  sort: 1
});