Skip to content

Commit 44eaedd

Browse files
xlorneclaude
andcommitted
feat: 实现 Groovy 代码编辑器并统一脚本参数名称
- 新增 GroovyCodeEditor 组件,基于 CodeMirror 6 实现 - 替换 PythonCodeEditor 为专用的 Groovy 代码编辑器 - 添加 CodeMirror 相关依赖 - 修复脚本参数名称不一致问题,将 operator/session 统一为 request Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a005d3e commit 44eaedd

8 files changed

Lines changed: 269 additions & 10 deletions

File tree

flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorMatchScript.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class OperatorMatchScript {
1313

1414
public static final String SCRIPT_ANY = """
1515
// @SCRIPT_TITLE 任意用户
16-
def run(operator){
16+
def run(request){
1717
return true
1818
}
1919
""";

flow-engine-framework/src/main/java/com/codingapi/flow/script/node/SubProcessScript.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public class SubProcessScript {
1515

1616
public static final String SCRIPT_DEFAULT = """
1717
// @SCRIPT_TITLE 创建当前流程
18-
def run(session){
19-
return session.toCreateRequest()
18+
def run(request){
19+
return request.toCreateRequest()
2020
}
2121
""";
2222

flow-engine-framework/src/main/java/com/codingapi/flow/script/node/TriggerScript.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class TriggerScript {
1313

1414
public static final String SCRIPT_DEFAULT = """
1515
// @SCRIPT_TITLE 示例触发节点(打印触发日志)
16-
def run(session){
16+
def run(request){
1717
print('hello trigger node.');
1818
}
1919
""";
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# GroovyCodeEditor 设计方案
2+
3+
## 概述
4+
5+
为 Flow Engine 工作流设计器创建一个专门的 Groovy 代码编辑器组件,用于替代当前使用的 PythonCodeEditor。
6+
7+
## 背景
8+
9+
当前 `ScriptEditor` 组件使用 `@flowgram.ai/form-materials` 中的 `PythonCodeEditor` 来编辑 Groovy 脚本,这存在以下问题:
10+
1. 编辑器语言标识不正确(显示 Python 而非 Groovy)
11+
2. 语法高亮不匹配(Python 语法与 Groovy 有差异)
12+
13+
## 技术选型
14+
15+
基于 **CodeMirror 6** 实现,理由:
16+
- 项目中已安装相关依赖(@codemirror/* 系列)
17+
- 社区成熟,支持扩展
18+
- 与现有 @flowgram.ai/coze-editor 技术栈一致
19+
20+
### 依赖
21+
22+
- `@codemirror/view` - 核心视图
23+
- `@codemirror/state` - 状态管理
24+
- `@codemirror/lang-java` - Java 语法支持(Groovy 与 Java 语法高度兼容)
25+
- `@codemirror/theme-one-dark` - 深色主题
26+
27+
## 组件接口
28+
29+
```typescript
30+
interface GroovyCodeEditorProps {
31+
/** 当前脚本内容 */
32+
value: string;
33+
/** 是否只读 */
34+
readonly?: boolean;
35+
/** 内容变更回调 */
36+
onChange?: (value: string) => void;
37+
/** 占位符 */
38+
placeholder?: string;
39+
/** 主题 */
40+
theme?: 'dark' | 'light';
41+
/** 编辑器选项 */
42+
options?: {
43+
fontSize?: number;
44+
minHeight?: number;
45+
maxHeight?: number;
46+
};
47+
}
48+
```
49+
50+
## 文件结构
51+
52+
```
53+
packages/flow-pc/flow-pc-design/src/components/script/components/
54+
├── groovy-code-editor.tsx # 新增:Groovy 代码编辑器
55+
└── advanced-script-editor.tsx # 修改:替换 PythonCodeEditor
56+
```
57+
58+
## 实现要点
59+
60+
### 1. 语法高亮
61+
使用 `@codemirror/lang-java` 实现 Groovy 语法高亮,该语言包与 Groovy 兼容性较好。
62+
63+
### 2. 主题支持
64+
- 深色主题:使用 `@codemirror/theme-one-dark`
65+
- 浅色主题:使用默认浅色主题
66+
67+
### 3. 样式适配
68+
- 编辑器容器样式与现有设计系统一致
69+
- 支持最小/最大高度限制
70+
- 自定义字体大小
71+
72+
## 测试计划
73+
74+
1. 单元测试:验证组件渲染、props 传递
75+
2. 手动测试:
76+
- 验证 Groovy 代码语法高亮效果
77+
- 验证主题切换功能
78+
- 验证只读模式
79+
- 验证与其他组件的集成
80+
81+
## 替代方案
82+
83+
如后续需要更完善的 Groovy 支持,可考虑:
84+
1. 基于 @codemirror/language 自定义 Groovy 语法高亮
85+
2. 添加 Groovy 特定关键字和内置函数自动补全

frontend/packages/flow-pc/flow-pc-design/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
},
2424
"dependencies": {
2525
"@ant-design/icons": "~6.1.0",
26+
"@codemirror/lang-java": "^6.0.2",
27+
"@codemirror/theme-one-dark": "^6.1.3",
2628
"@flow-engine/flow-core": "workspace:*",
27-
"@flow-engine/flow-types": "workspace:*",
2829
"@flow-engine/flow-pc-ui": "workspace:*",
30+
"@flow-engine/flow-types": "workspace:*",
2931
"@flowgram.ai/export-plugin": "1.0.7",
3032
"@flowgram.ai/fixed-layout-editor": "1.0.7",
3133
"@flowgram.ai/fixed-semi-materials": "1.0.7",

frontend/packages/flow-pc/flow-pc-design/src/components/script/components/advanced-script-editor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from "react";
33
import {GroovyScriptContent} from "@/components/script/components/groovy-script-modal";
44
import {MenuUnfoldOutlined, ReloadOutlined} from "@ant-design/icons";
55
import {GroovyVariableMapping, ScriptType} from "@/components/script/typings";
6-
import {PythonCodeEditor} from "@flowgram.ai/form-materials";
6+
import {GroovyCodeEditor} from './groovy-code-editor';
77
import {GroovyScriptConvertorUtil} from "@/components/script/utils/convertor";
88

99
interface ScriptEditorProps {
@@ -38,7 +38,7 @@ export const ScriptEditor: React.FC<ScriptEditorProps> = (props: ScriptEditorPro
3838
};
3939

4040
return (
41-
<PythonCodeEditor
41+
<GroovyCodeEditor
4242
value={script}
4343
readonly={readonly}
4444
onChange={handleChange}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import React, { useEffect, useRef } from 'react';
2+
import { EditorState } from '@codemirror/state';
3+
import { EditorView, keymap, placeholder as cmPlaceholder, lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine } from '@codemirror/view';
4+
import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
5+
import { indentOnInput, bracketMatching, foldGutter, foldKeymap } from '@codemirror/language';
6+
import { java } from '@codemirror/lang-java';
7+
import { oneDark } from '@codemirror/theme-one-dark';
8+
import { syntaxHighlighting, defaultHighlightStyle, HighlightStyle } from '@codemirror/language';
9+
import { tags } from '@lezer/highlight';
10+
11+
interface GroovyCodeEditorProps {
12+
value: string;
13+
readonly?: boolean;
14+
onChange?: (value: string) => void;
15+
placeholder?: string;
16+
theme?: 'dark' | 'light';
17+
options?: {
18+
fontSize?: number;
19+
minHeight?: number;
20+
maxHeight?: number;
21+
};
22+
}
23+
24+
const darkHighlightStyle = HighlightStyle.define([
25+
{ tag: tags.keyword, color: '#c678dd' },
26+
{ tag: tags.operator, color: '#56b6c2' },
27+
{ tag: tags.variableName, color: '#e5c07b' },
28+
{ tag: tags.string, color: '#98c379' },
29+
{ tag: tags.comment, color: '#5c6370', fontStyle: 'italic' },
30+
{ tag: tags.number, color: '#d19a66' },
31+
{ tag: tags.bool, color: '#d19a66' },
32+
{ tag: tags.null, color: '#d19a66' },
33+
{ tag: tags.propertyName, color: '#e06c75' },
34+
{ tag: tags.function(tags.variableName), color: '#61afef' },
35+
{ tag: tags.definition(tags.variableName), color: '#e5c07b' },
36+
{ tag: tags.typeName, color: '#e5c07b' },
37+
{ tag: tags.className, color: '#e5c07b' },
38+
{ tag: tags.annotation, color: '#d19a66' },
39+
]);
40+
41+
export const GroovyCodeEditor: React.FC<GroovyCodeEditorProps> = (props) => {
42+
const {
43+
value,
44+
readonly = false,
45+
onChange,
46+
placeholder = '请输入 Groovy 脚本...',
47+
theme = 'dark',
48+
options = {}
49+
} = props;
50+
51+
const editorRef = useRef<HTMLDivElement>(null);
52+
const viewRef = useRef<EditorView | null>(null);
53+
54+
const { fontSize = 14, minHeight = 300, maxHeight = 300 } = options;
55+
56+
useEffect(() => {
57+
if (!editorRef.current) return;
58+
59+
const extensions = [
60+
lineNumbers(),
61+
highlightActiveLineGutter(),
62+
highlightSpecialChars(),
63+
history(),
64+
foldGutter(),
65+
drawSelection(),
66+
dropCursor(),
67+
EditorState.allowMultipleSelections.of(true),
68+
indentOnInput(),
69+
bracketMatching(),
70+
rectangularSelection(),
71+
crosshairCursor(),
72+
highlightActiveLine(),
73+
keymap.of([
74+
...defaultKeymap,
75+
...historyKeymap,
76+
...foldKeymap,
77+
indentWithTab,
78+
]),
79+
java(),
80+
cmPlaceholder(placeholder),
81+
EditorView.updateListener.of((update) => {
82+
if (update.docChanged && onChange) {
83+
onChange(update.state.doc.toString());
84+
}
85+
}),
86+
EditorView.theme({
87+
'&': {
88+
fontSize: `${fontSize}px`,
89+
minHeight: `${minHeight}px`,
90+
maxHeight: `${maxHeight}px`,
91+
},
92+
'.cm-scroller': {
93+
overflow: 'auto',
94+
},
95+
'.cm-content': {
96+
fontFamily: 'monospace',
97+
},
98+
}),
99+
];
100+
101+
if (theme === 'dark') {
102+
extensions.push(oneDark);
103+
extensions.push(syntaxHighlighting(darkHighlightStyle));
104+
} else {
105+
extensions.push(syntaxHighlighting(defaultHighlightStyle));
106+
}
107+
108+
if (readonly) {
109+
extensions.push(EditorState.readOnly.of(true));
110+
}
111+
112+
const state = EditorState.create({
113+
doc: value,
114+
extensions,
115+
});
116+
117+
const view = new EditorView({
118+
state,
119+
parent: editorRef.current,
120+
});
121+
122+
viewRef.current = view;
123+
124+
return () => {
125+
view.destroy();
126+
};
127+
}, [theme, fontSize, minHeight, maxHeight, placeholder, readonly]);
128+
129+
useEffect(() => {
130+
if (viewRef.current && value !== viewRef.current.state.doc.toString()) {
131+
viewRef.current.dispatch({
132+
changes: {
133+
from: 0,
134+
to: viewRef.current.state.doc.length,
135+
insert: value,
136+
},
137+
});
138+
}
139+
}, [value]);
140+
141+
return <div ref={editorRef} style={{ border: '1px solid #434343', borderRadius: 6, overflow: 'hidden' }} />;
142+
};

frontend/pnpm-lock.yaml

Lines changed: 33 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)