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>
 |