codemirror vue3 中使用

本文介绍了如何在Vue项目中使用CodeMirror构建一个可定制的代码编辑器,包含多种语言插件(如Python、Java和JavaScript),并支持智能缩进和实时代码执行功能。组件还提供了过滤器、数学选项和数据添加功能,以及代码片段的插入和获取功能。
摘要由CSDN通过智能技术生成
<template>

  <div>

    <div class="editorRef min-h-[320px] h-[320px] border-1" ref="editorRef"></div>

  </div>

</template>



<script setup lang="ts">

import { ref, shallowRef, reactive, watchEffect, nextTick, onMounted } from 'vue';

import { basicSetup } from 'codemirror';

import { EditorState, EditorSelection } from '@codemirror/state';

import { EditorView, keymap, ViewPlugin } from '@codemirror/view';

import { autocompletion } from '@codemirror/autocomplete';

import { indentWithTab } from '@codemirror/commands';

import { githubLight } from '@uiw/codemirror-theme-github';

import { python } from '@codemirror/lang-python';

import { java } from '@codemirror/lang-java';

import { javascript } from '@codemirror/lang-javascript';



type Props = {

  doc: string;

};



const props = defineProps<Props>();

const editorRef = ref<any>(null);

const editorView = shallowRef<any>(null);

const editorState = shallowRef<any>(null);

const chinesePhrases = {

  // @codemirror/view

  'Control character': '控制字符',

  // @codemirror/commands

  'Selection deleted': '选择删除',

  // @codemirror/language

  'Folded lines': '折叠行',

  'Unfolded lines': '折叠行',

  to: '向前',

  'folded code': '折叠代码',

  unfold: '展开代码',

  'Fold line': '',

  'Unfold line': '展开行',

  // @codemirror/search

  'Go to line': '跳转到行',

  go: '确定',

  Find: '查找',

  Replace: '替换',

  next: '下一个',

  previous: '上一个',

  all: '所有',

  'match case': '区分大小写',

  regexp: '正则表达式',

  'by word': '全字匹配',

  replace: '替换',

  'replace all': '全部替换',

  close: '关闭',

  'current match': '当前匹配',

  'replaced $ matches': '$ Treffer ersetzt',

  'replaced match on line $': 'Treffer on Zeile $ ersetzt',

  'on line': 'auf Zeile',

  // @codemirror/autocomplete

  Completions: '完成',

  // @codemirror/lint

  Diagnostics: '诊断',

  'No diagnostics': '不诊断',

};



const initState = doc => {

  let state = {

    doc: mapDoc(doc) || '',

    extensions: [

      basicSetup,

      githubLight,

      keymap.of([indentWithTab]),

      python(),

      autocompletion(),

      EditorState.phrases.of(chinesePhrases),

    ],

    smartIndent: true,

  };

  editorState.value = EditorState.create(state);

};

const initEditor = () => {

  editorView.value = new EditorView({

    state: editorState.value,

    parent: editorRef.value,

  });



  const { state, dispatch } = editorView.value;

  const { selection } = state;



  if (selection && !selection.empty) {

    // 清空选择区域

    dispatch({ selection: { anchor: selection.main.head } });

  }

};

const insertValue = value => {

  const { state, dispatch } = editorView.value;

  const { selection } = state;



  // 如果存在选择区域,则替换选区内容

  if (selection && !selection.main.empty) {

    const { from, to } = selection.main;

    dispatch({

      changes: { from, to, insert: value },

      selection: { anchor: from + value.length },

    });



    return;

  }



  // 如果不存在选择区域,则在末尾

  const docLength = state.doc.length;

  dispatch({

    changes: { from: docLength, insert: value },

    selection: { anchor: docLength + value.length },

  });

};

const getValue = () => {

  return editorView.value.state.doc.toString();

};

const mapDoc = doc => {

  const lines = [];

  const maxLength = 2000;



  for (let i = 0; i < doc.length; i += maxLength) {

    lines.push(doc.substr(i, maxLength));

  }



  return lines.join('\n');

};



onMounted(() => {

  initState(props.doc);

  initEditor();

});



defineExpose({

  getValue,

  insertValue,

});

</script>



<style lang="scss" scoped>

:deep(.cm-focused) {

  outline: none !important;

}

:deep(.cm-editor) {

  height: 100% !important;

  overflow: auto;

  font-size: 15px;



  .cm-gutters {

    background-color: #fff !important;

    border-right: 0px !important;

  }

}

</style>

以上是组件  复制到项目中安装依赖

下边是使用的方法

<template>

  <el-drawer

    modal-class="d_drawer"

    :model-value="drawer"

    v-if="drawer"

    size="600px"

    @close="handleClose"

    direction="rtl"

  >

    <template #header>

      <h4 class="text-[#000] font-bold">{{ title }}</h4>

    </template>



    <template #default>

      <div class="">

        <div class="flex items-center justify-between mx-[16px] mt-[16px]">

          <div class="flex items-center text-[14px] text-[#606266] my_button">

            <el-popover placement="bottom" :offset="5" trigger="hover">

              <template #reference>

                <el-button v-noBtnFocus>过滤器</el-button>

              </template>



              <div v-for="(dataIetm, index) in filtersOptions" :key="index">

                <div

                  :class="index == 0 ? '' : 'mt-[8px]'"

                  class="select-none cursor-pointer hover:opacity-[0.8] overflow-hidden text-ellipsis whitespace-nowrap"

                  @click="handleInsertValue(dataIetm.value)"

                  :title="dataIetm.label"

                >

                  {{ dataIetm.label }}

                </div>

              </div>

            </el-popover>



            <el-popover placement="bottom" :offset="5" trigger="hover">

              <template #reference>

                <el-button v-noBtnFocus>数学</el-button>

              </template>



              <div v-for="(dataIetm, index) in mathOptions" :key="index">

                <div

                  :class="index == 0 ? '' : 'mt-[8px]'"

                  class="select-none cursor-pointer hover:opacity-[0.8] overflow-hidden text-ellipsis whitespace-nowrap"

                  @click="handleInsertValue(dataIetm.value)"

                  :title="dataIetm.label"

                >

                  {{ dataIetm.label }}

                </div>

              </div>

            </el-popover>



            <el-popover placement="bottom" :offset="5" trigger="hover">

              <template #reference>

                <el-button v-noBtnFocus>Python</el-button>

              </template>



              <div v-for="(dataIetm, index) in pythonOptions" :key="index">

                <div

                  :class="index == 0 ? '' : 'mt-[8px]'"

                  class="select-none cursor-pointer hover:opacity-[0.8] overflow-hidden text-ellipsis whitespace-nowrap"

                  @click="handleInsertValue(dataIetm.value)"

                  :title="dataIetm.label"

                >

                  {{ dataIetm.label }}

                </div>

              </div>

            </el-popover>

          </div>

          <div>

            <el-popover placement="bottom" :offset="5" trigger="hover">

              <template #reference>

                <el-button :icon="Plus" link v-noBtnFocus> 添加数据 </el-button>

              </template>



              <div v-for="(dataIetm, index) in addDataOptions" :key="index">

                <div

                  :class="index == 0 ? '' : 'mt-[8px]'"

                  class="select-none cursor-pointer hover:opacity-[0.8] overflow-hidden text-ellipsis whitespace-nowrap"

                  @click="handleInsertValue(dataIetm.value)"

                  :title="dataIetm.label"

                >

                  {{ dataIetm.label }}

                </div>

              </div>

            </el-popover>

          </div>

        </div>



        <div class="flex-1 mt-[8px] mx-[16px]">

          <CodemirrorVue ref="codemirrorVueRef" :doc="doc" />

        </div>



        <div class="mt-[8px] mx-[16px]">

          <el-button :loading="buttonLoading" v-noBtnFocus @click="handleGetCodemirror">

            <div class="flex items-center">

              <div

                class="i-ic-round-slow-motion-video w-[16px] h-[16px] color-[#2854e3] mr-[4px]"

              ></div>

              <div>运行</div>

            </div>

          </el-button>

        </div>



        <div class="mt-[16px] border-b-[1px] border-t-[1px] border-[#f8f8f8]">

          <div class="font-bold text-[14px] m-[16px] text-[#000]">运行结果</div>

          <div v-loading="buttonLoading" class="m-[16px] bg-[#fafbfc] min-h-[181px]">

            <JsonVue ref="jsonVueRef" />

          </div>

        </div>

      </div>

    </template>



    <template #footer>

      <div style="flex: auto">

        <el-button v-noBtnFocus @click="handleClose">取消</el-button>

        <el-button :loading="drawerLoading" v-noBtnFocus type="primary" @click="confirmClick()">

          确定

        </el-button>

      </div>

    </template>

  </el-drawer>

</template>



<script lang="ts" setup>

import { ref, inject } from 'vue';

import type { Ref } from 'vue';

import { Plus } from '@element-plus/icons-vue';

import { ElMessage } from 'element-plus';

import type { FormInstance, FormRules } from 'element-plus';

import CodemirrorVue from './components/CodemirrorVue/index.vue';

import TopoToolDrawer from '../TopoToolDrawer/index.vue';

import JsonVue from './components/JsonVue/index.vue';

import { useOptions } from './hooks/useOptions';

import { postFlowableInstanceCheckPythonApi } from '@/api/arrangement/index';



const { filtersOptions, mathOptions, pythonOptions } = useOptions();

const jsonVueRef = ref<InstanceType<typeof JsonVue> | null>(null);

const topoToolDrawerRef = inject<Ref<InstanceType<typeof TopoToolDrawer>>>('topoToolDrawerRef');

const codemirrorVueRef = ref<InstanceType<typeof CodemirrorVue> | null>(null);

const drawer = ref(false);

const title = ref('');

const drawerLoading = ref(false);

const doc = ref('');

const actionId = ref('');

const addDataOptions = ref<any[]>([]);

const buttonLoading = ref(false);



const confirmClick = async () => {

  doc.value = codemirrorVueRef.value?.getValue();

  topoToolDrawerRef.value.setSettingParameterValue(actionId.value, doc.value);

  handleClose();

};

const handleClose = () => {

  drawer.value = false;

  title.value = '';

  doc.value = '';

  actionId.value = '';

  jsonVueRef?.value.setJsonData('');

};

const handleGetCodemirror = async () => {

  try {

    buttonLoading.value = true;

    let req = codemirrorVueRef.value?.getValue();



    if (!req) return ElMessage.warning('请输入代码');



    if (req.length > 20000) return ElMessage.warning('代码长度不能超过20000');



    let res = await postFlowableInstanceCheckPythonApi({ script: req });



    jsonVueRef?.value.setJsonData(res.data.msg);

    if (!res.data.msg) return ElMessage.warning('运行结果为空');

  } catch (error) {

    console.log('🚀 🤷 ~ file: index.vue:164 ~ handleGetCodemirror ~ error 👉', error);

  } finally {

    buttonLoading.value = false;

  }

};

const handleInsertValue = val => {

  codemirrorVueRef.value?.insertValue(val);

};



defineExpose({

  drawer,

  title,

  doc,

  actionId,

  addDataOptions,

});

</script>

<style lang="scss" scoped>

.my_button {

  .el-button:focus,

  .el-button:hover {

    color: #909399;

    border-color: #f0f0f0;

    background-color: #fff;

  }

}

</style>


 

CodeMirror 是一个为浏览器提供源代码编辑器的 JavaScript 库,支持语法高亮、代码折叠等多种功能。Vue 3 是一个流行的前端框架,其组件化特性和响应式系统使得开发者可以更高效地构建用户界面。 在 Vue 3 集成 CodeMirror,通常可以通过以下步骤进行: 1. 引入 CodeMirror:可以通过 CDN 或者 npm 包管理器来引入 CodeMirror。如果是通过 CDN,可以在 HTML 文件直接通过 `<script>` 标签引入;如果是通过 npm,可以使用 npm 或 yarn 来安装 CodeMirror,然后在 Vue 3 组件使用 `import` 语句来引入。 2. 创建 CodeMirror 实例:在 Vue 3 组件的生命周期钩子创建 CodeMirror 实例。通常在 `mounted` 钩子初始化编辑器,使用 Vue 的 `ref` 来引用 DOM 元素,然后将这个元素作为容器传递给 CodeMirror。 3. 配置 CodeMirror:在创建实例时,可以传入各种配置项来自定义编辑器的行为和外观,例如语言模式、主题、键绑定等。 4. 绑定事件和数据:CodeMirror 实例创建后,可以通过 Vue 的响应式系统来绑定数据和事件。这样,编辑器的内容变化可以实时反映到 Vue 组件的数据,反之亦然。 下面是一个简单的示例代码: ```html <template> <div ref="editorContainer" class="editor"></div> </template> <script> import CodeMirror from 'codemirror'; export default { name: 'CodeMirrorComponent', setup() { let editor; onMounted(() => { const container = ref.value; editor = CodeMirror(container, { lineNumbers: true, mode: "javascript", }); }); watch(() => { // 你可以在这里监听 Vue 数据的变化,并反映到编辑器 }); return { editor, }; } }; </script> <style> .editor { height: 500px; } </style> ``` 在上面的代码,我们创建了一个 Vue 3 组件,使用了 `setup` 函数,并在 `mounted` 生命周期钩子初始化了 CodeMirror 编辑器。我们还定义了一个名为 `editor` 的响应式引用,用于在组件的其他部分与 CodeMirror 实例交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值