import React, { useState, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '@/components/ui/dialog'; import { DataTablePagination } from '@/components/ui/pagination'; import { Loader2, Plus, Search, Edit, Trash2, Play, CheckCircle2, Clock, Activity, Workflow, MoreHorizontal, AlertCircle, Eye } from 'lucide-react'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { useToast } from '@/components/ui/use-toast'; import * as service from './service'; import type { WorkflowDefinition, WorkflowDefinitionQuery, WorkflowCategoryResponse } from './types'; import { DEFAULT_PAGE_SIZE, DEFAULT_CURRENT } from '@/utils/page'; import EditModal from './components/EditModal'; const WorkflowDefinitionList: React.FC = () => { const navigate = useNavigate(); const { toast } = useToast(); const [loading, setLoading] = useState(false); const [pageData, setPageData] = useState<{ content: WorkflowDefinition[]; totalElements: number; size: number; number: number; } | null>(null); const [modalVisible, setModalVisible] = useState(false); const [currentRecord, setCurrentRecord] = useState(); const [categories, setCategories] = useState([]); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deleteRecord, setDeleteRecord] = useState(null); const [deployDialogOpen, setDeployDialogOpen] = useState(false); const [deployRecord, setDeployRecord] = useState(null); const [query, setQuery] = useState({ pageNum: DEFAULT_CURRENT - 1, pageSize: DEFAULT_PAGE_SIZE, name: '', categoryId: undefined, status: undefined }); const loadData = async (params: WorkflowDefinitionQuery) => { setLoading(true); try { const data = await service.getDefinitions(params); setPageData(data); } catch (error) { if (error instanceof Error) { toast({ title: '加载失败', description: error.message, variant: 'destructive' }); } } finally { setLoading(false); } }; const loadCategories = async () => { try { const data = await service.getWorkflowCategoryList(); setCategories(data); } catch (error) { console.error('加载工作流分类失败:', error); } }; useEffect(() => { loadCategories(); }, []); useEffect(() => { loadData(query); }, [query]); const handleCreateFlow = () => { setCurrentRecord(undefined); setModalVisible(true); }; const handleEditFlow = (record: WorkflowDefinition) => { setCurrentRecord(record); setModalVisible(true); }; const handleDesignFlow = (record: WorkflowDefinition) => { navigate(`/workflow/design/${record.id}`); }; const handleModalClose = () => { setModalVisible(false); setCurrentRecord(undefined); }; const handleSearch = () => { setQuery(prev => ({ ...prev, pageNum: 0, })); }; const handleReset = () => { setQuery({ pageNum: 0, pageSize: DEFAULT_PAGE_SIZE, name: '', categoryId: undefined, status: undefined }); }; const handleDeploy = (record: WorkflowDefinition) => { setDeployRecord(record); setDeployDialogOpen(true); }; const confirmDeploy = async () => { if (!deployRecord) return; try { await service.publishDefinition(deployRecord.id); toast({ title: '发布成功', description: `工作流 "${deployRecord.name}" 已发布`, }); loadData(query); setDeployDialogOpen(false); } catch (error) { if (error instanceof Error) { toast({ title: '发布失败', description: error.message, variant: 'destructive' }); } } }; const handleDelete = (record: WorkflowDefinition) => { setDeleteRecord(record); setDeleteDialogOpen(true); }; const confirmDelete = async () => { if (!deleteRecord) return; try { await service.deleteDefinition(deleteRecord.id); toast({ title: '删除成功', description: `工作流 "${deleteRecord.name}" 已删除`, }); loadData(query); setDeleteDialogOpen(false); } catch (error) { if (error instanceof Error) { toast({ title: '删除失败', description: error.message, variant: 'destructive' }); } } }; const handleStartFlow = async (record: WorkflowDefinition) => { try { await service.startWorkflowInstance(record.key, record.categoryId); toast({ title: '启动成功', description: `工作流 "${record.name}" 已启动`, }); } catch (error) { if (error instanceof Error) { toast({ title: '启动失败', description: error.message, variant: 'destructive' }); } } }; // 状态徽章 const getStatusBadge = (status: string) => { const statusMap: Record = { DRAFT: { variant: 'outline', text: '草稿', icon: Clock }, PUBLISHED: { variant: 'success', text: '已发布', icon: CheckCircle2 }, }; const statusInfo = statusMap[status] || { variant: 'outline', text: status, icon: Clock }; const Icon = statusInfo.icon; return ( {statusInfo.text} ); }; // 统计数据 const stats = useMemo(() => { const total = pageData?.totalElements || 0; const draftCount = pageData?.content?.filter(d => d.status === 'DRAFT').length || 0; const publishedCount = pageData?.content?.filter(d => d.status !== 'DRAFT').length || 0; return { total, draftCount, publishedCount }; }, [pageData]); const pageCount = pageData?.totalElements ? Math.ceil(pageData.totalElements / (query.pageSize || DEFAULT_PAGE_SIZE)) : 0; return (

工作流定义管理

{/* 统计卡片 */}
总工作流
{stats.total}

全部工作流定义

草稿
{stats.draftCount}

待发布的工作流

已发布
{stats.publishedCount}

正在使用的工作流

工作流列表 {/* 搜索栏 */}
setQuery(prev => ({ ...prev, name: e.target.value }))} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="h-9" />
{/* 表格 */}
流程名称 流程标识 流程分类 版本 状态 描述 操作 {loading ? (
加载中...
) : pageData?.content && pageData.content.length > 0 ? ( pageData.content.map((record) => { const categoryInfo = categories.find(c => c.id === record.categoryId); return ( {record.name} {record.key} {categoryInfo ? ( {categoryInfo.name} ) : ( 未分类 )} {record.flowVersion || 1} {getStatusBadge(record.status || 'DRAFT')} {record.description || '-'} {record.status === 'DRAFT' && ( <> handleEditFlow(record)}> 编辑 handleDesignFlow(record)}> 设计 handleDeploy(record)}> 发布 )} {record.status !== 'DRAFT' && ( <> handleStartFlow(record)}> 启动 handleDesignFlow(record)}> 查看 )} handleDelete(record)} className="text-red-600 focus:text-red-600" > 删除 ); }) ) : (
暂无工作流定义
点击右上角"新建工作流"开始设计您的第一个工作流。
)}
{/* 分页 */} {pageCount > 1 && ( setQuery(prev => ({ ...prev, pageNum: page - 1 }))} /> )}
{/* EditModal */} loadData(query)} record={currentRecord} /> {/* 发布确认对话框 */} {deployRecord && ( 确认发布工作流? 您确定要发布工作流 "{deployRecord.name}" 吗? 发布后将可以启动执行。 )} {/* 删除确认对话框 */} {deleteRecord && ( 确认删除工作流? 您确定要删除工作流 "{deleteRecord.name}" 吗? 此操作不可逆。

标识: {deleteRecord.key}

版本: {deleteRecord.flowVersion || 1}

状态: {getStatusBadge(deleteRecord.status || 'DRAFT')}

)}
); }; export default WorkflowDefinitionList;