下载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>