v1.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. namespace zin;
  3. require_once dirname(__DIR__) . DS . 'content' . DS . 'v1.php';
  4. require_once dirname(__DIR__) . DS . 'panel' . DS . 'v1.php';
  5. require_once dirname(__DIR__) . DS . 'entitytitle' . DS . 'v1.php';
  6. class detailCard extends wg
  7. {
  8. /**
  9. * @var mixed[]
  10. */
  11. protected static $defineProps = array
  12. (
  13. /* 对象类型,例如 `story`、`task` 等,如果不指定则已当前的模块名称作为对象类型。 */
  14. 'objectType' => '?string',
  15. /* 对象 ID,如果不指定则尝试使用当前页面上的 `${$objectType}->id` 或者 `${$objectType}ID` 的值,例如 `$task->id` 或 `$taskID`。 */
  16. 'objectID' => '?int',
  17. /* 对象,如果不指定则尝试使用当前页面上的 `${$objectType}` 的值,例如 `$task`。 */
  18. 'object' => '?object',
  19. /* 标题,如果不指定则尝试使用当前页面上的 `${$objectType}->title` 或 `${$objectType}->name` 的值,例如 `$story->title`、`$task->name` 。 */
  20. 'title' => '?string',
  21. /* 标题颜色。 */
  22. 'color' => '?string',
  23. /* 是否在标题显示 URL。 */
  24. 'url' => '?bool|string',
  25. /* 工具栏。 */
  26. 'toolbar' => '?array',
  27. /* 详情卡片的左侧主栏目内容区域,可以通过 `-` 来指定分割线,通过键名指定标题,通过 `html()` 来指定 HTML 内容,或者指定为 `callable` 或 `Closure` 动态生成内容,或者指定为 `content()` 属性。 */
  28. 'sections' => '?array',
  29. /* 内容区域。 */
  30. 'content' => '?array',
  31. /* 是否删除。 */
  32. 'deleted' => '?bool'
  33. );
  34. /**
  35. * @var mixed[]
  36. */
  37. protected static $defineBlocks = array
  38. (
  39. 'header' => array(),
  40. 'title' => array(),
  41. 'toolbar' => array('map' => 'btnGroup,toolbar'),
  42. 'body' => array('map' => 'content,section')
  43. );
  44. public static function getPageCSS()
  45. {
  46. return <<<CSS
  47. CSS;
  48. }
  49. protected function created()
  50. {
  51. global $app;
  52. $objectType = $this->prop('objectType');
  53. $objectID = $this->prop('objectID');
  54. $object = $this->prop('object');
  55. if(!$objectType) $objectType = $app->rawModule;
  56. if(!$object) $object = data($objectType);
  57. if(!$objectID) $objectID = $object ? $object->id : data($objectType . 'ID');
  58. if(!$this->hasProp('objectType')) $this->setProp('objectType', $objectType);
  59. if(!$this->hasProp('objectID')) $this->setProp('objectID', $objectID);
  60. if($object)
  61. {
  62. if(!$this->hasProp('object')) $this->setProp('object', $object);
  63. if(!$this->hasProp('color') && isset($object->color)) $this->setProp('color', $object->color);
  64. if(!$this->hasProp('title')) $this->setProp('title', isset($object->name) ? $object->name : $object->title);
  65. }
  66. }
  67. protected function buildToolbar()
  68. {
  69. $toolbar = $this->prop('toolbar');
  70. $toolbarBlock = $this->block('toolbar');
  71. if(!$toolbarBlock && !$toolbar) return null;
  72. $toolbarProps = array_is_list($toolbar) ? array('items' => $toolbar) : $toolbar;
  73. return div
  74. (
  75. setClass('detail-card-toolbar panel-actions'),
  76. $toolbarProps ? toolbar(set::size('sm'), set($toolbarProps)) : null,
  77. $toolbarBlock
  78. );
  79. }
  80. protected function buildTitle()
  81. {
  82. list($objectID, $title, $url, $color, $deleted) = $this->prop(array('objectID', 'title', 'url', 'color', 'deleted'));
  83. $titleBlock = $this->block('title');
  84. $titleView = $title;
  85. return new entityTitle
  86. (
  87. setClass('panel-title flex-1 w-0'),
  88. set::id($objectID),
  89. set::title($title),
  90. set::color($color),
  91. set::idClass('font-normal'),
  92. set::titleClass('text-base text-clip min-w-0 font-normal'),
  93. set::url($url),
  94. set::deleted($deleted),
  95. $titleBlock
  96. );
  97. }
  98. protected function buildHeader()
  99. {
  100. $isSimple = $this->prop('layout') === 'simple';
  101. return div
  102. (
  103. setClass('detail-card-header panel-heading row gap-2 items-center flex-none surface'),
  104. $this->buildTitle(),
  105. $this->block('header'),
  106. $this->buildToolbar()
  107. );
  108. }
  109. /**
  110. * @param mixed[]|callable|string|\zin\setting|\zin\node $item
  111. * @param string|null $title
  112. */
  113. protected function buildSection($item, $title = null)
  114. {
  115. if(is_callable($item)) $item = call_user_func($item, $title);
  116. elseif($item instanceof \Closure) $item = $item();
  117. if(is_null($title) && $item instanceof node) return $item;
  118. if($item instanceof setting) $item = $item->toArray();
  119. if(is_null($title) && isset($item['title']))
  120. {
  121. $title = $item['title'];
  122. unset($item['title']);
  123. }
  124. return div
  125. (
  126. setClass('detail-section'),
  127. $title ? h2(setClass('detail-section-title text-base text-gray py-1 font-normal'), $title) : null,
  128. div(setClass('detail-section-content py-1'), $item ? new content(is_array($item) ? set($item) : $item) : null)
  129. );
  130. }
  131. protected function buildMainSections()
  132. {
  133. $sections = $this->prop('sections', array());
  134. $content = $this->prop('content');
  135. $list = array();
  136. if($content) $list[] = $this->buildSection($content, null);
  137. foreach($sections as $key => $item)
  138. {
  139. if($item === '-')
  140. {
  141. $list[] = hr();
  142. continue;
  143. }
  144. $list[] = $this->buildSection($item, is_string($key) ? $key : null);
  145. }
  146. return $list;
  147. }
  148. protected function buildBody()
  149. {
  150. return div
  151. (
  152. setClass('detail-card-body panel-body'),
  153. $this->buildMainSections(),
  154. $this->block('body')
  155. );
  156. }
  157. protected function build()
  158. {
  159. return div
  160. (
  161. setClass('detail-card panel rounded'),
  162. $this->buildHeader(),
  163. $this->buildBody()
  164. );
  165. }
  166. }