| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- <?php
- /**
- * 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;
- include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php');
- $progress = ($execution->totalConsumed + $execution->totalLeft) ? floor($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft) * 1000) / 1000 * 100 : 0;
- $isKanban = isset($execution->type) && $execution->type == 'kanban';
- $chartURL = createLink('execution', $isKanban ? 'ajaxGetCFD' : 'ajaxGetBurn', "executionID={$execution->id}");
- $isEn = $app->getClientLang() == 'en';
- $storyTitle = $isEn ? $lang->common->stories : $lang->common->story;
- $taskTitle = $isEn ? $lang->task->plural : $lang->task->common;
- $bugTitle = $isEn ? $lang->bug->plural : $lang->bug->common;
- /* Construct suitable actions for the current execution. */
- $execution->rawID = $execution->id;
- $programDom = null;
- if($config->systemMode == 'ALM' && isset($execution->projectInfo->grade) && $execution->projectInfo->grade > 1 && helper::hasFeature('program'))
- {
- foreach($programList as $programID => $name)
- {
- if(common::hasPriv('program', 'product'))
- {
- $programLink = $this->createLink('program', 'product', "programID={$programID}");
- $programList[$programID] = "<a href='{$programLink}' title='{$name}'>{$name}</a>";
- }
- else
- {
- $programList[$programID] = "<span title='{$name}'>{$name}</span>";
- }
- }
- $programDom = div
- (
- icon('program mr-2'),
- html(implode('/ ', $programList))
- );
- }
- $relatedProducts = null;
- if(!empty($execution->projectInfo->hasProduct) || $features['plan'])
- {
- foreach($products as $productID => $product)
- {
- $productDom = null;
- $planDom = null;
- $branches = array();
- if(!empty($execution->projectInfo->hasProduct))
- {
- foreach($product->branches as $branchID)
- {
- $branchName = isset($branchGroups[$productID][$branchID]) ? '/' . $branchGroups[$productID][$branchID] : '';
- $branches[] = div(
- setClass('flex clip w-full items-center'),
- icon('product mr-2'),
- a
- (
- setClass('flex'),
- set::title($product->name . $branchName),
- hasPriv('product', 'browse') ? set::href(createLink('product', 'browse', "productID={$productID}&branch={$branchID}")) : null,
- span
- (
- setClass('flex-1'),
- setStyle('width', '0'),
- $product->name . $branchName
- )
- )
- );
- }
- $productDom = h::td
- (
- div
- (
- setClass('flex flex-wrap'),
- $branches
- )
- );
- }
- if($features['plan'])
- {
- $plans = array();
- foreach($product->plans as $planIDList)
- {
- $planIDList = explode(',', $planIDList);
- $planIDList = array_filter($planIDList);
- foreach($planIDList as $planID)
- {
- if(!isset($planGroups[$productID][$planID])) continue;
- $class = 'clip';
- if(count($planIDList) <= 2) $class .= ' flex flex-1 w-0 items-center';
- if(count($planIDList) > 2) $class .= ' flex-none w-1/3';
- if(count($plans) > 2) $class .= ' mt-2';
- if(count($plans) % 3 != 0) $class .= ' pl-6';
- $plans[] = div
- (
- setClass($class),
- icon('calendar mr-2'),
- a
- (
- set::title($product->name . '/' . $planGroups[$productID][$planID]),
- set('data-app', $execution->projectInfo->hasProduct ? '' : 'project'),
- hasPriv('productplan', 'view') ? set::href(createLink('productplan', 'view', "planID={$planID}")) : null,
- span($product->name . '/' . $planGroups[$productID][$planID])
- )
- );
- }
- }
- $planDom[] = h::td
- (
- div
- (
- setClass('flex flex-wrap'),
- $plans
- )
- );
- }
- $relatedProducts[] = h::tr(setClass('border-r'), $productDom, $planDom);
- }
- }
- div
- (
- setClass('flex w-full'),
- div
- (
- setClass('flex-auto canvas flex p-4 w-1/2'),
- div
- (
- setClass('text-center w-1/3 flex flex-col justify-center items-center'),
- div
- (
- set('class', 'chart pie-chart'),
- echarts
- (
- set::color(array('#2B80FF', '#E3E4E9')),
- set::width(120),
- set::height(120),
- set::series
- (
- array
- (
- array
- (
- 'type' => 'pie',
- 'radius' => array('80%', '90%'),
- 'itemStyle' => array('borderRadius' => '40'),
- 'label' => array('show' => false),
- 'data' => array($progress, 100 - $progress)
- )
- )
- )
- ),
- div
- (
- set::className('pie-chart-title text-center'),
- div(span(set::className('text-2xl font-bold'), $progress . '%')),
- div
- (
- span
- (
- setClass('text-sm text-gray'),
- $lang->allProgress,
- icon
- (
- 'help ml-1',
- toggle::tooltip(array('title' => $lang->execution->progressTip)),
- set('data-placement', 'bottom'),
- set('data-type', 'white'),
- set('data-class-name', 'text-gray border border-light'),
- setClass('text-gray')
- )
- )
- )
- )
- ),
- div
- (
- setClass('border w-3/4 flex justify-center items-center pl-4 py-2'),
- $features['story'] ? div
- (
- setClass('w-1/3'),
- div
- (
- setClass('text-lg font-bold'),
- $statData->storyCount
- ),
- $storyTitle
- ) : null,
- div
- (
- setClass('w-1/3'),
- div
- (
- setClass('text-lg font-bold'),
- $statData->taskCount
- ),
- $taskTitle
- ),
- $features['qa'] ? div
- (
- setClass('w-1/3'),
- div
- (
- setClass('text-lg font-bold'),
- $statData->bugCount
- ),
- $bugTitle
- ) : null
- )
- ),
- div
- (
- setClass('flex-none w-2/3'),
- div
- (
- setClass('flex items-center'),
- label
- (
- setClass('rounded-full'),
- $execution->id
- ),
- div
- (
- setClass('text-md font-bold ml-2 clip'),
- set::title($execution->name),
- $execution->name
- ),
- !empty($config->setCode) && !empty($execution->code) ? label
- (
- setClass('gray-300-outline mx-2 flex-none'),
- $execution->code
- ) : null,
- $execution->deleted ? label
- (
- setClass('danger-outline text-dange flex-noner'),
- $lang->execution->deleted
- ) : null,
- isset($execution->delay) ? label
- (
- setClass('danger-pale circle size-sm nowrap ml-2 flex-none'),
- sprintf($lang->project->delayInfo, $execution->delay)
- ) : label
- (
- setClass("status-{$execution->status} ml-2 flex-none"),
- $this->processStatus('execution', $execution)
- ),
- span
- (
- setClass('ml-2 flex-none mb-1'),
- $lang->execution->kanbanAclList[$execution->acl],
- icon
- (
- 'help',
- toggle::tooltip(array('title' => $lang->execution->aclList[$execution->acl])),
- set('data-placement', 'right'),
- set('data-type', 'white'),
- set('data-class-name', 'text-gray border border-light'),
- setClass('ml-2 mt-2 text-gray')
- )
- ),
- div(setClass('ai-menu-box flex-auto'))
- ),
- div
- (
- setClass('flex mt-4'),
- $programDom ? div
- (
- setClass('clip w-1/2'),
- $programDom
- ) : null,
- div
- (
- setClass('clip w-1/2'),
- icon('project mr-1'),
- common::hasPriv('project', 'index') ? a
- (
- setClass('clip'),
- set::href($this->createLink('project', 'index', "projectID={$execution->project}")),
- set::title(empty($execution->projectInfo->name) ? '' : $execution->projectInfo->name),
- empty($execution->projectInfo->name) ? '' : $execution->projectInfo->name
- ) : span
- (
- setClass('clip w-full'),
- set::title(empty($execution->projectInfo->name) ? '' : $execution->projectInfo->name),
- empty($execution->projectInfo->name) ? '' : $execution->projectInfo->name
- )
- )
- ),
- div
- (
- set::className('detail-content mt-4 overflow-hidden desc-box'),
- set::title(strip_tags($execution->desc)),
- html($execution->desc)
- )
- )
- ),
- div
- (
- setClass('flex-none w-1/3 canvas ml-4'),
- $isKanban ? to::heading
- (
- div
- (
- setClass('panel-title nowrap overflow-hidden'),
- set::title($execution->name . $lang->execution->CFD),
- $execution->name . $lang->execution->CFD
- )
- ) : null,
- div
- (
- setID('chartLine'),
- on::init()->call('loadTarget', $chartURL, '#chartLine')
- )
- )
- );
- $membersDom = array();
- foreach(array('PM', 'PO', 'QD', 'RD') as $field)
- {
- if(empty($execution->$field)) continue;
- $user = isset($userList[$execution->$field]) ? $userList[$execution->$field] : null;
- if(empty($user)) continue;
- $membersDom[] = div
- (
- setClass('w-1/6 center-y'),
- avatar
- (
- setClass('primary-outline'),
- set::size('36'),
- set::text($user->realname),
- set::src($user->avatar)
- ),
- span(setClass('my-2'), $user->realname),
- span(setClass('text-gray'), $lang->execution->$field)
- );
- unset($teamMembers[$execution->$field]);
- }
- $memberCount = count($membersDom);
- foreach($teamMembers as $teamMember)
- {
- if($memberCount >= 7) break;
- $user = isset($userList[$teamMember->account]) ? $userList[$teamMember->account] : null;
- if(!$user) continue;
- $membersDom[] = div
- (
- setClass('w-1/6 center-y'),
- avatar
- (
- set::size('36'),
- set::text($user->realname),
- set::src($user->avatar)
- ),
- span
- (
- setClass('my-2'),
- $user->realname
- ),
- span
- (
- setClass('text-gray'),
- $lang->execution->member
- )
- );
- $memberCount ++;
- }
- if(common::hasPriv('execution', 'manageMembers'))
- {
- $membersDom[] = a
- (
- set::href(createLink('execution', 'manageMembers', "executionID={$execution->id}")),
- setClass('w-1/6 center-y'),
- avatar
- (
- setClass('mb-2'),
- set::size('36'),
- set::foreColor('var(--color-primary-500-rgb)'),
- set::background('var(--color-primary-50)'),
- icon('plus')
- ),
- $lang->project->manage
- );
- }
- $docLibDom = array();
- if(common::hasPriv('execution', 'doc') && $execution->type != 'kanban')
- {
- $docLibCount = 0;
- foreach($docLibs as $libID => $docLib)
- {
- if($docLibCount > 4) break;
- $docLibDom[] = div
- (
- setClass('flex-none w-1/5 py-1 clip pl-4'),
- icon('wiki-lib mr-1'),
- a
- (
- $docLib->name,
- set('data-app', $app->tab == 'search' ? 'execution' : $app->tab),
- set::href($libID == 'files' ? $this->createLink('doc', 'showFiles', "type=execution&objectID={$execution->id}") : $this->createLink('execution', 'doc', "objectID={$execution->id}&libID={$libID}"))
- )
- );
- $docLibCount ++;
- }
- }
- if($canBeChanged && common::hasPriv('doc', 'createLib') && $execution->type != 'kanban')
- {
- $docLibDom[] = div
- (
- setClass('flex-none w-1/5 py-1 pl-4'),
- a
- (
- setClass('ghost text-gray'),
- icon('plus', setClass('bg-primary-50 text-primary mr-2')),
- span($lang->doc->createLib),
- set::href(createLink('doc', 'createLib', "type=execution&objectID={$execution->id}")),
- set('data-toggle', 'modal'),
- set('data-app', $app->tab)
- )
- );
- }
- div
- (
- setClass('my-4 flex w-full items-start'),
- div
- (
- setClass('w-2/3 canvas p-4 flex-auto'),
- div
- (
- /* Linked product and plan. */
- (!empty($execution->projectInfo->hasProduct) || $features['plan']) ? h::table
- (
- setClass('table condensed bordered'),
- setID('products'),
- h::thead
- (
- h::tr
- (
- !empty($execution->projectInfo->hasProduct) ? h::th
- (
- setClass('w-1/3'),
- div
- (
- setClass('flex items-center justify-between'),
- div
- (
- setClass('flex'),
- h::img(set::src('static/svg/product.svg'), setClass('mr-2')),
- $lang->execution->manageProducts
- ),
- common::hasPriv('execution', 'manageproducts') && $execution->type != 'stage' && $project->model != 'waterfallplus' ? btn
- (
- setClass('ghost text-gray font-normal'),
- set::trailingIcon('caret-right pb-0.5'),
- set::url(createLink('execution', 'manageproducts', "projectID={$execution->id}")),
- $lang->more
- ) : null
- )
- ) : null,
- $features['plan'] ? h::th
- (
- div
- (
- setClass('flex'),
- h::img(set::src('static/svg/productplan.svg'), setClass('mr-2')),
- $lang->execution->linkPlan
- )
- ) : null
- )
- ),
- h::tbody($relatedProducts)
- ) : null,
- /* Execution team. */
- h::table
- (
- setClass('table condensed bordered mt-4'),
- h::thead
- (
- h::tr
- (
- h::th
- (
- div
- (
- setClass('flex items-center justify-between'),
- span($lang->execution->team),
- hasPriv('execution', 'team') ? btn
- (
- setClass('ghost text-gray font-normal'),
- set::trailingIcon('caret-right pb-0.5'),
- set::url(createLink('execution', 'team', "executionID={$execution->id}")),
- $lang->more
- ) : null
- )
- )
- )
- ),
- h::tbody
- (
- h::tr
- (
- h::td(div(setClass('flex flex-wrap member-list pt-2'), $membersDom))
- )
- )
- ),
- /* Estimate statistics. */
- h::table
- (
- setClass('table condensed bordered mt-4'),
- h::thead
- (
- h::tr
- (
- h::th(div(setClass('flex items-center justify-between'), span($lang->execution->DurationStats)))
- )
- ),
- h::tbody
- (
- h::tr
- (
- h::td
- (
- div
- (
- setClass('flex flex-wrap pt-2 mx-4'),
- div
- (
- setClass('w-1/4'),
- span(setClass('text-gray'), $lang->execution->begin),
- span(setClass('ml-2'), $execution->begin)
- ),
- div
- (
- setClass('w-1/4'),
- span(setClass('text-gray'), $lang->execution->end),
- span(setClass('ml-2'), $execution->end)
- ),
- div
- (
- setClass('w-1/4'),
- span(setClass('text-gray'), $lang->execution->realBeganAB),
- span(setClass('ml-2'), helper::isZeroDate($execution->realBegan) ? '' : $execution->realBegan)
- ),
- div
- (
- setClass('w-1/4'),
- span(setClass('text-gray'), $lang->execution->realEndAB),
- span(setClass('ml-2'), helper::isZeroDate($execution->realEnd) ? '' : $execution->realEnd)
- )
- )
- )
- )
- )
- ),
- h::table
- (
- setClass('table condensed bordered mt-4'),
- h::thead
- (
- h::tr
- (
- h::th(div(setClass('flex items-center justify-between'), span($lang->execution->lblStats)))
- )
- ),
- h::tbody
- (
- h::tr
- (
- h::td
- (
- div
- (
- setClass('flex flex-wrap pt-2 mx-4'),
- div
- (
- setClass('w-1/3'),
- span(setClass('text-gray'), $lang->execution->estimateHours),
- span(setClass('ml-2'), helper::formatHours($execution->totalEstimate) . $lang->execution->workHourUnit)
- ),
- div
- (
- setClass('w-1/3'),
- span(setClass('text-gray'), $lang->execution->consumedHours),
- span(setClass('ml-2'), helper::formatHours($execution->totalConsumed) . $lang->execution->workHourUnit)
- ),
- div
- (
- setClass('w-1/3'),
- span(setClass('text-gray'), $lang->execution->leftHours),
- span(setClass('ml-2'), helper::formatHours($execution->totalLeft) . $lang->execution->workHourUnit)
- ),
- div
- (
- setClass('w-1/3 mt-4'),
- span(setClass('text-gray'), $lang->execution->totalDays),
- span(setClass('ml-2'), $execution->days)
- ),
- div
- (
- setClass('w-1/3 mt-4'),
- span(setClass('text-gray'), $lang->execution->totalHours),
- span(setClass('ml-2'), $execution->totalHours . $lang->execution->workHourUnit)
- )
- )
- )
- )
- )
- ),
- /* Execution doc lib. */
- hasPriv('execution', 'doc') && !$isKanban ? h::table
- (
- setClass('table condensed bordered mt-4'),
- h::thead
- (
- h::tr
- (
- h::th
- (
- div
- (
- setClass('flex items-center justify-between'),
- span($lang->execution->doclib),
- hasPriv('execution', 'doc') ? btn
- (
- setClass('ghost text-gray font-normal'),
- set::trailingIcon('caret-right pb-0.5'),
- set::url(createLink('execution', 'doc', "executionID={$execution->id}")),
- $lang->more
- ) : null
- )
- )
- )
- ),
- h::tbody
- (
- h::tr
- (
- h::td(div(setClass('flex flex-wrap pt-2'), $docLibDom))
- )
- )
- ) : null,
- html($this->printExtendFields($execution, 'html', 'position=info', false))
- )
- ),
- div
- (
- setClass('ml-4 w-1/3 flex-none'),
- panel
- (
- to::heading
- (
- div(set('class', 'panel-title'), $lang->execution->latestDynamic)
- ),
- to::headingActions
- (
- common::hasPriv('execution', 'dynamic') ? btn
- (
- setClass('ghost text-gray font-normal'),
- set::url(createLink('execution', 'dynamic', "executionID={$execution->id}&type=all")),
- $lang->more
- ) : null
- ),
- set::bodyClass('h-80 overflow-y-auto pt-0'),
- set::shadow(false),
- dynamic()
- ),
- html($this->printExtendFields($execution, 'html', 'position=basic', false)),
- div
- (
- setClass('mt-4'),
- history
- (
- set::commentUrl(createLink('action', 'comment', array('objectType' => 'execution', 'objectID' => $execution->id))),
- set::bodyClass('maxh-72 overflow-y-auto')
- )
- )
- )
- );
- $actions = $this->loadModel('common')->buildOperateMenu($execution);
- if($config->systemMode == 'PLM' && $config->edition == 'ipd')
- {
- $canStart = $this->execution->checkStageStatus($execution->id, 'start');
- $canClose = $this->execution->checkStageStatus($execution->id, 'close');
- foreach($actions['mainActions'] as $key => $action)
- {
- if(isset($action['icon']) && $action['icon'] == 'start' && $canStart['disabled']) unset($actions['mainActions'][$key]);
- if(isset($action['icon']) && $action['icon'] == 'off' && $canClose['disabled']) unset($actions['mainActions'][$key]);
- }
- }
- div
- (
- setClass('w-2/3 center fixed actions-menu'),
- floatToolbar
- (
- isAjaxRequest('modal') ? null : to::prefix(backBtn(set::icon('back'), $lang->goback)),
- set::main($actions['mainActions']),
- set::suffix($actions['suffixActions']),
- set::object($execution)
- )
- );
|