|
|
@@ -0,0 +1,154 @@
|
|
|
+<script lang="ts" setup>
|
|
|
+import type { PropType } from 'vue';
|
|
|
+
|
|
|
+import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
|
+
|
|
|
+import { alert } from '@vben/common-ui';
|
|
|
+
|
|
|
+import { useVModel } from '@vueuse/core';
|
|
|
+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';
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: 'BcMonaco',
|
|
|
+});
|
|
|
+const props = defineProps({
|
|
|
+ value: {
|
|
|
+ type: String as PropType<string>,
|
|
|
+ default: () => {
|
|
|
+ return '';
|
|
|
+ },
|
|
|
+ },
|
|
|
+ language: {
|
|
|
+ type: String as PropType<string>,
|
|
|
+ default: 'typescript',
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+const emit = defineEmits(['update:value']);
|
|
|
+
|
|
|
+const modelValue = useVModel(props, 'value', emit, {
|
|
|
+ defaultValue: props.value,
|
|
|
+ passive: true,
|
|
|
+});
|
|
|
+
|
|
|
+// 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();
|
|
|
+ },
|
|
|
+};
|
|
|
+const monacoEditorRef = ref();
|
|
|
+// 初始化monacoEditor对象
|
|
|
+let monacoEditor: any = null;
|
|
|
+
|
|
|
+const initMonacoEditor = () => {
|
|
|
+ monacoEditor = monaco.editor.create(monacoEditorRef.value, {
|
|
|
+ theme: 'vs-dark', // 主题 vs vs-dark hc-black
|
|
|
+ value: '', // 默认显示的值
|
|
|
+ language: 'javascript',
|
|
|
+ formatOnPaste: true,
|
|
|
+ wordWrap: 'on', // 自动换行,注意大小写
|
|
|
+ wrappingIndent: 'indent',
|
|
|
+ folding: true, // 是否折叠
|
|
|
+ foldingHighlight: true, // 折叠等高线
|
|
|
+ foldingStrategy: 'indentation', // 折叠方式 auto | indentation
|
|
|
+ showFoldingControls: 'always', // 是否一直显示折叠 always | mouSEOver
|
|
|
+ disableLayerHinting: true, // 等宽优化
|
|
|
+ emptySelectionClipboard: false, // 空选择剪切板
|
|
|
+ selectionClipboard: false, // 选择剪切板
|
|
|
+ automaticLayout: true, // 自动布局
|
|
|
+ codeLens: false, // 代码镜头
|
|
|
+ scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
|
|
|
+ colorDecorators: true, // 颜色装饰器
|
|
|
+ accessibilitySupport: 'auto', // 辅助功能支持 "auto" | "off" | "on"
|
|
|
+ lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function
|
|
|
+ lineNumbersMinChars: 5, // 行号最小字符 number
|
|
|
+ // enableSplitViewResizing: false,
|
|
|
+ readOnly: false, // 是否只读 取值 true | false
|
|
|
+ });
|
|
|
+
|
|
|
+ monacoEditor.onDidChangeModelContent(() => {
|
|
|
+ const scriptCode = monacoEditor?.getValue();
|
|
|
+ if (['javascript', 'json'].includes(props.language)) {
|
|
|
+ try {
|
|
|
+ modelValue.value = JSON.parse(scriptCode);
|
|
|
+ } catch {
|
|
|
+ alert({ content: '数据格式错误', icon: 'error' });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ modelValue.value = scriptCode;
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ initMonacoEditor();
|
|
|
+ nextTick(() => {
|
|
|
+ if (props.language) {
|
|
|
+ setLanguage(props.language);
|
|
|
+ }
|
|
|
+ if (props.value) {
|
|
|
+ setValue(props.value);
|
|
|
+ }
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+const setValue = (value: string) => {
|
|
|
+ let newValue = value;
|
|
|
+ if (['javascript', 'json'].includes(props.language)) {
|
|
|
+ newValue = JSON.stringify(value);
|
|
|
+ }
|
|
|
+ const scriptCode = monacoEditor?.getValue();
|
|
|
+ if (scriptCode !== newValue) {
|
|
|
+ monacoEditor?.setValue(newValue);
|
|
|
+ monacoEditor?.getAction('editor.action.formatDocument').run();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const setLanguage = (language: string) => {
|
|
|
+ monaco.editor.setModelLanguage(monacoEditor.getModel()!, language);
|
|
|
+};
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ monacoEditor.dispose();
|
|
|
+ monacoEditor = null;
|
|
|
+});
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.language,
|
|
|
+ (value) => {
|
|
|
+ setLanguage(value);
|
|
|
+ },
|
|
|
+ { deep: true },
|
|
|
+);
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.value,
|
|
|
+ (value) => {
|
|
|
+ if (monacoEditor) {
|
|
|
+ setValue(value);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true },
|
|
|
+);
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <div ref="monacoEditorRef" class="h-full w-full"></div>
|
|
|
+</template>
|