Вот обзор различных способов перемещения задач вместе с их зависимыми задачами.
Один из вариантов — использовать расширение Автоматическое планирование. Эта функция автоматически планирует задачи на основе их взаимосвязей.
Чтобы включить автоматическое планирование, активируйте его с помощью метода gantt.plugins:
gantt.plugins({
auto_scheduling: true
});
Затем установите свойство auto_scheduling в true
:
gantt.config.auto_scheduling = true;
Когда речь идет о ручном перетаскивании зависимых задач, общий подход следующий:
Вы можете выбрать один из двух методов:
В любом случае первым шагом будет получение всех связанных задач.
Чтобы определить связанные задачи, используйте свойства $source и $target в объекте задачи. Эти свойства автоматически генерируются и содержат идентификаторы связанных ссылок:
$source
- ссылки, исходящие из задачи.$target
- ссылки, приходящие в задачу.var taskObj = gantt.getTask("t1");
var sourceLinks = taskObj.$source; //-> ["l1","l4"] - ID исходящих ссылок
var targetLinks = taskObj.$target; //-> ["l5","l8"] - ID входящих ссылок
Получив ссылки, вы можете определить зависимые задачи.
Чтобы пройти по связанным задачам, вы можете использовать следующий итератор:
gantt.eachSuccessor = function(callback, root){
if(!this.isTaskExists(root))
return;
// Избегайте бесконечных циклов, отслеживая посещенные задачи
var traversedTasks = arguments[2] || {};
if(traversedTasks[root])
return;
traversedTasks[root] = true;
var rootTask = this.getTask(root);
var links = rootTask.$source;
if(links){
for(var i=0; i < links.length; i++){
var link = this.getLink(links[i]);
if(this.isTaskExists(link.target) && !traversedTasks[link.target]){
callback.call(this, this.getTask(link.target));
// Проходите по всей ветке, а не только по зависимостям первого уровня
this.eachSuccessor(callback, link.target, traversedTasks);
}
}
}
};
Зависимые задачи могут перемещаться одновременно с основной задачей при её перетаскивании. Это создаёт плавный визуальный эффект, хотя может повлиять на производительность, если перемещается много задач.
Начните с настройки итератора, как показано выше.
Затем прикрепите обработчик к событию onTaskDrag. Этот обработчик будет выполняться во время каждого кадра перетаскивания и корректировать позиции связанных задач.
gantt.attachEvent("onTaskDrag", function(id, mode, task, original){
var modes = gantt.config.drag_mode;
if(mode == modes.move){
var diff = task.start_date - original.start_date;
gantt.eachSuccessor(function(child){
child.start_date = new Date(+child.start_date + diff);
child.end_date = new Date(+child.end_date + diff);
gantt.refreshTask(child.id, true);
},id );
}
return true;
});
Наконец, когда перетаскивание завершено, округлите позиции зависимых задач до масштаба. Это можно сделать с помощью события onAfterTaskDrag:
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
gantt.eachSuccessor(function(child){
child.start_date = gantt.roundDate(child.start_date);
child.end_date = gantt.calculateEndDate(child.start_date, child.duration);
gantt.updateTask(child.id);
},id );
}
});
Этот метод работает хорошо, если только не слишком много связанных задач.
Другой вариант — корректировать зависимые задачи только после перемещения основной задачи. Этот подход упрощает процесс и улучшает производительность.
Вот как это можно сделать:
Начните с настройки итератора, как показано выше.
После завершения действия перетаскивания используйте событие onBeforeTaskChanged, чтобы зафиксировать разницу в датах между исходной и измененной задачами.
Имейте в виду, что перетаскивание всё ещё можно отменить на этом этапе, так как onBeforeTaskChanged
позволяет отмену. Избегайте изменения зависимых задач на этом этапе.
Вместо этого сохраните вычисленную разницу в переменной для дальнейшего использования.
var diff = 0;
gantt.attachEvent("onBeforeTaskChanged", function(id, mode, originalTask){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
var modifiedTask = gantt.getTask(id);
diff = modifiedTask.start_date - originalTask.start_date;
}
return true;
});
Наконец, используйте событие onAfterTaskDrag, чтобы применить вычисленную разницу ко всем зависимым задачам после подтверждения перетаскивания:
// Округлите позиции зависимых элементов до масштаба
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
gantt.eachSuccessor(function(child){
child.start_date = gantt.roundDate(new Date(child.start_date.valueOf() + diff));
child.end_date = gantt.calculateEndDate(child.start_date, child.duration);
gantt.updateTask(child.id);
},id );
}
});
Вот полный код для этого подхода:
(function(){
var diff = 0;
gantt.attachEvent("onBeforeTaskChanged", function(id, mode, originalTask){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
var modifiedTask = gantt.getTask(id);
diff = modifiedTask.start_date - originalTask.start_date;
}
return true;
});
// Округлите позиции зависимых элементов до масштаба
gantt.attachEvent("onAfterTaskDrag", function(id, mode, e){
var modes = gantt.config.drag_mode;
if(mode == modes.move ){
gantt.eachSuccessor(function(child){
child.start_date = gantt.roundDate(new Date(child.start_date.valueOf() + diff));
child.end_date = gantt.calculateEndDate(child.start_date, child.duration);
gantt.updateTask(child.id);
},id );
}
});
})();
К началу