MVC使用Gantt Chart实现甘特图,管理事情进度

借助"甘特图",可以直观地了解任务、活动、工作的进度。dhtmlxGantt是一个开源的Javacirpt库,能帮助我们快速创建"甘特图",本篇体验在MVC中的实现。主要包括:

  • 认识"甘特图"
  • 下载dhtmlxGantt包
  • 把dhtmlxGantt相关CSS、JS、样式引入到_Layout.cshtml中
  • 初始化dhtmlxGantt
  • 通过EF Code First创建初始数据
  • 显示数据
  • 保存数据

认识"甘特图"

下载dhtmlxGantt包

通过NuGet,输入关键字"Gantt Chart",下载dhtmlxGantt包。

把dhtmlxGantt相关CSS、JS、样式引入到_Layout.cshtml中

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    <script src="~/Scripts/dhtmlxgantt/dhtmlxgantt.js"></script>
    <link href="~/Content/dhtmlxgantt/dhtmlxgantt.css" rel="stylesheet" />
    <script src="~/Scripts/dhtmlxgantt/locale/locale_cn.js"></script>
    <style type="text/css">
        html, body {
            height: 100%;
            padding: 0px;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>
    @RenderBody()
    <script src="~/Scripts/main.js"></script>
</body>

以上,locale_cn.js用来汉化,main.js用来初始化配置。

初始化dhtmlxGantt

在Home/Index.cshtml中,创建一个id为ganttContainer的div,dhtmlxGantt将被加载到此div中。

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="ganttContainer" style="width: 100%; height: 100%;"></div>

main.js中的配置如下:

(function () {

    // add month scale
    gantt.config.scale_unit = "week"; //第一个时间尺度,即X轴的单位,包括:"minute", "hour", "day", "week", "month", "year"
    gantt.config.step = 1;//步进,默认为1
    gantt.templates.date_scale = function (date) {//日期格式化
        var dateToStr = gantt.date.date_to_str("%d %M");
        var endDate = gantt.date.add(gantt.date.add(date, 1, "week"), -1, "day");
        return dateToStr(date) + " - " + dateToStr(endDate);
    };
    gantt.config.subscales = [ //第二个时间尺度单位
        { unit: "day", step: 1, date: "%D" }
    ];
    gantt.config.scale_height = 50; //设置时间尺度和Grid树的高度

    // configure milestone description
    gantt.templates.rightside_text = function (start, end, task) {//进度条右侧的提示文字
        if (task.type == gantt.config.types.milestone) {
            return task.text;
        }
        return "";
    };
    // add section to type selection: task, project or milestone
    gantt.config.lightbox.sections = [//弹出对话框设置
        { name: "description", height: 70, map_to: "text", type: "textarea", focus: true },
        { name: "type", type: "typeselect", map_to: "type" },
        { name: "time", height: 72, type: "duration", map_to: "auto" }
    ];

    gantt.config.xml_date = "%Y-%m-%d %H:%i:%s"; // XML中的日期格式
    gantt.init("ganttContainer"); // 初始化dhtmlxGantt,ganttContainer为视图中div的id
    gantt.load("/Home/Data", "json");//加载数据

    // enable dataProcessor
    var dp = new dataProcessor("/Home/Save");//dhtmlxGantt保存变化,包括添加、更新、删除
    dp.init(gantt);

})();

通过EF Code First创建初始数据

参照dhtmlxGantt官方文档,我们建立这样的模型:

Link类体现Task间的相关性。

using System.ComponentModel.DataAnnotations;

namespace MyGanttChart.Models
{
    public class Link
    {
        public int Id { get; set; }
        [MaxLength(1)]
        public string Type { get; set; }
        public int SourceTaskId { get; set; }
        public int TargetTaskId { get; set; }
    }
}

Task类是任务的抽象。

using System;
using System.ComponentModel.DataAnnotations;

namespace MyGanttChart.Models
{
    public class Task
    {
        public int Id { get; set; }
        [MaxLength(255)]
        public string Text { get; set; }
        public DateTime StartDate { get; set; }
        public int Duration { get; set; }
        public decimal Progress { get; set; }
        public int SortOrder { get; set; }
        public string Type { get; set; }
        public int? ParentId { get; set; }
    }
}

创建一个派生于DbContext的上下文类:

using System.Data.Entity;
using MyGanttChart.Models;

namespace MyGanttChart.DAL
{
    public class GanttContext : DbContext
    {
        public GanttContext() : base("GanttContext") { }

        public DbSet<Task> Tasks { get; set; }
        public DbSet<Link> Links { get; set; }
    }
}

创建一些种子数据:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using MyGanttChart.Models;

namespace MyGanttChart.DAL
{
    public class GanttInitializer : DropCreateDatabaseIfModelChanges<GanttContext>
    {
        protected override void Seed(GanttContext context)
        {
            List<Task> tasks = new List<Task>()
            {
                new Task() { Id = 1, Text = "Project #2", StartDate = DateTime.Today.AddDays(-3), Duration = 18, SortOrder = 10, Progress = 0.4m, ParentId = null },
                new Task() { Id = 2, Text = "Task #1", StartDate = DateTime.Today.AddDays(-2), Duration = 8, SortOrder = 10, Progress = 0.6m, ParentId = 1 },
                new Task() { Id = 3, Text = "Task #2", StartDate = DateTime.Today.AddDays(-1), Duration = 8, SortOrder = 20, Progress = 0.6m, ParentId = 1 }
            };

            tasks.ForEach(s => context.Tasks.Add(s));
            context.SaveChanges();

            List<Link> links = new List<Link>()
            {
                new Link() { Id = 1, SourceTaskId = 1, TargetTaskId = 2, Type = "1" },
                new Link() { Id = 2, SourceTaskId = 2, TargetTaskId = 3, Type = "0" }
            };

            links.ForEach(s => context.Links.Add(s));
            context.SaveChanges();
        }
    }
}

在Web.config中配置种子数据:

<entityFramework>
    <contexts>
      <context type="MyGanttChart.DAL.GanttContext, MyGanttChart">
        <databaseInitializer type="MyGanttChart.DAL.GanttInitializer, MyGanttChart" />
      </context>
    </contexts>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>

以上,type="MyGanttChart.DAL.GanttInitializer, MyGanttChart"中,MyGanttChart.DAL为种子类GanttInitializer所在的命名空间,MyGanttChart为程序集的名称。

在Web.config中配置连接字符串:

<connectionStrings>
    <add name="GanttContex"
       connectionString="Data Source=.;User=some user name;Password=some password;Initial Catalog=Gantt;Integrated Security=True"
       providerName="System.Data.SqlClient"/>
  </connectionStrings>

显示数据

HomeController的Data()方法加载数据,以json格式返回。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Xml.Linq;
using MyGanttChart.DAL;
using MyGanttChart.Models;

namespace MyGanttChart.Controllers
{
    public class HomeController : Controller
    {
        private readonly GanttContext db = new GanttContext();

        public ActionResult Index()
        {
            return View();
        }

        [HttpGet]
        public JsonResult Data()
        {
            var jsonData = new
            {

                data = (
                    from t in db.Tasks.AsEnumerable()
                    select new
                    {
                        id = t.Id,
                        text = t.Text,
                        start_date = t.StartDate.ToString("u"),
                        duration = t.Duration,
                        order = t.SortOrder,
                        progress = t.Progress,
                        open = true,
                        parent = t.ParentId,
                        type = (t.Type != null) ? t.Type : String.Empty
                    }
                ).ToArray(),

                links = (
                    from l in db.Links.AsEnumerable()
                    select new
                    {
                        id = l.Id,
                        source = l.SourceTaskId,
                        target = l.TargetTaskId,
                        type = l.Type
                    }
                ).ToArray()
            };

            return new JsonResult { Data = jsonData, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }
        ......
}

在main.js中,已经对加载数据做了设置:

gantt.load("/Home/Data", "json");//加载数据

运行

保存数据

当在"甘特图"上进行任何的操作再保存到数据库的时候,请求表头信息大致如下:

dhtmlxGantt为我们提供了一个GanttRequest类,提供了Parse(FormCollection form, string ganttMode)方法把从客户端拿到的表头信息赋值到GanttRequest类的各个属性中。

public class GanttRequest
    {
        public GanttMode Mode { get; set; }
        public GanttAction Action { get; set; }

        public Task UpdatedTask { get; set; }
        public Link UpdatedLink { get; set; }
        public long SourceId { get; set; }

        /// <summary>
        /// Create new GanttData object and populate it
        /// </summary>
        /// <param name="form">Form collection</param>
        /// <returns>New GanttData</returns>
        public static List<GanttRequest> Parse(FormCollection form, string ganttMode)
        {
            // save current culture and change it to InvariantCulture for data parsing
            var currentCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            var dataActions = new List<GanttRequest>();
            var prefixes = form["ids"].Split(‘,‘);

            foreach (var prefix in prefixes)
            {
                var request = new GanttRequest();

                // lambda expression for form data parsing
                Func<string, string> parse = x => form[String.Format("{0}_{1}", prefix, x)];

                request.Mode = (GanttMode)Enum.Parse(typeof(GanttMode), ganttMode, true);
                request.Action = (GanttAction)Enum.Parse(typeof(GanttAction), parse("!nativeeditor_status"), true);
                request.SourceId = Int64.Parse(parse("id"));

                // parse gantt task
                if (request.Action != GanttAction.Deleted && request.Mode == GanttMode.Tasks)
                {
                    request.UpdatedTask = new Task()
                    {
                        Id = (request.Action == GanttAction.Updated) ? (int)request.SourceId : 0,
                        Text = parse("text"),
                        StartDate = DateTime.Parse(parse("start_date")),
                        Duration = Int32.Parse(parse("duration")),
                        Progress = Decimal.Parse(parse("progress")),
                        ParentId = (parse("parent") != "0") ? Int32.Parse(parse("parent")) : (int?)null,
                        SortOrder = (parse("order") != null) ? Int32.Parse(parse("order")) : 0,
                        Type = parse("type")
                    };
                }
                // parse gantt link
                else if (request.Action != GanttAction.Deleted && request.Mode == GanttMode.Links)
                {
                    request.UpdatedLink = new Link()
                    {
                        Id = (request.Action == GanttAction.Updated) ? (int)request.SourceId : 0,
                        SourceTaskId = Int32.Parse(parse("source")),
                        TargetTaskId = Int32.Parse(parse("target")),
                        Type = parse("type")
                    };
                }

                dataActions.Add(request);
            }

            // return current culture back
            Thread.CurrentThread.CurrentCulture = currentCulture;

            return dataActions;
        }
    }

保存数据,有可能是对Task的操作,也有可能是对Link的操作,把这2种模式封装到一个枚举中:

public enum GanttMode
    {
        Tasks,
        Links
    }

而所有的动作无非是添加、更新、删除等,也封装到枚举中:

public enum GanttAction
    {
        Inserted,
        Updated,
        Deleted,
        Error
    }

接下来,HomeController的Save()方法,根据请求表头的信息,借助GanttRequest类的静态方法Parse(FormCollection form, string ganttMode)把表头信息封装到GanttRequest类的属性中,然后根据这些属性采取相应的操作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Xml.Linq;
using MyGanttChart.DAL;
using MyGanttChart.Models;

namespace MyGanttChart.Controllers
{
    public class HomeController : Controller
    {
        private readonly GanttContext db = new GanttContext();

        ......

        /// <summary>
        /// Update Gantt tasks/links: insert/update/delete
        /// </summary>
        /// <param name="form">Gantt data</param>
        /// <returns>XML response</returns>
        [HttpPost]
        public ContentResult Save(FormCollection form)
        {
            var dataActions = GanttRequest.Parse(form, Request.QueryString["gantt_mode"]);
            try
            {
                foreach (var ganttData in dataActions)
                {
                    switch (ganttData.Mode)
                    {
                        case GanttMode.Tasks:
                            UpdateTasks(ganttData);
                            break;
                        case GanttMode.Links:
                            UpdateLinks(ganttData);
                            break;
                    }
                }
                db.SaveChanges();
            }
            catch
            {
                // return error to client if something went wrong
                dataActions.ForEach(g => { g.Action = GanttAction.Error; });
            }
            return GanttRespose(dataActions);
        }

        /// <summary>
        /// Update gantt tasks
        /// </summary>
        /// <param name="ganttData">GanttData object</param>
        private void UpdateTasks(GanttRequest ganttData)
        {
            switch (ganttData.Action)
            {
                case GanttAction.Inserted:
                    // add new gantt task entity
                    db.Tasks.Add(ganttData.UpdatedTask);
                    break;
                case GanttAction.Deleted:
                    // remove gantt tasks
                    db.Tasks.Remove(db.Tasks.Find(ganttData.SourceId));
                    break;
                case GanttAction.Updated:
                    // update gantt task
                    db.Entry(db.Tasks.Find(ganttData.UpdatedTask.Id)).CurrentValues.SetValues(ganttData.UpdatedTask);
                    break;
                default:
                    ganttData.Action = GanttAction.Error;
                    break;
            }
        }

        /// <summary>
        /// Update gantt links
        /// </summary>
        /// <param name="ganttData">GanttData object</param>
        private void UpdateLinks(GanttRequest ganttData)
        {
            switch (ganttData.Action)
            {
                case GanttAction.Inserted:
                    // add new gantt link
                    db.Links.Add(ganttData.UpdatedLink);
                    break;
                case GanttAction.Deleted:
                    // remove gantt link
                    db.Links.Remove(db.Links.Find(ganttData.SourceId));
                    break;
                case GanttAction.Updated:
                    // update gantt link
                    db.Entry(db.Links.Find(ganttData.UpdatedLink.Id)).CurrentValues.SetValues(ganttData.UpdatedLink);
                    break;
                default:
                    ganttData.Action = GanttAction.Error;
                    break;
            }
        }

        /// <summary>
        /// Create XML response for gantt
        /// </summary>
        /// <param name="ganttData">Gantt data</param>
        /// <returns>XML response</returns>
        private ContentResult GanttRespose(List<GanttRequest> ganttDataCollection)
        {
            var actions = new List<XElement>();
            foreach (var ganttData in ganttDataCollection)
            {
                var action = new XElement("action");
                action.SetAttributeValue("type", ganttData.Action.ToString().ToLower());
                action.SetAttributeValue("sid", ganttData.SourceId);
                action.SetAttributeValue("tid", (ganttData.Mode == GanttMode.Tasks) ? ganttData.UpdatedTask.Id : ganttData.UpdatedLink.Id);
                actions.Add(action);
            }

            var data = new XDocument(new XElement("data", actions));
            data.Declaration = new XDeclaration("1.0", "utf-8", "true");
            return Content(data.ToString(), "text/xml");
        }
    }
}

运行,点击+添加一个新的task:

参考资料:
Gantt Chart for ASP.NET MVC with dhtmlxGantt

MVC使用Gantt Chart实现甘特图,管理事情进度,布布扣,bubuko.com

时间: 2024-07-30 11:32:59

MVC使用Gantt Chart实现甘特图,管理事情进度的相关文章

项目管理-时间管理-甘特图

我发现往往一旦放下一个技术或知识一段时间,再回看它觉得是一个很好的.很需要总结的技能点,然而你当时在用时就不会发现,以为理所应当.现在就是这种情况,在过年休假及婚假的二十来天后,我觉得作为项目经理的我,有这么一个技能是好的,是可以提倡的,是应该持续做下去的,那就是项目管理中十大知识领域这一中很重要的:时间管理. 在上个项目的开发.管理过程中,在时间方面的控制是非常重要的,一方面原因在于业务单位希望在年前把这个项目交付完毕,另一方面的原因在于项目开发时间紧迫.在经历了创建工作分解结构WBS后,便需

生产排程甘特图应用及绘制步骤

概述:甘特图(Gantt chart)是一种按照时间进度标出工作活动的图表,常用于项目管理,航空管理,生产排程等.甘特图(Gantt chart)也叫横道图.条状图.生产计划进度图. 甘特图(Gantt chart)是一种按照时间进度标出工作活动的图表,常用于项目管理,航空管理,生产排程等. 甘特图(Gantt chart)也叫横道图.条状图.生产计划进度图.它是在第一次世界大战时期发明的,以亨利·L·甘特先生的名字命名,他制定了一个完整地用条形图表进度的标志系统.甘特图内在思想简单,即以图示的

教你读懂redmine中的甘特图

Redmine是用Ruby开发的基于web的项目管理软件,他可以自动绘制甘特图,对于我们了解项目进度有很重要的帮助.但很多新人小白在使用redmine时,就是当成一个简单的备忘录来使用,对于甘特图神马的根本就不care,那么如何正确的使用甘特图呢?如何读懂redmine中的甘特图呢? Redmine是可以根据建立的问题的开始时间.结束时间和完成百分比自动绘制甘特图的.所以我们要使用redmine的甘特图,必须在新建问题时根据项目计划设置好开始时间,预估结束时间,每天在结束一天的工作后,注意及时更

21天战拖记——Day17:如何将想法落地:SMART原则、思维导图、甘特图、九宫格、六个高度(2014-05-20)

学习<小强升职记(升级版)>的记录: 用SMART法则厘清你的目标 S--Specific:目标一定要明确,不能够模糊. M--Measurable:目标的可衡量性,是否有一个实现目标的标准. A--Attainable:目标的可实现性.一个目标必须是可以实现的,或者所经过努力是可以实现的. R--Relevant:目标必须和其他目标具有相关性.完成这个目标对你的其他目标有何帮助? T--Time-based:目标必须具有明确的截止日期,即一个目标只有在定的时间内成才有意义. 用思维导图梳理计

一款真正开源且功能强大的C#甘特图控件.NET Winforms Gantt Chart Control

甘特图在项目管理中非常重要,甘特图的思想比较简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间.它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比.管理者由此可便利地弄清一项任务(项目)还剩下哪些工作要做,并可评估工作进度.甘特图可以显示几个部门.机器或设备的运行和闲置情况.这表示了该系统的有关工作负荷状况,这样可使管理人员了解何种调整是恰当的. 由于项目需要,在网上找了很久,经过大量的对比和评估,发现一款真正开源且功能强大的C#甘特图控件.NET

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

本文首发于“生信补给站”微信公众号,https://mp.weixin.qq.com/s/CGz51qOjFSJ4Wx_qOMzjiw 更多关于R语言,ggplot2绘图,生信分析的内容,敬请关注小号. 甘特图(Gantt chart),又常被称为横道图或者条状图,是现代企业项目管理领域运用最为广泛的一种图示.就是通过条形来显示项目的进度.时间安排等相关情况的. 项目管理外,也可以用来管理学习计划.绘制甘特图的工具有很多,本文介绍使用R-plotly包绘制交互式的甘特图,保存html链接后,即可

JQuery.Gantt(甘特图)开发

一.简介 JQuery.Gantt是一个开源的基于JQuery库的用于实现甘特图效果的可扩展功能的JS组件库. 二.前端页面 2.1 资源引用 首先需要将下载到的源码中的CSS.IMG.JS等资源放入我们自己的项目中,然后在我们自己的页面中引用需要的资源文件. CSS样式文件 <link rel="stylesheet" href="css/style.css" /> JS脚本文件 <script src="js/jquery-1.8.2

Twproject Gantt开源甘特图功能扩展

1.Twproject Gantt甘特图介绍 Twproject Gantt 是一款基于 jQuery 开发的甘特图组件,也可以创建其它图表,例如任务树(Task Trees).内置编辑.缩放和 CSS 皮肤等功能.更重要的是,它是免费开源的. 官网地址是:https://gantt.twproject.com/ 源码可以从github下载: 2.扩展功能一:code自动层级编码,满足wbs编码要求 工作分解结构 (WBS) 代码是项目的识别您的分级显示结构中的每个任务的唯一位置的字母数字代码.

[课程分享]IT软件项目管理(企业项目甘特图案例评价、维护管理、文档管理、风险管理、人力资源管理)

对这个课程有兴趣的朋友可以加我的QQ2059055336和我联系 课程讲师:丁冬博士 课程分类:Java 适合人群:中级 课时数量:32课时 用到技术:IT软件项目配置.IT软件项目模板的制定 涉及项目:IT软件企业项目甘特图案.IT软件项目可行性报告分析.基于svn的IT软件项目配置管理案例 更新程度:完成 课程背景: 该课程是北风品牌项目管理课程系列之一<IT项目管理>课程.通过本课程的教学,使学生掌握IT项目管理的基本原理和基本技能,能够根据项目干系人的特征需求,确定项目的范围,经过计划