| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- <?php
- namespace zin;
- require_once dirname(__DIR__) . DS . 'btn' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'backbtn' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'content' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'history' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'entitytitle' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'formbase' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'formgroup' . DS . 'v1.php';
- require_once dirname(__DIR__) . DS . 'formrow' . DS . 'v1.php';
- class detailForm extends wg
- {
- /**
- * @var mixed[]
- */
- protected static $defineProps = array
- (
- /* ID,如果不指定则自动生成(使用 zin 部件 GID)。 */
- 'id ' => '?string="$GID"',
- /* 对象类型,例如 `story`、`task` 等,如果不指定则已当前的模块名称作为对象类型。 */
- 'objectType' => '?string',
- /* 对象 ID,如果不指定则尝试使用当前页面上的 `${$objectType}->id` 或者 `${$objectType}ID` 的值,例如 `$task->id` 或 `$taskID`。 */
- 'objectID' => '?int',
- /* 对象标题颜色。 */
- 'color' => '?string',
- /* 对象,如果不指定则尝试使用当前页面上的 `${$objectType}` 的值,例如 `$task`。 */
- 'object' => '?object',
- /* 标题,如果不指定则尝试使用当前页面上的 `${$objectType}->title` 或 `${$objectType}->name` 的值,例如 `$story->title`、`$task->name` 。 */
- 'title' => '?string',
- /* 标题前缀。 */
- 'titlePrefix' => '?string|bool',
- /* 表单操作按钮,如果不指定则使用默认行为的 “保存” 和 “返回” 按钮。 */
- 'actions' => '?array',
- /* 表单操作按钮栏类名。 */
- 'actionsClass' => '?string',
- /* 右上方工具栏的定义。 */
- 'toolbar' => '?array',
- /* 表单提交地址。 */
- 'url' => '?string',
- /* Ajax 表单选项。 */
- 'ajax' => '?array',
- /* 表单项标签默认数据。 */
- 'labelData' => 'null|array|object',
- /* 返回按钮,可以为:`true` 显示默认的返回按钮,`false` 不显示返回按钮,或者指定返回按钮 backBtn 的 back 属性,或者通过数组指定 backBtn 所有属性。 */
- 'backBtn' => '?bool|string|array',
- /* 主要栏表单字段列表。 */
- 'fields' => 'null|string|array|fieldList',
- /* 提交备注。 */
- 'comment' => '?string|bool|array=true',
- /* 侧边栏表单项分组标题。 */
- 'groupTitles'=> '?array',
- /* 右侧侧边栏宽度,如果不指定则默认为 `370`。 */
- 'sideWidth' => '?int=370',
- /* 左侧主栏表单项标签宽度。 */
- 'sideLabelWidth' => '?int|string',
- /* 左侧主栏目历史记录,如果设定为 `true` 显示当前对象默认的理智记录,否如果设置为 `false` 不显示历史记录,如果设置为数组则作为 `history()` 部件的属性来创建历史记录。 */
- 'history' => '?array|bool=true'
- );
- /**
- * @var mixed[]
- */
- protected static $defineBlocks = array
- (
- 'header' => array(),
- 'title' => array(),
- 'main' => array('map' => 'content'),
- 'side' => array('map' => 'tabs'),
- 'actions' => array('map' => 'btn'),
- 'toolbar' => array('map' => 'btnGroup,toolbar')
- );
- public static function getPageCSS()
- {
- return <<<'CSS'
- .detail-form-main .form-label {position: sticky; top: 0; background: var(--color-canvas); z-index: 1; margin-bottom: 4px}
- .detail-form-main .form-label .text {font-size: 14px; font-weight: bold;}
- .detail-form-main {border-right: 1px solid var(--color-border)}
- .detail-form-main .form-grid .form-label.required:after {order: 1}
- .detail-form-side {border-left: 1px solid var(--color-border); margin-left: -1px}
- .detail-form-main .form-grid {padding: 12px 16px}
- .detail-form-history {padding: 12px}
- .detail-form-side .form-row {padding: 8px 0}
- .detail-form-side .form-group {align-items: center; min-height: 20px}
- .detail-form-side .form-label {height: 20px; top: auto}
- .detail-form-side .form-control-static {padding: 0; min-height: 20px}
- CSS;
- }
- /**
- * @var mixed[]|null
- */
- protected $fieldMap;
- /**
- * @var object|null
- */
- protected $object;
- /**
- * @var null|mixed[]|object
- */
- protected $labelData = null;
- protected function created()
- {
- global $app, $lang;
- $objectType = $this->prop('objectType');
- $objectID = $this->prop('objectID');
- $object = $this->prop('object');
- if(!$objectType) $objectType = $app->rawModule;
- if(!$object) $object = data($objectType);
- if(!$objectID) $objectID = $object ? $object->id : data($objectType . 'ID');
- if(!$objectType || !$objectID || !$object)
- {
- $this->triggerError('The objectType, objectID or object property of widget "detail" is undefined.');
- }
- if(!$this->prop('objectType')) $this->setProp('objectType', $objectType);
- if(!$this->prop('objectID')) $this->setProp('objectID', $objectID);
- if(!$this->prop('object')) $this->setProp('object', $object);
- if(!$this->prop('backBtn')) $this->setProp('backBtn', !isInModal());
- if($object)
- {
- if(!$this->hasProp('title')) $this->setProp('title', isset($object->name) ? $object->name : $object->title);
- if(!$this->hasProp('color') && isset($object->color)) $this->setProp('color', $object->color);
- }
- if(!$this->hasProp('titlePrefix') && isset($lang->$objectType->edit)) $this->setProp('titlePrefix', $lang->$objectType->edit);
- if(!$this->hasProp('labelData') && isset($lang->$objectType)) $this->setProp('labelData', $lang->$objectType);
- }
- protected function buildTitle()
- {
- list($object, $objectID, $title, $color, $objectType, $titlePrefix) = $this->prop(array('object', 'objectID', 'title', 'color', 'objectType', 'titlePrefix'));
- $titleBlock = $this->block('title');
- return new entityTitle
- (
- setClass('min-w-0'),
- set::id($objectID),
- set::object($object),
- set::title($title),
- set::titleClass('text-lg text-clip font-bold'),
- set::type($objectType),
- set::color($color),
- $titlePrefix ? to::prefix(span(setClass('text-gray'), $titlePrefix)) : null,
- $titleBlock
- );
- }
- protected function buildToolbar()
- {
- $toolbar = $this->prop('toolbar');
- $toolbarBlock = $this->block('toolbar');
- if(!$toolbarBlock && !$toolbar) return null;
- $toolbarProps = array_is_list($toolbar) ? array('items' => $toolbar) : $toolbar;
- return div
- (
- setClass('detail-form-toolbar'),
- $toolbarProps ? toolbar(set::urlFormatter($this->prop('urlFormatter')), set($toolbarProps)) : null,
- $toolbarBlock
- );
- }
- /**
- * @param mixed[]|null $props
- */
- protected function buildBackBtn($props = null)
- {
- global $lang;
- $backBtn = $this->prop('backBtn');
- if($backBtn === false) return null;
- if(is_string($backBtn)) $backBtn = array('back' => $backBtn);
- elseif(!is_array($backBtn)) $backBtn = array();
- if($props) $backBtn = array_merge($backBtn, $props);
- return new backBtn
- (
- set::icon('back'),
- set::text($lang->goback),
- set($backBtn)
- );
- }
- protected function buildHeader()
- {
- return div
- (
- setClass('detail-form-header row gap-2 items-center flex-none'),
- $this->buildBackBtn(array('type' => 'primary-outline', 'class' => 'mr-2 size-md')),
- $this->buildTitle(),
- $this->block('header'),
- div(setClass('flex-auto')),
- $this->buildToolbar()
- );
- }
- protected function buildHistory()
- {
- $history = $this->prop('history');
- if($history === false) return null;
- if(!is_array($history)) $history = array();
- return new history
- (
- set::className('detail-form-history overflow-visible ring-0 border-t'),
- set::objectType($this->prop('objectType')),
- set::objectID($this->prop('objectID')),
- set::commentBtn(false),
- set($history)
- );
- }
- /**
- * @param string $name
- */
- protected function getFieldLabel($name)
- {
- $labelData = $this->labelData;
- $lblName = 'lbl' . ucfirst($name);
- if(is_object($labelData)) return isset($labelData->$lblName) ? $labelData->$lblName : (isset($labelData->$name) ? $labelData->$name : null);
- if(is_array($labelData)) return isset($labelData[$lblName]) ? $labelData[$lblName] : (isset($labelData[$name]) ? $labelData[$name] : null);
- return null;
- }
- /**
- * @param string $name
- */
- protected function getFieldValue($name)
- {
- $object = $this->object;
- if(!$object) return null;
- return isset($object->$name) ? strval($object->$name) : null;
- }
- /**
- * @param mixed[] $field
- * @param bool $useRow
- */
- protected function buildField($field, $useRow = false)
- {
- $name = $field['name'];
- if(!isset($field['value'])) $field['value'] = $this->getFieldValue($name);
- if(!isset($field['label'])) $field['label'] = $this->getFieldLabel($name);
- $control = isset($field['control']) ? $field['control'] : null;
- if(is_null($control)) $control = array('name' => $name);
- if(is_string($control)) $control = array('control' => $control, 'name' => $name);
- if(is_array($control) && !isset($control['id'])) $control['id'] = '';
- $field['control'] = $control;
- $view = new formGroup(set($field));
- if($useRow) $view = new formRow($view);
- return $view;
- }
- /**
- * @param string|mixed[] $groupOrFields
- * @param bool $useRow
- */
- protected function buildFields($groupOrFields = 'main', $useRow = false)
- {
- if(is_string($groupOrFields))
- {
- if(!isset($this->fieldMap[$groupOrFields])) return null;
- $fields = $this->fieldMap[$groupOrFields];
- }
- else
- {
- $fields = $groupOrFields;
- }
- if(!$fields) return null;
- $items = array();
- foreach($fields as $field)
- {
- $item = $this->buildField($field, $useRow);
- if(empty($field)) continue;
- $items[] = $item;
- }
- return $items;
- }
- protected function buildComment()
- {
- $comment = $this->prop('comment');
- if(!$comment) return null;
- $props = array('name' => 'comment', 'control' => 'editor');
- if(is_string($comment)) $props['name'] = $comment;
- elseif(is_array($comment)) $props = array_merge($props, $comment);
- if(!isset($props['label']))
- {
- global $lang;
- $label = $this->getFieldLabel('comment');
- $props['label'] = $label ? $label : $lang->comment;
- }
- return new formGroup(set($props));
- }
- protected function buildMain()
- {
- return div
- (
- setClass('detail-form-main flex-auto'),
- div
- (
- setClass('form-grid'),
- $this->buildFields(),
- $this->buildComment()
- ),
- $this->block('main'),
- $this->buildHistory()
- );
- }
- protected function buildSideGroups()
- {
- $groupTitles = $this->prop('groupTitles', array());
- $items = array();
- foreach($this->fieldMap as $group => $fields)
- {
- if($group === 'main') continue;
- $items[] = div
- (
- setClass('form-horz col pt-2 pr-5'),
- div
- (
- setClass('text-md font-bold sticky top-0 canvas py-2 px-4 z-5'),
- isset($groupTitles[$group]) ? $groupTitles[$group] : $group
- ),
- $this->buildFields($fields, true)
- );
- }
- return div
- (
- setClass('detail-form-side-groups pb-3'),
- setCssVar('--form-horz-label-width', $this->prop('sideLabelWidth')),
- $items
- );
- }
- protected function buildSide()
- {
- return div
- (
- setClass('detail-form-side flex-none col gap-1'),
- setStyle('width', $this->prop('sideWidth') . 'px'),
- $this->buildSideGroups(),
- $this->block('side')
- );
- }
- protected function buildBody()
- {
- return div
- (
- setClass('detail-form-body row items-start'),
- $this->buildMain(),
- $this->buildSide()
- );
- }
- protected function beforeBuild()
- {
- list($object, $labelData, $fields) = $this->prop(array('object', 'labelData', 'fields'));
- $this->object = $object;
- $this->labelData = $labelData ? $labelData : array();
- $this->fieldMap = array();
- if($fields instanceof fieldList) $fields = $fields->toArray();
- foreach($fields as $field)
- {
- if(!$field) continue;
- if($field instanceof setting) $field = $field->toArray();
- if(is_object($field)) $field = get_object_vars($field);
- if(!isset($field['name'])) continue;
- $group = (isset($field['group']) && $field['group']) ? $field['group'] : 'main';
- if(!isset($this->fieldMap[$group])) $this->fieldMap[$group] = array();
- $this->fieldMap[$group][$field['name']] = $field;
- }
- }
- protected function build()
- {
- $this->beforeBuild();
- list($objectType, $objectID, $backBtn) = $this->prop(array('objectType', 'objectID', 'backBtn'));
- $formProps = array_keys(formBase::definedPropsList());
- $backProps = array();
- if(is_string($backBtn)) $backProps['back'] = $backBtn;
- elseif(is_array($backBtn)) $backProps = $backBtn;
- return div
- (
- setClass('detail-form col gap-3'),
- setData('id', $objectID),
- setData('type', $objectType),
- set($this->getRestProps()),
- $this->buildHeader(),
- new formBase
- (
- setClass('canvas shadow ring rounded gap-0'),
- set($this->props->pick($formProps)),
- set($backProps),
- set::actionsClass('py-4 sticky border-t bottom-0 canvas z-5', isset($formProps['actionsClass']) ? $formProps['actionsClass'] : null),
- $this->buildBody(),
- $this->children()
- )
- );
- }
- }
|