增加审批组件
This commit is contained in:
parent
f8583ba3ef
commit
e340e3106d
@ -18,6 +18,7 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
||||
allNodes,
|
||||
allEdges,
|
||||
currentNodeId,
|
||||
formFields = [],
|
||||
variant = 'input',
|
||||
placeholder,
|
||||
disabled = false,
|
||||
@ -32,10 +33,23 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const highlightRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 收集可用变量
|
||||
// 将表单字段转换为变量格式(匹配 NodeVariable 结构)
|
||||
const formVariables = useMemo(() => {
|
||||
return formFields.map(field => ({
|
||||
nodeId: 'form',
|
||||
nodeName: '启动表单',
|
||||
fieldName: field.name,
|
||||
fieldType: field.type,
|
||||
displayText: `\${form.${field.name}}`,
|
||||
fullText: `form.${field.name}`,
|
||||
}));
|
||||
}, [formFields]);
|
||||
|
||||
// 收集可用变量(节点变量 + 表单变量)
|
||||
const allVariables = useMemo(() => {
|
||||
return collectNodeVariables(currentNodeId, allNodes, allEdges);
|
||||
}, [currentNodeId, allNodes, allEdges]);
|
||||
const nodeVariables = collectNodeVariables(currentNodeId, allNodes, allEdges);
|
||||
return [...formVariables, ...nodeVariables];
|
||||
}, [currentNodeId, allNodes, allEdges, formVariables]);
|
||||
|
||||
// 根据搜索文本过滤变量
|
||||
const filteredVariables = useMemo(() => {
|
||||
|
||||
@ -1,5 +1,22 @@
|
||||
import type { FlowNode, FlowEdge } from '@/pages/Workflow/Design/types';
|
||||
|
||||
/**
|
||||
* 表单字段信息(用于变量引用)
|
||||
*/
|
||||
export interface FormField {
|
||||
/** 字段名称(变量名)*/
|
||||
name: string;
|
||||
|
||||
/** 字段标签(显示名称)*/
|
||||
label: string;
|
||||
|
||||
/** 字段类型 */
|
||||
type: string;
|
||||
|
||||
/** 字段描述 */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变量输入组件的 Props
|
||||
*/
|
||||
@ -19,6 +36,9 @@ export interface VariableInputProps {
|
||||
/** 当前节点ID(用于过滤前序节点)*/
|
||||
currentNodeId: string;
|
||||
|
||||
/** 表单字段列表(用于支持 ${form.xxx} 变量)*/
|
||||
formFields?: FormField[];
|
||||
|
||||
/** 渲染类型 */
|
||||
variant?: 'input' | 'textarea';
|
||||
|
||||
|
||||
@ -11,12 +11,14 @@ import { useToast } from '@/components/ui/use-toast';
|
||||
import type { FlowEdge, FlowNode } from '../types';
|
||||
import { convertToUUID, convertToDisplayName } from '@/utils/workflow/variableConversion';
|
||||
import VariableInput from '@/components/VariableInput';
|
||||
import type { FormField as FormFieldType } from '@/components/VariableInput/types';
|
||||
|
||||
interface EdgeConfigModalProps {
|
||||
visible: boolean;
|
||||
edge: FlowEdge | null;
|
||||
allNodes: FlowNode[];
|
||||
allEdges: FlowEdge[];
|
||||
formFields?: FormFieldType[];
|
||||
onOk: (edgeId: string, condition: EdgeCondition) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
@ -58,6 +60,7 @@ const EdgeConfigModal: React.FC<EdgeConfigModalProps> = ({
|
||||
edge,
|
||||
allNodes,
|
||||
allEdges,
|
||||
formFields = [],
|
||||
onOk,
|
||||
onCancel
|
||||
}) => {
|
||||
@ -200,9 +203,10 @@ const EdgeConfigModal: React.FC<EdgeConfigModalProps> = ({
|
||||
allNodes={allNodes}
|
||||
allEdges={allEdges}
|
||||
currentNodeId={edge?.target || ''}
|
||||
formFields={formFields}
|
||||
variant="textarea"
|
||||
placeholder="请输入条件表达式,如:${Jenkins构建.buildStatus == 'SUCCESS'}"
|
||||
rows={4}
|
||||
placeholder="请输入条件表达式,如:${form.环境选择} 或 ${Jenkins构建.buildStatus == 'SUCCESS'}"
|
||||
rows={4}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
|
||||
@ -22,12 +22,14 @@ import { convertJsonSchemaToZod, extractDataSourceTypes } from '../utils/schemaC
|
||||
import { loadDataSource, DataSourceType, type DataSourceOption } from '@/domain/dataSource';
|
||||
import { convertObjectToUUID, convertObjectToDisplayName } from '@/utils/workflow/variableConversion';
|
||||
import VariableInput from '@/components/VariableInput';
|
||||
import type { FormField as FormFieldType } from '@/components/VariableInput/types';
|
||||
|
||||
interface NodeConfigModalProps {
|
||||
visible: boolean;
|
||||
node: FlowNode | null;
|
||||
allNodes: FlowNode[];
|
||||
allEdges: FlowEdge[];
|
||||
formFields?: FormFieldType[];
|
||||
onCancel: () => void;
|
||||
onOk: (nodeId: string, updatedData: Partial<FlowNodeData>) => void;
|
||||
}
|
||||
@ -125,6 +127,7 @@ const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
||||
node,
|
||||
allNodes,
|
||||
allEdges,
|
||||
formFields = [],
|
||||
onCancel,
|
||||
onOk
|
||||
}) => {
|
||||
@ -500,6 +503,7 @@ const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
||||
allNodes={allNodesRef.current}
|
||||
allEdges={allEdgesRef.current}
|
||||
currentNodeId={node?.id || ''}
|
||||
formFields={formFields}
|
||||
variant={isTextarea ? 'textarea' : 'input'}
|
||||
placeholder={prop.description || `请输入${prop.title || ''}`}
|
||||
disabled={loading}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import React, { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { message } from 'antd';
|
||||
import { ReactFlowProvider, useReactFlow } from '@xyflow/react';
|
||||
@ -57,6 +57,44 @@ const WorkflowDesignInner: React.FC = () => {
|
||||
const [formDefinition, setFormDefinition] = useState<FormDefinitionResponse | null>(null);
|
||||
const [formPreviewVisible, setFormPreviewVisible] = useState(false);
|
||||
|
||||
// 提取表单字段(用于变量引用)
|
||||
const formFields = useMemo(() => {
|
||||
if (!formDefinition?.schema?.fields) return [];
|
||||
|
||||
const extractFields = (fields: any[]): any[] => {
|
||||
const result: any[] = [];
|
||||
|
||||
for (const field of fields) {
|
||||
// 跳过布局组件
|
||||
if (['text', 'divider'].includes(field.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 栅格布局:递归提取子字段
|
||||
if (field.type === 'grid' && field.children) {
|
||||
for (const column of field.children) {
|
||||
if (Array.isArray(column)) {
|
||||
result.push(...extractFields(column));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 普通字段
|
||||
result.push({
|
||||
name: field.name,
|
||||
label: field.label || field.name,
|
||||
type: field.type,
|
||||
description: field.description,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return extractFields(formDefinition.schema.fields);
|
||||
}, [formDefinition]);
|
||||
|
||||
// 保存和加载hooks
|
||||
const { hasUnsavedChanges, saveWorkflow, markUnsaved } = useWorkflowSave();
|
||||
const { workflowDefinition, loadWorkflow } = useWorkflowLoad();
|
||||
@ -560,13 +598,14 @@ const WorkflowDesignInner: React.FC = () => {
|
||||
|
||||
{/* 节点配置弹窗 */}
|
||||
<NodeConfigModal
|
||||
visible={configModalVisible}
|
||||
visible={configModalVisible}
|
||||
node={configNode}
|
||||
allNodes={getNodes() as FlowNode[]}
|
||||
allEdges={getEdges() as FlowEdge[]}
|
||||
formFields={formFields}
|
||||
onCancel={handleCloseConfigModal}
|
||||
onOk={handleNodeConfigUpdate}
|
||||
/>
|
||||
onOk={handleNodeConfigUpdate}
|
||||
/>
|
||||
|
||||
{/* 边条件配置弹窗 */}
|
||||
<EdgeConfigModal
|
||||
@ -574,6 +613,7 @@ const WorkflowDesignInner: React.FC = () => {
|
||||
edge={configEdge}
|
||||
allNodes={getNodes() as FlowNode[]}
|
||||
allEdges={getEdges() as FlowEdge[]}
|
||||
formFields={formFields}
|
||||
onOk={handleEdgeConditionUpdate}
|
||||
onCancel={handleCloseEdgeConfigModal}
|
||||
/>
|
||||
|
||||
@ -29,6 +29,11 @@ export const convertToUUID = (
|
||||
}
|
||||
|
||||
return displayText.replace(VARIABLE_PATTERN, (match, nodeNameOrId, fieldName) => {
|
||||
// 特殊处理:表单字段
|
||||
if (nodeNameOrId === '启动表单' || nodeNameOrId === 'form') {
|
||||
return `\${form.${fieldName}}`;
|
||||
}
|
||||
|
||||
// 尝试通过名称查找节点
|
||||
const nodeByName = allNodes.find(n => {
|
||||
const nodeName = n.data?.label || n.data?.nodeDefinition?.nodeName;
|
||||
@ -67,6 +72,11 @@ export const convertToDisplayName = (
|
||||
}
|
||||
|
||||
return uuidText.replace(VARIABLE_PATTERN, (match, nodeIdOrName, fieldName) => {
|
||||
// 特殊处理:表单字段
|
||||
if (nodeIdOrName === 'form' || nodeIdOrName === '启动表单') {
|
||||
return `\${启动表单.${fieldName}}`;
|
||||
}
|
||||
|
||||
// 尝试通过UUID查找节点
|
||||
const nodeById = allNodes.find(n => n.id === nodeIdOrName);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user