实现xml,json,sql代码组件格式化高亮:
需要下载的依赖:
<template>
<div class="box">
<div class="top" v-if="flag">
<span class="text">Theme:</span>
<el-select v-model="defaultOptions.theme" placeholder="请选择编辑器主题" class="select" @change="changeTheme">
<el-option v-for="(theme, index) in themes" :key="index" :label="theme.label"
:value="theme.value"></el-option>
</el-select>
<span class="text">Language:</span>
<el-select v-model="defaultOptions.language" placeholder="请选择格式化语言" class="select">
<el-option v-for="(item, index) in languageList" :key="index" :label="item.label" :value="item.value">
</el-option>
</el-select>
<el-button type="primary" plain class="btn" @click="formatCode">格式化</el-button>
<el-button type="primary" plain class="btn" @click="clearSelection">清空</el-button>
</div>
<div :style="{ height, width }" :id="`monacoEditorContainer${index}`" class="container" @mouseleave="handleValue"></div>
</div>
</template>
<script>
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
import 'monaco-editor/esm/vs/editor/contrib/folding/folding.js';
// 代码高亮(将所有支持的语言全部显示)
import 'monaco-editor/esm/vs/basic-languages/monaco.contribution'
import { format } from 'sql-formatter';
import prettyData from 'pretty-data/pretty-data';
// 用于xml语法校验
import xml2js from 'xml2js';
export default {
props: {
flag: { //顶部按钮是否显示
type: Boolean,
default: true
},
options: {
type: Object,
default: () => {
}
},
code: {
type: String,
},
height: {
type: String,
default: '90%'
},
width: {
type: String,
default: '100%'
},
index: {
type: String,
default: "01"
},
},
data() {
return {
defaultOptions: {
value: '', // 编辑器的值
language: 'xml', //语言
folding: true, // 是否折叠
theme: 'vs-dark', // 编辑器主题:vs, hc-black, or vs-dark
autoIndent: true, // 自动缩进
wordWrap: 'on', // 启用自动换行
readOnly: false, // 是否只读
},
languageList: [
{ value: 'sql', label: 'SQL' },
{ value: 'javascript', label: 'JSON' },
{ value: 'xml', label: 'XML' },
],
themes: [
{ value: 'vs', label: 'vs' },
{ value: 'vs-dark', label: 'vs-dark' },
{ value: 'hc-black', label: 'hc-black' }
],
monacoEditor: null
};
},
mounted() {
this.createMonacoEditor();
},
watch: {
options: {
handler() {
this.$nextTick(() => {
this.monacoEditor.updateOptions(this.standaloneEditorConstructionOptions)
})
},
deep: true
},
code: {
handler(newCode) {
this.$nextTick(() => {
// 将新数据显示在monacoEditor上
this.monacoEditor.setValue(newCode);
this.formatCode();
});
},
deep: true,
immediate: true,
}
},
computed: {
standaloneEditorConstructionOptions() {
const options = Object.assign(this.defaultOptions, this.options);
if (options.language.toUpperCase() === "JSON") {
options.language = "javascript";
}
return options;
}
},
methods: {
formatCode() {
const code = this.monacoEditor.getValue();
if (!code.trim() || !this.defaultOptions.language) {
// 值为空或者语言为空,不执行格式化操作
return;
}
let formattedCode;
switch (this.defaultOptions.language) {
case 'sql':
formattedCode = format(code);
break;
case 'javascript':
try {
JSON.parse(code);
formattedCode = prettyData.pd.json(code);
} catch (error) {
this.$message.error(`JSON 解析错误: ${error}`);
return;
}
break;
case 'xml':
try {
// 使用 xml2js 库进行 XML 校验
const parser = new xml2js.Parser();
parser.parseString(code, (error) => {
if (error) {
this.$message.error(`XML 解析错误: ${error}`);
// Message.error(`XML 解析错误: ${error}`);
return;
}
});
formattedCode = prettyData.pd.xml(code);
} catch (error) {
return;
}
break;
default:
return;
}
monaco.editor.setModelLanguage(this.monacoEditor.getModel(), this.defaultOptions.language);
const model = this.monacoEditor.getModel();
const formattedContent = {
range: model.getFullModelRange(),
text: formattedCode,
};
this.monacoEditor.executeEdits('format', [formattedContent]);
},
createMonacoEditor() {
const container = document.getElementById(`monacoEditorContainer${this.index}`);
this.monacoEditor = monaco.editor.create(container, this.standaloneEditorConstructionOptions);
},
clearSelection() {
this.monacoEditor.setValue('');
},
changeTheme() {
monaco.editor.setTheme(this.defaultOptions.theme);
},
handleValue() {
this.formatCode();
this.$emit('getValue', this.monacoEditor.getValue())
// 鼠标失去焦点
document.activeElement.blur();
},
},
};
</script>
<style scoped lang="less">
.box {
width: 100%;
height: 100%;
.top {
margin-bottom: 10px;
.text {
margin-right: 4px;
}
.btn {
margin-right: 20px;
margin-left: 0px;
}
.select {
width: 200px!important;
margin-right: 20px;
}
}
.container {
// width: 100%;
// height: 90%;
// margin-top: 20px;
}
}
</style>
实现效果: