Vue3封装使用Echarts

echarts官网icon-default.png?t=N7T8https://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

希望能帮助到大家,一键三连,感谢支持,不了解的,有其他疑问可以评论区滴滴我

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值