185 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <section class="AiEchart">
 | |
|     <div ref="AiEchart" 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>
 | |
| 
 | |
| 
 | |
| export default {
 | |
|   name: "AiEchart",
 | |
|   props: {
 | |
|     data: {default: () => []},
 | |
|     ops: {default: () => ({})},
 | |
|     type: {default: "line"},
 | |
|     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 ['#2896FF', '#09DBFE', '#61FDB9', '#FFBB69', '#8429FF', '#ea7ccc']
 | |
|       }
 | |
| 
 | |
|       return ['#D4380D', '#CF1322', '#D55800', '#FA8C16', '#FFC53D', '#FFA940', '#FFC53D', '#780000']
 | |
|     },
 | |
| 
 | |
|     chartOptions() {
 | |
|       let {type, data, ops: options = {}} = this,
 | |
|           style = this.series ? this.series : this.ops.daemon ? this.ops.daemon : {},
 | |
|           colors = this.theme === '1' ? this.colors : (options.color || this.colors),
 | |
|           legend = {textStyle: {color: '#fff', padding: [0, 0, 0, 8], fontSize: 14}},
 | |
|           series = data?.[0] ? Array(Object.keys(data?.[0]).length - 1).fill(1)
 | |
|               .map((e, i) => ({type, ...(typeof style == 'object' ? style : style(colors[i]))})) : []
 | |
|       let ops = {
 | |
|         tooltip: {},
 | |
|         xAxis: {
 | |
|           type: 'category', nameGap: 20, axisTick: false,
 | |
|           axisLabel: {
 | |
|             color: '#fff',
 | |
|             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)'
 | |
|             }
 | |
|           }
 | |
|         },
 | |
|         // 声明一个 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: '#fff'}
 | |
|         },
 | |
|         legend, series, ...options,
 | |
|         color: colors,
 | |
|         grid: {
 | |
|           left: '0%',
 | |
|           right: '0%',
 | |
|           bottom: '0%',
 | |
|           top: '40px',
 | |
|           containLabel: true
 | |
|         },
 | |
|       }
 | |
|       if (JSON.stringify(this.ops) != JSON.stringify(ops)) this.$emit("update:ops", ops)
 | |
|       return ops
 | |
|     },
 | |
|     grid() {
 | |
|       let {width, height} = this.chartOptions.grid || {}
 | |
|       return {
 | |
|         width: width ? width + 'px' : 'auto',
 | |
|         height: height ? height + 'px' : 'auto'
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   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) {
 | |
|       if (!render) {
 | |
|         this.chart?.setOption({
 | |
|           dataset: {
 | |
|             source: this.data || []
 | |
|           }
 | |
|         })
 | |
|       } else {
 | |
|         this.chart?.setOption({
 | |
|           ...this.chartOptions,
 | |
|           dataset: {
 | |
|             source: this.data || []
 | |
|           }
 | |
|         }, true)
 | |
|       }
 | |
|     },
 | |
|     initChart() {
 | |
|       this.chart = echarts.init(this.$refs.AiEchart)
 | |
|       this.chart.setOption(this.chartOptions || {})
 | |
|     },
 | |
|     watchResize() {
 | |
|       this.timer && clearInterval(this.timer)
 | |
|       this.timer = setInterval(() => {
 | |
|         const {clientHeight, clientWidth} = this.$refs.AiEchart || {},
 | |
|             h = this.chart?.getHeight(), w = this.chart?.getWidth()
 | |
|         if (h != clientHeight || w != 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>
 | |
| .AiEchart {
 | |
|   width: 100%;
 | |
|   height: 100%;
 | |
|   flex: 1;
 | |
|   min-width: 100px;
 | |
|   min-height: 0;
 | |
|   position: relative;
 | |
| 
 | |
|   .chart {
 | |
|     height: 100%;
 | |
|     min-height: 100px;
 | |
|   }
 | |
| }
 | |
| </style>
 |