CodeEditor 代码编辑器
基于 Monaco Editor 的代码编辑器组件,支持多种编程语言和主题。
基础用法
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const code = ref(`function hello() {
console.log('Hello, World!')
}`)
</script>
<template>
<CodeEditor
v-model="code"
language="javascript"
/>
</template>多语言支持
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const language = ref('javascript')
const code = ref('')
const languages = [
'javascript',
'typescript',
'html',
'css',
'json',
'python',
'java',
'go',
'rust',
'sql'
]
</script>
<template>
<div>
<a-select
v-model:value="language"
style="width: 200px; margin-bottom: 16px"
>
<a-select-option
v-for="lang in languages"
:key="lang"
:value="lang"
>
{{ lang }}
</a-select-option>
</a-select>
<CodeEditor
v-model="code"
:language="language"
height="500px"
/>
</div>
</template>主题切换
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const code = ref('')
const theme = ref('vs-dark')
const themes = [
{ label: 'Visual Studio', value: 'vs' },
{ label: 'Visual Studio Dark', value: 'vs-dark' },
{ label: 'High Contrast Dark', value: 'hc-black' }
]
</script>
<template>
<div>
<a-radio-group
v-model:value="theme"
button-style="solid"
style="margin-bottom: 16px"
>
<a-radio-button
v-for="t in themes"
:key="t.value"
:value="t.value"
>
{{ t.label }}
</a-radio-button>
</a-radio-group>
<CodeEditor
v-model="code"
language="javascript"
:theme="theme"
height="500px"
/>
</div>
</template>只读模式
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const code = ref(`// 这是只读代码
function example() {
return 'Hello, World!'
}`)
</script>
<template>
<CodeEditor
v-model="code"
language="javascript"
readonly
/>
</template>API
Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 绑定值(代码字符串) | string | '' |
| language | 编程语言 | string | 'javascript' |
| theme | 主题 | 'vs' | 'vs-dark' | 'hc-black' | 'vs-dark' |
| height | 编辑器高度 | string | number | '400px' |
| readonly | 是否只读 | boolean | false |
| disabled | 是否禁用 | boolean | false |
| options | Monaco Editor 配置 | object | {} |
| diffEditor | 是否启用对比模式 | boolean | false |
| original | 对比模式的原始代码 | string | '' |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| update:modelValue | 内容变化时触发 | (value: string) => void |
| change | 内容变化时触发 | (value: string) => void |
| focus | 获得焦点时触发 | () => void |
| blur | 失去焦点时触发 | () => void |
| editorMounted | 编辑器挂载完成 | (editor: any) => void |
Methods
| 方法名 | 说明 | 参数 |
|---|---|---|
| getValue | 获取代码内容 | - |
| setValue | 设置代码内容 | (value: string) => void |
| format | 格式化代码 | - |
| focus | 聚焦编辑器 | - |
| getEditor | 获取编辑器实例 | - |
完整示例
vue
<script setup lang="ts">
import { ref } from 'vue'
import { message } from 'ant-design-vue'
import { CodeEditor } from '@vben/common-ui'
const editorRef = ref()
const language = ref('javascript')
const theme = ref('vs-dark')
const code = ref(`function fibonacci(n) {
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
}
console.log(fibonacci(10))`)
const languages = [
'javascript',
'typescript',
'html',
'css',
'json',
'python',
'java',
'go'
]
const themes = [
{ label: 'Light', value: 'vs' },
{ label: 'Dark', value: 'vs-dark' },
{ label: 'High Contrast', value: 'hc-black' }
]
const handleChange = (value: string) => {
console.log('代码变化:', value)
}
const handleFormat = () => {
editorRef.value?.format()
message.success('代码已格式化')
}
const handleCopy = async () => {
const code = editorRef.value?.getValue()
await navigator.clipboard.writeText(code)
message.success('代码已复制到剪贴板')
}
const handleRun = () => {
const code = editorRef.value?.getValue()
try {
// 注意:eval 有安全风险,仅用于演示
const result = eval(code)
console.log('执行结果:', result)
message.success('代码执行成功')
} catch (error) {
message.error(`执行错误:${error.message}`)
}
}
const handleEditorMounted = (editor: any) => {
console.log('编辑器已挂载:', editor)
// 添加自定义快捷键
editor.addCommand(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
() => {
message.info('保存快捷键触发')
}
)
}
</script>
<template>
<div class="editor-container">
<div class="editor-toolbar">
<a-space>
<a-select
v-model:value="language"
style="width: 150px"
>
<a-select-option
v-for="lang in languages"
:key="lang"
:value="lang"
>
{{ lang }}
</a-select-option>
</a-select>
<a-radio-group
v-model:value="theme"
button-style="solid"
>
<a-radio-button
v-for="t in themes"
:key="t.value"
:value="t.value"
>
{{ t.label }}
</a-radio-button>
</a-radio-group>
<a-button @click="handleFormat">格式化</a-button>
<a-button @click="handleCopy">复制</a-button>
<a-button type="primary" @click="handleRun">运行</a-button>
</a-space>
</div>
<CodeEditor
ref="editorRef"
v-model="code"
:language="language"
:theme="theme"
height="600px"
@change="handleChange"
@editor-mounted="handleEditorMounted"
/>
</div>
</template>
<style scoped>
.editor-container {
border: 1px solid #d9d9d9;
border-radius: 4px;
overflow: hidden;
}
.editor-toolbar {
padding: 12px 16px;
background: #fafafa;
border-bottom: 1px solid #d9d9d9;
}
</style>对比模式
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const original = ref(`function hello() {
console.log('Hello')
}`)
const modified = ref(`function hello() {
console.log('Hello, World!')
}`)
</script>
<template>
<CodeEditor
v-model="modified"
:original="original"
language="javascript"
diff-editor
height="500px"
/>
</template>自定义配置
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const code = ref('')
const options = {
fontSize: 14,
fontFamily: 'Fira Code, Consolas, Monaco, monospace',
lineNumbers: 'on',
minimap: { enabled: true },
scrollBeyondLastLine: false,
automaticLayout: true,
tabSize: 2,
wordWrap: 'on',
folding: true,
lineDecorationsWidth: 10,
lineNumbersMinChars: 3,
renderLineHighlight: 'all',
scrollbar: {
vertical: 'visible',
horizontal: 'visible'
}
}
</script>
<template>
<CodeEditor
v-model="code"
language="javascript"
:options="options"
/>
</template>语法验证
vue
<script setup lang="ts">
import { ref, watch } from 'vue'
import { CodeEditor } from '@vben/common-ui'
const code = ref('')
const errors = ref<any[]>([])
watch(code, (newCode) => {
// 简单的语法检查示例
try {
new Function(newCode)
errors.value = []
} catch (error) {
errors.value = [{
message: error.message,
severity: 'error'
}]
}
})
</script>
<template>
<div>
<CodeEditor
v-model="code"
language="javascript"
height="400px"
/>
<div v-if="errors.length" class="errors">
<a-alert
v-for="(error, index) in errors"
:key="index"
:message="error.message"
type="error"
show-icon
/>
</div>
</div>
</template>
<style scoped>
.errors {
margin-top: 16px;
}
</style>代码片段
typescript
// 注册代码片段
monaco.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems: () => {
return {
suggestions: [
{
label: 'log',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: 'console.log(${1:message})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Log to console'
},
{
label: 'func',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: 'function ${1:name}(${2:params}) {\n\t${3}\n}',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Function declaration'
}
]
}
}
})快捷键
| 快捷键 | 功能 |
|---|---|
| Ctrl/Cmd + S | 保存 |
| Ctrl/Cmd + F | 查找 |
| Ctrl/Cmd + H | 替换 |
| Ctrl/Cmd + / | 注释/取消注释 |
| Ctrl/Cmd + D | 选择下一个匹配项 |
| Alt + Up/Down | 移动行 |
| Shift + Alt + Up/Down | 复制行 |
| Ctrl/Cmd + Shift + K | 删除行 |
| Ctrl/Cmd + ] | 增加缩进 |
| Ctrl/Cmd + [ | 减少缩进 |
| Ctrl/Cmd + Shift + F | 格式化代码 |
| F12 | 跳转到定义 |
| Alt + F12 | 查看定义 |
支持的语言
Monaco Editor 支持以下编程语言:
- JavaScript / TypeScript
- HTML / CSS / SCSS / Less
- JSON / YAML / XML
- Python / Java / C# / C++
- Go / Rust / PHP
- SQL / Shell / Dockerfile
- Markdown
- 等等...
注意事项
注意
- Monaco Editor 体积较大,建议按需加载
- 在生产环境中不要使用 eval 执行代码
- 大文件编辑可能影响性能
- 需要配置 Monaco Editor 的 CDN 或本地路径
提示
- 使用
options属性自定义编辑器配置 - 可以通过
getEditor()获取编辑器实例 - 支持代码片段和自动补全
- 使用
diffEditor启用对比模式 - 支持自定义主题和语言