前端采用jqueryGantt,github地址为:https://github.com/robicch/jQueryGantt

原以为后端只需要简单地保存甘特图任务列表和返回任务列表就行了。

但功能需求是:创建一套任务模板(拖动图片,更改任务依赖关系),然后根据设置的项目开始时间和选择的任务模板动态生成项目的任务列表。

这个生成任务的时间有几点问题。

1.甘特图的任务时间是不包括周六日的时间(周末双休),生成的项目任务的开始时间计算要排除周六日时间。

2.这个任务间依赖规则,这个依赖规则不是按照任务顺序来(任务1要在任务3执行完后才开始执行即depends=3)

  ,如下图是按照从上到下下的顺序规则来执行任务的。

在经过研究这个jqueryGant甘特图的源码后,整理出了一套处理模板任务开始和结束时间的计算规则。

参考甘特图js过程

甘特图初始化任务过程:
gantt.html
   ge = new GanttMaster();
   ge.loadProject(Data)
GanttMaster.js
  loadProject(project)
    loadTasks(project.tasks)
      加载任务就是把所有任务属性都加入master的tasks中,在此过程中会将所有任务开始和结束时间进行计算
      updateLinks更新任务链接,需要判断所有links链接是否有循环引用。在这里会对存在依赖规则的任务开始和结束时间进行计算
执行这些过程需要用到了方法。
GanttUtilities.js ->computeStart(start)//计算开始日期,如果是周六日则会跳过。
GanttUtilities.js ->computeEnd(end) //计算结束日期也要把非工作日给去除。
i18nJs.js ->isHoliday(end) //判断是否是周六日。
GanttTask.js ->computeStartBySuperiors(proposedStart) //会计算依赖规则链接后算出这个任务开始时间的最大值
GanttTask.js ->computeEndByDuration(start, this.duration) //通过开始时间计算结束时间

后端要根据任务模板生成任务规则 :

前置条件:项目开始时间、任务模板列表、
处理过程:
  1.以项目开始时间作为任务模板列表中最早开始任务的开始时间。
  2.其他没有依赖规则的任务开始和结束时间优先计算,任务开始时间和这个固定不变的工作日时间差来计算任务开始时间。
  3.最后再统一计算有依赖规则的任务的开始和结束时间。

过程1

  根据甘特图的js限制规则,根节点,即level为0的节点开始时间一定比子节点的任务开始时间早。所以只要找出任务模板中任务是根节点,任务没有依赖规则且开始时间最早的任务作为最早任务(有依赖规则的任务的开始时间是不确定的。)。

代码

TaskTemplate earlyTask = null;
Date projectStartDate = project.getStartDate();
int taskPos = ;
for (int i = ; i < taskTemplateList.size(); i++) {
TaskTemplate taskItem = taskTemplateList.get(i);
if (taskItem.getLevel().equals() && StringUtils.isBlank(taskItem.getDepends())) {
if (earlyTask == null) {
earlyTask = taskItem;
taskPos = i;
} else {
if (earlyTask.getStart().getTime() > taskItem.getStart().getTime()) {
earlyTask = taskItem;
taskPos = i;
}
}
}
}
if (earlyTask == null) {
return null;//避免程序异常
}

过程2

  遍历一次任务模板列表,将所有任务属性都复制到任务列表中(保证顺序不变,或者自己后面根据sort由小到大重排序)

  在此过程中还要做件事,复制属性和计算无依赖规则的任务的开始和结束时间。

  根据任务列表创建任务依赖规则链接 links

代码:

        List<Task> taskList = new ArrayList<>();
//复制属性和计算无依赖规则任务时间,不处理任务文件
for (int i = 0; i < taskTemplateList.size(); i++) {
TaskTemplate item = taskTemplateList.get(i);
Task taskItem = new Task();
BeanUtils.copyProperties(item, taskItem);
if (item.getMilestone().equals(ProjectConsts.TASK_MILESTONE_YES)) {
taskItem.setCanDelete("false");
}
taskItem.setId(null);
taskItem.setProjectId(project.getId());
if (StringUtils.isNotBlank(taskItem.getDepends())) {
taskList.add(taskItem);
continue;
}
//无依赖规则任务计算
//初始化开始结束时间。当前任务和最早开始任务的时间差,只包含工作日
int subDay = DateUtils.getWorkdayTimeInDate(earlyTask.getStart(), item.getStart());
Date realStartDate = DateUtils.incrementDateByWorkingDays(projectStartDate, subDay);
taskItem.setStart(realStartDate);
Date realEndDate = DateUtils.incrementDateByWorkingDays(realStartDate, taskItem.getDuration());
taskItem.setEnd(realEndDate); taskList.add(taskItem);
} //创建任务依赖规则链接,并put到任务列表中
//创建没有依赖规则的任务开始和结束时间,获取所有任务的更新链接links
List<TaskLink> links = new ArrayList<>();
for (int i = 0; i < taskList.size(); i++) {
Task taskItem = taskList.get(i);
if (StringUtils.isBlank(taskItem.getDepends())) {
continue;//跳过
}
String[] depends = taskItem.getDepends().split(",");
for (int j = 0; j < depends.length; j++) {
String[] regular = depends[j].split(":");
TaskLink taskLink = null;
if (regular.length == 2) {
//这个规则的序号从1开始
taskLink = new TaskLink(taskList.get(Integer.valueOf(regular[0])-1), taskItem, Integer.valueOf(regular[1]));
} else {
taskLink = new TaskLink(taskList.get(Integer.valueOf(regular[0])-1), taskItem, 1);
}
links.add(taskLink);
}
}

过程3:最后再统一计算有依赖规则的任务的开始和结束时间

这个链接的结构

  这个过程需要用到两次递归:

  递归遍历获得该task节点的所有依赖规则。即获得List<TaskLink> taskLinkList

  递归处理taskLinkList数据,知道这个列表数据为空为止

两次递归代码

    /**
* 刷新taskList中存在依赖规则的开始时间和结束时间
*
* @param taskList
* @param taskLinkList
*/
public void refreshTaskLink(List<Task> taskList, List<TaskLink> taskLinkList) {
List<TaskLink> todoList = new ArrayList<>();
if (taskLinkList.isEmpty()) {
return;
}
TaskLink taskLink = taskLinkList.get(0);
todoList.add(taskLink);
if (StringUtils.isNotBlank(taskLink.getFrom().getDepends())) {
List<TaskLink> linkTmpList = getToLinkList(taskLink.getFrom(), taskLinkList);
if (linkTmpList != null) {
todoList.addAll(linkTmpList);
}
}
List<TaskLink> onceDealWithList = new ArrayList<>();
Task preTo = null; //处理第一个节点开始到没有依赖规则为止的嵌套规则列表
for (int i = todoList.size() - 1; i >= 0; i--) {
//倒叙处理依赖规则
TaskLink link = todoList.get(i);
if (preTo == null) {
preTo = link.getTo();
}
onceDealWithList.add(link);
//是否还有下一个节点
if (i - 1 >= 0) {
if (preTo.getTaskSort().equals(todoList.get(i - 1).getTo().getTaskSort())) {
//还是相等,则跳过
continue;
} else {
//不相同,处理链接列表,设置任务开始结束时间
Task task = taskList.get(link.getTo().getTaskSort() - 1);
dealTaskByOneDealWithList(task, onceDealWithList);
//重置处理数据
onceDealWithList = new ArrayList<>();
preTo = null;
}
} else {
//不相同,处理链接列表,设置任务开始结束时间
Task task = taskList.get(link.getTo().getTaskSort() - 1);
dealTaskByOneDealWithList(task, onceDealWithList);
//重置处理数据
onceDealWithList = new ArrayList<>();
preTo = null;
}
}
taskLinkList.removeAll(todoList);
refreshTaskLink(taskList, taskLinkList); } public void dealTaskByOneDealWithList(Task task, List<TaskLink> dealLinkList) {
Date superEnd = null;
for (TaskLink taskLink1 : dealLinkList) {
if (superEnd == null) {
superEnd = DateUtils.incrementDateByWorkingDays(taskLink1.getFrom().getEnd(), taskLink1.getLag());
} else {
Date curEnd = DateUtils.incrementDateByWorkingDays(taskLink1.getFrom().getEnd(), taskLink1.getLag());
if (curEnd.getTime() > superEnd.getTime()) {
superEnd = curEnd;
}
}
}
//设置开始和结束时间,开始时间和结束时间计算不用再加1,因为当天表示如2019-11-11开始2019-11-11结束表示1天
task.setStart(superEnd);
task.setEnd(DateUtils.incrementDateByWorkingDays(task.getStart(), task.getDuration() - 1));
} /**
* 递归遍历获得该task节点的所有依赖规则。
*
* @param to
* @param taskLinkList
* @return
*/
public List<TaskLink> getToLinkList(Task to, List<TaskLink> taskLinkList) {
List<TaskLink> list = null;
for (TaskLink taskLink : taskLinkList) {
if (taskLink.getTo().getTaskSort().equals(to.getTaskSort())) {
//序号相同
if (list == null) {
list = new ArrayList<>();
}
list.add(taskLink);
if (StringUtils.isNotBlank(taskLink.getFrom().getDepends())) {
List<TaskLink> childLinkList = getToLinkList(taskLink.getFrom(), taskLinkList);
if (childLinkList != null) {
list.addAll(childLinkList);
}
}
}
}
return list;
}

逻辑代码上传到github:https://github.com/innerjob/jQueryGantt/tree/master/java

java处理jqueryGantt甘特图数据的task.depends依赖规则方法的更多相关文章

  1. java中的排序(自定义数据排序)--使用Collections的sort方法

    排序:将一组数据按相应的规则 排列 顺序 1.规则:       基本数据类型:日常的大小排序. 引用类型: 内置引用类型(String,Integer..),内部已经指定规则,直接使用即可.---- ...

  2. R-plotly|交互式甘特图(Gantt chart)-项目管理/学习计划

    本文首发于“生信补给站”微信公众号,https://mp.weixin.qq.com/s/CGz51qOjFSJ4Wx_qOMzjiw 更多关于R语言,ggplot2绘图,生信分析的内容,敬请关注小号 ...

  3. 甘特图生产排程(APS)定制开发

    高速开发完毕APS的数据可视化.订单展示.资源调度.智能排程等差点儿所有功能模块. 自己主动智能排程功能 提供专业需求分析师及开发团队,按需开发"全自己主动智能排程"这一APS的主 ...

  4. 自己做的js甘特图插件

    版权所有,禁止转载 内容都在代码中,上图上代码! 代码 <!DOCTYPE html> <html> <head> <title>ganttu.html ...

  5. 基于 ECharts 封装甘特图并实现自动滚屏

    项目中需要用到甘特图组件,之前的图表一直基于 EChart 开发,但 EChart 本身没有甘特图组件,需要自行封装 经过一番鏖战,终于完成了... 我在工程中参考 v-chart 封装了一套图表组件 ...

  6. java实现甘特图的2种方法:SwiftGantt和Jfree (转)

    http://blog.sina.com.cn/s/blog_50a7c4a601009817.html 第一种方法使用SwiftGantt实现甘特图(进度图推荐这个)   import java.a ...

  7. Java甘特图控件swing版免费下载地址

    FlexGantt 控件是现在Java 平台下最先进的甘特图解决方案,使用一个很高的抽象层次,能适用于多种不同的域,例如 ERP 系统.生产计划和日程安排.制造流程系统或项目公文管理程序等.这些使得 ...

  8. java制作甘特图

    今日来做一下甘特图.网上搜到了这个源码,但是导的jar包,并没有给我.swiftganttdemo但是名为swiftgantt制作:所以灵机一动在网上搜到了swiftangantt组件:在组件中找到了 ...

  9. Twproject Gantt开源甘特图功能扩展

    1.Twproject Gantt甘特图介绍 Twproject Gantt 是一款基于 jQuery 开发的甘特图组件,也可以创建其它图表,例如任务树(Task Trees).内置编辑.缩放和 CS ...

随机推荐

  1. Linux下Mysql每天自动备份

    新建目录 mkdir -p /data/mysqlbal/data mkdir -p /data/mysqlbal/scripts mkdir -p /data/mysqlbal/logs 创建备份脚 ...

  2. Spring-HelloSpring-IOC创建对象的方式

    3.HelloSpring 思考 Hello 对象是谁创建的 ? [ hello 对象是由Spring创建的 ] Hello 对象的属性是怎么设置的 ? [hello 对象的属性是由Spring容器设 ...

  3. bug提交遵循的规则

    在提交缺陷时,需要遵循以下5个原则: 准确性:缺陷每个组成部分描述准确,不会产生误解,减少“异常”“正常”等模糊词的使用 完整性:复现该缺陷完整的步骤.截图.日志 一致性:按照一致的格式书写全部缺陷信 ...

  4. SEERC 2018 B. Broken Watch (CDQ分治)

    题目链接:http://codeforces.com/gym/101964/problem/B 题意:q 种操作,①在(x,y)处加一个点,②加一个矩阵{(x1,y1),(x2,y2)},问每次操作后 ...

  5. [Google Guava] 1.2-前置条件

    原文链接 译文链接 译者: 沈义扬 前置条件:让方法调用的前置条件判断更简单. Guava在Preconditions类中提供了若干前置条件判断的实用方法,我们强烈建议在Eclipse中静态导入这些方 ...

  6. 用jackson的@JsonProperty注解属性名,会多出一个字段

    遇见了这个情况,我的字段定义是xVal,yVal,用的lombok的@Data注解.然后查询到了下面这偏文章,https://bbs.csdn.net/topics/392305619,里面的回答是图 ...

  7. Doki Doki Literature Club ZOJ - 4035

    Doki Doki Literature Club ZOJ - 4035 题解:其实就是简单排序输出就没了. #include <cstdio> #include <cstring& ...

  8. 关于pycharm+opencv没有代码提示的问题解决方法记录

    代码可以看出实际我们引入的应该是cv2.cv2下面. 所以我们代码只需要import cv2.cv2 as cv 即可. 记着要重新启动下pycharm哦. 可以参考: https://blog.cs ...

  9. Selenium全屏截图,使用PIL拼接滚动截图

    Selenium默认的截图save_screenshot只支持对当前窗口内容进行截图,当如果你想要截取整个网页,那么,可以明确的告诉你. Selenium做不到. 你可以手工使用开发者工具Ctrl+S ...

  10. 一、docker安装CentOS7

    一.安装步骤 前提条件 Docker运行在CentOS7上,要求系统64位.系统内核版本为3.10以上. Docker是一个进程,一启动就两个进程,一个服务,一个守护进程.占用资源就非常少,启动速度非 ...