diff --git a/module/product/control.php b/module/product/control.php index 3f469d1cd1..23e7b7d78d 100755 --- a/module/product/control.php +++ b/module/product/control.php @@ -794,135 +794,71 @@ class product extends control * @access public * @return void */ - public function export($status, $orderBy) + public function export(string $status, string $orderBy) { if($_POST) { - $productLang = $this->lang->product; - $productConfig = $this->config->product; + /* 获取导出字段和数据。 */ + $fields = $this->productZen->getExportFields(); + $productStats = $this->productZen->getExportData($status, $orderBy); + $rowspan = $this->productZen->getExportRowspan($productStats); - /* Create field lists. */ - $fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $productConfig->list->exportFields); - foreach($fields as $key => $fieldName) + /* 如果只导出选中产品,删除非选中产品。 */ + if($this->post->exportType == 'selected') { - $fieldName = trim($fieldName); - $fields[$fieldName] = zget($productLang, $fieldName); - - unset($fields[$key]); - if($this->config->systemMode == 'light' and ($fieldName == 'line' or $fieldName == 'program')) unset($fields[$fieldName]); - } - - $lastProgram = $lastLine = ''; - $lines = $this->product->getLinePairs(); - $users = $this->user->getPairs('noletter'); - $productStats = $this->product->getStats($orderBy, null, $status); - foreach($productStats as $i => $product) - { - $product->line = zget($lines, $product->line, ''); - $product->manager = zget($users, $product->PO, ''); - $product->draftStories = (int)$product->stories['draft']; - $product->activeStories = (int)$product->stories['active']; - $product->changedStories = (int)$product->stories['changing']; - $product->reviewingStories = (int)$product->stories['reviewing']; - $product->closedStories = (int)$product->stories['closed']; - $product->totalStories = $product->activeStories + $product->changedStories + $product->draftStories + $product->closedStories + $product->reviewingStories; - $product->storyCompleteRate = ($product->totalStories == 0 ? 0 : round($product->closedStories / $product->totalStories, 3) * 100) . '%'; - $product->unResolvedBugs = (int)$product->unResolved; - $product->assignToNullBugs = (int)$product->assignToNull; - $product->bugFixedRate = (($product->unResolved + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unResolved + $product->fixedBugs), 3) * 100) . '%'; - $product->program = $product->programName; - - /* get rowspan data */ - if($lastProgram == '' or $product->program != $lastProgram) + $checkedItem = $this->cookie->checkedItem; + foreach($productStats as $i => $product) { - $rowspan[$i]['rows']['program'] = 1; - $programI = $i; - } - else - { - $rowspan[$programI]['rows']['program'] ++; - } - if($lastLine == '' or $product->line != $lastLine) - { - $rowspan[$i]['rows']['line'] = 1; - $lineI = $i; - } - else - { - $rowspan[$lineI]['rows']['line'] ++; - } - $lastProgram = $product->program; - $lastLine = $product->line; - - if($this->post->exportType == 'selected') - { - $checkedItem = $this->cookie->checkedItem; if(strpos(",$checkedItem,", ",{$product->id},") === false) unset($productStats[$i]); } } if($this->config->edition != 'open') list($fields, $productStats) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $productStats); - if(isset($rowspan)) $this->post->set('rowspan', $rowspan); + $this->post->set('rowspan', $rowspan); $this->post->set('fields', $fields); $this->post->set('rows', $productStats); $this->post->set('kind', 'product'); $this->fetch('file', 'export2' . $this->post->fileType, $_POST); } + $this->display(); } /** * Story track. * - * @param int $productID + * @param string $productID * @param int|string $branch - * @param int $projectID - * @param int $recTotal - * @param int $recPerPage - * @param int $pageID + * @param string $projectID + * @param string $recTotal + * @param string $recPerPage + * @param string $pageID * @access public * @return void */ - public function track($productID, $branch = '', $projectID = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1) + public function track(string $productID, string $branch = '', string $projectID = '0', string $recTotal = '0', string $recPerPage = '20', string $pageID = '1') { - $branch = ($this->cookie->preBranch !== '' and $branch === '') ? $this->cookie->preBranch : $branch; - setcookie('preBranch', $branch, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true); + $productID = (int)$productID; + $projectID = (int)$projectID; + $branch = ($this->cookie->preBranch !== '' and $branch === '') ? $this->cookie->preBranch : $branch; /* Set menu. The projectstory module does not execute. */ - if(!$projectID) - { - $products = $this->product->getPairs(); - $productID = $this->product->saveVisitState($productID, $products); - $this->product->products = $this->product->saveVisitState($productID, $products); - $this->product->setMenu($productID, $branch); - } - - /* Save session. */ - $this->session->set('storyList', $this->app->getURI(true), 'product'); - $this->session->set('taskList', $this->app->getURI(true), 'execution'); - $this->session->set('designList', $this->app->getURI(true), 'project'); - $this->session->set('bugList', $this->app->getURI(true), 'qa'); - $this->session->set('caseList', $this->app->getURI(true), 'qa'); - $this->session->set('revisionList', $this->app->getURI(true), 'repo'); + $this->productZen->setTrackMenu($productID, $branch, $projectID); /* Load pager and get tracks. */ $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); $tracks = $this->story->getTracks($productID, $branch, $projectID, $pager); - if($projectID) - { - $this->loadModel('project')->setMenu($projectID); - $projectProducts = $this->product->getProducts($projectID); - } - - $this->view->title = $this->lang->story->track; - $this->view->position[] = $this->lang->story->track; + /* Get project products. */ + $projectProducts = array(); + if($projectID) $projectProducts = $this->product->getProducts($projectID); + $this->view->title = $this->lang->story->track; $this->view->tracks = $tracks; $this->view->pager = $pager; $this->view->productID = $productID; - $this->view->projectProducts = isset($projectProducts) ? $projectProducts : array(); + $this->view->projectProducts = $projectProducts; $this->display(); } diff --git a/module/product/model.php b/module/product/model.php index eedf63f393..c1190879b2 100755 --- a/module/product/model.php +++ b/module/product/model.php @@ -275,7 +275,6 @@ class productModel extends model if(!empty($append) and is_array($append)) $append = implode(',', $append); /* 初始化变量。 */ - $projectID = (int)$projectID; $views = $this->app->user->view->products . (empty($append) ? '' : ",$append"); $projectProducts = $this->productTao->getProductsByProjectID($projectID, $views, $status, $orderBy, $noDeleted); $products = array(); @@ -1316,17 +1315,17 @@ class productModel extends model * 获取产品统计信息。 * Get product stats. * - * @param string $orderBy order_asc|program_asc - * @param object $pager - * @param string $status - * @param int $line - * @param string $storyType requirement|story - * @param int $programID - * @param int $param + * @param string $orderBy order_asc|program_asc + * @param object|null $pager + * @param string $status + * @param int $line + * @param string $storyType requirement|story + * @param int $programID + * @param int $param * @access public * @return array */ - public function getStats(string $orderBy = 'order_asc', object $pager = null, string $status = 'noclosed', int $line = 0, string $storyType = 'story', int $programID = 0, int $param = 0): array + public function getStats(string $orderBy = 'order_asc', object|null $pager = null, string $status = 'noclosed', int $line = 0, string $storyType = 'story', int $programID = 0, int $param = 0): array { /* Fetch products list. */ $products = strtolower($status) == static::ST_BYSEARCH ? $this->getListBySearch($param) : $this->productTao->getList($programID, $status, 0, $line); diff --git a/module/product/tao.php b/module/product/tao.php index 415a9edf53..7a39268352 100644 --- a/module/product/tao.php +++ b/module/product/tao.php @@ -400,14 +400,14 @@ class productTao extends productModel * 获取用于统计的产品列表。 * Get products list for statistic. * - * @param int[] $roductIDs - * @param int $programID - * @param string $orderBy - * @param object $pager + * @param int[] $roductIDs + * @param int $programID + * @param string $orderBy + * @param object|null $pager * @access protected * @return array */ - protected function getStatsProducts(array $productIDs, int $programID, string $orderBy, object $pager): array + protected function getStatsProducts(array $productIDs, int $programID, string $orderBy, object|null $pager = null): array { if($orderBy == static::OB_PROGRAM) $products = $this->getPagerProductsWithProgramIn($productIDs, $pager); else $products = $this->getPagerProductsIn($productIDs, $pager, $orderBy); diff --git a/module/product/zen.php b/module/product/zen.php index 2d35a84736..ab80676c1f 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -85,6 +85,35 @@ class productZen extends product $this->product->setMenu($productID); } + /** + * 设置跟踪矩阵导航 + * Set menu for track + * + * @param int $productID + * @param string $branch + * @param int $projectID + * @access protected + * @return void + */ + protected function setTrackMenu(int $productID, string $branch, int $projectID) + { + setcookie('preBranch', $branch, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, true); + + /* Save session. */ + $uri = $this->app->getURI(true); + $this->session->set('storyList', $uri, 'product'); + $this->session->set('taskList', $uri, 'execution'); + $this->session->set('designList', $uri, 'project'); + $this->session->set('bugList', $uri, 'qa'); + $this->session->set('caseList', $uri, 'qa'); + $this->session->set('revisionList', $uri, 'repo'); + + if($projectID) return $this->loadModel('project')->setMenu($projectID); + + $productID = $this->product->saveVisitState($productID, $this->products); + $this->product->setMenu($productID, $branch); + } + /** * 为showErrorNone方法,根据不同模块,设置不同的二级或三级导航配置。 * Set menu for showErrorNone page. @@ -321,6 +350,102 @@ class productZen extends product return array($productLines, $linePairs); } + /** + * 获取产品导出字段。 + * Get export fields. + * + * @access protected + * @return array + */ + protected function getExportFields(): array + { + $productLang = $this->lang->product; + $productConfig = $this->config->product; + + /* 获取字段语言项。 */ + $fields = explode(',', $productConfig->list->exportFields); + $fieldPairs = array(); + foreach($fields as $key => $fieldName) + { + $fieldName = trim($fieldName); + $fieldPairs[$fieldName] = zget($productLang, $fieldName); + + if($this->config->systemMode == 'light' and ($fieldName == 'line' or $fieldName == 'program')) unset($fieldPairs[$fieldName]); + } + + return $fieldPairs; + } + + /** + * 获取导出产品数据。 + * Get export product data. + * + * @param string $status + * @param string $orderBy + * @access protected + * @return array + */ + protected function getExportData(string $status, string $orderBy): array + { + $lines = $this->product->getLinePairs(); + $users = $this->user->getPairs('noletter'); + $productStats = $this->product->getStats($orderBy, null, $status); + + foreach($productStats as $i => $product) + { + $product->line = zget($lines, $product->line, ''); + $product->manager = zget($users, $product->PO, ''); + $product->draftStories = (int)$product->stories['draft']; + $product->activeStories = (int)$product->stories['active']; + $product->changedStories = (int)$product->stories['changing']; + $product->reviewingStories = (int)$product->stories['reviewing']; + $product->closedStories = (int)$product->stories['closed']; + $product->totalStories = $product->activeStories + $product->changedStories + $product->draftStories + $product->closedStories + $product->reviewingStories; + $product->storyCompleteRate = ($product->totalStories == 0 ? 0 : round($product->closedStories / $product->totalStories, 3) * 100) . '%'; + $product->unResolvedBugs = (int)$product->unResolved; + $product->assignToNullBugs = (int)$product->assignToNull; + $product->bugFixedRate = (($product->unResolved + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unResolved + $product->fixedBugs), 3) * 100) . '%'; + $product->program = $product->programName; + } + + return $productStats; + } + + /** + * 获取导出的合并单元格信息。 + * Get rowspan for export. + * + * @param array $products + * @access protected + * @return array + */ + protected function getExportRowspan(array $products): array + { + $lastRecord = array('program' => '', 'line' => ''); + $colIndexs = array('program' => 0, 'line' => 0); + $rowspan = array(); + + foreach($products as $i => $product) + { + foreach($lastRecord as $field => $lastID) + { + if($product->{$field} !== $lastID) + { + $rowspan[$i]['rows'][$field] = 1; + $colIndexs[$field] = $i; + } + else + { + $colIndex = $colIndexs[$field]; + $rowspan[$colIndex]['rows'][$field] ++; + } + $lastRecord[$field] = $product->{$field}; + } + } + + return $rowspan; + } + /** * 根据产品和已授权项目集,获取关联产品的未授权项目集。 * Get unauthorized programs by products and authorized programs.