This commit is contained in:
asp_ly 2024-12-30 20:49:04 +08:00
parent 8fe80fd780
commit 1c166c2559
2 changed files with 334 additions and 94 deletions

View File

@ -1,135 +1,368 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { ProTable } from '@ant-design/pro-components';
import type { ProColumns } from '@ant-design/pro-components';
import { Tag, Space, Button, Modal } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { PageContainer } from '@/components/ui/page-container';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import { NodeTypeEnum, type NodeDesignDataResponse } from './types'; import { NodeTypeEnum, type NodeDesignDataResponse } from './types';
import * as service from './service'; import * as service from './service';
import {
Table,
TableHeader,
TableBody,
TableHead,
TableRow,
TableCell,
} from "@/components/ui/table";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { useToast } from "@/components/ui/use-toast";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { DataTablePagination } from "@/components/ui/pagination";
// 节点类型标签颜色映射 // 节点类型标签样式映射
const nodeTypeColors = { const nodeTypeStyles = {
[NodeTypeEnum.START_EVENT]: 'green', [NodeTypeEnum.START_EVENT]: {
[NodeTypeEnum.END_EVENT]: 'red', variant: 'success' as const,
[NodeTypeEnum.SCRIPT_TASK]: 'blue', label: '开始事件'
},
[NodeTypeEnum.END_EVENT]: {
variant: 'destructive' as const,
label: '结束事件'
},
[NodeTypeEnum.SCRIPT_TASK]: {
variant: 'default' as const,
label: '脚本任务'
},
}; };
interface Column {
accessorKey?: keyof NodeDesignDataResponse;
id?: string;
header: string;
size: number;
cell?: (props: { row: { original: NodeDesignDataResponse } }) => React.ReactNode;
}
const NodeDesignList: React.FC = () => { const NodeDesignList: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [detailVisible, setDetailVisible] = useState(false); const [detailVisible, setDetailVisible] = useState(false);
const [currentNode, setCurrentNode] = useState<NodeDesignDataResponse>(); const [currentNode, setCurrentNode] = useState<NodeDesignDataResponse>();
const [list, setList] = useState<NodeDesignDataResponse[]>([]);
const [loading, setLoading] = useState(false);
const [pagination, setPagination] = useState({
pageNum: 1,
pageSize: 10,
totalElements: 0,
});
const { toast } = useToast();
// 表格列定义 const loadData = async () => {
const columns: ProColumns<NodeDesignDataResponse>[] = [ setLoading(true);
try {
const response = await service.getNodeDefinitions({
current: pagination.pageNum,
pageSize: pagination.pageSize,
});
setList(response.content || []);
setPagination({
...pagination,
totalElements: response.totalElements,
});
} catch (error) {
toast({
variant: "destructive",
title: "获取节点列表失败",
duration: 3000,
});
} finally {
setLoading(false);
}
};
const handlePageChange = (page: number) => {
setPagination({
...pagination,
pageNum: page,
});
};
const handleDelete = async (id: number) => {
try {
await service.deleteNodeDefinition(id);
toast({
title: "删除成功",
duration: 3000,
});
loadData();
} catch (error) {
toast({
variant: "destructive",
title: "删除失败",
duration: 3000,
});
}
};
useEffect(() => {
loadData();
}, [pagination.pageNum, pagination.pageSize]);
const columns: Column[] = [
{ {
title: '节点编码', accessorKey: 'nodeCode',
dataIndex: 'nodeCode', header: '节点编码',
width: 180, size: 180,
copyable: true, cell: ({ row }) => (
ellipsis: true, <div className="font-medium">{row.original.nodeCode}</div>
},
{
title: '节点名称',
dataIndex: 'nodeName',
width: 150,
},
{
title: '节点类型',
dataIndex: 'nodeCode',
width: 120,
render: (_, record) => (
<Tag color={nodeTypeColors[record.nodeCode as NodeTypeEnum]}>
{record.nodeCode}
</Tag>
), ),
}, },
{ {
title: '面板变量', accessorKey: 'nodeName',
dataIndex: 'panelVariablesSchema', header: '节点名称',
width: 100, size: 150,
render: (val) => (val ? <Tag color="success"></Tag> : <Tag></Tag>),
}, },
{ {
title: '本地变量', accessorKey: 'nodeType',
dataIndex: 'localVariablesSchema', header: '节点类型',
width: 100, size: 120,
render: (val) => (val ? <Tag color="success"></Tag> : <Tag></Tag>), cell: ({ row }) => {
const style = nodeTypeStyles[row.original.nodeType as NodeTypeEnum] || {
variant: 'secondary' as const,
label: row.original.nodeType || '未知类型'
};
return (
<Badge variant={style.variant}>
{style.label}
</Badge>
);
},
}, },
{ {
title: '表单变量', accessorKey: 'panelVariablesSchema',
dataIndex: 'formVariablesSchema', header: '面板变量',
width: 100, size: 100,
render: (val) => (val ? <Tag color="success"></Tag> : <Tag></Tag>), cell: ({ row }) => (
<Badge variant={row.original.panelVariablesSchema ? "outline" : "secondary"}>
{row.original.panelVariablesSchema ? '已配置' : '未配置'}
</Badge>
),
}, },
{ {
title: '操作', accessorKey: 'localVariablesSchema',
width: 180, header: '本地变量',
valueType: 'option', size: 100,
fixed: 'right', cell: ({ row }) => (
render: (_, record) => ( <Badge variant={row.original.localVariablesSchema ? "outline" : "secondary"}>
<Space> {row.original.localVariablesSchema ? '已配置' : '未配置'}
</Badge>
),
},
{
accessorKey: 'formVariablesSchema',
header: '表单变量',
size: 100,
cell: ({ row }) => (
<Badge variant={row.original.formVariablesSchema ? "outline" : "secondary"}>
{row.original.formVariablesSchema ? '已配置' : '未配置'}
</Badge>
),
},
{
id: 'actions',
header: '操作',
size: 180,
cell: ({ row }) => (
<div className="flex items-center gap-2">
<Button <Button
type="link" variant="ghost"
size="sm"
onClick={() => { onClick={() => {
setCurrentNode(record); setCurrentNode(row.original);
setDetailVisible(true); setDetailVisible(true);
}} }}
> >
</Button> </Button>
<Button <Button
type="link" variant="ghost"
onClick={() => navigate(`/workflow/node-design/design/${record.id}`)} size="sm"
onClick={() => navigate(`/workflow/node-design/design/${row.original.id}`)}
> >
</Button> </Button>
</Space> <AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="ghost"
size="sm"
className="text-destructive"
>
<DeleteOutlined className="mr-1" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction
onClick={() => handleDelete(row.original.id)}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
), ),
}, },
]; ];
return ( return (
<> <PageContainer>
<ProTable<NodeDesignDataResponse> <div className="flex items-center justify-between">
columns={columns} <h2 className="text-3xl font-bold tracking-tight"></h2>
request={async (params) => { <Button onClick={() => navigate('/workflow/node-design/create')}>
const { current, pageSize, ...rest } = params; <PlusOutlined className="mr-1" />
const response = await service.getNodeDefinitions({
current, </Button>
pageSize, </div>
...rest,
});
return {
data: response.content,
success: true,
total: response.totalElements
};
}}
rowKey="nodeCode"
search={false}
toolBarRender={() => [
<Button
key="create"
type="primary"
icon={<PlusOutlined />}
onClick={() => navigate('/workflow/node-design/create')}
>
</Button>,
]}
/>
<Modal <Card>
title="节点详情" <CardHeader className="flex flex-row items-center justify-between">
open={detailVisible} <CardTitle></CardTitle>
onCancel={() => setDetailVisible(false)} </CardHeader>
width={800} <CardContent>
footer={null} <div className="rounded-md border">
> <Table>
{/* TODO: 实现详情展示组件 */} <TableHeader>
<div></div> <TableRow>
</Modal> {columns.map((column) => (
</> <TableHead
key={column.accessorKey || column.id}
style={{ width: column.size }}
>
{column.header}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{list.map((item) => (
<TableRow key={item.id}>
{columns.map((column) => (
<TableCell
key={column.accessorKey || column.id}
>
{column.cell
? column.cell({ row: { original: item } })
: item[column.accessorKey!]}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
<div className="flex justify-end border-t border-border bg-muted/40">
<DataTablePagination
pageIndex={pagination.pageNum}
pageSize={pagination.pageSize}
pageCount={Math.ceil(pagination.totalElements / pagination.pageSize)}
onPageChange={handlePageChange}
/>
</div>
</div>
</CardContent>
</Card>
{/* 节点详情弹窗 */}
<AlertDialog open={detailVisible} onOpenChange={setDetailVisible}>
<AlertDialogContent className="max-w-[800px]">
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription asChild>
<div className="mt-4 space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-sm font-medium text-muted-foreground"></div>
<div className="mt-1">{currentNode?.nodeCode}</div>
</div>
<div>
<div className="text-sm font-medium text-muted-foreground"></div>
<div className="mt-1">{currentNode?.nodeName}</div>
</div>
<div>
<div className="text-sm font-medium text-muted-foreground"></div>
<div className="mt-1">
{currentNode?.nodeType && (
<Badge variant={(nodeTypeStyles[currentNode.nodeType as NodeTypeEnum] || {}).variant || 'secondary'}>
{(nodeTypeStyles[currentNode.nodeType as NodeTypeEnum] || {}).label || currentNode.nodeType}
</Badge>
)}
</div>
</div>
<div>
<div className="text-sm font-medium text-muted-foreground"></div>
<div className="mt-1">{currentNode?.category}</div>
</div>
</div>
<div>
<div className="text-sm font-medium text-muted-foreground"></div>
<div className="mt-1">{currentNode?.description || '-'}</div>
</div>
<div>
<div className="text-sm font-medium text-muted-foreground"></div>
<div className="mt-2 space-y-2">
<div className="flex items-center gap-2">
<span></span>
<Badge variant={currentNode?.panelVariablesSchema ? "outline" : "secondary"}>
{currentNode?.panelVariablesSchema ? '已配置' : '未配置'}
</Badge>
</div>
<div className="flex items-center gap-2">
<span></span>
<Badge variant={currentNode?.localVariablesSchema ? "outline" : "secondary"}>
{currentNode?.localVariablesSchema ? '已配置' : '未配置'}
</Badge>
</div>
<div className="flex items-center gap-2">
<span></span>
<Badge variant={currentNode?.formVariablesSchema ? "outline" : "secondary"}>
{currentNode?.formVariablesSchema ? '已配置' : '未配置'}
</Badge>
</div>
</div>
</div>
</div>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setDetailVisible(false)}></AlertDialogCancel>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</PageContainer>
); );
}; };

View File

@ -24,3 +24,10 @@ export const saveNodeDefinition = (data: NodeDesignDataResponse) =>
export const updateNodeDefinition = (id: number, data: NodeDesignDataResponse) => export const updateNodeDefinition = (id: number, data: NodeDesignDataResponse) =>
request.put<void>(`${BASE_URL}/${id}`, data); request.put<void>(`${BASE_URL}/${id}`, data);
/**
*
* @param id ID
*/
export const deleteNodeDefinition = (id: number) =>
request.delete<void>(`${BASE_URL}/${id}`);