204 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <section class="AiChartV2">
 | 
						|
    <div ref="chart" class="chart" :style="{minWidth:grid.width,minHeight:grid.height}"/>
 | 
						|
    <slot v-if="$slots.default"/>
 | 
						|
    <render-component v-else-if="ops.render" :render="ops.render" :options="chartOptions" :data="data"/>
 | 
						|
  </section>
 | 
						|
</template>
 | 
						|
 | 
						|
<script>
 | 
						|
const mergeOps = (ori = {}, ext = {}) => {
 | 
						|
  for (const k in ext) {
 | 
						|
    if (typeof ext[k] == 'object') {
 | 
						|
      ori[k] = mergeOps(ori[k], ext[k])
 | 
						|
    } else ori[k] = ext[k]
 | 
						|
  }
 | 
						|
  return ori
 | 
						|
}
 | 
						|
 | 
						|
export default {
 | 
						|
  name: "AiEchartV2",
 | 
						|
  props: {
 | 
						|
    data: {default: () => []},
 | 
						|
    ops: {default: () => ({})},
 | 
						|
    tpl: String,
 | 
						|
    series: Object,
 | 
						|
    theme: {
 | 
						|
      default: '0'
 | 
						|
    }
 | 
						|
  },
 | 
						|
  components: {
 | 
						|
    renderComponent: {
 | 
						|
      functional: true,
 | 
						|
      props: {
 | 
						|
        render: Function,
 | 
						|
        options: {default: () => ({})},
 | 
						|
        data: {default: () => []}
 | 
						|
      },
 | 
						|
      render(h, ins) {
 | 
						|
        let {options = {}, data} = ins.props
 | 
						|
 | 
						|
        return ins.props.render(h, {...options, data})
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      chart: null,
 | 
						|
      timer: null,
 | 
						|
    }
 | 
						|
  },
 | 
						|
  computed: {
 | 
						|
    colors() {
 | 
						|
      if (this.theme === '0') {
 | 
						|
        return ['#00F9FF', '#1890FF', '#B13BFF', '#FC3BFF', '#95FF44', '#ea7ccc']
 | 
						|
      }
 | 
						|
 | 
						|
      return ['#FFBA44', '#EC6666', '#FF3E18', '#C9FF82', '#29D7FA', '#ea7ccc']
 | 
						|
    },
 | 
						|
    chartOptions() {
 | 
						|
      const {daemon = {}} = this.$echartTpls[this.tpl] || {} //js代码进入json编辑器会导致函数丢失,所以在这里重新补齐
 | 
						|
      let {data, ops: options = {}} = this,
 | 
						|
          style = this.series || options.daemon || {},
 | 
						|
          colors = this.theme === '1' ? this.colors : (options.color || this.colors),
 | 
						|
          legend = {textStyle: {color: '#fff', padding: [0, 0, 0, 8], fontSize: 14}, show: false}
 | 
						|
      const series = data?.[0] ? Array(Object.keys(data?.[0]).length - 1).fill(1)
 | 
						|
          .map((e, i) => mergeOps(typeof daemon == 'function' ? daemon(colors[i]) : daemon, style)) : []
 | 
						|
      return {
 | 
						|
        tooltip: {},
 | 
						|
        xAxis: {
 | 
						|
          type: 'category', nameGap: 20, axisTick: false,
 | 
						|
          axisLabel: {
 | 
						|
            color: '#C3C4C4',
 | 
						|
            interval: 0,
 | 
						|
            rotate: this.data.length > 10 ? 40 : 0
 | 
						|
          },
 | 
						|
          axisLine: {
 | 
						|
            lineStyle: {
 | 
						|
              color: this.theme === '1' ? 'rgba(239, 163, 51, 0.8)' : 'rgba(255,255,255,.5)',
 | 
						|
              width: 1
 | 
						|
            }
 | 
						|
          }
 | 
						|
        },
 | 
						|
        // 声明一个 Y 轴,数值轴。
 | 
						|
        yAxis: {
 | 
						|
          nameGap: 23, minInterval: 1,
 | 
						|
          splitLine: {
 | 
						|
            lineStyle: {
 | 
						|
              color: this.theme === '1' ? 'rgba(255, 197, 52, 0.4)' : 'rgba(255, 255, 255, .2)',
 | 
						|
              type: 'dashed'
 | 
						|
            }
 | 
						|
          },
 | 
						|
          axisLabel: {color: '#C3C4C4'},
 | 
						|
          axisLine: {
 | 
						|
            lineStyle: {
 | 
						|
              color: '#C3C4C4'
 | 
						|
            }
 | 
						|
          }
 | 
						|
        },
 | 
						|
        grid: {
 | 
						|
          left: '0%',
 | 
						|
          right: '0%',
 | 
						|
          bottom: '0%',
 | 
						|
          top: '26px',
 | 
						|
          containLabel: true
 | 
						|
        },
 | 
						|
        legend, series, ...options,
 | 
						|
        color: colors
 | 
						|
      }
 | 
						|
    },
 | 
						|
    grid() {
 | 
						|
      let {width, height} = this.chartOptions.grid || {}
 | 
						|
      return {
 | 
						|
        width: width ? (width - this.legend === '1' ? 160 : 0) + 'px' : 'auto',
 | 
						|
        height: height ? height + 'px' : 'auto'
 | 
						|
      }
 | 
						|
    },
 | 
						|
    seriesType: v => [...new Set(v.chart.getOption().series?.map(e => e.type) || [])],
 | 
						|
  },
 | 
						|
  watch: {
 | 
						|
    data: {
 | 
						|
      deep: true, handler(v, old) {
 | 
						|
        let oldDims = Object.keys(old?.[0] || {})?.toString(),
 | 
						|
            current = Object.keys(v?.[0] || {})?.toString()
 | 
						|
        this.getChartData(oldDims != current)
 | 
						|
      }
 | 
						|
    },
 | 
						|
    theme() {
 | 
						|
      this.refresh()
 | 
						|
    },
 | 
						|
  },
 | 
						|
  methods: {
 | 
						|
    getChartData(render) {
 | 
						|
      let data = this.data, option = {}
 | 
						|
      const canUseDataset = !!["line", "bar", "pie", "scatter", "effectScatter", "parallel", "candlestick", "map", "funnel", "custom"].some(e => this.seriesType.includes(e))
 | 
						|
      if (!render) {
 | 
						|
        for (const k in this.chartOptions) {
 | 
						|
          option[k] = this.chartOptions[k]
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (canUseDataset) {
 | 
						|
        option.dataset = {source: data || []}
 | 
						|
      } else {
 | 
						|
        option.series = option.series?.map((e = {}) => {
 | 
						|
          if (e.dataRender) {
 | 
						|
            data = data.map(e.dataRender)
 | 
						|
          }
 | 
						|
          return {...e, data}
 | 
						|
        })
 | 
						|
        console.log(option.series)
 | 
						|
      }
 | 
						|
      this.chart.setOption(option)
 | 
						|
    },
 | 
						|
    initChart() {
 | 
						|
      const {echarts} = window
 | 
						|
      this.chart = echarts.init(this.$refs.chart)
 | 
						|
      this.chart.setOption(this.chartOptions || {})
 | 
						|
    },
 | 
						|
    watchResize() {
 | 
						|
      this.timer && clearInterval(this.timer)
 | 
						|
      this.timer = setInterval(() => {
 | 
						|
        if (this.chart?.getHeight() != this.$refs.chart?.clientHeight ||
 | 
						|
            this.chart?.getWidth() != this.$refs.chart?.clientWidth) {
 | 
						|
          this.chart?.resize()
 | 
						|
        }
 | 
						|
      }, 1000)
 | 
						|
      //5分钟后停止监听
 | 
						|
      setTimeout(() => this.timer && clearInterval(this.timer), 5 * 60 * 1000)
 | 
						|
    },
 | 
						|
    refresh() {
 | 
						|
      this.chart.setOption(this.chartOptions || {})
 | 
						|
    }
 | 
						|
  },
 | 
						|
  mounted() {
 | 
						|
    this.$nextTick(() => {
 | 
						|
      this.watchResize()
 | 
						|
      this.initChart()
 | 
						|
      this.getChartData()
 | 
						|
    })
 | 
						|
  },
 | 
						|
  beforeDestroy() {
 | 
						|
    this.timer && clearInterval(this.timer)
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
 | 
						|
<style lang="scss" scoped>
 | 
						|
.AiChartV2 {
 | 
						|
  display: flex;
 | 
						|
  width: 100%;
 | 
						|
  height: 100%;
 | 
						|
  flex: 1;
 | 
						|
  min-width: 100px;
 | 
						|
  min-height: 0;
 | 
						|
  position: relative;
 | 
						|
 | 
						|
  .chart {
 | 
						|
    flex: 1;
 | 
						|
    height: 100%;
 | 
						|
    min-height: 100px;
 | 
						|
  }
 | 
						|
}
 | 
						|
</style>
 |