This commit is contained in:
dengqichen 2025-10-22 14:40:54 +08:00
parent cec0962afd
commit e7b914a2ae
4 changed files with 96 additions and 74 deletions

View File

@ -68,7 +68,7 @@ const WorkflowDesignInner: React.FC = () => {
if (data) { if (data) {
setNodes(data.nodes); setNodes(data.nodes);
setEdges(data.edges); setEdges(data.edges);
setWorkflowTitle(data.definition.name); setWorkflowTitle(data.definition?.name || '未命名工作流');
} }
} }
}; };
@ -419,11 +419,16 @@ const WorkflowDesignInner: React.FC = () => {
tagName === 'TEXTAREA' || tagName === 'TEXTAREA' ||
target.isContentEditable || target.isContentEditable ||
target.getAttribute('contenteditable') === 'true'; target.getAttribute('contenteditable') === 'true';
const isInDrawer = target.closest('.ant-drawer-body') !== null;
const isInModal = target.closest('.ant-modal') !== null;
// 在抽屉或模态框内,且在输入元素中时,允许原生行为 // 检查是否在 shadcn/ui Sheet、Dialog 或其他模态框内
const shouldSkipShortcut = isInputElement || isInDrawer || isInModal; const isInSheet = target.closest('[role="dialog"]') !== null;
const isInModal = target.closest('[role="alertdialog"]') !== null;
const isInPopover = target.closest('[role="menu"]') !== null ||
target.closest('[role="listbox"]') !== null ||
target.closest('[data-radix-popper-content-wrapper]') !== null;
// 在模态框或弹窗内,或者在输入元素中时,允许原生行为
const shouldSkipShortcut = isInputElement || isInSheet || isInModal || isInPopover;
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const ctrlKey = isMac ? e.metaKey : e.ctrlKey; const ctrlKey = isMac ? e.metaKey : e.ctrlKey;

View File

@ -83,7 +83,7 @@ export const NotificationNodeDefinition: ConfigurableNodeDefinition = {
default: "" default: ""
} }
}, },
required: ["nodeName", "nodeCode", "notificationType", "title", "content", "recipients"] required: ["nodeName", "nodeCode", "notificationType", "title", "content"]
}, },
outputs: [{ outputs: [{
name: "status", name: "status",
@ -91,8 +91,7 @@ export const NotificationNodeDefinition: ConfigurableNodeDefinition = {
type: "string", type: "string",
enum: ["SUCCESS", "FAILURE"], enum: ["SUCCESS", "FAILURE"],
description: "执行的结果状态", description: "执行的结果状态",
example: "SUCCESS", example: "SUCCESS"
required: true
}] }]
}; };

View File

@ -9,6 +9,7 @@ export enum NodeType {
SCRIPT_TASK = 'SCRIPT_TASK', SCRIPT_TASK = 'SCRIPT_TASK',
DEPLOY_NODE = 'DEPLOY_NODE', DEPLOY_NODE = 'DEPLOY_NODE',
JENKINS_BUILD = 'JENKINS_BUILD', JENKINS_BUILD = 'JENKINS_BUILD',
NOTIFICATION = 'NOTIFICATION',
GATEWAY_NODE = 'GATEWAY_NODE', GATEWAY_NODE = 'GATEWAY_NODE',
SUB_PROCESS = 'SUB_PROCESS', SUB_PROCESS = 'SUB_PROCESS',
CALL_ACTIVITY = 'CALL_ACTIVITY' CALL_ACTIVITY = 'CALL_ACTIVITY'

View File

@ -15,26 +15,51 @@ export const convertJsonSchemaToZod = (jsonSchema: JSONSchema): z.ZodObject<any>
Object.entries(jsonSchema.properties).forEach(([key, prop]: [string, any]) => { Object.entries(jsonSchema.properties).forEach(([key, prop]: [string, any]) => {
let fieldSchema: z.ZodTypeAny; let fieldSchema: z.ZodTypeAny;
const isRequired = Array.isArray(jsonSchema.required) && jsonSchema.required.includes(key);
// 如果有枚举,直接使用枚举(枚举会覆盖其他类型)
if (prop.enum && Array.isArray(prop.enum)) {
const enumValues = prop.enum.map((v: any) => (typeof v === 'object' ? v.value : v));
fieldSchema = z.enum(enumValues as [string, ...string[]]);
// 处理默认值
if (prop.default !== undefined) {
fieldSchema = fieldSchema.default(prop.default);
}
// 处理必填/可选
if (!isRequired) {
fieldSchema = fieldSchema.optional();
}
} else {
// 根据类型创建 Zod Schema // 根据类型创建 Zod Schema
switch (prop.type) { switch (prop.type) {
case 'string': case 'string': {
fieldSchema = z.string(); let stringSchema = z.string();
// 必填字段需要非空验证
if (isRequired) {
stringSchema = stringSchema.min(1, `${prop.title || key}不能为空`);
}
if (prop.format === 'email') { if (prop.format === 'email') {
fieldSchema = (fieldSchema as z.ZodString).email(prop.title ? `${prop.title}格式不正确` : '邮箱格式不正确'); stringSchema = stringSchema.email(prop.title ? `${prop.title}格式不正确` : '邮箱格式不正确');
} else if (prop.format === 'url') { } else if (prop.format === 'url') {
fieldSchema = (fieldSchema as z.ZodString).url(prop.title ? `${prop.title}格式不正确` : 'URL格式不正确'); stringSchema = stringSchema.url(prop.title ? `${prop.title}格式不正确` : 'URL格式不正确');
} }
if (prop.minLength) { if (prop.minLength) {
fieldSchema = (fieldSchema as z.ZodString).min(prop.minLength, `${prop.title || key}至少需要${prop.minLength}个字符`); stringSchema = stringSchema.min(prop.minLength, `${prop.title || key}至少需要${prop.minLength}个字符`);
} }
if (prop.maxLength) { if (prop.maxLength) {
fieldSchema = (fieldSchema as z.ZodString).max(prop.maxLength, `${prop.title || key}最多${prop.maxLength}个字符`); stringSchema = stringSchema.max(prop.maxLength, `${prop.title || key}最多${prop.maxLength}个字符`);
} }
if (prop.pattern) { if (prop.pattern) {
fieldSchema = (fieldSchema as z.ZodString).regex(new RegExp(prop.pattern), `${prop.title || key}格式不正确`); stringSchema = stringSchema.regex(new RegExp(prop.pattern), `${prop.title || key}格式不正确`);
} }
fieldSchema = stringSchema;
break; break;
}
case 'number': case 'number':
case 'integer': case 'integer':
@ -74,19 +99,11 @@ export const convertJsonSchemaToZod = (jsonSchema: JSONSchema): z.ZodObject<any>
fieldSchema = fieldSchema.default(prop.default); fieldSchema = fieldSchema.default(prop.default);
} }
// 处理枚举 // 处理可选字段
if (prop.enum && Array.isArray(prop.enum)) { if (!isRequired) {
const enumValues = prop.enum.map((v: any) => (typeof v === 'object' ? v.value : v));
fieldSchema = z.enum(enumValues as [string, ...string[]]);
}
// 处理必填字段
if (Array.isArray(jsonSchema.required) && jsonSchema.required.includes(key)) {
// 已经是必填的,不需要额外处理
} else {
// 非必填字段设为 optional
fieldSchema = fieldSchema.optional(); fieldSchema = fieldSchema.optional();
} }
}
shape[key] = fieldSchema; shape[key] = fieldSchema;
}); });