204 lines
6.8 KiB
TypeScript
204 lines
6.8 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
||
import { Tabs } from 'antd';
|
||
import { Cell } from '@antv/x6';
|
||
import type { WorkflowNodeDefinition, ConfigurableNodeDefinition } from "../nodes/types";
|
||
import {
|
||
Sheet,
|
||
SheetContent,
|
||
SheetHeader,
|
||
SheetTitle,
|
||
} from "@/components/ui/sheet";
|
||
import { Button } from "@/components/ui/button";
|
||
import { convertJsonSchemaToColumns } from '@/utils/jsonSchemaUtils';
|
||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||
|
||
interface NodeConfigModalProps {
|
||
visible: boolean;
|
||
node: Cell | null;
|
||
nodeDefinition: WorkflowNodeDefinition | null;
|
||
onOk: (values: any) => void;
|
||
onCancel: () => void;
|
||
}
|
||
|
||
interface FormData {
|
||
configs?: Record<string, any>;
|
||
inputMapping?: Record<string, any>;
|
||
outputMapping?: Record<string, any>;
|
||
}
|
||
|
||
const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
||
visible,
|
||
node,
|
||
nodeDefinition,
|
||
onOk,
|
||
onCancel,
|
||
}) => {
|
||
const [formData, setFormData] = useState<FormData>({});
|
||
const [activeTab, setActiveTab] = useState('config');
|
||
|
||
// 判断是否为可配置节点
|
||
const isConfigurableNode = (def: WorkflowNodeDefinition): def is ConfigurableNodeDefinition => {
|
||
return 'inputMappingSchema' in def || 'outputMappingSchema' in def;
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (nodeDefinition && node) {
|
||
// 从节点数据中获取现有配置
|
||
const nodeData = node.getData() || {};
|
||
|
||
// 准备默认的基本信息配置
|
||
const defaultConfig = {
|
||
nodeName: nodeDefinition.nodeName, // 默认节点名称
|
||
nodeCode: nodeDefinition.nodeCode, // 默认节点编码
|
||
description: nodeDefinition.description // 默认节点描述
|
||
};
|
||
|
||
// 合并默认值和已保存的配置
|
||
setFormData({
|
||
configs: { ...defaultConfig, ...(nodeData.configs || {}) },
|
||
inputMapping: nodeData.inputMapping || {},
|
||
outputMapping: nodeData.outputMapping || {},
|
||
});
|
||
} else {
|
||
setFormData({});
|
||
}
|
||
}, [nodeDefinition, node]);
|
||
|
||
const handleSubmit = () => {
|
||
onOk(formData);
|
||
};
|
||
|
||
const handleConfigChange = (values: Record<string, any>) => {
|
||
setFormData(prev => ({
|
||
...prev,
|
||
configs: values
|
||
}));
|
||
};
|
||
|
||
const handleInputMappingChange = (values: Record<string, any>) => {
|
||
setFormData(prev => ({
|
||
...prev,
|
||
inputMapping: values
|
||
}));
|
||
};
|
||
|
||
const handleOutputMappingChange = (values: Record<string, any>) => {
|
||
setFormData(prev => ({
|
||
...prev,
|
||
outputMapping: values
|
||
}));
|
||
};
|
||
|
||
if (!nodeDefinition) {
|
||
return null;
|
||
}
|
||
|
||
// 构建Tabs配置
|
||
const tabItems = [
|
||
{
|
||
key: 'config',
|
||
label: '基本配置',
|
||
children: (
|
||
<div style={{ padding: '16px 0' }}>
|
||
<BetaSchemaForm
|
||
key={`configs-${node?.id}-${JSON.stringify(formData.configs)}`}
|
||
layoutType="Form"
|
||
columns={convertJsonSchemaToColumns(nodeDefinition.configSchema as any)}
|
||
initialValues={formData.configs}
|
||
onValuesChange={(_, allValues) => handleConfigChange(allValues)}
|
||
submitter={false}
|
||
/>
|
||
</div>
|
||
),
|
||
},
|
||
];
|
||
|
||
// 如果是可配置节点,添加输入和输出映射TAB
|
||
if (isConfigurableNode(nodeDefinition)) {
|
||
if (nodeDefinition.inputMappingSchema) {
|
||
tabItems.push({
|
||
key: 'input',
|
||
label: '输入映射',
|
||
children: (
|
||
<div style={{ padding: '16px 0' }}>
|
||
<BetaSchemaForm
|
||
key={`input-${node?.id}-${JSON.stringify(formData.inputMapping)}`}
|
||
layoutType="Form"
|
||
columns={convertJsonSchemaToColumns(nodeDefinition.inputMappingSchema as any)}
|
||
initialValues={formData.inputMapping}
|
||
onValuesChange={(_, allValues) => handleInputMappingChange(allValues)}
|
||
submitter={false}
|
||
/>
|
||
</div>
|
||
),
|
||
});
|
||
}
|
||
|
||
if (nodeDefinition.outputMappingSchema) {
|
||
tabItems.push({
|
||
key: 'output',
|
||
label: '输出映射',
|
||
children: (
|
||
<div style={{ padding: '16px 0' }}>
|
||
<BetaSchemaForm
|
||
key={`output-${node?.id}-${JSON.stringify(formData.outputMapping)}`}
|
||
layoutType="Form"
|
||
columns={convertJsonSchemaToColumns(nodeDefinition.outputMappingSchema as any)}
|
||
initialValues={formData.outputMapping}
|
||
onValuesChange={(_, allValues) => handleOutputMappingChange(allValues)}
|
||
submitter={false}
|
||
/>
|
||
</div>
|
||
),
|
||
});
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Sheet open={visible} onOpenChange={(open) => !open && onCancel()}>
|
||
<SheetContent
|
||
style={{
|
||
width: '600px',
|
||
maxWidth: '90vw',
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}}
|
||
>
|
||
<SheetHeader>
|
||
<SheetTitle>
|
||
编辑节点 - {nodeDefinition.nodeName}
|
||
</SheetTitle>
|
||
</SheetHeader>
|
||
|
||
<div style={{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
||
<Tabs
|
||
activeKey={activeTab}
|
||
onChange={setActiveTab}
|
||
items={tabItems}
|
||
style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
|
||
className="flex-tabs"
|
||
/>
|
||
</div>
|
||
|
||
<div
|
||
style={{
|
||
borderTop: '1px solid #f0f0f0',
|
||
padding: '16px 0 0 0',
|
||
display: 'flex',
|
||
justifyContent: 'flex-end',
|
||
gap: '8px'
|
||
}}
|
||
>
|
||
<Button variant="outline" onClick={onCancel}>
|
||
取消
|
||
</Button>
|
||
<Button onClick={handleSubmit}>
|
||
确定
|
||
</Button>
|
||
</div>
|
||
</SheetContent>
|
||
</Sheet>
|
||
);
|
||
};
|
||
|
||
export default NodeConfigModal; |