create.ui.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. window.waitDom('#form-task-create [name=story]', function(){setPreview();})
  2. window.waitDom('#form-task-create [name=story]', function(){setStoryRelated();})
  3. /**
  4. * 根据多人任务是否勾选展示团队。
  5. * Show team menu box.
  6. *
  7. * @access public
  8. * @return void
  9. */
  10. function toggleTeam()
  11. {
  12. $assignedToBox = $('.assignedToBox');
  13. if($('[name^=multiple]').prop('checked'))
  14. {
  15. $assignedToBox.find('.add-team').removeClass('hidden');
  16. $assignedToBox.find('.picker-box').addClass('hidden');
  17. $assignedToBox.find('.assignedToList').removeClass('hidden');
  18. $('input[name=estimate]').attr('disabled', true);
  19. $('[name=parent]').zui('picker').$.setValue('');
  20. $('[name=parent]').zui('picker').render({disabled: true});
  21. }
  22. else
  23. {
  24. $assignedToBox.find('.add-team').addClass('hidden');
  25. $assignedToBox.find('.picker-box').removeClass('hidden');
  26. $assignedToBox.find('.assignedToList').addClass('hidden');
  27. $('input[name=estimate]').removeAttr('disabled');
  28. $('[name=parent]').zui('picker').render({disabled: false});
  29. }
  30. }
  31. /**
  32. * 根据任务类型设置任务相关字段。
  33. * Set task-related fields based on the task type.
  34. *
  35. * @param object e
  36. * @access public
  37. * @return void
  38. */
  39. function typeChange()
  40. {
  41. const result = $('#form-task-create [name=type]').val();
  42. /* Change assigned person to multiple selection, and hide multiple team box. */
  43. const $assignedToPicker = $('#form-task-create [name^=assignedTo]').zui('picker');
  44. if(result == 'affair')
  45. {
  46. $('.assignedToBox .assignedToList').addClass('hidden');
  47. $('.assignedToBox .add-team').addClass('hidden');
  48. $('[name=multiple]').prop("checked", false);
  49. toggleTeam();
  50. $assignedToPicker.render(assignedToOptions.multiple);
  51. }
  52. /* If assigned selection is multiple, remove multiple and hide the selection of select all members. */
  53. else if($assignedToPicker.options.multiple)
  54. {
  55. $assignedToPicker.render(assignedToOptions.single);
  56. $assignedToPicker.$.setValue('');
  57. }
  58. $('#form-task-create [name=multiple]').closest('.checkbox-primary').toggleClass('hidden', result == 'affair');
  59. /* If the execution has story list, toggle between hiding and displaying the selection of select test story box. */
  60. const $selectTestStory = $('#form-task-create [name=selectTestStory]');
  61. const $testStoryBox = $selectTestStory.closest('.checkbox-primary');
  62. $selectTestStory.prop('checked', false);
  63. $testStoryBox.addClass('hidden');
  64. if(lifetime != 'ops' && attribute != 'review')
  65. {
  66. $testStoryBox.toggleClass('hidden', result != 'test');
  67. toggleSelectTestStory();
  68. }
  69. }
  70. /**
  71. * 根据选择研发需求是否勾选切换相关字段的展示与隐藏。
  72. * Dynamically control whether task fields are hidden based on selection status of selectTestStory.
  73. *
  74. * @param int executionID
  75. * @access public
  76. * @return void
  77. */
  78. function toggleSelectTestStory()
  79. {
  80. if(!$('#form-task-create [name=selectTestStory]').hasClass('hidden') && $('#form-task-create [name=selectTestStory]').prop('checked'))
  81. {
  82. $('#form-task-create [data-name=module]').addClass('hidden');
  83. $('#form-task-create [data-name=storyBox]').addClass('hidden');
  84. $('#form-task-create [data-name=datePlan]').addClass('hidden');
  85. $('#form-task-create [data-name=estimate]').addClass('hidden');
  86. $('#form-task-create [data-name=parent]').addClass('hidden');
  87. $('.panel-actions .btn').attr('disabled', 'disabled');
  88. if($('[name=parent]').length) $('[name=parent]').zui('picker').$.setValue('');
  89. $('#form-task-create [data-name=assignedToBox]').insertAfter('#form-task-create [data-name=name]');
  90. $('#form-task-create [name=multiple]').closest('.checkbox-primary').addClass('hidden');
  91. $('#form-task-create [data-name=name]').removeClass('w-full').addClass('w-1/2');
  92. $('#form-task-create [data-name=testStoryBox]').removeClass('hidden');
  93. const currentExecutionID = $('#form-task-create [name=execution]').val();
  94. loadTarget($.createLink('task', 'ajaxGetTestStories', 'executionID=' + currentExecutionID + '&taskID=' + taskID), '#testStoryBox');
  95. if($('[data-name=execution]').hasClass('hidden'))
  96. {
  97. $('#form-task-create [data-name=name]').removeClass('lite:w-full');
  98. $('#form-task-create [data-name=pri]').removeClass('w-1/4').addClass('w-1/2 full:w-1/4');
  99. $('#form-task-create [data-name=assignedToBox]').addClass('full:w-1/4');
  100. }
  101. else
  102. {
  103. $('#form-task-create [data-name=assignedToBox]').removeClass('w-1/2').addClass('w-1/4');
  104. }
  105. $('#form-task-create [name^=multiple]').prop('checked', false);
  106. toggleTeam();
  107. }
  108. else
  109. {
  110. $('.panel-actions .btn').removeAttr('disabled');
  111. $('#form-task-create [data-name=name]').removeClass('w-1/2').addClass('w-full');
  112. $('#form-task-create [data-name=module]').removeClass('hidden');
  113. $('#form-task-create [data-name=storyBox]').removeClass('hidden');
  114. $('#form-task-create [data-name=datePlan]').removeClass('hidden');
  115. $('#form-task-create [data-name=estimate]').removeClass('hidden');
  116. $('#form-task-create [data-name=parent]').removeClass('hidden');
  117. $('#form-task-create [data-name=assignedToBox]').insertBefore('#form-task-create [data-name=name]');
  118. $('#form-task-create [data-name=testStoryBox]').addClass('hidden');
  119. if($('#form-task-create [name=type]').val() != 'affair') $('#form-task-create [name=multiple]').closest('.checkbox-primary').removeClass('hidden');
  120. if($('#form-task-create [data-name=execution]').hasClass('hidden'))
  121. {
  122. $('#form-task-create [data-name=name]').addClass('lite:w-full');
  123. $('#form-task-create [data-name=pri]').addClass('w-1/4').removeClass('w-1/2 full:w-1/4');
  124. $('#form-task-create [data-name=assignedBox]').removeClass('full:w-1/4');
  125. }
  126. else
  127. {
  128. $('#form-task-create [data-name=assignedToBox]').removeClass('w-1/4').addClass('w-1/2');
  129. }
  130. }
  131. }
  132. /**
  133. * 根据执行ID加载模块、需求和团队成员。
  134. * Load module, stories and members base on the execution id.
  135. *
  136. * @access public
  137. * @return void
  138. */
  139. function loadAll()
  140. {
  141. const executionID = $('input[name=execution]').val();
  142. const fieldList = showFields + ',';
  143. lifetime = lifetimeList[executionID];
  144. attribute = attributeList[executionID];
  145. /* If the execution doesn't have story list, hide the related fields.*/
  146. if(lifetime == 'ops' || attribute == 'request' || attribute == 'review')
  147. {
  148. $('.storyBox').addClass('hidden');
  149. $("input[name='after'][value='toStoryList']").parent().hide();
  150. $("input[name='after'][value='continueAdding']").parent().hide();
  151. $("input[name='after'][value='toTaskList']").prop('checked', true);
  152. }
  153. /* If story field is showed, show the story field. */
  154. else if(fieldList.indexOf('story') >= 0)
  155. {
  156. $('.storyBox').removeClass('hidden');
  157. if($('#selectTestStory').prop('checked')) $('#selectTestStory').removeClass('hidden');
  158. $("input[name='after'][value='toStoryList']").parent().show();
  159. $("input[name='after'][value='continueAdding']").parent().show();
  160. }
  161. /* Load modules, stories and members of the execution. */
  162. loadModules(executionID);
  163. loadExecutionStories();
  164. loadExecutionMembers(executionID);
  165. $('#selectTestStory').prop('checked', false);
  166. toggleSelectTestStory(executionID);
  167. }
  168. /**
  169. * 加载执行的模块。
  170. * Load modules of the execution.
  171. *
  172. * @param int $executionID
  173. * @access public
  174. * @return void
  175. */
  176. function loadModules(executionID)
  177. {
  178. const extra = $('input[name=isShowAllModule]') ? 'allModule' : '';
  179. const getModuleLink = $.createLink('tree', 'ajaxGetOptionMenu', 'rootID=' + executionID + '&viewtype=task&branch=0&rootModuleID=0&returnType=items&fieldID=&extra=' + extra);
  180. $.get(getModuleLink, function(modules)
  181. {
  182. if(modules)
  183. {
  184. modules = JSON.parse(modules);
  185. const $modulePicker = $('input[name=module]').zui('picker');
  186. $modulePicker.render({items: modules});
  187. }
  188. });
  189. }
  190. /**
  191. * 加载执行的需求。
  192. * Load stories of the execution
  193. *
  194. * @access public
  195. * @return void
  196. */
  197. window.loadExecutionStories = function()
  198. {
  199. const storyID = $('input[name="story"]').val();
  200. const executionID = $('input[name="execution"]').length == 0 ? window.executionID : $('input[name="execution"]').val();
  201. const moduleID = $('input[name="module"]').val();
  202. const getStoryLink = $.createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID + '&storyID=' + storyID + '&pageType=&type=full&status=active');
  203. $.get(getStoryLink, function(stories)
  204. {
  205. stories = JSON.parse(stories);
  206. const $storyPicker = $('input[name=story]').zui('picker');
  207. $storyPicker.render({items: stories});
  208. $storyPicker.$.setValue(storyID);
  209. setPreview();
  210. /* If there is no story option, select will be hidden and text will be displayed; otherwise, the opposite is true */
  211. if(stories.length)
  212. {
  213. $('.setStoryBox').removeClass('hidden');
  214. $('.empty-story-tip').addClass('hidden');
  215. }
  216. else
  217. {
  218. $('.setStoryBox').addClass('hidden');
  219. $('.empty-story-tip').removeClass('hidden');
  220. }
  221. });
  222. }
  223. /**
  224. * 加载执行的团队成员。
  225. * Load team members of the execution.
  226. *
  227. * @param executionID $executionID
  228. * @access public
  229. * @return void
  230. */
  231. function loadExecutionMembers(executionID)
  232. {
  233. $('#multipleBox').removeAttr('checked');
  234. $('.team-group,.modeBox').addClass('hidden');
  235. const getAssignedToLink = $.createLink('execution', 'ajaxGetMembers', 'executionID=' + executionID + '&assignedTo=' + $('#assignedTo').val());
  236. $.get(getAssignedToLink, function(data)
  237. {
  238. if(data)
  239. {
  240. data = JSON.parse(data);
  241. const $assignedToPicker = $('input[name=assignedTo]').zui('picker');
  242. $assignedToPicker.render({items: data});
  243. }
  244. });
  245. }
  246. /**
  247. * 加载区域对应泳道。
  248. * Load lanes of the region.
  249. *
  250. * @access public
  251. * @return void
  252. */
  253. function loadLanes()
  254. {
  255. const regionID = $('input[name=region]').val();
  256. const getLaneLink = $.createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=task&field=lane');
  257. $.getJSON(getLaneLink, function(data)
  258. {
  259. const laneID = data.items.length > 0 ? data.items[0].value : '';
  260. const $lanePicker = $('input[name=lane]').zui('picker');
  261. $lanePicker.$.setValue(laneID);
  262. $lanePicker.render({items: data.items});
  263. });
  264. }
  265. /**
  266. * 根据选择需求设置查看链接和所属模块。
  267. * Set preview and module of story.
  268. *
  269. * @access public
  270. * @return void
  271. */
  272. function setStoryRelated()
  273. {
  274. $('[name=copyButton]').prop('checked', false);
  275. setPreview();
  276. setStoryModule();
  277. }
  278. /**
  279. * 设置需求的查看链接。
  280. * Set the story priview link.
  281. *
  282. * @access public
  283. * @return void
  284. */
  285. function setPreview()
  286. {
  287. let storyID = $("input[name='story']").val() ? $("input[name='story']").val() : 0;
  288. if(parseFloat(storyID) == 0 || !canViewStory)
  289. {
  290. $('#preview').addClass('hidden');
  291. }
  292. else
  293. {
  294. let storyLink = $.createLink('execution', 'storyView', "storyID=" + storyID);
  295. let concat = storyLink.indexOf('?') < 0 ? '?' : '&';
  296. if(storyLink.indexOf("onlybody=yes") < 0) storyLink = storyLink + concat + 'onlybody=yes';
  297. $('#preview').removeClass('hidden');
  298. $('#preview').attr('data-url', storyLink);
  299. $('#preview').attr('data-size', 'lg');
  300. }
  301. setAfter();
  302. }
  303. /**
  304. * 根据需求设置任务的所属模块。
  305. * Set the story module.
  306. *
  307. * @access public
  308. * @return void
  309. */
  310. function setStoryModule()
  311. {
  312. var storyID = $('input[name=story]').val();
  313. if(storyID)
  314. {
  315. var link = $.createLink('story', 'ajaxGetInfo', 'storyID=' + storyID);
  316. $.getJSON(link, function(storyInfo)
  317. {
  318. if(storyInfo)
  319. {
  320. $('input[name=module]').zui('picker').$.setValue(storyInfo.moduleID);
  321. $('input[name=storyEstimate]').val(storyInfo.estimate);
  322. $('input[name=storyPri]').val(storyInfo.pri);
  323. $('input[name=storyDesc]').val(storyInfo.spec);
  324. }
  325. });
  326. }
  327. }
  328. /**
  329. * 设置保存任务之后的选项。
  330. * Set after locate.
  331. *
  332. * @access public
  333. * @return void
  334. */
  335. function setAfter()
  336. {
  337. if($("input[name=story]").length == 0 || !$("input[name=story]").val() || $("input[name=story]").val() == 0)
  338. {
  339. /* If the exeuction doesn't have stories, hide the selections of story. */
  340. if($('input[value="continueAdding"]').prop('checked'))
  341. {
  342. $('input[value="toTaskList"]').prop('checked', true);
  343. }
  344. $('input[value="continueAdding"]').attr('disabled', 'disabled');
  345. $('input[value="toStoryList"]').attr('disabled', 'disabled');
  346. }
  347. else
  348. {
  349. /* If the exeuction has stories, show the selections of story. */
  350. if(!toTaskList) $('input[value="continueAdding"]').prop('checked', true);
  351. $('input[value="continueAdding"]').removeAttr('disabled');
  352. $('input[value="toStoryList"]').removeAttr('disabled');
  353. }
  354. }
  355. /**
  356. * Add a row.
  357. *
  358. * @param object $obj
  359. * @access public
  360. * @return void
  361. */
  362. window.addItem = function(obj)
  363. {
  364. let $newLine = $(obj).closest('tr').clone();
  365. /* 将已有需求下拉的最大name属性的值加1赋值给新行. */
  366. let index = 0;
  367. $("#testTaskTable [name^='testStory']").each(function()
  368. {
  369. let id = $(this).attr('name').replace(/[^\d]/g, '');
  370. id = parseInt(id);
  371. id ++;
  372. index = id > index ? id : index;
  373. });
  374. /* 重新初始化新一行的下拉控件. */
  375. $newLine.find('.c-testStory .form-group-wrapper').attr('id', `testStory${index}`).removeAttr('data-zui-picker').empty();
  376. $newLine.find('.c-testPri .form-group-wrapper').attr('id', `testPri${index}`).removeAttr('data-zui-picker').empty();
  377. $newLine.find('.c-testEstStarted .form-group-wrapper').attr('id', `testEstStarted${index}`).removeAttr('data-zui-picker').empty();
  378. $newLine.find('.c-testDeadline .form-group-wrapper').attr('id', `testDeadline${index}`).removeAttr('data-zui-picker').empty();
  379. $newLine.find('.c-testAssignedTo .form-group-wrapper').attr('id', `testAssignedTo${index}`).removeAttr('data-zui-picker').empty();
  380. $newLine.find('.c-estimate input').attr('id', `testEstimate${index}`).attr('name', `testEstimate[${index}]`).val('');
  381. $(obj).closest('tr').after($newLine);
  382. let storyOptions = $(obj).closest('tr').find("[name^='testStory']").zui('picker').options;
  383. storyOptions.name = `testStory[${index}]`;
  384. new zui.Picker(`#testStory${index}`, storyOptions);
  385. let priOptions = $(obj).closest('tr').find("[name^='testPri']").zui('priPicker').options;
  386. priOptions.name = `testPri[${index}]`;
  387. priOptions.defaultValue = '3';
  388. new zui.PriPicker(`#testPri${index}`, priOptions);
  389. let estStartedOptions = $(obj).closest('tr').find("[name^='testEstStarted']").zui('datePicker').options;
  390. estStartedOptions.name = `testEstStarted[${index}]`;
  391. estStartedOptions.defaultValue = '';
  392. new zui.DatePicker(`#testEstStarted${index}`, estStartedOptions);
  393. let deadlineOptions = $(obj).closest('tr').find("[name^='testDeadline']").zui('datePicker').options;
  394. deadlineOptions.name = `testDeadline[${index}]`;
  395. deadlineOptions.defaultValue = '';
  396. new zui.DatePicker(`#testDeadline${index}`, deadlineOptions);
  397. let assignedToOptions = $(obj).closest('tr').find("[name^='testAssignedTo']").zui('picker').options;
  398. assignedToOptions.name = `testAssignedTo[${index}]`;
  399. assignedToOptions.defaultValue = '';
  400. new zui.Picker(`#testAssignedTo${index}`, assignedToOptions);
  401. }
  402. /**
  403. * Remove a row.
  404. *
  405. * @param object $obj
  406. * @access public
  407. * @return void
  408. */
  409. window.removeItem = function(obj)
  410. {
  411. if($('#testStoryBox').find('tbody tr').length == 1) return false;
  412. $(obj).closest('tr').remove();
  413. }
  414. window.checkTeamMember = function()
  415. {
  416. $('div.assignedToList').html('');
  417. let team = [];
  418. let totalEstimate = 0;
  419. let error = false;
  420. let mode = $('[name="mode"]').val();
  421. let assignedToList = '';
  422. $(this).closest('#teamTable').find('.picker-box').each(function(index)
  423. {
  424. if(!$(this).find('[name^=team]').val()) return;
  425. let realname = $(this).find('.picker-single-selection').text();
  426. let account = $(this).find('[name^=team]').val();
  427. if(!team.includes(realname)) team.push(realname);
  428. let estimate = parseFloat($(this).closest('tr').find('[name^=teamEstimate]').val());
  429. if(!isNaN(estimate) && estimate > 0) totalEstimate += estimate;
  430. if(realname != '' && (isNaN(estimate) || estimate <= 0))
  431. {
  432. zui.Modal.alert(realname + ' ' + estimateNotEmpty);
  433. error = true;
  434. return false;
  435. }
  436. assignedToList += `<div class='picker-multi-selection' data-index=${index} data-account=${account}><span class='text'>${realname}</span><div class="picker-deselect-btn btn size-xs ghost"><span class="close"></span></div></div>`;
  437. if(mode == 'linear') assignedToList += '<i class="icon icon-arrow-right"></i>';
  438. })
  439. if(error) return false;
  440. if(team.length < 2)
  441. {
  442. zui.Modal.alert(teamMemberError);
  443. return false;
  444. }
  445. else
  446. {
  447. $('[data-name=estimate] input').val(totalEstimate);
  448. }
  449. /* 将选中的团队成员展示在指派给后面. */
  450. const regex = /<i class="icon icon-arrow-right"><\/i>(?!.*<i class="icon icon-arrow-right"><\/i>)/;
  451. assignedToList = assignedToList.replace(regex, '');
  452. $('div.assignedToList').prepend(assignedToList);
  453. zui.Modal.hide('#modalTeam');
  454. return false;
  455. };
  456. window.removeTeamMember = function()
  457. {
  458. /* 团队成员必须大于1人. */
  459. if($(this).closest('.assignedToList').find('.picker-multi-selection').length == 2)
  460. {
  461. zui.Modal.alert(teamMemberError);
  462. return false;
  463. }
  464. /* 去重后查看人数. */
  465. let accounts = [];
  466. $(this).closest('.assignedToList').find('.picker-multi-selection').not(this).each(function()
  467. {
  468. const account = $(this).data('account');
  469. if(account) accounts.push(account);
  470. })
  471. let uniqueAccounts = [...new Set(accounts)];
  472. if(uniqueAccounts && uniqueAccounts.length == 1)
  473. {
  474. zui.Modal.alert(teamMemberError);
  475. return false;
  476. }
  477. /* 删除人员前后的箭头. */
  478. if($(this).next('.icon').length)
  479. {
  480. $(this).next('.icon').remove();
  481. }
  482. else if($(this).prev('.icon').length)
  483. {
  484. $(this).prev('.icon').remove();
  485. }
  486. $(this).remove();
  487. /* 删除团队中,已经选中的人. */
  488. let index = $(this).data('index');
  489. $('#teamTable tbody').find('tr').eq(index).remove();
  490. let totalEstimate = 0;
  491. $('#teamTable').find('[name^=teamEstimate]').each(function(index)
  492. {
  493. let estimate = parseFloat($(this).val());
  494. if(!isNaN(estimate) && estimate > 0) totalEstimate += estimate;
  495. })
  496. $("[name='estimate']").val(totalEstimate);
  497. setLineIndex();
  498. }
  499. window.copyStoryTitle = function(e)
  500. {
  501. if(!$('[name=story]').val() || $('[name=story]').val() == 0) return;
  502. let storyTitle = $('[data-name=storyBox] .setStoryBox span.picker-single-selection').text();
  503. let startPosition = storyTitle.indexOf(':') + 1;
  504. if (startPosition > 0) {
  505. let endPosition = storyTitle.lastIndexOf('(');
  506. storyTitle = storyTitle.substr(startPosition, endPosition - startPosition);
  507. }
  508. if($(e.target).prop('checked'))
  509. {
  510. $('[name=name]').val(storyTitle);
  511. $('[name=estimate]').val($('input[name=storyEstimate]').val());
  512. $('zen-editor[name=desc]')[0].setHTML($('input[name=storyDesc]').val());
  513. $('input[name=pri]').zui('pripicker').$.setValue($('input[name=storyPri]').val());
  514. }
  515. else
  516. {
  517. $('[name=name]').val($('[name=taskName]').val());
  518. $('[name=estimate]').val($('[name=taskEstimate]').val());
  519. $('input[name=pri]').zui('pripicker').$.setValue(3);
  520. }
  521. }
  522. window.showAllModule = function(e)
  523. {
  524. const extra = $(e.target).prop('checked') ? 'allModule' : '';
  525. const getModuleLink = $.createLink('tree', 'ajaxGetOptionMenu', "rootID=" + executionID + '&viewType=task&branch=0&rootModuleID=0&returnType=items&fieldID=&extra=' + extra);
  526. $.getJSON(getModuleLink, function(modules)
  527. {
  528. const moduleID = $('input[name=module]').val();
  529. const $modulePicker = $('input[name=module]').zui('picker');
  530. $modulePicker.render({items: modules});
  531. $modulePicker.$.setValue(moduleID);
  532. });
  533. }
  534. window.closeModal = function(e)
  535. {
  536. const $modal = $(e.target).closest('.modal');
  537. if($modal.length == 0) return;
  538. const modalID = $modal.attr('id');
  539. zui.Modal.hide('#' + modalID);
  540. }
  541. window.saveTaskName = function(e)
  542. {
  543. $('[name=taskName]').val($(e.target).val());
  544. }
  545. window.saveTaskEstimate = function(e)
  546. {
  547. $('[name=taskEstimate]').val($(e.target).val());
  548. }
  549. window.changeTeamMember = function(e)
  550. {
  551. $(e.target).closest('td').next().toggleClass('required', $(e.target).val() != '');
  552. }
  553. window.renderRowData = function($row, index, row)
  554. {
  555. const mode = $('[name=mode]').val();
  556. $row.find('[data-name=id]').addClass('center').html("<span class='team-number'>" + $row.find('[data-name=id]').text() + "</span><i class='icon-angle-down " + (mode == 'linear' ? '' : 'hidden') + "'><i/>");
  557. /* 复制上一行的人员下拉。*/
  558. $row.find('[data-name=team]').find('.picker-box').on('inited', function(e, info)
  559. {
  560. const $team = info[0];
  561. const $preTeam = $row.prev().find('input[name^=team]').zui('picker');
  562. if($preTeam != undefined) $team.render({items: $preTeam.options.items});
  563. })
  564. }
  565. parentEstStarted = '';
  566. parentDeadline = '';
  567. overParentEstStartedLang = '';
  568. overParentDeadlineLang = '';
  569. window.getParentEstStartedAndDeadline = function()
  570. {
  571. const $parent = $('[name=parent]');
  572. const parent = $parent.val();
  573. if(!parent)
  574. {
  575. if(taskDateLimit != 'limit') return;
  576. const $form = $parent.closest('form');
  577. $form.find('[name=estStarted]').zui('datePicker').render({disabled: false});
  578. $form.find('[name=deadline]').zui('datePicker').render({disabled: false});
  579. return;
  580. }
  581. const link = $.createLink('task', 'ajaxGetTaskEstStartedAndDeadline', 'taskID=' + parent);
  582. $.getJSON(link, function(data)
  583. {
  584. parentEstStarted = data.estStarted;
  585. parentDeadline = data.deadline;
  586. overParentEstStartedLang = data.overParentEstStartedLang;
  587. overParentDeadlineLang = data.overParentDeadlineLang;
  588. window.checkEstStartedAndDeadline({target: $('[name=estStarted]')});
  589. window.checkEstStartedAndDeadline({target: $('[name=deadline]')});
  590. });
  591. }
  592. window.checkEstStartedAndDeadline = function(event)
  593. {
  594. if(taskDateLimit != 'limit') return;
  595. const parent = $('[name=parent]').val();
  596. if(!parent) return;
  597. const $form = $(event.target).closest('form');
  598. const field = $(event.target).attr('name')
  599. const $estStarted = $form.find('[name=estStarted]');
  600. const estStarted = $estStarted.val();
  601. const $deadline = $form.find('[name=deadline]');
  602. const deadline = $deadline.val();
  603. const $estStartedDiv = $estStarted.closest('.form-group-wrapper');
  604. if(field == 'estStarted') $estStartedDiv.find('#estStartedTip').remove();
  605. if(field == 'estStarted' && estStarted.length > 0 && parentEstStarted.length > 0 && estStarted < parentEstStarted)
  606. {
  607. let $datetip = $('<div class="form-tip text-danger" id="estStartedTip"></div>');
  608. $datetip.append('<div>' + overParentEstStartedLang + '</div>');
  609. $estStartedDiv.append($datetip);
  610. }
  611. const $deadlineDiv = $deadline.closest('.form-group-wrapper');
  612. if(field == 'deadline') $deadlineDiv.find('#deadlineTip').remove();
  613. if(field == 'deadline' && deadline.length > 0 && parentDeadline.length > 0 && deadline > parentDeadline)
  614. {
  615. let $datetip = $('<div class="form-tip text-danger" id="deadlineTip"></div>');
  616. $datetip.append('<div>' + overParentDeadlineLang + '</div>');
  617. $deadlineDiv.append($datetip);
  618. }
  619. let $estStartedPicker = $estStarted.zui('datePicker');
  620. let $deadlinePicker = $deadline.zui('datePicker');
  621. $estStartedPicker.render({disabled: parentEstStarted == ''});
  622. $deadlinePicker.render({disabled: parentDeadline == ''});
  623. if(parentEstStarted == '') $estStartedPicker.$.setValue('');
  624. if(parentDeadline == '') $deadlinePicker.$.setValue('');
  625. }