vue3+ts项目封装echarts饼图

下载echarts 

npm i echarts

饼图对应type为pie

成果图: (数据为动态展示所以要封装组件)

 封装组件 为 EPIPieChart.vue

封装组件 通用代码

<template>

</template>

<script setup lang="ts">
import echarts from '@/assets/ts/echarts';
import { nextTick, reactive, watch } from 'vue';

const props = defineProps({
     pid: {
        type: String,
        required: true
    },
    online: {
        type: Number,
        required: true
    },
    offline: {
        type: Number,
        required: true
    }
})

watch([() => props.online, () => props.offline], (newVal) => {
    option.series[0].data[0].value = props.online;
    option.series[0].data[1].value = props.offline;
    initChart();
})

let dataList: { name: string }[] = reactive([]);

nextTick(() => {
    const container = document.querySelector('#' + props.pid) as HTMLElement;
    
    initChart(container);
/*
 if (container) {
        initChart(container);
    }
*/
});

const option = {
//echarts配置项
}
let myChart: echarts.ECharts | null = null;
const initChart = (container?: HTMLElement) => {
    if (!myChart) myChart = echarts.init(container as HTMLElement);
    myChart.setOption(option)
}
}
</script>

<style lang="scss" scoped>
</style>

详细代码如下

<template></template>

<script setup lang="ts">
import { nextTick } from 'vue';
import echarts from '@/assets/ts/echarts';
import { ZRColor } from 'echarts/types/dist/shared';
import useResizeChart from '@/components/CommonChart/hooks/useResizeChart';

const props = defineProps({
    // 父容器ID 此处为声明所需要的变量
    pid: {
        type: String,
        required: true,
    },
    title: {
        type: String,
        required: true,
    },
    curData: {
        type: Object,
        required: true,
    },
    preData: {
        type: Object,
        required: true,
    },
    sequential: {
        type: Number,
        required: true,
    },
    // 饼图 颜色
    color: {
        type: Array as () => ZRColor[],
        default: [],
    },
});

// 环比 此处需要两组数据,所以添加两个对象

const data = [
    {
        value: props.curData.value,
        label: props.curData.value + ' kW·h',
        name: props.curData.title,
    },
    {
        value: props.preData.value,
        label: props.preData.value + ' kW·h',
        name: props.preData.title,
    },
];

nextTick(() => {
    const container = document.querySelector('#' + props.pid) as HTMLElement;
    if (container) {
        const myChart = echarts.init(container);
        const upIcon = new URL('../../assets/img/Profile/Charts/up.svg', import.meta.url).href;
        const downIcon = new URL('../../assets/img/Profile/Charts/down.svg', import.meta.url).href;

//此处可以根据需要自定义echarts的option类型
        const option = {
            color: props.color,
            title: [
                {
                    text: ` 环比 {${props.sequential > 0 ? 'upIcon' : 'downIcon'}|}`,
                    subtext: props.sequential + '%',

                    top: '40%',
                    left: 'center',
                    textStyle: {
                        fontSize: 12,
                        color: '#fff',
                        fontWeight: 500,
                        rich: {
                            upIcon: {
                                height: 10,
                                backgroundColor: {
                                    image: upIcon,
                                },
                            },
                            downIcon: {
                                height: 10,
                                backgroundColor: {
                                    image: downIcon,
                                },
                            },
                        },
                    },
                    subtextStyle: {
                        fontSize: 14,
                        color: '#fff',
                    },
                },
            ],
            // tooltip: {
            //     trigger: 'item',
            //     formatter: function (parms: any) {
            //         const str = parms.marker + '' + parms.name + '</br>' + '用电量:' 
             + parms.data.value + ' kW·h' + '</br>' + '占比:' + parms.percent + '%';
            //         return str;
            //     },
            // },
            legend: {
                // type: 'scroll',
                icon: 'circle',
                orient: 'horizontal',
                formatter: '{name}用电量',
                textStyle: {
                    color: '#fff',
                },
                bottom: 0,
                height: 250,
            },
            series: [
                {
                    // 新增: 外环
                    name: '',
                    type: 'pie',
                    radius: ['58%'],
                    itemStyle: {
                        color: 'transparent',
                        borderWidth: 1,
                        borderColor: '#0fc7c0',
                    },
                    data: [100],
                    animation: false,
                    // showBackground: true,
                    // backgroundStyle: {
                    //     color: 'rgba(66, 66, 66, .3)',
                    // },
                },
                {
                    // 新增: 内环
                    name: '',
                    type: 'pie',
                    radius: ['32%'],
                    avoidLabelOverlap: false,
                    itemStyle: {
                        color: 'transparent',
                        borderWidth: 1,
                        borderColor: '#0fc7c0',
                    },
                    data: [100],
                    animation: false,
                },
                {
                    // 数据环
                    name: props.title,
                    type: 'pie',
                    radius: ['35%', '55%'],
                    clockwise: false,

                    // 指示线
                    label: {
                        show: true,
                        position: 'outside',
                        fontSize: 12,
                        lineHeight: 16,
                        color: '#fff',
                        formatter: '{name|{b}}\n{c}\n',
                        rich: {
                            name: {
                                color: 'inherit', //此时inherit表示文字颜色使用父级对应的值
                            },
                        },
                    },

                    // 新增: 指示线的样式
                    labelLine: {
                        show: true,
                        lineStyle: {
                            type: 'dashed', //指示线为虚线
                        },
                    },

                    // 新增: 饼图的样式
                    itemStyle: {
                        borderRadius: 10,
                        borderColor: '#071629',
                        borderWidth: 4,
                    },
                    emphasis: {
                        scale: false,
                    },
                    data: data,
                },
            ],
        };
        // 是否显示 饼图外环
        // if (props.showRing) {
        //     ringData.forEach(item => {
        //        option.series.unshift(item);
        //     });
        // }
        myChart.setOption(option);
        // 自适应 chart
        useResizeChart(container, myChart as echarts.ECharts);
    }
});
</script>

<style lang="scss" scoped></style>

使用组件

<div id="day-total-electricity">
  <EPIPieChart pid="day-total-electricity" title="当日用电量"
 :curData="dayElectricity.cur" :preData="dayElectricity.pre" :sequential="siteData.dayCompare" :color="dayColor"></EPIPieChart>
    </div>
<div id="month-total-electricity">
    <EPIPieChart pid="month-total-electricity" title="当月用电量" :curData="monthElectricity.cur" :preData="monthElectricity.pre" :sequential="siteData.monthCompare" :color="monthColor"></EPIPieChart>
       </div>




<script lang="ts" setup>
// 用电概况
//声明日用电量所用的两种颜色
const dayColor = ['#ea713d', '#07ffd5'];
const dayElectricity = computed(() => ({
    cur: {
        title: '当日',
        value: siteData.value.EPITodaySum,
    },
    pre: {
        title: '昨日',
        value: siteData.value.EPIYesterdaySum,
    },
}));
const monthColor = ['#ff6f92', '#42d3f0'];
const monthElectricity = computed(() => ({
    cur: {
        title: '当月',
        value: siteData.value.EPIThisMonthSum,
    },
    pre: {
        title: '上月',
        value: siteData.value.EPILastMonthSum,
    },
}));
</script>

 

useResizeChart.ts文件

 用于自适应 echarts 图表大小

import echarts from "@/assets/ts/echarts";
export default function useResizeChart(container: HTMLElement, chart: echarts.ECharts) {

    // 监听容器大小变化
    const resizeObserver = new ResizeObserver(entries => {
        // 重新设置大小
        chart.resize({
            animation: {
                duration: 500
            }
        });
    });

    resizeObserver.observe(container);

    // onBeforeUnmount(() => {
    // 	// 销毁前解除监听
    // 	resizeObserver.unobserve(container);
    // })

}

封装折线图

<template></template>
<script lang="ts" setup >

// 合并配置方法
function mergeConfig(defaultConfig: object, config: object) {
    return Object.assign(defaultConfig, config);
}

// option
let option = initOption();

// chart 容器
let container: HTMLElement | null = null;

// chart 实例
let myChart: echarts.ECharts | null = null;

// 渲染方法
const renderChart = (notMerge: boolean = false) => {
    if (!myChart) myChart = echarts.init(container as HTMLElement);
    myChart.setOption(option, {
        notMerge
    });
    // useDarkTheme(myChart, option, props.darkColorList);
}


// 配置关键项
function initOption(): echarts.EChartsCoreOption {

    // series
    const series: Array<LineSeriesOption> = [];
    // 整理 生成 series 数据源
    props.series.forEach(item => {
        // 默认配置
        let defaultItem: LineSeriesOption = {
            type: "line",
            name: '',						// 数据名
            data: [],						// 数据
            color: '',						// 颜色
            smooth: true,					// 是否平滑
            symbol: "emptyCircle", 				// 标记的图形
            symbolSize: 9, 					// 标记的大小
            showSymbol: true,				// 是否显示标记
            lineStyle: {					// 线条样式
                width: 3,					// 线条宽度
            },
            zlevel: 0
        };
        // 合并用户配置,用户配置优先级更高
        const Item = mergeConfig(defaultItem, item as LineSeriesOption)

        // 存入 series
        series.push(Item);
    });

    // title 配置
    const title = mergeConfig({
        // 是否显示
        show: true,
        // title 文本
        text: '',
        left: 'center',
        // 文字样式
        textStyle: {
            // color: "#333",
            fontSize: 20
        },
    }, props.title);


    // 横坐标轴线配置
    const XAxisLine = mergeConfig({
        // 是否显示
        show: true,
        // 样式
        lineStyle: {
            // color: "#333",
            type: 'solid',
            width: 1
        },
    }, props.XAxisLine);

    // 纵坐标轴线配置
    const YAxisLine = mergeConfig({
        // 是否显示
        show: true,
        // 样式
        lineStyle: {
            // 是否显示
            show: true,
            // 样式
            lineStyle: {
                // color: "#333",
                type: 'solid',
                width: 1
            },
        },
    }, props.YAxisLine);

    // 图例标记配置
    const legend = mergeConfig({
        show: true,
        left: "center",
        top: "95%",
        icon: 'circle'
    }, props.legend)

    // 网格配置
    const grid = mergeConfig({
        // 在容器内的位置
        left: "2%",
        right: "5%",
        bottom: "55",
        height: "80%",
        // 是否开启刻度
        containLabel: true,
    }, props.grid);

    // 横坐标分割线配置
    const XSplitLine = mergeConfig({
        // 是否显示
        show: false,
        // 样式
        lineStyle: {
            // color: "#333",
            type: 'dashed',
            width: 1
        },
    }, props.XSplitLine);

    // 纵坐标分割线配置
    const YSplitLine = mergeConfig({
        // 是否显示
        show: true,
        // 样式
        lineStyle: {
            color: "#333",
            type: 'dashed',
            width: 1
        },
    }, props.YSplitLine);

    // 横坐标刻度配置
    const XAxisTick = mergeConfig({
        // 是否显示
        show: true,
        // 刻度长度
        length: 5,
        // 是否朝内
        inside: true,
        // 刻度是否居中
        alignWithLabel: true,
        // 样式
        lineStyle: {
            // color: "#333",
            type: 'solid',
            width: 1
        },
    }, props.XAxisTick);

    // 纵坐标刻度配置
    const YAxisTick = mergeConfig({
        // 是否显示
        show: true,
        // 刻度长度
        length: 5,
        // 是否朝内
        inside: true,
        // 刻度是否居中
        alignWithLabel: true,
        // 样式
        lineStyle: {
            // color: "#333",
            type: 'solid',
            width: 1
        },
    }, props.YAxisTick);

    // 缩放轴配置
    const dataZoom: any[] = [];
    props.dataZoom.forEach(item => {
        dataZoom.push(mergeConfig({
            show: true,
            type: 'slider',
            xAxisIndex: [0],
            start: 0,
            end: 100,
            height: 20,
            moveHandleSize: 0,
            handleSize: '100%',
            handleIcon: 'image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAmlJREFUOE+t1c2L1XUUx/HXuTPjKlsG4cIHWgyjPYi4MAxaFkptyodFLUQjR0EtHxZNi2JahaFhJbkxHbPUVYpW/0IEKqgtQo1GAjfqaD6kc79x7v39xuttRCW/8IMf5/s77+95+HzPL1SrlBJoRMR4KWUqluJNzMbTKLiIkziEAxExVkrpQTMicl9CdMGWYxizKkjrm46Vjmn7A5si4mAnNCpYDf8C71agZgVp1AdX9gTmU9u/wtrKJoGZZrOUkrBB3EZPKZl+V2wT5SFCHjiOPnwZEWtarCrlZdiPOwnriGhy4l1rRpo+CX07IvZmhE/iVzyTxa1Sces2fb3ZJe6Mt3Psy6No7U3pbUWZq/b5DfMTuApf50ad5o1/WPQxn63ghVlsP8yZP9k5yLWbvDbM9pU8OyOdWofW0FUJPIpXO4FXrjMwyL6NvDyHLd9w/Cw/fcSla8xey4HNLBxgvElPYwL4YwJHMa2tHpFpjN1g7jp2r+elAYZGOHmeH4a4/DfzNjDyHgv6aTZpNFoVyQKMJrBuhFLadRm7zvPr2LOhDfxghBPnOPJhGzh3Pfve58X+iQjrNjUTmK1PTT024ONM+UJGeAyvdDYlU+4f5NuqKZt3c/wcPz+4KccSuBK77pHNLRYPszVlM5PPD3N6lJ2r27J5/RO2pWym/0c27yTwCfyC/m5h1+J9SGH/jnn11XsLe6p73PuIVy+bmj7LI+K7zuGwA2v+93DoGl8JXd0xvjKD+mkpqypLvk8+viYZsDmlP8X0BwzYsxiKiP33DNj7/AJyAi3BG3gOT1WR/oVTOIjvI+Jq9y/gX52gXhFv/Dn/AAAAAElFTkSuQmCC'
        }, item as object))
    });

    // 汇总配置
    let option: echarts.EChartsCoreOption = {
        title,
        tooltip: {
            trigger: "axis",
        },
        legend: legend,
        grid: grid,
        dataZoom: dataZoom,
        xAxis: {
            type: "category",
            boundaryGap: false,
            data: props.xAxisData,
            axisLine: XAxisLine,
            splitLine: XSplitLine,
            axisTick: XAxisTick
        },
        yAxis: {
            type: "value",
            // 单位
            name: props.yUnit,
            axisLine: YAxisLine,
            splitLine: YSplitLine,
            axisTick: YAxisTick
        },

        series
    };
    // 合并配置生成最终配置
    option = Object.assign(option, props.config);
    return option;
}


// props
const props = defineProps({
    // 父容器ID
    pid: {
        type: String,
        required: true
    },
    title: {
        type: Object,
        default: {}
    },
    // 数据
    series: {
        type: Array as () => Array<LineSeriesOption>,
        required: true
    },
    // 横坐标
    xAxisData: {
        type: Array,
        required: true
    },
    // 图例标记
    legend: {
        type: Object,
        default: {}
    },
    // 绘图网格
    grid: {
        type: Object,
        default: {}
    },
    // 横坐标轴线
    XAxisLine: {
        type: Object,
        default: {}
    },
    // 纵坐标轴线
    YAxisLine: {
        type: Object,
        default: {}
    },
    // y轴单位
    yUnit: {
        type: String,
        default: ''
    },
    // 横坐标分割线
    XSplitLine: {
        type: Object,
        default: {}
    },
    // 纵坐标分割线
    YSplitLine: {
        type: Object,
        default: {}
    },
    // 横坐标刻度
    XAxisTick: {
        type: Object,
        default: {}
    },
    // 纵坐标刻度
    YAxisTick: {
        type: Object,
        default: {}
    },
    // 缩放轴
    dataZoom: {
        type: Array,
        default: [{}]
    },
    // 总配置,将与默认配置与用户传入的配置合并
    config: {
        type: Object as () => echarts.EChartsCoreOption,
        default: {}
    },
    // 夜间模式颜色列表
    darkColorList: {
        type: Object as () => darkColorList,
        default: {}
    }
});



// DOM加载后渲染 chart
nextTick(() => {
    // 获取容器
    container = document.querySelector('#' + props.pid) as HTMLElement;
    // 渲染 chart
    renderChart();
    // 自适应 chart
    useResizeChart(container, myChart as echarts.ECharts);
});

// 监听 props 变化
watch(() => props, (newVal, oldVal) => {
    // 是否在不与原有配置合并
    let notMerge = true;

    if (newVal.series !== oldVal.series) {
        alert(1);
        notMerge = true;
    };
    // 更新 option
    option = initOption();
    // 重新渲染chart
    renderChart(notMerge);
}, {
    deep: true
})


</script> 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值