基于Echarts进行图表组件的封装

什么是Echarts

是一个使用js实现的开源可视库,提供了多种图表,但是当我们在项目中进行使用的时候可能就是需要进行一系列的相关配置如: 标题,类型,x轴,y轴等,当我们使用较为频繁的时候就容易导致代码的冗余,并且整体将echarts进行安装引入也是比较大的,我们可以按照自己的需要进行对应组件的引入

Echarts的使用

安装Echarts

Echarts官网-安装echarst并实现按需引入Echarts图表和组件

基于Echarts相关配置进行组件的封装

// components/BarChart.vue
/* @/components/BarChart.vue */

<template>
  <div ref="chartDom" :style="{ height: getHeight }"></div>
</template>

<script setup lang="ts">
import { echarts, type ECOption } from '@/utils/echarts'
import { ref, shallowRef, watch, computed, onMounted, onBeforeUnmount, type ShallowRef, type Ref } from 'vue'
import type { EChartsType } from 'echarts/types/dist/core'
import type {
  XAXisOption, YAXisOption, LegendComponentOption, BarSeriesOption, DataZoomComponentOption
} from 'echarts/types/dist/shared'
import resize from '@/utils/resize'
import type { ChartSetting } from '@/types/ChartData'

//定义组件属性
const props = withDefaults(
  defineProps<{
    //数据
    data?: Array<string | number>
    //x轴数据
    xAxisData?: Array<string>
    //图表标题
    title?: string
    //系列配置
    series?: Array<BarSeriesOption>
    //x轴配置
    xAxis?: Array<XAXisOption>
    //y轴配置
    yAxis?: Array<YAXisOption>
    //图例配置
    legend?: LegendComponentOption
    //区域缩放配置
    dataZoom?: Array<DataZoomComponentOption>
    //图形高度
    height?: number | string
    //数据集
    datasetSource?: Array<any>
    //综合配置
    options: ChartSetting
  }>(),
  {
    data: () => [],
    xAxisData: () => [],
    title: 'ECharts柱状图',
  }
)
//要渲染的Dom元素
const chartDom: Ref<HTMLDivElement | null> = ref(null)
//渲染的chart对象要用shallowRef
const chart: ShallowRef<EChartsType | null | undefined> = shallowRef(null)
//高度同时支持string和number
const getHeight = computed(() => {
  return typeof props.height === 'number' ? props.height + 'px' : props.height
})
//监听数据变化,重新绘制
watch(
  () => props,
  () => {
    drawChart()
  },
  { deep: true }
)

//绘制
async function drawChart() {
  let datasetSource: Array<any> | undefined = props.datasetSource,
    series: Array<BarSeriesOption> = [],
    xAxisData: Array<string> = props.xAxisData
  let chartType = props.options.chartType || 'bar'; // 默认为柱状图,如果需要折线图则设置为 'line'

  if (props.options) {
    if (props.options.apiMethod) {
      //获取接口数据作为数据集
      let allx = await props.options.apiMethod()
      datasetSource = allx.data
      if (props.options.xProp) {
        //根据配置的x轴属性名生成x轴数据
        xAxisData = []
        datasetSource?.forEach(data => {
          xAxisData.push(data[props.options.xProp])
        })

      }
    }
    if (props.options.seriesOption) {
      props.options.seriesOption.forEach((opt: any) => {
        series.push({
          name: '车牌',
          barMaxWidth: 30,
          emphasis: { focus: 'series' },
          label: { show: true, position: 'top', color: 'inherit' },
          ...opt,
          type: chartType, // 设置图表类型
        })
      })
    }
  }
  // else {
  //   series = props.series ? props.series : [{
  //     name: '车牌',
  //     type: 'bar',
  //     barMaxWidth: 30,
  //     emphasis: { focus: 'self' },
  //     label: { show: true, position: 'inside', color: '#fff' },
  //     data: props.data
  //   }]
  // }

  let xAxis: Array<XAXisOption> = props.xAxis ? props.xAxis : [{
    type: 'category',
    axisTick: { show: false },
    data: xAxisData
  }]

  let yAxis: Array<YAXisOption> = props.yAxis ? props.yAxis : [{ type: 'value', minInterval: 1 }]

  let legend: LegendComponentOption = props.legend ? props.legend : {
    show: true,
    type: 'scroll',
    orient: 'horizontal',
    top: 25,
    left: 'center'
  }

  let dataZoom: Array<DataZoomComponentOption> = props.dataZoom ? props.dataZoom : []

  const options: ECOption = {
    backgroundColor: '',
    title: {
      text: props.title
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      },
      // appendToBody:true
    },
    legend: legend,
    grid: {
      left: 10,
      right: 10,
      bottom: props.dataZoom ? 40 : 10,
      containLabel: true
    },
    toolbox: {
      show: true,
      feature: {
        magicType: { type: ['line', 'bar'] },
        dataView: { readOnly: false },
        saveAsImage: {}
      }
    },
    xAxis: xAxis,
    yAxis: yAxis,
    dataZoom: dataZoom,
    dataset: {
      source: datasetSource
    },
    series: series
  }
  //开启notMerge保证配置数据不会叠加
  chart.value?.setOption(options, { notMerge: true });
}

const { chartObject, addResize, removeResize } = resize()
onMounted(() => {
  chart.value = echarts.init(chartDom.value);
  drawChart()
  //添加窗口自适应
  chartObject.value = chart.value
  addResize()
})

onBeforeUnmount(() => {
  removeResize()
  chart.value?.dispose()
})
</script>

在父组件中使用封装好的组件

<template>
  <el-row :gutter="16">
    <el-col v-for="(item, index) in chartOptionList" :key="index" :lg="12" style="margin-bottom: 10px;">
      <el-card>
        <BarChart :title="item.title" :height="item.height || 300" :options="item.chartOption" />
      </el-card>
    </el-col>
  </el-row>
</template>

<script setup lang="ts">
import BarChart from '@/components/BarChart.vue'
import type { ChartSetting } from '@/types/ChartData'
import { ref } from 'vue'
//axios api请求方法
import { getPayRecord, delicacies } from '@/services/http'

interface ChartCard {
  //标题
  title?: string,
  //高度
  height?: number,
  //图表y轴配置
  yAxis?: Array<any>
  chartOption: ChartSetting
}
const chartOptionList = ref<ChartCard[]>([
  {
    title: '图1',
    chartOption: {
      apiMethod: () => getPayRecord(),
      xProp: 'name',
      chartType: 'bar', // 设置为 'line' 以生成折线图
      seriesOption: [
        { name: '数量', encode: { x: 'name', y: 'count' } }
      ]
    }
  },
  {
    title: '图2',
    chartOption: {
      apiMethod: () => delicacies(),
      xProp: 'name',
      chartType: 'line', // 设置为 'line' 以生成折线图
      seriesOption: [
        { name: '销量', encode: { x: 'name', y: 'saleNum' } },
        { name: '好评', encode: { x: 'name', y: 'positiveReviews' } },

      ]
    }
  },
])
</script>

效果展示

在这里插入图片描述
其他:
在这里插入图片描述

// types.ChartData.ts
export interface SeriesData {
  name?: string
  data?: number[]
  color?: string
  yAxisIndex?: number
  radius?: string | string[]
  itemStyle?: any
  encode?: {
    x?: string
    y?: string
    itemName?: string
    value?: string
  }
}

export interface ChartSetting {
  //api接口方法
  apiMethod: Function
  // x轴属性名
  xProp: string
  chartType: string|undefined // 设置为 'line' 以生成折线图
  //图例配置
  seriesOption: SeriesData[]
}
// reqTypes.ts
import axiosInstance from '../utils/request'

export interface ApiResult<T> {
  code: number
  message: string
  data: T
}
export async function get<T>(url: string, params?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.get<ApiResult<T>>(url, { params })
  return response.data
}
export async function post<T>(url: string, data?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.post<ApiResult<T>>(url, data)
  return response.data
}
export async function put<T>(url: string, data?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.put<ApiResult<T>>(url, data)
  return response.data
}
export async function del<T>(url: string, params?: any): Promise<ApiResult<T>> {
  const response = await axiosInstance.delete<ApiResult<T>>(url, { params })
  return response.data
}

// utils.echarts.ts
/* @/utils/echarts.ts */
/**在ts中实现按需引入echarts 图表和组件*/
import * as Echarts from 'echarts/core'
import { BarChart, PieChart, LineChart } from 'echarts/charts'
import {
  // 标题组件
  TitleComponent,
  // 图例组件
  LegendComponent,
  // 提示框组件
  TooltipComponent,
  // 坐标系网格组件
  GridComponent,
  // 数据集组件
  DatasetComponent,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
  // 工具栏组件
  ToolboxComponent,
  // 区域缩放组件
  DataZoomComponent,
  // 原生图形元素组件
} from 'echarts/components'
// 标签自动布局、全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features'
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步 
// 使用canvas进行渲染 也可以使用 SVGRenderer 进行渲染
import { CanvasRenderer } from 'echarts/renderers'
import type {
  // 系列类型的定义后缀都为 SeriesOption
  BarSeriesOption, // 柱状图
  PieSeriesOption, //饼图
  LineSeriesOption, // 折线/面积图
} from 'echarts/charts'
import type {
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponentOption,
  GridComponentOption,
  DatasetComponentOption,
  ToolboxComponentOption,
  DataZoomComponentOption,
  GraphicComponentOption,
} from 'echarts/components'
import type { ComposeOption } from 'echarts/core'

// 注册必须的组件
Echarts.use([
  TitleComponent,
  LegendComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  ToolboxComponent,
  DataZoomComponent,
  GridComponent,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  BarChart,
  PieChart,
  LineChart,
])

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
export type ECOption = ComposeOption<
  | BarSeriesOption
  | PieSeriesOption
  | LineSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
  | ToolboxComponentOption
  | DataZoomComponentOption
  | GraphicComponentOption
>

export const echarts = Echarts
// request.ts  这里的baseUrl 是基于EasyMock进行模拟的数据
/**
 * 使用 axios.create() 创建了一个 axios 实例,并设置了基本 URL 和请求超时时间。我们还添加了请求和响应拦截器
 *
 */
import axios, {
  AxiosInstance,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'
const axiosInstance: AxiosInstance = axios.create({
  baseURL:
    'https://mock.presstime.cn/mock/6686990ecb2f4f1158f2a7b8/screen-big-sys',
  timeout: 5000,
})
// 添加请求拦截器
// 自定义请求头
axiosInstance.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 在发送请求之前做些什么
    const token = localStorage.getItem('ACCESS_TOKEN')
    if (token) {
      // 配置请求头
      config.headers.Authorization = 'Bearer ' + token
    }
    return config
  },
  (error: any) => {
    // 处理请求错误
    return Promise.reject(error)
  }
)
// 添加响应拦截器
axiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    // 对响应数据做点什么
    return response
  },
  (error: any) => {
    // 处理响应错误
    return Promise.reject(error)
  }
)
export default axiosInstance
// resize.ts
/**
 * 实现页面大小的自适应
 * ECharts提供的API会发现,它提供了一个resize 方法 重新渲染图表结合window.addEventListener
 * 功能:echarts图表自适应窗口变化封装方法
 */
//echarts图表自适应窗口变化封装方法
import { ref } from 'vue'
import { debounce } from 'lodash'

export default function () {
  //echarts图的实例
  const chartObject = ref()

  //使用防抖debounce函数,减少resize的次数
  const chartResizeHandler = debounce(() => {
    if (chartObject.value) {
      chartObject.value.resize()
    }
  }, 100)
  const initResizeEvent = () => {
    //添加窗口大小变化监听
    window.addEventListener('resize', chartResizeHandler)
  }
  const destroyResizeEvent = () => {
    //移除窗口大小变化监听
    window.removeEventListener('resize', chartResizeHandler)
  }

  const addResize = () => {
    initResizeEvent()
  }
  const removeResize = () => {
    destroyResizeEvent()
  }

  return {
    chartObject,
    addResize,
    removeResize,
  }
}
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import { createPinia } from 'pinia'
import 'element-plus/dist/index.css'
// 实现持久化标记
import { createPersistedState } from 'pinia-plugin-persistedstate'

const app = createApp(App)
const pinia = createPinia()
// 使用pinia-plugin-persistedstate插件
pinia.use(createPersistedState())

app.use(ElementPlus)
app.mount('#app')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/773706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

昇思25天学习打卡营第16天 | DCGAN生成漫画头像

这两天把minspore配置到我的电脑上了&#xff0c;然后运行就没什么问题了✨&#x1f60a; 今天学这个DCGAN生成漫画头像&#xff0c;我超级感兴趣的嘞&#x1f984;&#x1f970; GAN基础原理 这部分原理介绍参考GAN图像生成。 DCGAN原理 DCGAN&#xff08;深度卷积对抗生成…

一本超简单能用Python实现办公自动化的神书!让我轻松摆脱办公烦恼!

《超简单&#xff1a;用Python让Excel飞起来》 这本书旨在通过Python与Excel的“强强联手”&#xff0c;为办公人员提供一套高效的数据处理方案。书中还介绍了如何在Excel中调用Python代码&#xff0c;进一步拓宽了办公自动化的应用范围。 全书共9章。第1~3章主要讲解Python编…

【数据结构】06.栈队列

一、栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out)的原则。 压栈&#…

JAVA 对象存储OSS工具类(腾讯云)

对象存储OSS工具类 import com.qcloud.cos.COSClient; import com.qcloud.cos.ClientConfig; import com.qcloud.cos.auth.BasicCOSCredentials; import com.qcloud.cos.auth.COSCredentials; import com.qcloud.cos.model.ObjectMetadata; import com.qcloud.cos.model.PutObj…

洗地机品牌哪个最好用?硬核推荐五大实力爆款洗地机

在这个忙碌的时代&#xff0c;家就是我们放松的港湾&#xff0c;但要保持它的清洁与舒适常常很不容易。每天拖着疲惫的身体回家&#xff0c;还要面对地板上那些难缠的灰尘、污渍&#xff0c;真是非常让人头疼。不过&#xff0c;洗地机的出现就像是给家务清洁装上了智能引擎&…

idea中maven全局配置

配置了就不需要每次创建项目都来设置maven仓库了。 1.先把项目全关了 2. 进入全局设置 3.设置maven的仓库就可以了

一篇文章带你完全理解C语言数组

文章目录 1.一维数组的创建和初始化数组的创建1.2数组的初始化1.3 一维数组的使用1.4一维数组在内存中的存储 2.二维数组的创建和初始化2.1二维数组的创建2.2 二维数组的初始化2.3 二维数组的使用2.4 二维数组在内存中的存储 3.数组越界4.数组作为函数参数4.1 冒泡排序函数的错…

从零开始开发美颜SDK:打造属于平台的主播美颜工具

本篇文章&#xff0c;小编将从零开始&#xff0c;介绍如何打造一款属于平台的主播美颜工具。 一、需求分析 首先&#xff0c;明确开发美颜SDK的需求是至关重要的。当前市场上&#xff0c;美颜工具的功能主要包括&#xff1a; 1.实时美颜&#xff1a;磨皮、美白、瘦脸等基础功…

Static关键字的用法详解

Static关键字的用法详解 1、Static修饰内部类2、Static修饰方法3、Static修饰变量4、Static修饰代码块5、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java编程语言中&#xff0c;static是一个关键字&#xff0c;它可以用于多种上…

项目机会:4万平:智能仓,AGV,穿梭车,AMR,WMS,提升机,机器人……

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 如下为近期国内智能仓储物流相关项目的公开信息线索&#xff0c;这些项目具体信息会发布到知识星球&#xff0c;请感兴趣的球友先人一步到知识星球【智能仓储物流技术研习社】自行下载…

时钟系统框图(时钟树)解析

时钟系统框图&#xff08;时钟树&#xff09;解析 文章目录 时钟系统框图&#xff08;时钟树&#xff09;解析1、时钟树2、 4个时钟源&#xff1a;$HSI、HSE、LSI、LSE$3、PLL锁相环倍频输出4、系统时钟的来源5、Enable CSS&#xff08;时钟监视系统&#xff09;6、几个重要的时…

微信开发者工具使用

1.下载微信开发者工具 https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html 2.下载小程序项目代码 3.用微信开发者工具导入项目代码 4.npm安装依赖 5.构建 6.修改测试环境 7.清除缓存 观察切换test后&#xff0c;登录时是否test字样提醒&#xff0c;若…

使用Python+OpenCV实现姿态估计--20240705

姿态估计使用Opencv+Mediapipe来时实现 什么是Mediapipe? Mediapipe是主要用于构建多模式音频,视频或任何时间序列数据的框架。借助MediaPipe框架,可以构建令人印象深刻的ML管道,例如TensorFlow,TFLite等推理模型以及媒体处理功能。 安装命令: pip install mediapipe如果…

大模型提示词工程和落地思考

本文是一篇内部的个人分享&#xff08;已无敏感信息&#xff09; &#xff0c;目的是增加产品、开发同学对 LLM 的理解&#xff0c;以降低沟通中的阻力&#xff0c;更好推进落地。 以下经脱敏后的原文: 大模型并不神奇 很多人听到’大模型’这个词可能会觉得很神秘&#xff…

centos7固定ip

1.查看虚拟网络配置 2.修改网卡配置文件 [jiajinglocalhost ~]$ su - Password: Last login: Thu Jul 4 19:06:16 PDT 2024 on pts/0 [rootlocalhost ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE"Ethernet" PROXY_METHOD"none" BROWSER_ON…

倘若你的的B端系统如此漂亮,还担心拿不出手吗,尤其是面对客户

如果你的B端系统设计如此漂亮&#xff0c;那么通常来说&#xff0c;你不太需要担心在客户那里拿不出手。一个漂亮和易用的设计可以提升用户体验&#xff0c;增加客户对系统的满意度。 然而&#xff0c;还是有一些因素需要考虑&#xff0c;以确保你的B端系统在客户那里能够得到良…

综合项目实战--jenkins流水线

一、流水线定义 软件生产环节,如:需求调研、需求设计、概要设计、详细设计、编码、单元测试、集成测试、系统测试、用户验收测试、交付等,这些流程就组成一条完整的流水线。脚本式流水线(pipeline)的出现代表企业人员可以更自由的通过代码来实现不同的工作流程。 二、pi…

【夏季跨境】7-9月热门类目选品指南(附自用选品工具免费插件)

一、夏季跨境热门类目及品类 1 、运动与户外 随着夏季的到来&#xff0c;户外活动变得更加频繁&#xff0c;运动和户外装备的需求显著上升 自行车配件 自行车部件&#xff1a;踏板、自行车配件存储、自行车灯、自行车手机支架 整车及相关配件&#xff1a;成人/儿童自行车、…

JavaEE——计算机工作原理

冯诺依曼体系&#xff08;VonNeumannArchitecture&#xff09; 现代计算机&#xff0c;大多遵守冯诺依曼体系结构 CPU中央处理器&#xff1a;进行算术运算与逻辑判断 存储器&#xff1a;分为外存和内存&#xff0c;用于存储数据&#xff08;使用二进制存储&#xff09; 输入…

【python】OpenCV—Nighttime Low Illumination Image Enhancement

文章目录 1 背景介绍2 代码实现3 原理分析4 效果展示5 附录np.ndindexnumpy.ravelnumpy.argsortcv2.detailEnhancecv2.edgePreservingFilter 1 背景介绍 学习参考来自&#xff1a;OpenCV基础&#xff08;24&#xff09;改善夜间图像的照明 源码&#xff1a; 链接&#xff1a…