Skip to content

Commit 054268f

Browse files
committed
feat: Monaco Editor代码编辑器 - CODE/SQL节点高亮+Juggle变量自动补全(需求17)
1 parent 543bbab commit 054268f

4 files changed

Lines changed: 172 additions & 5 deletions

File tree

JuggleNet6.Frontend/package-lock.json

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

JuggleNet6.Frontend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
},
1111
"dependencies": {
1212
"@element-plus/icons-vue": "^2.3.2",
13+
"@monaco-editor/loader": "^1.7.0",
1314
"@vue-flow/background": "^1.3.2",
1415
"@vue-flow/controls": "^1.1.3",
1516
"@vue-flow/core": "^1.48.2",
@@ -18,6 +19,7 @@
1819
"docx": "^9.6.1",
1920
"element-plus": "^2.13.6",
2021
"file-saver": "^2.0.5",
22+
"monaco-editor": "^0.55.1",
2123
"pinia": "^3.0.4",
2224
"vue": "^3.5.30",
2325
"vue-router": "^4.6.4"
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<template>
2+
<div ref="containerRef" :style="{ width: width, height: height, border: '1px solid #ddd', borderRadius: '4px' }"></div>
3+
</template>
4+
5+
<script setup lang="ts">
6+
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
7+
import loader from '@monaco-editor/loader'
8+
9+
const props = withDefaults(defineProps<{
10+
modelValue?: string
11+
language?: string
12+
theme?: string
13+
width?: string
14+
height?: string
15+
readOnly?: boolean
16+
}>(), {
17+
modelValue: '',
18+
language: 'javascript',
19+
theme: 'vs-dark',
20+
width: '100%',
21+
height: '300px',
22+
readOnly: false
23+
})
24+
25+
const emit = defineEmits<{ (e: 'update:modelValue', val: string): void }>()
26+
27+
const containerRef = ref<HTMLElement>()
28+
let monacoEditor: any = null
29+
30+
onMounted(async () => {
31+
// 配置 Monaco loader 使用 CDN(可自行更换为本地路径)
32+
loader.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs' } })
33+
34+
await nextTick()
35+
const monaco = await loader.init()
36+
37+
monacoEditor = monaco.editor.create(containerRef.value!, {
38+
value: props.modelValue,
39+
language: props.language,
40+
theme: props.theme,
41+
readOnly: props.readOnly,
42+
automaticLayout: true,
43+
minimap: { enabled: false },
44+
fontSize: 13,
45+
lineNumbers: 'on',
46+
scrollBeyondLastLine: false,
47+
wordWrap: 'on',
48+
tabSize: 2,
49+
// 代码提示:$var / $static 对象
50+
suggestOnTriggerCharacters: true
51+
})
52+
53+
// 注册 Juggle JS 变量提示
54+
monaco.languages.registerCompletionItemProvider('javascript', {
55+
provideCompletionItems: (model: any, position: any) => {
56+
const word = model.getWordUntilPosition(position)
57+
const range = {
58+
startLineNumber: position.lineNumber,
59+
endLineNumber: position.lineNumber,
60+
startColumn: word.startColumn,
61+
endColumn: word.endColumn
62+
}
63+
return {
64+
suggestions: [
65+
{ label: '$var.getVariableValue', kind: monaco.languages.CompletionItemKind.Function,
66+
insertText: "$var.getVariableValue('${1:varName}')", insertTextRules: 4,
67+
documentation: '获取流程变量值', range },
68+
{ label: '$var.setVariableValue', kind: monaco.languages.CompletionItemKind.Function,
69+
insertText: "$var.setVariableValue('${1:varName}', ${2:value})", insertTextRules: 4,
70+
documentation: '设置流程变量值', range },
71+
{ label: '$static.getVariableValue', kind: monaco.languages.CompletionItemKind.Function,
72+
insertText: "$static.getVariableValue('${1:varCode}')", insertTextRules: 4,
73+
documentation: '获取全局静态变量', range },
74+
{ label: '$static.setVariableValue', kind: monaco.languages.CompletionItemKind.Function,
75+
insertText: "$static.setVariableValue('${1:varCode}', ${2:value})", insertTextRules: 4,
76+
documentation: '设置全局静态变量', range },
77+
{ label: 'JSON.parse', kind: monaco.languages.CompletionItemKind.Function,
78+
insertText: 'JSON.parse(${1:str})', insertTextRules: 4, range },
79+
{ label: 'JSON.stringify', kind: monaco.languages.CompletionItemKind.Function,
80+
insertText: 'JSON.stringify(${1:obj})', insertTextRules: 4, range }
81+
]
82+
}
83+
}
84+
})
85+
86+
monacoEditor.onDidChangeModelContent(() => {
87+
emit('update:modelValue', monacoEditor.getValue())
88+
})
89+
})
90+
91+
watch(() => props.modelValue, (newVal) => {
92+
if (monacoEditor && monacoEditor.getValue() !== newVal) {
93+
monacoEditor.setValue(newVal)
94+
}
95+
})
96+
97+
onBeforeUnmount(() => {
98+
monacoEditor?.dispose()
99+
})
100+
</script>

JuggleNet6.Frontend/src/views/flow/FlowDesign.vue

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,9 +494,13 @@
494494
</div>
495495
<div class="prop-item">
496496
<label>脚本内容</label>
497-
<el-input v-model="selectedNode.codeConfig.script" type="textarea" :rows="10"
498-
placeholder="// 示例:&#10;var name = $var.getVariableValue('input_name')&#10;$var.setVariableValue('output_result', 'Hello, ' + name)"
499-
class="code-editor" />
497+
<MonacoEditor
498+
v-model="selectedNode.codeConfig.script"
499+
language="javascript"
500+
theme="vs-dark"
501+
height="240px"
502+
style="border-radius:4px;overflow:hidden"
503+
/>
500504
</div>
501505
</template>
502506

@@ -519,8 +523,13 @@
519523
</div>
520524
<div class="prop-item">
521525
<label>SQL 语句</label>
522-
<el-input v-model="selectedNode.mysqlConfig.sql" type="textarea" :rows="5"
523-
placeholder="SELECT * FROM table WHERE id = ${input_id}" class="code-editor" />
526+
<MonacoEditor
527+
v-model="selectedNode.mysqlConfig.sql"
528+
language="sql"
529+
theme="vs-dark"
530+
height="160px"
531+
style="border-radius:4px;overflow:hidden"
532+
/>
524533
</div>
525534
<div class="prop-item" v-if="selectedNode.mysqlConfig.operationType === 'QUERY'">
526535
<label>查询结果写入</label>
@@ -876,6 +885,7 @@ import { ref, computed, onMounted, watch, nextTick } from 'vue'
876885
import { useRoute, useRouter } from 'vue-router'
877886
import { ElMessage } from 'element-plus'
878887
import request from '../../utils/request'
888+
import MonacoEditor from '../../components/MonacoEditor.vue'
879889
880890
// VueFlow
881891
import { VueFlow, Position } from '@vue-flow/core'

0 commit comments

Comments
 (0)