1
This commit is contained in:
parent
8fe80fd780
commit
1c166c2559
@ -1,135 +1,368 @@
|
||||
import React, { useState } 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 React, { useState, useEffect } from 'react';
|
||||
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 * 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 = {
|
||||
[NodeTypeEnum.START_EVENT]: 'green',
|
||||
[NodeTypeEnum.END_EVENT]: 'red',
|
||||
[NodeTypeEnum.SCRIPT_TASK]: 'blue',
|
||||
// 节点类型标签样式映射
|
||||
const nodeTypeStyles = {
|
||||
[NodeTypeEnum.START_EVENT]: {
|
||||
variant: 'success' as const,
|
||||
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 navigate = useNavigate();
|
||||
const [detailVisible, setDetailVisible] = useState(false);
|
||||
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 columns: ProColumns<NodeDesignDataResponse>[] = [
|
||||
const loadData = async () => {
|
||||
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: '节点编码',
|
||||
dataIndex: 'nodeCode',
|
||||
width: 180,
|
||||
copyable: true,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '节点名称',
|
||||
dataIndex: 'nodeName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '节点类型',
|
||||
dataIndex: 'nodeCode',
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Tag color={nodeTypeColors[record.nodeCode as NodeTypeEnum]}>
|
||||
{record.nodeCode}
|
||||
</Tag>
|
||||
accessorKey: 'nodeCode',
|
||||
header: '节点编码',
|
||||
size: 180,
|
||||
cell: ({ row }) => (
|
||||
<div className="font-medium">{row.original.nodeCode}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '面板变量',
|
||||
dataIndex: 'panelVariablesSchema',
|
||||
width: 100,
|
||||
render: (val) => (val ? <Tag color="success">已配置</Tag> : <Tag>未配置</Tag>),
|
||||
accessorKey: 'nodeName',
|
||||
header: '节点名称',
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
title: '本地变量',
|
||||
dataIndex: 'localVariablesSchema',
|
||||
width: 100,
|
||||
render: (val) => (val ? <Tag color="success">已配置</Tag> : <Tag>未配置</Tag>),
|
||||
accessorKey: 'nodeType',
|
||||
header: '节点类型',
|
||||
size: 120,
|
||||
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: '表单变量',
|
||||
dataIndex: 'formVariablesSchema',
|
||||
width: 100,
|
||||
render: (val) => (val ? <Tag color="success">已配置</Tag> : <Tag>未配置</Tag>),
|
||||
accessorKey: 'panelVariablesSchema',
|
||||
header: '面板变量',
|
||||
size: 100,
|
||||
cell: ({ row }) => (
|
||||
<Badge variant={row.original.panelVariablesSchema ? "outline" : "secondary"}>
|
||||
{row.original.panelVariablesSchema ? '已配置' : '未配置'}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
accessorKey: 'localVariablesSchema',
|
||||
header: '本地变量',
|
||||
size: 100,
|
||||
cell: ({ row }) => (
|
||||
<Badge variant={row.original.localVariablesSchema ? "outline" : "secondary"}>
|
||||
{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
|
||||
type="link"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setCurrentNode(record);
|
||||
setCurrentNode(row.original);
|
||||
setDetailVisible(true);
|
||||
}}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => navigate(`/workflow/node-design/design/${record.id}`)}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate(`/workflow/node-design/design/${row.original.id}`)}
|
||||
>
|
||||
编辑
|
||||
</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 (
|
||||
<>
|
||||
<ProTable<NodeDesignDataResponse>
|
||||
columns={columns}
|
||||
request={async (params) => {
|
||||
const { current, pageSize, ...rest } = params;
|
||||
const response = await service.getNodeDefinitions({
|
||||
current,
|
||||
pageSize,
|
||||
...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>,
|
||||
]}
|
||||
/>
|
||||
<PageContainer>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-3xl font-bold tracking-tight">节点管理</h2>
|
||||
<Button onClick={() => navigate('/workflow/node-design/create')}>
|
||||
<PlusOutlined className="mr-1" />
|
||||
新建节点
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title="节点详情"
|
||||
open={detailVisible}
|
||||
onCancel={() => setDetailVisible(false)}
|
||||
width={800}
|
||||
footer={null}
|
||||
>
|
||||
{/* TODO: 实现详情展示组件 */}
|
||||
<div>节点详情内容</div>
|
||||
</Modal>
|
||||
</>
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardTitle>节点列表</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -24,3 +24,10 @@ export const saveNodeDefinition = (data: NodeDesignDataResponse) =>
|
||||
|
||||
export const updateNodeDefinition = (id: number, data: NodeDesignDataResponse) =>
|
||||
request.put<void>(`${BASE_URL}/${id}`, data);
|
||||
|
||||
/**
|
||||
* 删除节点定义
|
||||
* @param id 节点ID
|
||||
*/
|
||||
export const deleteNodeDefinition = (id: number) =>
|
||||
request.delete<void>(`${BASE_URL}/${id}`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user