Files
fe.ems.vue3/src/components/ChartLine/index.vue

264 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import { nextTick, watch, onMounted, onBeforeUnmount, ref, markRaw } from 'vue';
import * as echarts from 'echarts/core';
import {
TitleComponent,
ToolboxComponent,
TooltipComponent,
GridComponent,
DataZoomComponent,
LegendComponent,
} from 'echarts/components';
import { LineChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { parseSizeFromKBs, parseSizeFromMB } from '@/utils/parse-utils';
echarts.use([
TitleComponent,
ToolboxComponent,
TooltipComponent,
GridComponent,
DataZoomComponent,
LegendComponent,
LineChart,
CanvasRenderer,
UniversalTransition,
]);
const props = defineProps({
/**
* 图表主题
*
* 'dark' | 'light'
*/
theme: {
type: String,
default: 'light', // 'dark' | 'light'
},
/**宽度默认100% */
width: {
type: String,
default: '100%',
},
/**高度 */
height: {
type: String,
default: '200px',
},
/**底部时间轴 */
dataZoom: {
type: Boolean,
default: false,
},
/**
* option: { title , xDatas, yDatas, formatStr, yAxis, grid, tooltip}
*
* grid 图偏移 列如{ left: '15%', right: '15%', bottom: '20%' }
* formatStr 支持 'KB/s'、'MB'、自定义格式
*/
option: {
type: Object,
required: true,
},
});
const seriesStyle = [
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(22, 119, 255, .5)',
},
{
offset: 1,
color: 'rgba(22, 119, 255, 0)',
},
]),
},
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(82, 196, 26, .5)',
},
{
offset: 1,
color: 'rgba(82, 196, 26, 0)',
},
]),
},
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(250, 173, 20, .5)',
},
{
offset: 1,
color: 'rgba(250, 173, 20, 0)',
},
]),
},
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(255, 77, 79, 0.5)',
},
{
offset: 1,
color: 'rgba(255, 77, 79, 0)',
},
]),
},
];
let newChart: echarts.ECharts;
const chartDom = ref<HTMLElement | undefined>(undefined);
/**初始化渲染图表 */
function handleRanderChart(container: HTMLElement | undefined) {
if (!container || !props.option.yDatas) return;
if (!newChart) {
newChart = markRaw(echarts.init(container, props.theme));
}
const series: any = [];
const yDatasLen = props.option.yDatas.length;
props.option.yDatas.forEach((item: any, index: number) => {
series.push({
name: item?.name,
type: 'line',
areaStyle: seriesStyle[index % yDatasLen],
data: item?.data,
showSymbol: false,
yAxisIndex: item.yAxisIndex ? 1 : null,
});
});
// 右侧y轴辅助线
const yAxis: any = [];
if (props.option.yAxis && props.option.yAxis.length > 0) {
props.option.yAxis.forEach((item: any) => {
yAxis.push({
splitLine: {
show: true,
//分隔辅助线
lineStyle: {
type: 'dashed', //线的类型 虚线0
opacity: props.theme === 'dark' ? 0.1 : 1, //透明度
},
},
...item,
});
});
}
// 把配置和数据放这里
const option = {
title: [
{
left: 'center',
text: props.option.title,
show: props.option.title,
},
],
zlevel: 1,
z: 1,
tooltip: props.option.tooltip || {
trigger: 'axis',
formatter: function (datas: any) {
let res = datas[0].name + '<br/>';
// 拼接值的格式
switch (props.option.formatStr) {
case 'KB/s':
for (const item of datas) {
res += `${item.marker} ${item.seriesName}${parseSizeFromKBs(
item.data
)}<br/>`;
}
break;
case 'MB':
for (const item of datas) {
res += `${item.marker} ${item.seriesName}${parseSizeFromMB(
item.data
)}<br/>`;
}
break;
default:
for (const item of datas) {
res += `${item.marker} ${item.seriesName}${item.data}${props.option.formatStr}<br/>`;
}
break;
}
return res;
},
},
grid: props.option.grid || { left: '8%', right: '8%', bottom: '20%' },
legend: props.option.legend || {
right: 10,
itemWidth: 8,
textStyle: {
color: '#646A73',
},
icon: 'circle',
},
xAxis: { data: props.option.xDatas, boundaryGap: false },
yAxis: props.option.yAxis
? yAxis
: {
name: '( ' + props.option.formatStr + ' )',
splitLine: {
//分隔辅助线
lineStyle: {
type: 'dashed', //线的类型 虚线0
opacity: props.theme === 'dark' ? 0.1 : 1, //透明度
},
},
},
series: series,
dataZoom: [{ startValue: props.option?.xDatas[0], show: props.dataZoom }],
};
// 渲染数据
option && newChart.setOption(option, true);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => {
if (newChart) {
newChart.resize();
}
});
// 监听元素大小变化
observer.observe(container);
}
watch(
() => props.option,
val => {
if (val) {
nextTick(() => {
handleRanderChart(chartDom.value);
});
}
}
);
onMounted(() => {
nextTick(() => {
handleRanderChart(chartDom.value);
});
});
onBeforeUnmount(() => {
newChart?.dispose();
});
</script>
<template>
<div ref="chartDom" :style="{ height: height, width: width }"></div>
</template>
<style lang="less" scoped></style>