import { useEffect, useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { archiveWorkflowDefinition, createWorkflowDefinition, fetchWorkflowDefinitions, publishWorkflowDefinition, updateWorkflowDefinition, type WorkflowDefinitionEditorModel, } from "../api/workflowDefinitions"; import { PageHeader } from "../components/common/PageHeader"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; import { toast } from "sonner"; const ENTITY_TYPES: WorkflowDefinitionEditorModel["entityType"][] = ["reimbursement", "leave", "purchase_request", "travel_request"]; type DraftStep = WorkflowDefinitionEditorModel["steps"][number]; function emptyStep(): DraftStep { return { id: `step_${Date.now()}`, name: "审批节点", assigneeRule: { type: "department_manager" } }; } export function WorkflowDefinitionsPage() { const queryClient = useQueryClient(); const [entityType, setEntityType] = useState("leave"); const [selectedId, setSelectedId] = useState(""); const [name, setName] = useState(""); const [notes, setNotes] = useState(""); const [steps, setSteps] = useState([emptyStep()]); const listQuery = useQuery({ queryKey: ["workflow-definitions"], queryFn: () => fetchWorkflowDefinitions() }); const items = listQuery.data?.items ?? []; const scopedItems = useMemo(() => items.filter((item) => item.entityType === entityType), [items, entityType]); const selected = useMemo(() => scopedItems.find((item) => item.id === selectedId) ?? scopedItems[0] ?? null, [scopedItems, selectedId]); useEffect(() => { if (!selected) return; setSelectedId(selected.id); setName(selected.name); setNotes(selected.notes ?? ""); setSteps(selected.steps.length ? selected.steps : [emptyStep()]); }, [selected?.id]); const refresh = () => queryClient.invalidateQueries({ queryKey: ["workflow-definitions"] }); const createMutation = useMutation({ mutationFn: () => createWorkflowDefinition({ entityType, name: name.trim() || `${entityType}-workflow`, notes, steps }), onSuccess: async ({ item }) => { setSelectedId(item.id); await refresh(); toast.success("流程定义草稿已创建"); }, }); const saveMutation = useMutation({ mutationFn: (id: string) => updateWorkflowDefinition(id, { name, notes, steps }), onSuccess: async () => { await refresh(); toast.success("流程定义已保存"); }, }); const publishMutation = useMutation({ mutationFn: publishWorkflowDefinition, onSuccess: async () => { await refresh(); toast.success("流程定义已发布"); }, }); const archiveMutation = useMutation({ mutationFn: archiveWorkflowDefinition, onSuccess: async () => { await refresh(); toast.success("流程定义已归档"); }, }); return (
{entityType !== "reimbursement" ? : null}

版本列表

{scopedItems.map((item) => ( ))} {!scopedItems.length ?
当前实体暂无流程定义
: null}
{selected ? ( <>
{selected.source}
{selected.name}
{selected.status !== "derived_readonly" ? (
) : null}
setName(event.target.value)} disabled={selected.status !== "draft"} />
setNotes(event.target.value)} disabled={selected.status !== "draft"} />

步骤

{selected.status === "draft" ? : null}
{steps.map((step, index) => (
setSteps((prev) => prev.map((item, itemIndex) => itemIndex === index ? { ...item, id: event.target.value } : item))} /> setSteps((prev) => prev.map((item, itemIndex) => itemIndex === index ? { ...item, name: event.target.value } : item))} />
{step.assigneeRule.type !== "department_manager" ? (
setSteps((prev) => prev.map((item, itemIndex) => itemIndex === index ? { ...item, assigneeRule: { ...item.assigneeRule, value: event.target.value } as DraftStep["assigneeRule"] } : item))} placeholder={step.assigneeRule.type === "role" ? "角色编码" : "用户 ID"} />
) : null}
))}
) : (
暂无可编辑流程定义
)}
); }