Highcharts甘特图基本用法(highcharts-gantt.js)
参考官方文档:
https://www.highcharts.com/docs/gantt/getting-started-gantt
https://www.highcharts.com/demo/gantt/project-management
https://www.hcharts.cn/demo/gantt
链接在下面按需引入
https://code.highcharts.com/gantt/highcharts-gantt.js
https://code.highcharts.com/highcharts.js
https://code.highcharts.com/gantt/modules/gantt.js
JS初始化举例:(这里只是初始化可以跳过直接看下面例子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓)
const day = 24 * 36e5,
today = Math.floor(Date.now() / day) * day;
const options = {
chart: {
plotBackgroundColor: 'rgba(128,128,128,0.02)',
plotBorderColor: 'rgba(128,128,128,0.1)',
plotBorderWidth: 1
},
plotOptions: {
series: {
borderRadius: '50%',
connectors: {
dashStyle: 'ShortDot',
lineWidth: 2,
radius: 5,
startMarker: {
enabled: false
}
},
groupPadding: 0,
dataLabels: [{
enabled: true,
align: 'left',
format: '{point.name}',
padding: 10,
style: {
fontWeight: 'normal',
textOutline: 'none'
}
}, {
enabled: true,
align: 'right',
format: '{#if point.completed}{(multiply ' +
'point.completed.amount 100):.0f}%{/if}',
padding: 10,
style: {
fontWeight: 'normal',
textOutline: 'none',
opacity: 0.6
}
}]
}
},
series: [{
name: 'Offices',
data: [{
name: 'New offices',
id: 'new_offices',
owner: 'Peter'
}, {
name: 'Prepare office building',
id: 'prepare_building',
parent: 'new_offices',
start: today - (2 * day),
end: today + (6 * day),
completed: {
amount: 0.2
},
owner: 'Linda'
}, {
name: 'Inspect building',
id: 'inspect_building',
dependency: 'prepare_building',
parent: 'new_offices',
start: today + 6 * day,
end: today + 8 * day,
owner: 'Ivy'
}, {
name: 'Passed inspection',
id: 'passed_inspection',
dependency: 'inspect_building',
parent: 'new_offices',
start: today + 9.5 * day,
milestone: true,
owner: 'Peter'
}, {
name: 'Relocate',
id: 'relocate',
dependency: 'passed_inspection',
parent: 'new_offices',
owner: 'Josh'
}, {
name: 'Relocate staff',
id: 'relocate_staff',
parent: 'relocate',
start: today + 10 * day,
end: today + 11 * day,
owner: 'Mark'
}, {
name: 'Relocate test facility',
dependency: 'relocate_staff',
parent: 'relocate',
start: today + 11 * day,
end: today + 13 * day,
owner: 'Anne'
}, {
name: 'Relocate cantina',
dependency: 'relocate_staff',
parent: 'relocate',
start: today + 11 * day,
end: today + 14 * day
}]
}, {
name: 'Product',
data: [{
name: 'New product launch',
id: 'new_product',
owner: 'Peter'
}, {
name: 'Development',
id: 'development',
parent: 'new_product',
start: today - day,
end: today + (11 * day),
completed: {
amount: 0.6,
fill: '#e80'
},
owner: 'Susan'
}, {
name: 'Beta',
id: 'beta',
dependency: 'development',
parent: 'new_product',
start: today + 12.5 * day,
milestone: true,
owner: 'Peter'
}, {
name: 'Final development',
id: 'finalize',
dependency: 'beta',
parent: 'new_product',
start: today + 13 * day,
end: today + 17 * day
}, {
name: 'Launch',
dependency: 'finalize',
parent: 'new_product',
start: today + 17.5 * day,
milestone: true,
owner: 'Peter'
}]
}],
tooltip: {
pointFormat: '<span style="font-weight: bold">{point.name}</span><br>' +
'{point.start:%e %b}' +
'{#unless point.milestone} → {point.end:%e %b}{/unless}' +
'<br>' +
'{#if point.completed}' +
'Completed: {multiply point.completed.amount 100}%<br>' +
'{/if}' +
'Owner: {#if point.owner}{point.owner}{else}unassigned{/if}'
},
title: {
text: 'Gantt Project Management'
},
xAxis: [{
currentDateIndicator: {
color: '#2caffe',
dashStyle: 'ShortDot',
width: 2,
label: {
format: ''
}
},
dateTimeLabelFormats: {
day: '%e<br><span style="opacity: 0.5; font-size: 0.7em">%a</span>'
},
grid: {
borderWidth: 0
},
gridLineWidth: 1,
min: today - 3 * day,
max: today + 18 * day,
custom: {
today,
weekendPlotBands: true
}
}],
yAxis: {
grid: {
borderWidth: 0
},
gridLineWidth: 0,
labels: {
symbol: {
width: 8,
height: 6,
x: -4,
y: -2
}
},
staticScale: 30
},
accessibility: {
keyboardNavigation: {
seriesNavigation: {
mode: 'serialize'
}
},
point: {
descriptionFormatter: function (point) {
const completedValue = point.completed ?
point.completed.amount || point.completed : null,
completed = completedValue ?
' Task ' + Math.round(completedValue * 1000) / 10 +
'% completed.' :
'',
dependency = point.dependency &&
point.series.chart.get(point.dependency).name,
dependsOn = dependency ?
' Depends on ' + dependency + '.' : '';
return Highcharts.format(
point.milestone ?
'{point.yCategory}. Milestone at {point.x:%Y-%m-%d}. ' +
'Owner: {point.owner}.{dependsOn}' :
'{point.yCategory}.{completed} Start ' +
'{point.x:%Y-%m-%d}, end {point.x2:%Y-%m-%d}. Owner: ' +
'{point.owner}.{dependsOn}',
{ point, completed, dependsOn }
);
}
}
},
lang: {
accessibility: {
axis: {
xAxisDescriptionPlural: 'The chart has a two-part X axis ' +
'showing time in both week numbers and days.'
}
}
}
};
// Plug-in to render plot bands for the weekends
Highcharts.addEvent(Highcharts.Axis, 'foundExtremes', e => {
if (e.target.options.custom && e.target.options.custom.weekendPlotBands) {
const axis = e.target,
chart = axis.chart,
day = 24 * 36e5,
isWeekend = t => /[06]/.test(chart.time.dateFormat('%w', t)),
plotBands = [];
let inWeekend = false;
for (
let x = Math.floor(axis.min / day) * day;
x <= Math.ceil(axis.max / day) * day;
x += day
) {
const last = plotBands.at(-1);
if (isWeekend(x) && !inWeekend) {
plotBands.push({
from: x,
color: {
pattern: {
path: 'M 0 10 L 10 0 M -1 1 L 1 -1 M 9 11 L 11 9',
width: 10,
height: 10,
color: 'rgba(128,128,128,0.15)'
}
}
});
inWeekend = true;
}
if (!isWeekend(x) && inWeekend && last) {
last.to = x;
inWeekend = false;
}
}
axis.options.plotBands = plotBands;
}
});
Highcharts.ganttChart('container', options);//container这里是div的id
例一、
效果:
这里先说一下start、end任务开始时间、结束时间数据传进去的时间,一定是时间戳 这里要注意
再说一下X轴时间显示问题 下面的例子中是自适应显示时间 (宽度越长时间越精确,精确到时、分)
如果需要固定精确到日,可以设置 tickInterval: 24 * 3600 * 1000,属性。如下图
下面进入正题:
/*项目里程/进度(甘特图)*/
const day = 24 * 36e5,
today = Math.floor(Date.now() / day) * day;
var startDay1=Date.parse('2024-09-01');
var endDay1=Date.parse('2024-09-03');
var startDay2=Date.parse('2024-09-05');
var endDay2=Date.parse('2024-09-07');
const options = {
chart: {
backgroundColor: 'rgba(0,0,0,0)',
},
plotOptions: {
series: {
borderRadius: '50%',
connectors: {
dashStyle: 'ShortDot',
lineWidth: 2,
radius: 5,
startMarker: {
enabled: false
}
},
groupPadding: 0,
dataLabels: [{
enabled: true,
align: 'left',
format: '{point.name}',
padding: 10,
style: {
fontWeight: 'normal',
textOutline: 'none'
}
}, {
enabled: true,
align: 'right',
format: '{#if point.completed}{(multiply ' +
'point.completed.amount 100):.0f}%{/if}',
padding: 10,
style: {
fontWeight: 'normal',
textOutline: 'none',
opacity: 0.6
}
}]
}
},
series: [{
name: 'Offices',//类别名称(注意这里!!缺少这里的name属性图表会出不来)
data: [{
name: '9-5测试1',//项目名称
id: '90a784f184c64c729e03fec63e829f7e',//项目id
owner: '张三',//项目创建人
}, {
name: '测试9.21',//任务名称(任务第一次)
id: 'b23d60f2f5bc40d2a55139e07e94df56',//任务id
dependency: 'b23d60f2f5bc40d2a55139e07e94df56',//上一次任务所属id(第一次任务可以是他本身的id)
parent: '90a784f184c64c729e03fec63e829f7e',//任务父级id(项目id)
start: startDay1,//开始时间(时间戳)
end: endDay1,//结束时间(时间戳)
completed: {
amount: 0.2,//完成进度(百分比,0为百分之0,1为百分之百)
},
owner: '李四',//任务创建人
}, {
name: '测试2',//任务名称(任务第二次)
id: '16fe05c5f6544b118af5ae3e25f2998e',//任务id
dependency: 'b23d60f2f5bc40d2a55139e07e94df56',//任务所属id(上一次任务所属id,这个参数可以理解为一个项目可以分为很多次任务完成,此次任务应该接住上一次的任务的id,所以这里应该是第一次任务的id)
parent: '90a784f184c64c729e03fec63e829f7e',//任务父级id(项目id)
start: startDay2,//开始时间(时间戳)
end: endDay2,//结束时间(时间戳)
completed: {
amount: 1,//完成进度(百分比)
},
owner: '王五',//任务创建人
}]
}],
tooltip: {
pointFormat: '<span style="font-weight: bold">{point.name}</span><br>' +
'{point.start:%e %b}' +
'{#unless point.milestone} → {point.end:%e %b}{/unless}' +
'<br>' +
'{#if point.completed}' +
'Completed: {multiply point.completed.amount 100}%<br>' +
'{/if}' +
'Owner: {#if point.owner}{point.owner}{else}unassigned{/if}'
},
title: {
text: '',
enabled: false,
},
//去水印
credits: {
enabled: false,
},
xAxis: [{
labels: {
style: {
color: '#FFFFFF',
},
},
dateTimeLabelFormats: {
day: '%e<br><span style="opacity: 0.5; font-size: 0.7em">%a</span>'
},
grid: {
borderWidth: 0
},
gridLineWidth: 1,
}],
yAxis: {
grid: {
borderWidth: 0
},
gridLineWidth: 0,
labels: {
style: {
color: '#FFFFFF',
},
symbol: {
width: 8,
height: 6,
x: -4,
y: -2
}
},
staticScale: 30
},
// scrollbar: {
// enabled: true,//显示滚动条
// },
accessibility: {
keyboardNavigation: {
seriesNavigation: {
mode: 'serialize'
}
},
point: {
descriptionFormatter: function (point) {
const completedValue = point.completed ?
point.completed.amount || point.completed : null,
completed = completedValue ?
' Task ' + Math.round(completedValue * 1000) / 10 +
'% completed.' :
'',
dependency = point.dependency &&
point.series.chart.get(point.dependency).name,
dependsOn = dependency ?
' Depends on ' + dependency + '.' : '';
return Highcharts.format(
point.milestone ?
'{point.yCategory}. Milestone at {point.x:%Y-%m-%d}. ' +
'Owner: {point.owner}.{dependsOn}' :
'{point.yCategory}.{completed} Start ' +
'{point.x:%Y-%m-%d}, end {point.x2:%Y-%m-%d}. Owner: ' +
'{point.owner}.{dependsOn}',
{point, completed, dependsOn}
);
}
}
},
};
//图表配置中文显示
Highcharts.setOptions({
lang:{
contextButtonTitle:"图表导出菜单",
decimalPoint:".",
downloadJPEG:"下载JPEG图片",
downloadPDF:"下载PDF文件",
downloadPNG:"下载PNG文件",
downloadSVG:"下载SVG文件",
drillUpText:"返回 {series.name}",
loading:"加载中",
months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],
noData:"没有数据",
numericSymbols: [ "千" , "兆" , "G" , "T" , "P" , "E"],
printChart:"打印图表",
resetZoom:"恢复缩放",
resetZoomTitle:"恢复图表",
shortMonths: [ "一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],
thousandsSep:",",
weekdays: ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六","星期天"]
}
});
Highcharts.ganttChart('xmlcjd', options);