上海普加软件有限公司
访问 http://www.plusgantt.com/download/
获得软件开发包后解压缩,并把解压缩后的项目文件,放入到Web项目。
如果是.Net版本,可以直接用Visual Studio以"打开网站"的方式打开运行。
如果是Java版本,请使用Eclipse以"导入项目"的方式导入打开。
在下载解压缩后的文件夹内,有一个"dbsql"目录,里面有mssql.sql、oracle.sql、mysql.sql数据库表文件。
请使用这里的表结构创建一个数据库。
比如新建一个数据库,名字为:testproject。然后把拷贝的SQL运行一下,就生成了plus_project、plus_task表。
创建数据库表完成后,请在"App_Code\Utils\DBUtil.cs"修改数据库连接配置信息以便正常运行。
Java的文件路径是"src\PluSoft\DBUtil.java"。
运行"demo/ImportProject.aspx"文件。选择本地MS PROJECT XML文件,上传导入到数据库中。
如果导入成功,会弹出提示信息,可以选择跳转到"Load.html"查看刚才导入的项目数据。
如果导入失败,会弹出异常提示,定位解决错误。
(注:通常导入错误都是数据库配置信息有误;或文件选择错误。)
必须导入"Upload\软件开发-100.xml"项目文件,它是作为DEMO示例的示例项目数据。
运行"demo/NewProject.aspx"文件。此文件使用服务端Project对象,创建一个空项目,并保存到项目中。
运行"demo/Projects.aspx"文件,打开一个当前项目列表,可以选择“打开”项目,跳转到Load.html,查看项目。
.Net开发包目录文件一一如下:
以下详细介绍PlusProject是如何从数据库加载数据,以及如何保存到数据库内的。
DBProject是用来处理从数据库加载和保存项目的。里面有两个重要方法:LoadProject和SaveProject。
开发者往往需要改造这个类,修改项目加载和保存的实现方式,以满足开发的需要。
PlusProject并不关心和限制开发者如何加载和保存项目数据,只需要在创建和使用PlusProject时,提供好符合数据格式的JSON数据即可。
所以开发者可以使用任意的数据库,以及数据库操作方式,灵活性和可控性非常高。
在DBProject的LoadProject方法中,根据项目UID,加载项目数据和任务数据,并进行数据格式转换工作,最后得到一个Hashtable对象返回。
开发者可以参考LoadProject方法的代码,使用自己熟悉的数据库操作方式,比如IBatis、Hibernate等第三方类库,查询数据后,按项目数据结构进行格式转换工作。
需要注意的是,获取任务的方法SelectTasks。
从数据库查询出任务列表后,需要进行一个for循环遍历操作,一一将任务属性转换为标准任务字段。
foreach (Hashtable dbtask in dbtasks) { Hashtable task = new Hashtable(); task["UID"] = dbtask["UID_"]; //唯一标识符 task["ID"] = dbtask["ID_"]; //序号 ...... tasks.Add(task); }
另外,获得任务列表后,需要生成树形结构数据。这个可以使用TreeUtil的标准方法来实现:
//任务列表 ArrayList tasks = SelectTasks(projectUID); //列表转树形 tasks = TreeUtil.ToTree(tasks, "children", "UID", "ParentTaskUID"); project["Tasks"] = tasks;
最后,需要获取项目的“部门”和“负责人”信息。这两个方法开发者可以重写实现。(示例是生成静态的数据)
//部门, 负责人获取 project["Departments"] = GetDepartments(projectUID); project["Principals"] = GetPrincipals(projectUID);
在DBProject有一个SaveAll方法。
其原理是:删除所有旧任务,新增所有新任务,达到更新任务的目的。
//删除任务 DeleteTasks(projectUID); //树形转列表 ArrayList tasks = TreeUtil.ToList((ArrayList)dataProject["Tasks"],"-1", "children", "UID", "ParentTaskUID"); //保存任务表 InsertTasks(projectUID, tasks);
在DBProject有一个SavePart方法。
其原理是:遍历所有任务(包括被删除的),判断任务的"_state"属性状态,分别做"增/删/改"数据库操作。
//树形转列表 ArrayList tasks = TreeUtil.ToList((ArrayList)dataProject["Tasks"],"-1", "children", "UID", "ParentTaskUID"); //把被删除的任务,加入遍历的集合中 ArrayList removedTasks = (ArrayList)dataProject["RemovedTasks"]; if (removedTasks != null) { tasks.AddRange(removedTasks); } //获取变动的任务, 分别保存 foreach (Hashtable task in tasks) { string state = Convert.ToString(task["_state"]); if (state == "") continue; //如果任务没有变动,则不更新 task["ProjectUID"] = projectUID; switch (state) { case "added": //新增 ...... break; case "modified": //修改 ...... break; case "removed": //删除 ...... break; } }
PlusProject提供了保存全部和保存局部两种方式的完整代码。开发者可以参考,并使用自己的方式实现。
对任务属性的扩展,主要处理如下几个地方:
plus_task表的新字段,是用来存放新字段数据的;
在加载数据时,确保任务对象上有新字段的属性值。因为任务对象是一个Hashtable,所以这一点极其简单。
在保存数据时,修改任务的"insert"语句,把新字段的值写入到数据库,保存起来。
做好这几点,其实任务的属性已经扩展了。
下面只是需要一个界面显示和操作的载体,一般用表格列来完成。
关于表格列的扩展,请参考:自定义表格列示例。
PlusProject针对任务的各种标准属性,如"ID","Name","Start","Finish","PercentComplete","Department","Principal"等提供了相应的标准列。
开发者在创建标准任务属性范围内的列时,直接使用这里例举的标准列用来创建表格列集合。
使用标准列示例代码如下:
var project = new PlusProject(); project.setColumns([ new PlusProject.IDColumn(), new PlusProject.StatusColumn(), new PlusProject.NameColumn(), new PlusProject.PredecessorLinkColumn(), new PlusProject.PercentCompleteColumn(), new PlusProject.DurationColumn(), new PlusProject.StartColumn(), new PlusProject.FinishColumn(), new PlusProject.WorkColumn(), new PlusProject.DepartmentColumn(), new PlusProject.PrincipalColumn(), new PlusProject.AssignmentsColumn() ]);
列名 | 任务属性 | 描述 |
PlusProject.IDColumn | ID | 任务序号。 |
PlusProject.WBSColumn | WBS | WBS编码。 |
PlusProject.NameColumn | Name | 任务名称。 |
PlusProject.DurationColumn | Duration | 工期。 |
PlusProject.StartColumn | Start | 开始日期。 |
PlusProject.FinishColumn | Finish | 完成日期。 |
PlusProject.PercentCompleteColumn | PercentComplete | 完成百分比。 |
PlusProject.WorkColumn | Work | 工时。 |
PlusProject.WeightColumn | Weight | 权重。 |
PlusProject.PriorityColumn | Priority | 重要级别。 |
PlusProject.OutlineNumberColumn | OutlineNumber | 大纲字段。 |
PlusProject.OutlineLevelColumn | OutlineLevel | 任务层次。 |
PlusProject.ConstraintDateColumn | ConstraintDate | 限制日期。 |
PlusProject.ConstraintTypeColumn | ConstraintType | 限制类型。 |
PlusProject.PredecessorLinkColumn | PredecessorLink | 前置任务。 |
PlusProject.StatusColumn | 无 | 状态列。它使用图标反映任务的一些状态,比如受限制、已完成、有注释等。 |
PlusProject.DepartmentColumn | Department | 部门。 |
PlusProject.PrincipalColumn | Principal | 负责人。 |
PlusProject.AssignmentsColumn | Assignments | 资源名称。 |
PlusProject.ActualStartColumn | ActualStart | 实际开始日期。 |
PlusProject.ActualFinishColumn | ActualFinish | 实际完成日期。 |
PlusProject.Critical2Column | Critical2 | 手动设置关键任务。 |
开发者可以根据自己扩展的任务属性类型,来创建自己的列。
具体请参考"自定义列示例"。
通过监听处理"drawcell"事件,可以根据任务信息,设置行、单元格样式,以及自定义单元格Html内容。
project.on("drawcell", function (e) { var task = e.record, column = e.column, field = e.field; //单元格样式 if (column.name == "Name") { e.cellCls = "mycellcls"; } //行样式 if (task.Summary == 1) { e.rowCls = "myrowcls"; } ////自定义单元格Html。如果是工期列, 并且工期大与5天, 显示红色 if (field == "Name" && task.Duration > 5) { e.cellHtml = '<b style="color:red;">' + task.Name + '</b>'; } if (field == "Name" && task.Duration <= 2) { e.cellHtml = '<span style="color:blue;">' + task.Name + '</span>'; } if (task.Duration == 0) { e.rowCls = "deletetask"; } });
示例请参考"自定义显示"
通过监听表格的"cellbeginedit"事件,可以控制每个行、每个单元格是否可编辑。
//控制单元格是否可编辑 project.on("cellbeginedit", function (e) { var task = e.record, column = e.column, field = e.field; if (task.Summary == 1) { e.cancel = true; } if (field == 'Duration') { e.cancel = true; } });
示例请参考"控制单元格可编辑"
开发者可以控制右侧条形图的HTML外观,达到任意的条形图效果:
//1)自定义条形图外观显示 project.on("drawitem", function (e) { var item = e.item; var left = e.itemBox.left, top = e.itemBox.top, width = e.itemBox.width, height = e.itemBox.height; if (!item.Summary && !item.Milestone) { var percentWidth = width * (item.PercentComplete / 100); e.itemHtml = '<div id="' + item._id + '" class="myitem" style="left:' + left + 'px;top:' + top + 'px;width:' + width + 'px;height:' + (height) + 'px;">'; e.itemHtml += '<div style="width:' + (percentWidth) + 'px;" class="percentcomplete"></div>'; e.itemHtml += '</div>'; //e.ItemHtml = '<a href="http://www.baidu.com" style="left:'+left+'px;top:'+top+'px;width:'+width+'px;height:'+(height-2)+'px;" class="myitem">111</a>'; } }); //2)自定义条形图提示信息 project.on('itemtooltipneeded', function (e) { var task = e.task; e.tooltip = "<div>任务:" + task.Name + "</div>" // + "<div ><div style='float:left;'>进度:<b>"+task.PercentComplete + "%</b></div>" // + "<div style='float:right;'>工期:"+task.Duration + "日</div></div>" + "<div style='clear:both;'>开始日期:" + mini.formatDate(task.Start, 'yyyy-MM-dd') + "</div>" + "<div>完成日期:" + mini.formatDate(task.Finish, 'yyyy-MM-dd') + "</div>"; });
示例请参考"自定义显示条形图"
弹出右键菜单时,根据当前选择的行,显示隐藏、启用禁用菜单项。
示例请参考"自定义右键菜单"
首先,对于jQuery, YUI, Prototype.js等框架来说,它们对页面的操作基于原始Dom元素的。
所以,你可以不用做任何额外的处理,按PlusProject正常的例子,把项目甘特图对象加入到页面元素就可以了。
其次,对于ExtJS这样封装度很高的框架,可以从Ext对象找到它的dom,然后render加入即可。使用代码如下:
//获取ext控件对象 var extControl = Ext.get(id); //设置尺寸为100%自适应 project.setStyle("width:100%;height:100%"); //把project加入到ext控件对象的dom属性中 project.render(extControl.dom);
只需要这样设置后,PlusProject就能在Ext的布局面板中自动调整尺寸大小,看上去跟Ext原生的控件一样了。
如果要显示他语言界面,只需要引用locale文件夹下资源js文件即可,例如英文资源包使用如下:
<script src="../scripts/miniui/locale/en_US.js" type="text/javascript"></script>
语言本地化(英语)示例,请查看这里。
PlusProject项目数据包含如下信息:项目本身数据、日历、任务列表、部门列表、负责人列表等。
其中,任务会跟部门、负责人有分配关系。
本节使用JSON格式描述项目数据结构,对应到服务端数据,转换类型如下:
//普加项目管理中间件数据结构 { UID: 100, Name: 'ProjectName', StartDate: '2007-01-01T08:00:00', FinishDate: '2007-05-14T15:00:00', CalendarUID: 1, //日历 Calendars: [ { UID: 1, Name: 'CalendarName', WeekDays: [ //工作周: DayType(1~7) { DayType: 1, DayWorking: 1 //工作日1, 非工作日0 } ], Exceptions: [ //例外日期: DayType = 0 { DayType: 0, DayWorking: 0, Name: '', TimePeriod: { //例外的日期范围 FromDate: '2007-01-01T00:00:00', ToDate: '2007-01-02T23:59:59' } } ] } ], //任务集合 Tasks: [ { UID: 1, //任务UID(唯一性标识符) Name: '', //任务名称 Start: '2010-01-01T00:00:00',//DateTime。开始日期 Finish: '2010-01-01T23:59:59',//DateTime。完成日期 Duration: 24, //Number。工期 PercentComplete: 100, //Number。进度 Manual: 0, //手动模式。0是自动,1是手动。 ConstraintType: 1, //限制类型:0越早越好;1越晚越好;2必须开始于;3必须完成于; //4不得早于...开始;5不得晚于...开始;6不得早于...完成;7不得晚于...完成 ConstraintDate: null, //DateTime。限制日期 FixedDate:1, //1或0。是否固定日期(仅限于摘要任务使用) ID: 1, //Number。序号 OutlineNumber: '1.2.1', //体现树形层次和顺序 OutlineLevel: 1, //层次 Work: 8, //Number。工时 Weight: 100, //Number。权重 Milestone: 1, //1或0。里程碑 Summary: 1, //1或0。摘要任务 Critical: 1, //1或0。关键任务 Priority: 200, //Number。重要级别 Notes: '', //任务备注 PredecessorLink: [ //前置任务 {PredecessorUID: 2,Type: 1,LinkLag: 0}, ...... ], Assignments: [ //资源分配关系 {ResourceUID: 2, Units: 1}, ...... ], Department: '1', //部门 Principal: '1,2' //负责人, children: [ //下一级子任务, 体现树形结构 ...... ] } ], //资源集合 Resources: [ {UID: 1, Name: '市场部', Type: 1, MaxUnits: 1}, ...... ], //部门集合 Departments: [ {UID: 1, Name: '市场部'}, ...... ], //负责人集合 Principals: [ {UID: 1, Name: '张三', Department: 1}, ...... ] }
字段 | 类型 | 描述 |
UID_ | varchar | 项目唯一标识符 |
NAME_ | varchar | 项目名称 |
STARTDATE_ | datetime | 项目开始日期 |
FINISHDATE_ | datetime | 项目完成日期 |
LASTSAVED_ | varchar | 最后保存日期 |
CALENDARS_ | varchar | 日历数据(JSON字符串) |
CALENDARUID_ | varchar | 项目日历UID |
字段 | 类型 | 描述 |
UID_ | varchar | 任务唯一标识符 |
ID_ | int | 序号(是一个数字,体现任务的前后顺序) |
PARENTTASKUID_ | varchar | 父任务UID(体现树形结构) |
NAME_ | varchar | 任务名称 |
START_ | datetime | 开始日期 |
FINISH_ | datetime | 完成日期 |
DURATION_ | int | 工期 |
WORK_ | int | 工时 |
PERCENTCOMPLETE_ | int | 完成百分比 |
WEIGHT_ | int | 权重 |
CONSTRAINTTYPE_ | int | 任务限制类型。 |
CONSTRAINTDATE_ | datetime | 任务限制日期 |
MILESTONE_ | int | 里程碑 |
SUMMARY_ | int | 摘要任务 |
CRITICAL_ | int | 关键任务 |
PRIORITY_ | int | 重要级别 |
NOTES_ | varchar | 备注 |
DEPARTMENT_ | varchar | 所属部门。如"1"。 |
PRINCIPAL_ | varchar | 任务负责人。如"101,220,201"。 |
PREDECESSORLINK_ | varchar | 前置任务(JSON字符串)。如"[{PredecessorUID: 2,Type: 1,LinkLag: 0}, ...]" |
FIXEDDATE_ | int | 限制日期(摘要任务专用) |
PROJECTUID_ | varchar | 项目UID |
ACTUALSTART_ | datetime | 实际开始日期 |
ACTUALSTART_ | datetime | 实际完成日期 |
ACTUALDURATION_ | int | 实际工期 |
注意:这些表是普加软件默认提供的项目和任务表。
使用者可以完全脱离我们的表结构设计,实现自定义的表。
只需要在DBProject类的LoadProject方法中,提供符合普加项目数据结构定义的数据即可。
属性可以从对象直接读取,但是不能进行赋值操作。如果想对属性进行赋值,必须使用提供的方法。
var project = new PlusProject(); var visible= project.visible; //正确 project.visible= true; //错误!!! project.setVisible(true); //正确
属性 | 类型 | 描述 |
readOnly | Boolean | 是否只读。 |
visible | Boolean | 是否显示。 |
width | Number | 宽度。 |
height | Number | 高度。 |
showTableView | Boolean | 是否显示任务表格。 |
showGanttView | Boolean | 是否显示条形图。 |
showLinkLines | Boolean | 是否显示箭头连线。 |
showCritical | Boolean | 是否显示关键路径 |
showGridLines | Boolean | 是否条形图表格线。 |
timeLines | Array | 时间线数组。[ {date: new Date(2007, 0, 3), text: "时间线"}, {date: new Date(2007, 0, 5), text: "时间线2", style: "width:2px;background:red;"} ] |
rowHeight | Number | 行高。 |
allowDragDrop | Boolean | 是否允许任务行拖拽。 |
multiSelect | Boolean | 是否允许多选任务。 |
方法 | 参数类型 | 描述 |
setStyle(String) | 设置样式,比如:gantt.setStyle("width:100%;height:400px")。 | |
loadData(Object) | Object:参考项目JSON数据。 | 加载项目JSON对象。 |
getData() | 返回项目JSON对象。 | |
getRemovedTasks() | 返回被删除的任务集合。 | |
acceptChanges() | 恢复任务状态(撤销任务增加、删除、修改标记)。 | |
newProject() | 创建新项目。 | |
setColumns( Array ) | 设置表格列集合。 | |
setTreeColumn( String ) | 设置树形节点列。 | |
findTasks(field, value) | field:String。如"Duration"。 value:Object。属性值。 |
返回符合条件的任务集合。 |
getTask(taskUID) | 根据任务UID,获取任务。 | |
getTaskByID(taskID) | 根据任务ID,获取任务。project.filter(function(task){ if(task.Duration == 2) return true; else return false; }); |
|
clearFilter() | 取消过滤任务 | |
getSelected() | 获取选中的任务。 | |
getSelecteds() | 获取选中的任务集合。 | |
isSelected(task) | 判断是否选中任务。 | |
select(task) | 选中任务。 | |
deselect(task) | 取消选中任务。 | |
selects(Array) | 选中多个任务。 | |
deselects(Array) | 取消选中多个任务。 | |
selectAll() | 选中所有任务。 | |
deselectAll() | 取消选中所有任务。 | |
getParentTask(task) | 获取父任务对象。 | |
getChildTasks(task) //下一级任务 | 获取子任务数组。 | |
getAllChildTasks(task) | 获取所有子任务数组。 | |
getAncestorTasks(task) | 获取父级任务数组。 | |
isAncestor(parentTask, task) | 判断两任务之间是否有父子关系。 | |
eachChild(task, fn, scope) | 遍历下一级子节点。 | |
cascadeChild(task, fn, scope) | 遍历所有子节点。 | |
bubbleParent(task, fn, scope) | 遍历父级子节点。 | |
newTask() | 新建任务。此时并没有加入到项目中。 | |
addTask(task) addTask(task, index) addTask(task, action, parentTask) |
task:Object。新任务对象。 index:Number。加入的索引位置。 action:String。加入的方式,before/after/add。 parentTask:父任务。 |
新增任务。 |
removeTask(task) | 删除任务。 | |
updateTask(task, property, value) updateTask(task, keyValues) |
property:String。任务属性名,如"Start"。 value:Object。属性值。 keyValues:Object。键值对,如{Name:'测试完成', PercentComplete: 0} |
更新任务属性。 |
moveTask(task, targetTask, action) | action:String。"before"/"after"/"add" targetTask:目标任务 action:移动方式 |
移动任务。 |
upgradeTask(task) | 升级任务。 | |
downgradeTask(task) | 降级任务。 | |
addTasks(tasks, index, parentTasks)
addTasks(tasks, action, parentTasks) |
tasks:Array。新任务数组。 index:Number/"before"/"after"/"add"。加入方式。 parentTasks:Array。父任务数组。 |
批量新增任务。 |
removeTasks(tasks) | 批量删除任务。 | |
updateTasks(tasks, keyValues) |
tasks:Array。任务数组。 keyValues:Object。键值对,如{Name:'测试完成', PercentComplete: 0} |
批量修改任务。 |
collapseAll ( ) | 折叠所有任务。 | |
expandAll ( ) | 展开所有任务。 | |
collapseLevel( Number ) | 折叠某层级任务。 | |
expandLevel( Number ) | 展开某层级任务。 | |
collapse(task) | 折叠任务。 | |
expand(task) | 展开任务。 | |
setShowTableView( Boolean ) | 设置表格是否显示。 | |
setShowGanttView( Boolean ) | 设置条形图是否显示。 | |
setTableViewExpanded( Boolean ) | 设置表格折叠。 | |
setGanttViewExpanded( Boolean ) | 设置条形图折叠。 | |
setTableViewWidth( Number ) | 设置表格宽度。 | |
setGanttViewWidth( Number ) | 设置条形图宽度。 | |
setShowLinkLines( Boolean ) | 设置是否显示箭头连线。 | |
setShowCritical( Boolean ) | 设置是否显示关键路径 | |
setShowGridLines( Boolean ) | 设置是否显示条形图背景表格线。 | |
setTimeLines( Array ) | 设置项目时间线。 | |
setRowHeight( Number ) | 设置行高。 | |
setMultiSelect( Boolean ) | 设置是否多选任务。 | |
setAllowDragDrop( Boolean ) | 设置是否允许任务行拖拽。 | |
setTopTimeScale( String ) | String:时间刻度。 "year/halfyear/quarter/month/week/day/hour" |
设置顶层时间刻度。 |
setBottomTimeScale( String ) | 同上 |
设置底层时间刻度。(底层必须比顶层要小) |
zoomIn( ) | 放大时间刻度 | |
zoomOut( ) | 缩小时间刻度 | |
scrollIntoView(task) | 定位显示任务。 | |
getCalendars( ) | 获取项目日历集合。 | |
getCalendars( ) | 获取项目日历集合。 | |
setCalendars( Array ) | 设置项目日历集合。 | |
setCalendarUID( calendarUID ) | 切换项目日历。 |
通过如下方式监听事件:
functon onTaskDblClick(e){ var project = e.source; var task = e.task; //e是事件对象, 具体请看每个事件的"参数类型" } project.on('taskdblclick', onTaskDblClick);
事件名称 | 事件对象 | 描述 |
drawcell |
{ source: Object, //甘特图对象 record: Object, //任务对象 column: Object, //列对象 field: String, //属性名 value: Object, //单元格值 cellHtml: Stirng//单元格内容HTML } |
绘制单元格时发生。 |
drawitem |
{ source: Object, //甘特图对象 item: Object, //条形图,任务对象 itemBox: Object, //条形图的坐标尺寸 itemHtml: Stirng//单元格内容HTML } |
绘制条形图时发生。 |
taskclick |
{ source: Object, //甘特图对象 task: Object //任务对象 } |
单击任务时发生。 |
taskdblclick |
{ source: Object, //甘特图对象 task: Object //任务对象 } |
双击任务时发生。 |
taskdragdrop |
{ source: Object, //甘特图对象 tasks: Array, //被拖拽的任务集合 targetTask: Object, //目标任务 action: String, //投放方式:before,after,append cancel: Boolean //是否取消操作 } |
拖拽行释放时发生。 |
cellbeginedit |
{ source: Object, //甘特图对象 record: Object, //任务对象 column: Object, //列对象 field: String, //属性名 value: Object, //单元格值 cancel: Boolean //是否取消操作 } |
单元格开始编辑时发生 |
CellCommitEdit |
{ source: Object, //甘特图对象 record: Object, //任务对象 column: Object, //列对象 field: String, //属性名 value: Object, //单元格值 cancel: Boolean //是否取消操作 } |
单元格提交编辑值时发生 |
itemdragstart |
{ source: Object, //甘特图对象 item: Object, //任务对象 action: String, //拖拽操作: |
条形图开始拖拽时发生。 |
itemdragcomplete |
{ source: Object, //甘特图对象 item: Object, //任务对象 action: String, //拖拽操作: |
条形图完成拖拽时发生。 |