//Components/VEcharts/echarts.ts 复制来自echarts官网
import * as echarts from 'echarts/core';
import {
BarChart,
// 系列类型的定义后缀都为 SeriesOption
BarSeriesOption,
LineChart,
LineSeriesOption,
} from 'echarts/charts';
import { LegendComponent } from 'echarts/components';
import {
TitleComponent,
// 组件类型的定义后缀都为 ComponentOption
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
// 数据集组件
DatasetComponent,
DatasetComponentOption,
// 内置数据转换器组件 (filter, sort)
TransformComponent
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
export type ECOption = echarts.ComposeOption<
| BarSeriesOption
| LineSeriesOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| DatasetComponentOption
>;
// 注册必须的组件
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
BarChart,
LineChart,
LabelLayout,
UniversalTransition,
CanvasRenderer
]);
export default echarts
//Components/VEcharts/index.vue
<template>
<div ref="echartsRef" class="echarts-container"></div>
</template>
<script setup lang="ts">
import echarts, { ECOption } from './echarts' //引入配置文件
let myChart: echarts.ECharts
const echartsRef = ref<HTMLElement>()
const props = defineProps({
width: {
type: String,
default: '100%',
required: true
},
height: {
type: String,
default: '100%',
required: true
},
option: {
type: Object,
required: true,
default: () => ({})
}
})
const debounce=(fn: Function, delay?: number)=> {
let delays = delay || 500;
let timer: NodeJS.Timeout|null
return (...rest:Array<any>)=> {
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
timer = null;
fn.apply(null, rest);
}, delays);
};
}
let debounceResize:any
const init = (dom: HTMLElement) => {
myChart = echarts.init(<HTMLElement>echartsRef.value)
myChart.setOption(props.option)
}
onMounted(() => {
if (echartsRef.value) {
init(echartsRef.value)
debounceResize=debounce(myChart.resize)
window.addEventListener('resize',debounceResize)
}
})
onBeforeUnmount(() => {
if (myChart) myChart.dispose() //销毁
window.removeEventListener('resize',debounceResize)
})
const getDom = () => {
if (myChart) return myChart.getDom()
}
const setOption = (option: ECOption) => {
if (myChart) myChart.setOption(option)
}
defineExpose({
getDom,
setOption
})
</script>
<style scoped>
.echarts-container {
width: v-bind("props.width");
height: v-bind("props.height");
}
</style>
//use
<template>
<div>
<VEcharts width="200px" height="300px" :option="option" ref="vEchartsRef"/>
<VEcharts width="200px" height="300px" :option="lineOption" ref="lineRef"/>
</div>
</template>
<script setup lang="ts">
import {useBarConfig,useLineConfig,useAreaBarConfig} from './config'
const {vEchartsRef,option}=useBarConfig()
const {vEchartsRef:lineRef,option:lineOption}=useLineConfig()
</script>
<style scoped></style>
// src/views/charts/config/index.ts 批量导入的文件
export {useChartConfig as useBarConfig} from './useBarConfig'
export {useChartConfig as useLineConfig} from './useLineConfig'
export {useChartConfig as useAreaBarConfig} from './useAreaBarConfig'
//views/charts/config/useBarConfig.ts 条形图例子1 hooks写法
import {ECOption} from '@/common/components/VEcharts/echarts'
import VEcharts from '@/common/components/VEcharts/index.vue'
export const useChartConfig=()=>{
const vEchartsRef=ref<InstanceType<typeof VEcharts>>()
const option:ECOption ={
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
}
onMounted(()=>{
const r=vEchartsRef.value?.getDom()
console.log(r);
vEchartsRef.value?.setOption({title:{text:'aw'}})
})
return {vEchartsRef,option}
}
//src/views/charts/config/useLineConfig.ts 柱形图例子2 hooks写法
import {ECOption} from '@/common/components/VEcharts/echarts'
import VEcharts from '@/common/components/VEcharts/index.vue'
export const useChartConfig=()=>{
const vEchartsRef=ref<InstanceType<typeof VEcharts>>()
const option:ECOption ={
tooltip:{},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
areaStyle: {}
}
]
};
onMounted(()=>{
const r=vEchartsRef.value?.getDom()
console.log(r);
})
return {vEchartsRef,option}
}
另起一个话题
usehooks里面的option数据真实情况是后端返回的。于是结合pinia状态仓库来处理。
//src/store/charts/bar.ts
import { defineStore } from "pinia";
import {ECOption} from '@/common/components/VEcharts/echarts'
export const useBarStore=defineStore('bar',{
state:()=>({
option:<ECOption>{}
}),
getters:{},
actions:{
setOption(){
//模拟网络请求
setTimeout(() => {
this.option={
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
}
}, 2000);
}
}
})
import VEcharts from '@/common/components/VEcharts/index.vue'
import {useBarStore} from '@/store/charts/bar'
import {storeToRefs} from 'pinia'
export const useChartConfig=()=>{
const vEchartsRef=ref<InstanceType<typeof VEcharts>>()
const barStore=useBarStore()
barStore.setOption() //获取数据请求
const {option}=storeToRefs(barStore)
onMounted(()=>{
const r=vEchartsRef.value?.getDom()
})
watch(option,(newVal,oldVal)=>{
vEchartsRef.value?.setOption(option.value) //watch监视option变换,一旦有变化图表数据重新赋值
})
return {vEchartsRef,option}
}
至此,数据从store提供->hooks接受数据->vue页面引入组件和hook进行渲染。之所以这样是实现试图(vue文件)-ts逻辑(hooks)-数据(pinia)分离。