diff --git a/module/block/css/bugstatisticblock.ui.css b/module/block/css/bugstatisticblock.ui.css new file mode 100644 index 0000000000..82dbad8a52 --- /dev/null +++ b/module/block/css/bugstatisticblock.ui.css @@ -0,0 +1,4 @@ +.bugstatistic-block .progress-circle text {font-size: 35px;} +.bugstatistic-block .progress-circle .progress-text {position: relative; top: -45px;} +.bugstatistic-block .bg-secondary-pale {background: #F3F6FA;} +.bugstatistic-block .tab-pane .nav-tabs>.nav-item>a:after {border-bottom: none;} diff --git a/module/block/css/common.ui.css b/module/block/css/common.ui.css index 269472aa94..18dd0e92b2 100644 --- a/module/block/css/common.ui.css +++ b/module/block/css/common.ui.css @@ -11,3 +11,7 @@ .nav-tabs.nav-stacked .nav-item.nav-next {display: none} .hidden-nowrap {overflow: hidden; white-space: nowrap;} + +.nav > li > .btn {padding: 0 5px; line-height: 24px; margin: 3px; color: #3C4353;} +.nav > li > .btn.btn-primary {color: #fff;} +.nav .icon-more {font-size: 14px;} diff --git a/module/block/lang/de.php b/module/block/lang/de.php index fcd086fe6f..12d42a10d0 100644 --- a/module/block/lang/de.php +++ b/module/block/lang/de.php @@ -585,6 +585,7 @@ $lang->block->qastatistic->resolvedToday = 'Resolved Today'; $lang->block->qastatistic->closedYesterday = 'Closed Yesterday'; $lang->block->qastatistic->closedToday = 'Closed Today'; $lang->block->qastatistic->latestTesttask = 'Latest Testtask'; +$lang->block->qastatistic->bugStatusStat = 'Bug Status Distribution'; $lang->block->executionstatistic = new stdclass(); $lang->block->executionstatistic->progress = 'Progress'; diff --git a/module/block/lang/en.php b/module/block/lang/en.php index 70b6bca796..47df318ebb 100644 --- a/module/block/lang/en.php +++ b/module/block/lang/en.php @@ -585,6 +585,7 @@ $lang->block->qastatistic->resolvedToday = 'Resolved Today'; $lang->block->qastatistic->closedYesterday = 'Closed Yesterday'; $lang->block->qastatistic->closedToday = 'Closed Today'; $lang->block->qastatistic->latestTesttask = 'Latest Testtask'; +$lang->block->qastatistic->bugStatusStat = 'Bug Status Distribution'; $lang->block->executionstatistic = new stdclass(); $lang->block->executionstatistic->progress = 'Progress'; diff --git a/module/block/lang/fr.php b/module/block/lang/fr.php index 68bfcaa682..4e712545b6 100644 --- a/module/block/lang/fr.php +++ b/module/block/lang/fr.php @@ -585,6 +585,7 @@ $lang->block->qastatistic->resolvedToday = 'Resolved Today'; $lang->block->qastatistic->closedYesterday = 'Closed Yesterday'; $lang->block->qastatistic->closedToday = 'Closed Today'; $lang->block->qastatistic->latestTesttask = 'Latest Testtask'; +$lang->block->qastatistic->bugStatusStat = 'Bug Status Distribution'; $lang->block->executionstatistic = new stdclass(); $lang->block->executionstatistic->progress = 'Progress'; diff --git a/module/block/lang/zh-cn.php b/module/block/lang/zh-cn.php index be13cf9a28..e0f2024204 100644 --- a/module/block/lang/zh-cn.php +++ b/module/block/lang/zh-cn.php @@ -599,6 +599,7 @@ $lang->block->qastatistic->resolvedToday = '今日解决'; $lang->block->qastatistic->closedYesterday = '昨日关闭'; $lang->block->qastatistic->closedToday = '今日关闭'; $lang->block->qastatistic->latestTesttask = '近期测试单'; +$lang->block->qastatistic->bugStatusStat = 'Bug状态分布'; $lang->block->executionstatistic = new stdclass(); $lang->block->executionstatistic->allProject = '全部项目'; diff --git a/module/block/ui/bugstatisticblock.html.php b/module/block/ui/bugstatisticblock.html.php index 871fa3c41f..b7ad975476 100644 --- a/module/block/ui/bugstatisticblock.html.php +++ b/module/block/ui/bugstatisticblock.html.php @@ -11,13 +11,288 @@ declare(strict_types=1); namespace zin; +$blockNavCode = 'nav-' . uniqid(); +$navTabs = array(); +$selected = key($products); +$statisticCells = array(); +$preproductID = 0; +$nextproductID = 0; +$productIdList = array_keys($products); +$tabItems = array(); +foreach($products as $productID => $product) +{ + $productInfo = div + ( + setClass('flex' . ($longBlock ? ' flex-nowrap' : ' flex-wrap')), + cell + ( + setClass('flex flex-wrap items-center content-center progress-circle'), + set::width($longBlock ? '30%' : '100%'), + div + ( + setClass('flex justify-center w-full'), + zui::progressCircle + ( + set('percent', zget($resolvedRate, $productID, 0)), + set('size', 112), + set('circleWidth', 6), + set('text', zget($resolvedRate, $productID, 0) . '%'), + set('textY', 50), + set('textStyle', 'font-size: 30px;'), + ), + ), + div + ( + setClass('flex justify-center w-full h-0'), + span + ( + setClass('text-gray text-sm progress-text'), + $lang->block->qastatistic->fixBugRate, + icon + ( + setClass('pl-0.5'), + set('data-toggle', 'tooltip'), + set('href', 'totalProgressTooltip'), + 'help' + ), + ), + div + ( + setClass('tooltip shadow bg-white text-gray leading-6'), + set::id('totalProgressTooltip'), + 'fixBugRateTip' + ), + ), + cell + ( + setClass('flex justify-center w-full mt-3 gap-x-4'), + col + ( + span + ( + setClass('flex justify-center'), + zget($totalBugs, $productID, 0), + ), + span + ( + setClass('text-sm text-gray'), + $lang->block->qastatistic->totalBug + ), + ), + col + ( + span + ( + setClass('flex justify-center'), + zget($closedBugs, $productID, 0), + ), + span + ( + setClass('text-sm text-gray'), + $lang->block->productstatistic->closed + ), + ), + col + ( + span + ( + setClass('flex justify-center'), + zget($unresovledBugs, $productID, 0), + ), + span + ( + setClass('text-sm text-gray'), + $lang->bug->unResolved + ), + ), + ), + ), + cell + ( + setClass('p-4' . ($longBlock ? ' mt-3' : ' pb-0')), + set::width($longBlock ? '70%' : '100%'), + echarts + ( + set::title(array('text' => $lang->block->qastatistic->bugStatusStat, 'textStyle' => array('fontSize' => '12'))), + set::color(array('#66a2ff', '#7adfba', '#9ea3b0')), + set::tooltip(array('trigger' => 'axis')), + set::grid(array('left' => '10px', 'top' => '50px', 'right' => '0', 'bottom' => '0', 'containLabel' => true)), + set::legend(array('show' => true, 'right' => '0', 'top' => '25px', 'textStyle' => array('fontSize' => '11'))), + set::xAxis(array('type' => 'category', 'data' => $months, 'splitLine' => array('show' => false), 'axisTick' => array('alignWithLabel' => true, 'interval' => 0), 'axisLabel' => array('fontSize' => $longBlock ? '8' : '10'))), + set::yAxis(array('type' => 'value', 'splitLine' => array('show' => false), 'axisLine' => array('show' => true, 'color' => '#DDD'), 'axisLabel' => array('showMaxLabel' => true, 'interval' => 'auto'))), + set::tooptop(array('show' => true, 'formatter' => '{b}: {c}')), + set::series + ( + array + ( + array + ( + 'type' => 'bar', + 'barWidth' => '8', + 'stack' => 'Ad', + 'name' => $lang->bug->activate, + 'data' => array_values($activateBugs[$productID]), + ), + array + ( + 'type' => 'bar', + 'name' => $lang->bug->resolve, + 'stack' => 'Ad', + 'data' => array_values($resolveBugs[$productID]), + ), + array + ( + 'type' => 'bar', + 'name' => $lang->bug->close, + 'stack' => 'Ad', + 'data' => array_values($closeBugs[$productID]), + ), + ), + ), + )->size('100%', 200), + ), + ); + + if($longBlock) + { + $navTabs[] = li + ( + setClass('nav-item'), + a + ( + setClass('ellipsis title ' . ($product->id == $selected ? ' active' : '')), + set('data-toggle', 'tab'), + set::href("#tab{$blockNavCode}Content{$product->id}"), + $product->name + + ), + a + ( + setClass('link flex-1 text-right hidden'), + set::href(helper::createLink('product', 'browse', "productID={$product->id}")), + icon + ( + setClass('rotate-90 text-primary'), + 'export' + ) + ) + ); + + $tabItems[] = div + ( + setClass('tab-pane' . ($product->id == $selected ? ' active' : '')), + set('id', "tab{$blockNavCode}Content{$product->id}"), + $productInfo, + ); + } + else + { + $index = array_search($product->id, $productIdList); + $nextProductID = $index !== false && !empty($productIdList[$index + 1]) ? $productIdList[$index + 1] : 0; + + $tabItems = array(); + $tabItems[] = cell + ( + ul + ( + setClass('nav nav-tabs h-10 px-1 justify-between items-center'), + set::width('100%'), + li + ( + setClass('nav-item'), + btn + ( + setClass('size-sm shadow-lg circle pre-button'), + set::square(true), + set::disabled(empty($preProductID)), + set::href("#tab{$blockNavCode}Content{$preProductID}"), + set('data-toggle', 'tab'), + set::iconClass('text-xl text-primary'), + set::icon('angle-left'), + ), + ), + li + ( + setClass('nav-item px-4'), + hasPriv('product', 'browse') ? btn + ( + setClass('ghost'), + set::url(createLink('product', 'browse', "productID={$product->id}")), + span + ( + setClass('text-primary'), + $product->name + ), + icon + ( + setClass('text-primary ml-4 rotate-90'), + 'export' + ), + ) : $product->name, + ), + li + ( + setClass('nav-item'), + btn + ( + setClass('size-sm shadow-lg circle next-button'), + set::square(true), + set::disabled(empty($nextProductID)), + set::href("#tab{$blockNavCode}Content{$nextProductID}"), + set('data-toggle', 'tab'), + set::iconClass('text-xl text-primary'), + set::icon('angle-right'), + ), + ), + ), + ); + $tabItems[] = cell + ( + setClass('tab-content pt-1'), + set::width('100%'), + div + ( + $productInfo, + ), + ); + $statisticCells[] = cell + ( + setClass('tab-pane pt-1 w-full' . ($product->id == $selected ? ' active' : '')), + set('id', "tab{$blockNavCode}Content{$product->id}"), + $tabItems, + ); + $preProductID = $product->id; + } +} +if($longBlock) +{ + $statisticCells[] = cell + ( + set::width('22%'), + setClass('bg-secondary-pale overflow-y-auto overflow-x-hidden'), + ul + ( + setClass('nav nav-tabs nav-stacked'), + $navTabs, + ), + ); + $statisticCells[] = cell + ( + setClass('tab-content'), + set::width('78%'), + $tabItems, + ); +} + panel ( - set('headingClass', 'border-b'), + setClass('bugstatistic-block ' . ($longBlock ? 'block-long' : 'block-sm')), + set::bodyClass('no-shadow border-t p-0'), set::title($block->title), div ( - '正在开发中...' + setClass('flex h-full' . (!$longBlock ? ' flex-wrap' : '')), + $statisticCells, ) ); diff --git a/module/block/zen.php b/module/block/zen.php index 98d45dd7a5..f7f6900f66 100644 --- a/module/block/zen.php +++ b/module/block/zen.php @@ -2313,6 +2313,71 @@ class blockZen extends block $this->view->maxBugCount = max($resolvedBugCount); } + /** + * Print bug statistic block. + * + * @param object $block + * @access protected + * @return void + */ + protected function printBugStatisticBlock(object $block) + { + $this->app->loadClass('pager', true); + $count = isset($block->params->count) ? (int)$block->params->count : 0; + $type = isset($block->params->type) ? $block->params->type : ''; + $pager = pager::init(0, $count , 1); + + $today = strtotime(helper::today()); + $begin = strtotime(date('Y-m', strtotime('+2 month', $today))); + $end = strtotime(date('Y-m', $today)); + $begin = strtotime('2023-09'); + $end = strtotime('2024-02'); + + $months = array(); + $products = $this->loadModel('product')->getList(0, $block->params->type); + $totalBugs = array(); + $closedBugs = array(); + $unresovledBugs = array(); + $resolvedRate = array(); + $activateBugs = array(); + $resolveBugs = array(); + $closeBugs = array(); + $products = $this->loadModel('product')->getList(0, $type); + foreach($products as $productID => $product) + { + $closedBugs[$productID] = rand(10, 10000); + $unresovledBugs[$productID] = rand(10, 1000); + $totalBugs[$productID] = rand(100, 10000); + $resolvedRate[$productID] = rand(1, 100); + for($date = $begin; $date <= $end; $date = strtotime('+1 month', $date)) + { + $month = date('Y-m', $date); + $activateBugs[$productID][$month] = rand(100, 400); + $resolveBugs[$productID][$month] = rand(100, 400); + $closeBugs[$productID][$month] = rand(100, 400); + + $month = (int)ltrim(date('m', $date), '0'); + + $monthName = in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? "{$month}{$this->lang->block->month}" : zget($this->lang->datepicker->monthNames, $month - 1, ''); + if($month == 1) $monthName .= "\n" . date('Y', $date) . (in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? $this->lang->year : ''); + + if(count($closedBugs) == 1) $months[] = $monthName; + } + } + + $this->app->loadLang('bug'); + + $this->view->months = $months; + $this->view->products = $products; + $this->view->totalBugs = $totalBugs; + $this->view->closedBugs = $closedBugs; + $this->view->unresovledBugs = $unresovledBugs; + $this->view->resolvedRate = $resolvedRate; + $this->view->activateBugs = $activateBugs; + $this->view->resolveBugs = $resolveBugs; + $this->view->closeBugs = $closeBugs; + } + /** * 判断是否为内部调用。 * Check request client is chandao or not.