332 lines
12 KiB
PHP
332 lines
12 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
/**
|
|
* The UI file of product 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 chen.tao <chentao@easycorp.ltd>
|
|
* @package product
|
|
* @link https://www.zentao.net
|
|
*/
|
|
|
|
namespace zin;
|
|
|
|
include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php');
|
|
|
|
/* Flag variable for hiding product code. */
|
|
$hiddenCode = (!isset($config->setCode) || $config->setCode == 0 || empty($product->code));
|
|
$allStoryCount = array_sum($product->stories);
|
|
$isEn = $app->getClientLang() == 'en';
|
|
$storiesClass = $isEn ? 'px-2' : 'w-1/3';
|
|
|
|
$membersDom = array();
|
|
foreach($config->product->memberFields as $field)
|
|
{
|
|
if(empty($product->$field)) continue;
|
|
if(!isset($members[$product->$field])) continue;
|
|
|
|
$user = $members[$product->$field];
|
|
$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->product->$field)
|
|
);
|
|
}
|
|
|
|
div
|
|
(
|
|
setClass('flex w-full'),
|
|
cell
|
|
(
|
|
setClass('mr-3 w-2/3'),
|
|
div
|
|
(
|
|
setClass('flex-auto canvas flex p-4'),
|
|
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($product->storyDeliveryRate, 100 - $product->storyDeliveryRate)))
|
|
)
|
|
),
|
|
div
|
|
(
|
|
set::className('pie-chart-title text-center'),
|
|
div(span(set::className('text-2xl font-bold'), $product->storyDeliveryRate . '%')),
|
|
div
|
|
(
|
|
span
|
|
(
|
|
setClass('text-sm text-gray'),
|
|
$lang->product->storyDeliveryRate,
|
|
icon
|
|
(
|
|
'help ml-1',
|
|
toggle::tooltip(array('title' => $lang->product->storyDeliveryRateTip)),
|
|
set('data-placement', 'right'),
|
|
set('data-type', 'white'),
|
|
set('data-class-name', 'text-gray border border-light'),
|
|
setClass('text-gray ')
|
|
)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
div
|
|
(
|
|
setClass('border flex justify-center items-center pl-4 py-2' . ($isEn ? '' : ' w-3/4')),
|
|
div
|
|
(
|
|
setClass($storiesClass),
|
|
div
|
|
(
|
|
setClass('text-lg font-bold'),
|
|
$allStoryCount
|
|
),
|
|
$lang->product->totalStories
|
|
),
|
|
div
|
|
(
|
|
setClass($storiesClass),
|
|
div
|
|
(
|
|
setClass('text-lg font-bold'),
|
|
$product->stories['closed']
|
|
),
|
|
$lang->story->statusList['closed']
|
|
),
|
|
div
|
|
(
|
|
setClass($storiesClass),
|
|
div
|
|
(
|
|
setClass('text-lg font-bold'),
|
|
$allStoryCount - $product->stories['closed']
|
|
),
|
|
$lang->story->unclosed
|
|
)
|
|
)
|
|
),
|
|
div
|
|
(
|
|
setClass('flex-none w-2/3'),
|
|
div
|
|
(
|
|
setClass('flex items-center'),
|
|
label
|
|
(
|
|
setClass('rounded-full'),
|
|
$product->id
|
|
),
|
|
div
|
|
(
|
|
setClass('text-md font-bold ml-2 clip'),
|
|
$product->name
|
|
),
|
|
!$hiddenCode ? label
|
|
(
|
|
setClass('gray-300-outline ml-2 flex-none'),
|
|
$product->code
|
|
) : null,
|
|
$product->type != 'normal' ? label
|
|
(
|
|
setClass('gray-300-outline ml-2 text-warning flex-none'),
|
|
$lang->product->typeList[$product->type]
|
|
) : null,
|
|
$product->deleted ? label
|
|
(
|
|
setClass('danger-outline text-dange flex-noner ml-2'),
|
|
$lang->product->deleted
|
|
) : null,
|
|
label
|
|
(
|
|
setClass("ml-2 flex-none"),
|
|
setClass($product->status == 'normal' ? 'text-success' : 'text-gray'),
|
|
$this->processStatus('product', $product)
|
|
),
|
|
div
|
|
(
|
|
setClass('ml-2 flex-none row items-center gap-2'),
|
|
$lang->product->abbr->aclList[$product->acl],
|
|
icon
|
|
(
|
|
'help',
|
|
toggle::tooltip(array('title' => $lang->product->aclList[$product->acl])),
|
|
set('data-placement', 'right'),
|
|
set('data-type', 'white'),
|
|
set('data-class-name', 'text-gray border border-light'),
|
|
setClass('text-gray')
|
|
)
|
|
),
|
|
div(setClass('ai-menu-box flex-auto'))
|
|
),
|
|
div
|
|
(
|
|
setClass('flex mt-4'),
|
|
in_array($this->config->systemMode, array('ALM', 'PLM')) && helper::hasFeature('program') && $product->program ? div
|
|
(
|
|
setClass('clip w-1/3'),
|
|
set::title($lang->product->program),
|
|
icon('program', setClass('pr-1')),
|
|
$product->programName
|
|
) : null,
|
|
$product->line ? div
|
|
(
|
|
setClass('clip w-1/3'),
|
|
set::title($lang->product->line),
|
|
icon('lane', setClass('pr-1')),
|
|
$product->lineName
|
|
) : null,
|
|
$config->edition != 'open' && $product->workflowGroup ? div
|
|
(
|
|
setClass('clip w-1/3'),
|
|
set::title($lang->product->workflowGroup),
|
|
icon('flow', setClass('pr-1')),
|
|
zget($workflowGroups, $product->workflowGroup)
|
|
) : null
|
|
),
|
|
div
|
|
(
|
|
set::className('detail-content mt-4 overflow-hidden desc-box'),
|
|
set::title(strip_tags($product->desc)),
|
|
html($product->desc)
|
|
)
|
|
)
|
|
),
|
|
div
|
|
(
|
|
setClass('mt-4 p-4 bg-white'),
|
|
panel
|
|
(
|
|
setClass('mb-4 memberBox'),
|
|
set::title($lang->product->manager),
|
|
div(setClass('flex flex-wrap member-list pt-2'), $membersDom)
|
|
),
|
|
panel
|
|
(
|
|
setClass('otherInfoBox'),
|
|
set::title($lang->product->otherInfo),
|
|
div
|
|
(
|
|
setClass('flex flex-wrap'),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->plans),
|
|
span(setClass('ml-2'), $product->plans)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->releases),
|
|
span(setClass('ml-2'), $product->releases)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->bugs),
|
|
span(setClass('ml-2'), $product->bugs)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->projects),
|
|
span(setClass('ml-2'), $product->projects)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->builds),
|
|
span(setClass('ml-2'), $product->builds)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->docs),
|
|
span(setClass('ml-2'), $product->docs)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->cases),
|
|
span(setClass('ml-2'), $product->cases)
|
|
),
|
|
div
|
|
(
|
|
setClass('w-1/4 item mb-3'),
|
|
span(setClass('text-gray'), $lang->product->executions),
|
|
span(setClass('ml-2'), $product->executions)
|
|
)
|
|
)
|
|
),
|
|
html($this->printExtendFields($product, 'html', "position=info", false))
|
|
)
|
|
),
|
|
cell
|
|
(
|
|
setClass('w-1/3'),
|
|
panel
|
|
(
|
|
to::heading
|
|
(
|
|
div(set('class', 'panel-title'), $lang->execution->latestDynamic)
|
|
),
|
|
to::headingActions
|
|
(
|
|
common::hasPriv('product', 'dynamic') ? btn
|
|
(
|
|
setClass('ghost text-gray font-normal'),
|
|
set::url(createLink('product', 'dynamic', "productID={$product->id}&type=all")),
|
|
$lang->more
|
|
) : null
|
|
),
|
|
set::bodyClass('h-80 overflow-y-auto pt-0'),
|
|
set::shadow(false),
|
|
dynamic()
|
|
),
|
|
html($this->printExtendFields($product, 'html', "position=basic", false)),
|
|
div
|
|
(
|
|
setClass('mt-4'),
|
|
history
|
|
(
|
|
set::commentUrl(createLink('action', 'comment', array('objectType' => 'product', 'objectID' => $product->id))),
|
|
set::bodyClass('maxh-72 overflow-y-auto')
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
$actionMenuList = !$product->deleted ? $this->loadModel('common')->buildOperateMenu($product) : array();
|
|
|
|
div
|
|
(
|
|
setClass('w-2/3 center fixed actions-menu'),
|
|
setClass($product->deleted ? 'no-divider' : ''),
|
|
floatToolbar
|
|
(
|
|
isAjaxRequest('modal') ? null : to::prefix(backBtn(set::icon('back'), $lang->goback)),
|
|
!empty($actionMenuList['mainActions']) ? set::main($actionMenuList['mainActions']) : null,
|
|
!empty($actionMenuList['suffixActions']) ? set::suffix($actionMenuList['suffixActions']) : null,
|
|
set::object($product)
|
|
)
|
|
);
|