vue页面中嵌入代码编辑器

一.页面样式:
在这里插入图片描述

在这里插入图片描述

二、组件代码:
首先建一个vue页面放到components\CodeEditor 目录下 index.vue
把下面代码放入期中

<script setup>
// 导入monaco编辑器
import * as monaco from "monaco-editor";
import { ref, onMounted, onUnmounted, watch, nextTick, toRaw } from "vue";

const props = defineProps({
  modelValue: {
    type: String,
    default: "",
  },
  language: {
    type: String,
    default: "javascript",
  },
  height: {
    type: Number,
    default: 300,
  },
  theme: {
    type: String,
    default: "vs-dark",
  },
});

const emit = defineEmits(["update:modelValue", "change"]);

// 编辑器容器div
const editorContainer = ref(null);
// 编辑器实列
const editor = ref(null);
let isInternalChange = false;
const isMaximized = ref(false);
const originalHeight = ref(props.height);

// 切换主题
const switchTheme = (themeName) => {
  if (editor.value) {
    const rawEditor = toRaw(editor.value);
    monaco.editor.setTheme(themeName);
  }
};

// 切换最大化状态
const toggleMaximize = () => {
  isMaximized.value = !isMaximized.value;
  if (editor.value) {
    const rawEditor = toRaw(editor.value);
    nextTick(() => {
      rawEditor.layout();
    });
  }
};

// 监听主题变化
watch(
  () => props.theme,
  (newTheme) => {
    switchTheme(newTheme);
  }
);

// 初始化编辑器
onMounted(async () => {
  await nextTick();
  if (editorContainer.value) {
    const rawContainer = toRaw(editorContainer.value);
    editor.value = monaco.editor.create(rawContainer, {
      value: props.modelValue,
      language: props.language,
      theme: props.theme,
      automaticLayout: true,
      minimap: { enabled: false },
      scrollBeyondLastLine: false,
      fontSize: 14,
      tabSize: 2,
      formatOnPaste: true,
      formatOnType: false,
      suggestOnTriggerCharacters: false,
      snippetSuggestions: "none",
      wordWrap: "on",
      renderWhitespace: "none",
      overviewRulerBorder: false,
      scrollBeyondLastColumn: 0,
      renderLineHighlight: "none",
      hideCursorInOverviewRuler: true,
      lineNumbers: "on",
      glyphMargin: false,
      folding: true,
      lineDecorationsWidth: 0,
      lineNumbersMinChars: 3,
    });

    const rawEditor = toRaw(editor.value);
    rawEditor.onDidChangeModelContent(() => {
      if (!isInternalChange) {
        const value = rawEditor.getValue();
        emit("update:modelValue", value);
        emit("change", value);
      }
    });

    // 确保编辑器布局正确
    setTimeout(() => {
      const rawEditor = toRaw(editor.value);
      rawEditor?.layout();
    }, 100);
  }
});

// 监听 modelValue 的变化
watch(
  () => props.modelValue,
  (newValue) => {
    if (editor.value) {
      const rawEditor = toRaw(editor.value);
      if (newValue !== rawEditor.getValue()) {
        isInternalChange = true;
        rawEditor.setValue(newValue);
        isInternalChange = false;
      }
    }
  }
);

// 当组件被卸载时调用该回调函数
onUnmounted(() => {
  if (editor.value) {
    const rawEditor = toRaw(editor.value);
    rawEditor.dispose();
  }
});

// 暴露方法给父组件
defineExpose({
  switchTheme,
  toggleMaximize,
});
</script>

<template>
  <div class="code-editor-container" :class="{ 'is-maximized': isMaximized }">
    <div class="editor-toolbar">
      <el-button-group>
        <el-button size="small" @click="switchTheme('vs')">浅色</el-button>
        <el-button size="small" @click="switchTheme('vs-dark')">深色</el-button>
      </el-button-group>
      <el-button size="small" @click="toggleMaximize">
        <el-icon
          ><full-screen v-if="!isMaximized" /><scale-to-original v-else
        /></el-icon>
      </el-button>
    </div>
    <div
      ref="editorContainer"
      :style="{
        height: isMaximized ? 'calc(100vh - 120px)' : `${height}px`,
        transition: 'height 0.3s',
      }"
    ></div>
  </div>
</template>

<style scoped>
.code-editor-container {
  position: relative;
  width: 100%;
}

.code-editor-container.is-maximized {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2000;
  background: var(--el-bg-color);
  padding: 20px;
}

.editor-toolbar {
  margin-bottom: 8px;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

div[ref="editorContainer"] {
  width: 100%;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
}
</style>

三、如何使用上面建好的组件

在需要使用的页面,把这个组件添加进去

import CodeEditor from "@/components/CodeEditor/index.vue";

具体的使用:

  <el-form-item label="Python代码" prop="pyCode">
          <code-editor
            v-model="form.pyCode"
            :language="'python'"
            :height="300"
            @change="handlePyCodeChange"
          />
        </el-form-item>
const data = reactive({
  form: {
    id: null,
    skillName: null,
    skillDesc: null,
    status: null,
    sitePkg: null,
    pyCode: "",
    inParam: "",
    outParam: "",
    remark: null,
  },
  rules: {
    pyCode: [{ required: true, message: "请输入Python代码", trigger: "blur" }],
  },
});

// Python 代码变更处理
const handlePyCodeChange = (value) => {
  // 把 具体的内容传到上面的变量中
  form.value.pyCode = value;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值