【vue2+ts】echarts封装公共组件
记录笔记…
VueEChart:
<template>
<div ref="el" :class="clsName"></div>
</template>
<script lang="ts">
import {
Component, Prop, Watch } from 'vue-property-decorator';
// 引入echarts
import echarts from 'echarts';
// eslint-disable-next-line import/extensions
import 'echarts-liquidfill/src/liquidFill.js';
import abpbase from '../../libs/abpbase';
import 'echarts-gl';
const EVENTS = [
'legendselectchanged',
'legendselected',
'legendunselected',
'legendscroll',
'datazoom',
'datarangeselected',
'timelinechanged',
'timelineplaychanged',
'restore',
'dataviewchanged',
'magictypechanged',
'geoselectchanged',
'geoselected',
'geounselected',
'pieselectchanged',
'pieselected',
'pieunselected',
'mapselectchanged',
'mapselected',
'mapunselected',
'axisareaselected',
'focusnodeadjacency',
'unfocusnodeadjacency',
'brush',
'brushselected',
'rendered',
'finished',
'click',
'dblclick',
'mouseover',
'mouseout',
'mousemove',
'mousedown',
'mouseup',
'globalout',
'contextmenu',
];
@Component({
name: 'VueEChart' })
export default class VueEChart extends abpbase {
@Prop({
type: Array,
default: () => ([]),
}) cname: string[];
@Prop({
type: Object,
default: () => ({
}),
}) options: object;
@Prop({
type: String,
default: '',
}) themeName: string;
@Prop({
type: Object,
default: () => ({
}),
}) themeData: object;
chart: any = null;
resizeInterval = null;
get clsName() {
return ['container', ...this.cname];
}
get echart() {
return this.chart;
}
init() {
if (this.chart) {
this.freeChart();
}
if (this.themeName && this.themeData) {
echarts.registerTheme(this.themeName, this.themeData);
this.chart = echarts.init(this.$refs.el as HTMLCanvasElement, this.themeName);
} else {
this.chart = echarts.init(this.$refs.el as HTMLCanvasElement);
}
this.chart.setOption(this.options, true);
this.chart.resize();
const self = this;
EVENTS.forEach((event) => {
self.chart.on(event, (params) => {
self.$emit(event, params);
});
self.chart.getZr()
.on(event, (params) => {
if (params) {
const pointInPixel = [params.offsetX, params.offsetY];
let xIndex: any; // x轴索引
if (self.chart.containPixel('grid', pointInPixel)) {
[xIndex] = self.chart.convertFromPixel({
seriesIndex: 0 }, [params.offsetX, params.offsetY]);
}
// 点击到图表其他区域不操作
if (typeof xIndex !== 'number') {
return;
}
const option = self.chart.getOption();
self.$emit(`${
event}Zr`, {
option,
xIndex,
dataIndex: xIndex,
params,
});
}
});
});
}
resizeEle() {
if (this.chart != null) {
this.chart.resize();
}
}
mounted() {
this.$nextTick(() => {
const self = this;
self.init();
self.resizeInterval = setInterval(() => {
self.resizeEle();
}, 2000);
});
}
@Watch('options', {
deep: true,
})
valueChange(newVal, oldVal) {
if (newVal && newVal !== oldVal) {
if (!this.chart) {
this.chart = echarts.init(this.$refs.el as HTMLCanvasElement);
}
this.chart.setOption(newVal, true);
}
}
freeChart() {
clearInterval(this.resizeInterval);
// 释放内存!
if (this.chart) {
this.chart.clear();
this.chart.dispose();
}
}
destroyed() {
this.freeChart();
}
}
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 100%;
}
</style>
使用:
<!--
* @fileName: 数据统计-数量统计-NumberStatistics.vue
* @date: yanghaoxing-2024-10-15 17:11:54
!-->
<template>
<div class="body-con pt-2">
<div class="flex title">
<img :src="'/images/pageIcon/sltj.png' | imgBasePath">
<span class="y-page-card-title">数量统计</span>
</div>
<div class="content">
<VueEChart :style="{ height: echartsHeight, width: '100%'}"
:options="state.options" />
</div>
</div>
</template>
<script lang="ts">
import abpbase from '@/libs/abpbase';
import {
Component, Emit, Mixins, Prop, Watch,
} from 'vue-property-decorator';
import EchartsMixin from '@/mixins/echartsMixin';
import VueEChart from '@/components/common/VueEChart.vue';
import echarts from 'echarts';
@Component({
name: 'NumberStatistics',
components: {
VueEChart,
},
})
export default class NumberStatistics extends Mixins(abpbase, EchartsMixin) {
state = {
options: {
xAxis: {
type: 'category',
axisLabel: {
interval: 0, rotate: 35 },
data: [
'历史文化名城',
'历史文化名镇',
'历史文化名村',
'历史文化名街',
'中国传统村落',
'历史建筑',
],
},
yAxis: {
type: 'value',
},
grid: {
left: '1%',
right: '4%',
bottom: 0,
top: '30px',
containLabel: true,
},
series: [
{
data: [300, 260, 210, 140, 220, 240],
type: 'bar',
label: {
show: true,
position: 'top',
valueAnimation: true,
formatter: ({
value }) => `${
value}个`,
},
barMaxWidth: '50%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0, color: '#d0a2a8' },
{
offset: 0.5, color: '#d0737e' },
{
offset: 1, color: '#c85460' },
]),
},
},
],
},
}
}
</script>
<style lang="less" scoped>
.body-con {
width: 100%;
.title {
align-items: center;
img {
height: 19px;
width: 19px;
margin-right: 2px;
}
}
.content {
width: 100%;
height: 100%;
min-height: 150px;
}
}
</style>