* Refactor view page of the execution.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
.timeline > li {position: relative; list-style: none}
|
||||
.timeline > li:before, .timeline > li > a:after, .timeline > li > div:after {position: absolute; left: -20px; display: block; width: 15px; height: 15px; content: ' '; border-radius: 50%}
|
||||
.timeline > li:before {top: 12px; left: -16px; z-index: 3; width: 7px; height: 7px; background-color: #c4c4c4; border: none; border: 1px solid #c4c4c4}
|
||||
.timeline > li > a:after, .timeline > li > div:after {top: 11px; left: -17px; z-index: 3; width: 9px; height: 9px; background-color: #2e7fff; border-radius: 50%; opacity: 0}
|
||||
.timeline > li + li:after {position: absolute; top: -12px; bottom: 20px; left: -13px; z-index: 1; display: block; content: ' '; border-left: 1px solid #eee}
|
||||
.timeline > li.active > a:after, .timeline > li.active > div:after {opacity: 1}
|
||||
.timeline > li.active:before {top: 8px; left: -20px; width: 15px; height: 15px; background-color: rgba(46,127,255, .2); border: none}
|
||||
.timeline > li > a, .timeline > li > div {display: block; padding: 5px; line-height: 20px}
|
||||
.timeline > li.active > a {color: #313c52}
|
||||
|
||||
.timeline-tag {position: absolute; top: 5px; left: -115px; font-size: 12px}
|
||||
.timeline-tag-left {padding-left: 115px}
|
||||
|
||||
.timeline-sm {font-size: 12px}
|
||||
.timeline-sm > li:before, .timeline-sm > li > a:after, .timeline-sm > li > div:after {top: 10px; left: -20px; width: 11px; height: 11px}
|
||||
.timeline-sm > li.active:before, .timeline-sm > li:before {top: 10px; left: -18px; width: 11px; height: 11px; background: 0 0; border: 1px solid #eee}
|
||||
.timeline-sm > li > a, .timeline-sm > li > div {line-height: 20px}
|
||||
.timeline-sm > li > a:after, .timeline-sm > li > div:after {top: 13px; left: -15px; width: 5px; height: 5px}
|
||||
|
||||
.linked-products > .items-center {justify-content: space-between;}
|
||||
.execution-statitic-hours .table-data-body, .execution-basic-info .table-data-body {display: flex; flex-wrap: wrap;}
|
||||
.execution-statitic-hours .table-data-body .table-data-tr {flex: 0 1 50%;}
|
||||
.execution-statitic-hours .table-data .table-data-th {min-width: 60px; text-align: right;}
|
||||
.execution-basic-info .table-data-body .table-data-tr {flex: 0 1 33%;}
|
||||
.execution-basic-info .table-data .table-data-th {min-width: 36px;}
|
||||
@@ -0,0 +1,18 @@
|
||||
$(function()
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
blocks.forEach(block =>
|
||||
{
|
||||
if(block.domID)
|
||||
{
|
||||
block.content = {html: $(`#${block.domID}`).html()};
|
||||
$(`#${block.domID}`).remove();
|
||||
delete(block.domID);
|
||||
}
|
||||
});
|
||||
|
||||
const $dashboard = $('#executionDashBoard').data('zui.Dashboard')
|
||||
$dashboard.render({blocks: blocks});
|
||||
}, 10);
|
||||
});
|
||||
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The view view file of execution module of ZenTaoPMS.
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package execution
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$blocks = array(
|
||||
array(
|
||||
'id' => 1,
|
||||
'size' => 'sm',
|
||||
'domID' => 'burnBlock'
|
||||
),
|
||||
array(
|
||||
'id' => 2,
|
||||
'size' => 'sm',
|
||||
'domID' => 'dynamicBlock'
|
||||
),
|
||||
array(
|
||||
'id' => 3,
|
||||
'size' => 'xl',
|
||||
'domID' => 'basicBlock'
|
||||
),
|
||||
array(
|
||||
'id' => 4,
|
||||
'size' => 'sm',
|
||||
'domID' => 'memberBlock'
|
||||
),
|
||||
array(
|
||||
'id' => 5,
|
||||
'size' => 'sm',
|
||||
'domID' => 'docBlock'
|
||||
),
|
||||
array(
|
||||
'id' => 6,
|
||||
'size' => 'smWide',
|
||||
'domID' => 'historyBlock',
|
||||
)
|
||||
);
|
||||
jsVar('blocks', $blocks);
|
||||
|
||||
dashboard
|
||||
(
|
||||
setID('executionDashBoard'),
|
||||
set::blocks($blocks),
|
||||
set::blockMenu(false)
|
||||
);
|
||||
|
||||
/* Dynamic list. */
|
||||
$dynamicDom = array();
|
||||
foreach($dynamics as $action)
|
||||
{
|
||||
$dynamicDom[] = li
|
||||
(
|
||||
setClass($action->major ? 'active': ''),
|
||||
div
|
||||
(
|
||||
span(
|
||||
setClass('timeline-tag'),
|
||||
$action->date
|
||||
),
|
||||
span(
|
||||
setClass('timeline-text clip'),
|
||||
zget($users, $action->actor),
|
||||
span
|
||||
(
|
||||
setClass('text-gray'),
|
||||
" {$action->actionLabel} "
|
||||
),
|
||||
span(" {$action->objectLabel} "),
|
||||
a
|
||||
(
|
||||
setClass('clip'),
|
||||
set::href($action->objectLink),
|
||||
set::title($action->objectName),
|
||||
$action->objectName
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
div
|
||||
(
|
||||
setID('dynamicBlock'),
|
||||
setClass('hidden'),
|
||||
panel
|
||||
(
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
$lang->execution->latestDynamic,
|
||||
)
|
||||
),
|
||||
to::headingActions
|
||||
(
|
||||
common::hasPriv('execution', 'dynamic') ? btn
|
||||
(
|
||||
setClass('ghost text-gray'),
|
||||
set::url(createLink('execution', 'dynamic', "executionID={$execution->id}&type=all")),
|
||||
$lang->more
|
||||
) : null
|
||||
),
|
||||
set::bodyClass('pt-0 overflow-x-hidden'),
|
||||
ul
|
||||
(
|
||||
setClass('timeline timeline-tag-left no-margin'),
|
||||
$dynamicDom
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/* Related members. */
|
||||
$membersDom = array();
|
||||
foreach(array('PM', 'PO', 'QD', 'RD') as $field)
|
||||
{
|
||||
if(empty($execution->$field)) continue;
|
||||
|
||||
$membersDom[] = div
|
||||
(
|
||||
setClass('flex-initial w-1/2 py-1'),
|
||||
icon('person', setClass('mr-2')),
|
||||
zget($users, $execution->$field),
|
||||
span
|
||||
(
|
||||
setClass('text-gray ml-2'),
|
||||
"( {$lang->execution->$field} )"
|
||||
)
|
||||
);
|
||||
|
||||
unset($teamMembers[$execution->$field]);
|
||||
}
|
||||
$memberCount = count($membersDom);
|
||||
foreach($teamMembers as $teamMember)
|
||||
{
|
||||
if($memberCount >= 10) break;
|
||||
|
||||
$membersDom[] = div
|
||||
(
|
||||
setClass('flex-initial w-1/2 py-1'),
|
||||
icon('person', setClass('mr-2')),
|
||||
zget($users, $teamMember->account),
|
||||
);
|
||||
$memberCount ++;
|
||||
}
|
||||
|
||||
if(common::hasPriv('execution', 'manageMembers'))
|
||||
{
|
||||
$membersDom[] = div
|
||||
(
|
||||
setClass('flex-initial w-1/2 py-1'),
|
||||
a
|
||||
(
|
||||
setClass('ghost text-gray'),
|
||||
icon('plus', setClass('bg-primary-50 text-primary mr-2')),
|
||||
span($lang->execution->manageMembers),
|
||||
set::href(createLink('execution', 'manageMembers', "executionID={$execution->id}"))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
div
|
||||
(
|
||||
setID('memberBlock'),
|
||||
setClass('hidden'),
|
||||
panel
|
||||
(
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
$lang->execution->relatedMember
|
||||
)
|
||||
),
|
||||
to::headingActions
|
||||
(
|
||||
common::hasPriv('execution', 'team') ? btn
|
||||
(
|
||||
setClass('ghost text-gray'),
|
||||
set::url(createLink('execution', 'team', "executionID={$execution->id}")),
|
||||
$lang->more
|
||||
) : null
|
||||
),
|
||||
set::bodyClass('flex flex-wrap pt-0'),
|
||||
$membersDom
|
||||
)
|
||||
);
|
||||
|
||||
/* History list. */
|
||||
div
|
||||
(
|
||||
setID('historyBlock'),
|
||||
setClass('hidden'),
|
||||
div
|
||||
(
|
||||
setClass('overflow-y-auto h-full'),
|
||||
history()
|
||||
)
|
||||
);
|
||||
|
||||
$programDom = null;
|
||||
if($execution->projectInfo->grade > 1)
|
||||
{
|
||||
foreach($programList as $programID => $name)
|
||||
{
|
||||
if(common::hasPriv('program', 'product'))
|
||||
{
|
||||
$programList[$programID] = html::a
|
||||
(
|
||||
$this->createLink('program', 'product', "programID={$programID}"),
|
||||
$name
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$programList[$programID] = span($name);
|
||||
}
|
||||
}
|
||||
|
||||
$programDom = div
|
||||
(
|
||||
icon('program mr-2'),
|
||||
html(implode('/ ', $programList))
|
||||
);
|
||||
}
|
||||
|
||||
$productsDom = null;
|
||||
if($execution->projectInfo->hasProduct)
|
||||
{
|
||||
foreach($products as $productID => $product)
|
||||
{
|
||||
foreach($product->branches as $branchID)
|
||||
{
|
||||
$branchName = isset($branchGroups[$productID][$branchID]) ? '/' . $branchGroups[$productID][$branchID] : '';
|
||||
$productsDom[] = div
|
||||
(
|
||||
setClass('flex-initial w-1/2 py-1'),
|
||||
icon('product mr-2'),
|
||||
a
|
||||
(
|
||||
set::href(createLink('product', 'browse', "productID={$productID}&branch={$branchID}")),
|
||||
span($product->name . $branchName)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$plansDom = null;
|
||||
if($features['plan'])
|
||||
{
|
||||
foreach($products as $productID => $product)
|
||||
{
|
||||
foreach($product->plans as $planIDList)
|
||||
{
|
||||
$planIDList = explode(',', $planIDList);
|
||||
foreach($planIDList as $planID)
|
||||
{
|
||||
if(!isset($planGroups[$productID][$planID])) continue;
|
||||
$plansDom[] = div
|
||||
(
|
||||
setClass('flex-initial py-1'),
|
||||
icon('calendar mr-2'),
|
||||
a
|
||||
(
|
||||
set::href(createLink('productplan', 'view', "planID={$planID}")),
|
||||
span($product->name . '/' . $planGroups[$productID][$planID])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$progress = ($execution->totalConsumed + $execution->totalLeft) ? floor($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft) * 1000) / 1000 * 100 : 0;
|
||||
$hoursDom = div
|
||||
(
|
||||
div
|
||||
(
|
||||
span($lang->execution->progress . ' ' . $progress . $lang->percent),
|
||||
div
|
||||
(
|
||||
set('class', 'progress'),
|
||||
div
|
||||
(
|
||||
set('class', 'progress-bar'),
|
||||
set('role', 'progressbar'),
|
||||
setStyle(['width' => $progress . $lang->percent]),
|
||||
)
|
||||
)
|
||||
),
|
||||
tableData
|
||||
(
|
||||
set::useTable(false),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->begin),
|
||||
$execution->begin
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->realBeganAB),
|
||||
helper::isZeroDate($execution->realBegan) ? '' : $execution->realBegan
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->end),
|
||||
$execution->end
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->realEndAB),
|
||||
helper::isZeroDate($execution->realEndAB) ? '' : $execution->realEndAB
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->totalEstimate),
|
||||
(float)$execution->totalEstimate . $lang->execution->workHour
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->totalDays),
|
||||
$execution->days . $lang->execution->day
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->totalConsumed),
|
||||
(float)$execution->totalConsumed . $lang->execution->workHour
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->totalHours),
|
||||
(float)$execution->totalHours . $lang->execution->workHour
|
||||
),
|
||||
item
|
||||
(
|
||||
set::name($lang->execution->totalLeft),
|
||||
(float)$execution->totalLeft . $lang->execution->workHour
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$basicInfoDom = tableData
|
||||
(
|
||||
set::useTable(false),
|
||||
$features['story'] ? item
|
||||
(
|
||||
set::name($lang->story->common),
|
||||
$statData->storyCount
|
||||
) : null,
|
||||
item
|
||||
(
|
||||
set::name($lang->task->common),
|
||||
$statData->taskCount
|
||||
),
|
||||
$features['qa'] ? item
|
||||
(
|
||||
set::name($lang->bug->common),
|
||||
$statData->bugCount
|
||||
) : null,
|
||||
);
|
||||
|
||||
|
||||
/* Baseic information. */
|
||||
div
|
||||
(
|
||||
setID('basicBlock'),
|
||||
setClass('hidden'),
|
||||
sectionList
|
||||
(
|
||||
section
|
||||
(
|
||||
setClass('border-b pb-4'),
|
||||
div
|
||||
(
|
||||
label
|
||||
(
|
||||
setClass('text-dark'),
|
||||
$execution->id
|
||||
),
|
||||
!empty($config->setCode) ? label
|
||||
(
|
||||
setClass('dark-outline text-dark mx-2'),
|
||||
$execution->code
|
||||
) : null,
|
||||
span(setClass('article-h2'), $execution->name),
|
||||
),
|
||||
div
|
||||
(
|
||||
set::class('detail-content article-content'),
|
||||
span($execution->desc),
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('mt-4'),
|
||||
$execution->deleted ? label
|
||||
(
|
||||
setClass('danger-outline text-danger'),
|
||||
$lang->execution->deleted
|
||||
) : null,
|
||||
!empty($execution->lifetime) && $execution->type != 'kanban' && $project->model != 'waterfall' && $project->model != 'waterfallplus' ? label
|
||||
(
|
||||
setClass('secondary-outline text-primary'),
|
||||
setClass($execution->deleted ? 'ml-2' : ''),
|
||||
zget($lang->execution->lifeTimeList, $execution->lifetime, '')
|
||||
) : null,
|
||||
isset($execution->delay) ? label
|
||||
(
|
||||
setClass('danger-outline text-danger ml-2'),
|
||||
$lang->execution->delayed
|
||||
) : label
|
||||
(
|
||||
setClass("success-pale ring-success ml-2"),
|
||||
$this->processStatus('execution', $execution)
|
||||
),
|
||||
)
|
||||
),
|
||||
$config->systemMode == 'ALM' ? section
|
||||
(
|
||||
setClass('border-b pb-4'),
|
||||
set::title($lang->project->parent),
|
||||
$programDom
|
||||
) : null,
|
||||
section
|
||||
(
|
||||
setClass('border-b pb-4'),
|
||||
set::title($lang->project->project),
|
||||
div
|
||||
(
|
||||
icon('project mr-2'),
|
||||
html
|
||||
(
|
||||
common::hasPriv('project', 'index') ? html::a
|
||||
(
|
||||
$this->createLink('project', 'index', "projectID={$execution->project}"),
|
||||
$execution->projectInfo->name
|
||||
) : span($execution->projectInfo->name),
|
||||
)
|
||||
)
|
||||
),
|
||||
$execution->projectInfo->hasProduct ? section
|
||||
(
|
||||
setClass('border-b pb-4 linked-products'),
|
||||
set::title($lang->execution->manageProducts),
|
||||
common::hasPriv('execution', 'manageproducts') && $execution->type != 'stage' && $project->model != 'waterfallplus' ? to::actions(btn
|
||||
(
|
||||
setClass('ghost text-gray'),
|
||||
set::url(createLink('execution', 'manageproducts', "projectID={$execution->id}")),
|
||||
$lang->more
|
||||
)) : null,
|
||||
div
|
||||
(
|
||||
setClass('flex flex-wrap'),
|
||||
$productsDom
|
||||
)
|
||||
) : null,
|
||||
$features['plan'] ? section
|
||||
(
|
||||
setClass('border-b pb-4 linked-plans'),
|
||||
set::title($lang->execution->linkPlan),
|
||||
div
|
||||
(
|
||||
setClass('flex flex-wrap'),
|
||||
$plansDom
|
||||
)
|
||||
) : null,
|
||||
section
|
||||
(
|
||||
setClass('border-b pb-4 execution-statitic-hours'),
|
||||
set::title($lang->execution->lblStats),
|
||||
$hoursDom,
|
||||
),
|
||||
section
|
||||
(
|
||||
setClass('border-b pb-4 execution-basic-info'),
|
||||
set::title($lang->execution->basicInfo),
|
||||
$basicInfoDom
|
||||
),
|
||||
section
|
||||
(
|
||||
setClass('pb-4'),
|
||||
set::title($lang->execution->acl),
|
||||
$lang->execution->aclList[$execution->acl]
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
/* ====== Render page ====== */
|
||||
render();
|
||||
Reference in New Issue
Block a user