iloom-flatten/src/engine/processEngine.ts

327 lines
7.8 KiB
TypeScript
Raw Normal View History

import type { StepType, StepStatus } from '../types';
// 流程步骤配置
export interface StepConfig {
type: StepType;
name: string;
icon: string;
description: string;
required: boolean;
allowSkip: boolean;
validate?: (data: any) => boolean | string;
onComplete?: (data: any) => void;
onEnter?: (data: any) => void;
}
// 流程引擎配置
export interface ProcessEngineConfig {
steps: StepConfig[];
allowParallel: boolean;
autoAdvance: boolean;
requireAllCompleted: boolean;
}
// 流程状态
export interface ProcessState {
currentStepIndex: number;
completedSteps: Set<string>;
skippedSteps: Set<string>;
stepData: Record<string, any>;
isComplete: boolean;
}
// 默认步骤顺序
export const DEFAULT_STEP_ORDER: StepType[] = [
'confirm',
'yarn_purchase',
'dyeing',
'machine_start',
'fabric_warehouse',
];
// 步骤名称映射
export const STEP_NAMES: Record<StepType, string> = {
confirm: '确认计划',
yarn_purchase: '采购纱线',
dyeing: '染纱',
machine_start: '上机生产',
fabric_warehouse: '坯布生产',
};
// 步骤图标映射
export const STEP_ICONS: Record<StepType, string> = {
confirm: 'CheckCircle',
yarn_purchase: 'Package',
dyeing: 'Droplet',
machine_start: 'Settings',
fabric_warehouse: 'Warehouse',
};
// 流程引擎类
export class ProcessEngine {
private config: ProcessEngineConfig;
private state: ProcessState;
private listeners: Set<(state: ProcessState) => void> = new Set();
constructor(config: ProcessEngineConfig) {
this.config = config;
this.state = {
currentStepIndex: 0,
completedSteps: new Set(),
skippedSteps: new Set(),
stepData: {},
isComplete: false,
};
}
// 获取当前步骤
getCurrentStep(): StepConfig | null {
return this.config.steps[this.state.currentStepIndex] || null;
}
// 获取当前步骤索引
getCurrentStepIndex(): number {
return this.state.currentStepIndex;
}
// 获取步骤状态
getStepStatus(stepType: StepType): StepStatus {
if (this.state.completedSteps.has(stepType)) {
return 'completed';
}
const stepIndex = this.config.steps.findIndex((s) => s.type === stepType);
if (stepIndex === this.state.currentStepIndex) {
return 'active';
}
return 'pending';
}
// 获取所有步骤
getSteps(): StepConfig[] {
return this.config.steps;
}
// 获取流程状态
getState(): ProcessState {
return { ...this.state };
}
// 检查是否可以进入下一步
canAdvance(): boolean {
const currentStep = this.getCurrentStep();
if (!currentStep) return false;
// 如果当前步骤已完成或允许跳过
if (
this.state.completedSteps.has(currentStep.type) ||
this.state.skippedSteps.has(currentStep.type)
) {
return true;
}
// 检查验证
if (currentStep.validate) {
const data = this.state.stepData[currentStep.type];
const result = currentStep.validate(data);
return result === true;
}
return !currentStep.required;
}
// 进入下一步
advance(): boolean {
if (!this.canAdvance()) return false;
const currentStep = this.getCurrentStep();
if (currentStep?.onComplete) {
currentStep.onComplete(this.state.stepData[currentStep.type]);
}
if (this.state.currentStepIndex < this.config.steps.length - 1) {
this.state.currentStepIndex++;
const nextStep = this.getCurrentStep();
if (nextStep?.onEnter) {
nextStep.onEnter(this.state.stepData[nextStep.type]);
}
} else {
this.state.isComplete = true;
}
this.notifyListeners();
return true;
}
// 跳转到指定步骤
jumpToStep(stepType: StepType): boolean {
const stepIndex = this.config.steps.findIndex((s) => s.type === stepType);
if (stepIndex === -1) return false;
// 检查是否可以跳转(只能跳转到已完成步骤的下一步)
if (!this.config.allowParallel) {
for (let i = 0; i < stepIndex; i++) {
const step = this.config.steps[i];
if (
step.required &&
!this.state.completedSteps.has(step.type) &&
!this.state.skippedSteps.has(step.type)
) {
return false;
}
}
}
this.state.currentStepIndex = stepIndex;
const step = this.getCurrentStep();
if (step?.onEnter) {
step.onEnter(this.state.stepData[step.type]);
}
this.notifyListeners();
return true;
}
// 完成当前步骤
completeStep(data?: any): boolean {
const currentStep = this.getCurrentStep();
if (!currentStep) return false;
// 验证数据
if (currentStep.validate && data !== undefined) {
const result = currentStep.validate(data);
if (result !== true) {
return false;
}
}
this.state.completedSteps.add(currentStep.type);
if (data !== undefined) {
this.state.stepData[currentStep.type] = data;
}
if (currentStep.onComplete) {
currentStep.onComplete(data);
}
if (this.config.autoAdvance) {
this.advance();
}
this.notifyListeners();
return true;
}
// 跳过当前步骤
skipStep(): boolean {
const currentStep = this.getCurrentStep();
if (!currentStep || !currentStep.allowSkip) return false;
this.state.skippedSteps.add(currentStep.type);
this.advance();
this.notifyListeners();
return true;
}
// 设置步骤数据
setStepData(stepType: StepType, data: any): void {
this.state.stepData[stepType] = data;
this.notifyListeners();
}
// 获取步骤数据
getStepData(stepType: StepType): any {
return this.state.stepData[stepType];
}
// 重置流程
reset(): void {
this.state = {
currentStepIndex: 0,
completedSteps: new Set(),
skippedSteps: new Set(),
stepData: {},
isComplete: false,
};
this.notifyListeners();
}
// 订阅状态变化
subscribe(listener: (state: ProcessState) => void): () => void {
this.listeners.add(listener);
return () => {
this.listeners.delete(listener);
};
}
// 通知监听器
private notifyListeners(): void {
this.listeners.forEach((listener) => listener(this.getState()));
}
}
// 创建默认流程引擎
export function createDefaultProcessEngine(
customSteps?: Partial<StepConfig>[]
): ProcessEngine {
const defaultSteps: StepConfig[] = DEFAULT_STEP_ORDER.map((type) => ({
type,
name: STEP_NAMES[type],
icon: STEP_ICONS[type],
description: '',
required: true,
allowSkip: false,
...(customSteps?.find((s) => s.type === type) || {}),
}));
return new ProcessEngine({
steps: defaultSteps,
allowParallel: false,
autoAdvance: false,
requireAllCompleted: true,
});
}
// React Hook 封装
import { useState, useEffect, useCallback } from 'react';
export function useProcessEngine(engine: ProcessEngine) {
const [state, setState] = useState<ProcessState>(engine.getState());
useEffect(() => {
return engine.subscribe((newState) => {
setState(newState);
});
}, [engine]);
const advance = useCallback(() => engine.advance(), [engine]);
const completeStep = useCallback(
(data?: any) => engine.completeStep(data),
[engine]
);
const skipStep = useCallback(() => engine.skipStep(), [engine]);
const jumpToStep = useCallback(
(stepType: StepType) => engine.jumpToStep(stepType),
[engine]
);
const setStepData = useCallback(
(stepType: StepType, data: any) => engine.setStepData(stepType, data),
[engine]
);
const reset = useCallback(() => engine.reset(), [engine]);
return {
state,
currentStep: engine.getCurrentStep(),
currentStepIndex: engine.getCurrentStepIndex(),
steps: engine.getSteps(),
getStepStatus: engine.getStepStatus.bind(engine),
canAdvance: engine.canAdvance.bind(engine),
advance,
completeStep,
skipStep,
jumpToStep,
setStepData,
reset,
};
}