动态生成echart图(拖拽生成echart)并导出json

这只是一个简单的echart生成功能进一步的效果优化暂无,大家也可以借鉴 form-generator 的方案进行改造(这是开源的动态生成页面项目)

组合页面代码

<template>
  <div class="hello">
    <div style="height: 100%;flex: 1;overflow-y: auto">
      <vuedraggable :group="{name:'menu',pull:'clone',put:false}" :sort="false" :list="echartArr" @end="addOne" :clone="cloneArr">
          <div v-for="(item ,index) in echartArr" :key="index">
            {{item.name}}
          </div>
      </vuedraggable>
    </div>
    <div  class="right">
      <el-button @click="sumbitEchart">保存Echart</el-button>
      <el-button @click="seeEchart=true">预览</el-button>
      <el-button @click="exportJSON">导出JSON</el-button>
      <vuedraggable  group='menu' :animation="340" :list="echartShow"  style="height: 90%;overflow-y: auto;border: 1px solid #47b347;width: 100%">
          <el-col v-for="(item ,index) in echartShow" :key="index" :span="item.style.span"
                  :style="{marginTop:item.style.marginTop+'px',marginBottom:item.style.marginBottom,marginLeft:item.style.marginLeft+'px',marginRight:item.style.marginRight+'px'}"
          >
            <div @click="clickEchart(item)">
              <echart :options="item.option" :span="item.style.span"  :height="item.height" :id="item.id" />
            </div>
          </el-col>
      </vuedraggable>
    </div>
    <div style="flex: 1;padding-right: 15px;height: 100%;overflow-y: auto;padding-left: 15px">
      <el-tabs v-model="activeName">
        <el-tab-pane label="组件" name="zujian">
          <el-form v-if="jsonEchart" label-position="top">
            <el-form-item label="ID">
              <el-input  v-model="currentEchart.id"  style="width: 80%" :disabled="false"/>
            </el-form-item>
            <el-form-item label="图表名称">
              <el-input  v-model="currentEchart.option.title.text" @blur="handleName" style="width: 80%"/>
            </el-form-item>
            <el-form-item label="高度">
              <el-input-number v-model="currentEchart.height" />
            </el-form-item>
            <el-form-item label="栅格宽度">
              <el-input-number v-model="currentEchart.style.span" />
            </el-form-item>
            <el-form-item label="外边距">
              <el-row>上<el-input-number v-model="currentEchart.style.marginTop" /></el-row>
              <el-row>下<el-input-number v-model="currentEchart.style.marginBottom" /></el-row>
              <el-row>左<el-input-number v-model="currentEchart.style.marginLeft" /></el-row>
              <el-row>右<el-input-number v-model="currentEchart.style.marginRight" /></el-row>
            </el-form-item>
          </el-form>
          <el-empty v-if="!jsonEchart" description="暂无数据"></el-empty>
        </el-tab-pane>
        <el-tab-pane  label="Echart" name="echart">
          <el-button  @click="drawer=true">更改数据</el-button>
        </el-tab-pane>
      </el-tabs>
    </div>
    <el-drawer
        title="Echart图表数据"
        :visible.sync="drawer"
        :size="800"
        >
                <b-code-editor
                    v-if="jsonEchart"
                    v-model="jsonEchart"
                    :auto-format="true"
                    :smart-indent="true"
                    theme="dracula"
                    :indent-unit="2"
                    :line-wrap="true"
                    :height="'800px'"
                    ref="editor"
                />
                <el-button type="primary" @click="onSubumit">保存</el-button>
    </el-drawer>
    <el-dialog title="预览" :visible.sync="seeEchart" width="60%">
      <el-row>
        <el-col v-for="(item ,index) in echartShow" :key="index" :span="item.style.span"
                :style="{marginTop:item.style.marginTop+'px',marginBottom:item.style.marginBottom,marginLeft:item.style.marginLeft+'px',marginRight:item.style.marginRight+'px'}"
        >
          <echart v-if="seeEchart" :options="item.option" :span="item.style.span"  :height="item.height" :id="item.id" />
        </el-col>
      </el-row>
    </el-dialog>
  </div>
</template>

<script>
import vuedraggable from 'vuedraggable';
import FileSaver from 'file-saver'
import echart from "./echart";
export default {
  name: 'HelloWorld',
  components: { vuedraggable ,echart},
  data(){
    return{
      activeName:'zujian',
      echartArr:[
        {
          name:'柱状图',
          id:'zzt',
          option:{
            title: {
              text: '柱状图',
              left: 'center'
            },
            xAxis: {
              type: 'category',
              data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
            },
            yAxis: {
              type: 'value'
            },
            series: [
              {
                data: [120, 200, 150, 80, 70, 110, 130],
                type: 'bar',
                showBackground: true,
                backgroundStyle: {
                  color: 'rgba(180, 180, 180, 0.2)'
                }
              }
            ]
          },
          style:{
            marginTop:'0',
            marginBottom:'0',
            marginLeft:'0',
            marginRight:'0',
            span:24
          },
          height:300
        },
        {
          name:'折线图',
          id:'zxt',
          style:{
            marginTop:'0',
            marginBottom:'0',
            marginLeft:'0',
            marginRight:'0',
            span:24
          },
          option: {
            title: {
              text: '折线图',
              left: 'center'
            },
            xAxis: {
              type: 'category',
              data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
            },
            yAxis: {
              type: 'value'
            },
            series: [
              {
                data: [150, 230, 224, 218, 135, 147, 260],
                type: 'line'
              }
            ]
          },
          height:300
        },
        {
          name:'饼图',
          id:'bt',
          style:{
            marginTop:'0',
            marginBottom:'0',
            marginLeft:'0',
            marginRight:'0',
            span:24
          },
          option: {
            title: {
              text: '饼图',
              subtext: 'Fake Data',
              left: 'center'
            },
            tooltip: {
              trigger: 'item'
            },
            legend: {
              orient: 'vertical',
              left: 'left'
            },
            series: [
              {
                name: 'Access From',
                type: 'pie',
                radius: '50%',
                data: [
                  { value: 1048, name: 'Search Engine' },
                  { value: 735, name: 'Direct' },
                  { value: 580, name: 'Email' },
                  { value: 484, name: 'Union Ads' },
                  { value: 300, name: 'Video Ads' }
                ],
                emphasis: {
                  itemStyle: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: 'rgba(0, 0, 0, 0.5)'
                  }
                }
              }
            ]
          },
          height:300
        },
        {
          name:'折柱混合图',
          id:'zzhh',
          style: {
            marginTop:'0',
            marginBottom:'0',
            marginLeft:'0',
            marginRight:'0',
            span:24
          },
          option : {
            title: {
              text: '折柱混合图',
              left: 'center'
            },
            tooltip: {
              trigger: 'axis',
              axisPointer: {
                type: 'cross',
                crossStyle: {
                  color: '#999'
                }
              }
            },
            legend: {
              data: ['Evaporation', 'Precipitation', 'Temperature'],
              left: 'left'
            },
            xAxis: [
              {
                type: 'category',
                data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
                axisPointer: {
                  type: 'shadow'
                }
              }
            ],
            yAxis: [
              {
                type: 'value',
                name: 'Precipitation',
                min: 0,
                max: 250,
                interval: 50,
                axisLabel: {
                  formatter: '{value} ml'
                }
              },
              {
                type: 'value',
                name: 'Temperature',
                min: 0,
                max: 25,
                interval: 5,
                axisLabel: {
                  formatter: '{value} °C'
                }
              }
            ],
            series: [
              {
                name: 'Evaporation',
                type: 'bar',
                tooltip: {
                  valueFormatter: function (value) {
                    return value + ' ml';
                  }
                },
                data: [
                  2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3
                ]
              },
              {
                name: 'Precipitation',
                type: 'bar',
                tooltip: {
                  valueFormatter: function (value) {
                    return value + ' ml';
                  }
                },
                data: [
                  2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3
                ]
              },
              {
                name: 'Temperature',
                type: 'line',
                yAxisIndex: 1,
                tooltip: {
                  valueFormatter: function (value) {
                    return value + ' °C';
                  }
                },
                data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
              }
            ]
          },
          height: 300
        }
      ],
      echartShow:[],
      currentEchart:'',
      jsonEchart:'',
      oneId:'',
      drawer:false,
      seeEchart:false
    }
  },
  mounted() {
// vuedraggle 拖拽在火狐浏览器的阻断
    document.body.ondrop = function (event) { event.preventDefault(); event.stopPropagation();}
  },
  methods:{
    cloneArr(e){
      let arr=JSON.parse(JSON.stringify(e))
// 动态生成随机id
      arr.id=Math.random().toString(36).substr(2)
      this.oneId=JSON.parse(JSON.stringify(arr.id))

      this.jsonEchart=''
      this.echartShow.push(arr)
      this.currentEchart=arr
      this.$nextTick(()=>{
        this.jsonEchart=JSON.stringify(arr.option)
      })
    },
    clickEchart(item){
      this.jsonEchart=''
      this.currentEchart=item
      this.oneId=JSON.parse(JSON.stringify(item.id))
      this.activeName='zujian'
      this.$nextTick(()=>{
        this.jsonEchart=JSON.stringify(item.option)
      })
    },
    // 检测json格式
    isJSON(str) {
      if (typeof str == 'string') {
        try {
          var obj=JSON.parse(str);
          if(typeof obj == 'object' && obj ){
            return true;
          }else{
            return false;
          }

        } catch(e) {
          return false;
        }
      }else if (typeof str == 'object'  && str) {
        return true;
      }
    },
    onSubumit(){
      if (!this.isJSON(this.jsonEchart)){
        this.$message.error(`json格式错误`)
        return false
      }
      this.echartShow.map((item)=>{
        if(item.id===this.oneId){
          item.option=JSON.parse(this.jsonEchart)
        }
      })
      this.$message.success('json格式正确')
    },
    // 更改名称
    handleName(){
      this.jsonEchart=''
      this.$nextTick(()=>{
        this.jsonEchart=JSON.stringify(this.currentEchart.option)
        console.log(this.jsonEchart)
      })
    },
    // 保存echarts图
    sumbitEchart(){
console.log(this.echartShow)
    },
    // 导出JSON
    exportJSON(){
      // const
      const data=JSON.stringify(this.echartShow,null,2)
      const name = 'Echart' + new Date().getTime() + '.json'
      console.log(new Date("month dd,yyyy hh:mm:ss"),1111)
      const blob = new Blob([data], {type: ''})
      FileSaver.saveAs(blob, name)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style  scoped >
.hello{
  width: 100%;
  height: 100%;
  padding: 0 10px;
  display: flex;
}
.right{
  flex: 4;
  height: 100%;
  /*background: navy;*/
  /*overflow-y: auto;*/
}
.right >div{
  /*min-height: 200px;*/
  width: 100%;
}
.CodeMirror {
  font-family: monospace;
  height: 800px !important;
  color: black;
  direction: ltr;
}
</style>

echart组件

<template>
  <div class="chart-zone" :style="{ height: height+'px' }">
    <div ref="echartdiv" :style="{ height: height+'px', width: width }"></div>
  </div>
</template>

<script>
import * as echarts from "echarts";
// let chart = null // chart不能在此定义,不然同一个页面有多个chart时,可能会导致数据渲染错乱
export default {
  name: "echart",
  props: {
    className: {
      type: String,
      default: "chart",
    },
    span:{
      type:Number
    },
    id: {
      type: String,
      default: "chart",
    },
    width: {
      type: String,
      default: "100%",
    },
    height: {
      type: Number,
    },
    options: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      // chart: null, // chart不能是响应式的,不然tooltip中的trigger为axis时会没有效果
    };
  },
  watch: {
    options: {
      handler(options) {
        // 设置true清空echart缓存
        this.chart.setOption(options, true);
      },
      deep: true,
    },
    height(val){
       this.chart.dispose()
      this.$nextTick(()=>{
        this.initChart();
      })
    },
    span(val){
      this.chart.dispose()
      this.$nextTick(()=>{
        this.initChart();
      })
    }
  },
  mounted() {
    this.initChart();
  },
  beforeUnmount() {
    this.chart = null;
  },
  methods: {
    initChart() {
      this.chart = null;
      // 初始化echart
        this.chart = echarts.init(this.$refs.echartdiv);
        this.chart.setOption(this.options, true);
    },
  },
};
</script>

<style scoped>
.chart-zone {
  position: relative;
  width: 100%;
}
</style>

效果


导出json文件内容

在线更改echart内容

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值