一、简介
本文主要介绍如何开发一个同echarts官网里面在线echarts编辑工具,通过页面中的代码编辑器修改echarts的option属性来实时渲染echarts的展示效果。其中主要使用到了【monaco-editor】这个组件。
下图为完成的效果图:
二、如何实现
-
引入monaco-editor包
npm install monaco-editor npm install monaco-editor-webpack-plugin
-
编辑器组件封装
通过引入monaco-editor组件,然后进行指定dom节点以及配置进行页面渲染,该组件通过v-model绑定编辑器的内容。
monaco-editor官方文档:https://microsoft.github.io/monaco-editor/api/modules/monaco.html
<template> <div id="jsMonacoEditor" ref="main" /> </template> <script> import * as monaco from 'monaco-editor' /* * @Description: monacoEditor组件封装 * @Author: linmt * @Date: @Date: 2022-09-11 08:45:55 */ export default { name: 'JsMonacoEditor', components: { }, model: { // 使用v-model绑定编辑器的内容 prop: 'value', event: 'change' }, props: { value: { type: String, default: '' } }, data() { return { monacoEditor: null } }, computed: { }, watch: { value() { this.init() } }, created() { }, mounted() { this.init() }, methods: { /** * 初始化 */ init() { if (this.monacoEditor) { return } this.monacoEditor = monaco.editor.create(this.$refs.main, { theme: 'vs', // 主题 value: this.value, // 默认显示的值 language: 'javascript', 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: 'off', // 辅助功能支持 "auto" | "off" | "on" lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function lineNumbersMinChars: 5, // 行号最小字符 number enableSplitViewResizing: false, readOnly: false, // 是否只读 取值 true | false minimap: { enabled: true // 是否启用预览图 } // 预览图设置 }) const self = this // _.debounce为lodashjs工具包的方法,见https://www.lodashjs.com/ // 主要用来防抖动 const changeValueFun = _.debounce(function() { self.changeValue() }, 500) // 监听编辑器内容改变事件 this.monacoEditor.onDidChangeModelContent((e) => { changeValueFun() }) }, changeValue() { this.$emit('change', this.monacoEditor.getValue()) } } } </script> <style lang='scss' scoped> #jsMonacoEditor{ height: 100%; width: 100%; } </style>
-
将代码编辑器和echarts渲染结合
实现逻辑:通过监听编辑器的组件的change事件来刷新echarts图,用编辑器里面的内容使用new Function(code)方式实现js动态的代码执行并获取返回值,也就是echarts的option属性,然后通过option的值进行echarts渲染,再通过通过定时的方式进行echarts的大小重绘。
<template> <div id="codeEditor"> <div class="code-box"> <JsMonacoEditor v-model="code" @change="changeCode" /> </div> <div class="view-box"> <el-button type="primary" size="small" @click="refreshChart">运行/刷新</el-button> <div ref="chart" class="chart" /> </div> </div> </template> <script> import JsMonacoEditor from '@/businessComponents/jsMonacoEditor' let timer = null /* * @Description: 代码编辑器 * @Author: linmt * @Date: @Date: 2022-09-11 09:01:31 */ export default { name: 'CodeEditor', components: { JsMonacoEditor }, props: { echartDemoData: { type: Object, default: null } }, data() { return { code: 'const option = {};', chart: null } }, computed: { }, watch: { }, created() { if (this.echartDemoData) { this.code = this.echartDemoData.code } }, mounted() { this.refreshChart() }, beforeDestroy() { if (timer) window.clearInterval(timer) }, methods: { changeCode() { this.refreshChart() }, refreshChart() { let _option = null try { const funCode = new Function(`option=null;${this.code};return option;`) _option = funCode() } catch (e) { console.error(e) } if (!_option) { this.$message.error('代码存在问题') if (this.chart) { this.chart.clear() } return } if (_option) { if (!this.chart) { this.chart = window.echarts.init(this.$refs.chart) } else { this.chart.clear() } // 通过定时的方式进行echarts的大小重绘 if (timer) window.clearInterval(timer) timer = setInterval(() => { if (this.chart) { this.chart.resize() } }, 200) this.chart.setOption(_option, true) } } } } </script> <style lang='scss' scoped> #codeEditor{ height: 100%; width: 100%; display: flex; .code-box{ width: 50%; height: 100%; } .view-box{ width: 50%; height: 100%; padding-left: 10px; .chart{ margin-top: 20px; height: calc(100% - 80px); width: 100%; } } } </style>