简述

昨天试着用纯html制作一个echarts在线编辑器,今天试着用vue的方式实现。
用html制作简单的echarts编辑器

思路

效果图

在这里插入图片描述

页面分布

主页面中放有编辑器子组件和图表页面子组件

main.js的配置

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import * as echarts from 'echarts'
import ecStat from 'echarts-stat';
import ace from 'ace-builds'
import jq from 'jquery'
import componentsInstall from '@/components/install'
// 引入解析Excel表的组件
import vueXlsxTable from 'vue-xlsx-table'
// 引入全局css
import './assets/css/global.less'
// 将全局的echarts对象挂载到vue的原型对象上
Vue.prototype.$echarts = echarts
Vue.prototype.$ecStat = ecStat
// 创建bus总线
Vue.prototype.bus = new Vue()
window.$ = jq
Vue.config.productionTip = false
Vue.use(vueXlsxTable,{rABS:false})
// 转换时间格式挂载到Date原型对象上
Date.prototype.format = function (fmt) { //author: meizz   
  var o = {
      "M+": this.getMonth() + 1,                 //月份   
      "d+": this.getDate(),                    //日   
      "h+": this.getHours(),                   //小时   
      "m+": this.getMinutes(),                 //分   
      "s+": this.getSeconds(),                 //秒   
      "q+": Math.floor((this.getMonth() + 3) / 3), //季度   
      "S": this.getMilliseconds()             //毫秒   
  };
  if (/(y+)/.test(fmt))
      fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
  for (var k in o)
      if (new RegExp("(" + k + ")").test(fmt))
          fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  return fmt;
}
Vue.use(ace)
Vue.use(componentsInstall)
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

其中ace编辑器的配置

参考这篇文章
Vue.js简单集成ACE代码编辑器

按钮功能

在编辑器组件中需要三个按钮,分别是上传Excel重置数据运行
1.上传Excel
用vue-xlsx-table插件生成该按钮,点击按钮选择excel文件后,能够获取数据表内的数据,这些数据就是用来生成echarts图表的数据源
2.重置数据
用来清除已保存的excel数据,至于为什么要这么做,继续往下看
3.运行
如同echarts官网示例一样,在编辑区中输入配置后点击按钮,生成图表

实现思路

跟用html实现的思路其实是一样的,都是先获取编辑器内的代码,然后用new Function()来实现动态编译

关于new Function()可参考此链接

具体实现

图表初始化

先在图表组件的方法中设置好图表容器,并初始化一张图表,随便从官网拿,最后将图表的配置给保存好

// 初始化图表
    initChart() {
      // 初始化echart实例
      let myChart = this.$echarts.init(this.$refs.chartRef)
      let option
      option = {
      	// 具体配置随便在官网示例中拿一个
       }
      myChart.setOption(option)
      // 保存该实例
      this.chartInstance = myChart
      // 将option保存到chartOption,此时chartOption内容是一个对象
      this.chartOption = option
    },

图表组件传给父组件option配置对象

1.在图表组件的方法中

// 向父组件传递图表option对象
    sendOption() {
      this.$emit('sendOption', this.chartOption)
    },

子组件用 this.$emit触发自定义事件sendOption,向外传递chartOption值
2.在父组件中接收图表组件传来的图表配置值
在图表组件标签中绑定方法名,该方法名就是子组件的自定义事件名称sendOption,该事件绑定一个方法,方法名自定义,这里我的是getOption

<template>
  <div class="screen-container">
    <div class="screen-header">
    // 编辑器组件
      <ace :option="scriptStr"></ace>
    </div>
    <div class="screen-body">
    // 图表组件
      <echartContainer ref="eCRef" @sendOption=getOption></echartContainer>
    </div>
  </div>
</template>

父组件的方法中

getOption(res) {
      // 接收图表组件传来的option对象,将其转换为字符串形式
      res = JSON.stringify(res,null,2)
      // 拼接成要返回给编辑器内容的字符串格式,准备传给编辑器子组件
      this.scriptStr = `option = ${res};`
    },

父组件传给编辑器组件scriptStr字符串

1.在父组件中编辑器组件的标签内绑定一个属性option
scriptStr是要传递给编辑器子组件的内容,值为经过字符串拼接的图表组件的option配置,是一串模板字符串

// 编辑器组件
 <ace :option="scriptStr"></ace>

2.编辑器子组件接收父组件传来的scriptStr字符串
props是一个数组,数组里面的每一个元素是父组件传递过来的属性名,接收后可以当data中定义的属性一样直接使用

props:['option'],

编辑器组件编辑区显示option配置内容

1.监听并保存父组件传来的option配置字符串

watch:{
    // 监听父组件传来的值
    option:function(newVal,oldVal){
      // 将新值,也就是option编辑器内容字符串,赋值给scriptStr
      this.scriptStr = newVal
      // 设置编辑器内容
      this.setAceEditorValue()
    }
  },

2.设置编辑区内容为scriptStr字符串
编辑器组件方法中

  // 设置编辑器内容
    setAceEditorValue() {
      this.aceEditor.setValue(this.scriptStr, 1)
    },

到这里就能实现打开页面时像echarts官网那样,根据初始化的图表,编辑器中显示其对应的option配置代码,下面要开始进入重点了

编辑器点击运行按钮向图表组件传递编辑器内容字符串

1.用bus总线向图表兄弟组件传递新的图表配置信息

// 点击运行按钮向图表组件传输新的图表配置
    sendChartOption() {
      // 点击运行按钮获取编辑器内容,保存到scriptStr
      this.scriptStr = this.aceEditor.getValue()
      let script = this.scriptStr
      // 将新的 编辑器内容 传给图表组件
      this.bus.$emit('sendScript',script)
    },

2.图表组件接收新的图表配置信息
在mounted(){}里面

// 接收编辑器组件传来的新的图表配置信息代码
    this.bus.$on('sendScript', res => {
    // 保存给图表组件的chartOption
      this.chartOption = res
      // 传入新的配置代码,重新渲染图表
      this.changeChart(this.chartOption)
      }
    })

根据新的图表配置重新渲染图表

图表组件方法里

changeChart(script) {
      // 用echarts时,如果不存在DOM,就会报错,处理方法先检查是否DOM存在:
      if (this.$refs.chartRef == null) {
        return
      }
      // 用echarts时,如果存在DOM,就会报存在警告,处理方法删除DOM:
      this.$echarts.dispose(this.$refs.chartRef)
      try {
        let func = new Function(
          'echarts',
          'ecStat',
          `const ROOT_PATH = 'https://cdn.jsdelivr.net/gh/apache/echarts-website@asf-site/examples';var option;let myChart = echarts.init(this.$refs.chartRef);` +
            script +
            `myChart.clear();option && myChart.setOption(option);`
        ).bind(this)
        func(this.$echarts, this.$ecStat)
      } catch (e) {
      // 打印错误信息
        console.log(e)
      }
    },

这里要为new Function重新绑定this指向vue实例对象,否则里面this指向的是window全局对象,会报错。
每次重新渲染图表时记得用clear()清除前一个图表,不然新图表会和前图表糅杂到一块。

到这里其实已经实现了echarts官网的那种在线编辑图表示例了,下面是拓展

拓展

使用导入excel数据的方法是为了方便在数据确定的情况下,可以根据需求在编辑器中实时修改图表的样式进行预览,避免了当需求临时变化时需要多次回到项目中修改代码这种重复性动作。

实现的效果

当我输入图表配置信息时候并没有像官网那样直接在里面定义data数据源,而是从excel表里获取数据源。
若没在编辑器中配置好data,那么运行的时候会报data未定义的错误。

思路

获取excel里的数据,点击运行按钮后连同编辑区的配置代码一并传给图表组件,再在那边做字符串拼接。

上传excel并获取表格数据

这里用的是vue-xlsx-table插件
在编辑器组件中

<vue-xlsx-table @on-select-file="handleSelectedFile">
上传excel
</vue-xlsx-table>
// 点击上传按钮获取excel表数据
    handleSelectedFile(convertedData) {
    // 将数据保存到data中
      this.data = convertedData.body
    },

修改sendChartOption方法,将新的编辑器配置内容和表格数据一同传给图表组件

// 点击运行按钮向图表组件传输新的图表配置
    sendChartOption() {
      // 点击运行按钮获取编辑器内容
      this.scriptStr = this.aceEditor.getValue()
      let script = this.scriptStr
      // 将新的 编辑器内容 以及 excel表数据 传给图表组件
      this.bus.$emit('sendScript',[script,this.data])
    },

图表组件接收新数据并生成图表

在mounted里将接收代码修改为

// 接收编辑器组件传来的新的图表配置信息代码
    this.bus.$on('sendScript', res => {
      this.chartOption = res[0]
      if (res[1] === null) {
      // 如果接收到的excel表格数据为空,说明可能已经在编辑器内容中定义好了data数据源,不用再做字符串拼接,那么直接渲染图表
        this.changeChart(this.chartOption)
      } else {
      // 如果表格数据不为空,则说明需要做字符串拼接,否则运行后会报data未定义,之后再渲染图表
        this.chartOption = `let data = ` + JSON.stringify(res[1]) + ';' + res[0]
        this.changeChart(this.chartOption)
      }
    })

重置数据

当我们导入excel数据后,再想往编辑器中写入echarts官网的那些配置示例,这时候会显示data已经被声明,无法重复声明,因为官网的自带数据源和我们的excel数据源重复了,因此需要清除excel的数据源。
在编辑器组件中点击重置数据按钮,触发事件清除data,再点击运行时就不会有数据源冲突的情况了。

// 点击重置数据按钮,清除excel表数据
    resetChartData(){
      this.data = null
    },

效果

在这里插入图片描述
在这里插入图片描述

结尾

本人尚在学习阶段,可能这是制作在线编辑器的一种土办法,如有更好的方法还望分享。

Logo

前往低代码交流专区

更多推荐