当前位置: 首页 > news >正文

Vue3 使用 echarts 甘特图(GanttChart)

Documentation - Apache ECharts

可自定义设置以下属性

  • 甘特图数据(ganttData),类型:Gantt[],必传,默认 []
  • 容器宽度(width),类型:number | string,默认 ‘100%’
  • 容器高度(height),类型:number | string,默认 ‘100%’
  • 主题色(themeColor),类型: string,默认 ‘#1677ff’
  • 状态映射表(statusMap),类型: Status[],默认 []

type Gantt

  • 名称(name),类型:string,必传
  • 开始时间(start),类型:string | number | Date,必传
  • 结束时间(end),类型:string | number | Date,必传
  • 状态值(status),类型:number | string,用于着色

type Status

  • 状态值(value),类型:number | string,必传
  • 状态名(label),类型:string,必传
  • 状态颜色(color),类型:string,必传

效果如下图:echarts@6.0.0

在这里插入图片描述

在线预览

安装插件

pnpm add echarts

创建甘特图组件GanttChart.vue

<script setup lang="ts">
import { ref, useTemplateRef, onMounted, onBeforeUnmount, watch, computed } from 'vue'
import { useResizeObserver } from '../utils'
import * as echarts from 'echarts/core'
import { TooltipComponent, GridComponent, LegendComponent } from 'echarts/components'
import { CustomChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers'echarts.use([TooltipComponent, GridComponent, LegendComponent, CustomChart, CanvasRenderer])const chartRef = useTemplateRef('chartRef')
const myChart = ref<any>()
let option: anyinterface Gantt {name: string // 名称start: string | number | Date // 开始时间end: string | number | Date // 结束时间status: number | string // 状态值,用于着色
}
interface Status {value: number | string // 状态值label: string // 状态名color: string // 状态颜色
}
interface Props {ganttData?: Gantt[] // 数据width?: string | number // 容器宽度height?: string | number // 容器高度themeColor?: string // 主题色statusMap?: Status[] // 状态映射表
}
const props = withDefaults(defineProps<Props>(), {ganttData: () => [],width: '100%',height: '100%',themeColor: '#1677FF',statusMap: () => []
})
const chartWidth = computed(() => {if (typeof props.width === 'number') {return `${props.width}px`}return props.width
})
const chartHeight = computed(() => {if (typeof props.height === 'number') {return `${props.height}px`}return props.height
})
watch(() => props.ganttData,() => {myChart.value && myChart.value.setOption(buildOption(), true)},{ deep: true }
)
// 统一转换为时间戳格式
function toTimestamp(value: string | number | Date): number {if (typeof value === 'number') return valueif (value instanceof Date) return value.getTime()// 替换空格为 'T' 以提升跨浏览器解析稳定性const normalized = value.replace(' ', 'T')const t = Date.parse(normalized)return isNaN(t) ? new Date(value).getTime() : t
}
// 处理数据:输出 y 轴分类与自定义系列的数据
function getSeriesData() {const yAxisData = props.ganttData.map((item) => item.name)const data = props.ganttData.map((item, index) => {return {value: [index, // 类别索引toTimestamp(item.start), // 开始时间戳toTimestamp(item.end), // 结束时间戳item.status],name: item.name}})return { yAxisData, data }
}
// 根据状态值获取对应的颜色
function getStatusColor(value: string | number): string {const statusItem = props.statusMap.find((status) => String(status.value) === String(value))const color = statusItem ? statusItem.color : '#5470c6' // 默认颜色return color
}
function renderGanttBar(params: any, api: any) {const categoryIndex = api.value(0)const startCoord = api.coord([api.value(1), categoryIndex])const endCoord = api.coord([api.value(2), categoryIndex])const barHeight = Math.max(api.size([0, 1])[1] * 0.6, 2)const rectShape = {x: startCoord[0],y: startCoord[1] - barHeight / 2,width: Math.max(endCoord[0] - startCoord[0], 0),height: barHeight}const shape = echarts.graphic.clipRectByRect(rectShape, {x: params.coordSys.x,y: params.coordSys.y,width: params.coordSys.width,height: params.coordSys.height})if (shape) {// 为矩形添加圆角;(shape as any).r = 2}// 获取状态值const statusValue = api.value(3)// 获取对应的颜色const color = getStatusColor(statusValue)return { // 返回自定义矩形元素type: 'rect',shape,style: {fill: color,stroke: color}}
}
// 构建图表配置项 option
function buildOption() {const { yAxisData, data } = getSeriesData()option = {grid: {top: 0,left: 0,right: 0,bottom: 42,containLabel: true},legend: {orient: 'horizontal',left: 'center',bottom: 0,itemGap: 18,textStyle: {fontWeight: 400,fontSize: 14,color: '#333',lineHeight: 22},data: props.statusMap.map((status: Status) => {return status.label})},tooltip: {trigger: 'item',formatter: (params: any) => {// console.log('params', params)const start = params.value[1]const end = params.value[2]const format = (timestamp: number) => {return echarts.time.format(timestamp, '{HH}:{mm}:{ss}', false)}const piecesMap = new Map(props.statusMap.map((status: Status) => [String(status.value), status.label]))const statusLabel = piecesMap.get(String(params.value[3])) || ''return `${params.name}<br/>${format(start)} - ${format(end)}${statusLabel ? `<br/>状态:${params.marker} ${statusLabel}` : ''}`}},xAxis: {type: 'time',position: 'top',splitLine: {show: true,lineStyle: {type: 'dashed',color: '#ccc'}},axisLabel: {color: '#333',fontWeight: 400,fontSize: 14,lineHeight: 22,align: 'center',showMinLabel: true,showMaxLabel: true,formatter: (value: number) => {// return echarts.time.format(value, '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}', false)return echarts.time.format(value, '{HH}:{mm}', false)}}},yAxis: {type: 'category',inverse: true,data: yAxisData,axisLabel: {color: 'rgba(0, 0, 0, 0.88)',fontWeight: 500,fontSize: 14,lineHeight: 22}},series: props.statusMap.map((status) => ({name: status.label,type: 'custom',renderItem: renderGanttBar, // 以 Function 形式提供图形渲染的逻辑encode: { x: [1, 2], y: 0 }, // 定义 data 的哪个维度被编码成什么itemStyle: { // 图形样式color: getStatusColor(status.value)},data: data.filter((item) => String(item.value[3]) === String(status.value))}))}return option
}
function initChart() {myChart.value = echarts.init(chartRef.value)myChart.value.setOption(buildOption())
}
function showLoading (config: any) {myChart.value && myChart.value.showLoading('default', { text: '', color: props.themeColor, ...config }) // 显示加载动画效果
}
function hideLoading () {myChart.value && myChart.value.hideLoading() // 隐藏动画加载效果
}
// 监听图表容器尺寸变化,重新初始化图表
useResizeObserver(chartRef, () => {requestAnimationFrame(() => {myChart.value && myChart.value.resize()})
})
onMounted(() => {initChart()
})
onBeforeUnmount(() => {myChart.value && myChart.value.dispose() // 销毁图表实例
})
defineExpose({showLoading,hideLoading
})
</script>
<template><divclass="chart-container"ref="chartRef":style="`--chart-width: ${chartWidth}; --chart-height: ${chartHeight};`"></div>
</template>
<style lang="less" scoped>
.chart-container {width: var(--chart-width);height: var(--chart-height);
}
</style>

在要使用的页面引入

<script setup lang="ts">
import GanttChart from './GanttChart.vue'
import { useTemplateRef, ref, onMounted } from 'vue'
const ganttRef = useTemplateRef('ganttRef')
const ganttData = ref<any[]>([])
onMounted(() => {getGanttData()
})
function getGanttData () { // 模拟接口调用ganttRef.value.showLoading()setTimeout(() => {ganttData.value.push({name: '任务一',start: '2025-08-12 00:00:00',end: '2025-08-12 04:00:00',status: '1'},{name: '任务二',start: '2025-08-12 04:00:00',end: '2025-08-12 05:00:00',status: '2'},{name: '任务三',start: '2025-08-12 05:00:00',end: '2025-08-12 06:00:00',status: '3'},{name: '任务四',start: '2025-08-12 06:00:00',end: '2025-08-12 08:00:00',status: '4'},{name: '任务五',start: '2025-08-12 08:00:00',end: '2025-08-12 13:00:00',status: '5'})ganttRef.value.hideLoading()}, 1500)
}
const statusMap = [{ value: '1', label: '启动', color: '#5470c6' },{ value: '2', label: '运行', color: '#91cc75' },{ value: '3', label: '等待', color: '#fac858' },{ value: '4', label: '成功', color: '#ee6666' },{ value: '5', label: '失败', color: '#73c0de' },{ value: '6', label: '停止', color: '#3ba272' }
]
</script>
<template><div><h1>GaugeChart 参考文档</h1><ul class="m-list"><li><a class="u-file" href="https://echarts.apache.org/handbook/zh/get-started" target="_blank">使用手册</a></li><li><a class="u-file" href="https://echarts.apache.org/handbook/zh/basics/import" target="_blank">在项目中引入 ECharts</a></li><li><a class="u-file" href="https://echarts.apache.org/zh/builder.html" target="_blank">ECharts 在线定制</a></li></ul><GanttChartref="ganttRef":gantt-data="ganttData":status-map="statusMap":height="500"/></div>
</template>
http://www.lryc.cn/news/619316.html

相关文章:

  • Java -- Vector底层结构-- ArrayList和LinkedList的比较
  • C++主流string的使用
  • 工业元宇宙:迈向星辰大海的“玄奘之路”
  • C++ 类和对象4---(初始化列表,类型转化,static成员)
  • nuxt相比于vue的优点
  • java-泛型接口
  • C++多态:理解面向对象的“一个接口,多种实现”
  • 智能算法流程图在临床工作中的编程视角系统分析
  • 【算法】位运算经典例题
  • 论“证明的终点”:从“定义域 = 正确”看西方体系的自证困境
  • 模式设计:策略模式及其应用场景
  • 全面深入-JVM虚拟机
  • 神经网络的核心组件解析:从理论到实践
  • Deep Agents:用于复杂任务自动化的 AI 代理框架
  • 什么是HTTP的无状态(举例详解)
  • python的游戏评级论坛系统
  • 面试实战 问题三十 HTTP协议中TCP三次握手与四次挥手详解
  • 字体优化:Web 排版最佳实践
  • 【cs336学习笔记】[第5课]详解GPU架构,性能优化
  • Debian 网络服务管理的深度解析:传统与现代工具的碰撞
  • 三方相机问题分析六:【没用相机,诡异的手电筒不可使用】下拉状态栏,手电筒置灰,无法打开,提提示相机正在使用
  • YOLOv1 到 YOLOv2 模型训练过程全解析
  • Java面试宝典:ZGC
  • 大模型能力评测方式很多?
  • 《Python学习之基础语法2:掌握程序流程控制的艺术》
  • RTCP详解
  • 【安卓,问题记录】ImageView 在布局顺序上位于 Button 上方,却出现图像内容被 Button 遮挡
  • [激光原理与应用-263]:理论 - 几何光学 - 光纤通信:以光为媒的现代通信基石
  • MySQL宝典
  • html原生js文件使用javascript-obfuscator插件进行加密处理