一款开源且功能强大的WEB甘特图控件----普加甘特图(PlusGantt)
甘特图在项目管理中非常重要,甘特图的思想比较简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间。它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比。管理者由此可便利地弄清一项任务(项目)还剩下哪些工作要做,并可评估工作进度。甘特图可以显示几个部门、机器或设备的运行和闲置情况。这表示了该系统的有关工作负荷状况,这样可使管理人员了解何种调整是恰当的。
由于项目需要,在网上找了很久,经过大量的对比和评估,发现一款真正开源且功能强大的WEB甘特图控件普加甘特图(PlusGantt)(http://http://www.plusgantt.com/project/),效果图如下:
该款甘特图控件具有如下特征:
1、完善的排程计算,类似Project的效果;
2、支持自动任务,手动任务,任务联动,前后置关系,限制条件等诸多控制;
3、支持资源配置;
4、可对任务的计划和实际进行对比,以百分比进行进度跟踪;
5、支持外观修改;
6、支持前锋线;
7、支持关键路径。
以下为官方演示代码:
var ProjectApp = mini.Panel.extend({ props: { allowUndo: true, showHeader: false, showToolbar: true, borderStyle: 'border:0', bodyStyle: "padding:0px", toolbarStyle: "border-bottom:0", width: "auto", height: "100%", toolbar: ToolbarTemplate }, constructor: function (config) { ProjectApp.base.call(this, config); this.initProject(); this.initColumns(); this.initMenu(); }, initProject: function () { this.project = new PlusProject(); this.project.setStyle("width:100%;height:100%;"); $(this.bodyEl).append(this.project.el); var project = this.project; //project.allowOrderProject = false; //禁?止1任?务?排?程ì算?法ぁ? project.enableManualSchedule = true; //启?用?手?动ˉ模£式? project.enableActualState = true; //MSProject的?实害?际ê状痢?态?处鋦理え?逻?辑- // project.enableHalfDay = true; //启?用?半?天?工¤期ú模£式? //project.undoMaxLength = 50; project.setAllowUndo(this.allowUndo); //设Θ?置?“°撤·销ú恢?复′”±功|能ü project.setShowCriticalPath(true); //设Θ?置?显?示?关?键ü路·径? project.setAllowResize(false); project.setMultiSelect(true); //设Θ?置?多à选? //project.enableTaskWorkWithDuration = false; //禁?止1任?务?的?工¤时骸?与?工¤期ú联动ˉ this.refreshButtons(); project.on('undochange', this.refreshButtons, this); project.on('beforelinkcreate', function (e) { e.cancel = true; var predecessorTask = project.getTask(e.link.PredecessorUID); var task = project.getTask(e.link.TaskUID); var prevLink = project.getPredecessorLink(task, predecessorTask); if (!prevLink) { var links = (task.PredecessorLink || []).clone(); links.push(e.link); var action = new PlusProject.UpdateTaskAction(project, task, 'PredecessorLink', links); project.executeAction(action); } }); project.on('beforelinkremove', function (e) { e.cancel = true; var win = new PlusProject.PredecessorLinkWindow({ closeAction: 'destroy' }); win.project = project; win.setData(e.link); win.show(); }); project.tableView.on("refresh", this.refreshButtons, this); }, initColumns: function () { var project = this.project; var columns = [ new PlusProject.IDColumn(), new mini.CheckColumn(), new PlusProject.StatusColumn(), new PlusProject.ManualColumn(), new PlusProject.NameColumn(), new PlusProject.PredecessorLinkColumn(), new PlusProject.WorkColumn(), new PlusProject.DurationColumn(), new PlusProject.StartColumn(), new PlusProject.FinishColumn(), new PlusProject.PercentCompleteColumn(), new PlusProject.ActualStartColumn(), new PlusProject.ActualFinishColumn(), new PlusProject.EarlyStartColumn(), new PlusProject.EarlyFinishColumn(), new PlusProject.LateStartColumn(), new PlusProject.LateFinishColumn(), new PlusProject.TotalSlackColumn(), new PlusProject.CriticalColumn(), new PlusProject.DepartmentColumn(), new PlusProject.PrincipalColumn(), new PlusProject.AssignmentsColumn() ]; project.setColumns(columns); }, initTimeScales: function () { this.project.setTopTimeScale('week'); this.project.setBottomTimeScale('day'); }, initMenu: function () { var project = this.project; this.tableMenu = new PlusProject.TableMenu(project); this.tableHeaderMenu = new PlusProject.TableHeaderMenu(project); this.ganttMenu = new PlusProject.GanttMenu(project); this.ganttHeaderMenu = new PlusProject.GanttHeaderMenu(project); this.ganttBarMenu = new PlusProject.GanttBarMenu(project); }, ////////////////////////////////////////////////////////////// refreshButtons: function () { var project = this.project; this.refs.undo.setEnabled(project.canUndo()); this.refs.redo.setEnabled(project.canRedo()); var saveButton = this.refs.save; // saveButton.refs.button.setEnabled(project.isDataChanged()); var saveItem = saveButton.refs.arrow.menu.items[0]; // saveItem.setEnabled(project.isDataChanged()); }, execUndo: function () { this.project.undo(); }, execRedo: function () { this.project.redo(); }, onAddTask: function () { var project = this.project; var targetTask = project.getSelected(); var newTask = project.newTask(); newTask.Name = ''; var action = new PlusProject.InsertTaskAction(project, newTask, 'before', targetTask); project.executeAction(action); }, onEditTask: function () { var project = this.project; var item = project.getSelected(); if (item == project.tasks.root) return ShowTaskWindow(project); }, onRemoveTask: function () { var project = this.project; var items = project.getSelecteds(); if (items.length == 0) return; var action = new PlusProject.RemoveTaskAction(project, items); project.executeAction(action); }, onUpgradeTask: function () { var project = this.project; var tasks = project.getSelecteds(); if (tasks.length > 0) { var action = new PlusProject.UpgradeTaskAction(project, tasks); project.executeAction(action); } }, onDowngradeTask: function () { var project = this.project; var tasks = project.getSelecteds(); if (tasks.length > 0) { var action = new PlusProject.DowngradeTaskAction(project, tasks); project.executeAction(action); } }, saveProject: function () { PlusProject.api.saveProject(this.project); }, saveAsTemplate: function () { var project = this.project; var data = project.data; var name = data.Name + ' - ' + mini.formatDate(new Date(), 'yyyy-MM-dd'); var template = { name: name, type: 1, //1 项?目?模£板?, 2任?务?模£板? data: data } project.mask('另ⅷ?存?为a项?目?模£板?'); PlusProject.api.saveTemplate(template, function (success) { if (success) { alert("项?目?模£板? “°" + name + "”± 创洹?建¨成é功|"); } project.unmask(); }); }, trackProject: function () { TrackProject(this.project); }, showProjectListWindow: function () { var win = new PlusProject.ProjectListWindow({ project: this.project }); win.setData(); win.show(); }, showProjectTemplateWindow: function () { var win = new PlusProject.ProjectTemplateWindow({ project: this.project }); win.setData(); win.show(); }, showTaskTemplateWindow: function () { var win = new PlusProject.TaskTemplateWindow({ project: this.project }); win.setData(); win.show(); }, showNewProjectWindow: function () { var project = this.project; function fn() { var newProject = new PlusProject(); var win = new PlusProject.ProjectWindow({ title: '新?建¨项?目?', mode: 'new', project: newProject, closeHandler: function (action) { if (action == 'ok') { var data = this.getData(); var dataProject = mini.clone(newProject.data); $.extend(dataProject, data); project.loadData(dataProject); project.lastScheduleDate = new Date(new Date().getTime() + 1); } } }); win.setData(); win.show(); } if (project.isDataChanged()) { if (confirm("是?否?保馈?存?当獭?前°项?目???")) { PlusProject.api.saveProject(project, fn); } else { fn(); } } else { fn(); } }, showNewFromTemplateWindow: function () { var project = this.project; function fn() { var win = new PlusProject.ProjectTemplateWindow({ project: project }); win.setData(); win.show(); } if (project.isDataChanged()) { if (confirm("是?否?保馈?存?当獭?前°项?目???")) { PlusProject.api.saveProject(project, fn); } else { fn(); } } else { fn(); } }, showProjectWindow: function () { var win = new PlusProject.ProjectWindow({ project: this.project }); win.setData(); win.show(); }, showCalendarWindow: function () { ShowCalendarWindow(this.project); }, showResourcesWindow: function () { ShowResourcesWindow(this.project); }, showMoveProjectWindow: function () { var win = new PlusProject.MoveProjectWindow({ project: this.project }); win.setData(); win.show(); }, showImportProjectWindow: function () { var win = new PlusProject.ImportProjectWindow({ project: this.project }); win.setData(); win.show(); }, showExportProjectWindow: function () { var win = new PlusProject.ExportProjectWindow({ project: this.project }); win.setData(); win.show(); }, showPrintProjectWindow: function () { var win = new PlusProject.PrintProjectWindow({ project: this.project }); win.setData(); win.show(); } });
甘特图在项目管理中非常重要,甘特图的思想比较简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间。它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比。管理者由此可便利地弄清一项任务(项目)还剩下哪些工作要做,并可评估工作进度。甘特图可以显示几个部门、机器或设备的运行和闲置情况。这表示了该系统的有关工作负荷状况,这样可使管理人员了解何种调整是恰当的。
由于项目需要,在网上找了很久,经过大量的对比和评估,发现一款真正开源且功能强大的WEB甘特图控件普加甘特图(PlusGantt)(http://http://www.plusgantt.com/project/),效果图如下:
该款甘特图控件具有如下特征:
1、完善的排程计算,类似Project的效果;
2、支持自动任务,手动任务,任务联动,前后置关系,限制条件等诸多控制;
3、支持资源配置;
4、可对任务的计划和实际进行对比,以百分比进行进度跟踪;
5、支持外观修改;
6、支持前锋线;
7、支持关键路径。
以下为官方演示代码:
var ProjectApp = mini.Panel.extend({
props: {
allowUndo: true,
showHeader: false,
showToolbar: true,
borderStyle: 'border:0',
bodyStyle: "padding:0px",
toolbarStyle: "border-bottom:0",
width: "auto",
height: "100%",
toolbar: ToolbarTemplate
},
constructor: function (config) {
ProjectApp.base.call(this, config);
this.initProject();
this.initColumns();
this.initMenu();
},
initProject: function () {
this.project = new PlusProject();
this.project.setStyle("width:100%;height:100%;");
$(this.bodyEl).append(this.project.el);
var project = this.project;
//project.allowOrderProject = false; //禁?止1任?务?排?程ì算?法ぁ?
project.enableManualSchedule = true; //启?用?手?动ˉ模£式?
project.enableActualState = true; //MSProject的?实害?际ê状痢?态?处鋦理え?逻?辑-
// project.enableHalfDay = true; //启?用?半?天?工¤期ú模£式?
//project.undoMaxLength = 50;
project.setAllowUndo(this.allowUndo); //设Θ?置?“°撤·销ú恢?复′”±功|能ü
project.setShowCriticalPath(true); //设Θ?置?显?示?关?键ü路·径?
project.setAllowResize(false);
project.setMultiSelect(true); //设Θ?置?多à选?
//project.enableTaskWorkWithDuration = false; //禁?止1任?务?的?工¤时骸?与?工¤期ú联动ˉ
this.refreshButtons();
project.on('undochange', this.refreshButtons, this);
project.on('beforelinkcreate', function (e) {
e.cancel = true;
var predecessorTask = project.getTask(e.link.PredecessorUID);
var task = project.getTask(e.link.TaskUID);
var prevLink = project.getPredecessorLink(task, predecessorTask);
if (!prevLink) {
var links = (task.PredecessorLink || []).clone();
links.push(e.link);
var action = new PlusProject.UpdateTaskAction(project, task, 'PredecessorLink', links);
project.executeAction(action);
}
});
project.on('beforelinkremove', function (e) {
e.cancel = true;
var win = new PlusProject.PredecessorLinkWindow({
closeAction: 'destroy'
});
win.project = project;
win.setData(e.link);
win.show();
});
project.tableView.on("refresh", this.refreshButtons, this);
},
initColumns: function () {
var project = this.project;
var columns = [
new PlusProject.IDColumn(),
new mini.CheckColumn(),
new PlusProject.StatusColumn(),
new PlusProject.ManualColumn(),
new PlusProject.NameColumn(),
new PlusProject.PredecessorLinkColumn(),
new PlusProject.WorkColumn(),
new PlusProject.DurationColumn(),
new PlusProject.StartColumn(),
new PlusProject.FinishColumn(),
new PlusProject.PercentCompleteColumn(),
new PlusProject.ActualStartColumn(),
new PlusProject.ActualFinishColumn(),
new PlusProject.EarlyStartColumn(),
new PlusProject.EarlyFinishColumn(),
new PlusProject.LateStartColumn(),
new PlusProject.LateFinishColumn(),
new PlusProject.TotalSlackColumn(),
new PlusProject.CriticalColumn(),
new PlusProject.DepartmentColumn(),
new PlusProject.PrincipalColumn(),
new PlusProject.AssignmentsColumn()
];
project.setColumns(columns);
},
initTimeScales: function () {
this.project.setTopTimeScale('week');
this.project.setBottomTimeScale('day');
},
initMenu: function () {
var project = this.project;
this.tableMenu = new PlusProject.TableMenu(project);
this.tableHeaderMenu = new PlusProject.TableHeaderMenu(project);
this.ganttMenu = new PlusProject.GanttMenu(project);
this.ganttHeaderMenu = new PlusProject.GanttHeaderMenu(project);
this.ganttBarMenu = new PlusProject.GanttBarMenu(project);
},
//////////////////////////////////////////////////////////////
refreshButtons: function () {
var project = this.project;
this.refs.undo.setEnabled(project.canUndo());
this.refs.redo.setEnabled(project.canRedo());
var saveButton = this.refs.save;
// saveButton.refs.button.setEnabled(project.isDataChanged());
var saveItem = saveButton.refs.arrow.menu.items[0];
// saveItem.setEnabled(project.isDataChanged());
},
execUndo: function () {
this.project.undo();
},
execRedo: function () {
this.project.redo();
},
onAddTask: function () {
var project = this.project;
var targetTask = project.getSelected();
var newTask = project.newTask();
newTask.Name = '';
var action = new PlusProject.InsertTaskAction(project, newTask, 'before', targetTask);
project.executeAction(action);
},
onEditTask: function () {
var project = this.project;
var item = project.getSelected();
if (item == project.tasks.root) return
ShowTaskWindow(project);
},
onRemoveTask: function () {
var project = this.project;
var items = project.getSelecteds();
if (items.length == 0) return;
var action = new PlusProject.RemoveTaskAction(project, items);
project.executeAction(action);
},
onUpgradeTask: function () {
var project = this.project;
var tasks = project.getSelecteds();
if (tasks.length > 0) {
var action = new PlusProject.UpgradeTaskAction(project, tasks);
project.executeAction(action);
}
},
onDowngradeTask: function () {
var project = this.project;
var tasks = project.getSelecteds();
if (tasks.length > 0) {
var action = new PlusProject.DowngradeTaskAction(project, tasks);
project.executeAction(action);
}
},
saveProject: function () {
PlusProject.api.saveProject(this.project);
},
saveAsTemplate: function () {
var project = this.project;
var data = project.data;
var name = data.Name + ' - ' + mini.formatDate(new Date(), 'yyyy-MM-dd');
var template = {
name: name,
type: 1, //1 项?目?模£板?, 2任?务?模£板?
data: data
}
project.mask('另ⅷ?存?为a项?目?模£板?');
PlusProject.api.saveTemplate(template, function (success) {
if (success) {
alert("项?目?模£板? “°" + name + "”± 创洹?建¨成é功|");
}
project.unmask();
});
},
trackProject: function () {
TrackProject(this.project);
},
showProjectListWindow: function () {
var win = new PlusProject.ProjectListWindow({
project: this.project
});
win.setData();
win.show();
},
showProjectTemplateWindow: function () {
var win = new PlusProject.ProjectTemplateWindow({
project: this.project
});
win.setData();
win.show();
},
showTaskTemplateWindow: function () {
var win = new PlusProject.TaskTemplateWindow({
project: this.project
});
win.setData();
win.show();
},
showNewProjectWindow: function () {
var project = this.project;
function fn() {
var newProject = new PlusProject();
var win = new PlusProject.ProjectWindow({
title: '新?建¨项?目?',
mode: 'new',
project: newProject,
closeHandler: function (action) {
if (action == 'ok') {
var data = this.getData();
var dataProject = mini.clone(newProject.data);
$.extend(dataProject, data);
project.loadData(dataProject);
project.lastScheduleDate = new Date(new Date().getTime() + 1);
}
}
});
win.setData();
win.show();
}
if (project.isDataChanged()) {
if (confirm("是?否?保馈?存?当獭?前°项?目???")) {
PlusProject.api.saveProject(project, fn);
} else {
fn();
}
} else {
fn();
}
},
showNewFromTemplateWindow: function () {
var project = this.project;
function fn() {
var win = new PlusProject.ProjectTemplateWindow({
project: project
});
win.setData();
win.show();
}
if (project.isDataChanged()) {
if (confirm("是?否?保馈?存?当獭?前°项?目???")) {
PlusProject.api.saveProject(project, fn);
} else {
fn();
}
} else {
fn();
}
},
showProjectWindow: function () {
var win = new PlusProject.ProjectWindow({
project: this.project
});
win.setData();
win.show();
},
showCalendarWindow: function () {
ShowCalendarWindow(this.project);
},
showResourcesWindow: function () {
ShowResourcesWindow(this.project);
},
showMoveProjectWindow: function () {
var win = new PlusProject.MoveProjectWindow({
project: this.project
});
win.setData();
win.show();
},
showImportProjectWindow: function () {
var win = new PlusProject.ImportProjectWindow({
project: this.project
});
win.setData();
win.show();
},
showExportProjectWindow: function () {
var win = new PlusProject.ExportProjectWindow({
project: this.project
});
win.setData();
win.show();
},
showPrintProjectWindow: function () {
var win = new PlusProject.PrintProjectWindow({
project: this.project
});
win.setData();
win.show();
}
});
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具