Available only in PRO Edition

Пользовательские элементы в области временной шкалы

Эта функциональность доступна только в PRO версии

dhtmlxGantt включает встроенные функции для отображения дополнительных элементов, таких как базовые линии, дедлайны и ограничения задач по умолчанию. Если вы хотите изменить или расширить эти функции, вы можете вручную добавить пользовательские элементы на временную шкалу, как описано ниже.

Добавление дополнительных элементов обычно включает создание нового слоя отображения и размещение пользовательских элементов там с использованием абсолютного позиционирования для их выравнивания с соответствующими задачами.

Чтобы добавить новый слой в область временной шкалы, вы можете использовать метод gantt.addTaskLayer. Этот метод принимает функцию в качестве параметра, которая:

  • Получает объект задачи;
  • Возвращает либо DOM элемент для отображения, либо false, если элемент для задачи не должен отображаться.
gantt.addTaskLayer(function myNewElement(task) {
    var el = document.createElement('div');
    // ваш код
    return el;
});

Related sample:  Displaying deadlines

Основные моменты:

  1. Когда вы вызываете этот метод, dhtmlxGantt добавляет контейнер в область временной шкалы.
  2. Во время рендеринга данных метод gantt.addTaskLayer запускается для каждой задачи, и возвращенный DOM элемент добавляется в контейнер.
  3. Вы можете использовать стандартное абсолютное позиционирование для размещения элементов.
  4. Если задача в Gantt обновляется, она также обновится во всех слоях, включая пользовательские, так как функция будет запускаться заново для обновленной задачи, заменяя соответствующий DOM элемент.
  5. Чтобы вычислить позицию и размер задачи, вы можете использовать метод gantt.getTaskPosition, который облегчает определение размещения и размеров ваших пользовательских элементов.

Советы по улучшению скорости рендеринга пользовательских элементов можно найти в руководстве по умному рендерингу для пользовательских слоев.

Если вы хотите показывать пользовательский контент в каждой ячейке временной шкалы, вы можете непосредственно вставлять HTML в ячейки, используя шаблон gantt.timeline_cell_content_template. Этот подход проще и работает быстрее.

Пример использования

Вот пример сценария: у вас есть запланированное и фактическое время для задач, и вам нужно, чтобы оба отображались.

Шаг 1. Настройка высоты задач и положения линий задач

Изначально задачи выглядят следующим образом:

Чтобы освободить место для базовых линий под задачами, уменьшите высоту полосы задачи до примерно половины высоты строки:

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;
}

Результат будет выглядеть так:

Шаг 2. Добавление новых свойств данных

Далее добавьте дополнительные свойства к объекту задачи. Например, используйте 'planned_start' и 'planned_end':

Шаг 3. Разбор новых свойств данных в объекты Date

dhtmlxGantt автоматически разбирает 'start_date' и 'end_date' в объекты Date. Для других свойств даты требуется дополнительная обработка. Чтобы 'planned_start' и 'planned_end' были распознаваемыми, разбирайте их с помощью метода gantt.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;
});

Шаг 4. Отображение пользовательских элементов для запланированного времени

Теперь используйте метод gantt.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;
});

Шаг 5. Добавление CSS для пользовательских элементов

Наконец, определите стиль для ваших пользовательских элементов:

.baseline {
    position: absolute;
    border-radius: 2px;
    opacity: 0.6;
    margin-top: -7px;
    height: 12px;
    background: #ffd180;
    border: 1px solid rgb(255,153,0);
}

Шаг 6. Включение редактирования добавленных свойств данных в лайтбоксе

Чтобы разрешить редактирование вновь добавленных свойств непосредственно из пользовательского интерфейса, вы можете переопределить структуру лайтбокса.

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 = "Запланировано";

Полная реализация этого примера доступна в соответствующем примере.

Related sample:  Display baselines

Примеры пользовательского контента

Вот несколько примеров, демонстрирующих, как использовать метод addTaskLayer() для улучшения временной шкалы диаграммы Ганта пользовательскими элементами:

Перетаскивание для пользовательских элементов

Если вам нужна функция перетаскивания для пользовательских элементов, вы можете реализовать ее вручную. Хотя DHTMLX Gantt не предоставляет встроенного решения для этого, его довольно просто настроить, обрабатывая три DOM события: mousedown, mousemove и mouseup. Вам также понадобятся несколько флагов, чтобы отслеживать состояние перетаскивания.

  1. Используйте событие mousedown, чтобы определить, когда может начаться перетаскивание. В этот момент сохраните начальную позицию мыши и любые другие релевантные данные.
var dndRequested = false;
var dndActivated = false;
var startPosition = null;
var startTimestamp = null
var taskId = null;
var domUtils = gantt.utils.dom;
 
gantt.event(gantt.$task_data, 'mousedown', function(e) {
  var draggableElement = domUtils.closest(e.target, '.baseline');
 
  if (draggableElement) {
    dndRequested = true;
    startTimestamp = Date.now();
    startPosition = domUtils.getRelativeEventPosition(e, gantt.$task_data);
    taskId = draggableElement.getAttribute("data-task");
  }
});

Использование gantt.event вместо нативных слушателей событий гарантирует, что все обработчики будут автоматически удалены при уничтожении экземпляра Gantt с помощью gantt.destructor.

  1. Событие mousemove — это то место, где фактически начинается процесс перетаскивания. Сравните текущую позицию мыши с начальной, чтобы определить, происходит ли перетаскивание. Если да, обновите позицию перетаскиваемого элемента.
gantt.event(window, 'mousemove', function(e) {
  if (dndRequested && gantt.isTaskExists(taskId)) {
    var currentPosition = domUtils.getRelativeEventPosition(e, gantt.$task_data);
    if (!dndActivated) {
      if(Math.abs(currentPosition.x - startPosition.x) > 5 || (Date.now() - startTimestamp) > 500) {
          dndActivated = true;
      }
    }
    if (dndActivated) {
      var pointerDate = gantt.dateFromPos(currentPosition.x);
      gantt.getTask(taskId).baseline_date = pointerDate;
      gantt.refreshTask(taskId);
    }
  }
});
  1. Наконец, используйте событие mouseup, чтобы завершить процесс перетаскивания. Примените изменения к перемещенному объекту и сбросьте все временные флаги.
gantt.event(window, 'mouseup', function(e) {
  if (dndActivated) {
    var task = gantt.getTask(taskId);
    task.baseline_date = gantt.roundDate({
      date: task.baseline_date,
      unit: "hour",
      step: 1    
    });
    gantt.updateTask(taskId);
  }
  dndRequested = false;
  dndActivated = false;
  startPosition = null;
  startTimestamp = null;
  taskId = null;
});

Добавление дополнительного оверлея на диаграмму

Вы можете добавить пользовательский контент на диаграмму Ганта, используя дополнительный оверлей. Это может быть контейнер div, HTML canvas или аналогичные элементы. Любая сторонняя библиотека может быть использована для рендеринга контента оверлея.

Например, вы можете захотеть включить S-кривую для отслеживания расходов или прогресса проекта. Вот как добавить оверлей:

  1. Включите расширение overlay с помощью gantt.plugins.
gantt.plugins({
    overlay: true
});
  1. Используйте addOverlay() из объекта gantt.ext.overlay, чтобы определить контент оверлея.
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 оверлея.

Related sample:  Gantt chart with overlay and zoom (S-Curve)

API расширения Overlay

Расширение overlay предоставляет несколько методов для управления оверлеями, доступных через gantt.ext.overlay:

  • addOverlay: Добавляет новый оверлей и возвращает его ID.
var overlay = gantt.ext.overlay.addOverlay(function(container){});
  • deleteOverlay: Удаляет оверлей по его ID.
gantt.ext.overlay.deleteOverlay(id);
  • getOverlaysIds: Возвращает массив ID оверлеев.
var ids = gantt.ext.overlay.getOverlaysIds();
  • refreshOverlay: Перерисовывает определенный оверлей.
gantt.ext.overlay.refreshOverlay(id);
  • showOverlay: Показывает оверлей по его ID.
gantt.ext.overlay.showOverlay(id);
  • hideOverlay: Скрывает оверлей по его ID.
gantt.ext.overlay.hideOverlay(id);
  • isOverlayVisible: Проверяет, виден ли оверлей.
var isVisible = gantt.ext.overlay.isOverlayVisible(id);
К началу