65.9K
CodeProject 正在变化。 阅读更多。
Home

SIcon 甘特图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (17投票s)

2008 年 2 月 27 日

GPL3

3分钟阅读

viewsIcon

178553

downloadIcon

4351

使用 JScript 的快速且创新的甘特图。

引言

我正在开发一个基于 Web 的应用程序,用于管理项目中的任务和事件。 需要一个甘特图来以可视化的方式显示任务列表,我尝试寻找一个可免费使用的解决方案(因为我不想增加预算)。 我找到了一些,但都不是很好。 所以,我决定自己构建一个。 这是我工作的第一个成果。 SIcon 的这个第一个版本只用了几个小时就构建完成了,所以我认为它仍然存在一些问题。 但是,我想与您分享这个想法。 希望这有帮助。

SIcon 现在支持 FireFox!

背景

只需要对 JScript 和 CSS 有一些了解即可理解和使用它。

使用代码

代码并不复杂,所以我将在这里展示所有代码

/* --------- SICON GANTT CHART ---------------------------------------------------------
* AUTHOR        : Dathq - ICT Service Engineering Jsc, - dathq@ise.com.vn
* LICENSE        : Free
* DESCRIPTION    : Create a new task item with these info
*        - from: start date (format: mm/dd/dddd)
*        - to: deadline of task (format: mm/dd/dddd)
*        - task: name of the task, what has to be solved (not includes ')
*        - resource: who have to solve this task (not includes ')
*        - progress: how is it going? (format: integer value from 0 to 100,
*                    not includes %)
*--------------------------------------------------------------------------------------*/
function Task(from, to, task, resource, progress)
{
    var _from = new Date();    
    var _to = new Date();
    var _task = task;
    var _resource = resource;                        
    var _progress = progress;
    var dvArr = from.split('/');
    _from.setFullYear(parseInt(dvArr[2], 10), parseInt(dvArr[0], 10) - 1,
        parseInt(dvArr[1], 10));
    dvArr = to.split('/'); 
    _to.setFullYear(parseInt(dvArr[2], 10), parseInt(dvArr[0], 10) - 1,
        parseInt(dvArr[1], 10));        

    this.getFrom = function(){ return _from};
    this.getTo = function(){ return _to};
    this.getTask = function(){ return _task};
    this.getResource = function(){ return _resource};
    this.getProgress = function(){ return _progress};
}

function Gantt(gDiv)
{
    var _GanttDiv = gDiv;
    var _taskList = new Array();        
    this.AddTaskDetail = function(value)
    {
        _taskList.push(value);

    }
    this.Draw = function()
    {
        var _offSet = 0;
        var _dateDiff = 0;
        var _currentDate = new Date();
        var _maxDate = new Date();
        var _minDate = new Date();    
        var _dTemp = new Date();
        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>";
        var _thirdRow = ""; 
        var _gStr = "";        
        var _colSpan = 0;
        var counter = 0;

        _currentDate.setFullYear(_currentDate.getFullYear(), _currentDate.getMonth(),
            _currentDate.getDate());
        if(_taskList.length > 0)
        {
            _maxDate.setFullYear(_taskList[0].getTo().getFullYear(),
                _taskList[0].getTo().getMonth(), _taskList[0].getTo().getDate());
            _minDate.setFullYear(_taskList[0].getFrom().getFullYear(),
                _taskList[0].getFrom().getMonth(), _taskList[0].getFrom().getDate());
            for(i = 0; i < _taskList.length; i++)
            {
                if(Date.parse(_taskList[i].getFrom()) < Date.parse(_minDate))
                    _minDate.setFullYear(_taskList[i].getFrom().getFullYear(),
                    _taskList[i].getFrom().getMonth(), _taskList[i].getFrom().getDate());
                if(Date.parse(_taskList[i].getTo()) > Date.parse(_maxDate))
                    _maxDate.setFullYear(_taskList[i].getTo().getFullYear(),
                    _taskList[i].getTo().getMonth(), _taskList[i].getTo().getDate()); 
            }

            //---- Fix _maxDate value for better displaying-----
            // Add at least 5 days

            if(_maxDate.getMonth() == 11) //December
            {
                if(_maxDate.getDay() + 5 > getDaysInMonth(_maxDate.getMonth() + 1,
                    _maxDate.getFullYear()))
                    //The fifth day of next month will be used
                    _maxDate.setFullYear(_maxDate.getFullYear() + 1, 1, 5);       
            else
                    //The fifth day of next month will be used
                    _maxDate.setFullYear(_maxDate.getFullYear(), _maxDate.getMonth(),
                        _maxDate.getDate() + 5);            }
            else
            {
                if(_maxDate.getDay() + 5 > getDaysInMonth(_maxDate.getMonth() + 1,
                    _maxDate.getFullYear()))
                    //The fifth day of next month will be used
                    _maxDate.setFullYear(_maxDate.getFullYear(), _maxDate.getMonth() + 1,
                        5);
                else
                    //The fifth day of next month will be used
                    _maxDate.setFullYear(_maxDate.getFullYear(), _maxDate.getMonth(),
                        _maxDate.getDate() + 5);
            }

            //--------------------------------------------------

            _gStr = "";
            _gStr += "</tr><tr>";
            _thirdRow = "<tr><td> </td>";
            _dTemp.setFullYear(_minDate.getFullYear(), _minDate.getMonth(),
                _minDate.getDate());
            while(Date.parse(_dTemp) <= Date.parse(_maxDate))
            {    
                if(_dTemp.getDay() % 6 == 0) //Weekend
                {
                    _gStr += "<td class='GWeekend'><div style='width:24px;'>" +
                        _dTemp.getDate() + "</div></td>";
                    if(Date.parse(_dTemp) == Date.parse(_currentDate))                   
                        _thirdRow += "<td id='GC_" + (counter++) + 
                        "' class='GToDay' style='height:" +
                        (_taskList.length * 21) + "'> </td>";
                    else
                        _thirdRow += "<td id='GC_" + (counter++) +
                        "' class='GWeekend' style='height:" + (_taskList.length * 21) +
                        "'> </td>";
                }
                else
                {
                    _gStr += "<td class='GDay'><div style='width:24px;'>" +
                        _dTemp.getDate() + "</div></td>";
                    if(Date.parse(_dTemp) == Date.parse(_currentDate))                   
                        _thirdRow += "<td id='GC_" + (counter++) +
                        "' class='GToDay' style='height:" + (_taskList.length * 21) +
                        "'> </td>";
                    else
                        _thirdRow += "<td id='GC_" + (counter++) + 
                       "' class='GDay'> </td>";
                }
                if(_dTemp.getDate() < getDaysInMonth(_dTemp.getMonth() + 1,
                    _dTemp.getFullYear()))
                {
                    if(Date.parse(_dTemp) == Date.parse(_maxDate))
                    {                            
                        _firstRowStr += "<td class='GMonth' colspan='" + 
                       (_colSpan + 1) + "'>T" + (_dTemp.getMonth() + 1) + "/" + 
                       _dTemp.getFullYear() + "</td>"; 
                    }
                    _dTemp.setDate(_dTemp.getDate() + 1);
                    _colSpan++;
                }                    
                else 
                {
                    _firstRowStr += "<td class='GMonth' colspan='" +
                        (_colSpan + 1) + "'>T" + (_dTemp.getMonth() + 1) +
                        "/" + _dTemp.getFullYear() + "</td>";
                    _colSpan = 0;
                    if(_dTemp.getMonth() == 11) //December
                    {
                        _dTemp.setFullYear(_dTemp.getFullYear() + 1, 0, 1);
                    }
                    else
                    {
                        _dTemp.setFullYear(_dTemp.getFullYear(),
                            _dTemp.getMonth() + 1, 1);
                    }
                }                    
            }
            _thirdRow += "</tr>";                 
            _gStr += "</tr>" + _thirdRow;                
            _gStr += "</table>";
            _gStr = _firstRowStr + _gStr;                
            for(i = 0; i < _taskList.length; i++)
            {
                _offSet = (Date.parse(_taskList[i].getFrom()) - Date.parse(_minDate)) /
                    (24 * 60 * 60 * 1000);
                _dateDiff = (Date.parse(_taskList[i].getTo()) - 
                    Date.parse(_taskList[i].getFrom())) / (24 * 60 * 60 * 1000) + 1;
                _gStr += "<div style='position:absolute; top:" + (20 * (i + 2)) + 
                "; left:" + (_offSet * 27 + 204) + "; width:" + 
                (27 * _dateDiff - 1 + 100) + "'><div title='" + 
                _taskList[i].getTask() + "' class='GTask' style='float:left; width:" +
                (27 * _dateDiff - 1) + "px;'>" +
                getProgressDiv(_taskList[i].getProgress()) +
                "</div><div style='float:left; padding-left:3'>" + 
                _taskList[i].getResource() + "</div></div>";
                _gStr += "<div style='position:absolute; top:" +
                    (20 * (i + 2) + 1) + "; left:5px'>" + _taskList[i].getTask() +
                    "</div>";
            }
            _GanttDiv.innerHTML = _gStr;
        }
    }
}        

function getProgressDiv(progress)
{
    return "<div class='GProgress' style='width:" + progress +
       "%; overflow:hidden'></div>"
}
// GET NUMBER OF DAYS IN MONTH
function getDaysInMonth(month, year)  
{

    var days;        
    switch(month)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        days = 31;
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        days = 30;
        break;
    case 2:
        if (((year% 4)==0) && ((year% 100)!=0) || ((year% 400)==0))                
            days = 29;                
        else                                
            days = 28;                
        break;
    }
    return (days);
}                
/*----- END OF MY CODE FOR Gantt CHART GENERATOR -----*/

如何使用此脚本

在您的 HTML、ASCX、ASPX 或 PHP 文档的主体中... 将这些行放在您想要显示甘特图的位置。

<body>    
    <h3>Diagram</h3>    
    <div style="position:relative" class="Gantt" id="GanttChart"></div>
</body>
<script>
    var g = new Gantt(document.all.GanttChart);
    g.AddTaskDetail(new Task('2/11/2008', '2/12/2008', 
                    '<b>Sample task 1 1</b>', 'Dathq', 50));
    g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', 
                    '... Sample task 1.1', 'Dathq, Thanhdd', 30));
    g.AddTaskDetail(new Task('2/12/2008', '3/4/2008', 'Sample task 2', 'Hanhnd', 60));
    g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', 'Sample task 3', 'Dathq', 50));
    
    g.Draw();    
</script>

使用语句 var g = new Gantt(document.all.GanttChart); 将您的甘特图渲染到名为“GanttChart”的 div 元素(您可以在 body 中看到它)。

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

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

如何更改此组件的外观

这里,我定义了一些用于自定义甘特图的类,并提供了完整的源代码。 您可以定义更多的类,并且可以非常轻松地更改甘特图的外观。

通过更改样式表,并在我的代码中为任务对象添加一些属性,您可以制作一个智能甘特图。 例如:不同的任务条颜色、漂亮的背景图像、更好的图表工具提示,...

/*----- SICON GANTT CHART STYLE CLASSES --------------------------
* DESCRIPTION    : Theses class is required for SIcon Gantt Chart
* NOTE            : Should change the color, the text style only
*----------------------------------------------------------------*/
.Gantt
{
    font-family:tahoma, arial, verdana;
    font-size:11px;
}

.GTaskTitle
{
    font-family:tahoma, arial, verdana;
    font-size:11px;
    font-weight:bold;
}

.GMonth
{
    padding-left:5px;
    font-family:tahoma, arial, verdana;
    font-size:11px;
    font-weight:bold;    
}

.GToday
{
    background-color: #FDFDE0;    
}

.GWeekend
{
    font-family:tahoma, arial, verdana;
    font-size:11px;
    background-color:#F5F5F5;
    text-align:center;
}

.GDay
{
    font-family:tahoma, arial, verdana;
    font-size:11px;
    text-align:center;
}

.GTask
{
    border-top:1px solid #CACACA;
    border-bottom:1px solid #CACACA;
    height:14px;
    background-color:yellow;
}

.GProgress
{
    background-color:black;
    height:2px;
    overflow: hidden;
    margin-top:5px;
}

图表

var g = new Gantt(document.all.GanttChart);
g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', 'Sample task 1 1', 'Dathq', 50));
g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', '... Sample task 1.1', 
    'Dathq, Thanhdo', 30));
g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', 'Sample task 3', 'Dathq', 50));
g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', 'Sample task 1 1', 'Dathq', 50));
g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', '... Sample task 1.1',
    'Dathq, Thanhdo', 30));
g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', 'Sample task 3', 'Dathq', 50));
g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', 'Sample task 1 1', 'Dathq', 50));
g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', '... Sample task 1.1',
    'Dathq, Thanhdo', 30));
g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
g.AddTaskDetail(new Task('2/11/2008', '2/16/2008', 'Sample task 3', 'Dathq', 50));

g.AddTaskDetail(new Task('2/11/2008', '2/19/2008', 'Sample task 1 1', 'Dathq', 50));
g.AddTaskDetail(new Task('2/16/2008', '2/19/2008', '... Sample task 1.1',
     'Dathq, Thanhdo', 30));
g.AddTaskDetail(new Task('2/12/2008', '3/2/2008', 'Sample task 2', 'Hanhnd', 60));
g.AddTaskDetail(new Task('5/11/2008', '5/16/2008', 'Sample task 3', 'Dathq', 50));

g.Draw();

关注点

您会注意到的第一个点是该图运行速度非常快。

SIcon 甘特图是用 JavaScript 构建的,这意味着您可以在大多数 Web 开发场景中使用它。 我喜欢 ASP.NET,当然我可以在 ASP.NET 中使用此组件。 此外,您可以使用 AJAX 来获得更酷的应用程序。 您可以根据需要自定义 CSS 类,并获得一个“美观”的甘特图。 我将尝试添加一个链接到任务,以描述它们之间的关系,以及像 MS Project 中看到的那样显示父任务。

甘特图使用 HTML 显示,使其具有轻量级的结构,因此您可以为图表添加更多功能。 例如,工具提示、链接、图像... 通过对 Jscript 代码进行一些基本更改。

最后一点,它是完全免费的。

编码愉快!:)

历史

  • 2008 年 2 月 27 日:SIcon 甘特图的第一个版本。
  • 2008 年 6 月 7 日:修复了 Firefox 中的显示错误。
© . All rights reserved.