executionstatisticblock.html.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <?php
  2. /**
  3. * The execution 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. $active = isset($params['active']) ? $params['active'] : key($executions);
  12. $isEn = $app->getClientLang() == 'en';
  13. $execution = new stdclass();
  14. $execution->progress = '';
  15. $execution->doneStory = '';
  16. $execution->totalStory = '';
  17. $execution->totalTask = '';
  18. $execution->undoneTask = '';
  19. $execution->yesterdayDoneTask = '';
  20. $execution->type = '';
  21. $items = array();
  22. foreach($executions as $executionItem)
  23. {
  24. $params = helper::safe64Encode("module={$block->module}&project={$currentProjectID}&active={$executionItem->id}");
  25. $items[] = array
  26. (
  27. 'id' => $executionItem->id,
  28. 'text' => $executionItem->name,
  29. 'url' => createLink('execution', 'task', "executionID={$executionItem->id}"),
  30. 'activeUrl' => createLink('block', 'printBlock', "blockID={$block->id}&params={$params}")
  31. );
  32. if($executionItem->id == $active) $execution = $executionItem;
  33. }
  34. $projectItems = array();
  35. $projectItems[] = array('value' => '0', 'text' => $lang->block->executionstatistic->allProject, 'data-url' => createLink('block', 'printBlock', "blockID={$block->id}&params=" . helper::safe64Encode("module={$block->module}")), 'data-on' => 'click', 'data-do' => "loadBlock('$block->id', options.url)");
  36. foreach($projects as $projectID => $projectName)
  37. {
  38. $url = createLink('block', 'printBlock', "blockID={$block->id}&params=" . helper::safe64Encode("module={$block->module}&project={$projectID}"));
  39. $projectItems[] = array('value' => $projectID, 'text' => $projectName, 'data-url' => $url, 'data-on' => 'click', 'data-do' => "loadBlock('$block->id', options.url)");
  40. }
  41. if($execution->type == 'kanban')
  42. {
  43. $index = 0;
  44. $chartColors = array('#33B4DB', '#7ECF69', '#FFC73A', '#FF5A61', '#50C8D0', '#AF5AFF', '#4EA3FF', '#FF8C5A', '#6C73FF');
  45. $chartSeries = array();
  46. if(!empty($chartData['line']))
  47. {
  48. foreach($chartData['line'] as $label => $set)
  49. {
  50. $chartSeries[] = array(
  51. 'name' => $label,
  52. 'type' => 'line',
  53. 'stack' => 'Total',
  54. 'data' => array_values($set),
  55. 'color' => $chartColors[$index],
  56. 'areaStyle' => array('color' => $chartColors[$index], 'opacity' => 0.2),
  57. 'itemStyle' => array('normal' => array('lineStyle' => array('width' => 1))),
  58. 'emphasis' => array('focus' => 'series')
  59. );
  60. $index ++;
  61. }
  62. }
  63. /* 任务计流图图。CFD chart. */
  64. $chart = div
  65. (
  66. div(setClass('font-bold mb-2'), $lang->block->executionstatistic->cfd),
  67. echarts
  68. (
  69. set::series($chartSeries),
  70. set::width('100%'),
  71. set::height(200),
  72. set::tooltip(array(
  73. 'trigger' => 'axis',
  74. 'axisPointer' => array('type' => 'cross', 'label' => array('backgroundColor' => '#6a7985')),
  75. 'textStyle' => array('fontWeight' => 100),
  76. 'formatter' => "RAWJS<function(rowDatas){return window.randTipInfo(rowDatas);}>RAWJS"
  77. )),
  78. set::legend(array(
  79. 'data' => !empty($chartData['line']) ? array_keys(array_reverse($chartData['line'])) : null
  80. )),
  81. set::grid(array(
  82. 'top' => !empty($chartData['labels']) ? '80px' : '40px',
  83. 'left' => !empty($chartData['labels']) ? '2px' : '24px',
  84. 'right' => '2px',
  85. 'bottom' => '2px',
  86. 'containLabel' => true
  87. )),
  88. set::xAxis(array(array(
  89. 'type' => 'category',
  90. 'boundaryGap' => false,
  91. 'data' => !empty($chartData['labels']) ? $chartData['labels'] : null,
  92. 'name' => $lang->execution->burnXUnit,
  93. 'axisLine' => array('show' => true, 'lineStyle' =>array('color' => '#999', 'width' =>1))
  94. ))),
  95. set::yAxis(array(array(
  96. 'type' => 'value',
  97. 'name' => $lang->execution->count,
  98. 'minInterval' => 1,
  99. 'nameTextStyle' => array('fontWeight' => 'normal'),
  100. 'axisPointer' => array('label' => array('show' => true, 'precision' => 0)),
  101. 'axisLine' => array('show' => true, 'lineStyle' => array('color' => '#999', 'width' => 1))
  102. )))
  103. )
  104. );
  105. $taskStoryInfo = null;
  106. }
  107. else
  108. {
  109. /* 燃尽图。Burn chart. */
  110. $chart = div
  111. (
  112. div(setClass('font-bold mb-2'), $lang->block->executionstatistic->burn),
  113. echarts
  114. (
  115. set::color(array('#2B80FF', '#D2D6E5')),
  116. set::width('100%'),
  117. set::height(140),
  118. set::grid(array('left' => '2px', 'top' => '30px', 'right' => '10px', 'bottom' => '0', 'containLabel' => true)),
  119. set::legend(array('show' => true, 'right' => '0')),
  120. set::xAxis(array('type' => 'category', 'data' => $chartData['labels'], 'boundaryGap' => false, 'splitLine' => array('show' => false), 'axisTick' => array('alignWithLabel' => true, 'interval' => '0'), 'axisLabel' => array('rotate' => 45))),
  121. set::yAxis(array('type' => 'value', 'name' => 'h', 'minInterval' => zget($chartData['baseLine'], 0), 'splitLine' => array('show' => false), 'axisLine' => array('show' => true, 'color' => '#DDD'))),
  122. set::series
  123. (
  124. array
  125. (
  126. array
  127. (
  128. 'type' => 'line',
  129. 'name' => $lang->execution->charts->burn->graph->actuality,
  130. 'data' => $chartData['burnLine'],
  131. 'emphasis' => array('label' => array('show' => true)),
  132. 'symbolSize' => 8,
  133. 'symbol' => 'circle'
  134. ),
  135. array
  136. (
  137. 'type' => 'line',
  138. 'name' => $lang->execution->charts->burn->graph->reference,
  139. 'data' => $chartData['baseLine'],
  140. 'emphasis' => array('label' => array('show' => true)),
  141. 'symbolSize' => 8,
  142. 'symbol' => 'circle'
  143. ),
  144. array
  145. (
  146. 'data' => empty($chartData['delayLine']) ? array() : $chartData['delayLine'],
  147. 'type' => 'line',
  148. 'name' => $lang->execution->charts->burn->graph->delay,
  149. 'symbolSize' => 8,
  150. 'symbol' => 'circle',
  151. 'itemStyle' => array
  152. (
  153. 'normal' => array
  154. (
  155. 'color' => '#F00',
  156. 'lineStyle' => array
  157. (
  158. 'color' => '#F00'
  159. )
  160. ),
  161. 'emphasis' => array
  162. (
  163. 'color' => '#fff',
  164. 'borderColor' => '#F00',
  165. 'borderWidth' => 2
  166. )
  167. ),
  168. )
  169. )
  170. )
  171. )
  172. );
  173. /* 任务故事信息。Task story info. */
  174. $taskStoryInfo = col
  175. (
  176. setClass('gap-2'),
  177. row
  178. (
  179. div(setClass('w-12 flex-none'), strong($lang->block->executionstatistic->task)),
  180. row
  181. (
  182. setClass('flex-auto'),
  183. cell
  184. (
  185. setClass('w-1/3'),
  186. span(setClass('text-sm text-gray'), $lang->block->executionstatistic->totalTask),
  187. strong(setClass('num ml-2'), $execution->totalTask)
  188. ),
  189. cell
  190. (
  191. setClass('w-1/3'),
  192. span(setClass('text-sm text-gray'), $lang->block->executionstatistic->undoneTask),
  193. strong(setClass('num ml-2'), $execution->undoneTask)
  194. ),
  195. $isEn ? null : cell
  196. (
  197. setClass('w-1/3'),
  198. span(setClass('text-sm text-gray'), $lang->block->executionstatistic->yesterdayDoneTask),
  199. strong(setClass('num ml-2'), $execution->yesterdayDoneTask)
  200. )
  201. )
  202. ),
  203. row
  204. (
  205. div(setClass('w-12 flex-none'), strong($lang->block->executionstatistic->story)),
  206. row
  207. (
  208. setClass('flex-auto'),
  209. cell
  210. (
  211. setClass('w-1/3'),
  212. span(setClass('text-sm text-gray'), $lang->block->executionstatistic->totalStory),
  213. strong(setClass('num ml-2'), $execution->totalStory)
  214. ),
  215. cell
  216. (
  217. setClass('w-1/3'),
  218. span(setClass('text-sm text-gray'), $lang->block->executionstatistic->doneStory),
  219. strong(setClass('num ml-2'), $execution->doneStory)
  220. )
  221. )
  222. )
  223. );
  224. }
  225. /* 进度环。Progress circle. */
  226. $progressCircle = div
  227. (
  228. setClass('w-full'),
  229. row
  230. (
  231. setClass('font-bold items-center gap-1 mb-2'),
  232. $lang->block->executionstatistic->progress,
  233. icon
  234. (
  235. 'help',
  236. setClass('opacity-50 text-sm cursor-pointer'),
  237. toggle::popover(array
  238. (
  239. 'content' => array('html' => $lang->block->tooltips['executionProgress']),
  240. 'placement' => 'bottom',
  241. 'width' => 400,
  242. 'trigger' => 'hover',
  243. 'closeBtn' => false,
  244. 'className' => 'leading-5'
  245. ))
  246. )
  247. ),
  248. div
  249. (
  250. setClass('w-full center'),
  251. progressCircle
  252. (
  253. setClass('relative w-28 h-28 hide-before-init opacity-0 transition-opacity'),
  254. set::percent($execution->progress),
  255. set::size(112),
  256. set::text(false),
  257. set::circleWidth(0.06),
  258. div(span(setClass('text-2xl font-bold'), $execution->progress), '%')
  259. )
  260. )
  261. );
  262. /* 工时信息。 Hours info. */
  263. $hoursInfo = row
  264. (
  265. setClass('justify-evenly w-full py-1'),
  266. cell
  267. (
  268. setClass('flex-1 text-center'),
  269. div
  270. (
  271. span(setClass('text-lg num font-bold'), !empty($execution->totalEstimate) ? $execution->totalEstimate : 0),
  272. span(setClass('text-gray'), ' h')
  273. ),
  274. div
  275. (
  276. setClass('text-sm text-gray'),
  277. $lang->block->executionstatistic->totalEstimate
  278. )
  279. ),
  280. cell
  281. (
  282. setClass('flex-1 text-center'),
  283. div
  284. (
  285. span(setClass('text-lg num font-bold'), !empty($execution->totalConsumed) ? $execution->totalConsumed : 0),
  286. span(setClass('text-gray'), ' h')
  287. ),
  288. div
  289. (
  290. setClass('text-sm text-gray'),
  291. $lang->block->executionstatistic->totalConsumed
  292. )
  293. ),
  294. cell
  295. (
  296. setClass('flex-1 text-center'),
  297. div
  298. (
  299. span(setClass('text-lg num font-bold'), !empty($execution->totalLeft) ? $execution->totalLeft : 0),
  300. span(setClass('text-gray'), ' h')
  301. ),
  302. div
  303. (
  304. setClass('text-sm text-gray'),
  305. $lang->block->executionstatistic->totalLeft
  306. )
  307. )
  308. );
  309. $blockView = null;
  310. if($longBlock)
  311. {
  312. $blockView = row
  313. (
  314. setClass('gap-4 h-full items-center px-4'),
  315. col
  316. (
  317. setClass('gap-6 w-2/5 flex-none items-center pr-2'),
  318. $progressCircle,
  319. $hoursInfo
  320. ),
  321. col
  322. (
  323. setClass('gap-0 flex-auto'),
  324. $chart,
  325. $taskStoryInfo
  326. )
  327. );
  328. }
  329. else
  330. {
  331. $blockView = col
  332. (
  333. setClass('gap-8 px-6 pt-10'),
  334. row
  335. (
  336. setClass('gap-2 items-center w-full'),
  337. cell(setClass('gap-2 w-2/5 flex-none items-center'), $progressCircle),
  338. cell
  339. (
  340. setClass('flex-auto min-w-0'),
  341. $chart
  342. )
  343. ),
  344. $hoursInfo,
  345. $taskStoryInfo
  346. );
  347. }
  348. statisticBlock(to::titleSuffix
  349. (
  350. icon
  351. (
  352. setClass('text-light text-sm cursor-pointer'),
  353. toggle::tooltip
  354. (
  355. array
  356. (
  357. 'title' => sprintf($lang->block->tooltips['metricTime'], $metricTime),
  358. 'placement' => $app->getClientLang() == 'en' ? 'bottom-end' : 'bottom',
  359. 'type' => 'white',
  360. 'className' => 'text-dark border border-light leading-5'
  361. )
  362. ),
  363. 'help'
  364. ),
  365. picker
  366. (
  367. setClass('font-normal gray-400-outline ml-3 text-base circle filter-project-pricker'),
  368. set::width($isEn ? '220px' : '120px'),
  369. set::placeholder($lang->block->filterProject),
  370. set::name('project'),
  371. set::items($projectItems),
  372. set::value(isset($projects[$currentProjectID]) ? $currentProjectID : 0)
  373. )
  374. ), set::block($block), set::active($active), set::moreLink(hasPriv('execution', 'all') && $config->vision != 'lite' ? createLink('execution', 'all', 'status=' . zget($block->params, 'type', '')) : ''), set::items($items), $blockView);