gantt.html.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php include $app->getModuleRoot() . 'common/view/gantt.html.php';?>
  2. <style>
  3. #mainContent:before {background: #fff;}
  4. #ganttView .gantt_layout_cell {min-width: 560px!important;}
  5. .gantt-fullscreen #header,
  6. .gantt-fullscreen #mainMenu,
  7. .gantt-fullscreen #footer {display: none!important;}
  8. .gantt-fullscreen #mainContent {position: fixed; top: 0; right: 0; bottom: 0; left: 0}
  9. .gantt_task_content{display: none;}
  10. </style>
  11. <div id='mainContent' class='main-content load-indicator' data-loading='<?php echo $lang->review->exporting;?>'>
  12. <div id='ganttContainer'>
  13. <div class='gantt' id='ganttView'></div>
  14. </div>
  15. </div>
  16. <script>
  17. var scriptLoadedMap = {};
  18. var loadingPrefixText = '<?php echo $lang->review->exporting;?>';
  19. function getRemoteScript(url, sucessCallback, errorCallback)
  20. {
  21. if(scriptLoadedMap[url]) return sucessCallback && sucessCallback();
  22. $.getScript(url, function()
  23. {
  24. scriptLoadedMap[url] = true;
  25. if(sucessCallback) sucessCallback();
  26. }).fail(function()
  27. {
  28. if(errorCallback) errorCallback('Cannot load "' + url + '".');
  29. });
  30. }
  31. function updateProgress(progress)
  32. {
  33. var progressText = loadingPrefixText;
  34. if(progress < 1) progressText += Math.floor(progress * 100) + '%';
  35. $('#mainContent').attr('data-loading', progressText);
  36. }
  37. function drawGanttToCanvas(exportType, sucessCallback, errorCallback)
  38. {
  39. updateProgress(0);
  40. exportType = exportType || 'image';
  41. var $ganttView = $('#ganttView');
  42. var oldHeight = $ganttView.css('height');
  43. var $ganttContainer = $('#ganttContainer');
  44. var $ganttDataArea = $ganttView.find('.gantt_data_area');
  45. var $ganttDridData = $ganttView.find('.gantt_grid_data');
  46. var ganttHeight = $ganttView.find('.gantt_task_bg').outerHeight() + $ganttView.find('.gantt_grid_scale').outerHeight() + 1;
  47. var ganttWidth = $ganttDataArea.outerWidth() + $ganttDridData.outerWidth();
  48. $ganttContainer.css(
  49. {
  50. height: ganttHeight + $('#ganttHeader').outerHeight() + 80,
  51. width: ganttWidth + 93
  52. });
  53. $ganttView.css('height', ganttHeight);
  54. gantt.render();
  55. updateProgress(0.1);
  56. getRemoteScript('<?php echo $jsRoot . 'html2canvas/min.js';?>', function()
  57. {
  58. updateProgress(0.2);
  59. var afterFinish = function(canvas)
  60. {
  61. $ganttContainer.css(
  62. {
  63. width: '',
  64. height: ''
  65. });
  66. $ganttView.css('height', oldHeight);
  67. if(canvas) canvas.remove();
  68. };
  69. var delayTime = Math.max(1000, Math.floor(10 * (ganttHeight * ganttWidth) / 100000));
  70. var progressTimer;
  71. if(delayTime > 1500)
  72. {
  73. var startProgress = 0.2;
  74. var deltaProgress = 0.5 / Math.floor(delayTime/1000);
  75. progressTimer = setInterval(function()
  76. {
  77. startProgress += deltaProgress;
  78. updateProgress(Math.min(0.7, startProgress));
  79. }, 1000);
  80. }
  81. setTimeout(function()
  82. {
  83. if(progressTimer) clearInterval(progressTimer);
  84. updateProgress(0.7);
  85. html2canvas($ganttContainer[0], {logging: false}).then(function(canvas)
  86. {
  87. var isExportPDF = exportType === 'pdf';
  88. updateProgress(isExportPDF ? 0.8 : 0.9);
  89. canvas.onerror = function()
  90. {
  91. afterFinish(canvas);
  92. if(errorCallback) errorCallback('Cannot convert image to blob.');
  93. };
  94. if(isExportPDF)
  95. {
  96. var width = canvas.width;
  97. var height = canvas.height;
  98. getRemoteScript('<?php echo $jsRoot . 'pdfjs/min.js';?>', function()
  99. {
  100. updateProgress(0.8);
  101. var pdf = new jsPDF(
  102. {
  103. format: [width, height],
  104. unit: 'px',
  105. orientation: width > height ? 'l' : 'p'
  106. });
  107. pdf.addImage(canvas, 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
  108. pdf.save('gantt-export-<?php echo $review->project;?>.pdf');
  109. if(sucessCallback) sucessCallback(pdf);
  110. afterFinish();
  111. }, function(error)
  112. {
  113. afterFinish(canvas);
  114. if(errorCallback) errorCallback(error);
  115. });
  116. }
  117. else
  118. {
  119. canvas.toBlob(function(blob)
  120. {
  121. updateProgress(0.95);
  122. var imageUrl = URL.createObjectURL(blob);
  123. if(navigator.msSaveBlob)
  124. {
  125. navigator.msSaveOrOpenBlob(blob, 'gantt.png');
  126. }
  127. else
  128. {
  129. $('#ganttDownload').attr({href: imageUrl})[0].click();
  130. }
  131. if(sucessCallback) sucessCallback(imageUrl);
  132. afterFinish(canvas);
  133. });
  134. }
  135. }).catch(function(error)
  136. {
  137. afterFinish();
  138. if(errorCallback) errorCallback('Cannot draw graphic to canvas.');
  139. });
  140. }, delayTime);
  141. }, errorCallback);
  142. }
  143. function exportGantt(exportType)
  144. {
  145. var $mainContent = $('#mainContent');
  146. $mainContent.addClass('loading').css('height', Math.max(200, Math.floor($(window).height() - $('#footer').outerHeight() - $('#header').outerHeight() - $('#mainMenu').outerHeight() - 38)));
  147. $('#ganttExportDate').text(new Date().format('yyyy-MM-dd hh:mm:ss'));
  148. var afterFinish = function(url)
  149. {
  150. setTimeout(function()
  151. {
  152. $mainContent.css('height', '').removeClass('loading');
  153. }, 300);
  154. updateProgress(1);
  155. };
  156. drawGanttToCanvas(exportType, afterFinish, function(errorText)
  157. {
  158. afterFinish();
  159. $.zui.messager.danger('<?php echo $lang->execution->gantt->exportFail;?>' + (errorText || ''));
  160. });
  161. }
  162. function getByIdForGantt(list, id)
  163. {
  164. for (var i = 0; i < list.length; i++)
  165. {
  166. if (list[i].key == id) return list[i].label || "";
  167. }
  168. return "";
  169. }
  170. function zoomTasks(node)
  171. {
  172. switch(node.value)
  173. {
  174. case "day":
  175. gantt.config.min_column_width = 70;
  176. gantt.config.scales = [{unit: 'day', step: 1, format: '%m-%d'}];
  177. gantt.config.scale_height = 35;
  178. break;
  179. case "week":
  180. gantt.config.min_column_width = 70;
  181. gantt.config.scales = [{unit: 'week', step: 1, format: "<?php echo $lang->execution->gantt->zooming['week'];?> #%W"}, {unit:"day", step:1, date:"%D"}]
  182. gantt.config.scale_height = 60;
  183. break;
  184. case "month":
  185. gantt.config.min_column_width = 70;
  186. gantt.config.scale_height = 60;
  187. gantt.config.scales = [{unit: 'month', step: 1, format: '%M'}, {unit:"week", step:1, date:"<?php echo $lang->execution->gantt->zooming['week'];?> #%W"}];
  188. break;
  189. }
  190. gantt.render();
  191. }
  192. function updateCriticalPath()
  193. {
  194. gantt.config.highlight_critical_path = !gantt.config.highlight_critical_path;
  195. if(gantt.config.highlight_critical_path)
  196. {
  197. $('#criticalPath').html(<?php echo json_encode($lang->execution->gantt->hideCriticalPath);?>);
  198. gantt.config.highlight_critical_path = true;
  199. }
  200. else
  201. {
  202. $('#criticalPath').html(<?php echo json_encode($lang->execution->gantt->showCriticalPath);?>);
  203. gantt.config.highlight_critical_path = false;
  204. }
  205. gantt.render();
  206. }
  207. function exitHandler()
  208. {
  209. if (!document.fullscreenElement && !document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement) {
  210. location.reload();
  211. }
  212. }
  213. $(function()
  214. {
  215. document.addEventListener('fullscreenchange', exitHandler);
  216. var layout = '';
  217. // Set gantt view height
  218. var resizeGanttView = function()
  219. {
  220. if(gantt.getState().fullscreen) return false;
  221. $('#ganttView').css('height', Math.max(200, Math.floor($(window).height() - $('#footer').outerHeight() - $('#header').outerHeight() - $('#mainMenu').outerHeight() - 100)));
  222. };
  223. var ganttData = $.parseJSON(<?php echo json_encode(json_encode($plans));?>);
  224. if(!ganttData.data) ganttData.data = [];
  225. gantt.config.readonly = true;
  226. gantt.config.row_height = 25;
  227. gantt.config.min_column_width = 40;
  228. gantt.config.details_on_create = false;
  229. gantt.config.scales = [{unit: 'day', step: 1, format: '%m-%d'}];
  230. gantt.config.duration_unit = "day";
  231. gantt.config.columns = [
  232. {name: 'text', width: '*', tree: true, resize: true, width:200},
  233. {name: 'start_date', align: 'center', resize: true, width: 80},
  234. {name: 'deadline', align: 'center', resize: true, width: 80},
  235. {name: 'duration', align: 'center', resize: true, width: 60},
  236. {name: 'percent', align: 'center', resize: true, width:70, template: function(plan)
  237. {
  238. if(plan.percent) return Math.round(plan.percent) + '%';
  239. }
  240. },
  241. {name: 'taskProgress', align: 'center', resize: true, width: 60},
  242. {name: 'realStarted', align: 'center', resize: true, width: 80},
  243. {name: 'realFinished', align: 'center', width: 80}
  244. ];
  245. layout = gantt.config.layout;
  246. gantt.config.layout = {
  247. css: "gantt_container",
  248. cols: [
  249. {
  250. rows:[
  251. {view: "grid", scrollX: "gridScroll", scrollable: true, scrollY: "scrollVer"},
  252. {view: "scrollbar", id: "gridScroll", group:"horizontal"}
  253. ]
  254. }
  255. ]
  256. };
  257. gantt.locale.labels.column_text = "<?php echo $lang->programplan->name;?>";
  258. gantt.locale.labels.column_percent = "<?php echo $lang->programplan->percentAB;?>";
  259. gantt.locale.labels.column_taskProgress = "<?php echo $lang->programplan->taskProgress;?>";
  260. gantt.locale.labels.column_start_date = "<?php echo $lang->programplan->begin;?>";
  261. gantt.locale.labels.column_deadline = "<?php echo $lang->programplan->end;?>";
  262. gantt.locale.labels.column_realStarted = "<?php echo $lang->programplan->realBegan;?>";
  263. gantt.locale.labels.column_realFinished = "<?php echo $lang->programplan->realEnd;?>";
  264. gantt.locale.labels.column_duration = "<?php echo $lang->programplan->duration;?>";
  265. var date2Str = gantt.date.date_to_str(gantt.config.task_date);
  266. var today = new Date();
  267. var todayTips = "<?php echo $lang->programplan->today;?>";
  268. gantt.addMarker({
  269. start_date: today,
  270. css: "today",
  271. text: todayTips,
  272. title: todayTips + ": " + date2Str(today)
  273. });
  274. gantt.templates.scale_cell_class = function(date)
  275. {
  276. if(date.getDay() == 0 || date.getDay() == 6) return 'weekend';
  277. };
  278. gantt.templates.timeline_cell_class = function(item, date)
  279. {
  280. if(date.getDay() == 0 || date.getDay() == 6) return 'weekend';
  281. };
  282. gantt.attachEvent('onTemplatesReady', function()
  283. {
  284. $('#fullScreenBtn').click(function()
  285. {
  286. gantt.config.layout = layout;
  287. gantt.init('ganttView');
  288. gantt.expand();
  289. });
  290. });
  291. var isGanttExpand = false;
  292. var delayTimer = null;
  293. var handleFullscreen = function()
  294. {
  295. if(isGanttExpand)
  296. {
  297. $('body').addClass('gantt-fullscreen');
  298. $('#ganttView').css('height', $(window).height() - 40);
  299. isGanttExpand = false;
  300. }
  301. else
  302. {
  303. $('body').removeClass('gantt-fullscreen');
  304. resizeGanttView();
  305. }
  306. delayTimer = null;
  307. };
  308. var delayHandleFullscreen = function()
  309. {
  310. if(delayTimer) clearTimeout(delayTimer);
  311. delayTimer = setTimeout(handleFullscreen, 50);
  312. };
  313. gantt.attachEvent('onBeforeExpand', function()
  314. {
  315. $('body').addClass('gantt-fullscreen');
  316. isGanttExpand = true;
  317. return true;
  318. });
  319. if(document.addEventListener)
  320. {
  321. document.addEventListener('webkitfullscreenchange', delayHandleFullscreen, false);
  322. document.addEventListener('mozfullscreenchange', delayHandleFullscreen, false);
  323. document.addEventListener('fullscreenchange', delayHandleFullscreen, false);
  324. document.addEventListener('MSFullscreenChange', delayHandleFullscreen, false);
  325. }
  326. resizeGanttView();
  327. $(window).resize(resizeGanttView);
  328. gantt.templates.grid_folder = function(item) {
  329. return "";
  330. };
  331. gantt.templates.grid_file = function(item) {
  332. return "";
  333. };
  334. gantt.init('ganttView');
  335. gantt.parse(ganttData);
  336. gantt.showDate(new Date());
  337. // Show task in modal on click task
  338. var taskModalTrigger = new $.zui.ModalTrigger({type: 'iframe', width: '80%'});
  339. gantt.attachEvent('onTaskClick', function(id, e)
  340. {
  341. if($(e.srcElement).hasClass('gantt_close') || $(e.srcElement).hasClass('gantt_open')) return false;
  342. if(typeof id === 'string') id = parseInt(id);
  343. if(!isNaN(id) && id > 0)
  344. {
  345. //taskModalTrigger.show({url: createLink('task', 'view', 'taskID=' + id, 'html', true)});
  346. }
  347. });
  348. // Make folder can open or close by click
  349. $('#ganttView').on('click', '.gantt_close,.gantt_open', function()
  350. {
  351. var $task = $(this).closest('.gantt_row_task');
  352. var task = gantt.getTask($task.attr('task_id'));
  353. if(task) gantt[task.$open ? 'close' : 'open'](task.id);
  354. });
  355. });
  356. </script>