增加审批组件

This commit is contained in:
dengqichen 2025-10-24 23:03:54 +08:00
parent 164a39bd42
commit 114d549a5c
3 changed files with 166 additions and 81 deletions

View File

@ -11,10 +11,12 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Loader2, FileText, Tag, AlignLeft, Folder } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { useToast } from '@/components/ui/use-toast'; import { useToast } from '@/components/ui/use-toast';
import type { WorkflowDefinition, WorkflowCategoryResponse } from '../types'; import type { WorkflowDefinition, WorkflowCategoryResponse, WorkflowDefinitionRequest } from '../types';
import { saveDefinition, updateDefinition, getWorkflowCategoryList } from '../service'; import { saveDefinition, updateDefinition, getWorkflowCategoryList } from '../service';
import { getDefinitions as getFormDefinitions } from '@/pages/Form/Definition/service';
import type { FormDefinitionResponse } from '@/pages/Form/Definition/types';
interface EditModalProps { interface EditModalProps {
visible: boolean; visible: boolean;
@ -28,32 +30,34 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
const isEdit = !!record; const isEdit = !!record;
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const [categories, setCategories] = useState<WorkflowCategoryResponse[]>([]); const [categories, setCategories] = useState<WorkflowCategoryResponse[]>([]);
const [formDefinitions, setFormDefinitions] = useState<FormDefinitionResponse[]>([]);
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
name: '', name: '',
key: '', key: '',
categoryId: undefined as number | undefined, categoryId: undefined as number | undefined,
formDefinitionId: undefined as number | undefined,
description: '', description: '',
triggers: [] as string[],
}); });
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
loadCategories(); loadCategories();
loadFormDefinitions();
if (record) { if (record) {
setFormData({ setFormData({
name: record.name, name: record.name,
key: record.key, key: record.key,
categoryId: record.categoryId, categoryId: record.categoryId,
formDefinitionId: record.formDefinitionId,
description: record.description || '', description: record.description || '',
triggers: record.triggers || [],
}); });
} else { } else {
setFormData({ setFormData({
name: '', name: '',
key: '', key: '',
categoryId: undefined, categoryId: undefined,
formDefinitionId: undefined,
description: '', description: '',
triggers: [],
}); });
} }
} }
@ -73,6 +77,17 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
} }
}; };
const loadFormDefinitions = async () => {
try {
const result = await getFormDefinitions({ status: 'PUBLISHED' });
if (result?.content) {
setFormDefinitions(result.content);
}
} catch (error) {
console.error('加载表单定义失败:', error);
}
};
const handleClose = () => { const handleClose = () => {
if (!submitting) { if (!submitting) {
onClose(); onClose();
@ -124,22 +139,39 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
setSubmitting(true); setSubmitting(true);
try { try {
const submitData: WorkflowDefinition = {
...formData,
id: record?.id || 0,
flowVersion: isEdit ? record.flowVersion : 1,
status: isEdit ? record.status : 'DRAFT',
graph: record?.graph || { nodes: [], edges: [] },
formConfig: record?.formConfig || { formItems: [] },
} as WorkflowDefinition;
if (isEdit && record) { if (isEdit && record) {
// 更新时需要传递完整的数据和版本号
const submitData: WorkflowDefinitionRequest = {
name: formData.name,
key: formData.key,
categoryId: formData.categoryId,
formDefinitionId: formData.formDefinitionId,
flowVersion: record.flowVersion, // 流程版本
description: formData.description,
graph: record.graph,
bpmnXml: record.bpmnXml,
status: record.status,
version: record.version, // 乐观锁版本号
};
await updateDefinition(record.id, submitData); await updateDefinition(record.id, submitData);
toast({ toast({
title: '更新成功', title: '更新成功',
description: `工作流 "${formData.name}" 已更新`, description: `工作流 "${formData.name}" 已更新`,
}); });
} else { } else {
// 新建时初始化基本数据
const submitData: WorkflowDefinitionRequest = {
name: formData.name,
key: formData.key,
categoryId: formData.categoryId,
formDefinitionId: formData.formDefinitionId,
flowVersion: 1, // 新建时流程版本为1
description: formData.description,
graph: { nodes: [], edges: [] },
status: 'DRAFT',
};
await saveDefinition(submitData); await saveDefinition(submitData);
toast({ toast({
title: '创建成功', title: '创建成功',
@ -161,8 +193,6 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
} }
}; };
const selectedCategory = categories.find(c => c.id === formData.categoryId);
return ( return (
<Dialog open={visible} onOpenChange={handleClose}> <Dialog open={visible} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[600px]"> <DialogContent className="sm:max-w-[600px]">
@ -172,8 +202,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
<div className="grid gap-6 py-4"> <div className="grid gap-6 py-4">
{/* 流程分类 */} {/* 流程分类 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="categoryId" className="flex items-center gap-2"> <Label htmlFor="categoryId">
<Folder className="h-4 w-4 text-muted-foreground" />
<span className="text-destructive">*</span> <span className="text-destructive">*</span>
</Label> </Label>
<Select <Select
@ -182,7 +211,6 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
setFormData(prev => ({ setFormData(prev => ({
...prev, ...prev,
categoryId: Number(value), categoryId: Number(value),
triggers: [], // 切换分类时清空触发器
})); }));
}} }}
disabled={isEdit} disabled={isEdit}
@ -205,27 +233,56 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
)} )}
</div> </div>
{/* 流程名称 */} {/* 启动表单 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="name" className="flex items-center gap-2"> <Label htmlFor="formDefinitionId">
<FileText className="h-4 w-4 text-muted-foreground" />
<span className="text-destructive">*</span> {isEdit && <span className="text-xs text-muted-foreground ml-1">()</span>}
</Label> </Label>
<Input <div className="flex gap-2">
id="name" <Select
placeholder="例如Jenkins 构建流程" value={formData.formDefinitionId?.toString() || ''}
value={formData.name} onValueChange={(value) => {
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))} setFormData(prev => ({
className="h-10" ...prev,
/> formDefinitionId: value ? Number(value) : undefined,
}));
}}
disabled={isEdit}
>
<SelectTrigger id="formDefinitionId" className="h-10 flex-1">
<SelectValue placeholder="请选择启动表单(可选)" />
</SelectTrigger>
<SelectContent>
{formDefinitions.map(form => (
<SelectItem key={form.id} value={form.id.toString()}>
{form.name} ({form.key})
</SelectItem>
))}
</SelectContent>
</Select>
{formData.formDefinitionId && !isEdit && (
<Button
type="button"
variant="outline"
size="icon"
className="h-10 w-10 shrink-0"
onClick={() => setFormData(prev => ({ ...prev, formDefinitionId: undefined }))}
>
×
</Button>
)}
</div>
<p className="text-xs text-muted-foreground">
</p>
</div> </div>
{/* 流程标识 */} {/* 流程标识 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="key" className="flex items-center gap-2"> <Label htmlFor="key">
<Tag className="h-4 w-4 text-muted-foreground" />
<span className="text-destructive">*</span> <span className="text-destructive">*</span>
{isEdit && <span className="text-xs text-muted-foreground">()</span>} {isEdit && <span className="text-xs text-muted-foreground ml-1">()</span>}
</Label> </Label>
<Input <Input
id="key" id="key"
@ -240,10 +297,25 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
</p> </p>
</div> </div>
{/* 流程名称 */}
<div className="space-y-2">
<Label htmlFor="name">
<span className="text-destructive">*</span>
{isEdit && <span className="text-xs text-muted-foreground ml-1">()</span>}
</Label>
<Input
id="name"
placeholder="例如Jenkins 构建流程"
value={formData.name}
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
disabled={isEdit}
className="h-10"
/>
</div>
{/* 描述 */} {/* 描述 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="description" className="flex items-center gap-2"> <Label htmlFor="description">
<AlignLeft className="h-4 w-4 text-muted-foreground" />
</Label> </Label>
<Textarea <Textarea
@ -255,17 +327,6 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
/> />
</div> </div>
{/* 提示:触发方式在设计阶段配置 */}
{selectedCategory && selectedCategory.supportedTriggers && selectedCategory.supportedTriggers.length > 0 && (
<div className="rounded-lg border p-4 bg-muted/30">
<p className="text-sm text-muted-foreground">
<strong></strong> {selectedCategory.supportedTriggers.join(', ')}
</p>
<p className="text-xs text-muted-foreground mt-1">
</p>
</div>
)}
</div> </div>
<DialogFooter> <DialogFooter>

View File

@ -2,6 +2,7 @@ import request from '@/utils/request';
import { import {
WorkflowDefinition, WorkflowDefinition,
WorkflowDefinitionQuery, WorkflowDefinitionQuery,
WorkflowDefinitionRequest,
WorkflowCategoryResponse, WorkflowCategoryResponse,
WorkflowCategoryQuery, WorkflowCategoryQuery,
WorkflowCategoryRequest WorkflowCategoryRequest
@ -27,10 +28,10 @@ export const deployDefinition = (id: number) =>
export const deleteDefinition = (id: number) => export const deleteDefinition = (id: number) =>
request.delete<void>(`${DEFINITION_URL}/${id}`); request.delete<void>(`${DEFINITION_URL}/${id}`);
export const saveDefinition = (data: WorkflowDefinition) => export const saveDefinition = (data: WorkflowDefinitionRequest) =>
request.post<WorkflowDefinition>(`${DEFINITION_URL}`, data); request.post<WorkflowDefinition>(`${DEFINITION_URL}`, data);
export const updateDefinition = (id: number, data: WorkflowDefinition) => export const updateDefinition = (id: number, data: WorkflowDefinitionRequest) =>
request.put<WorkflowDefinition>(`${DEFINITION_URL}/${id}`, data); request.put<WorkflowDefinition>(`${DEFINITION_URL}/${id}`, data);
/** /**

View File

@ -1,41 +1,45 @@
import {BaseResponse, BaseQuery} from '@/types/base'; import {BaseResponse, BaseQuery} from '@/types/base';
/**
*
*/
export type WorkflowDefinitionStatus = 'DRAFT' | 'PUBLISHED' | 'DISABLED';
/**
*
*/
export interface WorkflowDefinition extends BaseResponse { export interface WorkflowDefinition extends BaseResponse {
id: number;
name: string; name: string;
key: string; key: string;
description?: string;
flowVersion?: number;
status?: string;
categoryId?: number; // 分类ID categoryId?: number; // 分类ID
triggers: string[]; formDefinitionId?: number; // 启动表单ID
graph: { processDefinitionId?: string; // 流程定义ID
nodes: WorkflowDefinitionNode[]; flowVersion: number; // 流程版本
edges: any[]; bpmnXml?: string; // BPMN XML内容
}; graph?: WorkflowDefinitionGraph; // 流程图数据
formConfig: { status: WorkflowDefinitionStatus; // 流程状态
formItems: any[]; description?: string; // 流程描述
}; }
formVariablesSchema?: {
type: string; /**
required?: string[]; *
properties: { */
[key: string]: { export interface WorkflowDefinitionGraph {
type: string; nodes: WorkflowDefinitionNode[];
title: string; edges: WorkflowDefinitionEdge[];
description?: string; }
dataSource?: {
url: string; /**
type: string; *
params?: Record<string, any>; */
dependsOn?: string[]; export interface WorkflowDefinitionEdge {
labelField?: string; id: string;
valueField?: string; source: string;
}; target: string;
}; sourceHandle?: string;
}; targetHandle?: string;
}; type?: string;
bpmnXml?: string; label?: string;
} }
export interface WorkflowDefinitionNode { export interface WorkflowDefinitionNode {
@ -57,11 +61,30 @@ export interface WorkflowDefinitionNode {
}>; }>;
} }
/**
*
*/
export interface WorkflowDefinitionQuery extends BaseQuery { export interface WorkflowDefinitionQuery extends BaseQuery {
name?: string; name?: string;
key?: string; key?: string;
categoryId?: number; // 分类ID筛选 categoryId?: number; // 分类ID筛选
status?: string; status?: WorkflowDefinitionStatus;
}
/**
* /
*/
export interface WorkflowDefinitionRequest {
name: string;
key: string;
categoryId?: number;
formDefinitionId?: number;
flowVersion?: number; // 流程版本新建时传1更新时传当前版本
description?: string;
graph?: WorkflowDefinitionGraph;
bpmnXml?: string;
status?: WorkflowDefinitionStatus;
version?: number; // 乐观锁版本号(更新时必传)
} }
/** /**