diff --git a/electron/main/common/platform.ts b/electron/main/common/platform.ts index 1d499b4250a8fd91ab390ee32c5c14dc5757f703..1cd6acc8c47a0cf694670540552a6103b77b686f 100644 --- a/electron/main/common/platform.ts +++ b/electron/main/common/platform.ts @@ -42,7 +42,7 @@ export interface INodeProcess { cwd: () => string; } -let nodeProcess: INodeProcess | undefined = process; +const nodeProcess: INodeProcess | undefined = process; const isElectronProcess = typeof nodeProcess?.versions?.electron === 'string'; diff --git a/src/api/variable.js b/src/api/variable.js index 42ea4a72af6de112d1ab6ad5c05dcbab8cece311..969ec9057581091b57677848e16c79b53d470d72 100644 --- a/src/api/variable.js +++ b/src/api/variable.js @@ -64,6 +64,7 @@ export async function getVariable(params) { * @param {string} [params.flow_id] 流程ID(环境级和对话级变量必需) * @param {string} [params.conversation_id] 对话ID(系统级和对话级变量必需) * @param {string} [params.current_step_id] 当前步骤ID(用于获取前置节点变量,仅对话变量有效) + * @param {string} [params.exclude_pattern] 排除模式('step_id'排除包含.的变量名) */ export async function listVariables(params = {}) { const [error, response] = await get(`${BASE_URL}/list`, params) diff --git a/src/apis/paths/knowledge.ts b/src/apis/paths/knowledge.ts index 14d93209821af4af590bfc3472212f0a0fde0ae7..b5ccbe2c51d57e67d086ad0a8734b263c48f4ab7 100644 --- a/src/apis/paths/knowledge.ts +++ b/src/apis/paths/knowledge.ts @@ -21,7 +21,7 @@ export const updateKnowledgeList = ({ kb_ids: string[]; conversationId: string; }): Promise<[any, FcResponse<{}> | undefined]> => { - let kbIds = kb_ids; + const kbIds = kb_ids; return put('/api/knowledge', { kbIds }, { conversationId }); }; diff --git a/src/apis/workFlow/workFlowService.ts b/src/apis/workFlow/workFlowService.ts index 3f094d9ab2ad15d94c5f30d2fe2e196d24abc8aa..518437fc1b29841eb7d128a24c32b98ce028c9c1 100644 --- a/src/apis/workFlow/workFlowService.ts +++ b/src/apis/workFlow/workFlowService.ts @@ -64,6 +64,35 @@ export const delFlowTopology = (params: { return del(`/api/flow?appId=${params.appId}&flowId=${params.flowId}`); }; +/** + * 创建/修改子工作流拓扑结构 + * @param params + * @returns + */ +export const createOrUpdateSubFlowTopology = ( + params: { + appId: string; + flowId: string; + subFlowId: string; + }, + data: CreateOrUpdataFlowBodyType, +): Promise<[any, FcResponse | undefined]> => { + return put('/api/flow/subflow', data, params); +}; + +/** + * 获取子工作流拓扑结构 + * @param params + * @returns + */ +export const querySingleSubFlowTopology = (params: { + appId: string; + flowId: string; + subFlowId: string; +}): Promise<[any, FcResponse | undefined]> => { + return get('/api/flow/subflow', params); +}; + export const workFlowApi = { queryAllFlowService, querySingleFlowServiceNode, diff --git a/src/assets/svgs/Refresh.svg b/src/assets/svgs/Refresh.svg new file mode 100644 index 0000000000000000000000000000000000000000..092d34d332d71e5fb626e6a394203e03d0a7e00b --- /dev/null +++ b/src/assets/svgs/Refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/StopFilled.svg b/src/assets/svgs/StopFilled.svg new file mode 100644 index 0000000000000000000000000000000000000000..953380293887bea80fe0db660f874ee8e968c3ef --- /dev/null +++ b/src/assets/svgs/StopFilled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/JSONMonacoEditor.vue b/src/components/JSONMonacoEditor.vue index 7c652a984e5727c3ce21902ac3a6bdd9d863b089..809a68b5181a26b09b2073c3c5edb937dd023908 100644 --- a/src/components/JSONMonacoEditor.vue +++ b/src/components/JSONMonacoEditor.vue @@ -144,7 +144,7 @@ const initMonacoEditor = async () => { setTimeout(() => { if (editor) { editor.layout(); - console.log('🔧 Monaco Editor post-creation layout complete'); + // 移除详细日志 } }, 100); @@ -196,18 +196,22 @@ const setValue = (value: string) => { // 监听props变化 watch(() => props.modelValue, (newVal) => { if (editor) { - editor.setValue(newVal || ''); - // 强制刷新编辑器布局 - setTimeout(() => { - if (editor) { - editor.layout(); - console.log('📐 Monaco refreshed'); - } - }, 100); + const currentValue = editor.getValue(); + // 只有在值真正不同时才更新 + if (currentValue !== (newVal || '')) { + editor.setValue(newVal || ''); + // 减少频繁的layout调用 + setTimeout(() => { + if (editor) { + editor.layout(); + // 移除详细日志 + } + }, 150); // 增加延迟,减少频率 + } } else if (!editor && editorContainer.value) { // 处理降级方案(textarea) const textarea = editorContainer.value.querySelector('textarea'); - if (textarea) { + if (textarea && textarea.value !== (newVal || '')) { textarea.value = newVal || ''; // 触发输入事件确保显示更新 textarea.dispatchEvent(new Event('input')); diff --git a/src/components/VariableChooser.vue b/src/components/VariableChooser.vue index c1623bf961be1d4035847d03c17036a223c44e62..3f0f3bcb8bfd8ebedf2fc6679f50ac1aec5db3d4 100644 --- a/src/components/VariableChooser.vue +++ b/src/components/VariableChooser.vue @@ -51,6 +51,8 @@ :flow-id="flowId" :conversation-id="conversationId" :current-step-id="currentStepId" + :self-variables="selfVariables" + :self-scope-label="selfScopeLabel" :show-variable-reference="showVariableReference" @variable-selected="handleVariableSelected" /> @@ -133,6 +135,8 @@ interface Props { flowId?: string // 流程ID conversationId?: string // 对话ID currentStepId?: string // 当前步骤ID + selfVariables?: any[] // 当前节点定义的变量列表 + selfScopeLabel?: string // 当前节点变量分组的标签 // 显示控制 showVariableName?: boolean // 是否显示变量名输入 @@ -163,6 +167,8 @@ const props = withDefaults(defineProps(), { flowId: '', conversationId: '', currentStepId: '', + selfVariables: () => [], + selfScopeLabel: '', showVariableName: false, showLabel: true, showActions: true, @@ -197,7 +203,7 @@ const parseVariableReference = (reference: string) => { // 移除可能的 {{ }} 包装 const cleanRef = reference.replace(/^\{\{(.*)\}\}$/, '$1').trim() - // 解析格式:conversation.step_id.variable_name 或 scope.variable_name + // 解析格式:conversation.step_id.variable_name、scope.variable_name 或 self.variable_name const parts = cleanRef.split('.') if (parts.length === 3 && parts[0] === 'conversation') { @@ -219,6 +225,21 @@ const parseVariableReference = (reference: string) => { // 根据解析的引用信息查找对应的变量 const findVariableByReference = async (parsedRef: { scope: string; step_id?: string; name: string }) => { try { + // 如果是当前节点变量,直接从 props.selfVariables 中查找 + if (parsedRef.scope === 'self') { + const selfVar = props.selfVariables?.find(v => v.name === parsedRef.name) + if (selfVar) { + return { + name: selfVar.name, + var_type: selfVar.type || 'string', + scope: 'self', + value: `{{self.${selfVar.name}}}`, + description: `当前节点变量: ${selfVar.name}` + } + } + return undefined + } + const params: any = { scope: parsedRef.scope, flow_id: props.flowId, @@ -263,6 +284,17 @@ const findVariableByReference = async (parsedRef: { scope: string; step_id?: str const generateVariableReference = (variable: Variable, format: 'raw' | 'wrapped' = 'raw'): string => { let reference = '' + // 当前节点变量已经包含完整的引用格式 + if (variable.scope === 'self') { + if (format === 'raw') { + // 从 {{self.name}} 中提取 self.name + return variable.value.replace(/^\{\{(.*)\}\}$/, '$1') + } else { + // 直接返回 {{self.name}} + return variable.value + } + } + if (variable.scope === 'conversation' && variable.step_id) { reference = `conversation.${variable.step_id}.${variable.name}` } else { @@ -272,15 +304,51 @@ const generateVariableReference = (variable: Variable, format: 'raw' | 'wrapped' return format === 'wrapped' ? `{{${reference}}}` : reference } +// 创建变量对象从解析的引用(无需API查询) +const createVariableFromReference = (parsedRef: { scope: string; step_id?: string; name: string }): Variable => { + if (parsedRef.scope === 'conversation' && parsedRef.step_id) { + return { + name: parsedRef.name, + var_type: 'string', // 默认类型,实际类型在运行时确定 + scope: parsedRef.scope, + value: `{{${parsedRef.scope}.${parsedRef.step_id}.${parsedRef.name}}}`, + description: `对话变量: ${parsedRef.name}`, + step_id: parsedRef.step_id, + step: parsedRef.step_id // 使用step_id作为step的默认值 + } + } else { + return { + name: parsedRef.name, + var_type: 'string', // 默认类型 + scope: parsedRef.scope, + value: `{{${parsedRef.scope}.${parsedRef.name}}}`, + description: `${parsedRef.scope}变量: ${parsedRef.name}` + } + } +} + // 初始化选中的变量 const initializeSelectedVariable = async () => { if (props.modelValue && !selectedVariable.value) { const parsedRef = parseVariableReference(props.modelValue) if (parsedRef) { - const foundVariable = await findVariableByReference(parsedRef) - if (foundVariable) { - selectedVariable.value = foundVariable - emit('update:selectedVariable', foundVariable) + // 首先尝试快速创建变量对象(无需API查询) + const quickVariable = createVariableFromReference(parsedRef) + selectedVariable.value = quickVariable + emit('update:selectedVariable', quickVariable) + + // 可选:在后台异步获取完整的变量信息(用于显示更准确的类型和描述) + // 这样用户看不到延迟,但仍能获得准确信息 + try { + const foundVariable = await findVariableByReference(parsedRef) + if (foundVariable) { + // 只有在找到更详细信息时才更新 + selectedVariable.value = foundVariable + emit('update:selectedVariable', foundVariable) + } + } catch (error) { + // 如果API查询失败,保持使用快速创建的变量对象 + console.debug('变量详细信息查询失败,使用默认信息:', error) } } } @@ -299,7 +367,24 @@ watch(() => props.variableName, (newVal) => { watch(() => props.modelValue, async (newVal) => { // 当 modelValue 变化时,重新初始化 selectedVariable if (newVal && !selectedVariable.value) { - await initializeSelectedVariable() + const parsedRef = parseVariableReference(newVal) + if (parsedRef) { + // 快速创建并显示变量对象 + const quickVariable = createVariableFromReference(parsedRef) + selectedVariable.value = quickVariable + emit('update:selectedVariable', quickVariable) + + // 后台获取详细信息 + try { + const foundVariable = await findVariableByReference(parsedRef) + if (foundVariable) { + selectedVariable.value = foundVariable + emit('update:selectedVariable', foundVariable) + } + } catch (error) { + console.debug('变量详细信息查询失败,使用默认信息:', error) + } + } } else if (!newVal) { selectedVariable.value = undefined } @@ -309,6 +394,30 @@ watch(() => props.selectedVariable, (newVal) => { selectedVariable.value = newVal }, { immediate: true }) +// 监听 selfVariables 变化,重新初始化选中的变量 +watch(() => props.selfVariables, async () => { + if (props.modelValue && !selectedVariable.value) { + const parsedRef = parseVariableReference(props.modelValue) + if (parsedRef) { + // 快速创建并显示变量对象 + const quickVariable = createVariableFromReference(parsedRef) + selectedVariable.value = quickVariable + emit('update:selectedVariable', quickVariable) + + // 后台获取详细信息 + try { + const foundVariable = await findVariableByReference(parsedRef) + if (foundVariable) { + selectedVariable.value = foundVariable + emit('update:selectedVariable', foundVariable) + } + } catch (error) { + console.debug('变量详细信息查询失败,使用默认信息:', error) + } + } + } +}, { deep: true }) + // 处理变量名变化 const handleNameChange = (value: string) => { emit('update:variableName', value) @@ -345,6 +454,9 @@ const clearVariable = () => { const getVariableDisplayName = (variable: Variable): string => { if (variable.scope === 'conversation' && variable.step) { return `${variable.step}.${variable.name}` + } else if (variable.scope === 'self') { + // 对于当前节点变量,只显示变量名 + return variable.name } else if (variable.scope !== 'conversation' && variable.scope !== 'env'){ return `${variable.scope}.${variable.name}` } diff --git a/src/components/VariableSelector.vue b/src/components/VariableSelector.vue index cad846bc1dd576ee4b67b29cdc346827e5c6ccec..84d4f03f3858dfe84ebb2f292f2ceb0ab4afaf5f 100644 --- a/src/components/VariableSelector.vue +++ b/src/components/VariableSelector.vue @@ -23,6 +23,8 @@ interface Props { conversationId?: string showVariableReference?: boolean currentStepId?: string + selfVariables?: any[] + selfScopeLabel?: string } const props = withDefaults(defineProps(), { @@ -30,7 +32,9 @@ const props = withDefaults(defineProps(), { placeholder: '选择变量', allowMultiple: false, supportedScopes: () => ['conversation', 'system', 'env', 'user'], - showVariableReference: true + showVariableReference: true, + selfVariables: () => [], + selfScopeLabel: '' }) const emit = defineEmits<{ @@ -73,6 +77,32 @@ const groupedVariables = computed(() => { hasVariables: boolean }> = [] + // 首先添加当前节点变量分组(如果存在且有变量和标签) + if (props.selfVariables && props.selfVariables.length > 0 && props.selfScopeLabel) { + const selfVars = props.selfVariables + .filter(v => v.name) // 只显示有名称的变量 + .map(v => ({ + name: v.name, + var_type: v.type || 'string', + scope: 'self', + value: `{{self.${v.name}}}`, + description: `${props.selfScopeLabel}: ${v.name}` + })) + .filter(variable => + !searchText.value || + variable.name.toLowerCase().includes(searchText.value.toLowerCase()) || + variable.description?.toLowerCase().includes(searchText.value.toLowerCase()) + ) + + if (selfVars.length > 0) { + result.push({ + scope: 'self', + variables: selfVars, + hasVariables: true + }) + } + } + // 按照supportedScopes的顺序返回分组,确保渲染顺序正确 for (const scope of props.supportedScopes) { const variables = groups[scope] || [] @@ -120,6 +150,7 @@ const groupedVariables = computed(() => { }) const scopeLabels: Record = { + self: '当前节点变量', system: '系统变量', user: '用户变量', env: '环境变量', @@ -127,6 +158,9 @@ const scopeLabels: Record = { } const getScopeLabel = (scope: string, nodeId?: string | null, nodeName?: string | null): string => { + if (scope === 'self' && props.selfScopeLabel) { + return props.selfScopeLabel + } if (scope.startsWith('conversation_node_') && nodeName) { return `节点 ${nodeName} 输出` } @@ -300,12 +334,18 @@ const loadVariableTypes = async () => { const formatVariableReference = (variable: Variable): string => { const scopeMap = { + self: 'self', system: 'system', user: 'user', env: 'env', conversation: 'conversation' } + // 当前节点变量已经包含完整的引用格式 + if (variable.scope === 'self') { + return variable.value // 已经是 {{self.name}} 格式 + } + // 前置节点变量需要使用step_id.name的格式 if (variable.step_id && variable.scope === 'conversation') { return `{{${scopeMap[variable.scope]}.${variable.step_id}.${variable.name}}}` diff --git a/src/components/dialoguePanel/DialogueFlow.vue b/src/components/dialoguePanel/DialogueFlow.vue index 82537dcc14e33ef1e67f4eab892035478c4f51fd..11115469b43e576f3ae9f89f57df99d05b82ef21 100644 --- a/src/components/dialoguePanel/DialogueFlow.vue +++ b/src/components/dialoguePanel/DialogueFlow.vue @@ -15,7 +15,6 @@ const props = withDefaults( // 调试方法 const debugCode = (desc: any, title: string) => { - console.log(`🎯 DialogueFlow passing to FlowCode - title: "${title}", code:`, desc); return desc; }; diff --git a/src/components/dialoguePanel/FlowCode.vue b/src/components/dialoguePanel/FlowCode.vue index 5e8cb5bc63be75ba4e9aa7a02e91d14de9798787..3816293f456aeadff1312e604c4bd7e5e6ed7962 100644 --- a/src/components/dialoguePanel/FlowCode.vue +++ b/src/components/dialoguePanel/FlowCode.vue @@ -1,5 +1,5 @@