ouu-echarts.vue 9.85 KB
<template>
	<view class="content">
		<!-- #ifdef APP-PLUS || H5 -->
		<view
			v-show="echartData && echartData.option && echartData.option.series && echartData.option.series.length"
			class="echarts"
			:id="id"
			:prop="echartData"
			:change:prop="echarts.updateEcharts"
			@click.stop="onClick($event)"
		></view>
		<view class="loadingSeven" v-show="echartData && Object.keys(echartData.option).length == 0">
			<span 
				class="loading-seven-item"
				v-for="(item, index) in 5" :key="index"
				:style="{
					'--delay': `${index * 0.1}s`
				}"
			></span>
		</view>
		<!-- #endif -->
		<!-- #ifndef APP-PLUS || H5 -->
		<view> APPH5 环境不支持</view>
		<!-- #endif -->
	</view>
</template>

<script>
	/**
	 * H5、APP echarts组件
	 * @description H5 echarts组件
	 * @property {Object} chartOption echarts配置参数
	 * @event {Function()} echartClick 点击类目触发 返回 index, option
	 * @event {Function()} blankClick 点击的是空白
	 */
	export default {
		name: 'ouu-echarts',
		props: {
			// 配置参数, 必传
			chartOption: Object,
			// 自动缩放, true
			autoResize: {
				type: Boolean,
				default: true
			}, 
		},
		data() {
			return {
				id: this.getUUID(),
				echartData: {
					id: '',
					option: {},
					isChange: false,
					autoResize: false
				},
				clickA: false,
				clickB: false
			}
		},
		mounted() {
			this.init()
		},
		watch:{
			chartOption: {
				deep: true,
				handler(newVal, oldVal) {
					this.init()
				}
			}
		},
		methods: {
			init() {
				if(this.chartOption && Object.keys(this.chartOption).length) {
					let obj = {
						option: JSON.parse(JSON.stringify(this.chartOption)),
						id: this.id,
						autoResize: this.autoResize
					}
					this.echartData = obj
				}
				
			},
			// 屏幕尺寸变化
			setResize() {
				// console.log("屏幕变化")
				this.echartData.isChange = JSON.parse(JSON.stringify(!this.echartData.isChange))
			},
			// 防抖
			debounce(fn, delay) {
				let time = null;//time用来控制事件的触发
				return function () {
					if (time !== null) {
						clearTimeout(time);
					}
					time = setTimeout(() => {
						fn();
					}, delay)
				}
			},
			// 获取uuid
			getUUID() {
				return 'kxxx-yxxxxxxx'.replace(/[xy]/g, function (c) {
					let r = (Math.random() * 16) | 0,
						v = c == 'x' ? r : (r & 0x3) | 0x8;
					return v.toString(16);
				});
			},
			onViewClick() { // 
				console.log("点击空白区域触发", )
			},
			echartClick({option, index}) { // 点击类目触发
				this.clickA = !this.clickA
				// 点击类目轴触发 返回 索引index,和配置option
				this.$emit('echartClick', index, option)
			},
			// 点击整个空白触发
			onClick(e) { // 点击的是空白
				if(this.clickA != this.clickB) { // 点击的是类目轴
					this.clickB = this.clickA
				} else { 
					// 点击空白区域事件
					this.$emit('blankClick')
				}
				
			}
		}
	}
</script>

<script module="echarts" lang="renderjs">
	let myChart = {}
	let chartId = ''
	let echartResize = {}
	let isFullScreen = false
	import * as echarts from 'echarts';
	window.wx = undefined
	export default {
		mounted() {
			if (typeof window.echarts === 'object') {
				this.initEcharts()
			}
			window.onresize = () => {
				for (let id in myChart) {
					const { autoResize, option} = echartResize[id]
					const element = document.getElementById(id)
					if(autoResize && element) {
						myChart[id].dispose() // 销毁实例
						myChart[id] = echarts.init(document.getElementById(id))
						myChart[id].setOption(option, option.notMerge)
					}
				}
			}
		},
		methods: {
			initEcharts() {
				// 观测更新的数据在 view 层可以直接访问到
				// if(this.echartData && Object.keys(this.echartData.option).length && myChart) {
				// 	console.log("echarts初始化", this.echartData.option)
				// 	myChart.setOption(this.echartData.option)
				// }
			},
			formatThousands(value) {
			  if (value === undefined || value === null) {
			    value = ''
			  }
			  if (!isNaN(value)) {
			    value = value + ''
			  }
				let n1 = String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
			  return n1
			},
			tooltipFormatter(tooltip) {
				const { nameUnit } = tooltip
			  return params => {
			    let result = ''
			    for (let i in params) {
			      if (i == 0) {
			        result += nameUnit ? params[i].axisValueLabel + nameUnit : params[i].axisValueLabel
			      }
						let resObj = {}
			      if (params[i].data !== null && params[i].data !== '' && params[i].data !== '-') {
							resObj = this.setPublicFormatter(tooltip, params[i].value, 'object')
			      }
						const { value, unit } = resObj
			      // #ifdef H5
			      result += '<br/>' + params[i].marker + params[i].seriesName + '<span style="margin-left: 20px;"></span>' + value + ' ' + '<span style="letter-spacing: -1px;">'+ unit +'</span>'
			      // #endif
			      
			      // #ifdef APP-PLUS
			      result += '<br/>' + params[i].marker + params[i].seriesName + '&nbsp;&nbsp;' + value + ' ' + '<span style="letter-spacing: -1px;">'+ unit +'</span>'
			      // #endif
			    }
			    return result
			  }
			},
			labelFormatter(label) {
				return params => {
					if (params.data !== null && params.data !== '' && params.data !== '-') {
						return this.setPublicFormatter(label, params.value);
					}
				}
			},
			// 半圆格式化detail
			detailFormatter(detail) {
				return params => {
					// console.log(params)
					if (params !== null && params !== '' && params !== '-') {
						return this.setPublicFormatter(detail, params);
					}
				}
			},
			/**
			 * @description 单位 + 数字格式化
			 * @param {object} detail option格式化信息中格式化数据内容 按自定义规则来
			 * @param {number} paramsVal 显示value, 需要格式化的数值
			 * @param {string} mode 模式 'add': 相加(数值和单位拼接), 'object': 返回对象 自定义使用{value, unit}
			 */
			setPublicFormatter(detail, paramsVal, mode = 'add') {
				const { nameUnit, valueFormat, valueStandard, unitStandard, decimals } = detail
				let value = '-'
				let unit = '' 
				value = paramsVal
				// 格式单位
				if (valueFormat) { // 格式化单位
					unit = +value >= valueStandard ? unitStandard[1] : unitStandard[0]
				} else {
					unit = unitStandard && unitStandard[0] ? unitStandard[0] : ''
				}
				if (valueFormat) { // 格式化数值
					value = +value
					value = value >= valueStandard ? (value / valueStandard)  : value
				}
				if (decimals) { // 保留两位小数
					value = +value
					value = value.toFixed(decimals)
				}
				// 添加千分位
				value = this.formatThousands(value)
				return mode === 'add' ? value + unit : {value, unit};
			},
			updateEcharts(newValue, oldValue, ownerInstance, instance) {
				// 监听 service 层数据变更
				const { id, autoResize, option, isChange} = newValue
				if(id && option && newValue.option.series && newValue.option.series.length ) {
					this.debounce(this.setEcharts(newValue), 500)
				}
			},
			setEcharts(newValue) {
				const { id, autoResize, option, isChange} = newValue
				chartId = id
				echartResize[id] = {
					autoResize,
					option
				}
				if (option.tooltip) {
					// 判断是否格式化tooltip
					if (option.tooltip.formatterStatus) {
						option.tooltip.formatter = this.tooltipFormatter(option.tooltip)
					}
					option.tooltip.confine = true
					// 设置tooltip浮层层级
					option.tooltip.extraCssText = 'z-index: 9999999;'
				}
				for (let item of option.series) {
					if(item.label && item.label.formatterStatus) {
						item.label.formatter = this.labelFormatter(item.label)
					} else if(item.detail && item.detail.formatterStatus) { // 半圓的格式化
						item.detail.formatter = this.detailFormatter(item.detail)
					}
				}
				if(document.getElementById(id)) {
					myChart[id] = echarts.init(document.getElementById(id))
					myChart[id].clear() 
					myChart[id].setOption(option, option.notMerge)
					let series = myChart[id].getOption().series[0].data
					let that = this
					// 点击类目事件
					myChart[id].off('click')
					myChart[id].on('click', function(params) {
						let data = {
							option,
							index: params.seriesIndex
						}
						that.$ownerInstance.callMethod('echartClick', data)
					})
				}
				// // 点击空白事件
				// myChart[newValue.id].getZr().off('click')
				// myChart[newValue.id].getZr().on('click', function(params) {
				//   var pointInPixel = [params.offsetX, params.offsetY]
				//   if (myChart[newValue.id].containPixel('grid', pointInPixel)) {
				//     //红色文字空白处事件
				// 		that.onBlankClick(), 1000)
				//   }
				//   else{
				// 		//橘黄色文字空白处事件
				// 		that.onBlankClick()
				//   }
				// })
			},
			// 防抖
			debounce(fn, delay) {
				let time = null;//time用来控制事件的触发
				return function () {
					if (time !== null) {
						clearTimeout(time);
					}
					time = setTimeout(() => {
						fn.call(this);
					}, delay)
				}
			},
			// 空白区域点击事件
			onBlankClick(event, ownerInstance) {
				// 调用 service 层的方法
				console.log("点击外空白事件22",)
				this.$ownerInstance.callMethod('onViewClick')
			}
		}
	}
</script>

<style scoped>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		height: 100%;
		width: 100%;
	}
	.echarts {
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
	}
	.loadingSeven{
		width: 40px;
		height: 26px;
		margin: 0 auto;
		display: flex;
		justify-content: space-between;
		align-items: center;
	}
	.loadingSeven .loading-seven-item{
		display: inline-block;
		width: 6px;
		height: 100%;
		border-radius: 4px;
		animation: loadsaven 1.1s ease infinite;
		animation-delay: var(--delay);
	}
	@keyframes loadsaven {
		0%,
		100% {
			height: 30%;
			background: #bbd6da;
		}
		50% {
			height: 100%;
			background: #5ba1c6;
		}
	}
</style>