<template>
<div class="chart-wrapper">
<div class="chart-dom" ref="chartRef" />
<div class="chart-overlay" v-if="isEmpty">
<span>暂无数据</span>
</div>
<div class="chart-overlay" v-if="loading">
<a-icon slot="indicator" type="loading" style="font-size: 24px" />
</div>
</div>
</template>
<script>
import * as echarts from 'echarts5/core'
import { BarChart, LineChart, PieChart } from 'echarts5/charts'
import {
GridComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
TransformComponent,
} from 'echarts5/components'
import { CanvasRenderer } from 'echarts5/renderers'
import debounce from 'lodash/debounce'
import { cloneDeep, merge } from 'lodash'
import graphic from 'echarts/lib/util/graphic'
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
TransformComponent,
CanvasRenderer,
LegendComponent,
PieChart,
BarChart,
LineChart
])
const hexToRgba = (hex, opacity) => {
let rgbaColor = "";
let reg = /^#[\da-f]{6}$/i;
if (reg.test(hex)) {
rgbaColor = `rgba(${parseInt("0x" + hex.slice(1, 3))},${parseInt(
"0x" + hex.slice(3, 5)
)},${parseInt("0x" + hex.slice(5, 7))},${opacity})`;
}
return rgbaColor;
}
const grid = {
left: 10,
right: 20,
top: 20,
bottom: 2,
containLabel: true
}
const legend = {
show: false,
left: 'right',
itemWidth: 16,
itemHeight: 2,
selectedMode: false,
icon: 'rect',
}
const xAxis = {
type: 'category',
boundaryGap: true,
axisTick: {
show: false,
},
axisLabel: {
fontSize: 12,
color:'#adadad',
},
axisLine: {
show: false,
},
data: []
}
const tooltip = {
trigger: 'axis',
appendToBody: true,
confine: true,
}
const palette = {
yh: ['#5C8FF9','#F7A35D','#FFDB5C','#FF9D9A','#B9E1FF','#8CA5CD','#6DC8EC','#EDF0F4'],
year2: ['#5C8FF9', '#e7e7e7'],
year3: ['#5C8FF9', '#9AC5FF', '#e7e7e7']
}
const yAxis = {
type: 'value',
splitLine: {
lineStyle: {
color: '#f7f7f7',
}
},
}
export default {
name: 'a-chart',
props: {
option: Object,
loading: Boolean,
},
data() {
return {}
},
computed: {
isEmpty() {
return !this.option?.series || this.option?.series?.every(s => {return !s.data?.length})
}
},
watch: {},
mounted() {
const resizeFunc = debounce(() => {
if(!this.chart || this.chart._disposed) {
return
}
this.chart.resize({
animation: {
duration: 300,
easing: 'cubicInOut'
}
})
}, 100)
resizeFunc()
const ob = new ResizeObserver((entries) => {
resizeFunc()
})
ob.observe(this.$refs.chartRef)
window.addEventListener('resize', resizeFunc)
this.$on('beforeDestroy', () => {
window.removeEventListener('resize', resizeFunc)
ob.disconnect()
})
setTimeout(() => {
this.renderChart()
}, 100)
this.$watch('option', () => {
this.renderChart()
})
},
beforeDestroy() {
this.chart?.dispose()
},
methods: {
renderChart() {
if (!this.chart) {
this.chart = echarts.init(this.$refs.chartRef)
}
this.chart.clear()
this.chart.setOption(merge({}, {
grid,
xAxis,
yAxis,
tooltip,
legend
},this.parseOption(this.option)))
},
parseOption(option) {
const color = option?.color ? option.color : option?.palette ? palette[option?.palette] : palette.year2
const op = cloneDeep(option) || {}
const {series} = op
merge(op, {
series: series?.map((item, idx) => {
let { type, barWidth } = item || {}
if (!type) {
type = 'line'
}
const gradient = item.gradient
const thisColor = color[idx]
return {
...item,
type,
z: 10 - idx,
color: color[idx],
...(type === 'line' ? {
symbol: 'none',
areaStyle: {
color: gradient === false ? 'transparent' : new graphic.LinearGradient(0, 0, 0, 1, [
{offset: 0, color: hexToRgba(thisColor, 0.6)},
{offset: 1, color: hexToRgba(thisColor, 0.1)},
]),
},
emphasis: {
disabled: true,
},
} : {}),
...(type === 'bar' ? {
barWidth: barWidth || 20,
emphasis: {
disabled: true,
},
} : {})
}
})
})
return op
},
}
}
</script>
<style scoped lang="scss">
.chart-wrapper {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
overflow: hidden;
position: relative;
.chart-dom {
height: 100%;
width: 100%;
}
.chart-overlay {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
border-radius: 4px;
background: #fafafa;
display: flex;
align-items: center;
justify-content: center;
color: #a9a9a9;
}
}
</style>