view.html.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. /**
  3. * The view file of charter module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2024 禅道软件(青岛)有限公司(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 Yuting Wang <wangyuting@easycorp.ltd>
  8. * @package charter
  9. * @link https://www.zentao.net
  10. */
  11. namespace zin;
  12. jsVar('browseType', zget($_SESSION, 'browseType', 'all'));
  13. jsVar('vision', $config->vision);
  14. jsVar('userViewProjects', ",{$app->user->view->projects},");
  15. /* 初始化头部右上方工具栏。Init detail toolbar. */
  16. $toolbar = array();
  17. if(!isInModal() && hasPriv('charter', 'create'))
  18. {
  19. $toolbar[] = array
  20. (
  21. 'icon' => 'plus',
  22. 'type' => 'primary',
  23. 'text' => $lang->charter->create,
  24. 'url' => createLink('charter', 'create')
  25. );
  26. }
  27. /* 基本信息 */
  28. $basicInfoItems = array();
  29. $basicInfoItems[$lang->charter->level] = array('control' => 'priLabel', 'text' => zget($levelList, $charter->level), 'pri' => $charter->level);
  30. $basicInfoItems[$lang->charter->category] = array('control' => 'text', 'text' => zget($lang->charter->categoryList, $charter->category));
  31. $basicInfoItems[$lang->charter->market] = array('control' => 'text', 'text' => zget($lang->charter->marketList, $charter->market));
  32. $basicInfoItems[$lang->charter->budget] = array('control' => 'text', 'text' => $charter->budget);
  33. $basicInfoItems[$lang->charter->status] = array('control' => 'text', 'text' => zget($lang->charter->statusList, $charter->status));
  34. $basicInfoItems[$lang->charter->reviewStatus] = array('control' => 'text', 'text' => $charter->status == 'closed' || ($charter->status == 'wait' && $charter->reviewStatus == 'wait') || ($charter->status == 'canceled' && $charter->prevCanceledStatus == 'wait') ? '' : zget($lang->charter->reviewStatusList, $charter->reviewStatus));
  35. $basicInfo = datalist
  36. (
  37. set::className('charter-basic-info'),
  38. set::items($basicInfoItems)
  39. );
  40. $projectApproval = zget($charter->approvalList, 'projectApproval', array());
  41. $completionApproval = zget($charter->approvalList, 'completionApproval', array());
  42. $cancelProjectApproval = zget($charter->approvalList, 'cancelProjectApproval', array());
  43. $activateProjectApproval = zget($charter->approvalList, 'activateProjectApproval', array());
  44. function convertUserName($accounts, $users)
  45. {
  46. $userName = '';
  47. foreach(explode(',', $accounts) as $account)
  48. {
  49. if(empty($account)) continue;
  50. $userName .= zget($users, trim($account)) . ' ';
  51. }
  52. return $userName;
  53. }
  54. /* 立项的一生 */
  55. $lifeTimeItems = array();
  56. $lifeTimeItems[$lang->charter->createdBy] = array('control' => 'text', 'text' => $charter->createdBy ? zget($users, $charter->createdBy) . $lang->at . $charter->createdDate : '');
  57. $lifeTimeItems[$lang->charter->charteredBy] = array('control' => 'text', 'text' => !empty($projectApproval['appliedBy']) ? zget($users, $projectApproval['appliedBy']) . $lang->at . $projectApproval['appliedDate'] : '');
  58. $lifeTimeItems[$lang->charter->charterReviewers] = array('control' => 'text', 'text' => !empty($projectApproval['reviewers']) ? convertUserName($projectApproval['reviewers'], $users) : '');
  59. $lifeTimeItems[$lang->charter->completionBy] = array('control' => 'text', 'text' => !empty($completionApproval['appliedBy']) ? zget($users, $completionApproval['appliedBy']) . $lang->at . $completionApproval['appliedDate'] : '');
  60. $lifeTimeItems[$lang->charter->completionReviewers] = array('control' => 'text', 'text' => !empty($completionApproval['reviewers']) ? convertUserName($completionApproval['reviewers'], $users) : '');
  61. $lifeTimeItems[$lang->charter->canceledBy] = array('control' => 'text', 'text' => !empty($cancelProjectApproval['appliedBy']) ? zget($users, $cancelProjectApproval['appliedBy']) . $lang->at . $cancelProjectApproval['appliedDate'] : '');
  62. $lifeTimeItems[$lang->charter->canceledReviewers] = array('control' => 'text', 'text' => !empty($cancelProjectApproval['reviewers']) ? convertUserName($cancelProjectApproval['reviewers'], $users) : '');
  63. $lifeTimeItems[$lang->charter->activatedBy] = array('control' => 'text', 'text' => !empty($activateProjectApproval['appliedBy']) ? zget($users, $activateProjectApproval['appliedBy']) . $lang->at . $activateProjectApproval['appliedDate'] : '');
  64. $lifeTimeItems[$lang->charter->activatedReviewers] = array('control' => 'text', 'text' => !empty($activateProjectApproval['reviewers']) ? convertUserName($activateProjectApproval['reviewers'], $users) : '');
  65. $lifeTimeItems[$lang->charter->closedBy] = array('control' => 'text', 'text' => $charter->closedBy ? zget($users, $charter->closedBy) . $lang->at . $charter->closedDate : '');
  66. $lifeTime = datalist
  67. (
  68. set::className('charter-life-item'),
  69. set::items($lifeTimeItems)
  70. );
  71. $tabs = array();
  72. $tabs[] = setting()
  73. ->group('basic')
  74. ->title($lang->charter->legendBasicInfo)
  75. ->children(wg($basicInfo));
  76. $tabs[] = setting()
  77. ->group('life')
  78. ->title($lang->charter->legendLifeTime)
  79. ->children(wg($lifeTime));
  80. /* 获取文件信息。*/
  81. function getFileData($charter, $reviewType)
  82. {
  83. global $config;
  84. $fileData = array();
  85. $charterFiles = !empty($charter->filesConfig) ? json_decode($charter->filesConfig, true) : json_decode($config->custom->charterFiles, true);
  86. if(!empty($charterFiles[$charter->level]))
  87. {
  88. foreach($charterFiles[$charter->level][$reviewType] as $file)
  89. {
  90. $fileData[] = div(setClass('flex'), cell(set::width('100px'), setClass('p-1 text-left text-gray text-clip'), set::title($file['name']), $file['name']), cell(setClass('pl-1'), fileList(set::files($charter->files), set::extra($reviewType . '-' . $file['index']), set::object($charter), set::fieldset(false), set::showDelete(false))));
  91. }
  92. }
  93. return $fileData;
  94. }
  95. /* 产品和计划/路标 */
  96. $tableData = array();
  97. $canViewProduct = common::hasPriv('product', 'view');
  98. $this->loadModel('productplan');
  99. foreach($groupDate as $productID => $linkedList)
  100. {
  101. $hasPriv = $this->product->checkPriv($productID);
  102. $linkedData = array();
  103. $productInfo = zget($products, $productID);
  104. $isLaunched = $productInfo->vision == 'rnd' || strpos(",{$productInfo->vision},", ",{$config->vision},") !== false;
  105. foreach($linkedList as $data)
  106. {
  107. $module = $data->linkedType == 'plan' ? 'productplan' : $data->linkedType;
  108. $linkedURL = $this->createLink($module, 'view', "id=$data->id");
  109. $linkedName = $data->linkedName . " [{$data->begin} ~ {$data->end}]";;
  110. if($data->begin == $this->config->productplan->future && $data->end == $this->config->productplan->future) $linkedName = $data->linkedName . ' ' . $this->lang->productplan->future;
  111. if($productInfo->type != 'normal')
  112. {
  113. $branchName = '';
  114. foreach(explode(',', (string)$data->branch) as $dataBranch) $branchName .= !empty($branchGroups[$data->product]) && !empty($branchGroups[$data->product][$dataBranch]) ? $branchGroups[$data->product][$dataBranch] . ',' : '';
  115. $linkedName = $linkedName . ' / ' . trim($branchName, ',');
  116. }
  117. $class = 'clip';
  118. if(count($linkedList) <= 2) $class .= ' flex flex-1 w-0 items-center';
  119. if(count($linkedList) > 2) $class .= ' flex-none w-1/3';
  120. if(count($linkedData) > 2) $class .= ' mt-2';
  121. if(count($linkedData) % 3 != 0) $class .= ' pl-6';
  122. $linkedData[] = div
  123. (
  124. setClass($class),
  125. icon('productplan mr-2 '),
  126. hasPriv($module, 'view') ? a
  127. (
  128. set::title($linkedName),
  129. set::href($linkedURL),
  130. span($linkedName)
  131. )
  132. : span($linkedName)
  133. );
  134. }
  135. $productItems = array();
  136. $productLink = $this->createLink('product', 'view', "productID=$productID");
  137. if(isset($branchGroups[$productID]))
  138. {
  139. foreach($branchGroups[$productID] as $branchID => $branchName)
  140. {
  141. $branchName = $productInfo->name . '/' . $branchName;
  142. $productItems[] = $canViewProduct && $isLaunched && $hasPriv ? div(setClass('flex clip w-full items-center'), icon('product mr-2'), a(setClass('flex'), set::href($productLink), set::title($branchName), span(setClass('flex-1'), setStyle('width', '0'), $branchName))) : div($branchName);
  143. }
  144. }
  145. else
  146. {
  147. $productItems[] = $canViewProduct && $isLaunched && $hasPriv ? div(setClass('flex clip w-full items-center'), icon('product mr-2'), a(setClass('flex'), set::href($productLink), set::title($productInfo->name), span(setClass('flex-1'), setStyle('width', '0'), $productInfo->name))) : div($productInfo->name);
  148. }
  149. $tableData[] = h::tr(setClass('text-center'), h::td(html($productItems)), h::td(div(setClass('flex flex-wrap'), $linkedData)));
  150. }
  151. /* 立项信息 */
  152. $charterInfo = div
  153. (
  154. div
  155. (
  156. setClass('text-base font-semibold py-1'),
  157. $lang->charter->spec
  158. ),
  159. div
  160. (
  161. setClass('py-1'),
  162. html($charter->spec)
  163. ),
  164. div
  165. (
  166. setClass('text-base font-semibold py-1'),
  167. $lang->charter->charterFiles
  168. ),
  169. div
  170. (
  171. setClass('py-1'),
  172. div(getFileData($charter, 'projectApproval'))
  173. ),
  174. div
  175. (
  176. setClass('text-base font-semibold py-1'),
  177. $charter->type == 'plan' ? $lang->charter->productAndPlan : $lang->charter->productRoadmap
  178. ),
  179. !empty($groupDate) ? h::table
  180. (
  181. setClass('table bordered'),
  182. h::tr(h::th(setClass('w-64'), $lang->charter->product), h::th($charter->type == 'plan' ? $lang->charter->plan : $lang->charter->roadmap)),
  183. $tableData
  184. ) : null
  185. );
  186. $sections = array();
  187. $sections[] = setting()
  188. ->title($lang->charter->charterInfo)
  189. ->children(wg($charterInfo));
  190. /* 项目集和项目 */
  191. $programAndProjectDetail = null;
  192. foreach($programList as $program)
  193. {
  194. $programBudget = $this->loadModel('project')->getBudgetWithUnit($program->budget);
  195. $program->budget = !empty($program->budget) ? zget($lang->project->currencySymbol, $program->budgetUnit) . ' ' . $programBudget : $lang->project->future;
  196. }
  197. if(!empty($programList))
  198. {
  199. $programAndProjectDetail = div
  200. (
  201. setClass('detail-sections canvas shadow rounded px-6 py-4'),
  202. div
  203. (
  204. setClass('detail-section'),
  205. div
  206. (
  207. setClass('detail-section-title row items-center gap-2'),
  208. span
  209. (
  210. setClass('text-md py-1 font-bold'),
  211. $lang->charter->programAndProject
  212. )
  213. ),
  214. div
  215. (
  216. dtable
  217. (
  218. set::onRenderCell(jsRaw('window.onRenderCell')),
  219. set::cols($config->charter->programList->fieldList),
  220. set::data(array_values($programList)),
  221. set::checkable(false),
  222. set::sortType(false),
  223. set::userMap(array(0 => '') + $users)
  224. )
  225. )
  226. )
  227. );
  228. }
  229. function printBlockInfo($blockTitle, $blockContent)
  230. {
  231. $blockInfo = div
  232. (
  233. setClass('detail-sections canvas shadow rounded px-6 py-4'),
  234. div
  235. (
  236. setClass('detail-section'),
  237. div
  238. (
  239. setClass('detail-section-title row items-center gap-2'),
  240. span
  241. (
  242. setClass('text-md py-1 font-bold'),
  243. $blockTitle
  244. )
  245. ),
  246. div
  247. (
  248. setClass('detail-section-content py-1'),
  249. div
  250. (
  251. setClass('text-base font-semibold py-1'),
  252. $blockContent['desc']['title']
  253. ),
  254. div
  255. (
  256. setClass('py-1'),
  257. html($blockContent['desc']['content'])
  258. ),
  259. div
  260. (
  261. setClass('text-base font-semibold py-1'),
  262. $blockContent['file']['title']
  263. ),
  264. div
  265. (
  266. setClass('py-1'),
  267. div($blockContent['file']['content'])
  268. )
  269. )
  270. )
  271. );
  272. return $blockInfo;
  273. }
  274. /* 操作栏 */
  275. if(!empty($charter->approval)) $config->charter->actions->view['mainActions'][] = 'approvalProgress';
  276. if($config->vision == 'rnd' && $charter->status == 'launched' && !in_array($charter->reviewStatus, array('completionDoing', 'cancelDoing'))) $config->charter->actions->view['mainActions'][] = 'createProgramAndProject';
  277. if($this->charter->isClickable($charter, 'projectapproval'))
  278. {
  279. $config->charter->actionList['cancelProjectApproval']['hint'] = $lang->charter->abbr->cancel;
  280. $config->charter->actionList['cancelProjectApproval']['data-toggle'] = 'modal';
  281. }
  282. if($charter->prevCanceledStatus == 'wait')
  283. {
  284. $config->charter->actionList['activateProjectApproval']['hint'] = $lang->charter->abbr->activate;
  285. }
  286. $operateList = $this->loadModel('common')->buildOperateMenu($charter);
  287. $actions = empty($operateList['suffixActions']) ? $operateList['mainActions'] : array_merge($operateList['mainActions'], array(array('type' => 'divider')), $operateList['suffixActions']);
  288. detail
  289. (
  290. set::urlFormatter(array('{id}' => $charter->id, '{from}' => 'view', '{approval}' => $charter->approval)),
  291. set::toolbar($toolbar),
  292. set::sections($sections),
  293. $programAndProjectDetail,
  294. set::tabs($tabs),
  295. set::actions(array_values($actions)),
  296. in_array($charter->reviewStatus, array('completionDoing', 'completionReject', 'completionPass')) ? printBlockInfo($lang->charter->completionInfo, array('desc' => array('title' => $lang->charter->completionDesc, 'content' => zget($charter, 'completionApprovalDesc', '')), 'file' => array('title' => $lang->charter->completionFiles, 'content' => getFileData($charter, 'completeApproval')))) : null,
  297. in_array($charter->reviewStatus, array('cancelDoing', 'cancelReject', 'cancelPass', 'activateDoing', 'activateReject')) ? printBlockInfo($lang->charter->canceledInfo, array('desc' => array('title' => $lang->charter->canceledDesc, 'content' => zget($charter, 'cancelProjectApprovalDesc', '')), 'file' => array('title' => $lang->charter->canceledFiles, 'content' => getFileData($charter, 'cancelApproval')))) : null
  298. );