v1.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <?php
  2. namespace zin;
  3. class productsBox extends wg
  4. {
  5. /**
  6. * Define widget properties.
  7. *
  8. * @var array
  9. * @access protected
  10. */
  11. protected static $defineProps = array(
  12. 'productItems?: array', // 产品列表。
  13. 'branchGroups?: array', // 产品分支分组列表。
  14. 'planGroups?: array', // 产品计划分组列表。
  15. 'roadmapGroups?: array=array()', // 产品路标分组列表。
  16. 'linkedProducts?: array', // 关联的产品。
  17. 'linkedBranches?: array', // 关联的分支。
  18. 'currentProduct?: int=0', // 来源产品ID。
  19. 'currentPlan?: array|int=0', // 来源计划。
  20. 'currentRoadmap?: int=0', // 来源路标。
  21. 'productPlans?: array=array()', // 同来源计划所属产品的计划列表。
  22. 'project?: object', // 关联的项目。
  23. 'isStage?: bool', // 是否是阶段类型。
  24. 'hasNewProduct?: bool=false', // 是否有新产品。
  25. 'errorSameProducts?: string', // 选择同一个产品的提示。
  26. 'required?: bool=false', // 是否是必填。
  27. 'from?: string=project', // 来源类型。
  28. 'type?: string="plan"', // 类型。 plan|roadmap
  29. 'selectTip?: string=""', // 产品下拉提示。
  30. 'hidden?: bool=false', // 是否隐藏
  31. 'charterID?: int=0' // 立项ID
  32. );
  33. public static function getPageCSS()
  34. {
  35. return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
  36. }
  37. public static function getPageJS()
  38. {
  39. return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
  40. }
  41. protected function build()
  42. {
  43. list($project, $productItems, $linkedProducts, $errorSameProducts, $type, $hidden) = $this->prop(array('project', 'productItems', 'linkedProducts', 'errorSameProducts', 'type', 'hidden'));
  44. $productsBox = array();
  45. if((!empty($project->hasProduct) || is_null($project) || $type == 'roadmap') && $linkedProducts)
  46. {
  47. $productsBox = $this->buildLinkedProducts($linkedProducts);
  48. }
  49. elseif(!empty($project) && empty($project->hasProduct) && !in_array($project->model, array('waterfall', 'kanban', 'waterfallplus')))
  50. {
  51. $productsBox = $this->buildOnlyLinkPlans($linkedProducts);
  52. }
  53. else
  54. {
  55. $productsBox = $this->initProductsBox();
  56. }
  57. return div
  58. (
  59. setClass('productsBox', $hidden ? ' hidden' : ''),
  60. setData('linkType', $type),
  61. jsVar('multiBranchProducts', data('multiBranchProducts')),
  62. jsVar('project', \zget($project, 'id', 0)),
  63. jsVar('errorSameProducts', $errorSameProducts),
  64. $productsBox
  65. );
  66. }
  67. protected function initProductsBox()
  68. {
  69. global $lang, $app;
  70. list($productItems, $project, $isStage, $hasNewProduct, $type) = $this->prop(array('productItems', 'project', 'isStage', 'hasNewProduct', 'type'));
  71. $typeLang = $type == 'plan' ? $lang->project->associatePlan : $lang->project->manageRoadmap;
  72. $typeClass = $type == 'plan' ? 'planBox' : 'roadmapBox';
  73. $typeIdAttr = $type == 'plan' ? 'plan0' : 'roadmap0';
  74. $typeNameAttr = $type == 'plan' ? 'plans[0][]' : 'roadmaps[0][]';
  75. $productsBox = array();
  76. $hidden = !empty($project) && empty($project->hasProduct) ? 'hidden' : '';
  77. $productsBox[] = $hasNewProduct ? div
  78. (
  79. setClass('addProductBox flex hidden'),
  80. formGroup
  81. (
  82. on::change()->call('toggleNewProduct'),
  83. set::width('1/2'),
  84. set::checkbox(array('text' => $lang->project->addProduct, 'name' => 'newProduct', 'checked' => false)),
  85. set::required(true),
  86. set::label($lang->project->manageProducts),
  87. set::name('productName')
  88. )
  89. ) : null;
  90. $productsBox[] = div
  91. (
  92. set::className("productBox flex items-center $hidden"),
  93. formGroup(on::change()->call('toggleNewProduct'), set::width('1/2'), setClass('linkProduct'), set::required($this->prop('required') || ($project && in_array($project->model, array('waterfall', 'waterfallplus')))), set::label($lang->project->manageProducts), set::labelFor('productBox'), $hasNewProduct ? set::checkbox(array('text' => $lang->project->addProduct, 'name' => 'addProduct', 'checked' => false)) : false, picker
  94. (
  95. bind::change('loadBranches(event)'),
  96. set::name('products[0]'),
  97. set::items($productItems),
  98. !empty($project) && empty($project->hasProduct) ? set::value(current(array_keys($productItems))) : null,
  99. set::placeholder($this->prop('selectTip'))
  100. )),
  101. formGroup
  102. (
  103. set::width('1/4'),
  104. setClass('hidden linkBranch ml-px'),
  105. set::label(''),
  106. inputGroup
  107. (
  108. setClass('branchBox'),
  109. picker
  110. (
  111. set::name('branch[0][]'),
  112. set::items(array()),
  113. set::multiple(true),
  114. on::change("branchChange")
  115. )
  116. )
  117. ),
  118. formGroup(set::width('1/2'), set::label($typeLang), set::className($typeClass), inputGroup
  119. (
  120. set::id($typeIdAttr),
  121. picker
  122. (
  123. set::name($typeNameAttr),
  124. set::items(array()),
  125. set::multiple(true)
  126. )
  127. )),
  128. ($isStage && isset($project->stageBy) && $project->stageBy == 'product') ? null : div
  129. (
  130. setClass('pl-2 flex self-center line-btn c-actions first-action'),
  131. btn
  132. (
  133. bind::click('addNewLine(event)'),
  134. setClass('btn btn-link text-gray addLine'),
  135. icon('plus')
  136. ),
  137. btn
  138. (
  139. bind::click('removeLine(event)'),
  140. setClass('btn btn-link text-gray removeLine'),
  141. setClass('hidden'),
  142. icon('trash')
  143. )
  144. )
  145. );
  146. return $productsBox;
  147. }
  148. /**
  149. * @param mixed[] $productItems
  150. */
  151. protected function buildOnlyLinkPlans($productItems)
  152. {
  153. global $lang;
  154. list($currentPlan, $productPlans, $from) = $this->prop(array('currentPlan', 'productPlans', 'from'));
  155. $planProductID = current(array_keys($productItems));
  156. $productsBox = array();
  157. $productsBox[] = $from == 'execution' ? div
  158. (
  159. set::className('productBox noProductBox'),
  160. formGroup
  161. (
  162. set::label($lang->execution->linkPlan),
  163. set::className('planBox'),
  164. picker
  165. (
  166. set::name("plans[{$planProductID}][]"),
  167. set::items(!empty($productPlans) ? $productPlans : array()),
  168. set::value($currentPlan),
  169. set::multiple(true),
  170. formHidden("products[{$planProductID}]", $planProductID),
  171. formHidden("branch[{$planProductID}][0]", 0)
  172. )
  173. ),
  174. formHidden("products[{$planProductID}]", $planProductID),
  175. formHidden("branch[{$planProductID}][0]", 0)
  176. ) : div
  177. (
  178. set::className('productBox'),
  179. formHidden("products[{$planProductID}]", $planProductID),
  180. formHidden("branch[{$planProductID}][0]", 0)
  181. );
  182. return $productsBox;
  183. }
  184. /**
  185. * @param mixed[] $linkedProducts
  186. */
  187. protected function buildLinkedProducts($linkedProducts)
  188. {
  189. if(empty($linkedProducts)) return array();
  190. global $lang;
  191. list($productItems, $branchGroups, $planGroups, $productPlans, $type, $roadmapGroups, $charterID) = $this->prop(array('productItems', 'branchGroups', 'planGroups', 'productPlans', 'type', 'roadmapGroups', 'charterID'));
  192. list($linkedBranches, $currentProduct, $currentPlan, $project, $isStage) = $this->prop(array('linkedBranches', 'currentProduct', 'currentPlan', 'project', 'isStage'));
  193. $unmodifiableProducts = data('unmodifiableProducts') ? data('unmodifiableProducts') : array();
  194. $typeLang = $type == 'plan' ? $lang->project->associatePlan : $lang->project->manageRoadmap;
  195. $typeClass = $type == 'plan' ? 'planBox' : 'roadmapBox';
  196. $linkedProductsBox = array();
  197. $hiddenPlusBtn = count($productItems) == count(array_keys($linkedProducts)) ? ' hidden' : '';
  198. foreach(array_values($linkedProducts) as $i => $product)
  199. {
  200. $hasBranch = $product->type != 'normal' && isset($branchGroups[$product->id]);
  201. $branches = isset($branchGroups[$product->id]) ? $branchGroups[$product->id] : array();
  202. $disabledProduct = !empty($project) && (in_array($product->id, $unmodifiableProducts) || $isStage || (data('disableModel') && $charterID));
  203. $branchIdList = '';
  204. if(isset($product->branches)) $branchIdList = $product->branches;
  205. if(!empty($linkedBranches[$product->id])) $branchIdList = is_array($linkedBranches) ? array_keys($linkedBranches[$product->id]) : $linkedBranches[$product->id];
  206. $objectID = '';
  207. if(empty($currentProduct) || ($currentProduct != $product->id))
  208. {
  209. $objects = array();
  210. $objectGroups = $type == 'plan' ? $planGroups : $roadmapGroups;
  211. if(is_array($branchIdList) && !empty($branchIdList) && isset($objectGroups[$product->id]))
  212. {
  213. foreach($branchIdList as $branchID)
  214. {
  215. if(isset($objectGroups[$product->id][$branchID])) $objects += $objectGroups[$product->id][$branchID];
  216. }
  217. }
  218. if(empty($product->branches) && empty($objects) && !empty($objectGroups[$product->id])) $objects += $objectGroups[$product->id];
  219. if($type == 'plan')
  220. {
  221. $objectID = isset($product->plans) && is_array($product->plans) ? implode(',', $product->plans) : '';
  222. }
  223. elseif($type == 'roadmap')
  224. {
  225. $objectID = isset($product->roadmaps) && is_array($product->roadmaps) ? implode(',', $product->roadmaps) : '';
  226. }
  227. $objectID = trim($objectID, ',');
  228. }
  229. else
  230. {
  231. $plans = !empty($productPlans) ? $productPlans : array();
  232. $planID = isset($currentPlan) && isset($productPlans[$currentPlan]) ? $currentPlan : '';
  233. $roadmaps = array();
  234. $roadmapID = '';
  235. $objects = $type == 'plan' ? $plans : $roadmaps;
  236. $objectID = $type == 'plan' ? $planID : $roadmapID;
  237. }
  238. if($objectID && empty($objects)) $objectID = '';
  239. $linkedProductsBox[] = div
  240. (
  241. set::className('productBox flex'),
  242. formGroup
  243. (
  244. set::width($hasBranch ? '1/4' : '1/2'),
  245. setClass('linkProduct'),
  246. set::required($this->prop('required') || ($project && in_array($project->model, array('waterfall', 'waterfallplus')))),
  247. $i == 0 ? set::label($lang->project->manageProducts) : null,
  248. inputGroup
  249. (
  250. div
  251. (
  252. setClass('grow w-full'),
  253. picker
  254. (
  255. setData(array('on' => 'change', 'call' => 'loadBranches', 'params' => 'event')),
  256. set::name("products[$i]"),
  257. set::value($product->id),
  258. set::items($productItems),
  259. set::last($product->id),
  260. set::disabled($disabledProduct),
  261. $i === 0 && $type != 'roadmap' ? set::placeholder($this->prop('selectTip')) : null,
  262. $hasBranch ? set::lastBranch(empty($product->branches) ? 0 : implode(',', $product->branches)) : null,
  263. $disabledProduct ? formHidden("products[$i]", $product->id) : null
  264. )
  265. )
  266. )
  267. ),
  268. formGroup
  269. (
  270. set::width('1/4'),
  271. setClass('ml-px linkBranch'),
  272. count($linkedProductsBox) == 0 ? set::label('') : null,
  273. $hasBranch ? null : setClass('hidden'),
  274. inputGroup
  275. (
  276. setClass('branchBox'),
  277. picker
  278. (
  279. set::name("branch[$i][]"),
  280. set::items($branches),
  281. set::value(is_array($branchIdList) ? implode(',', $branchIdList) : $branchIdList),
  282. set::disabled($disabledProduct),
  283. set::multiple(true),
  284. setData(array('on' => 'change', 'call' => 'branchChange', 'params' => 'event'))
  285. )
  286. )
  287. ),
  288. $disabledProduct ? div
  289. (
  290. setClass('hidden branchBoxHidden'),
  291. picker
  292. (
  293. set::name("branch[$i][]"),
  294. set::items($branches),
  295. set::multiple(true),
  296. set::value(is_array($branchIdList) ? implode(',', $branchIdList) : $branchIdList)
  297. )
  298. ) : null,
  299. formGroup
  300. (
  301. set::width('1/2'),
  302. $i == 0 ? set::label($typeLang) : null,
  303. $type == 'roadmap' ? set::checkbox(array('text' => $lang->project->linkStoryToProject, 'name' => 'isLinkStory', 'checked' => true)) : null,
  304. set::className($typeClass),
  305. inputGroup
  306. (
  307. set::id($type == 'plan' ? "plan{$i}" : "roadmap{$i}"),
  308. picker
  309. (
  310. set::name($type == 'plan' ? "plans[$product->id][]" : "roadmaps[$product->id][]"),
  311. set::items($objects),
  312. set::value($objectID),
  313. set::multiple(true),
  314. set::disabled($disabledProduct && $charterID)
  315. )
  316. )
  317. ),
  318. $disabledProduct && $charterID ? div
  319. (
  320. setClass('hidden'),
  321. picker
  322. (
  323. set::name($type == 'plan' ? "plans[$product->id][]" : "roadmaps[$product->id][]"),
  324. set::items($objects),
  325. set::value($objectID),
  326. set::multiple(true)
  327. )
  328. ) : null,
  329. (!empty($project) && $isStage) ? null : div
  330. (
  331. setClass('pl-2 flex self-center line-btn c-actions', $i == 0 ? 'first-action' : ''),
  332. btn
  333. (
  334. bind::click('addNewLine(event)'),
  335. setClass("btn btn-link text-gray addLine $hiddenPlusBtn"),
  336. icon('plus')
  337. ),
  338. btn
  339. (
  340. bind::click('removeLine(event)'),
  341. setClass('btn btn-link text-gray removeLine'),
  342. setClass(($i == 0 || $disabledProduct || in_array($product->id, $unmodifiableProducts)) ? 'hidden' : ''),
  343. icon('trash')
  344. )
  345. )
  346. );
  347. }
  348. return $linkedProductsBox;
  349. }
  350. }