diff --git a/module/build/zen.php b/module/build/zen.php
index 079a1e7974..23156531f3 100644
--- a/module/build/zen.php
+++ b/module/build/zen.php
@@ -26,7 +26,7 @@ class buildZen extends build
{
$productGroups = $branchGroups = array();
$executions = $this->loadModel('execution')->getPairs($projectID, 'all', 'stagefilter|leaf|order_asc');
- $executionID = empty($executionID) ? key($executions) : $executionID;
+ $executionID = empty($executionID) ? (int)key($executions) : $executionID;
if($executionID)
{
$productGroups = $this->loadModel('product')->getProducts($executionID, $status);
diff --git a/module/project/config/dtable.php b/module/project/config/dtable.php
index d0dc5ef088..4000c1bc15 100644
--- a/module/project/config/dtable.php
+++ b/module/project/config/dtable.php
@@ -2,6 +2,7 @@
global $lang,$app;
$config->project->dtable = new stdclass();
$config->project->dtable->team = new stdclass();
+$config->project->dtable->testtask = new stdclass();
$config->project->dtable->defaultField = array('id', 'name', 'status', 'PM', 'budget', 'begin', 'end', 'progress', 'actions');
$config->project->dtable->fieldList['id']['title'] = $lang->idAB;
@@ -371,3 +372,62 @@ $config->project->dtable->team->fieldList['limited']['sortType'] = false;
$config->project->dtable->team->fieldList['actions']['type'] = 'actions';
$config->project->dtable->team->fieldList['actions']['minWidth'] = 60;
$config->project->dtable->team->fieldList['actions']['actionsMap'] = $config->project->team->actionList;
+
+$app->loadLang('testcase');
+$app->loadLang('testtask');
+$app->loadModuleConfig('testtask');
+
+$config->project->dtable->testtask->fieldList['product']['name'] = 'productName';
+$config->project->dtable->testtask->fieldList['product']['title'] = $lang->testtask->product;
+$config->project->dtable->testtask->fieldList['product']['type'] = 'text';
+$config->project->dtable->testtask->fieldList['product']['group'] = '1';
+
+$config->project->dtable->testtask->fieldList['id']['name'] = 'id';
+$config->project->dtable->testtask->fieldList['id']['title'] = $lang->idAB;
+$config->project->dtable->testtask->fieldList['id']['type'] = 'id';
+$config->project->dtable->testtask->fieldList['id']['group'] = '2';
+$config->project->dtable->testtask->fieldList['id']['fixed'] = false;
+
+$config->project->dtable->testtask->fieldList['title']['name'] = 'name';
+$config->project->dtable->testtask->fieldList['title']['title'] = $lang->testtask->name;
+$config->project->dtable->testtask->fieldList['title']['type'] = 'title';
+$config->project->dtable->testtask->fieldList['title']['link'] = array('module' => 'testtask', 'method' => 'cases', 'params' => 'taskID={id}');
+$config->project->dtable->testtask->fieldList['title']['group'] = '2';
+$config->project->dtable->testtask->fieldList['title']['fixed'] = false;
+$config->project->dtable->testtask->fieldList['title']['width'] = '356';
+
+$config->project->dtable->testtask->fieldList['build']['name'] = 'buildName';
+$config->project->dtable->testtask->fieldList['build']['title'] = $lang->testtask->build;
+$config->project->dtable->testtask->fieldList['build']['type'] = 'text';
+$config->project->dtable->testtask->fieldList['build']['link'] = array('module' => 'build', 'method' => 'view', 'params' => 'buildID={build}');
+$config->project->dtable->testtask->fieldList['build']['group'] = 'text';
+$config->project->dtable->testtask->fieldList['build']['group'] = '3';
+
+$config->project->dtable->testtask->fieldList['owner']['name'] = 'owner';
+$config->project->dtable->testtask->fieldList['owner']['title'] = $lang->testtask->owner;
+$config->project->dtable->testtask->fieldList['owner']['type'] = 'user';
+$config->project->dtable->testtask->fieldList['owner']['group'] = '4';
+
+$config->project->dtable->testtask->fieldList['begin']['name'] = 'begin';
+$config->project->dtable->testtask->fieldList['begin']['title'] = $lang->testtask->begin;
+$config->project->dtable->testtask->fieldList['begin']['type'] = 'date';
+$config->project->dtable->testtask->fieldList['begin']['group'] = '4';
+
+$config->project->dtable->testtask->fieldList['end']['name'] = 'end';
+$config->project->dtable->testtask->fieldList['end']['title'] = $lang->testtask->end;
+$config->project->dtable->testtask->fieldList['end']['type'] = 'date';
+$config->project->dtable->testtask->fieldList['end']['group'] = '4';
+
+$config->project->dtable->testtask->fieldList['status']['name'] = 'status';
+$config->project->dtable->testtask->fieldList['status']['title'] = $lang->testtask->status;
+$config->project->dtable->testtask->fieldList['status']['type'] = 'status';
+$config->project->dtable->testtask->fieldList['status']['statusMap'] = $lang->testtask->statusList;
+$config->project->dtable->testtask->fieldList['status']['group'] = '4';
+
+$config->project->dtable->testtask->fieldList['actions']['name'] = 'actions';
+$config->project->dtable->testtask->fieldList['actions']['title'] = $lang->actions;
+$config->project->dtable->testtask->fieldList['actions']['type'] = 'actions';
+$config->project->dtable->testtask->fieldList['actions']['sortType'] = false;
+$config->project->dtable->testtask->fieldList['actions']['fixed'] = false;
+$config->project->dtable->testtask->fieldList['actions']['list'] = $config->testtask->actionList;
+$config->project->dtable->testtask->fieldList['actions']['menu'] = array('cases', 'linkCase', 'report', 'view', 'edit', 'delete');
diff --git a/module/project/control.php b/module/project/control.php
index b746af56a0..fbe44decf5 100644
--- a/module/project/control.php
+++ b/module/project/control.php
@@ -885,11 +885,12 @@ class project extends control
$project = $this->project->getByID($projectID);
$tasks = $this->loadModel('testtask')->getProjectTasks($projectID, $orderBy, $pager);
+ $this->projectZen->assignTesttaskVars($tasks);
+
$this->view->title = $project->name . $this->lang->colon . $this->lang->project->common;
$this->view->project = $project;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
- $this->view->tasks = $tasks;
$this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter');
$this->view->products = $this->loadModel('product')->getPairs('', 0);
$this->view->actions = $this->loadModel('action')->getList('testtask', $projectID);
diff --git a/module/project/css/testtask.ui.css b/module/project/css/testtask.ui.css
new file mode 100644
index 0000000000..992f34327c
--- /dev/null
+++ b/module/project/css/testtask.ui.css
@@ -0,0 +1,2 @@
+#taskTable .toggle-icon.is-collapsed:before,.toggle-icon.is-expanded:before,.toggle-icon.is-collapsed:after {background-color: var(--color-current); content: " "; display: block; height: 1px; left: 2px; position: absolute; right: 2px; top: calc((var(--toggle-icon-size) - 3px)/2);}
+#taskTable .toggle-icon.is-collapsed:after {--tw-rotate: 90deg; transform: translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));}
diff --git a/module/project/js/testtask.ui.js b/module/project/js/testtask.ui.js
new file mode 100644
index 0000000000..82a43e07fa
--- /dev/null
+++ b/module/project/js/testtask.ui.js
@@ -0,0 +1,106 @@
+$(function()
+{
+ const options = zui.DTable.query().options;
+ initialOptions = $.extend(true, {}, options);
+});
+
+/**
+ * 产品列合并单元格。
+ * Merge cell in the product column.
+ *
+ * @param object cell
+ * @access public
+ * @return object
+ */
+window.getCellSpan = function(cell)
+{
+ if(cell.col.name == 'productName' && cell.row.data.rowspan)
+ {
+ return {rowSpan: cell.row.data.rowspan};
+ }
+ if(cell.col.name == 'id' && cell.row.data.colspan)
+ {
+ return {colSpan: cell.row.data.colspan};
+ }
+}
+
+/**
+ * 产品列显示展开收起的图标。
+ * Display show icon in the product column.
+ *
+ * @param object result
+ * @param object info
+ * @access public
+ * @return object
+ */
+window.onRenderCell = function(result, {row, col})
+{
+ if(result && col.name == 'productName')
+ {
+ if(row.data.hidden)
+ {
+ result.unshift({html: ''});
+ }
+ else
+ {
+ result.unshift({html: ''});
+ result.push({outer: false, style: {alignItems: 'start', 'padding-top': '8px'}})
+ }
+ }
+ if(result && col.name == 'id' && row.data.hidden)
+ {
+ result.push({outer: false, style: {alignItems: 'center', justifyContent: 'start'}})
+ }
+
+ return result;
+}
+
+window.deformation = function(event)
+{
+ let newData = [];
+ const options = zui.DTable.query().options;
+ const product = $(event.target).closest('a').data('product');
+ const oldOptions = $.extend(true, {}, initialOptions);
+
+ if($(event.target).closest('a').find('span').hasClass('is-collapsed'))
+ {
+ $.each(options.data, function(index)
+ {
+ if(!options.data[index]) return;
+ if(options.data[index].product == product)
+ {
+ $.each(oldOptions.data, function(key)
+ {
+ if(!oldOptions.data[key]) return;
+ if(oldOptions.data[key].product == product) newData.push(oldOptions.data[key]);
+ });
+ }
+ else
+ {
+ newData.push(options.data[index]);
+ }
+ });
+ options.data = newData;
+ $(event.target).closest('a').find('span').removeClass('is-collapsed').addClass('is-expanded');
+ $('#taskTable').zui('dtable').render(options);
+ }
+ else
+ {
+ options.data = options.data.filter(function(option)
+ {
+ return option.product != product || option.rowspan != 0;
+ });
+ $.each(options.data, function(index)
+ {
+ if(options.data[index] && options.data[index].product == product)
+ {
+ options.data[index].id = {html: '' + allTasks + ' ' + '' + options.data[index].rowspan + ''};
+ options.data[index].rowspan = 1;
+ options.data[index].colspan = 10;
+ options.data[index].hidden = 1;
+ }
+ });
+ $(event.target).closest('a').find('span').removeClass('is-expanded').addClass('is-collapsed');
+ $('#taskTable').zui('dtable').render();
+ }
+}
diff --git a/module/project/lang/de.php b/module/project/lang/de.php
index 606e3e7707..632deb956d 100644
--- a/module/project/lang/de.php
+++ b/module/project/lang/de.php
@@ -326,8 +326,6 @@ $lang->project->featureBar['bug']['unresolved'] = 'Unresolved';
$app->loadLang('testcase');
$lang->project->featureBar['testcase'] = $lang->testcase->featureBar['browse'];
-$lang->project->featureBar['testtask']['all'] = 'All';
-
$lang->project->featureBar['build']['all'] = 'Build List';
$lang->project->featureBar['group']['all'] = 'All Groups';
diff --git a/module/project/lang/en.php b/module/project/lang/en.php
index 30c97f3c8c..82e550ca85 100644
--- a/module/project/lang/en.php
+++ b/module/project/lang/en.php
@@ -326,8 +326,6 @@ $lang->project->featureBar['bug']['unresolved'] = 'Unresolved';
$app->loadLang('testcase');
$lang->project->featureBar['testcase'] = $lang->testcase->featureBar['browse'];
-$lang->project->featureBar['testtask']['all'] = 'All';
-
$lang->project->featureBar['build']['all'] = 'Build List';
$lang->project->featureBar['group']['all'] = 'All Groups';
diff --git a/module/project/lang/fr.php b/module/project/lang/fr.php
index 606e3e7707..632deb956d 100644
--- a/module/project/lang/fr.php
+++ b/module/project/lang/fr.php
@@ -326,8 +326,6 @@ $lang->project->featureBar['bug']['unresolved'] = 'Unresolved';
$app->loadLang('testcase');
$lang->project->featureBar['testcase'] = $lang->testcase->featureBar['browse'];
-$lang->project->featureBar['testtask']['all'] = 'All';
-
$lang->project->featureBar['build']['all'] = 'Build List';
$lang->project->featureBar['group']['all'] = 'All Groups';
diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php
index 6eda2a1167..b3cdade820 100644
--- a/module/project/lang/zh-cn.php
+++ b/module/project/lang/zh-cn.php
@@ -326,8 +326,6 @@ $lang->project->featureBar['bug']['unresolved'] = '未解决';
$app->loadLang('testcase');
$lang->project->featureBar['testcase'] = $lang->testcase->featureBar['browse'];
-$lang->project->featureBar['testtask']['all'] = '全部';
-
$lang->project->featureBar['build']['all'] = '全部版本';
$lang->project->featureBar['group']['all'] = '浏览分组';
diff --git a/module/project/ui/testtask.html.php b/module/project/ui/testtask.html.php
index 9fac1f9a3e..8274b3c809 100644
--- a/module/project/ui/testtask.html.php
+++ b/module/project/ui/testtask.html.php
@@ -10,24 +10,18 @@ declare(strict_types=1);
*/
namespace zin;
-/* Compute summary. */
-$waitCount = 0;
-$testingCount = 0;
-$blockedCount = 0;
-$doneCount = 0;
-foreach($tasks as $task)
-{
- if($task->status == 'wait') $waitCount ++;
- if($task->status == 'doing') $testingCount ++;
- if($task->status == 'blocked') $blockedCount ++;
- if($task->status == 'done') $doneCount ++;
- if($task->build == 'trunk' || empty($task->buildName)) $task->buildName = $this->lang->trunk;
-}
-
+jsVar('allTasks', $lang->testtask->allTasks);
featureBar
(
- set::current('all'),
- set::linkParams("projectID={$project->id}")
+ li
+ (
+ setClass('nav-item'),
+ a
+ (
+ set('class', 'active'),
+ $lang->testtask->browse
+ )
+ )
);
toolbar
@@ -45,19 +39,21 @@ toolbar
unset($config->testtask->dtable->fieldList['product']);
unset($config->testtask->dtable->fieldList['execution']);
-$tasks = initTableData($tasks, $config->testtask->dtable->fieldList, $this->testtask);
-$cols = array_values($config->testtask->dtable->fieldList);
-$data = array_values($tasks);
-$footerHTML = sprintf($lang->testtask->allSummary, count($tasks), $waitCount, $testingCount, $blockedCount, $doneCount);
+$tasks = initTableData($tasks, $config->project->dtable->testtask->fieldList);
+$summary = sprintf($lang->testtask->allSummary, count($tasks), $waitCount, $testingCount, $blockedCount, $doneCount);
dtable
(
- set::cols($cols),
- set::data($data),
+ set::id('taskTable'),
+ set::cols($config->project->dtable->testtask->fieldList),
+ set::data($tasks),
+ set::onRenderCell(jsRaw('window.onRenderCell')),
set::userMap($users),
- set::fixedLeftWidth('20%'),
set::orderBy($orderBy),
set::sortLink(createLink('project', 'testtask', "projectID={$project->id}&orderBy={name}_{sortType}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}")),
- set::footer(array(array('html' => $footerHTML), 'flex', 'pager')),
+ set::plugins(array('cellspan')),
+ set::getCellSpan(jsRaw('window.getCellSpan')),
+ set::fixedLeftWidth('20%'),
+ set::footer(array(array('html' => $summary), 'flex', 'pager')),
set::footPager(usePager()),
);
diff --git a/module/project/zen.php b/module/project/zen.php
index 96daf61661..83b2e07a38 100644
--- a/module/project/zen.php
+++ b/module/project/zen.php
@@ -1519,4 +1519,48 @@ class projectZen extends project
return $executionIdList;
}
+
+ /**
+ * 展示测试单的相关变量。
+ * Show the testtask related variables.
+ *
+ * @param array $tasks
+ * @access protected
+ * @return void
+ */
+ protected function assignTesttaskVars(array $tasks): void
+ {
+ /* Compute rowspan. */
+ $productGroup = array();
+ $waitCount = 0;
+ $testingCount = 0;
+ $blockedCount = 0;
+ $doneCount = 0;
+ foreach($tasks as $task)
+ {
+ $productGroup[$task->product][] = $task;
+ if($task->status == 'wait') $waitCount ++;
+ if($task->status == 'doing') $testingCount ++;
+ if($task->status == 'blocked') $blockedCount ++;
+ if($task->status == 'done') $doneCount ++;
+ if($task->build == 'trunk' || empty($task->buildName)) $task->buildName = $this->lang->trunk;
+ }
+
+ $lastProduct = '';
+ foreach($tasks as $taskID => $task)
+ {
+ $task->rowspan = 0;
+ if($lastProduct !== $task->product)
+ {
+ $lastProduct = $task->product;
+ if(!empty($productGroup[$task->product])) $task->rowspan = count($productGroup[$task->product]);
+ }
+ }
+
+ $this->view->waitCount = $waitCount;
+ $this->view->testingCount = $testingCount;
+ $this->view->blockedCount = $blockedCount;
+ $this->view->doneCount = $doneCount;
+ $this->view->tasks = $tasks;
+ }
}