
说实话我第一次在需求单上看到“Highcharts 甘特图要带里程碑和进度条”的时候第一反应是甘特图不是得用专门的大控件吗但翻了一圈官方配置文档才发现Highcharts 自身就带 Gantt 模块底层是 xrange 系列几行配置就能把任务条、里程碑、进度全部画出来而且是 SVG 渲染放大缩小、导出图片都顺带解决了。这篇文章就把我实际配置过程中用到的任务定义、里程碑创建、进度条显示以及官方文档里对应的关键参数全部拆开讲一遍新手照着抄就行老手也能拿来当速查表。1. 在动手配置之前先搞懂Highcharts甘特图的底层逻辑1.1 甘特图的本质xrange 系列 时间刻度很多同学以为甘特图是 Highcharts 里的一个独立图表类型其实不是。官方文档里甘特图被归类在 Chart and Series Types 下的 Gantt Chart它的内部实现就是 xrange 系列横轴是 datetime 时间轴纵轴是 category 分类轴每一个任务就是一个“有开始时间、有结束时间”的矩形条。理解了这一点你在配置时就不会被各种新名词绕晕因为 xrange 系列的所有参数比如颜色、边框、数据标签、堆叠方式几乎都能直接用在甘特图上。我建议你在翻配置文档之前先打开官方 demo 看一眼任务条是横向的方块任务名称显示在左侧时间越长方块越宽两头的圆角、进度条覆盖、依赖箭头都是基于这个矩形扩展出来的。官方文档描述甘特图时反复提到你只需要“start、end、y 三个字段”这就说明它的数据结构天然简单复杂的是你对业务任务怎么建模。1.2 数据结构的核心一行一个任务时间区间决定柱子长度甘特图的数据项本质上就是一组对象数组每个对象代表一个任务。最核心的字段是这四个id任务唯一标识后面做里程碑、依赖关系都要靠它name任务名称显示在 y 轴分类或者柱子上start/end任务开始和结束的毫秒时间戳注意是毫秒不是秒y任务所在的行索引从 0 开始对应 yAxis 上的分类位置。我第一次配置的时候就是被y这个字段坑了一下以为它表示时间结果它是行号。你甚至可以不写y官方会自动按顺序往下排但一旦需要指定任务放在第几行就必须显式给值。如果任务数量多建议在数据层就把行号和任务顺序对应好不然图表上任务会乱序排列。1.3 为什么说“配置”比“开发”重要官方配置文档毛发般长但真正高频用到的选项不超过 20 个。我在实际项目里最快的出图方式不是一行一行读 API而是复制官方 demo 里最接近需求的例子然后改数据源和颜色。甘特图这种图表难点从来不是技术而是数据建模哪些任务算一行、里程碑时间点怎么提取、进度百分比怎么折算。把这些想清楚配置就是填表。2. 任务配置从最基础的“一行任务”开始2.1 最小可用的任务配置模板先给一份可以直接跑起来的最小示例你把它贴到 HTML 里引入 Highcharts Gantt 模块就行!DOCTYPE html html head meta charsetUTF-8 / script srchttps://code.highcharts.com/highcharts.js/script script srchttps://code.highcharts.com/gantt/modules/gantt.js/script /head body div idchart styleheight: 300px;/div script Highcharts.ganttChart(chart, { title: { text: 基础任务配置 }, xAxis: { type: datetime }, series: [{ name: 计划, data: [{ id: task-1, name: 需求分析, start: Date.UTC(2025, 4, 12), end: Date.UTC(2025, 4, 15), y: 0 }] }] }); /script /body /html这里有个细节Highcharts.ganttChart是官方封装好的构造函数等价于Highcharts.chart(chart, { chart: { type: xrange } })。如果你在别人的项目里看到直接用chart配series.type xrange那是更底层的写法效果一样只是少了 gantt 相关的默认设置。2.2 四个核心字段的语义要一次性搞清Date.UTC(2025, 4, 12)返回的是毫秒时间戳月份从 0 开始计数所以 4 代表五月这是最容易踩的坑之一。如果你在业务代码里生成时间戳直接new Date(2025-05-12).getTime()或者后端返回的毫秒值都可以只要保证是毫秒。y字段代表的是行号很多人把y误解成“YYYY 年份”。它就等于 yAxis 分类数组的下标。比如 yAxis 的 categories 是[任务A, 任务B, 任务C]那么y: 1对应的就是“任务B”那一行。如果 yAxis 不显式设置 categoriesHighcharts 会从数据里自动提取 name 作为分类。2.3 任务颜色、名称、数据标签的自定义单个任务颜色直接写在数据项上优先级高于 series 级配置{ id: task-1, name: 需求分析, start: Date.UTC(2025, 4, 12), end: Date.UTC(2025, 4, 15), y: 0, color: #7cb5ec, borderColor: #3f7fb5, borderRadius: 4, dataLabels: { enabled: true, format: {point.name} } }borderRadius控制任务条圆角默认是 0但官方 demo 里通常给个 2~4视觉上更柔和不生硬。dataLabels的format可以用{point.name}、{point.start:%Y-%m-%d}这些模板变量start 和 end 是时间戳直接格式化很方便。我一般会把开始日期放在任务条左侧结束日期放在右侧来回对照日期就不用悬停鼠标了。3. 里程碑和进度条创建标题里的两大核心需求3.1 里程碑一个时间点的特殊任务里程碑在甘特图里不是一个新图表类型本质就是一个start end的任务点再加上一个milestone: true开关。官方文档对此的表述很简单设置milestone: true后这个点会渲染成一个菱形而不是矩形条。我把最常用的里程碑配置整理成下面这样{ id: milestone-1, name: 版本发布, start: Date.UTC(2025, 5, 1), end: Date.UTC(2025, 5, 1), y: 2, milestone: true, symbol: diamond, color: #f7a35c, dataLabels: { enabled: true, verticalAlign: top, y: -12, format: {point.name} } }这里有几个容易忽略的细节。第一symbol支持diamond、circle、square等常见形状默认是菱形。如果你想让里程碑更像“旗帜”可以换成triangle也可以自己传一段 SVG path 路径。第二里程碑的标签默认会压在点和任务条中间我用verticalAlign: top加y: -12把它顶上去视觉上更清楚。第三milestone: true和symbol是可以组合的就算你忘了写milestone: true只要 start end 且设置了 symbol也能画成一个点但官方推荐用 milestone 开关因为很多交互特性和辅助线会跟着生效。3.2 进度条progress.amount 不是百分比是小数进度条是甘特图里最容易出问题的地方因为官方文档给出的参数是progress.amount数值范围是 0 到 1而不是 0 到 100。很多人第一次配置时写成amount: 80结果进度条直接变成全灰没有任何覆盖效果就是这个原因。标准的进度条配置如下{ id: task-2, name: 开发联调, start: Date.UTC(2025, 4, 16), end: Date.UTC(2025, 4, 20), y: 1, progress: { amount: 0.6, style: { fill: #90ed7d, opacity: 0.6 } } }amount表示完成度style.fill是进度覆盖层颜色opacity是透明度。我个人的习惯是任务条底色用浅灰蓝色进度条用绿色透明度设 0.4~0.6这样即使进度覆盖了 90%底下的任务条边界线依然隐约可见。如果你做的是项目看板可以再配合一个自定义字段custom.done在数据加载的时候把百分比换算成 0~1 的小数再赋给amount不要在前端到处写死数字。3.3 把任务、里程碑、进度条组合到一个图里组合才是真实业务的常态。我的习惯是让常规任务设置progress关键节点设置milestone排期路径用dependency串联。下面这段代码是组合后的核心配置你可以直接复制到上一节的 HTML 里看效果Highcharts.ganttChart(chart, { title: { text: 产品迭代甘特图 }, xAxis: { type: datetime, min: Date.UTC(2025, 4, 12), max: Date.UTC(2025, 5, 2) }, yAxis: { type: category, categories: [需求, 开发, 测试, 发布] }, series: [{ name: 迭代计划, data: [{ id: req, name: 需求评审, start: Date.UTC(2025, 4, 12), end: Date.UTC(2025, 4, 14), y: 0, progress: { amount: 1 } }, { id: dev, name: 功能开发, start: Date.UTC(2025, 4, 14), end: Date.UTC(2025, 4, 20), y: 1, dependency: req, progress: { amount: 0.65 } }, { id: test, name: 测试用例通过, start: Date.UTC(2025, 4, 22), end: Date.UTC(2025, 4, 22), y: 2, milestone: true, dependency: dev }, { id: pub, name: 发布上线, start: Date.UTC(2025, 4, 25), end: Date.UTC(2025, 4, 28), y: 3, dependency: test, progress: { amount: 0.2 } }] }] });这里有个组合技巧dependency指向的是另一个任务的id它会在两个任务之间画一条带箭头的连接线默认在任务条顶部。如果你不想让连接线太乱可以在 series 级设置connector: { lineColor: #999, lineWidth: 1 }统一调整样式。4. 依赖关系、时间轴刻度与任务分组的进阶玩法4.1 依赖箭头用 dependency 代替手工画线很多甘特图工具需要单独维护一套依赖关系数据但 Highcharts 直接在数据项里加dependency字段就完事了。官方文档里的解释是dependency的值可以是字符串或字符串数组字符串就是目标任务的 id。我最常用的依赖配置是把它和里程碑配合比如“联调完成”这个里程碑依赖“开发任务”的结束箭头会自动从任务终点连到里程碑的菱形上非常直观。如果你需要多条依赖就写数组例如dependency: [task1, task2]。有一点要注意如果依赖指向的 id 在数据里不存在图表非但不报错还会在顶部画一根悬空的线那个排查起来相当迷惑所以数据清洗时务必要做 id 去重和存在性校验。4.2 时间轴刻度细化tickInterval 和当前时间线甘特图的横轴默认会按月或者按天自动切分但在排期密集的场景里我建议显式控制刻度间隔。官方文档里 xAxis 是普通的 datetime 轴所以所有时间轴参数都有效xAxis: { type: datetime, min: Date.UTC(2025, 4, 12), max: Date.UTC(2025, 5, 2), tickInterval: 24 * 3600 * 1000, // 一天一格 gridLineWidth: 1, labels: { format: {value:%m-%d} } }tickInterval单位是毫秒一天就是86400000。我还喜欢加一条“今天”参考线类似看板上的“当前时间线”xAxis: { plotLines: [{ value: Date.now(), color: #ff8080, dashStyle: dash, width: 2, label: { text: 今天, align: left } }] }这条红色虚线非常实用尤其在看板投屏的时候整个团队一眼就能看到哪些任务延期了。注意plotLines的value是一个时间戳如果你要求每天自动更新这个值需要在渲染前由 JS 动态生成。4.3 任务分组多个 series 还是单 series 加行号甘特图的任务分组有两种常见做法。第一种是单 series、多行数据用y指定行位置yAxis 的 categories 里放项目节点名称第二种是多个 series每个 series 代表一个团队或一个项目阶段。我推荐优先使用单 series。原因很简单Highcharts 的图例和数据联动机制在单 series 下更可控而且多 series 会让依赖箭头、进度条样式变得混乱。多 series 的场景更适合“对比两个独立计划”比如“计划版 vs 实际版”而不是“同一计划里的分组”。如果你只是想让某几个任务在视觉上分成一组可以直接把任务条颜色设为同一个色系或者用 yAxis 的categories里加入“分组标题行”。4.4 关键参数速查表需求参数位置注意事项任务条长度start/end数据项毫秒时间戳月份从 0 开始任务所在行y数据项对应 yAxis.categories 的下标里程碑milestone: true数据项建议同时设置symbol进度条progress.amount数据项0~1 之间的小数进度条颜色progress.style.fill数据项配合 opacity 使用效果更佳依赖箭头dependency数据项值是目标任务的 id当前时间线xAxis.plotLines[0].value轴配置value 是毫秒时间戳刻度间隔tickInterval轴配置一天用 864000005. 实战一个完整的甘特图配置示例5.1 完整 HTML 代码可以直接复制运行为了让你能直接跑起来的完整例子我把上面的组合配置补成一个页面。这里用的是官方 CDN你在内网环境的话记得把 gantt 模块文件下载下来放到自己的静态资源目录。!DOCTYPE html html head meta charsetUTF-8 / titleHighcharts 甘特图实例任务、里程碑、进度条/title script srchttps://code.highcharts.com/highcharts.js/script script srchttps://code.highcharts.com/gantt/modules/gantt.js/script /head body div idgantt stylemax-width: 1200px; height: 400px; margin: 0 auto;/div script const now Date.now(); Highcharts.ganttChart(gantt, { title: { text: 新产品版本迭代排期 }, subtitle: { text: 示例任务 里程碑 进度条 }, xAxis: { type: datetime, min: Date.UTC(2025, 4, 12), max: Date.UTC(2025, 5, 5), tickInterval: 24 * 3600 * 1000, plotLines: [{ value: now, color: #ff6666, dashStyle: dash, width: 2, label: { text: 当前时间 } }] }, yAxis: { type: category, categories: [需求, 设计, 开发, 测试, 发布] }, legend: { enabled: true }, series: [{ name: 版本迭代, connector: { lineColor: #cccccc, lineWidth: 1 }, data: [{ id: requirement, name: 需求收集与评审, start: Date.UTC(2025, 4, 12), end: Date.UTC(2025, 4, 14), y: 0, progress: { amount: 1, style: { fill: #90ed7d } } }, { id: design, name: UI 设计, start: Date.UTC(2025, 4, 14), end: Date.UTC(2025, 4, 16), y: 1, dependency: requirement, progress: { amount: 0.8, style: { fill: #90ed7d, opacity: 0.5 } } }, { id: dev, name: 前后端开发, start: Date.UTC(2025, 4, 16), end: Date.UTC(2025, 4, 23), y: 2, dependency: design, progress: { amount: 0.55, style: { fill: #ffbf5e, opacity: 0.6 } } }, { id: test-milestone, name: 提测通过, start: Date.UTC(2025, 4, 25), end: Date.UTC(2025, 4, 25), y: 3, dependency: dev, milestone: true, symbol: diamond, color: #f7a35c, dataLabels: { enabled: true, verticalAlign: top, y: -15, format: {point.name} } }, { id: release, name: 发布上线, start: Date.UTC(2025, 5, 1), end: Date.UTC(2025, 5, 4), y: 4, dependency: test-milestone, progress: { amount: 0.1, style: { fill: #90ed7d, opacity: 0.4 } } }] }] }); /script /body /html5.2 逐步解释关键配置xAxis.plotLines里的now我建议不要在组件 mounted 的时候一次性写死而是放到定时器里隔一段时间重绘一次。对于长期挂着的看板页面这个“当前时间线”如果不更新过两天就成“过去时间线”了误导性很强。dependency的箭头方向自动从“前序任务结束”指向“后续任务开始”。如果前序任务还没画完箭头会出现在后续任务的起点左侧这与直觉稍有出入但看久了就习惯了。里程碑的dataLabels配置在数据项上这样只对这一个里程碑起作用不会影响其他任务如果你希望所有里程碑都统一样式就在 series 级配一个dataLabels然后点级通过style覆盖。5.3 运行效果和后续调整思路跑起来以后你会看到五条任务横向排开开发任务的进度条是橘色的提测通过是一个菱形里程碑发布上线是绿色进度条但只有 10%。这个视觉效果已经接近商业项目看板了。接下来你要改的就是两部分一是把Date.UTC换成真实的接口字段二是在chart.events.load里做一次数据清洗比如把后端返回的“五月初”这类字符串日期统一转换成毫秒时间戳。如果你还需要更细的排期可以把 yAxis 改成多级分类不过 Highcharts 原生不支持树形 y 轴分组你可以利用任务名字加空格缩进或者在地层塞多个 series 并用linkedTo控制图例这是另一个话题了。6. 高频问题排查速查表与我的踩坑经验6.1 里程碑不显示或显示成了矩形条优先级最高的检查项就是start和end是否完全相等。官方判定里程碑其实不只是看milestone: true它会检查时间段长度如果长度为 0 而你又没设置里程碑就画成一个很窄的矩形视觉上像一条竖线。我的建议是里程碑的start和end必须用同一个时间戳变量赋值千万别从数据库里取了个 2025-05-01 和 2025-05-01 23:59:59那样画出来就是一个非常短的小横条排查的时候很难发现。另一个原因是 symbol 被 series 级的 marker 覆盖了。xrange 系列的 marker 选项默认开启如果你全局设置了marker: { symbol: rect }里程碑的菱形就会被覆盖。遇到这种问题先检查 series 级 marker 配置再检查主题插件。6.2 进度条百分比对不上的两个坑第一个坑就是amount用了 0~100 的数值第二个坑是你给整个 series 设置了统一进度导致点级配置失效。网络上有一些老旧的示例代码在 series 级写progress: { amount: 0.5 }它确实会让该 series 所有任务都显示 50% 进度看起来像是默认值。如果你想逐任务着色加进度series 级不要写 progress全部放在数据项里。如果进度条颜色偏淡多半不是透明度问题而是因为某个主题 CSS 给rect加了一层淡色遮罩。我碰到过一次浏览器控制台里看元素进度条填充色明明是#90ed7d但显示出来却发灰最后发现是项目全局样式里给 SVG 的rect加了opacity: 0.8。6.3 时间轴错乱任务位置对不上日期格式问题是最常见的元凶。后端如果返回的是 ISO 字符串比如2025-05-12T00:00:00Z在 JavaScript 里直接用new Date(str).getTime()一般没事但如果你收到的是2025-05-12 00:00:00这种中划线格式在部分老版本的 Safari 里解析会失败返回 NaN。解决方案是统一在数据层做格式化能选毫秒时间戳就选毫秒不能选就手动用正则拆分年月日再走Date.UTC。还有一种错乱是xAxis.min和xAxis.max设置太早把任务数据挤出了可视区间。我在第一版配置时把min设成了 5 月 1 日结果 5 月 12 日开始的任务全部挤在最右边还以为是数据结构错了。后来看官方文档才想起datetime 轴的min也是毫秒时间戳我在配置里直接传了字符串。6.4 模块加载失败图表空白甘特图不是 Highcharts 基础包的一部分。如果你只引了highcharts.js不管怎么配置都只会画出一个空容器控制台也没有明显的报错。官方给出的模块路径是gantt/modules/gantt.js记得要放在highcharts.js之后加载。如果用了打包工具就在 import 时先引highcharts再引highcharts/modules/gantt然后执行Highcharts.ganttChart前确认模块已经注册。还有一个小坑如果你的图表是在弹窗或者隐藏 tab 里初始化的容器宽度可能是 0导致整个甘特图渲染出来是一根竖线。Highcharts 有一个chart.reflow()方法在容器可见后调用一下就行或者初始化时给容器一个明确的固定高度和宽度。6.5 问题排查速查表现象可能原因解决办法里程碑变成小横条start 和 end 不是同一个时间戳用同一个时间变量赋值里程碑变成了矩形series.marker 覆盖了 symbol检查 series 级 marker 配置进度条全灰amount 写成了 80 而不是 0.8将百分比除以 100进度条颜色发淡全局 CSS 设置了 rect 透明度用浏览器检查元素定位样式来源任务全部挤在右侧xAxis.min 设置了错误的时间串把 min 改成毫秒时间戳或去掉图表空白只引入了 highcharts.js没引 gantt 模块补充 gantt/modules/gantt.js甘特图只显示竖线容器初始化时宽度为 0调用 chart.reflow() 或延迟渲染依赖箭头悬空dependency 指向了不存在的 id数据清洗时校验 id我自己的体会是Highcharts 甘特图最花时间的不是配置本身而是把后台接口的杂乱时间数据“捋”成它要的三个字段start、end、y。只要数据模型稳了里程碑和进度条都是手到擒来的事。最后再分享一个小技巧别一上来就追求花哨的样式先把官方 demo 跑通再逐步加依赖、加里程碑、加进度条每一步肉眼可见地变化这样即使出了错也能立刻知道是哪一个配置引入的问题。