diff --git a/module/task/ui/view.html.php b/module/task/ui/view.html.php
index 729cb0f687..842ce19c5f 100644
--- a/module/task/ui/view.html.php
+++ b/module/task/ui/view.html.php
@@ -12,540 +12,163 @@ namespace zin;
include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php');
-global $lang;
+$isInModal = isInModal();
-/* Join mailto. */
-$mailtoList = array();
-if(!empty($task->mailto))
+/* 初始化头部右上方工具栏。Init detail toolbar. */
+$toolbar = array();
+if(!$isInModal && hasPriv('task', 'create', $task))
{
- foreach(explode(',', $task->mailto) as $account)
- {
- if(empty($account)) continue;
- $mailtoList[] = zget($users, trim($account));
- }
-}
-$mailtoList = implode($lang->comma, $mailtoList);
-
-detailHeader
-(
- to::title
+ $toolbar[] = array
(
- $task->team ? span
- (
- setClass('label primary-pale'),
- $lang->task->modeList[$task->mode]
- ) : null,
- entityLabel
- (
- set
- (
- array
- (
- 'entityID' => $task->id,
- 'level' => 1
- )
- ),
- $task->parent > 0 ?
- span
- (
- setClass('text'),
- set::title($task->name),
- span(setClass('label gray-pale rounded-xl'), $lang->task->childrenAB),
- a(set::href(createLink('task', 'view', "taskID={$task->parent}")), $task->parentName),
- span('/'),
- span(setStyle(array('color' => $task->color)), $task->name)
- ) : span(setStyle(array('color' => $task->color)), $task->name)
- ),
- $task->deleted ? span(setClass('label danger'), $lang->task->deleted) : null
- ),
- !isAjaxRequest('modal') && common::hasPriv('task', 'create', $task) ? to::suffix(btn(set::icon('plus'), set::url(createLink('task', 'create', "executionID={$task->execution}")), set::type('primary'), set('data-app', $app->tab), $lang->task->create)) : null
-);
-
-/* Construct suitable actions for the current task. */
-if(common::hasPriv('repo', 'createBranch') && empty($task->linkedBranch)) $hasRepo = $this->loadModel('repo')->getRepoPairs('execution', $task->execution, false);
-
-$operateMenus = array();
-foreach($config->task->view->operateList['main'] as $operate)
-{
- if($operate == 'createBranch')
- {
- if(empty($hasRepo) || !common::hasPriv('repo', $operate) || !empty($task->linkedBranch) || !common::canModify('execution', $execution)) continue;
- }
- else
- {
- if(!common::hasPriv('task', $operate, $task)) continue;
- if(!$this->task->isClickable($task, $operate)) continue;
-
- if($operate == 'batchCreate') $config->task->actionList['batchCreate']['text'] = $lang->task->children;
- }
-
- $operateMenus[] = $config->task->actionList[$operate];
-}
-
-/* Construct common actions for task. */
-$commonActions = array();
-foreach($config->task->view->operateList['common'] as $operate)
-{
- if(!common::hasPriv('task', $operate, $task)) continue;
- if($operate == 'view' && $task->parent <= 0) continue;
-
- $settings = $config->task->actionList[$operate];
- if($operate != 'view') $settings['text'] = '';
-
- $commonActions[] = $settings;
-}
-
-if($task->children) $children = initTableData($task->children, $config->task->dtable->children->fieldList, $this->task);
-if($task->team)
-{
- $teams = array();
- foreach($task->team as $team)
- {
- $teams[] = h::tr
- (
- h::td
- (
- zget($users, $team->account)
- ),
- h::td
- (
- (float)$team->estimate
- ),
- h::td
- (
- (float)$team->consumed
- ),
- h::td
- (
- (float)$team->left
- ),
- h::td
- (
- setClass("status-{$team->status}"),
- zget($lang->task->statusList, $team->status)
- )
- );
- }
-}
-
-/* Set the module name of the task. */
-$moduleTitle = '';
-$moduleItems = array();
-if(empty($modulePath))
-{
- $moduleTitle .= '/';
- $moduleItems[] = span('/');
-}
-else
-{
- if($product)
- {
- $moduleTitle .= $product->name . '/';
- $moduleItems[] = span(a(set::href(createLink('product', 'browse', "productID=$product->id")), $product->name));
- $moduleItems[] = icon('angle-right');
- }
- foreach($modulePath as $key => $module)
- {
- $moduleTitle .= $module->name;
- $moduleItems[] = a(set::href(createLink('execution', 'task', "executionID=$task->execution&browseType=byModule¶m=$module->id")), $module->name) ?? span($module->name);
- if(isset($modulePath[$key + 1]))
- {
- $moduleTitle .= '/';
- $moduleItems[] = icon('angle-right');
- }
- }
-}
-
-$canViewMR = common::hasPriv('mr', 'view');
-$linkedMR = array();
-foreach($linkMRTitles as $MRID => $linkMRTitle)
-{
- $linkedMR[] = $canViewMR ? a
- (
- set::href(createLink('mr', 'view', "MRID=$MRID")),
- "#$MRID $linkMRTitle",
- setData('app', 'devops')
- ) : div("#$MRID $linkMRTitle");
-}
-
-$canViewRevision = common::hasPriv('repo', 'revision');
-$linkedCommits = array();
-foreach($linkCommits as $commit)
-{
- if(empty($commit->revision)) continue;
-
- $revision = substr($commit->revision, 0, 10);
- $linkedCommits[] = $canViewRevision ? a
- (
- set::href(createLink('repo', 'revision', "repoID={$commit->repo}&objectID={$task->execution}&revision={$commit->revision}")),
- "#$revision",
- setData('app', 'devops')
- ) : div($revision . ' ' . $commit->comment);
-}
-
-$linkedCases = array();
-$canViewCase = common::hasPriv('testcase', 'view');
-if(!empty($task->cases))
-{
- foreach($task->cases as $caseID => $case)
- {
- $linkedCases[] = $canViewCase ? a
- (
- setClass('flex clip'),
- set::href(createLink('testcase', 'view', "caseID={$caseID}")),
- setData('toggle', 'modal'),
- setData('size', 'lg'),
- set::title($case),
- "#{$caseID} " . $case
- ) : div(
- setClass('flex clip'),
- set::title($case),
- "#{$caseID} " . $case
- );
- }
-}
-
-detailBody
-(
- sectionList
- (
- section
- (
- set::title($lang->task->legendDesc),
- set::content(empty($task->desc) ? $lang->noData : $task->desc),
- set::useHtml(true)
- ),
- $task->fromBug ?
- section
- (
- set::title($lang->task->fromBug),
- sectionCard
- (
- entityLabel
- (
- set::entityID($task->fromBug),
- set::text($fromBug->title)
- ),
- item
- (
- set::title($lang->bug->steps),
- empty($fromBug->steps) ? $lang->noData : html($fromBug->steps)
- )
- )
- ) : null,
- !$task->fromBug && $task->story ?
- section
- (
- set::title($lang->task->story),
- sectionCard
- (
- entityLabel
- (
- set::entityID($task->storyID),
- set::text($task->storyTitle),
- set::href(createLink('story', 'view', "storyID=$task->storyID")),
- set::textClass('clip'),
- $task->needConfirm ? to::suffix
- (
- div
- (
- setClass('flex-1 text-right nowrap'),
- span($lang->task->storyChange),
- a
- (
- setClass('mx-2 btn primary size-sm'),
- set::href(createLink('task', 'confirmStoryChange', "taskID={$task->id}")),
- $lang->confirm
- )
- )
- ) : null
- ),
- div
- (
- setClass('p-4'),
- span("[{$lang->story->legendSpec}]", setClass('text-gray')),
- empty($task->storySpec) && empty($task->storyFiles) ? $lang->noData : html($task->storySpec)
- ),
- div
- (
- setClass('p-4'),
- span("[{$lang->task->storyVerify}]", setClass('text-gray')),
- empty($task->storyVerify) ? $lang->noData : html($task->storyVerify)
- )
- )
- ) : null,
- !empty($task->cases) ? section
- (
- set::title($lang->task->case),
- div($linkedCases)
- ) : null,
- $task->children ?
- section
- (
- set::title($lang->task->children),
- dtable
- (
- set::cols(array_values($config->task->dtable->children->fieldList)),
- set::userMap($users),
- set::data($children),
- set::checkable(false)
- )
- ) : null
- ),
- $task->files ? fileList
- (
- set::files($task->files)
- ) : null,
- history(),
- floatToolbar
- (
- isAjaxRequest('modal') ? null : to::prefix(backBtn(set::icon('back'), $lang->goback)),
- $task->deleted == '0' ? set::main($operateMenus) : null,
- $task->deleted == '0' ? set::suffix($commonActions) : null,
- set::object($task)
- ),
- detailSide
- (
- tabs
- (
- set::collapse(true),
- tabPane
- (
- set::key('legend-basic'),
- set::title($lang->task->legendBasic),
- set::active(true),
- tableData
- (
- $execution->multiple ? item
- (
- set::name($lang->task->execution),
- a
- (
- set::href(createLink('execution', 'view', "executionID=$execution->id")),
- $execution->name
- )
- ) : null,
- item
- (
- set::name($lang->task->module),
- set::title($moduleTitle),
- $moduleItems
- ),
- item
- (
- set::name($lang->task->fromBug),
- !empty($fromBug) ? a
- (
- setData
- (
- array
- (
- 'toggle' => 'modal',
- 'size' => 'lg'
- )
- ),
- set::href(createLink('bug', 'view', "id={$task->fromBug}")),
- set::title($fromBug->title),
- $fromBug->title
- ) : null
- ),
- item
- (
- set::name($lang->task->assignedTo),
- zget($users, $task->assignedTo, '')
- ),
- item
- (
- set::name($lang->task->type),
- zget($this->lang->task->typeList, $task->type, $task->type)
- ),
- item
- (
- set::name($lang->task->status),
- $this->processStatus('task', $task)
- ),
- item
- (
- set::name($lang->task->progress),
- $task->progress . ' %'
- ),
- item
- (
- set::name($lang->task->pri),
- priLabel
- (
- setClass('align-sub'),
- $task->pri,
- set::text($lang->task->priList)
- )
- ),
- item
- (
- set::name($lang->task->keywords),
- $task->keywords
- ),
- item
- (
- set::name($lang->task->mailto),
- $mailtoList
- )
- )
- ),
- tabPane
- (
- set::key('legend-life'),
- set::title($lang->task->legendLife),
- tableData
- (
- item
- (
- set::name($lang->task->openedBy),
- $task->openedBy ? zget($users, $task->openedBy, $task->openedBy) . $lang->at . $task->openedDate : ''
- ),
- item
- (
- set::name($lang->task->finishedBy),
- $task->finishedBy ? zget($users, $task->finishedBy, $task->finishedBy) . $lang->at . $task->finishedDate : ''
- ),
- item
- (
- set::name($lang->task->canceledBy),
- $task->canceledBy ? zget($users, $task->canceledBy, $task->canceledBy) . $lang->at . $task->canceledDate : ''
- ),
- item
- (
- set::name($lang->task->closedBy),
- $task->closedBy ? zget($users, $task->closedBy, $task->closedBy) . $lang->at . $task->closedDate : ''
- ),
- item
- (
- set::name($lang->task->closedReason),
- $task->closedReason ? $lang->task->reasonList[$task->closedReason] : ''
- ),
- item
- (
- set::name($lang->task->lastEdited),
- $task->lastEditedBy ? zget($users, $task->lastEditedBy, $task->lastEditedBy) . $lang->at . $task->lastEditedDate : ''
- )
- )
- ),
- $task->team ? tabPane
- (
- set::key('legend-team'),
- set::title($lang->task->team),
- h::table
- (
- setClass('table condensed bordered'),
- setID('team'),
- h::thead
- (
- h::tr
- (
- h::th
- (
- $lang->task->team,
- set::width('80px')
- ),
- h::th
- (
- $lang->task->estimateAB,
- set::width('60px')
- ),
- h::th
- (
- $lang->task->consumedAB,
- set::width('60px')
- ),
- h::th
- (
- $lang->task->leftAB,
- set::width('60px')
- ),
- h::th
- (
- $lang->task->statusAB,
- set::width('80px')
- )
- )
- ),
- h::tbody($teams)
- )
- ) : null
- ),
- tabs
- (
- set::collapse(true),
- tabPane
- (
- set::key('legend-effort'),
- set::title($lang->task->legendEffort),
- set::active(true),
- tableData
- (
- item
- (
- set::name($lang->task->estimate),
- $task->estimate . ' ' . $lang->task->suffixHour
- ),
- item
- (
- set::name($lang->task->consumed),
- round($task->consumed, 2) . ' ' . $lang->task->suffixHour
- ),
- item
- (
- set::name($lang->task->left),
- $task->left . ' ' . $lang->task->suffixHour
- ),
- item
- (
- set::name($lang->task->estStarted),
- helper::isZeroDate($task->estStarted) ? '' : $task->estStarted
- ),
- item
- (
- set::name($lang->task->realStarted),
- helper::isZeroDate($task->realStarted) ? '' : substr($task->realStarted, 0, 19)
- ),
- item
- (
- set::name($lang->task->deadline),
- helper::isZeroDate($task->deadline) ? '' : $task->deadline
- )
- )
- ),
- tabPane
- (
- set::key('legend-misc'),
- set::title($lang->task->legendMisc),
- tableData
- (
- empty($task->linkedBranch) ? null : item
- (
- set::name($lang->task->relatedBranch),
- current($task->linkedBranch)
- ),
- item
- (
- set::name($lang->task->linkMR),
- div($linkedMR)
- ),
- item
- (
- set::name($lang->task->linkCommit),
- div($linkedCommits)
- )
- )
- )
- )
- )
-);
-
-if(!isInModal())
-{
- floatPreNextBtn
- (
- !empty($preAndNext->pre) ? set::preLink(createLink('task', 'view', "taskID={$preAndNext->pre->id}")) : null,
- !empty($preAndNext->next) ? set::nextLink(createLink('task', 'view', "taskID={$preAndNext->next->id}")) : null
+ 'icon' => 'plus',
+ 'type' => 'primary',
+ 'text' => $lang->task->create,
+ 'url' => createLink('task', 'create', "executionID={$task->execution}")
);
}
+
+/* 初始化底部操作栏。Init bottom actions. */
+$actions = array();
+if(!$task->deleted)
+{
+ $hasRepo = common::hasPriv('repo', 'createBranch') && empty($task->linkedBranch) && $this->loadModel('repo')->getRepoPairs('execution', $task->execution, false);
+ /* Construct suitable actions for the current task. */
+ foreach($config->task->view->operateList['main'] as $operate)
+ {
+ if($operate == 'createBranch')
+ {
+ if(empty($hasRepo) || !common::hasPriv('repo', $operate) || !empty($task->linkedBranch) || !common::canModify('execution', $execution)) continue;
+ }
+ else
+ {
+ if(!common::hasPriv('task', $operate, $task)) continue;
+ if(!$this->task->isClickable($task, $operate)) continue;
+
+ if($operate == 'batchCreate') $config->task->actionList['batchCreate']['text'] = $lang->task->children;
+ }
+
+ $actions[] = $config->task->actionList[$operate];
+ }
+ /* Construct common actions for task. */
+ $commonActions = array();
+ foreach($config->task->view->operateList['common'] as $operate)
+ {
+ if(!common::hasPriv('task', $operate, $task)) continue;
+ if($operate == 'view' && $task->parent <= 0) continue;
+
+ $settings = $config->task->actionList[$operate];
+ if($operate != 'view') $settings['text'] = '';
+
+ $commonActions[] = $settings;
+ }
+ if($commonActions) $actions = array_merge($actions, array(array('type' => 'divider')), $commonActions);
+}
+
+/* 初始化主栏内容。Init sections in main column. */
+$sections = array();
+$sections[] = setting()
+ ->title($lang->task->legendDesc)
+ ->control('html')
+ ->content(empty($task->desc) ? $lang->noDesc : $task->desc);
+if($task->fromBug)
+{
+ $sections[] = setting()
+ ->title($lang->task->fromBug)
+ ->control('detailCard')
+ ->object($fromBug)
+ ->content(setting()->title($lang->bug->steps)->control('html')->content(empty($fromBug->steps) ? $lang->noData : $fromBug->steps));
+}
+if(!$task->fromBug && $task->story)
+{
+ $storyDetailProps = array
+ (
+ 'control' => 'detailCard',
+ 'title' => $task->storyTitle,
+ 'url' => createLink('story', 'view', "storyID=$task->storyID"),
+ 'objectID' => $task->storyID,
+ 'toolbar' => !$task->needConfirm ? array
+ (
+ array('text' => $lang->task->storyChange, 'class' => 'ghost pointer-events-none'),
+ array('text' => $lang->confirm, 'type' => 'primary', 'url' => createLink('task', 'confirmStoryChange', "taskID={$task->id}"))
+ ) : null,
+ 'sections' => array
+ (
+ setting()->title("[{$lang->story->legendSpec}]")->control('html')->content(empty($task->storySpec) && empty($task->storyFiles) ? $lang->noData : $task->storySpec),
+ setting()->title("[{$lang->task->storyVerify}]")->control('html')->content(empty($task->storyVerify) ? $lang->noData : $task->storyVerify)
+ )
+ );
+ $sections[] = setting()
+ ->title($lang->task->story)
+ ->control($storyDetailProps);
+}
+if(!empty($task->cases))
+{
+ $sections[] = setting()
+ ->title($lang->task->case)
+ ->control('entityList')
+ ->type('testcase')
+ ->items($task->cases);
+}
+if($task->children)
+{
+ $children = initTableData($task->children, $config->task->dtable->children->fieldList, $this->task);
+ $sections[] = setting()
+ ->title($lang->task->children)
+ ->control('dtable')
+ ->className('ring')
+ ->cols(array_values($config->task->dtable->children->fieldList))
+ ->userMap($users)
+ ->data($children)
+ ->checkable(false);
+}
+if($task->files)
+{
+ $sections[] = array
+ (
+ 'title' => $lang->files,
+ 'control' => 'fileList',
+ 'files' => $task->files,
+ 'object' => $task
+ );
+}
+
+/* 初始化侧边栏标签页。Init sidebar tabs. */
+$tabs = array();
+
+/* 基本信息。Legend basic items. */
+$tabs[] = setting()
+ ->group('basic')
+ ->title($lang->task->legendBasic)
+ ->control('taskBasicInfo')
+ ->statusText($this->processStatus('task', $task));
+
+/* 一生信息。Legend life items. */
+$tabs[] = setting()
+ ->group('basic')
+ ->title($lang->task->legendLife)
+ ->control('taskLifeInfo');
+
+if($task->team)
+{
+ $tabs[] = setting()
+ ->group('basic')
+ ->title($lang->task->team)
+ ->control('taskTeam');
+}
+
+$tabs[] = setting()
+ ->group('related')
+ ->title($lang->task->legendEffort)
+ ->control('taskEffortInfo');
+$tabs[] = setting()
+ ->group('related')
+ ->title($lang->task->legendMisc)
+ ->control('taskMiscInfo');
+
+detail
+(
+ set::toolbar($toolbar),
+ set::sections($sections),
+ set::tabs($tabs),
+ set::actions($actions)
+);