edit.html.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. <?php
  2. /**
  3. * The edit view of task of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author zenggang<zenggang@easycorp.ltd>
  8. * @package task
  9. * @link https://www.zentao.net
  10. */
  11. namespace zin;
  12. include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php');
  13. $taskStatus = isset($task->rawStatus) ? $task->rawStatus : $task->status;
  14. /* ====== Preparing and processing page data ====== */
  15. jsVar('oldStoryID', $task->story);
  16. jsVar('oldAssignedTo', $task->assignedTo);
  17. jsVar('oldExecutionID', $task->execution);
  18. jsVar('oldConsumed', $task->consumed);
  19. jsVar('objectID', $execution->multiple ? $execution->id : $execution->project);
  20. jsVar('taskStatus', $taskStatus);
  21. jsVar('currentUser', $app->user->account);
  22. jsVar('team', array_values($task->members));
  23. jsVar('members', $members);
  24. jsVar('page', 'edit');
  25. jsVar('confirmChangeExecution', $lang->task->confirmChangeExecution);
  26. jsVar('teamMemberError', $lang->task->error->teamMember);
  27. jsVar('totalLeftError', sprintf($this->lang->task->error->leftEmptyAB, zget($this->lang->task->statusList, $taskStatus)));
  28. jsVar('confirmRecord', $lang->task->confirmRecord);
  29. jsVar('estimateNotEmpty', sprintf($lang->error->gt, $lang->task->estimate, '0'));
  30. jsVar('leftNotEmpty', sprintf($lang->error->gt, $lang->task->left, '0'));
  31. jsVar('requiredFields', $config->task->edit->requiredFields);
  32. jsVar('+parentEstStarted', !empty($parentTask) ? $parentTask->estStarted : '');
  33. jsVar('+parentDeadline', !empty($parentTask) ? $parentTask->deadline : '');
  34. jsVar('childDateLimit', $childDateLimit);
  35. jsVar('ignoreLang', $lang->project->ignore);
  36. jsVar('+overParentEstStartedLang', !empty($parentTask) ? sprintf($lang->task->overParentEsStarted, $parentTask->estStarted) : '');
  37. jsVar('+overParentDeadlineLang', !empty($parentTask) ? sprintf($lang->task->overParentDeadline, $parentTask->deadline) : '');
  38. jsVar('+overChildEstStartedLang', sprintf($lang->task->overChildEstStarted, $childDateLimit['estStarted']));
  39. jsVar('+overChildDeadlineLang', sprintf($lang->task->overChildDeadline, $childDateLimit['deadline']));
  40. jsVar('taskDateLimit', empty($project) ? '' : $project->taskDateLimit);
  41. $confirmSyncTip = '';
  42. if(!empty($syncChildren) && !empty($task->children)) $confirmSyncTip = sprintf($lang->task->syncStoryToChildrenTip, 'ID' . implode(', ID', $syncChildren));
  43. jsVar('confirmSyncTip', $confirmSyncTip);
  44. jsVar('taskID', $task->id);
  45. jsVar('taskStory', $task->story);
  46. /* zin: Set variables to define picker options for form */
  47. $formTitle = $task->name;
  48. $executionOptions = $executions;
  49. $moduleOptions = $modules;
  50. $storyOptions = $stories;
  51. if($taskStatus == 'wait' and $task->isParent == '0')
  52. {
  53. $modeOptions = $lang->task->editModeList;
  54. }
  55. else
  56. {
  57. $modeText = $task->mode == '' ? $lang->task->editModeList['single'] : zget($lang->task->editModeList, $task->mode);
  58. }
  59. $assignedToOptions = $taskMembers;
  60. $typeOptions = $lang->task->typeList;
  61. $statusOptions = (array)$lang->task->statusList;
  62. $priOptions = $lang->task->priList;
  63. $mailtoOptions = $users;
  64. $finishedByOptions = $members;
  65. $canceledByOptions = $users;
  66. $closedByOptions = $users;
  67. $closedReasonOptions = $lang->task->reasonList;
  68. $teamOptions = $members;
  69. $hiddenTeam = $task->mode != '' ? '' : 'hidden';
  70. $disabledEstStarted = $project && $project->taskDateLimit == 'limit' && !empty($parentTask) && helper::isZeroDate($parentTask->estStarted);
  71. $disabledDeadline = $project && $project->taskDateLimit == 'limit' && !empty($parentTask) && helper::isZeroDate($parentTask->deadline);
  72. /* 检查是否可以开始执行。*/
  73. if($this->config->edition == 'ipd')
  74. {
  75. $execution->canStartExecution = $this->loadModel('execution')->checkStageStatus($execution->id, 'start');
  76. $task->executionInfo = $execution;
  77. }
  78. if($taskStatus == 'wait') unset($statusOptions['pause']);
  79. if(!empty($task->team))
  80. {
  81. foreach($task->team as $member)
  82. {
  83. $member->team = $member->account;
  84. $member->teamSource = $member->account;
  85. $member->teamEstimate = (float)$member->estimate;
  86. $member->teamConsumed = (float)$member->consumed;
  87. $member->teamLeft = (float)$member->left;
  88. $member->memberDisabled = false;
  89. $member->memberStatus = $member->status;
  90. if($member->memberStatus == 'done') $member->memberDisabled = true;
  91. if(strpos('|closed|cancel|pause|', $taskStatus) !== false && $app->rawMethod != 'activate')
  92. {
  93. $member->memberStatus = $taskStatus;
  94. $member->memberDisabled = true;
  95. }
  96. $member->hourDisabled = $member->memberDisabled;
  97. }
  98. }
  99. /* ====== Define the page structure with zin widgets ====== */
  100. detailHeader
  101. (
  102. to::prefix(null),
  103. to::title
  104. (
  105. entityLabel
  106. (
  107. set::entityID($task->id),
  108. set::level(1),
  109. set::text($task->name),
  110. set::reverse(true)
  111. )
  112. )
  113. );
  114. $recordWorkhourDisabled = !empty($task->children) ? 'disabled' : '';
  115. if(!empty($task->team))
  116. {
  117. $assignedToComponent = picker(setID('assignedTo'), setClass('w-full'), set::name('assignedTo'), set::value($task->assignedTo), set::items($assignedToOptions), set::required(true), $task->mode == 'linear' && !in_array($taskStatus, array('done', 'closed')) ? set::disabled(true) : null, $taskStatus == 'closed' ? set::disabled(true) : null);
  118. }
  119. else
  120. {
  121. $assignedToComponent = taskAssignedTo
  122. (
  123. setID('assignedTo'),
  124. setClass('w-full'),
  125. set::name('assignedTo'),
  126. set::value($task->assignedTo),
  127. set::items($assignedToOptions),
  128. $taskStatus == 'closed' ? set::disabled(true) : null,
  129. $manageLink ? set::manageLink($manageLink) : null
  130. );
  131. }
  132. detailBody
  133. (
  134. set::isForm(true),
  135. set::formID("taskEditForm{$task->id}"),
  136. set::ajax(array('beforeSubmit' => jsRaw('clickSubmit'))),
  137. sectionList
  138. (
  139. section
  140. (
  141. set::title($lang->task->name),
  142. set::required(true),
  143. formGroup
  144. (
  145. inputControl
  146. (
  147. input(set::name('name'), set::value($task->name), set::placeholder($lang->task->name)),
  148. to::suffix
  149. (
  150. colorPicker
  151. (
  152. set::heading($lang->task->colorTag),
  153. set::name('color'),
  154. set::value($task->color),
  155. set::syncColor('#name')
  156. )
  157. ),
  158. set::suffixWidth('35')
  159. )
  160. )
  161. ),
  162. section
  163. (
  164. set::title($lang->task->desc),
  165. editor
  166. (
  167. set::name('desc'),
  168. $task->desc && isHTML($task->desc) ? html($task->desc) : $task->desc
  169. )
  170. ),
  171. $execution->lifetime != 'ops' ? section
  172. (
  173. set::title($lang->task->story),
  174. formGroup
  175. (
  176. set::width('1/2'),
  177. control
  178. (
  179. set::type('picker'),
  180. set::name('story'),
  181. set::value($task->story),
  182. set::items($storyOptions),
  183. on::change('setStoryModule')
  184. )
  185. )
  186. ) : null,
  187. section
  188. (
  189. setID('files'),
  190. set::title($lang->files),
  191. fileSelector($task->files ? set::defaultFiles(array_values($task->files)) : null)
  192. ),
  193. formHidden('lastEditedDate', helper::isZeroDate($task->lastEditedDate) ? '' : $task->lastEditedDate)
  194. ),
  195. history(),
  196. detailSide
  197. (
  198. set::isForm(true),
  199. tableData
  200. (
  201. setClass('mt-5'),
  202. set::title($lang->task->legendBasic),
  203. item
  204. (
  205. set::name($lang->task->execution),
  206. formGroup
  207. (
  208. picker
  209. (
  210. set::name('execution'),
  211. set::value($task->execution),
  212. set::items($executionOptions),
  213. on::change('loadAll')
  214. )
  215. )
  216. ),
  217. item
  218. (
  219. set::trClass('moduleTR'),
  220. set::name($lang->task->module),
  221. set::required(strpos(",{$this->config->task->edit->requiredFields},", ",module,") !== false),
  222. formGroup
  223. (
  224. inputGroup
  225. (
  226. div
  227. (
  228. setClass('flex grow'),
  229. picker
  230. (
  231. setClass('w-full'),
  232. set::name('module'),
  233. set::value($task->module),
  234. set::items($moduleOptions),
  235. set::width(2/3),
  236. set::required(true),
  237. on::change('loadStories')
  238. )
  239. ),
  240. div
  241. (
  242. setClass('flex'),
  243. checkbox(
  244. setID('showAllModule'),
  245. set::rootClass('items-center ml-3'),
  246. set::name('showAllModule'),
  247. set::text($lang->all),
  248. set::value(1),
  249. set::checked($showAllModule),
  250. on::change('loadAllModule')
  251. )
  252. )
  253. )
  254. )
  255. ),
  256. $task->parent >= 0 && $config->vision != 'lite' ? item
  257. (
  258. set::name($lang->task->parent),
  259. picker
  260. (
  261. set::name('parent'),
  262. set::value($task->parent),
  263. set::items($tasks),
  264. set::disabled(!empty($task->mode)),
  265. on::change('getParentEstStartedAndDeadline')
  266. )
  267. ) : formHidden('parent', $task->parent),
  268. empty($modeText) ? item
  269. (
  270. set::name($lang->task->mode),
  271. picker
  272. (
  273. set::name('mode'),
  274. set::value($task->mode),
  275. set::items($modeOptions),
  276. set::required(true),
  277. on::change('changeMode')
  278. )
  279. ) : item
  280. (
  281. set::name($lang->task->mode),
  282. $modeText,
  283. formHidden('mode', $task->mode)
  284. ),
  285. item
  286. (
  287. set::name($lang->task->assignedTo),
  288. set::required(strpos(",{$this->config->task->edit->requiredFields},", ",assignedTo,") !== false),
  289. inputGroup
  290. (
  291. div
  292. (
  293. setClass('flex grow'),
  294. $assignedToComponent
  295. ),
  296. div
  297. (
  298. btn
  299. (
  300. $lang->task->team,
  301. setClass('input-group-btn team-group', empty($task->team) && (!$task->mode) ? 'hidden' : ''),
  302. set::url('#modalTeam'),
  303. setData('toggle', 'modal'),
  304. $task->mode == 'multi' ? on::click('disableMembers') : null
  305. )
  306. )
  307. )
  308. ),
  309. item
  310. (
  311. set::name($lang->task->type),
  312. picker
  313. (
  314. set::name('type'),
  315. set::value($task->type),
  316. set::items($typeOptions),
  317. set::required(true)
  318. )
  319. ),
  320. empty($task->children) ? item
  321. (
  322. set::name($lang->task->status),
  323. picker
  324. (
  325. on::change()->do('statusChange(target)'),
  326. set::name('status'),
  327. set::value($taskStatus),
  328. set::items($statusOptions),
  329. set::required(true)
  330. )
  331. ) : formHidden('status', $taskStatus),
  332. item
  333. (
  334. set::name($lang->task->pri),
  335. set::required(strpos(",{$this->config->task->edit->requiredFields},", ",pri,") !== false),
  336. formGroup
  337. (
  338. priPicker(set::name('pri'), set::value($task->pri), set::items($priOptions))
  339. )
  340. ),
  341. item
  342. (
  343. set::name($lang->task->progress),
  344. progresscircle
  345. (
  346. set::percent($task->progress),
  347. set::circleColor('var(--color-success-500)'),
  348. set::circleBg('var(--color-border)'),
  349. set::circleWidth(1)
  350. )
  351. ),
  352. item
  353. (
  354. set::name($lang->task->keywords),
  355. input(set::name('keywords'), set::value($task->keywords))
  356. ),
  357. item
  358. (
  359. set::name($lang->task->mailto),
  360. mailto(set::items($mailtoOptions), set::value($task->mailto))
  361. )
  362. ),
  363. modalTrigger
  364. (
  365. modal
  366. (
  367. setID('modalTeam'),
  368. set::title($lang->task->teamMember),
  369. formBatch
  370. (
  371. set::tagName('div'),
  372. setID('teamTable'),
  373. set::mode('add'),
  374. !empty($task->team) ? set::data(array_values($task->team)) : null,
  375. set::sortable(true),
  376. set::size('sm'),
  377. set::minRows(3),
  378. set::onRenderRow(jsRaw('renderRowData')),
  379. formBatchItem
  380. (
  381. set::name('id'),
  382. set::width('32px'),
  383. set::control('index')
  384. ),
  385. formBatchItem
  386. (
  387. set::name('team'),
  388. set::label($lang->task->teamMember),
  389. set::width('160px'),
  390. set::control(array('control' => 'taskAssignedTo', 'manageLink' => ($manageLink ? $manageLink : ''))),
  391. set::items($members)
  392. ),
  393. formBatchItem
  394. (
  395. set::name('estimateBox'),
  396. set::label($lang->task->estimate),
  397. set::width('135px'),
  398. set::control('inputGroup'),
  399. set::required(true),
  400. inputControl
  401. (
  402. input(set::name("teamEstimate")),
  403. to::suffix($lang->task->suffixHour),
  404. set::suffixWidth(20)
  405. )
  406. ),
  407. !empty($task->team) ? formBatchItem
  408. (
  409. set::name('consumedBox'),
  410. set::label($lang->task->consumedAB),
  411. set::width('135px'),
  412. set::control('inputGroup'),
  413. inputControl
  414. (
  415. input(set::name("teamConsumed")),
  416. to::suffix($lang->task->suffixHour),
  417. set::suffixWidth(20)
  418. )
  419. ) : null,
  420. !empty($task->team) ? formBatchItem
  421. (
  422. set::name('leftBox'),
  423. set::label($lang->task->left),
  424. set::width('135px'),
  425. set::control('inputGroup'),
  426. set::required(true),
  427. inputControl
  428. (
  429. input(set::name("teamLeft")),
  430. to::suffix($lang->task->suffixHour),
  431. set::suffixWidth(20)
  432. )
  433. ) : null,
  434. formBatchItem
  435. (
  436. set::name('teamSource'),
  437. set::control('input'),
  438. set::hidden(true)
  439. ),
  440. set::actions(array(array('text' => $lang->save, 'class' => 'primary team-saveBtn'))),
  441. on::click('.team-saveBtn')->call('saveTeam')
  442. )
  443. )
  444. ),
  445. tableData
  446. (
  447. setClass('mt-4'),
  448. set::title($lang->task->legendEffort),
  449. item
  450. (
  451. set::name($lang->task->estStarted),
  452. set::required(strpos(",{$this->config->task->edit->requiredFields},", ",estStarted,") !== false),
  453. formGroup
  454. (
  455. datePicker
  456. (
  457. set::name('estStarted'),
  458. set::disabled($disabledEstStarted),
  459. on::change('checkEstStartedAndDeadline'),
  460. set::value((helper::isZeroDate($task->estStarted) || $disabledEstStarted) ? null : $task->estStarted)
  461. )
  462. )
  463. ),
  464. item
  465. (
  466. set::name($lang->task->deadline),
  467. set::required(strpos(",{$this->config->task->edit->requiredFields},", ",deadline,") !== false),
  468. formGroup
  469. (
  470. datePicker
  471. (
  472. set::name('deadline'),
  473. set::disabled($disabledDeadline),
  474. on::change('checkEstStartedAndDeadline'),
  475. set::value((helper::isZeroDate($task->deadline) || $disabledDeadline) ? null : $task->deadline)
  476. )
  477. )
  478. ),
  479. item
  480. (
  481. set::name($lang->task->estimate),
  482. set::required(strpos(",{$this->config->task->edit->requiredFields},", ",estimate,") !== false),
  483. formGroup
  484. (
  485. inputControl
  486. (
  487. input
  488. (
  489. set::name('estimate'),
  490. set::value(helper::formatHours($task->estimate)),
  491. !empty($task->team) || !empty($task->children) ? set::readonly(true) : null
  492. ),
  493. to::suffix($lang->task->suffixHour),
  494. set::suffixWidth(20)
  495. )
  496. )
  497. ),
  498. item
  499. (
  500. set::name($lang->task->consumed),
  501. inputGroup
  502. (
  503. setClass('items-center'),
  504. span
  505. (
  506. setClass('span-text'),
  507. setID('consumedSpan'),
  508. $task->consumed . $lang->task->suffixHour
  509. ),
  510. $this->task->isClickable($task, 'recordWorkhour') ? btn
  511. (
  512. setClass('ghost text-primary', $recordWorkhourDisabled),
  513. icon('time'),
  514. set::href(inlink('recordWorkhour', "id={$task->id}&from=edittask")),
  515. setData('toggle', 'modal')
  516. ) : null,
  517. formHidden('consumed', helper::formatHours($task->consumed))
  518. )
  519. ),
  520. item
  521. (
  522. set::name($lang->task->left),
  523. formGroup
  524. (
  525. inputControl
  526. (
  527. input
  528. (
  529. set::name('left'),
  530. set::value(helper::formatHours($task->left)),
  531. !empty($task->team) || !empty($task->children) ? set::readonly(true) : null
  532. ),
  533. to::suffix($lang->task->suffixHour),
  534. set::suffixWidth(20)
  535. )
  536. )
  537. )
  538. ),
  539. tableData
  540. (
  541. setClass('mt-4'),
  542. set::title($lang->task->legendLife),
  543. item
  544. (
  545. set::name($lang->task->realStarted),
  546. formGroup
  547. (
  548. datetimePicker
  549. (
  550. set::name('realStarted'),
  551. set::value(helper::isZeroDate($task->realStarted) ? '' : $task->realStarted)
  552. )
  553. )
  554. ),
  555. item
  556. (
  557. set::name($lang->task->finishedBy),
  558. formGroup
  559. (
  560. picker
  561. (
  562. set::name('finishedBy'),
  563. set::value($task->finishedBy),
  564. set::items($finishedByOptions)
  565. )
  566. )
  567. ),
  568. item
  569. (
  570. set::name($lang->task->finishedDate),
  571. formGroup
  572. (
  573. datetimePicker
  574. (
  575. set::name('finishedDate'),
  576. set::value(helper::isZeroDate($task->finishedDate) ? '' : $task->finishedDate)
  577. )
  578. )
  579. ),
  580. item
  581. (
  582. set::name($lang->task->canceledBy),
  583. formGroup
  584. (
  585. picker
  586. (
  587. set::name('canceledBy'),
  588. set::value($task->canceledBy),
  589. set::items($canceledByOptions)
  590. )
  591. )
  592. ),
  593. item
  594. (
  595. set::name($lang->task->canceledDate),
  596. formGroup
  597. (
  598. datetimePicker
  599. (
  600. set::name('canceledDate'),
  601. set::value(helper::isZeroDate($task->canceledDate) ? '' : $task->canceledDate)
  602. )
  603. )
  604. ),
  605. item
  606. (
  607. set::name($lang->task->closedBy),
  608. formGroup
  609. (
  610. picker
  611. (
  612. set::name('closedBy'),
  613. set::value($task->closedBy),
  614. set::items($closedByOptions)
  615. )
  616. )
  617. ),
  618. item
  619. (
  620. set::name($lang->task->closedReason),
  621. formGroup
  622. (
  623. picker
  624. (
  625. set::name('closedReason'),
  626. set::value($task->closedReason),
  627. set::items($closedReasonOptions)
  628. )
  629. )
  630. ),
  631. item
  632. (
  633. set::name($lang->task->closedDate),
  634. formGroup
  635. (
  636. datetimePicker
  637. (
  638. set::name('closedDate'),
  639. set::value(helper::isZeroDate($task->closedDate) ? '' : $task->closedDate)
  640. )
  641. )
  642. )
  643. )
  644. )
  645. );