v1.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. /**
  3. * The statistic block widget class file of zin 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 Gang Liu <liugang@easycorp.ltd>
  8. * @package zin
  9. * @link https://www.zentao.net
  10. */
  11. namespace zin;
  12. require_once dirname(__DIR__) . DS . 'blockpanel' . DS . 'v1.php';
  13. /**
  14. * 统计类区块(statisticBlock)部件类
  15. * The statisticBlock widget class
  16. */
  17. class statisticBlock extends blockPanel
  18. {
  19. /**
  20. * Define widget properties.
  21. *
  22. * @var array
  23. * @access protected
  24. */
  25. protected static $defineProps = array
  26. (
  27. 'items: array', // 列表项目,格式为:{id: string, text: string, url: string, activeUrl: string}[]
  28. 'active?: string' // 当前激活的项目 ID。
  29. );
  30. /**
  31. * Get page CSS code.
  32. *
  33. * @return string|false
  34. * @access protected
  35. */
  36. public static function getPageCSS()
  37. {
  38. return <<<'CSS'
  39. .block-statistic-nav {overflow-y: overlay; --nav-active-bg: var(--color-primary-50); --nav-active-color: var(--color-fore)}
  40. .block-statistic-nav-item .text {opacity: .8;}
  41. .block-statistic-nav-item.active .text, .block-statistic-nav-item:hover .text {opacity: 1;}
  42. .is-long .block-statistic-nav-item {width: auto!important; height: 36px!important;}
  43. .is-long .block-statistic-nav-item:hover {padding-right: 32px;}
  44. .is-long .block-statistic-nav-url {position: absolute!important; padding: 0!important; width: 32px!important; justify-content: center!important; height: 36px!important;}
  45. .is-long .block-statistic-nav-url:hover {background-color: var(--color-canvas);}
  46. .is-short .block-statistic-nav .nav {justify-content: center;}
  47. .is-short .block-statistic-nav .nav-item {gap: 0;}
  48. .is-short .block-statistic-nav .nav-item.active {gap: 0;}
  49. .is-short .block-statistic-nav .nav-item:not(.active) {display: none;}
  50. .is-short .block-statistic-nav .nav-item > a {gap: 0; padding: 0px 0.25rem;}
  51. .is-short .block-statistic-nav .nav-item .block-statistic-nav-item {display: none;}
  52. .block-statistic-nav-btn {opacity: 1;}
  53. CSS;
  54. return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
  55. }
  56. /**
  57. * Build navigator.
  58. *
  59. * @param string $id
  60. * @param array $items
  61. * @param string $active
  62. * @param bool $longBlock
  63. * @return node|null
  64. */
  65. protected function buildNav($id, $items, $active, $longBlock)
  66. {
  67. if(empty($items)) return null;
  68. $navItems = array();
  69. $hasPrev = true;
  70. $hasNext = true;
  71. foreach($items as $index => $item)
  72. {
  73. if($item['id'] == $active)
  74. {
  75. if($index == 0) $hasPrev = false;
  76. if($index + 1 == count($items)) $hasNext = false;
  77. }
  78. $navItems[] = li
  79. (
  80. setClass('nav-item item group' . ($item['id'] == $active ? ' active' : '')),
  81. a
  82. (
  83. toggle::tab(array('target' => "#blockTab_{$id}_{$item['id']}")),
  84. setClass('block-statistic-nav-item flex-auto min-w-0', $item['id'] == $active ? 'active' : ''),
  85. span(setClass('text clip'), set('title', $item['text']), $item['text'])
  86. ),
  87. !$longBlock ? span(setClass('block-statistic-nav-title text text-primary font-bold clip'), set('title', $item['text']), $item['text']) : null,
  88. !empty($item['url']) ? a
  89. (
  90. $longBlock ? setClass('block-statistic-nav-url top-0 right-0 opacity-0 group-hover:opacity-100 transition-opacity') : null,
  91. set::href($item['url']),
  92. !empty($item['data-app']) ? setData('app', $item['data-app']) : null,
  93. icon('import rotate-270 primary-pale rounded-full w-5 h-5 center')
  94. ) : null
  95. );
  96. }
  97. return div
  98. (
  99. setClass('flex-none block-statistic-nav border-r', $longBlock ? 'w-52' : 'relative w-full'),
  100. nav
  101. (
  102. setClass('scrollbar-thin scrollbar-hover pr-0.5', $longBlock ? 'p-2 overflow-y-auto overflow-x-hidden h-full' : 'm-2 bg-primary-50 px-4 overflow-x-auto overflow-y-hidden'),
  103. set::stacked($longBlock),
  104. $navItems
  105. ),
  106. $longBlock ? null : array
  107. (
  108. btn(span(setClass('chevron-left scale-75')), setClass('block-statistic-nav-btn size-sm square w-6 transition-opacity ghost text-primary absolute top-3 left-4'), setData('type', 'prev'), set::disabled(!$hasPrev)),
  109. btn(span(setClass('chevron-right scale-75')), setClass('block-statistic-nav-btn size-sm square w-6 transition-opacity ghost text-primary absolute top-3 right-4'), setData('type', 'next'), set::disabled(!$hasNext)),
  110. bind::click('.block-statistic-nav-btn', implode('', array
  111. (
  112. 'const disabled = "disabled";',
  113. 'const type = $target.data("type");',
  114. 'const $nextItem = $element.find(".nav-item>.active").parent()[type]();',
  115. 'if(!$nextItem.length) return $target.addClass(disabled);',
  116. '$element.find(".nav-item>.active").parent().removeClass("active");',
  117. '$element.find(".nav-item>.active").removeClass("active").removeClass("scroll-into-view");',
  118. '$nextItem.scrollIntoView({block: "nearest", inline: "center", behavior: "smooth", ifNeeded: false}).find("a")[0].click();',
  119. '$nextItem.addClass("active").addClass("scroll-into-view");',
  120. '$element.find(".block-statistic-nav-btn[data-type=\'prev\']").toggleClass(disabled, !$nextItem.prev().length);',
  121. '$element.find(".block-statistic-nav-btn[data-type=\'next\']").toggleClass(disabled, !$nextItem.next().length);'
  122. )))
  123. )
  124. );
  125. }
  126. /**
  127. * Build tabs panes.
  128. *
  129. * @param string $id
  130. * @param array $items
  131. * @param string $active
  132. * @param bool $longBlock
  133. * @return node|null
  134. */
  135. protected function buildPanes($id, $items, $active, $longBlock)
  136. {
  137. if(empty($items))
  138. {
  139. global $lang;
  140. return center
  141. (
  142. setClass('text-gray flex-auto'),
  143. $lang->noData
  144. );
  145. }
  146. $panes = array();
  147. foreach($items as $item)
  148. {
  149. $isActive = $item['id'] == $active;
  150. $panes[] = div
  151. (
  152. isset($item['activeUrl']) ? setData('active', $item['activeUrl']) : null,
  153. setData('name', $item['id']),
  154. setID("blockTab_{$id}_{$item['id']}"),
  155. setClass('tab-pane h-full', $isActive ? 'active' : 'need-load'),
  156. $isActive ? $this->children() : null
  157. );
  158. }
  159. return div
  160. (
  161. setClass('flex-auto block-statistic-panes overflow-clip'),
  162. $panes,
  163. on::show('.tab-pane.need-load', <<<'JS'
  164. const $target = $(target);
  165. if(!$target.hasClass('tab-pane')) return;
  166. const blockID = $target.closest(".dashboard-block").attr("data-id");
  167. const url = $target.data("active");
  168. loadPartial(url, `#${target.id}>*`, {id: "blockTab_' . $id . '", success: () => $target.removeClass("need-load")});
  169. $("#dashboard").dashboard("update", {id: blockID, fetch: url, needLoad: false});
  170. JS
  171. )
  172. );
  173. }
  174. /**
  175. * Build panel body.
  176. *
  177. * @return node
  178. */
  179. protected function buildBody()
  180. {
  181. list($id, $title, $block, $longBlock, $items, $active, $bodyProps) = $this->prop(array('id', 'title', 'block', 'longBlock', 'items', 'active', 'bodyProps'));
  182. if($longBlock === null) $longBlock = data('longBlock');
  183. return div
  184. (
  185. setClass('panel-body p-0 block-statistic', $longBlock ? 'row' : 'col', $this->prop('bodyClass')),
  186. set($bodyProps),
  187. $this->buildNav($id, $items, $active, $longBlock),
  188. $this->buildPanes($id, $items, $active, $longBlock)
  189. );
  190. }
  191. }