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

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

  认识"甘特图"

2

 

  下载dhtmlxGantt包

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

1

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

复制代码

1<head> 2 <meta charset="utf-8" /> 3 <meta name="viewport" content="width=device-width" /> 4 <title>@ViewBag.Title</title> 5 @Styles.Render("~/Content/css") 6 <script src="https://my.oschina.net//u/4419748/blog/3428287/Scripts/dhtmlxgantt/dhtmlxgantt.js"></script> 7 <link href="https://my.oschina.net//u/4419748/blog/3428287/Content/dhtmlxgantt/dhtmlxgantt.css" rel="stylesheet" /> 8 <script src="https://my.oschina.net//u/4419748/blog/3428287/Scripts/dhtmlxgantt/locale/locale_cn.js"></script> 9 <style type="text/css"> 10 html, body { 11 height: 100%; 12 padding: 0px; 13 margin: 0px; 14 overflow: hidden; 15 } 16 </style> 17</head> 18<body> 19 @RenderBody() 20 <script src="https://my.oschina.net//u/4419748/blog/3428287/Scripts/main.js"></script> 21</body>

复制代码

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

  初始化dhtmlxGantt

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

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

main.js中的配置如下:

复制代码

1(function () { 2 3 // add month scale 4 gantt.config.scale_unit = "week"; //第一个时间尺度,即X轴的单位,包括:"minute", "hour", "day", "week", "month", "year" 5 gantt.config.step = 1;//步进,默认为1 6 gantt.templates.date_scale = function (date) {//日期格式化 7 var dateToStr = gantt.date.date_to_str("%d %M"); 8 var endDate = gantt.date.add(gantt.date.add(date, 1, "week"), -1, "day"); 9 return dateToStr(date) + " - " + dateToStr(endDate); 10 }; 11 gantt.config.subscales = [ //第二个时间尺度单位 12 { unit: "day", step: 1, date: "%D" } 13 ]; 14 gantt.config.scale_height = 50; //设置时间尺度和Grid树的高度 15 16 // configure milestone description 17 gantt.templates.rightside_text = function (start, end, task) {//进度条右侧的提示文字 18 if (task.type == gantt.config.types.milestone) { 19 return task.text; 20 } 21 return ""; 22 }; 23 // add section to type selection: task, project or milestone 24 gantt.config.lightbox.sections = [//弹出对话框设置 25 { name: "description", height: 70, map_to: "text", type: "textarea", focus: true }, 26 { name: "type", type: "typeselect", map_to: "type" }, 27 { name: "time", height: 72, type: "duration", map_to: "auto" } 28 ]; 29 30 gantt.config.xml_date = "%Y-%m-%d %H:%i:%s"; // XML中的日期格式 31 gantt.init("ganttContainer"); // 初始化dhtmlxGantt,ganttContainer为视图中div的id 32 gantt.load("/Home/Data", "json");//加载数据 33 34 // enable dataProcessor 35 var dp = new dataProcessor("/Home/Save");//dhtmlxGantt保存变化,包括添加、更新、删除 36 dp.init(gantt); 37 38})();

复制代码

  通过EF Code First创建初始数据

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

Link类体现Task间的相关性。

复制代码

1using System.ComponentModel.DataAnnotations; 2 3namespace MyGanttChart.Models 4{ 5 public class Link 6 { 7 public int Id { get; set; } 8 [MaxLength(1)] 9 public string Type { get; set; } 10 public int SourceTaskId { get; set; } 11 public int TargetTaskId { get; set; } 12 } 13}

复制代码

Task类是任务的抽象。

复制代码

1using System; 2using System.ComponentModel.DataAnnotations; 3 4namespace MyGanttChart.Models 5{ 6 public class Task 7 { 8 public int Id { get; set; } 9 [MaxLength(255)] 10 public string Text { get; set; } 11 public DateTime StartDate { get; set; } 12 public int Duration { get; set; } 13 public decimal Progress { get; set; } 14 public int SortOrder { get; set; } 15 public string Type { get; set; } 16 public int? ParentId { get; set; } 17 } 18}

复制代码

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

复制代码

1using System.Data.Entity; 2using MyGanttChart.Models; 3 4namespace MyGanttChart.DAL 5{ 6 public class GanttContext : DbContext 7 { 8 public GanttContext() : base("GanttContext") { } 9 10 public DbSet<Task> Tasks { get; set; } 11 public DbSet<Link> Links { get; set; } 12 } 13}

复制代码

创建一些种子数据:

复制代码

1using System; 2using System.Collections.Generic; 3using System.Data.Entity; 4using MyGanttChart.Models; 5 6namespace MyGanttChart.DAL 7{ 8 public class GanttInitializer : DropCreateDatabaseIfModelChanges<GanttContext> 9 { 10 protected override void Seed(GanttContext context) 11 { 12 List<Task> tasks = new List<Task>() 13 { 14 new Task() { Id = 1, Text = "Project #2", StartDate = DateTime.Today.AddDays(-3), Duration = 18, SortOrder = 10, Progress = 0.4m, ParentId = null }, 15 new Task() { Id = 2, Text = "Task #1", StartDate = DateTime.Today.AddDays(-2), Duration = 8, SortOrder = 10, Progress = 0.6m, ParentId = 1 }, 16 new Task() { Id = 3, Text = "Task #2", StartDate = DateTime.Today.AddDays(-1), Duration = 8, SortOrder = 20, Progress = 0.6m, ParentId = 1 } 17 }; 18 19 tasks.ForEach(s => context.Tasks.Add(s)); 20 context.SaveChanges(); 21 22 List<Link> links = new List<Link>() 23 { 24 new Link() { Id = 1, SourceTaskId = 1, TargetTaskId = 2, Type = "1" }, 25 new Link() { Id = 2, SourceTaskId = 2, TargetTaskId = 3, Type = "0" } 26 }; 27 28 links.ForEach(s => context.Links.Add(s)); 29 context.SaveChanges(); 30 } 31 } 32}

复制代码

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

复制代码

1<entityFramework> 2 <contexts> 3 <context type="MyGanttChart.DAL.GanttContext, MyGanttChart"> 4 <databaseInitializer type="MyGanttChart.DAL.GanttInitializer, MyGanttChart" /> 5 </context> 6 </contexts> 7 <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" /> 8 <providers> 9 <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> 10 </providers> 11 </entityFramework>

复制代码

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

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

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

  显示数据

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

复制代码

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Web.Mvc; 5using System.Xml.Linq; 6using MyGanttChart.DAL; 7using MyGanttChart.Models; 8 9namespace MyGanttChart.Controllers 10{ 11 public class HomeController : Controller 12 { 13 private readonly GanttContext db = new GanttContext(); 14 15 public ActionResult Index() 16 { 17 return View(); 18 } 19 20 [HttpGet] 21 public JsonResult Data() 22 { 23 var jsonData = new 24 { 25 26 data = ( 27 from t in db.Tasks.AsEnumerable() 28 select new 29 { 30 id = t.Id, 31 text = t.Text, 32 start_date = t.StartDate.ToString("u"), 33 duration = t.Duration, 34 order = t.SortOrder, 35 progress = t.Progress, 36 open = true, 37 parent = t.ParentId, 38 type = (t.Type != null) ? t.Type : String.Empty 39 } 40 ).ToArray(), 41 42 links = ( 43 from l in db.Links.AsEnumerable() 44 select new 45 { 46 id = l.Id, 47 source = l.SourceTaskId, 48 target = l.TargetTaskId, 49 type = l.Type 50 } 51 ).ToArray() 52 }; 53 54 return new JsonResult { Data = jsonData, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; 55 } 56 ...... 57}

复制代码

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

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

运行
3

  保存数据

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

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

复制代码

1public class GanttRequest 2 { 3 public GanttMode Mode { get; set; } 4 public GanttAction Action { get; set; } 5 6 public Task UpdatedTask { get; set; } 7 public Link UpdatedLink { get; set; } 8 public long SourceId { get; set; } 9 10 /// <summary> 11 /// Create new GanttData object and populate it 12 /// </summary> 13 /// <param name="form">Form collection</param> 14 /// <returns>New GanttData</returns> 15 public static List<GanttRequest> Parse(FormCollection form, string ganttMode) 16 { 17 // save current culture and change it to InvariantCulture for data parsing 18 var currentCulture = Thread.CurrentThread.CurrentCulture; 19 Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; 20 21 var dataActions = new List<GanttRequest>(); 22 var prefixes = form["ids"].Split(','); 23 24 foreach (var prefix in prefixes) 25 { 26 var request = new GanttRequest(); 27 28 // lambda expression for form data parsing 29 Func<string, string> parse = x => form[String.Format("{0}_{1}", prefix, x)]; 30 31 request.Mode = (GanttMode)Enum.Parse(typeof(GanttMode), ganttMode, true); 32 request.Action = (GanttAction)Enum.Parse(typeof(GanttAction), parse("!nativeeditor_status"), true); 33 request.SourceId = Int64.Parse(parse("id")); 34 35 // parse gantt task 36 if (request.Action != GanttAction.Deleted && request.Mode == GanttMode.Tasks) 37 { 38 request.UpdatedTask = new Task() 39 { 40 Id = (request.Action == GanttAction.Updated) ? (int)request.SourceId : 0, 41 Text = parse("text"), 42 StartDate = DateTime.Parse(parse("start_date")), 43 Duration = Int32.Parse(parse("duration")), 44 Progress = Decimal.Parse(parse("progress")), 45 ParentId = (parse("parent") != "0") ? Int32.Parse(parse("parent")) : (int?)null, 46 SortOrder = (parse("order") != null) ? Int32.Parse(parse("order")) : 0, 47 Type = parse("type") 48 }; 49 } 50 // parse gantt link 51 else if (request.Action != GanttAction.Deleted && request.Mode == GanttMode.Links) 52 { 53 request.UpdatedLink = new Link() 54 { 55 Id = (request.Action == GanttAction.Updated) ? (int)request.SourceId : 0, 56 SourceTaskId = Int32.Parse(parse("source")), 57 TargetTaskId = Int32.Parse(parse("target")), 58 Type = parse("type") 59 }; 60 } 61 62 dataActions.Add(request); 63 } 64 65 // return current culture back 66 Thread.CurrentThread.CurrentCulture = currentCulture; 67 68 return dataActions; 69 } 70 }

复制代码

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

1public enum GanttMode 2 { 3 Tasks, 4 Links 5 }

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

复制代码

1public enum GanttAction 2 { 3 Inserted, 4 Updated, 5 Deleted, 6 Error 7 }

复制代码

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

复制代码

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Web.Mvc; 5using System.Xml.Linq; 6using MyGanttChart.DAL; 7using MyGanttChart.Models; 8 9namespace MyGanttChart.Controllers 10{ 11 public class HomeController : Controller 12 { 13 private readonly GanttContext db = new GanttContext(); 14 15 ...... 16 17 /// <summary> 18 /// Update Gantt tasks/links: insert/update/delete 19 /// </summary> 20 /// <param name="form">Gantt data</param> 21 /// <returns>XML response</returns> 22 [HttpPost] 23 public ContentResult Save(FormCollection form) 24 { 25 var dataActions = GanttRequest.Parse(form, Request.QueryString["gantt_mode"]); 26 try 27 { 28 foreach (var ganttData in dataActions) 29 { 30 switch (ganttData.Mode) 31 { 32 case GanttMode.Tasks: 33 UpdateTasks(ganttData); 34 break; 35 case GanttMode.Links: 36 UpdateLinks(ganttData); 37 break; 38 } 39 } 40 db.SaveChanges(); 41 } 42 catch 43 { 44 // return error to client if something went wrong 45 dataActions.ForEach(g => { g.Action = GanttAction.Error; }); 46 } 47 return GanttRespose(dataActions); 48 } 49 50 /// <summary> 51 /// Update gantt tasks 52 /// </summary> 53 /// <param name="ganttData">GanttData object</param> 54 private void UpdateTasks(GanttRequest ganttData) 55 { 56 switch (ganttData.Action) 57 { 58 case GanttAction.Inserted: 59 // add new gantt task entity 60 db.Tasks.Add(ganttData.UpdatedTask); 61 break; 62 case GanttAction.Deleted: 63 // remove gantt tasks 64 db.Tasks.Remove(db.Tasks.Find(ganttData.SourceId)); 65 break; 66 case GanttAction.Updated: 67 // update gantt task 68 db.Entry(db.Tasks.Find(ganttData.UpdatedTask.Id)).CurrentValues.SetValues(ganttData.UpdatedTask); 69 break; 70 default: 71 ganttData.Action = GanttAction.Error; 72 break; 73 } 74 } 75 76 /// <summary> 77 /// Update gantt links 78 /// </summary> 79 /// <param name="ganttData">GanttData object</param> 80 private void UpdateLinks(GanttRequest ganttData) 81 { 82 switch (ganttData.Action) 83 { 84 case GanttAction.Inserted: 85 // add new gantt link 86 db.Links.Add(ganttData.UpdatedLink); 87 break; 88 case GanttAction.Deleted: 89 // remove gantt link 90 db.Links.Remove(db.Links.Find(ganttData.SourceId)); 91 break; 92 case GanttAction.Updated: 93 // update gantt link 94 db.Entry(db.Links.Find(ganttData.UpdatedLink.Id)).CurrentValues.SetValues(ganttData.UpdatedLink); 95 break; 96 default: 97 ganttData.Action = GanttAction.Error; 98 break; 99 } 100 } 101 102 /// <summary> 103 /// Create XML response for gantt 104 /// </summary> 105 /// <param name="ganttData">Gantt data</param> 106 /// <returns>XML response</returns> 107 private ContentResult GanttRespose(List<GanttRequest> ganttDataCollection) 108 { 109 var actions = new List<XElement>(); 110 foreach (var ganttData in ganttDataCollection) 111 { 112 var action = new XElement("action"); 113 action.SetAttributeValue("type", ganttData.Action.ToString().ToLower()); 114 action.SetAttributeValue("sid", ganttData.SourceId); 115 action.SetAttributeValue("tid", (ganttData.Mode == GanttMode.Tasks) ? ganttData.UpdatedTask.Id : ganttData.UpdatedLink.Id); 116 actions.Add(action); 117 } 118 119 var data = new XDocument(new XElement("data", actions)); 120 data.Declaration = new XDeclaration("1.0", "utf-8", "true"); 121 return Content(data.ToString(), "text/xml"); 122 } 123 } 124}

复制代码

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

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )