StepDefinition
StepDefinition định nghĩa một bước trong Flow, mỗi bước có thể là một Action, xử lý sự kiện hoặc thao tác khác. Bước là đơn vị thực thi cơ bản của Flow.
Định nghĩa kiểu
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>);
}
Cách dùng
class MyModel extends FlowModel {}
MyModel.registerFlow({
key: 'pageSettings',
steps: {
step1: {
use: 'actionName',
title: 'First Step',
sort: 0,
preset: true
},
step2: {
handler: async (ctx, params) => {
// Logic xử lý tùy chỉnh
return { result: 'success' };
},
title: 'Second Step',
sort: 1
}
}
});
Mô tả thuộc tính
key
Kiểu: string
Bắt buộc: Không
Mô tả: Định danh duy nhất của bước trong Flow
Nếu không cung cấp, sẽ dùng tên key của bước trong đối tượng steps.
Ví dụ:
steps: {
loadData: { // key là 'loadData'
use: 'loadDataAction'
}
}
use
Kiểu: string
Bắt buộc: Không
Mô tả: Tên ActionDefinition đã đăng ký cần dùng
Thông qua thuộc tính use có thể tham chiếu Action đã đăng ký, tránh định nghĩa lặp lại.
Ví dụ:
// Đăng ký Action trước
MyModel.registerAction({
name: 'loadDataAction',
handler: async (ctx, params) => {
// Logic tải dữ liệu
}
});
// Dùng trong bước
steps: {
step1: {
use: 'loadDataAction', // Tham chiếu Action đã đăng ký
title: 'Load Data'
}
}
title
Kiểu: string
Bắt buộc: Không
Mô tả: Tiêu đề hiển thị của bước
Dùng cho hiển thị giao diện và debug.
Ví dụ:
title: 'Load Data'
title: 'Process Information'
title: 'Save Results'
sort
Kiểu: number
Bắt buộc: Không
Mô tả: Thứ tự thực thi bước, số càng nhỏ càng được thực thi trước
Dùng để kiểm soát thứ tự thực thi của nhiều bước trong cùng một Flow.
Ví dụ:
steps: {
step1: { sort: 0 }, // Thực thi đầu tiên
step2: { sort: 1 }, // Thực thi tiếp theo
step3: { sort: 2 } // Thực thi cuối cùng
}
handler
Kiểu: (ctx: FlowRuntimeContext<TModel>, params: any) => Promise<any> | any
Bắt buộc: Không
Mô tả: Hàm xử lý của bước
Khi không dùng thuộc tính use, có thể định nghĩa hàm xử lý trực tiếp.
Ví dụ:
handler: async (ctx, params) => {
// Lấy thông tin context
const { model, flowEngine } = ctx;
// Logic xử lý
const result = await processData(params);
// Trả về kết quả
return { success: true, data: result };
}
defaultParams
Kiểu: Record<string, any> | ((ctx: FlowRuntimeContext<TModel>) => Record<string, any> | Promise<Record<string, any>>)
Bắt buộc: Không
Mô tả: Tham số mặc định của bước
Trước khi bước thực thi, điền giá trị mặc định cho tham số.
Ví dụ:
// Tham số mặc định tĩnh
defaultParams: {
timeout: 5000,
retries: 3,
format: 'json'
}
// Tham số mặc định động
defaultParams: (ctx) => {
return {
userId: ctx.model.uid,
timestamp: Date.now()
}
}
// Tham số mặc định bất đồng bộ
defaultParams: async (ctx) => {
const config = await loadConfig();
return {
apiUrl: config.apiUrl,
apiKey: config.apiKey
}
}
uiSchema
Kiểu: Record<string, ISchema> | ((ctx: FlowRuntimeContext<TModel>) => Record<string, ISchema> | Promise<Record<string, ISchema>>)
Bắt buộc: Không
Mô tả: Schema cấu hình UI của bước
Định nghĩa cách hiển thị của bước trong giao diện và cấu hình form.
Ví dụ:
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
Kiểu: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>
Bắt buộc: Không
Mô tả: Hook function trước khi lưu tham số
Thực thi trước khi tham số bước được lưu, có thể dùng để validate hoặc chuyển đổi tham số.
Ví dụ:
beforeParamsSave: (ctx, params, previousParams) => {
// Validate tham số
if (!params.name) {
throw new Error('Name is required');
}
// Chuyển đổi tham số
params.name = params.name.trim().toLowerCase();
}
afterParamsSave
Kiểu: (ctx: FlowSettingsContext<TModel>, params: any, previousParams: any) => void | Promise<void>
Bắt buộc: Không
Mô tả: Hook function sau khi lưu tham số
Thực thi sau khi tham số bước được lưu, có thể dùng để kích hoạt các thao tác khác.
Ví dụ:
afterParamsSave: (ctx, params, previousParams) => {
// Ghi log
console.log('Step params saved:', params);
// Kích hoạt các thao tác khác
ctx.model.emitter.emit('paramsChanged', params);
}
uiMode
Kiểu: StepUIMode | ((ctx: FlowRuntimeContext<TModel>) => StepUIMode | Promise<StepUIMode>)
Bắt buộc: Không
Mô tả: Chế độ hiển thị UI của bước
Kiểm soát cách hiển thị của bước trong giao diện.
Các chế độ được hỗ trợ:
'dialog' - Chế độ dialog
'drawer' - Chế độ drawer
'embed' - Chế độ nhúng
- Hoặc đối tượng cấu hình tùy chỉnh
Ví dụ:
// Chế độ đơn giản
uiMode: 'dialog'
// Cấu hình tùy chỉnh
uiMode: {
type: 'dialog',
props: {
width: 800,
title: 'Step Configuration'
}
}
// Chế độ động
uiMode: (ctx) => {
return ctx.model.isMobile ? 'drawer' : 'dialog';
}
preset
Kiểu: boolean
Bắt buộc: Không
Mô tả: Có phải bước preset hay không
Tham số của bước có preset: true cần được điền khi tạo, các bước không được đánh dấu có thể điền sau khi tạo model.
Ví dụ:
steps: {
step1: {
preset: true, // Bắt buộc điền tham số khi tạo
use: 'requiredAction'
},
step2: {
preset: false, // Có thể điền tham số sau
use: 'optionalAction'
}
}
paramsRequired
Kiểu: boolean
Bắt buộc: Không
Mô tả: Tham số bước có bắt buộc hay không
Nếu là true, sẽ mở dialog cấu hình trước khi thêm model.
Ví dụ:
paramsRequired: true // Bắt buộc cấu hình tham số trước khi thêm model
paramsRequired: false // Có thể cấu hình tham số sau
hideInSettings
Kiểu: boolean
Bắt buộc: Không
Mô tả: Có ẩn bước trong menu cài đặt hay không
Ví dụ:
hideInSettings: true // Ẩn trong cài đặt
hideInSettings: false // Hiển thị trong cài đặt (mặc định)
isAwait
Kiểu: boolean
Bắt buộc: Không
Mặc định: true
Mô tả: Có chờ hàm xử lý hoàn thành hay không
Ví dụ:
isAwait: true // Chờ hàm xử lý hoàn thành (mặc định)
isAwait: false // Không chờ, thực thi bất đồng bộ
Ví dụ đầy đủ
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'
}
}
}
}
});