效果如下:
npm 下载 插件
在这里插入代码片
/**
* jQuery Gantt Chart
*
* @see http://taitems.github.io/jQuery.Gantt/
* @license MIT
*/
/*jshint camelcase:true, freeze:true, jquery:true */
(function($, undefined) {
"use strict";
var UTC_DAY_IN_MS = 24 * 60 * 60 * 1000;
// custom selector `:findday` used to match on specified day in ms.
//
// The selector is passed a date in ms and elements are added to the
// selection filter if the element date matches, as determined by the
// id attribute containing a parsable date in ms.
function findDay(elt, text) {
var cd = new Date(parseInt(text, 10));
cd.setHours(0, 0, 0, 0);
var id = $(elt).attr("id") || "";
var si = id.indexOf("-") + 1;
var ed = new Date(parseInt(id.substring(si, id.length), 10));
ed.setHours(0, 0, 0, 0);
return cd.getTime() === ed.getTime();
}
$.expr.pseudos.findday = $.expr.createPseudo ?
$.expr.createPseudo(function(text) {
return function(elt) {
return findDay(elt, text);
};
}) :
function(elt, i, match) {
return findDay(elt, match[3]);
};
// custom selector `:findweek` used to match on specified week in ms.
function findWeek(elt, text) {
var cd = new Date(parseInt(text, 10));
var y = cd.getFullYear();
var w = cd.getWeekOfYear();
var m = cd.getMonth();
if (m === 11 && w === 1) {
y++;
} else if (!m && w > 51) {
y--;
}
cd = y + "-" + w;
var id = $(elt).attr("id") || "";
var si = id.indexOf("-") + 1;
var ed = id.substring(si, id.length);
return cd === ed;
}
$.expr.pseudos.findweek = $.expr.createPseudo ?
$.expr.createPseudo(function(text) {
return function(elt) {
return findWeek(elt, text);
};
}) :
function(elt, i, match) {
return findWeek(elt, match[3]);
};
// custom selector `:findmonth` used to match on specified month in ms.
function findMonth(elt, text) {
var cd = new Date(parseInt(text, 10));
cd = cd.getFullYear() + "-" + cd.getMonth();
var id = $(elt).attr("id") || "";
var si = id.indexOf("-") + 1;
var ed = id.substring(si, id.length);
return cd === ed;
}
$.expr[':'].findmonth = $.expr.createPseudo ?
$.expr.createPseudo(function(text) {
return function(elt) {
return findMonth(elt, text);
};
}) :
function(elt, i, match) {
return findMonth(elt, match[3]);
};
// Date prototype helpers
// ======================
// `getWeekId` returns a string in the form of 'dh-YYYY-WW', where WW is
// the week # for the year.
// It is used to add an id to the week divs
Date.prototype.getWeekId = function() {
var y = this.getFullYear();
var w = this.getWeekOfYear();
var m = this.getMonth();
if (m === 11 && w === 1) {
y++;
} else if (!m && w > 51) {
y--;
}
return 'dh-' + y + "-" + w;
};
// `getRepDate` returns the milliseconds since the epoch for a given date
// depending on the active scale
Date.prototype.getRepDate = function(scale) {
switch (scale) {
case "hours":
return this.getTime();
case "weeks":
return this.getDayForWeek().getTime();
case "months":
return new Date(this.getFullYear(), this.getMonth(), 1).getTime();
case "days":
/* falls through */
default:
return this.getTime();
}
};
// `getDayOfYear` returns the day number for the year
Date.prototype.getDayOfYear = function() {
var year = this.getFullYear();
return (Date.UTC(year, this.getMonth(), this.getDate()) -
Date.UTC(year, 0, 0)) / UTC_DAY_IN_MS;
};
// Use ISO week by default
//TODO: make these options.
var firstDay = 1; // ISO week starts with Monday (1); use Sunday (0) for, e.g., North America
var weekOneDate = 4; // ISO week one always contains 4 Jan; use 1 Jan for, e.g., North America
// `getWeekOfYear` returns the week number for the year
//TODO: fix bug when firstDay=6/weekOneDate=1 : https://github.com/moment/moment/issues/2115
Date.prototype.getWeekOfYear = function() {
var year = this.getFullYear(),
month = this.getMonth(),
date = this.getDate(),
day = this.getDay();
//var diff = weekOneDate - day + 7 * (day < firstDay ? -1 : 1);
var diff = weekOneDate - day;
if (day < firstDay) {
diff -= 7;
}
if (diff + 7 < weekOneDate - firstDay) {
diff += 7;
}
return Math.ceil(new Date(year, month, date + diff).getDayOfYear() / 7);
};
// `getDayForWeek` returns the first day of this Date's week
Date.prototype.getDayForWeek = function() {
var day = this.getDay();
var diff = (day < firstDay ? -7 : 0) + firstDay - day;
return new Date(this.getFullYear(), this.getMonth(), this.getDate() + diff);
};
$.fn.gantt = function(options) {
var scales = ["hours", "days", "weeks", "months"];
//Default settings
var settings = {
source: [],
holidays: [],
// paging
itemsPerPage: 7,
// localisation
dow: ["日", "一", "二", "三", "四", "五", "六"],
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
waitText: "Please wait...",
// navigation
navigate: "scroll",
scrollToToday: false,
// cookie options
useCookie: false,
cookieKey: "jquery.fn.gantt",
// scale parameters
scale: "days",
maxScale: "months",
minScale: "hours",
// callbacks
onItemClick: function(data) {
return; },
onAddClick: function(dt, rowId) {
return; },
onRender: $.noop
};
// read options
$.extend(settings, options);
// can't use cookie if don't have `$.cookie`
settings.useCookie = settings.useCookie && $.isFunction($.cookie);
// Grid management
// ===============
// Core object is responsible for navigation and rendering
var core = {
// Return the element whose topmost point lies under the given point
// Normalizes for old browsers (NOTE: doesn't work when element is outside viewport)
//TODO: https://github.com/taitems/jQuery.Gantt/issues/137
elementFromPoint: (function() {
// IIFE
// version for normal browsers
if (document.compatMode === "CSS1Compat") {
return function(x, y) {
x -= window.pageXOffset;
y -= window.pageYOffset;
return document.elementFromPoint(x, y);
};
}
// version for older browsers
return function(x, y) {
x -= $(document).scrollLeft();
y -= $(document).scrollTop();
return document.elementFromPoint(x, y);
};
})(),
// 创建图表
create: function(element) {
// 使用json对象初始化数据或通过xhr获取数据
// 请求取决于“settings.source”
if (typeof settings.source !== "string") {
element.data = settings.source;
} else {
$.getJSON(settings.source, function(jsData) {
element.data = jsData;
});
}
element.dataColumns = settings.dataColumns;
element.dataHeaders = settings.dataHeaders;
core.init(element);
},
// 初始化视图,计算行数、页数、可见的开始时间与结束时间
init: function(element) {
element.rowsNum = element.data.length;
element.pageCount = Math.ceil(element.rowsNum / settings.itemsPerPage);
element.rowsOnLastPage = element.rowsNum - (Math.floor(element.rowsNum / settings.itemsPerPage) * settings.itemsPerPage);
element.dateStart = tools.getMinDate(element);
element.dateEnd = tools.getMaxDate(element);
/* core.render(element); */
core.waitToggle(element, function() {
core.render(element); });
},
// **渲染grid**
render: function(element) {
var content = $('<div class="fn-content"/>');
var $leftPanel = core.leftPanel(element);
content.append($leftPanel);
var $rightPanel = core.rightPanel(element, $leftPanel);
var pLeft, hPos;
content.append($rightPanel);
content.append(core.navigation(element));
var $dataPanel = $rightPanel.find(".dataPanel");
element.gantt = $('<div class="fn-gantt" />').append(content);
$(element).empty().append(element.gantt);
element.scrollNavigation.panelMargin = parseInt($dataPanel.css("left").replace("px", ""), 10);
element.scrollNavigation.panelMaxPos = ($dataPanel.width() - $rightPanel.width());
element.scrollNavigation.canScroll = ($dataPanel.width() > $rightPanel.width());
core.markNow(element);
core.fillData(element, $dataPanel, $leftPanel);
// Set a cookie to record current position in the view
if (settings.useCookie) {
var sc = $.cookie(settings.cookieKey + "ScrollPos");
if (sc) {
element.hPosition = sc;
}
}
// Scroll the grid to today's date
if (settings.scrollToToday) {
core.navigateTo(element, 'now');
core.scrollPanel(element, 0);
// or, scroll the grid to the left most date in the panel
} else {
if (element.hPosition !== 0) {
if (element.scaleOldWidth) {
pLeft = ($dataPanel.width() - $rightPanel.width());
hPos = pLeft * element.hPosition / element.scaleOldWidth;
element.hPosition = hPos > 0 ? 0 : hPos;
element.scaleOldWidth = null;
}
$dataPanel.css({
"left": element.hPosition });
element.scrollNavigation.panelMargin = element.hPosition;
}
core.repositionLabel(element);
}
$dataPanel.css({
height: $leftPanel.height() });
core.waitToggle(element);
settings.onRender();
},
// 创建左侧Panel
leftPanel: function(element) {
//源码
/*左侧Panel */
//var ganttLeftPanel = $('<div class="leftPanel"/>').append($('<div class="row spacer"/>').css("height", tools.getCellSize() * element.headerRows));
//修改后
var rowSpace = $('<div class="row spacer"/>');
var width = "100%";
if (element.dataHeaders) {
width = 100 / Object.keys(element.dataHeaders).length + "%";
}
$.each(element.dataHeaders, function(name, entryColumn) {
rowSpace.append($('<div class="dataHead ' + name + '"/>').html(entryColumn).css("width", width).css("border-right", "1px solid #e3e3e3"));
});
rowSpace.css("height", tools.getCellSize() * element.headerRows + "px");
var ganttLeftPanel = $('<div class="leftPanel" style="width: 450px;"/>').append(rowSpace);
var entries = [];
var _entries = [];
$.each(element.data, function(i, entry) {
// debugger
if (i >= element.pageNum * settings.itemsPerPage && i < (element.pageNum * settings.itemsPerPage + settings.itemsPerPage)) {
//修改后
$.each(element.dataColumns, function(ic, entryColumn) {
entries.push('<div class="row name ' + entryColumn + ' row' + i + '" style="width:' + width + '" id="rowheader' + i + '" data-offset="' + i % settings.itemsPerPage * tools.getCellSize() + '">');
entries.push('<span title="' + entry[entryColumn] + '" class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry[entryColumn] + '</span>');
entries.push('</div>');
});
//源码
// var dataId = ('id' in entry) ? '" data-id="' + entry.id : '';
// entries.push(
// '<div class="row name row' + i + (entry.desc ? '' : (' fn-wide ' + dataId)) + '" id="rowheader' + i + '" data-offset="' + i % settings.itemsPerPage * tools.getCellSize() + '">' +
// '<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + (entry.name || '') + '</span>' +
// '</div>'
// );
// // if (entry.desc) {
// entries.push(
// '<div class="row desc row' + i + ' " id="RowdId_' + i + dataId + '">' +
// '<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry.desc + '</span>' +
// '</div>'
// );
// entries.push(
// '<div class="row relationPeople row' + i + ' " id="RowdId_' + i + dataId + '">' +
// '<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry.relationPeople + '</span>' +
// '</div>'
// );
// // }
}
});
return ganttLeftPanel.append(entries.join(""));
},
// 创建右侧数据Panel
dataPanel: function(element, width) {
var dataPanel = $('<div class="dataPanel" style="width: ' + width + 'px;"/>');
// 处理鼠标滚轮事件以滚动数据面板
// var wheel = 'onwheel' in element ? 'wheel' : document.onmousewheel !== undefined ? 'mousewheel' : 'DOMMouseScroll';
// $(element).on(wheel, function(e) { core.wheelScroll(element, e); });
var mousewheelevt = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel";
if (document.attachEvent) {
element.attachEvent("on" + mousewheelevt, function(e) {
core.wheelScroll(element, e);
});
} else if (document.addEventListener) {
element.addEventListener(mousewheelevt, function(e) {
core.wheelScroll(element, e);
}, false);
}
// 处理单击事件并分派到已注册的“onAddClick”函数
dataPanel.click(function(e) {
e.stopPropagation();
var corrX /* <- never used? */ , corrY;
var leftpanel = $(element).find(".fn-gantt .leftPanel");
var datapanel = $(element).find(".fn-gantt .dataPanel")