Skip to content

Commit a04c4bc

Browse files
xlorneclaude
andcommitted
fix: properly generate and parse Groovy script format for node title
- Fix toGroovySyntax to generate complete function format: def run(request){// @title\nreturn ...} - Fix toLabelExpression to parse new function wrapper format - Fix isAdvancedMode to correctly identify new script format with // @title and def run() wrapper - Simplify node-title component to avoid render loop issues The backend expects the full Groovy function format, not just the return statement. This fix ensures the title script is correctly saved and loaded from backend. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 87165bc commit a04c4bc

3 files changed

Lines changed: 34 additions & 35 deletions

File tree

frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/strategy/node-title.tsx

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useMemo, useRef } from 'react';
1+
import React, { useMemo, useRef, useState, useCallback } from 'react';
22
import { Form, Button, Space } from 'antd';
33
import { EditOutlined } from '@ant-design/icons';
44
import { Field, FieldRenderProps } from '@flowgram.ai/fixed-layout-editor';
@@ -11,9 +11,8 @@ import { TitleConfigModal } from './TitleConfigModal';
1111
*/
1212
export const NodeTitleStrategy: React.FC = () => {
1313
const [showConfigModal, setShowConfigModal] = useState(false);
14-
const [currentScript, setCurrentScript] = useState('');
15-
const [displayScript, setDisplayScript] = useState('');
16-
// 使用 ref 保存 onChange 回调,以便在弹框确认后更新编辑器状态
14+
const [modalScript, setModalScript] = useState('');
15+
// 使用 ref 保存 onChange 回调
1716
const onChangeRef = useRef<((value: string) => void) | null>(null);
1817

1918
// 获取表单字段(从context获取)
@@ -26,7 +25,7 @@ export const NodeTitleStrategy: React.FC = () => {
2625
const mappings = GroovyVariableService.getAllMappings(formFields);
2726

2827
// 渲染预览内容
29-
const renderPreview = (script: string) => {
28+
const renderPreview = useCallback((script: string) => {
3029
if (!script) {
3130
return '(未配置)';
3231
}
@@ -44,23 +43,20 @@ export const NodeTitleStrategy: React.FC = () => {
4443
}
4544

4645
return '(自定义配置)';
47-
};
46+
}, [mappings]);
4847

49-
const handleOpenConfig = () => {
50-
// 使用当前显示的脚本值
51-
setCurrentScript(displayScript);
48+
const handleOpenConfig = (currentValue: string) => {
49+
setModalScript(currentValue || '');
5250
setShowConfigModal(true);
5351
};
5452

55-
const handleConfirm = (script: string) => {
56-
// 更新显示的脚本值
57-
setDisplayScript(script);
58-
// 关键修复:调用 onChange 回调更新编辑器状态
53+
const handleConfirm = useCallback((script: string) => {
54+
// 调用 onChange 回调更新编辑器状态
5955
if (onChangeRef.current) {
6056
onChangeRef.current(script);
6157
}
6258
setShowConfigModal(false);
63-
};
59+
}, []);
6460

6561
return (
6662
<>
@@ -73,11 +69,6 @@ export const NodeTitleStrategy: React.FC = () => {
7369
// 保存 onChange 回调到 ref
7470
onChangeRef.current = onChange;
7571

76-
// 更新显示的脚本值(当编辑器状态改变时)
77-
if (value !== displayScript) {
78-
setDisplayScript(value || '');
79-
}
80-
8172
return (
8273
<Space.Compact style={{ width: '100%' }}>
8374
<div
@@ -93,11 +84,11 @@ export const NodeTitleStrategy: React.FC = () => {
9384
textOverflow: 'ellipsis',
9485
}}
9586
>
96-
{renderPreview(value)}
87+
{renderPreview(value || '')}
9788
</div>
9889
<Button
9990
icon={<EditOutlined />}
100-
onClick={handleOpenConfig}
91+
onClick={() => handleOpenConfig(value || '')}
10192
style={{ borderRadius: '0 6px 6px 0' }}
10293
>
10394
编辑
@@ -111,7 +102,7 @@ export const NodeTitleStrategy: React.FC = () => {
111102

112103
{showConfigModal && (
113104
<TitleConfigModal
114-
script={currentScript}
105+
script={modalScript}
115106
formFields={formFields}
116107
onConfirm={handleConfirm}
117108
onCancel={() => setShowConfigModal(false)}

frontend/packages/flow-pc/flow-pc-design/src/services/groovy-variable-service.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,14 @@ export class GroovyVariableService {
134134

135135
/**
136136
* 检查是否为高级模式(无法解析回可视化)
137-
* 通过检测 // @TITLE 注释
137+
* 通过检测完整的 def run(request){// @TITLE ...} 格式
138138
*/
139139
static isAdvancedMode(script: string): boolean {
140-
return script.includes('// @TITLE');
140+
// 检查是否包含 // @TITLE 注释
141+
if (!script.includes('// @TITLE')) {
142+
return false;
143+
}
144+
// 检查是否有完整的函数包装 def run(request){...}
145+
return /def\s+run\s*\(\s*request\s*\)\s*\{[\s\S]*\/\/\s*@TITLE[\s\S]*\}/.test(script);
141146
}
142147
}

frontend/packages/flow-pc/flow-pc-design/src/utils/title-syntax-converter.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ export class TitleSyntaxConverter {
6969
groovyExpression = parts.join(' + ');
7070
}
7171

72-
// 添加 return 和标题注释
73-
return `${TITLE_COMMENT}\nreturn ${groovyExpression}`;
72+
// 添加 return 和标题注释,包装成完整的Groovy函数
73+
// 后端期望格式: def run(request){// @TITLE\nreturn "..."}
74+
return `def run(request){\n${TITLE_COMMENT}\nreturn ${groovyExpression}\n}`;
7475
}
7576

7677
/**
@@ -100,6 +101,13 @@ export class TitleSyntaxConverter {
100101
return null; // 无法解析
101102
}
102103

104+
// 移除函数包装 def run(request){...}
105+
// 提取函数体内容
106+
const funcMatch = result.match(/def\s+run\s*\([^)]*\)\s*\{([\s\S]*)\}/);
107+
if (funcMatch) {
108+
result = funcMatch[1];
109+
}
110+
103111
// 移除 @TITLE 注释
104112
result = result.replace(TITLE_COMMENT, '').trim();
105113

@@ -129,16 +137,11 @@ export class TitleSyntaxConverter {
129137
placeholders++;
130138
}
131139

132-
// 现在处理字符串拼接,移除引号和 +
133-
// 移除所有 " + " (带空格的字符串拼接)
134-
result = result.replace(/\s*\+\s*/g, '');
135-
136-
// 移除引号,但保留占位符
137-
result = result.replace(/^"([^"]*)"$/g, '$1'); // 单独的字符串
138-
result = result.replace(/^"([^"]*)"(.*)"$/g, '$1$2'); // 开头的引号
139-
result = result.replace(/^(.*?)"([^"]*)"$/g, '$1$2'); // 结尾的引号
140+
// 处理字符串拼接:移除引号之间的 + 连接符
141+
// 使用更精确的正则来处理 "text" + expr + "text" 格式
142+
result = result.replace(/"([^"]*)"\s*\+\s*/g, '$1'); // "text" + -> text
143+
result = result.replace(/\s*\+\s*"([^"]*)"/g, '$1'); // + "text" -> text
140144

141-
// 还有一些情况需要处理,让我们用更简单的方法
142145
// 移除所有剩余的引号
143146
result = result.replace(/"/g, '');
144147

0 commit comments

Comments
 (0)