这几天在对之前开发的项目管理系统可视化部分当中的甘特图模块进行功能优化,过程中发现了之前未发现的Bug问题。

如下图所示,甘特图的工期计算结果少1天:

为此我排查了之前写的存储过程,对甘特图所需的duration字段值的计算追加1天:

  1 CREATE DEFINER=`root`@`localhost` PROCEDURE `pmstest`.`P_GanttAnalysis`(in ProjectId varchar(64))
2 BEGIN
3
4 DECLARE done INT default 0;
5 DECLARE id_value varchar(50);
6
7 DECLARE cur CURSOR FOR
8 SELECT id FROM pm_plan where status = '0' and proj_id = ProjectId;
9
10 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
11
12 DROP TABLE IF exists temp_result;
13
14 create temporary table temp_result
15 (
16 id varchar(50),
17 text varchar(200),
18 start_date varchar(32),
19 end_date varchar(32),
20 user varchar(64),
21 username varchar(100),
22 duration int,
23 parent varchar(64),
24 progress DECIMAL(8,2),
25 open varchar(10),
26 classification varchar(100)
27 );
28
29 INSERT INTO temp_result(id,text,start_date,end_date,user,username,duration,parent,progress,open,classification)
30 select A.id,A.text,A.start_date,A.end_date,A.user,A.username,A.duration,A.parent,B.progress,A.open,A.classification
31 from
32 (
33 select a.id as id,a.proj_name as text,a.start_date as start_date,a.end_date as end_date
34 ,a.proj_manager as user,b.user_name as username,DATEDIFF(a.end_date,a.start_date)+1 as duration,'' as parent,"true" as open,a.status,'项目' as classification
35 from pm_infor a
36 left join js_sys_user b
37 on a.proj_manager = b.user_code
38 where a.status = '0' and id = ProjectId
39 ) A
40 left join
41 (
42 select m.proj_id,m.taskcount,m.taskcompletecount,
43 CASE
44 WHEN m.taskcount !=0 then ROUND(m.taskcompletecount/m.taskcount,2)
45 else 0
46 END as progress
47 from
48 (
49 select a.proj_id,
50 SUM(CASE WHEN a.status = '0' THEN 1 ELSE 0 END) AS taskcount,
51 SUM(CASE WHEN (a.status='0' and (a.task_status = '2' or a.task_status = '4')) then 1 else 0 end) as taskcompletecount
52 from pm_task a
53 where a.proj_id = ProjectId
54 group by a.proj_id
55 )m
56 )B
57 on A.id = B.proj_id;
58
59 DROP TABLE IF exists temp_plan_right;
60
61
62 create temporary table temp_plan_right
63 (
64 plan_id varchar(50),
65 taskcount int,
66 taskcompletecount int,
67 progress DECIMAL(8,2)
68 );
69
70
71
72 OPEN cur;
73 read_loop: LOOP
74 FETCH cur INTO id_value;
75 IF done = 1 THEN
76 LEAVE read_loop;
77 END IF;
78
79
80 INSERT INTO temp_plan_right(plan_id,taskcount,taskcompletecount,progress)
81 select m.plan_id,m.taskcount,m.taskcompletecount,
82 CASE
83 WHEN m.taskcount !=0 then ROUND(m.taskcompletecount/m.taskcount,2)
84 else 0
85 END as progress
86 from
87 (
88 select a.plan_id,
89 SUM(CASE WHEN a.status = '0' THEN 1 ELSE 0 END) AS taskcount,
90 SUM(CASE WHEN (a.status='0' and (a.task_status = '2' or a.task_status = '4')) then 1 else 0 end) as taskcompletecount
91 from pm_task a
92 where a.plan_id = id_value
93 group by a.plan_id
94 ) m ;
95
96
97 END LOOP;
98
99 CLOSE cur;
100
101 INSERT INTO temp_result(id,text,start_date,end_date,user,username,duration,parent,progress,open,classification)
102 select A.id,A.text,A.start_date,A.end_date,A.user,A.username,A.duration,A.parent,B.progress,A.open,A.classification
103 FROM
104 (
105 select a.id as id,a.plan_name as text,a.plan_start_date as start_date,a.plan_end_date as end_date
106 ,a.plan_manager as user,b.user_name as username,DATEDIFF(a.plan_end_date,a.plan_start_date)+1 as duration,a.proj_id as parent,"true" as open,a.status,'计划' as classification
107 from pm_plan a
108 left join js_sys_user b
109 on a.plan_manager = b.user_code
110 where a.status = '0' and a.proj_id = ProjectId
111 ) A
112 left join temp_plan_right B
113 on A.id = B.plan_id;
114
115
116 INSERT INTO temp_result(id,text,start_date,end_date,user,username,duration,parent,progress,open,classification)
117 select a.id as id,a.task_name as text,a.task_start_date as start_date,a.task_end_date as end_date,task_manager as user,b.user_name as username,
118 DATEDIFF(a.task_end_date,a.task_start_date)+1 as duration,a.plan_id as parent,
119 CASE WHEN (a.task_status = '2' or a.task_status = '4') then 1 else 0 end
120 as progress
121 ,"true" as open,'任务' as classification
122 FROM
123 pm_task a left join js_sys_user b
124 on a.task_manager = b.user_code
125 where a.status = '0' and a.proj_id = ProjectId;
126
127
128 select * from temp_result;
129
130
131 END

运行页面后,发现【工期】duration列数据没有任何改变,还是少1天。我又对前端拿到的后端数据打了日志,发现后端确实是把加了1天的duration数据给到前端了,部分日志如下:

1 甘特图图表数据:{"data":[{"id":"1800444694382850048","text":"智能测试评估系统开发","start_date":"2024-06-03","end_date":"2024-09-30","user":"XXXX_tllq","username":"XXX","duration":"120","parent":null,"progress":"0.62","open":"true","classification":"项目"},{"id":"1800445991181955072","text":"系统管理员大屏V1.0展示开发","start_date":"2024-06-01","end_date":"2024-06-25","user":"XXXX_nbjb","username":"XX","duration":"25","parent":"1800444694382850048","progress":"0.60","open":"true","classification":"计划"},{"id":"1800448916302147584","text":"用户看板V1.0展示开发","start_date":"2024-06-26","end_date":"2024-07-31","user":"XXXXXXX_nbjb","username":"XX","duration":"36","parent":"1800444694382850048","progress":"0.00","open":"true","classification":"计划"}

那么为什么DHTMLX Gantt的甘特图页面的工期列还是没有加1呢?

我查了网络上关于DHTMLX Gantt的工期问题相关资料,发现了问题缘由:

首先虽然前端我是按照DHTMLX Gantt官方语法格式定义的列,如下:

 1  gantt.config.columns = [
3 {
4 name: "text", label: "工作描述", tree: true, min_width: 200, width: 220, resize: true,
5 //20240308 保证项目名超过特定字数,后面以...进行显示
6 template: obj => obj.text.length > 10 ? obj.text.substring(0, 10) + '...' : obj.text
7 },
9 {
10 name: "progress", label: "进度", align: "center", width: 50, resize: true,
11 //template: obj => `${Math.round(obj.progress * 100, 2) + '%'}`
12 template: obj => (Math.round(obj.progress * 100)).toString() + '%'
13 },
15 { name: "start_date", label: "开始日期", align: "center", width: 75, resize: true },
16 {
17 name: "end_date", label: "结束日期", align: "center", width: 75, resize: true
18 },
19 {
20 name: "duration", label: "工期(天)", align: "center", width: 60, resize: true
21 },
22 {
23 name: "username", label: "责任人", align: "center", width: 70, resize: true
24 }
25 ];

并且在后端存储过程返回的数据也明确给定了duration的值(+1后的值),但是DHTMLX Gantt有个问题是它前端显示的duration是不依赖于后端计算的duration的,它是根据后端传过来的开始日期、结束日期进行计算,得出他的“duration”进行显示的!!这本来也没有问题,但是由于数据的开始日期、结束日期的格式是YYYY-MM-dd,没有时分秒,在这种情况下,DHTMLX Gantt的duration默认计算方式是按照结束日期YYYY-MM-dd 00:00:00 - 开始日期的YYYY-MM-dd 00:00:00。这就会导致少1天。如某项任务开始日期2024-06-11,结束日期2024-06-12,实际上正确计算出的duration应当是2天(即按照:2024-06-12 23:59:59 - 2024-06-11 00:00:00 = 2天),但是按照DHTMLX Gantt的duration默认计算方式,则变成的了duration为1天了(即按照:2024-06-12 00:00:00 - 2024-06-11 00:00:00 = 1天),这与我们想要的实际不符!

解决的其中一个办法就是对DHTMLX Gantt的结束日期多加1天,可这会导致数据库的结束日期与前端界面显示的结束日期不一致的问题,这是不合理的!

后面考虑的方案是,结束日期不修改,遵循数据库的数据;但人为修改duration值的显示。

以下是解决的过程,以及对过程中产生的新问题进行解决的过程:

1.解决左侧工期列值少1天问题:对duration显示时人为+1

在gantt.config.columns列定义中,修改duration的定义,内部追加模板修改工期的显示值:

 {
name: "duration", label: "工期(天)", align: "center", width: 60, resize: true,
template: function (task) {
// 如果duration是null或undefined,返回0
return (task.duration || 0) + 1; // 在当前duration的基础上加1
}
},

这样确实工期列值实现了加1,可是右侧的进度条图形显示还是按照原本少一天进行显示,如下图:

2.解决右侧进度条甘特图图形显示还是按照DHTMLX Gantt原始duration值显示的问题(即还是少1天的问题)

改一下两处代码:

 1 gantt.config.columns = [
2 // ... 其他列定义 ...
3 { name: "start_date", label: "开始日期", align: "center", width: 75, resize: true },
4 {
5 name: "end_date",
6 label: "结束日期",
7 align: "center",
8 width: 75,
9 resize: true,
10 template: function(task) {
11 // 返回任务的原始结束日期
12 return task.original_end_date ? gantt.date.date_to_str("%Y-%m-%d")(task.original_end_date) : "";
13 }
14 },
15 { name: "duration", label: "工期(天)", align: "center", width: 60, resize: true },
16 // ... 其他列定义 ...
17 ];
18
19 // 解析任务数据前调整结束日期
20 gantt.attachEvent("onParse", function() {
21 gantt.eachTask(function(task) {
22 // 保存原始结束日期
23 task.original_end_date = task.end_date;
24
25 if (task.start_date && task.duration !== null && task.duration !== undefined) {
26 // 基于目前的开始日期和持续时间加1天而更新结束日期
27 task.end_date = gantt.date.add(task.start_date, task.duration + 1, "day");
28 }
29 });
30 });

修改以上两处代码问题解决,问题解决。可是又发现了新问题:甘特图的浮动框显示的End Date值不对,被改成了数据库日期+1的值,而我希望保留显示数据库的原始结束日期值。

现象图如下:

3.解决浮动框End Date结束日期值被加1问题

在以上代码修改的基础上,追加DHTMLX Gantt提供的gantt.templates.tooltip_text方法,自定义鼠标悬停时显示的内容。

 1   //20250211 tooltip浮动框显示的End Date被追加1的问题修复(应该显示数据库的原始值)
2 // 自定义浮动框的显示内容
3 gantt.templates.tooltip_text = function (start, end, task) {
4 // 使用原始结束日期显示
5 var originalEndDate = task.original_end_date ? gantt.date.date_to_str("%Y-%m-%d")(task.original_end_date) : "";
6 return `<b>工作描述:</b> ${task.text}<br/>
7 <b>开始日期:</b> ${gantt.date.date_to_str("%Y-%m-%d")(task.start_date)}<br/>
8 <b>结束日期:</b> ${originalEndDate}<br/>
9 <b>工期:</b> ${task.duration + 1}天<br/>
10 <b>进度:</b> ${task.progress*100}%<br/>
11 <b>责任人:</b> ${task.username}<br/>
12 ` ;
13 };

好了,以上所有问题解决,最终效果图如下:

DHTMLX Gantt 甘特图计算工期少1天的问题解决及解决过程附带产生的问题解决的更多相关文章

  1. gantt甘特图的制作过程

    甘特图主要是用来做项目管理的,可以清楚的看到任务间的逻辑关系,任务与时间关系和任务间并行关系. 在甘特图中,横轴方向表示时间,纵轴方向并列着活动列表.图表内可以用线条.数字.文字代号等来表示计划(实际 ...

  2. gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)

    前言   Excel功能强大,应用广泛.随着web应用的兴起和完善,用户的要求也越来越高.很多Excel的功能都搬到了sass里面.恨不得给他们做个Excel出来...程序员太难了... 去年我遇到了 ...

  3. 使用GridVIew显示Gantt(甘特图),动态增减列

    说明:本例是做了工厂的排机报表 一.根据查询日期初始化GridView列 private void IniGridView(DateTime p_DateS,DateTime p_DateE) { / ...

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

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

  5. jquery甘特图免费下载

    Silverlight Gantt甘特图是一款非常丰富,可定制,轻量级和高性能的控件. 项目甘特图: 可视化层次的任务列表. 可移动和拖拽调整条形图 可视化时间编辑器 编辑任务依赖关系 调整任务进度条 ...

  6. 在博客文章中使用mermaid 定义流程图,序列图,甘特图

    概述 Mermaid(美人鱼)是一套markdown语法规范,用来在markdown文档中定义图形,包括流程图.序列图.甘特图等等. 它的官方网站是 https://mermaid-js.github ...

  7. 甘特图dhtmlx Gantt入门

    (以下截图来自别人的博客,来源地址已经忘记了,若后期找到会补充上来!) API地址:https://docs.dhtmlx.com/gantt/desktop__guides.html,这是英文的网页 ...

  8. 【转载】 JQuery.Gantt(甘特图) 开发指南

    转载来自: http://www.cnblogs.com/liusuqi/archive/2013/06/09/3129293.html JQuery.Gantt是一个开源的基于JQuery库的用于实 ...

  9. JQuery.Gantt(甘特图)开发

    一.简介 JQuery.Gantt是一个开源的基于JQuery库的用于实现甘特图效果的可扩展功能的JS组件库. 二.前端页面 2.1 资源引用 首先需要将下载到的源码中的CSS.IMG.JS等资源放入 ...

  10. 甘特图 (Gantt )的优缺点

    时间管理 - 甘特图 (Gantt ) 优点:甘特图直观.简单.容易制作,便于理解,能很清晰地标识出直到每一项任务的起始与结束时间,一般适用比较简单的小型项目,可用于WBS的任何层次.进度控制.资源优 ...

随机推荐

  1. K8s集群中的DNS服务(CoreDNS)详解

    概述 官网文档:https://kubernetes.io/zh-cn/docs/concepts/services-networking/dns-pod-service/ 在 Kubernetes( ...

  2. GHCTF 2025 web 萌新初探wp

    ctf萌新第一次写wp,如有错误请师傅们指出 [GHCTF 2025]SQL??? 打开靶机是一个用户查询的页面,结合题目名称猜测是sql注入,但是常规方法都试过了没办法注入,当时也是很懵逼,后来一个 ...

  3. 从理解AI到驾驭文字:一位技术爱好者的写作工具探索手记

    三年前,当我第一次接触AI写作工具时,它生成的文字还带着明显的机械感,段落间的逻辑时断时续.如今,这些系统已经能写出颇具文采的散文,甚至模仿特定作家的风格.这种进化轨迹恰好为学习者提供了一个观察AI发 ...

  4. [书籍精读]《基于MVC的JavaScript Web富应用开发》精读笔记分享

    写在前面 书籍介绍:<JavaScript异步编程>讲述基本的异步处理技巧,包括PubSub.事件模式.Promises等,通过这些技巧,可以更好的应对大型Web应用程序的复杂性,交互快速 ...

  5. HyperMesh基础教程:概述与有限元分析简介

    1.1 HyperMesh 概述 本节将介绍有限单元法基本原理,HyperMesh 软件基本功能及界面介绍,获取在线帮助等内容. 1.1.1 有限元分析方法简介 有限单元法(FEM)是一种可以精确预测 ...

  6. Vite加Vue3加Ts创建项目一些问题汇总

    版权声明:本文为CSDN博主「一尾流莺_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/m0_48721669 ...

  7. secp256k1算法详解三(点操作关键理论及源码分析)

    1 基础概念 1.1 Short Weierstrass Curve 椭圆曲线(Elliptic Curve,EC)是密码学中非常重要的代数结构,在几何上,椭圆曲线是由三次方程描述的一些曲线.不同的公 ...

  8. 将各种实体类转成JSON(com.alibaba.fastjson.JSONObject)

    当和其他系统对接时,因为某些原因,本系统需要根据不同条件查询不通的实体类,并以JSON格式传输数据,需要将各种实体类转成json 1 public <U> JSONObject proce ...

  9. MySQL 09 普通索引和唯一索引,应该怎么选择?

    唯一索引:字段值不能重复. 普通索引:字段值可以重复. 假设数据如下图,且字段k上的值都不重复: 接下来,从两种索引对查询语句和更新语句的性能影响来分析. 查询过程 假设查询语句为select id ...

  10. GX面试笔试

    awk 转载 http://blog.csdn.net/hairetz/article/details/4141043/ 一.预备知识-程序的内存分配    一个由C/C++编译的程序占用的内存分为以 ...