Skip to content

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是否只读booleanfalse
disabled是否禁用booleanfalse
optionsMonaco Editor 配置object{}
diffEditor是否启用对比模式booleanfalse
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
  • 等等...

注意事项

注意

  1. Monaco Editor 体积较大,建议按需加载
  2. 在生产环境中不要使用 eval 执行代码
  3. 大文件编辑可能影响性能
  4. 需要配置 Monaco Editor 的 CDN 或本地路径

提示

  • 使用 options 属性自定义编辑器配置
  • 可以通过 getEditor() 获取编辑器实例
  • 支持代码片段和自动补全
  • 使用 diffEditor 启用对比模式
  • 支持自定义主题和语言

相关链接

MIT License