安装
npm i echarts -s
引用
import * as echarts from 'echarts'
const app = createApp(App)
app.config.globalProperties.$echarts = echarts
export default app
父组件
<template>
<el-button @click="isShow = !isShow">点击</el-button>
<div class="echarts-box">
<!--
theme:图表主题
isEmpty:图表是否有数据
-->
<t-chart
v-show="isShow"
:options="options"
theme="shine"
:isEmpty="false"
@chart="chart"
@click="click"
@dblclick="addData()"
@mousedown="mousedown"
@mousemove="mousemove"
@mouseover="mouseover"
@mouseout="mouseout"
@globalout="globalout"
@contextmenu="contextmenu"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, getCurrentInstance } from "vue";
const isShow = ref(true);
import TChart from "../components/TCharts.vue";
const options = ref({
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed"],
},
yAxis: {
type: "value",
},
series: [
{
data: [150, 230, 224],
type: "line",
},
],
});
//获取子组件图表实例
const chart = (chart) => {
// console.log("实例", chart);
};
//双击添加数据
const addData = () => {
options.value.xAxis.data.push(
"test" + Math.random().toString(36).substring(2, 8)
);
options.value.series[0].data.push(Math.random() * 200);
};
const click = (e) => {
console.log("click-----", e);
};
const mousedown = (e) => {
console.log("mousedown-----", e);
};
const mousemove = (e) => {
console.log("mousemove-----", e);
};
const mouseover = (e) => {
console.log("mouseover-----", e);
};
const mouseout = (e) => {
console.log("mouseout-----", e);
};
const globalout = (e) => {
console.log("globalout-----", e);
};
const contextmenu = (e) => {
console.log("contextmenu-----", e);
};
</script>
<style lang="scss" scoped>
.echarts-box {
width: 500px;
height: 500px;
border: 1px solid red;
}
</style>
子组件
<template>
<!-- v-bind="$attrs" 将父组件传递过来的属性都绑定到子组件的根组件上,可以在不需要prop接收的情况下通过getCurrentInstance().attrs.xxx或this.$attrs.xxx接收属性 -->
<div class="t-chart" v-bind="$attrs">
<div
v-show="!formatEmpty"
class="t-chart-container"
:id="id"
ref="echartRef"
/>
<slot v-if="formatEmpty" name="empty">
<el-empty v-bind="$attrs" :description="description" />
</slot>
<slot></slot>
</div>
</template>
<script lang="ts" name="TChart" setup>
import { useResizeObserver } from "@vueuse/core";
import {
getCurrentInstance,
onMounted,
defineProps,
computed,
ref,
markRaw,
nextTick,
useAttrs,
watch,
onBeforeUnmount,
} from "vue";
import { debounce, toLine } from "@/utils/index";
import app from "@/main";
//接受传递过来的属性
const props = defineProps({
options: {
type: Object,
default: () => ({}),
},
id: {
type: String,
default: () => Math.random().toString(36).substring(2, 8),
},
// 判断是否数据为空,可以是布尔值,也可以是函数
isEmpty: {
type: [Boolean, Function],
default: false,
},
description: {
type: String,
default: "暂无数据",
},
theme: {
type: String,
default: "",
},
});
//proxy提供了对当前组件内属性和方法的访问
console.log(getCurrentInstance());
const { proxy } = getCurrentInstance() as any;
const $echarts = app.config.globalProperties.$echarts;
const echartRef = ref<HTMLDivElement>();
const chart = ref();
const emits = defineEmits();
//获取所有父组件传递的非props的属性,形成键值对数组
const events = Object.entries(useAttrs());
console.log("event", events);
onMounted(() => {
renderChart();
});
onBeforeUnmount(() => {
// 取消监听
// window.removeEventListener('resize', resizeChart)
// 销毁echarts实例
chart.value.dispose();
chart.value = null;
});
//监听options变化
watch(
() => props.options,
async (nw) => {
await nextTick();
setOption(nw);
},
{ deep: true }
);
//监听echarts主题变化
watch(
() => props.theme,
async () => {
chart.value.dispose();
renderChart();
}
);
//计算是否展示图表
const formatEmpty = computed(() => {
if (typeof props.isEmpty === "function") {
return props.isEmpty(props.options);
}
return props.isEmpty;
});
//图表初始化
const renderChart = () => {
//通过markRaw,将echarts实例标记为普通对象,减少响应式带来的损耗。init方法的第二个参数是:当前图表应用的主题 Object | String
chart.value = markRaw($echarts.init(echartRef.value, props.theme));
setOption(props.options);
//返回chart实例
emits("chart", chart.value);
//监听图表事件
events.forEach(([key, value]) => {
if (key.startsWith("on") && !key.startsWith("onChart")) {
//以on开头又不是onChart的方法,获取该方法名methodName
const methodName = toLine(key).substring(3);
//echart.on('xxx',function)为事件监听
chart.value.on(methodName, (...args) => emits(methodName, ...args));
}
});
//监听元素变化
useResizeObserver(echartRef.value, resizeChart);
};
//设置图表函数,防抖
const setOption = debounce(
async (options) => {
if (!chart.value) return;
chart.value.setOption(options, true, true);
await nextTick();
resizeChart();
},
300,
true
);
//重绘图表函数
const resizeChart = debounce(
() => {
chart.value?.resize();
},
300,
true
);
</script>
<style lang="scss" scoped>
.t-chart {
position: relative;
width: 100%;
height: 100%;
&-container {
width: 100%;
height: 100%;
}
}
</style>
遇到的问题
- props.isEmpty()是函数时,要传递options对象?
- main.ts中引入的$echarts,无法在组件实例中访问到,最后只能export出去