Vue 3项目实战:用gantt-elastic插件从零搭建一个可交互的甘特图(附完整配置代码)
2026/4/15 12:00:26 网站建设 项目流程

Vue 3项目实战:用gantt-elastic插件从零搭建可交互甘特图

在项目管理工具中,甘特图因其直观的时间轴展示方式成为进度管理的标配。但现有解决方案要么功能臃肿,要么定制性不足。gantt-elastic作为轻量级开源库,以其灵活的配置和响应式设计脱颖而出。本文将带你从零实现一个能与后端数据联动的动态甘特图,解决官方文档缺失的核心配置问题。

1. 环境搭建与基础配置

1.1 初始化Vue 3项目

使用Vite创建项目能获得更快的构建速度:

npm create vite@latest vue-gantt-demo --template vue-ts cd vue-gantt-demo npm install gantt-elastic dayjs

1.2 基础组件结构

创建GanttChart.vue单文件组件,包含以下核心结构:

<template> <div class="gantt-container"> <gantt-elastic :tasks="processedTasks" :options="ganttOptions" @tasks-changed="handleTaskUpdate" /> </div> </template> <script setup lang="ts"> import { ref, computed } from 'vue' import GanttElastic from 'gantt-elastic' import dayjs from 'dayjs' </script>

2. 数据模型与动态绑定

2.1 任务数据结构设计

甘特图需要包含层级关系的时间数据,建议采用如下格式:

interface GanttTask { id: string | number label: string start: Date | string end: Date | string duration?: number // 毫秒单位 percent?: number type: 'project' | 'task' | 'milestone' parentId?: string | number style?: { base: { fill: string stroke: string } } }

2.2 后端API数据转换

实际项目中需要将API数据转换为gantt-elastic识别的格式:

const transformAPIData = (apiData) => { return apiData.map(item => ({ id: item.taskId, label: item.taskName, start: dayjs(item.plannedStart).format('YYYY-MM-DD'), end: dayjs(item.plannedEnd).format('YYYY-MM-DD'), percent: item.progress * 100, type: item.isMilestone ? 'milestone' : 'task' })) }

3. 核心配置详解

3.1 时间轴配置

通过options对象控制甘特图的显示精度和样式:

const ganttOptions = ref({ times: { timeScale: 24 * 60 * 60 * 1000, // 按天显示 timeZoom: 18, // 缩放级别(1-20) }, row: { height: 24, // 行高 }, calendar: { workingDays: [1, 2, 3, 4, 5], // 工作日设置 } })

3.2 任务列表列配置

自定义左侧任务列表的显示列:

const taskListColumns = [ { id: 'name', label: '任务名称', value: 'label', width: 200, expander: true }, { id: 'owner', label: '负责人', value: 'user', width: 100 }, { id: 'progress', label: '进度', value: task => `${task.percent}%`, width: 80 } ]

4. 高级交互实现

4.1 拖拽更新回调

处理用户拖拽任务条后的数据更新:

const handleTaskUpdate = (newTasks) => { const changedTasks = newTasks.filter(t => t.start !== originalTasks.value.find(ot => ot.id === t.id)?.start ) if (changedTasks.length) { updateTaskDates(changedTasks).then(() => { fetchLatestData() }) } }

4.2 动态样式控制

根据任务状态应用不同样式:

const processedTasks = computed(() => { return rawTasks.value.map(task => ({ ...task, style: task.percent >= 100 ? successStyle : dayjs(task.end).isBefore(dayjs()) ? overdueStyle : defaultStyle })) })

5. 性能优化技巧

5.1 虚拟滚动配置

处理大规模数据时的性能优化:

const performanceOptions = { maxRows: 50, // 同时显示的最大行数 table: { maxWidth: 600 // 任务列表最大宽度 } }

5.2 按需渲染策略

结合Vue的shallowRef减少不必要的响应式开销:

import { shallowRef } from 'vue' const heavyTaskData = shallowRef([]) // 大数据量更新时 const updateBigData = (newData) => { heavyTaskData.value = [...newData] }

6. 常见问题解决方案

6.1 时间显示异常处理

确保时间数据格式统一:

const normalizeDates = (tasks) => { return tasks.map(task => ({ ...task, start: dayjs(task.start).format('YYYY-MM-DD'), end: dayjs(task.end).format('YYYY-MM-DD') })) }

6.2 中文 locale 配置

解决默认显示英文的问题:

const localeConfig = { weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"], months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"] }

在最近的一个电商后台系统中,我们采用这套方案成功实现了2000+任务的流畅渲染。关键点在于合理设置timeZoom级别和使用shallowRef控制响应式数据量。当遇到横向滚动卡顿时,将timeScale从小时调整为天级别即可显著改善性能。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询