view.html.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php
  2. /**
  3. * The UI file of product module 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 chen.tao <chentao@easycorp.ltd>
  8. * @package product
  9. * @link https://www.zentao.net
  10. */
  11. namespace zin;
  12. include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php');
  13. /* Flag variable for hiding product code. */
  14. $hiddenCode = (!isset($config->setCode) || $config->setCode == 0 || empty($product->code));
  15. $allStoryCount = array_sum($product->stories);
  16. $isEn = $app->getClientLang() == 'en';
  17. $storiesClass = $isEn ? 'px-2' : 'w-1/3';
  18. $membersDom = array();
  19. foreach($config->product->memberFields as $field)
  20. {
  21. if(empty($product->$field)) continue;
  22. if(!isset($members[$product->$field])) continue;
  23. $user = $members[$product->$field];
  24. $membersDom[] = div
  25. (
  26. setClass('w-1/6 center-y'),
  27. avatar
  28. (
  29. setClass('primary-outline'),
  30. set::size('36'),
  31. set::text($user->realname),
  32. set::src($user->avatar)
  33. ),
  34. span(setClass('my-2'), $user->realname),
  35. span(setClass('text-gray'), $lang->product->$field)
  36. );
  37. }
  38. div
  39. (
  40. setClass('flex w-full'),
  41. cell
  42. (
  43. setClass('mr-3 w-2/3'),
  44. div
  45. (
  46. setClass('flex-auto canvas flex p-4'),
  47. div
  48. (
  49. setClass('text-center w-1/3 flex flex-col justify-center items-center'),
  50. div
  51. (
  52. set('class', 'chart pie-chart'),
  53. echarts
  54. (
  55. set::color(array('#2B80FF', '#E3E4E9')),
  56. set::width(120),
  57. set::height(120),
  58. set::series
  59. (
  60. array(array('type' => 'pie', 'radius' => array('80%', '90%'), 'itemStyle' => array('borderRadius' => '40'), 'label' => array('show' => false), 'data' => array($product->storyDeliveryRate, 100 - $product->storyDeliveryRate)))
  61. )
  62. ),
  63. div
  64. (
  65. set::className('pie-chart-title text-center'),
  66. div(span(set::className('text-2xl font-bold'), $product->storyDeliveryRate . '%')),
  67. div
  68. (
  69. span
  70. (
  71. setClass('text-sm text-gray'),
  72. $lang->product->storyDeliveryRate,
  73. icon
  74. (
  75. 'help ml-1',
  76. toggle::tooltip(array('title' => $lang->product->storyDeliveryRateTip)),
  77. set('data-placement', 'right'),
  78. set('data-type', 'white'),
  79. set('data-class-name', 'text-gray border border-light'),
  80. setClass('text-gray ')
  81. )
  82. )
  83. )
  84. )
  85. ),
  86. div
  87. (
  88. setClass('border flex justify-center items-center pl-4 py-2' . ($isEn ? '' : ' w-3/4')),
  89. div
  90. (
  91. setClass($storiesClass),
  92. div
  93. (
  94. setClass('text-lg font-bold'),
  95. $allStoryCount
  96. ),
  97. $lang->product->totalStories
  98. ),
  99. div
  100. (
  101. setClass($storiesClass),
  102. div
  103. (
  104. setClass('text-lg font-bold'),
  105. $product->stories['closed']
  106. ),
  107. $lang->story->statusList['closed']
  108. ),
  109. div
  110. (
  111. setClass($storiesClass),
  112. div
  113. (
  114. setClass('text-lg font-bold'),
  115. $allStoryCount - $product->stories['closed']
  116. ),
  117. $lang->story->unclosed
  118. )
  119. )
  120. ),
  121. div
  122. (
  123. setClass('flex-none w-2/3'),
  124. div
  125. (
  126. setClass('flex items-center'),
  127. label
  128. (
  129. setClass('rounded-full'),
  130. $product->id
  131. ),
  132. div
  133. (
  134. setClass('text-md font-bold ml-2 clip'),
  135. $product->name
  136. ),
  137. !$hiddenCode ? label
  138. (
  139. setClass('gray-300-outline ml-2 flex-none'),
  140. $product->code
  141. ) : null,
  142. $product->type != 'normal' ? label
  143. (
  144. setClass('gray-300-outline ml-2 text-warning flex-none'),
  145. $lang->product->typeList[$product->type]
  146. ) : null,
  147. $product->deleted ? label
  148. (
  149. setClass('danger-outline text-dange flex-noner ml-2'),
  150. $lang->product->deleted
  151. ) : null,
  152. label
  153. (
  154. setClass("ml-2 flex-none"),
  155. setClass($product->status == 'normal' ? 'text-success' : 'text-gray'),
  156. $this->processStatus('product', $product)
  157. ),
  158. div
  159. (
  160. setClass('ml-2 flex-none row items-center gap-2'),
  161. $lang->product->abbr->aclList[$product->acl],
  162. icon
  163. (
  164. 'help',
  165. toggle::tooltip(array('title' => $lang->product->aclList[$product->acl])),
  166. set('data-placement', 'right'),
  167. set('data-type', 'white'),
  168. set('data-class-name', 'text-gray border border-light'),
  169. setClass('text-gray')
  170. )
  171. ),
  172. div(setClass('ai-menu-box flex-auto'))
  173. ),
  174. div
  175. (
  176. setClass('flex mt-4'),
  177. in_array($this->config->systemMode, array('ALM', 'PLM')) && helper::hasFeature('program') && $product->program ? div
  178. (
  179. setClass('clip w-1/3'),
  180. set::title($lang->product->program),
  181. icon('program', setClass('pr-1')),
  182. $product->programName
  183. ) : null,
  184. $product->line ? div
  185. (
  186. setClass('clip w-1/3'),
  187. set::title($lang->product->line),
  188. icon('lane', setClass('pr-1')),
  189. $product->lineName
  190. ) : null,
  191. $config->edition != 'open' && $product->workflowGroup ? div
  192. (
  193. setClass('clip w-1/3'),
  194. set::title($lang->product->workflowGroup),
  195. icon('flow', setClass('pr-1')),
  196. zget($workflowGroups, $product->workflowGroup)
  197. ) : null
  198. ),
  199. div
  200. (
  201. set::className('detail-content mt-4 overflow-hidden desc-box'),
  202. set::title(strip_tags($product->desc)),
  203. html($product->desc)
  204. )
  205. )
  206. ),
  207. div
  208. (
  209. setClass('mt-4 p-4 bg-white'),
  210. panel
  211. (
  212. setClass('mb-4 memberBox'),
  213. set::title($lang->product->manager),
  214. div(setClass('flex flex-wrap member-list pt-2'), $membersDom)
  215. ),
  216. panel
  217. (
  218. setClass('otherInfoBox'),
  219. set::title($lang->product->otherInfo),
  220. div
  221. (
  222. setClass('flex flex-wrap'),
  223. div
  224. (
  225. setClass('w-1/4 item mb-3'),
  226. span(setClass('text-gray'), $lang->product->plans),
  227. span(setClass('ml-2'), $product->plans)
  228. ),
  229. div
  230. (
  231. setClass('w-1/4 item mb-3'),
  232. span(setClass('text-gray'), $lang->product->releases),
  233. span(setClass('ml-2'), $product->releases)
  234. ),
  235. div
  236. (
  237. setClass('w-1/4 item mb-3'),
  238. span(setClass('text-gray'), $lang->product->bugs),
  239. span(setClass('ml-2'), $product->bugs)
  240. ),
  241. div
  242. (
  243. setClass('w-1/4 item mb-3'),
  244. span(setClass('text-gray'), $lang->product->projects),
  245. span(setClass('ml-2'), $product->projects)
  246. ),
  247. div
  248. (
  249. setClass('w-1/4 item mb-3'),
  250. span(setClass('text-gray'), $lang->product->builds),
  251. span(setClass('ml-2'), $product->builds)
  252. ),
  253. div
  254. (
  255. setClass('w-1/4 item mb-3'),
  256. span(setClass('text-gray'), $lang->product->docs),
  257. span(setClass('ml-2'), $product->docs)
  258. ),
  259. div
  260. (
  261. setClass('w-1/4 item mb-3'),
  262. span(setClass('text-gray'), $lang->product->cases),
  263. span(setClass('ml-2'), $product->cases)
  264. ),
  265. div
  266. (
  267. setClass('w-1/4 item mb-3'),
  268. span(setClass('text-gray'), $lang->product->executions),
  269. span(setClass('ml-2'), $product->executions)
  270. )
  271. )
  272. ),
  273. html($this->printExtendFields($product, 'html', "position=info", false))
  274. )
  275. ),
  276. cell
  277. (
  278. setClass('w-1/3'),
  279. panel
  280. (
  281. to::heading
  282. (
  283. div(set('class', 'panel-title'), $lang->execution->latestDynamic)
  284. ),
  285. to::headingActions
  286. (
  287. common::hasPriv('product', 'dynamic') ? btn
  288. (
  289. setClass('ghost text-gray font-normal'),
  290. set::url(createLink('product', 'dynamic', "productID={$product->id}&type=all")),
  291. $lang->more
  292. ) : null
  293. ),
  294. set::bodyClass('h-80 overflow-y-auto pt-0'),
  295. set::shadow(false),
  296. dynamic()
  297. ),
  298. html($this->printExtendFields($product, 'html', "position=basic", false)),
  299. div
  300. (
  301. setClass('mt-4'),
  302. history
  303. (
  304. set::commentUrl(createLink('action', 'comment', array('objectType' => 'product', 'objectID' => $product->id))),
  305. set::bodyClass('maxh-72 overflow-y-auto')
  306. )
  307. )
  308. )
  309. );
  310. $actionMenuList = !$product->deleted ? $this->loadModel('common')->buildOperateMenu($product) : array();
  311. div
  312. (
  313. setClass('w-2/3 center fixed actions-menu'),
  314. setClass($product->deleted ? 'no-divider' : ''),
  315. floatToolbar
  316. (
  317. isAjaxRequest('modal') ? null : to::prefix(backBtn(set::icon('back'), $lang->goback)),
  318. !empty($actionMenuList['mainActions']) ? set::main($actionMenuList['mainActions']) : null,
  319. !empty($actionMenuList['suffixActions']) ? set::suffix($actionMenuList['suffixActions']) : null,
  320. set::object($product)
  321. )
  322. );