Available only in PRO Edition
Эта функция доступна только в PRO-редакции
dhtmlxGantt включает встроенную функциональность для отображения дополнительных элементов, таких как базовые планы, крайние сроки и ограничения задач по умолчанию. Если вы хотите расширить или изменить эти возможности, вы можете вручную добавить пользовательские элементы на временную шкалу, как описано ниже.
Добавление дополнительных элементов обычно включает создание слоя отображения и позиционирование пользовательских элементов с помощью абсолютного позиционирования, чтобы выровнять их относительно соответствующей задачи.
Чтобы добавить дополнительный слой в область временной шкалы, используйте метод addTaskLayer. Этот метод принимает функцию в качестве параметра, которая:
gantt.addTaskLayer(function myNewElement(task) {
var el = document.createElement('div');
// ваш код
return el;
});
Related sample: Displaying deadlines
Примечания:
Советы по повышению производительности отрисовки пользовательских элементов см. в статье addTaskLayer.
Если вы хотите отображать пользовательский контент в каждой ячейке временной шкалы, проще и быстрее вставлять HTML напрямую в ячейки с помощью шаблона timeline_cell_content.
Вот пример, иллюстрирующий использование данной функциональности: предположим, у вас есть как запланированное, так и фактическое время выполнения задач, и вы хотите отобразить оба варианта.
Изначально задачи выглядят так:
Чтобы освободить место для базовых планов под задачами, уменьшите высоту полосы задачи примерно до половины высоты строки:
gantt.config.bar_height = 16;
gantt.config.row_height = 40;
Затем переместите линию задачи к верхней части строки с помощью следующего CSS:
.gantt_task_line, .gantt_line_wrapper {
margin-top: -9px;
}
.gantt_side_content {
margin-bottom: 7px;
}
.gantt_task_link .gantt_link_arrow {
margin-top: -12px
}
.gantt_side_content.gantt_right {
bottom: 0;
}
В результате получится:
Далее добавьте дополнительные свойства данных в объект задачи, например, 'planned_start' и 'planned_end'.
dhtmlxGantt автоматически распознаёт и преобразует 'start_date' и 'end_date' в объекты Date. Другие свойства дат требуют ручного преобразования.
Чтобы сделать 'planned_start' и 'planned_end' пригодными для использования в dhtmlxGantt, преобразуйте их в объекты Date с помощью метода parseDate() внутри обработчика события onTaskLoading.
gantt.attachEvent("onTaskLoading", function(task){
task.planned_start = gantt.date.parseDate(task.planned_start, "xml_date");
task.planned_end = gantt.date.parseDate(task.planned_end, "xml_date");
return true;
});
Затем используйте метод addTaskLayer для отображения запланированного времени для каждой задачи (от 'planned_start' до 'planned_end').
gantt.addTaskLayer(function draw_planned(task) {
if (task.planned_start && task.planned_end) {
var sizes = gantt.getTaskPosition(task, task.planned_start, task.planned_end);
var el = document.createElement('div');
el.className = 'baseline';
el.style.left = sizes.left + 'px';
el.style.width = sizes.width + 'px';
el.style.top = sizes.top + gantt.config.task_height + 13 + 'px';
return el;
}
return false;
});
Наконец, добавьте CSS-стили для новых элементов:
.baseline {
position: absolute;
border-radius: 2px;
opacity: 0.6;
margin-top: -7px;
height: 12px;
background: #ffd180;
border: 1px solid rgb(255,153,0);
}
Чтобы пользователи могли редактировать недавно добавленные свойства через интерфейс, необходимо соответствующим образом переопределить структуру lightbox.
gantt.config.lightbox.sections = [
{name: "description", height: 70, map_to: "text", type: "textarea", focus: true},
{name: "time", height: 72, map_to: "auto", type: "duration"},
{name: "baseline", height: 72, map_to: {
start_date: "planned_start", end_date: "planned_end"}, type: "duration"}
];
gantt.locale.labels.section_baseline = "Planned";
Полный пример кода вы можете найти в соответствующем примере.
Related sample: Display baselines
Ниже приведены примеры использования метода addTaskLayer() для расширения временной шкалы Gantt различными пользовательскими элементами:
Если вы хотите реализовать drag-and-drop для пользовательских элементов, важно понимать, что DHTMLX Gantt не предоставляет встроенной поддержки этой функции, однако вы можете реализовать её вручную с помощью нескольких простых шагов.
Идея заключается в том, чтобы отслеживать три DOM-события (mousedown, mousemove, mouseup) и использовать несколько флагов для отслеживания состояния drag-and-drop между этими событиями.
1. Событие mousedown сигнализирует о начале потенциального drag-and-drop. Однако это также может быть обычный клик, который не должен инициировать перетаскивание. На этом этапе установите флаг, что drag-and-drop запрошен, и сохраните начальную позицию мыши и другие необходимые данные.
var dndRequested = false;
var dndActivated = false;
var startPosition = null;
var startTimestamp = null
var taskId = null;
var domUtils = gantt.utils.dom;
// в этом примере мы будем перетаскивать элементы `.baseline` внутри контейнера `gantt.$task_data`
gantt.event(gantt.$task_data, 'mousedown', function(e) {
// используйте element.closest или gantt.utils.dom.closest для поиска перетаскиваемого элемента
var draggableElement = domUtils.closest(e.target, '.baseline');
if (draggableElement) {
// мы ещё не знаем, собирается ли пользователь перетаскивать элемент или просто кликнуть по нему
// сохраняем информацию о событии, проверим это при 'mousemove'
dndRequested = true;
startTimestamp = Date.now();
startPosition = domUtils.getRelativeEventPosition(e, gantt.$task_data);
taskId = draggableElement.getAttribute("data-task");
}
});
Обратите внимание, что обработчик события добавляется с помощью gantt.event, а не через нативный Element.addEventListener. Это связано с тем, что при уничтожении экземпляра Gantt через gantt.destructor все обработчики, добавленные через gantt.event, удаляются автоматически. При использовании нативных слушателей событий потребуется ручная очистка для предотвращения утечек памяти.
2. Сам процесс drag-and-drop начинается при событии mousemove. Вместо немедленного начала перетаскивания по mouse down, сравнивайте текущую позицию мыши с ранее сохранённой. Таким образом, перетаскивание начинается только при смещении курсора на определённое расстояние. Также можно проверить, удерживается ли кнопка мыши дольше обычного клика.
После начала перетаскивания обработчик mousemove обновляет позицию перетаскиваемого элемента. Для элементов, добавленных через gantt.addTaskLayer
, предпочтительно обновлять связанные данные задачи и обновлять задачу с помощью gantt.refreshTask, а не напрямую изменять DOM.
gantt.event(window, 'mousemove', function(e) {
if (dndRequested && gantt.isTaskExists(taskId)) {
// мы зафиксировали 'mousemove' после события 'mousedown'
var currentPosition = domUtils.getRelativeEventPosition(e, gantt.$task_data);
if (!dndActivated) {
// 'mousemove' может быть частью обычного клика,
// не нужно запускать drag-and-drop при обычном клике
// проверяем, изменилось ли положение мыши существенно,
// или пользователь удерживает кнопку мыши дольше, чем обычно при клике
if(Math.abs(
currentPosition.x - startPosition.x) > 5 || (Date.now() - startTimestamp
) > 500) {
// если да — предполагаем, что начался drag-and-drop
dndActivated = true;
}
}
if (dndActivated) {
// здесь можно обновить позицию перетаскиваемого элемента.
// при перетаскивании элемента, добавленного через `gantt.addTaskLayer`,
// лучше обновлять объект задачи
// и перерисовывать его через `gantt.refreshTask`
// также можно получить соответствующую дату из временной шкалы:
var pointerDate = gantt.dateFromPos(currentPosition.x);
gantt.getTask(taskId).baseline_date = pointerDate;
gantt.refreshTask(taskId);
}
}
});
3. Наконец, отслеживайте событие mouseup. Если перетаскивание происходило, завершите изменения, округлив дату, вызовите gantt.updateTask при необходимости и сбросьте все временные флаги.
gantt.event(window, 'mouseup', function(e) {
// применяем изменения, если drag-and-drop был в процессе
if (dndActivated) {
// проверить и завершить изменения при необходимости
var task = gantt.getTask(taskId);
task.baseline_date = gantt.roundDate({
date: task.baseline_date,
unit: "hour",
step: 1
});
// вызовите gantt.updateTask для обновления данных
gantt.updateTask(taskId);
}
// очистить все флаги, установленные на предыдущих этапах
dndRequested = false;
dndActivated = false;
startPosition = null;
startTimestamp = null;
taskId = null;
});
dhtmlxGantt позволяет добавить дополнительный слой поверх диаграммы для размещения пользовательского контента. Этот слой может быть div-контейнером, HTML canvas или другими элементами. Для отображения содержимого внутри него можно использовать сторонние библиотеки.
Например, вы можете добавить наложение S-кривой, которая часто используется для визуализации роста расходов, потребления материалов или общего прогресса проекта.
Чтобы добавить overlay, выполните два шага:
gantt.plugins({
overlay: true
});
Ниже приведён пример добавления canvas overlay с S-кривыми, отображающими целевой и фактический прогресс проекта, с использованием библиотеки ChartJS:
var overlay = gantt.ext.overlay.addOverlay(function(container){
var canvas = document.createElement("canvas");
container.appendChild(canvas);
canvas.style.height = container.offsetHeight + "px";
canvas.style.width = container.offsetWidth + "px";
var ctx = canvas.getContext("2d");
var myChart = new Chart(ctx, {
type: "line",
// полная конфигурация графика
});
});
Метод gantt.ext.overlay.addOverlay() возвращает числовой id созданного overlay.
Related sample: Gantt chart with overlay and zoom (S-Curve)
Расширение dhtmlxgantt_overlay предоставляет набор API-методов для работы с overlay, доступных через объект gantt.ext.overlay.
Добавляет новый overlay на диаграмму Gantt и возвращает его id. Вы передаёте функцию, получающую контейнер для вашего пользовательского контента.
var overlay = gantt.ext.overlay.addOverlay(function(container){});
Удаляет overlay по его id.
gantt.ext.overlay.deleteOverlay(id);
Возвращает массив id всех overlay, добавленных на диаграмму.
var ids = gantt.ext.overlay.getOverlaysIds();
Перерисовывает указанный overlay по его id.
gantt.ext.overlay.refreshOverlay(id);
Делает overlay видимым по его id.
gantt.ext.overlay.showOverlay(id);
Скрывает overlay по его id.
gantt.ext.overlay.hideOverlay(id);
Проверяет, видим ли указанный overlay. Возвращает true, если видим.
var isVisible = gantt.ext.overlay.isOverlayVisible(id);
К началу