增加审批组件
This commit is contained in:
parent
164a39bd42
commit
114d549a5c
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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
|
||||||
|
flowVersion: number; // 流程版本
|
||||||
|
bpmnXml?: string; // BPMN XML内容
|
||||||
|
graph?: WorkflowDefinitionGraph; // 流程图数据
|
||||||
|
status: WorkflowDefinitionStatus; // 流程状态
|
||||||
|
description?: string; // 流程描述
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作流定义图数据
|
||||||
|
*/
|
||||||
|
export interface WorkflowDefinitionGraph {
|
||||||
nodes: WorkflowDefinitionNode[];
|
nodes: WorkflowDefinitionNode[];
|
||||||
edges: any[];
|
edges: WorkflowDefinitionEdge[];
|
||||||
};
|
}
|
||||||
formConfig: {
|
|
||||||
formItems: any[];
|
/**
|
||||||
};
|
* 工作流定义边
|
||||||
formVariablesSchema?: {
|
*/
|
||||||
type: string;
|
export interface WorkflowDefinitionEdge {
|
||||||
required?: string[];
|
id: string;
|
||||||
properties: {
|
source: string;
|
||||||
[key: string]: {
|
target: string;
|
||||||
type: string;
|
sourceHandle?: string;
|
||||||
title: string;
|
targetHandle?: string;
|
||||||
description?: string;
|
type?: string;
|
||||||
dataSource?: {
|
label?: string;
|
||||||
url: string;
|
|
||||||
type: string;
|
|
||||||
params?: Record<string, any>;
|
|
||||||
dependsOn?: string[];
|
|
||||||
labelField?: string;
|
|
||||||
valueField?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
bpmnXml?: 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; // 乐观锁版本号(更新时必传)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user