File size: 3,830 Bytes
7fc5208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<template>
  <div ref="editorContainer" class="h-full"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, onBeforeUnmount } from 'vue';
import monaco from '@monaco-editor/loader';

const props = defineProps<{
  value: string,
  originalValue?: string,
  language?: string,
  options?: Record<string, any>,
}>();
const emit = defineEmits(['update:value']);

const editorContainer = ref<HTMLElement>();
let editorInstance: any = null;
let monacoInstance: any = null;
let editorModel: any = null;

// 创建编辑器的函数
const createEditor = () => {
  if (!editorContainer.value || !monacoInstance) return;
  
  // 如果已存在编辑器实例,先销毁
  if (editorInstance) {
    editorInstance.dispose();
    editorInstance = null;
  }

  const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  const commonOptions = {
    language: props.language || 'json',
    minimap: { enabled: !isMobile },
    fontSize: isMobile ? 16 : 14,
    lineNumbers: isMobile ? 'off' as const : 'on' as const,
    scrollBeyondLastLine: false,
    automaticLayout: true,
    ...props.options
  };

  // 每次都创建新模型,确保内容刷新
  if (editorModel) {
    editorModel.dispose();
  }
  editorModel = monacoInstance.editor.createModel(props.value, props.language);

  if (props.originalValue !== undefined) {
    // 创建差异编辑器
    const originalModel = monacoInstance.editor.createModel(props.originalValue, props.language);
    editorInstance = monacoInstance.editor.createDiffEditor(editorContainer.value, {
      ...commonOptions,
      readOnly: false,
      renderSideBySide: true,
      ignoreTrimWhitespace: false,
      renderOverviewRuler: true,
      diffWordWrap: 'on',
    });

    editorInstance.setModel({
      original: originalModel,
      modified: editorModel
    });
    const modifiedEditor = editorInstance.getModifiedEditor();
    modifiedEditor.onDidChangeModelContent(() => {
      emit('update:value', modifiedEditor.getValue());
    });
  } else {
    // 创建普通编辑器
    editorInstance = monacoInstance.editor.create(editorContainer.value, {
      ...commonOptions,
      model: editorModel
    });
    editorInstance.onDidChangeModelContent(() => {
      emit('update:value', editorInstance.getValue());
    });
  }
};

onMounted(() => {
  monaco.init().then(instance => {
    monacoInstance = instance;
    createEditor();
  });
});

// 监听 value 和 originalValue 的变化
watch([() => props.value, () => props.originalValue], ([newVal, newOriginalVal], [oldVal, oldOriginalVal]) => {
  // 判断是否需要重建编辑器
  const modeChanged =
    (oldOriginalVal === undefined && newOriginalVal !== undefined) ||
    (oldOriginalVal !== undefined && newOriginalVal === undefined);
    
  if (modeChanged) {
    // 编辑器模式变化,重新创建
    createEditor();
  } else if (editorInstance) {
    if (props.originalValue !== undefined) {
      // 差异模式下更新内容
      const model = editorInstance.getModel();
      if (model && model.original && model.original.getValue() !== newOriginalVal) {
        model.original.setValue(newOriginalVal || '');
      }
      if (model && model.modified && model.modified.getValue() !== newVal) {
        model.modified.setValue(newVal || '');
      }
    } else {
      // 普通模式下更新内容
      if (editorInstance.getValue() !== newVal) {
        editorInstance.setValue(newVal || '');
      }
    }
  }
}, { deep: true });

onBeforeUnmount(() => {
  if (editorInstance) {
    editorInstance.dispose();
  }
  if (editorModel) {
    editorModel.dispose();
  }
  editorInstance = null;
  editorModel = null;
});
</script>

<style scoped>
:deep(.monaco-diff-editor),
:deep(.monaco-editor) {
  height: 100%;
}
</style>