echarts官网https://echarts.apache.org/zh/index.html
一、ECharts 引入
1.1 安装
npm install echarts --save
1.2 注册使用
import * as Echarts from 'echarts/core';
import {
BarChart,
PieChart,
LineChart,
MapChart,
HeatmapChart
} from 'echarts/charts';
import {
TitleComponent,
LegendComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
ToolboxComponent,
DataZoomComponent,
GraphicComponent,
VisualMapComponent
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import type {
BarSeriesOption,
PieSeriesOption,
LineSeriesOption,
HeatmapSeriesOption
} from 'echarts/charts';
import type {
TitleComponentOption,
TooltipComponentOption,
GridComponentOption,
DatasetComponentOption,
ToolboxComponentOption,
DataZoomComponentOption,
GraphicComponentOption,
VisualMapComponentOption
} from 'echarts/components';
import type { ComposeOption } from 'echarts/core';
// 注册必须的组件
Echarts.use([
TitleComponent,
LegendComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
ToolboxComponent,
DataZoomComponent,
GraphicComponent,
VisualMapComponent,
LabelLayout,
UniversalTransition,
CanvasRenderer,
BarChart,
PieChart,
LineChart,
MapChart,
HeatmapChart
]);
export type ECOption = ComposeOption<
| BarSeriesOption
| PieSeriesOption
| LineSeriesOption
| HeatmapSeriesOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| DatasetComponentOption
| ToolboxComponentOption
| DataZoomComponentOption
| GraphicComponentOption
| VisualMapComponentOption
>;
export const echarts = Echarts;
二、创建基础图表组件
2.1 新建文件 chart.vue 于 src/components/chart/
2.2 代码示例 (已经实现响应屏幕宽度变化)
<template>
<div ref="chartRef" style="height: 320px"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, onUnmounted, Ref } from 'vue';
import { echarts, ECOption } from '@/utils/echarts.ts';
const props = defineProps<{
option: ECOption;
data: [];
}>();
let chartInstance: echarts.ECharts;
let isFinishedHandled = false;
let resizeObserver: ResizeObserver | null = null;
const chartRef = ref<HTMLElement | null>(null);
/**
* 处理图表渲染完成事件
* @param chartInstance
* @param chartRef
*/
function handleChartRenderFinished(
chartInstance: echarts.ECharts,
chartRef: Ref<HTMLElement | null>
) {
if (isFinishedHandled) return;
isFinishedHandled = true;
resizeObserver = new ResizeObserver(() => {
chartInstance.resize();
});
resizeObserver.observe(chartRef.value!);
}
onMounted(() => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
chartInstance.setOption(props.option);
// 监听图表渲染完成事件 - 响应宽度变化
chartInstance.on('finished', () =>
handleChartRenderFinished(chartInstance, chartRef)
);
});
onUnmounted(() => {
resizeObserver?.disconnect();
chartInstance?.dispose();
});
watch(
() => props.option,
newOption => {
chartInstance?.setOption(newOption);
}
);
watch(
() => props.data,
newData => {
chartInstance?.setOption({ series: [{ data: newData }] });
}
);
</script>
实现响应的关键:new ResizeObserver
三、创建各图表组件
3.1 我是在home/component/新建各图表组件
3.1.1 各图表组件只需配置配置项,数据从pinia获取,往下看
四、home.vue模拟数据
<template>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<info-panel v-for="panel in panels" :key="panel.id">
<template #panel-title>
<h2>{
{ panel.title }}</h2>
</template>
<template #panel-button v-if="panel.button">
<i class="fi fi-rr-menu-dots-vertical"></i>
</template>
<template #panel-content>
<component :is="views[panel.chartComponent]" />
</template>
</info-panel>
</div>
</template>
<script setup lang="ts">
import InfoPanel from '@/components/info-panel/info-panel.vue';
import { useChartDataStore } from '@/stores/models/chartData.ts';
import DepartmentCountChart from '@/views/home/components/department-count-chart.vue';
import ApplicationStatusChart from '@/views/home/components/application-status-chart.vue';
import DocumentToolTrendChart from '@/views/home/components/document-tool-trend-chart.vue';
import DocumentToolCountChart from '@/views/home/components/document-tool-count-chart.vue';
import TotalCountTrendChart from '@/views/home/components/total-visits-trend-chart.vue';
const views = {
DepartmentCountChart,
ApplicationStatusChart,
DocumentToolTrendChart,
DocumentToolCountChart,
TotalCountTrendChart
};
interface Panels {
id: number;
title: string;
button?: boolean;
chartComponent: string;
}
const panels: Panels[] = [
{
id: 1,
title: '网站访问量',
chartComponent: 'TotalCountTrendChart'
},
{
id: 2,
title: '部门人数',
button: true,
chartComponent: 'DepartmentCountChart'
},
{
id: 3,
title: '申请状态',
button: true,
chartComponent: 'ApplicationStatusChart'
},
{
id: 4,
title: '文档工具数量',
button: true,
chartComponent: 'DocumentToolCountChart'
},
{
id: 5,
title: '文档工具访问量',
chartComponent: 'DocumentToolTrendChart'
}
];
// 部门人数数据
const departmentCountData = [
{ department: '技术部', count: 15 },
{ department: '秘书部', count: 8 },
{ department: '宣策部', count: 12 },
{ department: '外联部', count: 5 }
];
// 申请状态数据
const applicationStatusData = [
{ status: '未通过', count: 5 },
{ status: '已通过', count: 20 },
{ status: '未批准', count: 10 },
{ status: '空邮箱', count: 2 }
];
// 文档和工具访问量趋势数据
const docToolTrendData = [
{
type: '前端相关',
documentCount: 32,
toolCount: 18,
totalVisits: 120,
date: '2024-06-15'
},
{
type: '后端相关',
documentCount: 25,
toolCount: 15,
totalVisits: 80,
date: '2024-06-15'
},
{
type: '插本相关',
documentCount: 18,
toolCount: 10,
totalVisits: 60,
date: '2024-06-15'
},
{
type: 'UI相关',
documentCount: 15,
toolCount: 8,
totalVisits: 40,
date: '2024-06-15'
},
{
type: 'AI相关',
documentCount: 10,
toolCount: 5,
totalVisits: 20,
date: '2024-06-15'
},
{
type: '前端相关',
documentCount: 38,
toolCount: 22,
totalVisits: 150,
date: '2024-06-22'
},
{
type: '后端相关',
documentCount: 28,
toolCount: 18,
totalVisits: 90,
date: '2024-06-22'
},
{
type: '插本相关',
documentCount: 20,
toolCount: 12,
totalVisits: 70,
date: '2024-06-22'
},
{
type: 'UI相关',
documentCount: 18,
toolCount: 10,
totalVisits: 50,
date: '2024-06-22'
},
{
type: 'AI相关',
documentCount: 12,
toolCount: 8,
totalVisits: 30,
date: '2024-06-22'
}
];
// 总量变化趋势数据
const totalVisitsTrendData = [
{ date: '2024-06-15', totalVisits: 320 },
{ date: '2024-06-22', totalVisits: 440 },
{ date: '2024-06-23', totalVisits: 210 },
{ date: '2024-06-24', totalVisits: 480 },
{ date: '2024-06-25', totalVisits: 760 }
];
// 文档工具数量数据
const docToolCountData = [
[
{ date: '2024-06-15', type: '前端相关', totalVisits: 120 },
{ date: '2024-06-15', type: '后端相关', totalVisits: 80 },
{ date: '2024-06-15', type: '插本相关', totalVisits: 60 },
{ date: '2024-06-15', type: 'UI相关', totalVisits: 40 },
{ date: '2024-06-15', type: 'AI相关', totalVisits: 20 }
],
[
{ date: '2024-06-22', type: '前端相关', totalVisits: 150 },
{ date: '2024-06-22', type: '后端相关', totalVisits: 90 },
{ date: '2024-06-22', type: '插本相关', totalVisits: 70 },
{ date: '2024-06-22', type: 'UI相关', totalVisits: 50 },
{ date: '2024-06-22', type: 'AI相关', totalVisits: 30 }
]
];
const chartDataStore = useChartDataStore();
chartDataStore.setDepartmentList(departmentCountData);
chartDataStore.setApplicationStatusList(applicationStatusData);
chartDataStore.setDocToolTrendList(docToolTrendData);
chartDataStore.setTotalVisitsTrendList(totalVisitsTrendData);
chartDataStore.setDocToolCountList(docToolCountData);
</script>
五、pinia转接数据
5.1 先定义数据类型
// 部门数据
export interface Department {
department: string;
count: number;
}
// 申请状态数据
export interface ApplicationStatus {
status: string;
count: number;
}
// 文档工具趋势数据
export interface DocToolTrend {
type: string;
documentCount: number;
toolCount: number;
totalVisits: number;
date: string;
}
// 总访问量趋势数据
export interface TotalVisitsTrend {
date: string;
totalVisits: number;
}
// 文档工具访问量数据
export interface DocToolCount {
date: string;
type: string;
totalVisits: number;
}
// 图表数据状态
export interface ChartDataState {
departmentList: Department[];
applicationStatusList: ApplicationStatus[];
docToolTrendList: DocToolTrend[];
totalVisitsTrendList: TotalVisitsTrend[];
docToolCountList: DocToolCount[][];
}
5.2 创建chartData.ts
import { defineStore } from 'pinia';
import {
ChartDataState,
Department,
ApplicationStatus,
DocToolTrend,
TotalVisitsTrend,
DocToolCount
} from '@/types/models/chartData';
export const useChartDataStore = defineStore('chartData', {
state: (): ChartDataState => ({
departmentList: [],
applicationStatusList: [],
docToolTrendList: [],
totalVisitsTrendList: [],
docToolCountList: []
}),
actions: {
setDepartmentList(data: Department[]) {
this.departmentList = data;
},
setApplicationStatusList(data: ApplicationStatus[]) {
this.applicationStatusList = data;
},
setDocToolTrendList(data: DocToolTrend[]) {
this.docToolTrendList = data;
},
setTotalVisitsTrendList(data: TotalVisitsTrend[]) {
this.totalVisitsTrendList = data;
},
setDocToolCountList(data: DocToolCount[][]) {
this.docToolCountList = data;
}
}
});
5.3 回溯各图表组件
application-status-chart.vue
<template>
<Chart :option="applicationStatusOptions" :data="applicationStatusData" />
</template>
<script setup lang="ts">
import Chart from '@/components/chart/chart.vue';
import { useChartDataStore } from '@/stores/models/chartData.ts';
const chartDataStore = useChartDataStore();
const applicationStatusData = chartDataStore.applicationStatusList;
const applicationStatusOptions = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
bottom: 'bottom'
},
series: [
{
name: '申请结果',
type: 'pie',
radius: '50%',
data: applicationStatusData.map(item => ({
name: item.status,
value: item.count
})),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
</script>
department-count-chart.vue
<template>
<Chart :option="departmentOption" :data="departmentData" />
</template>
<script setup lang="ts">
import Chart from '@/components/chart/chart.vue';
import { useChartDataStore } from '@/stores/models/chartData.ts';
import { computed } from 'vue';
const chartDataStore = useChartDataStore();
const departmentData = computed(() => chartDataStore.departmentList);
const departmentOption = computed(() => ({
tooltip: {
trigger: 'item'
},
xAxis: {
type: 'category',
data: departmentData.value.map(item => item.department)
},
yAxis: {
type: 'value'
},
series: [
{
data: departmentData.value.map(item => item.count),
type: 'bar'
}
]
}));
</script>
document-tool-count-chart.vue
<template>
<Chart :option="docToolCountOptions" :data="docToolCountData" />
</template>
<script setup lang="ts">
import { computed } from 'vue';
import Chart from '@/components/chart/chart.vue';
import { useChartDataStore } from '@/stores/models/chartData.ts';
const chartDataStore = useChartDataStore();
const docToolCountData = computed(() => chartDataStore.docToolCountList);
const dates = computed(() => [
...new Set(docToolCountData.value.flatMap(item => item.map(i => i.date)))
]);
const types = computed(() => [
...new Set(docToolCountData.value.flatMap(item => item.map(i => i.type)))
]);
const docToolCountOptions = computed(() => ({
tooltip: {
position: 'top'
},
grid: {
height: '70%',
top: '10%'
},
xAxis: {
type: 'category',
data: dates.value,
splitArea: {
show: true
}
},
yAxis: {
type: 'category',
data: types.value,
splitArea: {
show: true
}
},
visualMap: {
min: 0,
max: 200,
calculable: true,s
orient: 'horizontal',
left: 'center',
bottom: '0%'
},
series: [
{
name: '访问量',
type: 'heatmap',
data: docToolCountData.value.flatMap((items, dateIndex) =>
items.map((item, typeIndex) => [dateIndex, typeIndex, item.totalVisits])
),
label: {
show: true
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}));
</script>
document-tool-trend-chart.vue
<template>
<Chart :option="docToolTrendOptions" :data="docToolTrendData" />
</template>
<script setup lang="ts">
import { computed } from 'vue';
import Chart from '@/components/chart/chart.vue';
import { useChartDataStore } from '@/stores/models/chartData.ts';
const chartDataStore = useChartDataStore();
const docToolTrendData = computed(() => chartDataStore.docToolTrendList);
const dates = computed(() => [
...new Set(docToolTrendData.value.map(item => item.date))
]);
const docToolTrendOptions = computed(() => ({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['文档', '工具']
},
yAxis: [
{
type: 'category',
data: dates.value,
inverse: true,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
margin: 20,
fontSize: 14
},
axisPointer: {
label: {
show: true,
margin: 30
}
}
}
],
xAxis: [
{
splitLine: { show: false },
axisLabel: { show: false },
axisTick: { show: false },
axisLine: { show: false }
}
],
grid: {
containLabel: true,
left: 20
},
series: [
{
name: '文档',
type: 'bar',
data: docToolTrendData.value.map(item => item.documentCount),
label: {
show: true,
position: 'right'
}
},
{
name: '工具',
type: 'bar',
data: docToolTrendData.value.map(item => item.toolCount),
label: {
show: true,
position: 'right'
}
}
]
}));
</script>
total-visits-trend-chart.vue
<template>
<Chart :option="totalVisitsTrendOptions" :data="totalVisitsTrendData" />
</template>
<script setup lang="ts">
import { computed } from 'vue';
import Chart from '@/components/chart/chart.vue';
import { useChartDataStore } from '@/stores/models/chartData.ts';
const chartDataStore = useChartDataStore();
const totalVisitsTrendData = computed(() => chartDataStore.totalVisitsTrendList);
const totalVisitsTrendOptions = computed(() => ({
tooltip: {
trigger: 'axis',
showContent: false,
axisPointer: {
type: 'line',
label: {
show: true,
backgroundColor: '#a9b5d9',
formatter: (params: any) => {
const date = params.value;
const visits = totalVisitsTrendData.value.find(item => item.date === date)?.totalVisits;
return `${date}\n网站访客量:${visits}`;
}
}
}
},
xAxis: {
type: 'category',
data: totalVisitsTrendData.value.map(item => item.date)
},
yAxis: {
type: 'value'
},
series: [
{
name: '网站访客量',
data: totalVisitsTrendData.value.map(item => item.totalVisits),
type: 'line',
smooth: true,
areaStyle: {
opacity: 0.3
}
}
]
}));
</script>
六、效果图:支持响应式,性能也不错

如果你对ehcart配置不熟悉,请访问学习Documentation - Apache ECharts
我推荐到官网的示例,去里面调试配置项,你很快就能明白的Examples - Apache ECharts
希望能帮助到大家,一键三连,感谢支持,不了解的,有其他疑问可以评论区滴滴我