如下图,左右两边的Y轴线不在同一刻度上。

image.png
使用vue2代码修改后的效果:

image.png
注意:直接将option配置放在echarts官网示例中运行是没效果的,原因是修改的option.yAxis.interval后,没有执行【myChart.clear(); 与 myChart.setOption(option);】两个函数。
echarts的option代码如下:
<template>
<div>
<JEcharts ref="JEcharts" :option="option" :emptyState="false" :height="300" />
</div>
</template>
<script>
export default {
name: '',
components: {},
computed: {},
data() {
return {
option: {
// color: ['#70b603', '#e3935d'],
title: {
text: '能源结构分布趋势',
top: 15,
left: 'center',
textStyle: {
color: '#333',
fontSize: 15,
},
},
tooltip: {
trigger: 'axis',
},
legend: {
type: 'scroll',
top: '48', //与上方的距离 可百分比% 可像素px
},
grid: {
top: 100,
left: 50,
right: 60,
bottom: '3%',
containLabel: true,
},
toolbox: {
// x: '1250', //下载的字体显示不全
right: 25,
},
xAxis: {
type: 'category',
// boundaryGap: true,
data: ['1月', '2月', '3月', '4月', '5月'],
// data: [],
},
yAxis: [
{
type: 'value',
name: '用电量(kW·h)',
nameTextStyle: {
width: 200,
padding: [0, 0, 0, 50],
fontSize: 12,
color: '#127ffb', // 设置 Y 轴标题颜色为红色
lineHeight: 20, // 行高
},
splitLine: {
show: true,
lineStyle: {
color: '#ccc',
type: 'dashed',
},
},
axisLine: {
//坐标轴轴线相关设置。数学上的x轴
show: false, //false 不显示
lineStyle: {
color: '#797777',
},
},
axisLabel: {
color: '#127ffb',
fontSize: 12, // Y轴字体大小 12px
},
axisTick: {
show: false, //false 不显示
},
// min: 0,
// splitNumber: 5, // 与 yAxis[1] 保持相同分割数,使两侧刻度线对齐
interval: 100,
min: (value) => (value.min > 0 || isNaN(value.min) ? 0 : value.min),
max: (value) => {
//刻度分割成5等份。注意:yAxis[0]下标,vue2的箭头函数的this指向实例(vue3语法不是用this)
try {
let echartsOption = this.option || option
let splitNumber = 5 //分为五份
let yAxisObj = echartsOption.yAxis[0]
if (isNaN(value.max)) {
if (yAxisObj.interval != 1) yAxisObj.interval = 1
return splitNumber
}
if (!echartsOption) throw new Error('echartsOption is null!')
if (value.max > 10) value.max = Math.ceil(value.max / splitNumber) * splitNumber // 确保大于10时,最大值能被5整除,这样刻度值就不会出现小数
let { max, min } = value
let getDigitLen = (num1 = '', num2 = '') => {
return Math.max((String(num1).split('.')[1] || '').length, (String(num2).split('.')[1] || '').length)
}
if (min < 0) {
let len = getDigitLen(max, min) //小数点长度
max = (max * Math.pow(10, len) - min * Math.pow(10, len)) / Math.pow(10, len) // 防止精度丢失
}
let digitLen = getDigitLen(max) //小数点长度
let interval = (Number(String(max).replace('.', '')) * 100) / splitNumber / Math.pow(10, digitLen + 2) //分割间隔,分为五份!乘100(再除多100)目的是能完全整除5,否则会有精度丢失(测试到第七位小数精度也会丢失,请另找方法处理)
if (yAxisObj.interval != interval) yAxisObj.interval = interval //没这个条件,会进入死循环
return value.max
} catch (error) {
console.error('echarts图表yAxis[0]的max函数运行出错了: ', error)
return value.max
}
},
},
{
type: 'value',
name: '环比上升率%',
nameTextStyle: {
width: 200,
padding: [0, 0, 0, 50],
fontSize: 12,
color: '#127ffb', // 设置 Y 轴标题颜色为红色
lineHeight: 20, // 行高
},
splitLine: {
show: false, // 右侧轴不重复显示网格线(建议隐藏,有时计算出来差1个像素可以忽略)
lineStyle: {
color: '#ccc',
type: 'dashed',
},
},
axisLine: {
//坐标轴轴线相关设置。数学上的x轴
show: false, //false 不显示
lineStyle: {
color: '#797777',
},
},
axisLabel: {
color: '#127ffb',
fontSize: 12, // Y轴字体大小 12px
},
axisTick: {
show: false, //false 不显示
},
// min: 0,
// splitNumber: 5, // 与 yAxis[0] 保持相同分割数,使两侧刻度线对齐
interval: 100,
min: (value) => (value.min > 0 || isNaN(value.min) ? 0 : value.min),
max: (value) => {
//刻度分割成5等份。注意:yAxis[0]下标,vue2的箭头函数的this指向实例(vue3语法不是用this)
try {
let echartsOption = this.option || option
let splitNumber = 5 //分为五份
let yAxisObj = echartsOption.yAxis[1]
if (isNaN(value.max)) {
if (yAxisObj.interval != 1) yAxisObj.interval = 1
return splitNumber
}
if (!echartsOption) throw new Error('echartsOption is null!')
if (value.max > 10) value.max = Math.ceil(value.max / splitNumber) * splitNumber // 确保大于10时,最大值能被5整除,这样刻度值就不会出现小数
let { max, min } = value
let getDigitLen = (num1 = '', num2 = '') => {
return Math.max((String(num1).split('.')[1] || '').length, (String(num2).split('.')[1] || '').length)
}
if (min < 0) {
let len = getDigitLen(max, min) //小数点长度
max = (max * Math.pow(10, len) - min * Math.pow(10, len)) / Math.pow(10, len) // 防止精度丢失
}
let digitLen = getDigitLen(max) //小数点长度
let interval = (Number(String(max).replace('.', '')) * 100) / splitNumber / Math.pow(10, digitLen + 2) //分割间隔,分为五份!乘100(再除多100)目的是能完全整除5,否则会有精度丢失(测试到第七位小数精度也会丢失,请另找方法处理)
if (yAxisObj.interval != interval) yAxisObj.interval = interval //没这个条件,会进入死循环
return value.max
} catch (error) {
console.error('echarts图表yAxis[1]的max函数运行出错了: ', error)
return value.max
}
},
},
],
series: [
{
name: '空调',
type: 'bar',
yAxisIndex: [0],
stack: 'total',
barMaxWidth: '30',
// color: '#70b603',
data: [121, 32, 101, 134, 90],
// data: [],
},
{
name: '风扇',
type: 'bar',
yAxisIndex: [0],
stack: 'total',
barMaxWidth: '30',
// color: '#e3935d',
data: [220, 181, 18, 234, 291],
// data: [],
},
{
name: '环比上升率',
type: 'line',
yAxisIndex: [1],
// color: '#e3935d',
data: [-0.1, 0.33, 0.065, 0.1, 0.312],
// data: [],
},
],
},
}
},
created() {},
methods: {},
}
</script>
<style lang="less" scoped></style>
JEcharts.vue组件
<template>
<div class="JEcharts">
<a-empty
v-if="isShowEmpty"
:class="emptyClassName"
style="
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
border: 1px solid #e8e8e8;
margin: auto;
"
/>
<div v-show="!isShowEmpty" :id="echartsID" :style="{ height: countHeight }"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import 'echarts-wordcloud'
export default {
name: 'JEcharts',
props: {
emptyClassName: {
type: String,
default: '',
},
height: {
type: Number | String,
default: 0,
required: false,
},
option: {
type: Object,
default: () => {},
required: false,
},
//是否需要展示【空状态:暂无数据】,默认不展示
emptyState: {
type: Boolean,
default: false,
required: false,
},
theme: {
type: Object | String,
default: '',
},
echartInitOptions: {
type: Object,
default: undefined,
},
},
computed: {
isShowEmpty() {
if (!this.option && this.emptyState) {
return true
} else {
if (Object.keys(this.option).length == 0 && this.emptyState) {
return true
} else {
return false
}
}
},
countHeight() {
if (this.height === 0) {
return '100%'
}
if (typeof this.height === 'number') {
return `${this.height}px`
}
if (typeof this.height === 'string') {
if (isNaN(Number(this.height))) {
return this.height //可能的单位是px,rem,em,vh,%
} else {
return `${this.height}px`
}
}
},
},
watch: {
option: {
immediate: true,
handler(nVal, oVal) {
this.$nextTick(function () {
// 由于Vue无法监听到深层对象,所以在父页面中需要对option重新赋值
// 1.【this.option = Object.assign({},this.option);】推荐
// 2.【this.$refs.JEcharts.setOption(this.option)】
this.setOption(nVal)
})
},
deep: true,
},
$route: function (newRoute, oldRoute) {
this.$nextTick(() => {
if (this.myChart && !this.myChart.isDisposed()) {
this.myChart.resize()
}
})
},
},
data() {
return {
echartsID: `JEchartsId-${parseInt(Math.random() * 1e12)}`, //生成唯一的ID
myChart: null,
}
},
created() {},
mounted() {
this.initEchart()
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose()
this.myChart.clear() //清空画布
}
if (this.instanceObserver) {
if (document.getElementById(this.echartsID)) {
this.instanceObserver.unobserve(document.getElementById(this.echartsID))
}
this.instanceObserver.disconnect() // 停止所有观察
}
if (this.windowResize) {
window.removeEventListener('resize', this.windowResize)
}
},
destroyed() {},
activated() {},
methods: {
initEchart() {
if (!this.myChart && document.getElementById(this.echartsID)) {
this.myChart = echarts.init(document.getElementById(this.echartsID), this.theme, this.echartInitOptions)
this.handleYAxisAxisLabelFormatter(this.option)
this.myChart.setOption(this.option || {})
this.myChart.on('finished', () => this.$emit('finish'))
this.$nextTick(() => {
this.ResizeObserver()
})
}
},
setOption(option = {}) {
if (this.isShowEmpty) return
if (this.myChart) {
this.handleYAxisAxisLabelFormatter(option)
this.myChart.clear() //清空画布(如果不清空画布,有数据缓存的bug,图表有旧数据存在)
this.myChart.setOption(option)
} else {
this.initEchart()
}
},
//使用ResizeObserver监听内容变化引起的高度变化,然后刷新图表
ResizeObserver() {
if (window.ResizeObserver) {
// 创建一个ResizeObserver实例,并定义回调函数
this.instanceObserver = new ResizeObserver((entries) => {
let { width, height } = entries[0].contentRect
let chartWidth = this.myChart.getHeight()
let chartHeight = this.myChart.getWidth()
//宽度/高度无变化(向上取整,原因有可能出现小数)!宽度/高度相差2px以内,默认是宽度/高度无变化
if (
Math.abs(Math.ceil(width) - Math.ceil(chartWidth)) > 2 ||
Math.abs(Math.ceil(height) - Math.ceil(chartHeight)) > 2
) {
//放大或缩小屏幕时,重新渲染图表,执行动画
this.myChart.clear() //清空画布
this.myChart.resize()
this.myChart.setOption(this.option) //动画会重新渲染
}
})
// 开始观察目标元素
this.instanceObserver.observe(document.getElementById(this.echartsID))
} else {
//旧浏览器不支持ResizeObserver,需要兼容处理
this.windowResize = () => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.myChart.clear() //清空画布
this.myChart.resize()
this.myChart.setOption(this.option) //动画会重新渲染
let timer2 = setTimeout(() => {
this.myChart.resize()
clearTimeout(timer2)
}, 1500)
clearTimeout(this.timer)
}, 500)
}
window.addEventListener('resize', this.windowResize)
}
},
//Y轴,数字过大,缩减数字后,在其后面添加【万】【亿】
handleYAxisAxisLabelFormatter(option) {
let handleYAxis = (obj) => {
if (obj.axisLabel && obj.axisLabel._formatter === true) {
delete obj.axisLabel._formatter
obj.axisLabel.formatter = (value, index) => {
return this.axisLabelFormatter(value, index, option.series)
}
}
}
if (Object.prototype.toString.call(option.yAxis) == '[object Array]') {
for (let i = 0; i < option.yAxis.length; i++) {
handleYAxis(option.yAxis[i])
}
}
if (Object.prototype.toString.call(option.yAxis) == '[object Object]') {
handleYAxis(option.yAxis)
}
},
//数字后面可能带【万、亿】
axisLabelFormatter(value, index, series) {
let unit = ''
let max = 0
series = series || []
for (let i = 0; i < series.length; i++) {
let num = Math.max.apply(null, series[i].data)
max = Math.max(num, max)
}
if (max > 1e4 && max <= 1e8) {
unit = '万'
value = Math.floor((value * 100) / 1e4) / 100 //保留两位小数,向下取整
}
if (max > 1e8) {
unit = '亿'
value = Math.floor((value * 100) / 1e8) / 100 //保留两位小数,向下取整
}
return value + unit
},
},
}
</script>
<style lang="less" scoped>
.JEcharts {
height: 100%;
min-height: 10px;
position: relative;
}
</style>
<style lang="less">
//大屏Echarts tooltip提示框组件颜色修改
.JEcharts-fullsceen > div > div:nth-child(2) {
background-color: rgba(4, 15, 39, 0.9) !important;
border-color: #2b709f !important;
box-shadow: inset 0px 0px 10px #009cae !important;
&,
* {
color: #fff !important;
}
}
</style>