1
This commit is contained in:
parent
8fe80fd780
commit
1c166c2559
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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}`);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user