productstatisticblock.html.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. /**
  3. * The product statistic block view file of block module of ZenTaoPMS.
  4. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  5. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  6. * @author Yuting Wang <wangyuting@easycorp.ltd>
  7. * @package block
  8. * @link https://www.zentao.net
  9. */
  10. namespace zin;
  11. $app->control->loadModel('execution');
  12. $app->control->loadModel('project');
  13. $active = isset($params['active']) ? $params['active'] : key($products); // 当前产品 ID。
  14. $product = null; // 当前产品。 Current active product.
  15. $items = array(); // 产品导航列表。 Product nav list.
  16. foreach($products as $productItem)
  17. {
  18. $projectID = isset($params['projectID']) ? $params['projectID'] : 0;
  19. $params = helper::safe64Encode("module={$block->module}&projectID={$projectID}&active={$productItem->id}");
  20. $isShadow = $productItem->shadow;
  21. $url = createLink('product', 'browse', "productID=$productItem->id");
  22. if($isShadow)
  23. {
  24. $project = $this->project->getByShadowProduct($productItem->id);
  25. $executionID = $project ? $this->execution->getNoMultipleID($project->id) : 0;
  26. $url = $executionID ? createLink('execution', 'story', "executionID=$executionID") : createLink('projectstory', 'story', "projectID=$project->id");
  27. }
  28. $items[] = array
  29. (
  30. 'id' => $productItem->id,
  31. 'text' => $productItem->name,
  32. 'url' => $url,
  33. 'data-app' => $isShadow && !empty($project) ? 'project' : 'product',
  34. 'activeUrl' => createLink('block', 'printBlock', "blockID=$block->id&params=$params")
  35. );
  36. if($productItem->id == $active) $product = $productItem;
  37. }
  38. $doneData = array();
  39. $openedData = array();
  40. if($product)
  41. {
  42. foreach($product->monthFinish as $date => $count)
  43. {
  44. if($date == date('Y-m'))
  45. {
  46. $product->monthFinish[$lang->datepicker->dpText->TEXT_THIS_MONTH] = $count;
  47. unset($product->monthFinish[$date]);
  48. }
  49. $doneData[] = $count;
  50. }
  51. foreach($product->monthCreated as $date => $count)
  52. {
  53. if($date == date('Y-m'))
  54. {
  55. $product->monthCreated[$lang->datepicker->dpText->TEXT_THIS_MONTH] = $count;
  56. unset($product->monthCreated[$date]);
  57. }
  58. $openedData[] = $count;
  59. }
  60. }
  61. $monthFinish = !empty($product) ? $product->monthFinish : array();
  62. $monthCreated = !empty($product) ? $product->monthCreated : array();
  63. statisticBlock
  64. (
  65. to::titleSuffix
  66. (
  67. icon
  68. (
  69. setClass('text-light text-sm cursor-pointer'),
  70. toggle::tooltip
  71. (
  72. array
  73. (
  74. 'title' => sprintf($lang->block->tooltips['metricTime'], $metricTime),
  75. 'placement' => $app->getClientLang() == 'en' ? 'bottom-end' : 'bottom',
  76. 'type' => 'white',
  77. 'className' => 'text-dark border border-light leading-5'
  78. )
  79. ),
  80. 'help'
  81. )
  82. ),
  83. set::block($block),
  84. set::active($active),
  85. set::moreLink(createLink('product', 'all', 'browseType=' . $block->params->type)),
  86. set::items($items),
  87. $product ? div
  88. (
  89. setClass($longBlock ? 'row' : 'col gap-3 pl-3 pt-1', 'h-full overflow-hidden items-stretch p-2'),
  90. div
  91. (
  92. setClass('flex-1 gap-4'),
  93. div
  94. (
  95. setClass('row items-center gap-1 font-bold', $longBlock ? 'py-2' : ''),
  96. $lang->block->productstatistic->deliveryRate,
  97. icon
  98. (
  99. setClass('text-light text-sm'),
  100. toggle::tooltip
  101. (
  102. array
  103. (
  104. 'title' => $lang->block->tooltips['deliveryRate'],
  105. 'placement' => 'bottom',
  106. 'type' => 'white',
  107. 'className' => 'text-dark border border-light leading-5'
  108. )
  109. ),
  110. 'help'
  111. )
  112. ),
  113. progressCircle
  114. (
  115. set::percent($product->storyDeliveryRate),
  116. set::size(112),
  117. set::text(false),
  118. set::circleWidth(0.06),
  119. div(span(setClass('text-2xl font-bold'), $product->storyDeliveryRate), '%')
  120. ),
  121. row
  122. (
  123. setClass('justify-center items-center gap-4 mt-4'),
  124. center
  125. (
  126. div
  127. (
  128. common::hasPriv('product', 'browse') && $product->totalStories ? a
  129. (
  130. set('href', helper::createLink('product', 'browse', "productID={$product->id}&branch=all&browseType=allStory&param=0&storyType=story")),
  131. $product->totalStories
  132. ) : span($product->totalStories)
  133. ),
  134. div
  135. (
  136. span
  137. (
  138. setClass('text-sm text-gray'),
  139. $lang->block->productstatistic->effectiveStory,
  140. toggle::tooltip
  141. (
  142. array
  143. (
  144. 'title' => $lang->block->tooltips['effectiveStory'],
  145. 'placement' => 'bottom',
  146. 'type' => 'white',
  147. 'className' => 'text-dark border border-light leading-5'
  148. )
  149. )
  150. )
  151. )
  152. ),
  153. center
  154. (
  155. div
  156. (
  157. common::hasPriv('product', 'browse') && $product->closedStories ? a
  158. (
  159. set('href', helper::createLink('product', 'browse', "productID={$product->id}&branch=all&browseType=closedstory&param=0&storyType=story")),
  160. $product->closedStories
  161. ) : span($product->closedStories)
  162. ),
  163. div
  164. (
  165. span
  166. (
  167. setClass('text-sm text-gray'),
  168. $lang->block->productstatistic->delivered,
  169. toggle::tooltip
  170. (
  171. array
  172. (
  173. 'title' => $lang->block->tooltips['deliveredStory'],
  174. 'placement' => 'bottom',
  175. 'type' => 'white',
  176. 'className' => 'text-dark border border-light leading-5'
  177. )
  178. )
  179. )
  180. )
  181. ),
  182. center
  183. (
  184. div
  185. (
  186. common::hasPriv('product', 'browse') && $product->unclosedStories ? a
  187. (
  188. set('href', helper::createLink('product', 'browse', "productID={$product->id}&branch=all&browseType=unclosed&param=0&storyType=story")),
  189. $product->unclosedStories
  190. ) : span($product->unclosedStories)
  191. ),
  192. div
  193. (
  194. span
  195. (
  196. setClass('text-sm text-gray'),
  197. $lang->block->productstatistic->unclosed
  198. )
  199. )
  200. )
  201. )
  202. ),
  203. col
  204. (
  205. setClass('flex-1 gap-1.5 pr-3 ', $longBlock ? 'py-2' : 'pl-3'),
  206. div(setClass('font-bold'), $lang->block->productstatistic->storyStatistics),
  207. row
  208. (
  209. setClass('text-sm text-gray gap-2'),
  210. html(sprintf($lang->block->productstatistic->monthDone, !empty($monthFinish[$lang->datepicker->dpText->TEXT_THIS_MONTH]) ? $monthFinish[$lang->datepicker->dpText->TEXT_THIS_MONTH] : 0)),
  211. divider(),
  212. html(sprintf($lang->block->productstatistic->monthOpened, !empty($monthCreated[$lang->datepicker->dpText->TEXT_THIS_MONTH]) ? $monthCreated[$lang->datepicker->dpText->TEXT_THIS_MONTH] : 0))
  213. ),
  214. echarts
  215. (
  216. set::color(array('#2B80FF', '#17CE97')),
  217. set::width('100%'),
  218. set::height(170),
  219. set::grid(array('left' => '10px', 'top' => '30px', 'right' => '0', 'bottom' => '0', 'containLabel' => true)),
  220. set::legend(array('show' => true, 'right' => '0')),
  221. set::xAxis(array('type' => 'category', 'data' => array_keys($monthFinish), 'splitLine' => array('show' => false), 'axisTick' => array('alignWithLabel' => true, 'interval' => '0'), 'axisLabel' => array('rotate' => 45))),
  222. set::yAxis(array('type' => 'value', 'name' => $lang->number, 'splitLine' => array('show' => false), 'axisLine' => array('show' => true, 'color' => '#DDD'))),
  223. set::series
  224. (
  225. array
  226. (
  227. array
  228. (
  229. 'type' => 'line',
  230. 'name' => $lang->block->productstatistic->opened,
  231. 'data' => $openedData,
  232. 'emphasis' => array('label' => array('show' => true))
  233. ),
  234. array
  235. (
  236. 'type' => 'line',
  237. 'name' => $lang->block->productstatistic->done,
  238. 'data' => $doneData,
  239. 'emphasis' => array('label' => array('show' => true))
  240. )
  241. )
  242. )
  243. )
  244. ),
  245. ($product->newPlan || $product->newExecution || $product->newRelease) ? col
  246. (
  247. setClass('flex-1 gap-3 pr-3 ', $longBlock ? 'border-l pl-4 py-2' : 'pl-3 pt-2'),
  248. div(setClass('font-bold'), $lang->block->productstatistic->news),
  249. $product->newPlan ? div
  250. (
  251. setClass($longBlock ? 'col' : 'row', 'gap-2'),
  252. div(setClass('text-sm'), $lang->block->productstatistic->newPlan),
  253. row
  254. (
  255. setClass('gap-2'),
  256. hasPriv('productplan', 'view') ? a
  257. (
  258. set('href', helper::createLink('productplan', 'view', "planID={$product->newPlan->id}")),
  259. $product->newPlan->title
  260. ) : $product->newPlan->title,
  261. label
  262. (
  263. setClass('gray-pale rounded-full px-1 nowrap'),
  264. zget($lang->productplan->statusList, $product->newPlan->status)
  265. )
  266. )
  267. ) : null,
  268. $product->newExecution ? div
  269. (
  270. setClass($longBlock ? 'col' : 'row', 'gap-2'),
  271. div(setClass('text-sm'), $lang->block->productstatistic->newExecution),
  272. row
  273. (
  274. setClass('gap-2'),
  275. hasPriv('execution', 'task') ? a
  276. (
  277. set('href', helper::createLink('execution', 'task', "executionID={$product->newExecution->id}")),
  278. $product->newExecution->name
  279. ) : $product->newExecution->name,
  280. label
  281. (
  282. setClass('important-pale rounded-full nowrap'),
  283. zget($lang->execution->statusList, $product->newExecution->status)
  284. )
  285. )
  286. ) : null,
  287. $product->newRelease ? div
  288. (
  289. setClass($longBlock ? 'col' : 'row', 'gap-2'),
  290. div(setClass('text-sm'), $lang->block->productstatistic->newRelease),
  291. row
  292. (
  293. setClass('gap-2'),
  294. hasPriv('release', 'view') ? a
  295. (
  296. set('href', helper::createLink('release', 'view', "releaseID={$product->newRelease->id}")),
  297. $product->newRelease->name
  298. ) : $product->newRelease->name,
  299. label
  300. (
  301. setClass('rounded-full nowrap', ($product->newRelease->status == 'normal' ? 'success-pale' : 'gray-pale')),
  302. zget($lang->release->statusList, $product->newRelease->status)
  303. )
  304. )
  305. ) : null
  306. ) : null
  307. ) : null
  308. );