最近接了一个需求,前面的都好说。但是最后一个需求就有点麻烦了——绘制甘特图。
一、需求分析
1、什么是甘特图?(•́へ•́╬)
甘特图(Gantt chart)又称为横道图、条状图(Bar chart)。其通过条状图来显示项目,进度,和其他时间相关的系统进展的内在关系随着时间进展的情况。【百度百科】
2、绘制甘特图的方法?
- 集成echarts等插件自己绘制
- 集成专门绘制甘特图的插件。
3、分析需求
这边经过分析需求,甘特图是左表右图的样式。echarts绘制的甘特图左侧的表要自己写,而且折叠效果、对齐的css之类的都要自己写,特麻烦且时间不够完成的。最后决定用插件。
插件:
- JQuery Gantt 开源免费 【左侧有表,但不是树表】
- PlusGantt 收费的 【不考虑】
- DHX Gantt 开源免费,也有收费版的 【可以满足需求】
- ECharts 开源免费(例如:ECharts Gallery)【左侧表要自己写】
- HighCharts 开源免费。【左侧表要自己写】
最后能满足要求左表(树表,带折叠功能)右图只有DHXGantt。
二、开始绘制甘特图工作
1、下载 DHX Gantt
官网:JavaScript UI Framework with HTML5 UI Components
下载地址:Download dhtmlxGantt Standard Edition
2、解压文档
4、导入js
//DHX-Gantt 基础css
<link rel="stylesheet" href="../dhx-gannt/dhtmlxgantt.css?v=6.3.0">
//JQuery
<script src="../jquery-easyui-1.3.2/jquery-1.8.0.min.js"></script>
//DHX-Gantt 基础js
<script src="../dhx-gannt/dhtmlxgantt.js?v=6.3.0"></script>
//DHX-Gantt 扩展功能“全屏”js
<script src="../dhx-gannt/ext/dhtmlxgantt_fullscreen.js"></script>
5、建立页面加载点
<div>
<!--高度可调整-->
<div id="gantt_here" style='width:100%; height:578px;'></div>
</div>
6、集成JQuery,加载甘特图
$(function () {
$.ajax({
url: '/task/progress',
data: {taskId: $("#id").val()},
dataType: 'json',
type: 'post',
success: function (data) {
let progress_data = {data: data};
//集成JQuery
let gantt = $("#gantt_here").dhx_gantt({
data: progress_data,
columns: [
{name: "text", label: "任务名称", tree: true},
{name: "start_date", label: "开始时间", align: "center"},
{name: "end_date", label: "结束时间", align: "center", width: 100}
],//自定义树表显示字段。不自己定义,末尾列会出现 “+”,点击会触发"添加任务"事件
drag_mode: { //模块禁止移动
move: false
},
// drag_move:false,//图柱禁止移动
// drag_links:false,//禁止拉关系
// details_on_dblclick:false,//禁止详情点击
// details_on_create:false,//禁止创建新的任务
// select_task:false,//禁止选中任务
show_task_cells: false,//启用/禁用在图表区域中显示【列边框】
show_tasks_outside_timescale: true,//启用在甘特图中显示超出指定日期范围的任务
// task_date:"%Y-%m-%d",
// date_grid:"%Y-%m-%d",
date_format: "%Y-%m-%d", //数据表时间转换
// placeholder_task:false,//在任务列表的末尾添加一个空行,以简化通过键盘进行的任务编辑
readonly: true,//设为只读,上面的禁止项默认都会禁止
scales: [{
unit: "day", step: 1, format: function (date) {
//跨年显示不友好,自己设置x轴样式。
return "<span title='" + dateFormat_4(date) + "'>" + getMonth(date) + "-" + getDay(date) + "</span>";
}
}],
});
//这地方跟easyui写法不一样,不能onCollapse写在dhx_gantt({……})里面,否则不生效。
//所有的触犯事件都是这样的写法
//使用自带的【退回全屏】功能时触发。
gantt.attachEvent("onCollapse", function () {
gantt.collapse();
//这个地方触发显示,是因为项目最顶层是导入的导航栏,全屏时会显示并且遮住了x轴刻度
//全屏时,需要隐藏顶部导航栏
//退出时,需要显示顶部导航栏
$(document.body).find(".top-nav").show();
});
}
});
let button = document.getElementById("full");
button.addEventListener("click", function () {
if (!gantt.getState().fullscreen) {
//全屏
gantt.expand();
//这个地方触发显示,是因为项目最顶层是导入的导航栏,全屏时会显示并且遮住了x轴刻度
//全屏时,需要隐藏顶部导航栏
$(document.body).find(".top-nav").hide();
} else {
//这个地方,不是自定义的【退出全屏】不会触发
// 退出全屏//将甘特压缩到正常模式
gantt.collapse()
$(document.body).find(".top-nav").show();
}
}, false);
});
7、后台返回json格式
var users_data = {
"data":[
{"id":"2843a52a8de64e0d8d8d2d3b6e4eb5fa", "text":"Project #1", "start_date":"01-04-2018", "duration":"11", "progress": 0.6, "open": true, "users": ["John", "Mike", "Anna"], "priority": "2"},
{"id":"2", "text":"Task #1", "start_date":"03-04-2018", "duration":"5", "parent":"2843a52a8de64e0d8d8d2d3b6e4eb5fa", "progress": 1, "open": true, "users": ["John", "Mike"], "priority": "1"},
{"id":"3", "text":"Task #2", "start_date":"02-04-2018", "duration":"7", "parent":"2843a52a8de64e0d8d8d2d3b6e4eb5fa", "progress": 0.5, "open": true, "users": ["Anna"], "priority": "1"},
{"id":"4", "text":"Task #3", "start_date":"02-04-2018", "duration":"6", "parent":"2843a52a8de64e0d8d8d2d3b6e4eb5fa", "progress": 0.8, "open": true, "users": ["Mike", "Anna"], "priority": "2"},
{"id":"5", "text":"Task #4", "start_date":"02-04-2018", "duration":"5", "parent":"2843a52a8de64e0d8d8d2d3b6e4eb5fa", "progress": 0.2, "open": true, "users": ["John"], "priority": "3"},
{"id":"6", "text":"Task #5", "start_date":"02-04-2018", "duration":"7", "parent":"2843a52a8de64e0d8d8d2d3b6e4eb5fa", "progress": 0, "open": true, "users": ["John"], "priority": "2"},
{"id":"7", "text":"Task #2.1", "start_date":"03-04-2018", "duration":"2", "parent":"3", "progress": 1, "open": true, "users": ["Mike", "Anna"], "priority": "2"},
{"id":"8", "text":"Task #2.2", "start_date":"06-04-2018", "duration":"3", "parent":"3", "progress": 0.8, "open": true, "users": ["Anna"], "priority": "3"},
{"id":"9", "text":"Task #2.3", "start_date":"10-04-2018", "duration":"4", "parent":"3", "progress": 0.2, "open": true, "users": ["Mike", "Anna"], "priority": "1"},
{"id":"10", "text":"Task #2.4", "start_date":"10-04-2018", "duration":"4", "parent":"3", "progress": 0, "open": true, "users": ["John", "Mike"], "priority": "1"},
{"id":"11", "text":"Task #4.1", "start_date":"03-04-2018", "duration":"4", "parent":"5", "progress": 0.5, "open": true, "users": ["John", "Anna"], "priority": "3"},
{"id":"12", "text":"Task #4.2", "start_date":"03-04-2018", "duration":"4", "parent":"5", "progress": 0.1, "open": true, "users": ["John"], "priority": "3"},
{"id":"13", "text":"Task #4.3", "start_date":"03-04-2018", "duration":"5", "parent":"5", "progress": 0, "open": true}
]
};
*注意:
- 经过测试id,parent可以为String类型
- 返回json数据类型为 {"data":[{……},{……}]}。我这直接返回的list,所以需要前端需要在分装一层
- id,parent,startdate,duration 为必传项。id,parent确定表tree父子关系,start_date确定开始位置。duration确定长度。
- 如果你只是截取的某一段子树显示。最顶层的父节点 parent为0才会显示。
8、API文档
官网:Gantt API Gantt Docs
自己整理文档:有道云笔记-dhx-gannt-API.xlsx
*因为官网是英语的,对于我们学渣来说特不友好,我这就把官网文档整理了一下。虽然不好友好,但是建议还是多看看官方文档,因为详细的配置信息,我这根本没有整理。
有些属性,是花钱才能使用的,注意不要使用这些属性。
9、做成效果图(全屏模式)。数据是测试数据,大家将就着看吧!
10、事后补充:发现统计性的甘特图,用这个后期扩展性不强。(2019-12-18)
1、因为有道云特不友好,网速卡的根本打不开API文档。特给观众老爷附上百度云盘地址:
链接: https://pan.baidu.com/s/1ASaHJWMM_QI2dMjJwrIoaQ 提取码: aw2e
2、突然发现了几个dhx-gantt缺陷:
- 这个甘特图插件,是专门用来做任务计划的。意思是:在做工作(任务)之前用的,而不是任务结束后做统计用的。
- 如有计划时间跟实际时间的对比时。样式如下:白条表示计划任务,蓝条表示实际任务。
- 如图所示,他的实际开始时间跟计划开始时间都是一个。在甘特图中,也就是开始时间的其实都是一个点。而且他采用的两个时间都是在一个图柱上显示,特不友好。
- 甘特图的子任务超限问题。当一个任务延期了,在图中根本显示不出来,因为超限后,会覆盖计划结束时间。