增加CodeEdiotr组件,调整MonocaEditor全局样式

This commit is contained in:
fofolee
2025-01-11 11:44:02 +08:00
parent 9eee3d1b14
commit a954cf0764
3 changed files with 310 additions and 2 deletions

View File

@@ -0,0 +1,228 @@
<template>
<div class="code-editor" :style="{ height: height }">
<div ref="editorContainer" class="editor-container"></div>
</div>
</template>
<script>
import * as monaco from "monaco-editor";
import { toRaw } from "vue";
export default {
name: "CodeEditor",
props: {
// v-model 绑定值
modelValue: {
type: String,
default: "",
},
// 编程语言
language: {
type: String,
default: "plaintext",
},
// 编辑器高度
height: {
type: String,
default: "200px",
},
// 编辑器主题
theme: {
type: String,
default: "vs",
},
// 编辑器选项
options: {
type: Object,
default: () => ({}),
},
},
emits: ["update:modelValue", "change"],
data() {
return {
editor: null,
value: null,
resizeTimeout: null,
defaultOptions: {
value: "",
// 自动布局
automaticLayout: true,
// 折叠策略
foldingStrategy: "indentation",
// 自动关闭括号
autoClosingBrackets: true,
// 制表符大小
tabSize: 2,
// 最小化
minimap: {
enabled: false,
},
// 自动格式化
formatOnType: true,
// 自动格式化
formatOnPaste: true,
// 自动缩进
autoIndent: "full",
// 滚动超出最后一行
scrollBeyondLastLine: false,
// 字体大小
fontSize: 14,
// 行号
lineNumbers: "on",
// 行号最小字符数
lineNumbersMinChars: 3,
// 行号
renderLineNumbers: "on",
// 行装饰宽度
lineDecorationsWidth: 0,
// 圆角
roundedSelection: false,
// 行高亮
renderLineHighlight: "all",
// 仅在聚焦时高亮行
renderLineHighlightOnlyWhenFocus: true,
// 隐藏光标
hideCursorInOverviewRuler: true,
// 隐藏概览边框
overviewRulerBorder: false,
// 隐藏概览线
overviewRulerLanes: 0,
// 滚动条
scrollBars: {
vertical: "visible",
horizontal: "visible",
},
// 只读
readOnly: false,
// 光标样式
cursorStyle: "line",
},
};
},
watch: {
// 监听 v-model 值变化
modelValue: {
immediate: true,
handler(newValue) {
if (this.value !== newValue) {
this.value = newValue;
if (this.editor && this.editor.getValue() !== newValue) {
this.editor.setValue(newValue || "");
}
}
},
},
// 监听语言变化
language: {
immediate: true,
handler(newValue) {
if (this.editor) {
monaco.editor.setModelLanguage(this.editor.getModel(), newValue);
}
},
},
"$q.dark.isActive": {
immediate: true,
handler(newValue) {
monaco.editor.setTheme(newValue ? "vs-dark" : "vs");
},
},
},
mounted() {
this.initEditor();
// 手动监听窗口大小变化解决Monaco自动调整大小时导致ResizeObserver loop limit exceeded错误
window.addEventListener("resize", this.resizeEditor);
},
beforeUnmount() {
this.destroyEditor();
},
methods: {
// 初始化编辑器
initEditor() {
const options = {
...this.defaultOptions,
...this.options,
value: this.value || "",
language: this.language,
theme: this.theme,
};
this.editor = monaco.editor.create(this.$refs.editorContainer, options);
this.listenEditorValue();
// 初始化完成后立即触发一次布局更新
this.$nextTick(() => {
this.resizeEditor();
});
},
// 监听编辑器值变化
listenEditorValue() {
this.rawEditor().focus();
this.rawEditor().onDidChangeModelContent(() => {
this.value = this.getEditorValue();
this.$emit("update:modelValue", this.value);
this.$emit("change", this.value);
});
},
// 处理窗口大小变化
resizeEditor() {
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
}
this.resizeTimeout = setTimeout(() => {
this.rawEditor().layout();
}, 50);
},
// 销毁编辑器
destroyEditor() {
if (this.editor) {
window.removeEventListener("resize", this.resizeEditor);
this.rawEditor().dispose();
this.editor = null;
}
},
// 获取原始编辑器实例
rawEditor() {
return toRaw(this.editor);
},
// 获取编辑器实例
getEditor() {
return this.editor;
},
// 设置编辑器内容
setValue(value) {
if (this.editor) {
this.editor.setValue(value || "");
}
},
// 获取编辑器内容
getValue() {
return this.editor ? this.editor.getValue() : "";
},
// 获取编辑器内容
getEditorValue() {
return this.rawEditor().getValue();
},
// 聚焦编辑器
focus() {
if (this.editor) {
this.editor.focus();
}
},
},
};
</script>
<style scoped>
.code-editor {
width: 100%;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 4px;
overflow: hidden;
}
.editor-container {
width: 100%;
height: 100%;
}
</style>

View File

@@ -59,16 +59,39 @@ export default {
initEditor() {
let monacoEditorPreferences = {
value: "",
// 自动布局
automaticLayout: true,
// 折叠策略
foldingStrategy: "indentation",
// 自动关闭括号
autoClosingBrackets: true,
// 制表符大小
tabSize: 2,
minimap: {
enabled: false,
},
// 自动格式化
formatOnType: true,
formatOnPaste: true,
// 自动缩进
autoIndent: "full",
// 行号
lineNumbersMinChars: 3,
renderLineNumbers: "on",
// 行装饰宽度
lineDecorationsWidth: 0,
// 圆角
roundedSelection: false,
// 行高亮
renderLineHighlight: "all",
// 仅在聚焦时高亮行
renderLineHighlightOnlyWhenFocus: true,
// 隐藏光标
hideCursorInOverviewRuler: true,
// 隐藏概览边框
overviewRulerBorder: false,
// 隐藏概览线
overviewRulerLanes: 0,
// JavaScript 特定的格式化选项
"javascript.format.insertSpaceAfterSemicolonInForStatements": true,
"javascript.format.insertSpaceBeforeAndAfterBinaryOperators": true,

View File

@@ -13,10 +13,63 @@ body {
color: #333;
}
.q-tooltip {
font-size: 11px;
/* Monaco Editor 调整行号栏样式 */
.monaco-editor .margin {
width: 40px !important;
}
.monaco-editor .line-numbers {
text-align: center !important;
width: 40px !important;
padding-right: 5px !important;
left: 0 !important;
}
/* Monaco Editor 当前行高亮样式 */
.monaco-editor .current-line {
border: none !important;
background-color: rgba(0, 0, 0, 0.03);
}
.monaco-editor .line-numbers {
color: rgba(0, 0, 0, 0.5) !important;
}
.body--dark .monaco-editor .line-numbers {
color: rgba(255, 255, 255, 0.5) !important;
}
.monaco-editor .current-line~.line-numbers {
color: var(--q-primary) !important;
}
.body--dark .monaco-editor .current-line {
background-color: rgba(255, 255, 255, 0.05);
}
.monaco-editor .margin-view-overlays .current-line {
background-color: transparent !important;
}
/* Monaco Editor 滚动条样式 */
.monaco-editor .scrollbar {
width: 5px !important;
height: 5px !important;
}
.monaco-editor .scrollbar.vertical .slider {
width: 5px !important;
border-radius: 2px !important;
background: rgba(0, 0, 0, 0.2) !important;
}
.monaco-editor .scrollbar.horizontal .slider {
height: 5px !important;
border-radius: 2px !important;
background: rgba(0, 0, 0, 0.2) !important;
}
/* 标签样式 */
.q-chip {
background: #e3e3e39a;
}
@@ -54,6 +107,10 @@ body {
}
/* 优化 Tooltip 样式 */
.q-tooltip {
font-size: 11px;
}
.q-tooltip {
background: rgba(255, 255, 255, 0.18) !important;
border-radius: 6px;