zoukankan      html  css  js  c++  java
  • MVC使用Gantt Chart实现甘特图,管理事情进度

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

      认识"甘特图"

    2


     

      下载dhtmlxGantt包

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

    1

      把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=" 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");//加载数据

    运行
    3

      保存数据

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


    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:
    4

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

  • 相关阅读:
    PHP下实现两种ajax跨域的解决方案之jsonp
    实际应用中git(合并本地与服务器项目)
    centos7 编译安装nginx
    windows vagrant共享目录设置问题
    shell 需要注意的点
    插入排序(直接插入排序)
    选择排序
    快速排序
    冒泡排序
    centos7.5安装redis-5.0.4
  • 原文地址:https://www.cnblogs.com/lydg/p/11362323.html
Copyright © 2011-2022 走看看