统一状态流转为前端状态机

This commit is contained in:
Cheng Zhou
2025-12-17 20:52:55 +08:00
parent c074c2b5bc
commit c2328594b0
15 changed files with 361 additions and 188 deletions
+19
View File
@@ -0,0 +1,19 @@
import type { StateMachine } from "./types";
export const aeMachine: StateMachine = {
states: {
NEW: { value: "NEW", label: "新增", color: "info" },
FOLLOW_UP: { value: "FOLLOW_UP", label: "随访中", color: "warning" },
CLOSED: { value: "CLOSED", label: "已关闭", color: "success", isTerminal: true },
},
actions: {
close: {
key: "close",
label: "关闭",
from: ["NEW", "FOLLOW_UP"],
to: "CLOSED",
confirm: true,
confirmText: "确定关闭该 AE?该操作不可撤销。",
},
},
};
@@ -0,0 +1,43 @@
import type { StateMachine } from "./types";
export const financeMachine: StateMachine = {
states: {
DRAFT: { value: "DRAFT", label: "草稿", color: "info" },
SUBMITTED: { value: "SUBMITTED", label: "已提交", color: "warning" },
APPROVED: { value: "APPROVED", label: "已审批", color: "success" },
REJECTED: { value: "REJECTED", label: "已驳回", color: "danger", isTerminal: true },
PAID: { value: "PAID", label: "已支付", color: "success", isTerminal: true },
},
actions: {
submit: {
key: "submit",
label: "提交",
from: ["DRAFT"],
to: "SUBMITTED",
},
approve: {
key: "approve",
label: "审批通过",
from: ["SUBMITTED"],
to: "APPROVED",
},
reject: {
key: "reject",
label: "驳回",
from: ["SUBMITTED"],
to: "REJECTED",
confirm: true,
confirmText: "确认驳回该费用吗?",
danger: true,
},
pay: {
key: "pay",
label: "确认支付",
from: ["APPROVED"],
to: "PAID",
confirm: true,
confirmText: "确认支付该费用吗?",
danger: true,
},
},
};
@@ -0,0 +1,8 @@
import type { StateMachine } from "./types";
export const impMachine: StateMachine = {
states: {
CREATED: { value: "CREATED", label: "已创建", color: "info" },
},
actions: {},
};
+30
View File
@@ -0,0 +1,30 @@
import type { StateMachine } from "./types";
export const getStateLabel = (machine: StateMachine, stateValue?: string | null) => {
if (!stateValue) return "";
return machine.states[stateValue]?.label || stateValue;
};
export const getStateColor = (machine: StateMachine, stateValue?: string | null) => {
if (!stateValue) return undefined;
return machine.states[stateValue]?.color;
};
export const getAvailableActions = (machine: StateMachine, currentState?: string | null) => {
if (!currentState) return [];
return Object.values(machine.actions).filter((a) => a.from.includes(currentState));
};
export const canDoAction = (machine: StateMachine, actionKey: string, currentState?: string | null) => {
if (!currentState) return false;
const action = machine.actions[actionKey];
if (!action) return false;
return action.from.includes(currentState);
};
export * from "./types";
export * from "./task.machine";
export * from "./subject.machine";
export * from "./ae.machine";
export * from "./finance.machine";
export * from "./imp.machine";
@@ -0,0 +1,33 @@
import type { StateMachine } from "./types";
export const subjectMachine: StateMachine = {
states: {
SCREENING: { value: "SCREENING", label: "筛选中", color: "info" },
ENROLLED: { value: "ENROLLED", label: "已入组", color: "success" },
COMPLETED: { value: "COMPLETED", label: "已完成", color: "success", isTerminal: true },
DROPPED: { value: "DROPPED", label: "已脱落", color: "danger", isTerminal: true },
},
actions: {
enroll: {
key: "enroll",
label: "标记入组",
from: ["SCREENING"],
to: "ENROLLED",
},
complete: {
key: "complete",
label: "标记完成",
from: ["ENROLLED"],
to: "COMPLETED",
},
drop: {
key: "drop",
label: "标记脱落",
from: ["SCREENING", "ENROLLED"],
to: "DROPPED",
danger: true,
confirm: true,
confirmText: "确定将该受试者标记为脱落?",
},
},
};
@@ -0,0 +1,16 @@
import type { StateMachine } from "./types";
export const taskMachine: StateMachine = {
states: {
TODO: { value: "TODO", label: "待处理", color: "info" },
DONE: { value: "DONE", label: "已完成", color: "success", isTerminal: true },
},
actions: {
complete: {
key: "complete",
label: "完成",
from: ["TODO"],
to: "DONE",
},
},
};
+21
View File
@@ -0,0 +1,21 @@
export interface StateConfig {
value: string;
label: string;
color?: string;
isTerminal?: boolean;
}
export interface ActionConfig {
key: string;
label: string;
from: string[];
to: string;
confirm?: boolean;
confirmText?: string;
danger?: boolean;
}
export interface StateMachine {
states: Record<string, StateConfig>;
actions: Record<string, ActionConfig>;
}