ny
22 小时以前 282fbc6488f4e8ceb5fda759f963ee88fbf7b999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<!-- eslint-disable new-cap -->
<script lang="ts">
import { defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue';
 
import { usePreferences } from '@vben/preferences';
 
import * as monaco from 'monaco-editor';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
 
import { defaultOptions, editorProps } from './monacoEditorType';
 
export default defineComponent({
  emits: ['update:modelValue', 'change', 'editorMounted'],
  name: 'MonacoEditor',
  props: editorProps,
  setup(props, { emit, expose }) {
    const { isDark } = usePreferences();
    // eslint-disable-next-line no-restricted-globals
    self.MonacoEnvironment = {
      getWorker(_: string, label: string) {
        if (label === 'json') {
          return new jsonWorker();
        }
        if (['css', 'less', 'scss'].includes(label)) {
          return new cssWorker();
        }
        if (['handlebars', 'html', 'razor'].includes(label)) {
          return new htmlWorker();
        }
        if (['javascript', 'typescript'].includes(label)) {
          return new tsWorker();
        }
        return new EditorWorker();
      },
    };
    let editor: monaco.editor.IStandaloneCodeEditor;
    const codeEditBox = ref();
 
    const init = () => {
      monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
        noSemanticValidation: true,
        noSyntaxValidation: false,
      });
      monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
        allowNonTsExtensions: true,
        target: monaco.languages.typescript.ScriptTarget.ES2020,
      });
 
      editor = monaco.editor.create(codeEditBox.value, {
        language: props.language,
        theme: isDark.value ? 'vs-dark' : 'vs',
        value: props.modelValue,
        ...defaultOptions,
        ...props.options,
      });
 
      // 监听值的变化
      editor.onDidChangeModelContent(() => {
        const value = editor.getValue(); // 给父组件实时返回最新文本
        emit('update:modelValue', value);
        emit('change', value);
      });
 
      emit('editorMounted', editor);
    };
    watch(
      () => props.modelValue,
      (newValue) => {
        if (editor) {
          const value = editor.getValue();
          if (newValue !== value) {
            editor.setValue(newValue);
          }
        }
      },
    );
 
    watch(
      () => props.options,
      (newValue) => {
        editor.updateOptions({ ...defaultOptions, ...newValue });
      },
      { deep: true },
    );
 
    watch(
      () => props.language,
      (newValue) => {
        monaco.editor.setModelLanguage(editor.getModel()!, newValue);
      },
    );
    watch(
      () => isDark.value,
      async () => {
        monaco.editor.setTheme(isDark.value ? 'vs-dark' : 'vs');
      },
      { immediate: true },
    );
 
    function insert(text = '') {
      const position = editor.getPosition();
      editor.executeEdits('', [
        {
          range: {
            endColumn: position?.column as number,
            endLineNumber: position?.lineNumber as number,
            startColumn: position?.column as number,
            startLineNumber: position?.lineNumber as number,
          },
          text,
        },
      ]);
    }
    expose({ insert });
 
    onBeforeUnmount(() => {
      editor.dispose();
    });
 
    onMounted(() => {
      init();
    });
 
    return { codeEditBox };
  },
});
</script>
 
<template>
  <div ref="codeEditBox" class="codeEditBox"></div>
</template>
 
<style lang="scss" scoped>
.codeEditBox {
  width: v-bind(width);
  height: v-bind(height);
}
</style>