1
This commit is contained in:
parent
6b2e127eaa
commit
4fded64d7c
@ -51,6 +51,9 @@ const HighlightLayer: React.FC<HighlightLayerProps> = ({ value, className }) =>
|
||||
textDecorationColor: 'hsl(199 89% 48% / 0.6)',
|
||||
textDecorationThickness: '2px',
|
||||
textUnderlineOffset: '2px',
|
||||
// 确保垂直对齐
|
||||
display: 'inline',
|
||||
verticalAlign: 'baseline',
|
||||
}}
|
||||
>
|
||||
{match[0]}
|
||||
|
||||
@ -142,6 +142,8 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
||||
highlightElement.style.whiteSpace = variant === 'textarea' ? 'pre-wrap' : 'nowrap';
|
||||
highlightElement.style.wordWrap = 'break-word';
|
||||
highlightElement.style.overflowWrap = inputStyles.overflowWrap;
|
||||
highlightElement.style.verticalAlign = inputStyles.verticalAlign;
|
||||
highlightElement.style.boxSizing = inputStyles.boxSizing;
|
||||
};
|
||||
|
||||
// 同步滚动位置(仅 textarea)
|
||||
@ -443,10 +445,14 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
display: variant === 'input' ? 'flex' : 'block',
|
||||
alignItems: variant === 'input' ? 'center' : 'flex-start',
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '100%', flex: variant === 'input' ? 'none' : undefined }}>
|
||||
<HighlightLayer value={value} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@ -68,12 +68,12 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = {
|
||||
inputMappingSchema: {
|
||||
type: "object",
|
||||
title: "输入",
|
||||
description: "从上游节点接收的数据映射配置",
|
||||
description: "当前节点所需数据配置",
|
||||
properties: {
|
||||
jenkinsServerId: {
|
||||
type: "number",
|
||||
title: "Jenkins服务器",
|
||||
description: "选择要使用的Jenkins服务器",
|
||||
title: "服务器",
|
||||
description: "选择要使用的服务器",
|
||||
'x-dataSource': DataSourceType.JENKINS_SERVERS
|
||||
},
|
||||
project: {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {ConfigurableNodeDefinition, NodeType, NodeCategory} from './types';
|
||||
import {DataSourceType} from "@/pages/Workflow/Design/utils/dataSourceLoader.ts";
|
||||
|
||||
/**
|
||||
* 通知节点定义
|
||||
@ -60,14 +61,28 @@ export const NotificationNodeDefinition: ConfigurableNodeDefinition = {
|
||||
title: "节点描述",
|
||||
description: "节点的详细说明",
|
||||
default: "发送通知消息到指定渠道"
|
||||
}
|
||||
},
|
||||
required: ["nodeName", "nodeCode", "notificationType", "title", "content"]
|
||||
},
|
||||
inputMappingSchema: {
|
||||
type: "object",
|
||||
title: "输入",
|
||||
description: "当前节点所需数据配置",
|
||||
properties: {
|
||||
// notificationType: {
|
||||
// type: "string",
|
||||
// title: "通知类型",
|
||||
// description: "选择通知发送的渠道",
|
||||
// enum: ["EMAIL", "DINGTALK", "WECHAT_WORK", "SMS"],
|
||||
// enumNames: ["邮件", "钉钉", "企业微信", "短信"],
|
||||
// default: "EMAIL"
|
||||
// },
|
||||
notificationType: {
|
||||
type: "string",
|
||||
title: "通知类型",
|
||||
description: "选择通知发送的渠道",
|
||||
enum: ["EMAIL", "DINGTALK", "WECHAT_WORK", "SMS"],
|
||||
enumNames: ["邮件", "钉钉", "企业微信", "短信"],
|
||||
default: "EMAIL"
|
||||
'x-dataSource': DataSourceType.NOTIFICATION_CHANNEL_TYPES
|
||||
},
|
||||
title: {
|
||||
type: "string",
|
||||
@ -83,7 +98,7 @@ export const NotificationNodeDefinition: ConfigurableNodeDefinition = {
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
required: ["nodeName", "nodeCode", "notificationType", "title", "content"]
|
||||
required: ["jenkinsServerId"]
|
||||
},
|
||||
outputs: [{
|
||||
name: "status",
|
||||
|
||||
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { Handle, Position, NodeProps } from '@xyflow/react';
|
||||
import type { FlowNodeData } from '../../types';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { isConfigurableNode } from '../types';
|
||||
|
||||
/**
|
||||
* BaseNode - shadcn/ui 风格节点
|
||||
@ -17,6 +18,52 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
|
||||
|
||||
const config = definition.renderConfig;
|
||||
|
||||
// 渲染输入字段标签(来自 inputMappingSchema)
|
||||
const renderInputSection = () => {
|
||||
if (!isConfigurableNode(definition) || !definition.inputMappingSchema) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const schema = definition.inputMappingSchema;
|
||||
|
||||
// 获取所有定义的输入字段(从 schema)
|
||||
if (!schema.properties) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const allInputs = Object.keys(schema.properties).map(key => {
|
||||
const fieldSchema = schema.properties![key];
|
||||
return {
|
||||
key,
|
||||
title: fieldSchema.title || key,
|
||||
description: fieldSchema.description,
|
||||
};
|
||||
});
|
||||
|
||||
if (allInputs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{/* 输入标签 - 靠左 */}
|
||||
<div className="text-xs text-muted-foreground font-medium">输入</div>
|
||||
{/* 输入字段标签 */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{allInputs.map((input, index) => (
|
||||
<code
|
||||
key={index}
|
||||
className="text-xs px-2 py-1 bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300 rounded-md font-mono inline-flex items-center"
|
||||
title={input.description}
|
||||
>
|
||||
{input.title}
|
||||
</code>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染输出字段标签
|
||||
const renderOutputSection = () => {
|
||||
if (!nodeData.outputs || nodeData.outputs.length === 0) {
|
||||
@ -32,9 +79,9 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
|
||||
<code
|
||||
key={index}
|
||||
className="text-xs px-2 py-1 bg-secondary text-secondary-foreground rounded-md font-mono inline-flex items-center"
|
||||
title={output.title || output.description}
|
||||
title={output.description}
|
||||
>
|
||||
{output.name}
|
||||
{output.title || output.name}
|
||||
</code>
|
||||
))}
|
||||
</div>
|
||||
@ -150,9 +197,13 @@ const BaseNode: React.FC<NodeProps> = ({ data, selected }) => {
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
{/* 输入/输出部分 */}
|
||||
{(renderInputSection() || renderOutputSection()) && (
|
||||
<CardContent className="pt-0 space-y-3">
|
||||
{/* 输入部分 */}
|
||||
{renderInputSection()}
|
||||
|
||||
{/* 输出部分 */}
|
||||
{renderOutputSection() && (
|
||||
<CardContent className="pt-0">
|
||||
{renderOutputSection()}
|
||||
</CardContent>
|
||||
)}
|
||||
|
||||
@ -7,7 +7,8 @@ export enum DataSourceType {
|
||||
JENKINS_SERVERS = 'JENKINS_SERVERS',
|
||||
K8S_CLUSTERS = 'K8S_CLUSTERS',
|
||||
GIT_REPOSITORIES = 'GIT_REPOSITORIES',
|
||||
DOCKER_REGISTRIES = 'DOCKER_REGISTRIES'
|
||||
DOCKER_REGISTRIES = 'DOCKER_REGISTRIES',
|
||||
NOTIFICATION_CHANNEL_TYPES = 'NOTIFICATION_CHANNEL_TYPES'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,6 +45,15 @@ export const DATA_SOURCE_REGISTRY: Record<DataSourceType, DataSourceConfig> = {
|
||||
}));
|
||||
}
|
||||
},
|
||||
[DataSourceType.NOTIFICATION_CHANNEL_TYPES]: {
|
||||
url: '/api/v1/notification-channel/types',
|
||||
transform: (data: any[]) => {
|
||||
return data.map((item: any) => ({
|
||||
label: `${item.label}`,
|
||||
value: item.code
|
||||
}));
|
||||
}
|
||||
},
|
||||
[DataSourceType.K8S_CLUSTERS]: {
|
||||
url: '/api/v1/k8s-cluster/list',
|
||||
params: { enabled: true },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user