简介
笔者在开发一个基于Web的应用程序提供对人物和项目事件的管理。项目需要一个甘特图 控件可视化表示任务列表,笔者尝试寻找自由可用的解决方案(因为不想增加开销)。笔者找到了一些例子,不过并不满意。于是,决定创建一个自己的甘特图控件。此次是笔者工作的最初成果。这个SIcon的最初版本只做了几个小时,所以仍会有些问题。尽管如此,笔者还是希望与各位分享,希望能帮助大家。

SICon现在支持FireFox浏览器!

背景

掌握和使用这个示例需要Jscript和 CSS知识。

使用代码

代码并不是很复杂,所以我在这里将展示全部:

脚本代码

1.   1 /* --------- SICON GANTT CHART -----------------------------------------------------------
2.   2  * AUTHOR        : Dathq - ICT Service Engineering Jsc, - [email]dathq@ise.com.vn[/email]
3.   3  * LICENSE        : Free
4.   4  * DESCRIPTION    : Create a new task item with these info
5.   5  *        - from: start date (format: mm/dd/dddd)
6.   6  *        - to: deadline of task (format: mm/dd/dddd)
7.   7  *        - task: name of the task, what has to be solved (not includes ')
8.   8  *        - resource: who have to solve this task (not includes ')
9.   9  *        - progress: how is it going? (format: integer value from 0 to 100, not includes %)
10. 10  *----------------------------------------------------------------------------------------*/
11. 11 function Task(from, to, task, resource, progress)
12. 12 {
13. 13    var _from = new Date();    
14. 14    var _to = new Date();
15. 15    var _task = task;
16. 16    var _resource = resource;                        
17. 17    var _progress = progress;
18. 18    var dvArr = from.split('/');
19. 19    _from.setFullYear(parseInt(dvArr[2], 10), parseInt(dvArr[0], 10) - 1, parseInt(dvArr[1], 10));
20. 20    dvArr = to.split('/'); 
21. 21    _to.setFullYear(parseInt(dvArr[2], 10), parseInt(dvArr[0], 10) - 1, parseInt(dvArr[1], 10));        
22. 22    
23. 23    this.getFrom = function(){ return _from};
24. 24    this.getTo = function(){ return _to};
25. 25    this.getTask = function(){ return _task};
26. 26    this.getResource = function(){ return _resource};
27. 27    this.getProgress = function(){ return _progress};
28. 28 }
29. 29 
30. 30 function Gantt(gDiv)
31. 31 {
32. 32    var _GanttDiv = gDiv;
33. 33    var _taskList = new Array();        
34. 34    this.AddTaskDetail = function(value)
35. 35    {
36. 36        _taskList.push(value);
37. 37        
38. 38    }
39. 39    this.Draw = function()
40. 40    {
41. 41        var _offSet = 0;
42. 42        var _dateDiff = 0;
43. 43        var _currentDate = new Date();
44. 44        var _maxDate = new Date();
45. 45        var _minDate = new Date();    
46. 46        var _dTemp = new Date();
47. 47        var _firstRowStr = "<table border=1 style='border-collapse:collapse'><tr><td rowspan='2' width='200px' style='width:200px;'><div class='GTaskTitle' style='width:200px;'>Task</div></td>";
48. 48        var _thirdRow = ""; 
49. 49        var _gStr = "";        
50. 50        var _colSpan = 0;
51. 51        var counter = 0;
52. 52        
53. 53        _currentDate.setFullYear(_currentDate.getFullYear(), _currentDate.getMonth(), _currentDate.getDate());
54. 54        if(_taskList.length > 0)
55. 55        {
56. 56            _maxDate.setFullYear(_taskList[0].getTo().getFullYear(), _taskList[0].getTo().getMonth(), _taskList[0].getTo().getDate());
57. 57            _minDate.setFullYear(_taskList[0].getFrom().getFullYear(), _taskList[0].getFrom().getMonth(), _taskList[0].getFrom().getDate());
58. 58            for(i = 0; i < _taskList.length; i++)
59. 59            {
60. 60                if(Date.parse(_taskList.getFrom()) < Date.parse(_minDate))
61. 61                    _minDate.setFullYear(_taskList.getFrom().getFullYear(), _taskList.getFrom().getMonth(), _taskList.getFrom().getDate());
62. 62                if(Date.parse(_taskList.getTo()) > Date.parse(_maxDate))
63. 63                    _maxDate.setFullYear(_taskList.getTo().getFullYear(), _taskList.getTo().getMonth(), _taskList.getTo().getDate());                        
64. 64            }
65. 65            
66. 66            //---- Fix _maxDate value for better displaying-----
67. 67            // Add at least 5 days
68. 68            
69. 69            if(_maxDate.getMonth() == 11) //December
70. 70            {
71. 71                if(_maxDate.getDay() + 5 > getDaysInMonth(_maxDate.getMonth() + 1, _maxDate.getFullYear()))                    
72. 72                    _maxDate.setFullYear(_maxDate.getFullYear() + 1, 1, 5); //The fifth day of next month will be used
73. 73                else
74. 74                    _maxDate.setFullYear(_maxDate.getFullYear(), _maxDate.getMonth(), _maxDate.getDate() + 5); //The fifth day of next month will be used
75. 75            }
76. 76            else
77. 77            {
78. 78                if(_maxDate.getDay() + 5 > getDaysInMonth(_maxDate.getMonth() + 1, _maxDate.getFullYear()))                    
79. 79                    _maxDate.setFullYear(_maxDate.getFullYear(), _maxDate.getMonth() + 1, 5); //The fifth day of next month will be used
80. 80                else
81. 81                    _maxDate.setFullYear(_maxDate.getFullYear(), _maxDate.getMonth(), _maxDate.getDate() + 5); //The fifth day of next month will be used
82. 82            }
83. 83            
84. 84            //--------------------------------------------------
85. 85            
86. 86            _gStr = "";
87. 87            _gStr += "</tr><tr>";
88. 88            _thirdRow = "<tr><td> </td>";
89. 89            _dTemp.setFullYear(_minDate.getFullYear(), _minDate.getMonth(), _minDate.getDate());
90. 90            while(Date.parse(_dTemp) <= Date.parse(_maxDate))
91. 91            {    
92. 92                if(_dTemp.getDay() % 6 == 0) //Weekend
93. 93                {
94. 94                    _gStr += "<td class='GWeekend'><div style='width:24px;'>" + _dTemp.getDate() + "</div></td>";
95. 95                    if(Date.parse(_dTemp) == Date.parse(_currentDate))                        
96. 96                        _thirdRow += "<td id='GC_" + (counter++) + "' class='GToDay' style='height:" + (_taskList.length * 21) + "'> </td>";
97. 97                    else
98. 98                        _thirdRow += "<td id='GC_" + (counter++) + "' class='GWeekend' style='height:" + (_taskList.length * 21) + "'> </td>";
99. 99                }
100. 100                else
101. 101                {
102. 102                    _gStr += "<td class='GDay'><div style='width:24px;'>" + _dTemp.getDate() + "</div></td>";
103. 103                    if(Date.parse(_dTemp) == Date.parse(_currentDate))                        
104. 104                        _thirdRow += "<td id='GC_" + (counter++) + "' class='GToDay' style='height:" + (_taskList.length * 21) + "'> </td>";
105. 105                    else
106. 106                        _thirdRow += "<td id='GC_" + (counter++) + "' class='GDay'> </td>";
107. 107                }
108. 108                if(_dTemp.getDate() < getDaysInMonth(_dTemp.getMonth() + 1, _dTemp.getFullYear()))
109. 109                {
110. 110                    if(Date.parse(_dTemp) == Date.parse(_maxDate))
111. 111                    {                            
112. 112                        _firstRowStr += "<td class='GMonth' colspan='" + (_colSpan + 1) + "'>T" + (_dTemp.getMonth() + 1) + "/" + _dTemp.getFullYear() + "</td>";                            
113. 113                    }
114. 114                    _dTemp.setDate(_dTemp.getDate() + 1);
115. 115                    _colSpan++;
116. 116                }                    
117. 117                else 
118. 118                {
119. 119                    _firstRowStr += "<td class='GMonth' colspan='" + (_colSpan + 1) + "'>T" + (_dTemp.getMonth() + 1) + "/" + _dTemp.getFullYear() + "</td>";
120. 120                    _colSpan = 0;
121. 121                    if(_dTemp.getMonth() == 11) //December
122. 122                    {
123. 123                        _dTemp.setFullYear(_dTemp.getFullYear() + 1, 0, 1);
124. 124                    }
125. 125                    else
126. 126                    {
127. 127                        _dTemp.setFullYear(_dTemp.getFullYear(), _dTemp.getMonth() + 1, 1);
128. 128                    }
129. 129                }                    
130. 130            }
131. 131            _thirdRow += "</tr>";                
132. 132            _gStr += "</tr>" + _thirdRow;                
133. 133            _gStr += "</table>";
134. 134            _gStr = _firstRowStr + _gStr;                
135. 135            for(i = 0; i < _taskList.length; i++)
136. 136            {
137. 137                _offSet = (Date.parse(_taskList.getFrom()) - Date.parse(_minDate)) / (24 * 60 * 60 * 1000);
138. 138                _dateDiff = (Date.parse(_taskList.getTo()) - Date.parse(_taskList.getFrom())) / (24 * 60 * 60 * 1000) + 1;
139. 139                _gStr += "<div style='position:absolute; top:" + (20 * (i + 2)) + "; left:" + (_offSet * 27 + 204) + "; width:" + (27 * _dateDiff - 1 + 100) + "'><div title='" + _taskList.getTask() + "' class='GTask' style='float:left; width:" + (27 * _dateDiff - 1) + "px;'>" + getProgressDiv(_taskList.getProgress()) + "</div><div style='float:left; padding-left:3'>" + _taskList.getResource() + "</div></div>";
140. 140                _gStr += "<div style='position:absolute; top:" + (20 * (i + 2) + 1) + "; left:5px'>" + _taskList.getTask() + "</div>";
141. 141            }
142. 142            _GanttDiv.innerHTML = _gStr;
143. 143        }
144. 144    }
145. 145 }        
146. 146 
147. 147 function getProgressDiv(progress)
148. 148 {
149. 149    return "<div class='GProgress' style='width:" + progress + "%; overflow:hidden'></div>"
150. 150 }
151. 151 // GET NUMBER OF DAYS IN MONTH
152. 152 function getDaysInMonth(month, year)  
153. 153 {
154. 154    
155. 155    var days;        
156. 156    switch(month)
157. 157    {
158. 158        case 1:
159. 159        case 3:
160. 160        case 5:
161. 161        case 7:
162. 162        case 8:
163. 163        case 10:
164. 164        case 12:
165. 165            days = 31;
166. 166            break;
167. 167        case 4:
168. 168        case 6:
169. 169        case 9:
170. 170        case 11:
171. 171            days = 30;
172. 172            break;
173. 173        case 2:
174. 174            if (((year% 4)==0) && ((year% 100)!=0) || ((year% 400)==0))                
175. 175                days = 29;                
176. 176            else                                
177. 177                days = 28;                
178. 178            break;
179. 179    }
180. 180    return (days);
181. 181 }                
182. 182 /*----- END OF MY CODE FOR Gantt CHART GENERATOR -----*/
 
 复制代码 
 
如何使用这段脚本 


 在你的HTML, ASCX, ASPX或PHP文档的Body部分,把下面几行代码拷贝到你想表示甘特图的地方。 
1. 1 <body>    
2. 2    <h3>Diagram</h3>    
3. 3    <div style="position:relative" class="Gantt" id="GanttChart"></div>
4. 4 </body>
5. 5 <script>
6. 6    var g = new Gantt(document.all.GanttChart);
7. 7    g.AddTaskDetail(new Task('2/11/2008', '2/12/2008', 
8. 8                    '<b>Sample task 1 1</b>', 'Dathq', 50));
9. 9    g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', 
10. 10                    ' Sample task 1.1', 'Dathq, Thanhdd', 30));
11. 11    g.AddTaskDetail(new Task('2/12/2008', '3/4/2008', 'Sample task 2', 'Hanhnd', 60));
12. 12    g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', 'Sample task 3', 'Dathq', 50));
13. 13    
14. 14    g.Draw();    
15. 15 </script>
 
 复制代码 
 使用var g = new Gantt(document.all.GanttChart);语句指定你要显示的甘特图到命名为"GanttChart"的DIV元素(你可以在body中看到它)。 


 g.AddTaskDetail()方法添加任务到任务列表。你可以使用AJAX或任何你喜欢的方式来生成这些命令以添加任务的集合。 


 使用g.Draw()来渲染基于追加到甘特图对象的任务列表的甘特图。 


如何改变组件外观 


 这里笔者定义了一些类用来自定义甘特图的样式,为此提供了全部代码。你可以定义更多的类并且更轻易地改变甘特图控件的外观。 


 通过改变样式表,并且在代码中添加属性到任务对象,你可以为你自己创建更加智能的甘特图。例如:为任务条设置不同的颜色,漂亮的背景图片,更好的图标提示等等。 


 样式表 
1. 1    /*----- SICON GANTT CHART STYLE CLASSES --------------------------
2. 2      * DESCRIPTION    : Theses class is required for SIcon Gantt Chart
3. 3      * NOTE            : Should change the color, the text style only
4. 4      *----------------------------------------------------------------*/
5. 5    .Gantt
6. 6    {
7. 7        font-family:tahoma, arial, verdana;
8. 8        font-size:11px;
9. 9    }
10. 10    
11. 11    .GTaskTitle
12. 12    {
13. 13        font-family:tahoma, arial, verdana;
14. 14        font-size:11px;
15. 15        font-weight:bold;
16. 16    }
17. 17    
18. 18    .GMonth
19. 19    {
20. 20        padding-left:5px;
21. 21        font-family:tahoma, arial, verdana;
22. 22        font-size:11px;
23. 23        font-weight:bold;    
24. 24    }
25. 25    
26. 26    .GToday
27. 27    {
28. 28        background-color: #FDFDE0;    
29. 29    }
30. 30    
31. 31    .GWeekend
32. 32    {
33. 33        font-family:tahoma, arial, verdana;
34. 34        font-size:11px;
35. 35        background-color:#F5F5F5;
36. 36        text-align:center;
37. 37    }
38. 38    
39. 39    .GDay
40. 40    {
41. 41        font-family:tahoma, arial, verdana;
42. 42        font-size:11px;
43. 43        text-align:center;
44. 44    }
45. 45    
46. 46    .GTask
47. 47    {
48. 48        border-top:1px solid #CACACA;
49. 49        border-bottom:1px solid #CACACA;
50. 50        height:14px;
51. 51        background-color:yellow;
52. 52    }
53. 53    
54. 54    .GProgress
55. 55    {
56. 56        background-color:black;
57. 57        height:2px;
58. 58        overflow: hidden;
59. 59        margin-top:5px;
60. 60    }
 
 复制代码 
 
图表 


 Code 
1. 1    var g = new Gantt(document.all.GanttChart);
2. 2    g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', '<b>Sample task 1 1</b>', 'Dathq', 50));
3. 3    g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', ' Sample task 1.1', 'Dathq, Thanhdo', 30));
4. 4    g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
5. 5    g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', '<i>Sample task 3<i>', 'Dathq', 50));
6. 6    g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', '<b>Sample task 1 1</b>', 'Dathq', 50));
7. 7    g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', ' Sample task 1.1', 'Dathq, Thanhdo', 30));
8. 8    g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
9. 9    g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', '<i>Sample task 3<i>', 'Dathq', 50));
10. 10    g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', '<b>Sample task 1 1</b>', 'Dathq', 50));
11. 11    g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', ' Sample task 1.1', 'Dathq, Thanhdo', 30));
12. 12    g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
13. 13    g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', '<i>Sample task 3<i>', 'Dathq', 50));
14. 14 
15. 15    g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', '<b>Sample task 1 1</b>', 'Dathq', 50));
16. 16    g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', ' Sample task 1.1', 'Dathq, Thanhdo', 30));
17. 17    g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
18. 18    g.AddTaskDetail(new Task('5/11/2008', '5/16/2008', '<i>Sample task 3<i>', 'Dathq', 50));
19. 19 
20. 20    g.Draw();


复制代码


兴趣点

你会先注意到图标运行起来速度很快。

SIcon甘特图是通过Javascript创建的,这意味着你可以在众多的Web开发环境中使用它。我比较喜欢ASP.NET,所以我会把此控件和ASP.NET一起使用。另外,你可以使用AJAX创建更酷的应用。你可以自定义CSS类得到更好看的甘特图。我将试图添加链接到任务用来描述他们的关系,并将显示一个父任务,就像我们在MS Project中看到的一样。

甘特图通过HTML显示,因为它使用的是轻量级结构,所以你可以添加更多的功能到图表。例如,提示、链接、图片等,在JScript中使用一些基本的变化就可以获得。