diff --git a/module/testcase/config/action.php b/module/testcase/config/action.php
index 7a8bf2056b..e78f94fed6 100644
--- a/module/testcase/config/action.php
+++ b/module/testcase/config/action.php
@@ -55,7 +55,7 @@ $config->testcase->actionList['create']['url'] = array('module' => 'testcase',
$config->testcase->actionList['unlinkCase']['icon'] = 'unlink';
$config->testcase->actionList['unlinkCase']['text'] = $lang->testtask->unlinkCase;
$config->testcase->actionList['unlinkCase']['hint'] = $lang->testtask->unlinkCase;
-$config->testcase->actionList['unlinkCase']['url'] = array('module' => 'testtask', 'method' => 'unlinkCase', 'params' => 'caseID={id}&confirm=yes');
+$config->testcase->actionList['unlinkCase']['url'] = array('module' => 'testtask', 'method' => 'unlinkCase', 'params' => 'runID={id}&confirm=yes');
$config->testcase->actionList['showScript']['icon'] = 'file-code';
$config->testcase->actionList['showScript']['text'] = $lang->testcase->showScript;
diff --git a/module/testtask/control.php b/module/testtask/control.php
index f27bc04da0..d04e6a35c0 100644
--- a/module/testtask/control.php
+++ b/module/testtask/control.php
@@ -714,13 +714,11 @@ class testtask extends control
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false);
$runs = $this->testcase->appendData($runs, 'run');
$groupCases = array();
- $groupByList = array();
foreach($runs as $run)
{
if($groupBy == 'story')
{
$groupCases[$run->story][] = $run;
- $groupByList[$run->story] = $run->storyTitle;
}
elseif($groupBy == 'assignedTo')
{
@@ -735,7 +733,17 @@ class testtask extends control
foreach($buildStories as $buildStory)
{
$groupCases[$buildStory->id][] = $buildStory;
- $groupByList[$buildStory->id] = $buildStory->title;
+ }
+ }
+
+ $story = '';
+ foreach($runs as $index => $case)
+ {
+ $case->rowspan = 0;
+ if($story !== $case->story)
+ {
+ $story = $case->story;
+ if(!empty($groupCases[$case->story])) $case->rowspan = count($groupCases[$case->story]);
}
}
@@ -747,8 +755,7 @@ class testtask extends control
$this->view->taskID = $taskID;
$this->view->browseType = 'group';
$this->view->groupBy = $groupBy;
- $this->view->groupByList = $groupByList;
- $this->view->cases = $runs;
+ $this->view->runs = $runs;
$this->view->account = 'all';
$this->view->canBeChanged = $canBeChanged;
$this->display();
diff --git a/module/testtask/css/groupcase.ui.css b/module/testtask/css/groupcase.ui.css
new file mode 100644
index 0000000000..ab88b822a1
--- /dev/null
+++ b/module/testtask/css/groupcase.ui.css
@@ -0,0 +1,2 @@
+.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);}
+.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/testtask/js/groupcase.ui.js b/module/testtask/js/groupcase.ui.js
new file mode 100644
index 0000000000..11c9ad0a03
--- /dev/null
+++ b/module/testtask/js/groupcase.ui.js
@@ -0,0 +1,108 @@
+$(function()
+{
+ const options = zui.DTable.query().options;
+ initialOptions = $.extend(true, {}, options);
+});
+
+/**
+ * 需求列显示展开收起的图标。
+ * Display show icon in the story column.
+ *
+ * @param object result
+ * @param object info
+ * @access public
+ * @return object
+ */
+window.onRenderCell = function(result, {row, col})
+{
+ if(result && col.name == 'storyTitle')
+ {
+ 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;
+}
+
+/**
+ * 需求列合并单元格。
+ * cell span in the story column.
+ *
+ * @param object cell
+ * @access public
+ * @return object
+ */
+window.getCellSpan = function(cell)
+{
+ if(cell.col.name == 'storyTitle' && 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};
+ }
+}
+
+window.deformation = function(event)
+{
+ let newData = [];
+ const options = zui.DTable.query().options;
+ const story = $(event.target).closest('a').data('story');
+ const oldOptions = $.extend(true, {}, initialOptions);
+
+
+ if($(event.target).closest('a').find('span').hasClass('is-collapsed'))
+ {
+ console.log(initialOptions.data);
+ $.each(options.data, function(index)
+ {
+ if(!options.data[index]) return;
+ if(options.data[index].story == story)
+ {
+ $.each(oldOptions.data, function(key)
+ {
+ if(!oldOptions.data[key]) return;
+ if(oldOptions.data[key].story == story) 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');
+ $('#groupCaseTable').zui('dtable').render(options);
+ }
+ else
+ {
+ options.data = options.data.filter(function(option)
+ {
+ return option.story != story || option.rowspan != 0;
+ });
+ $.each(options.data, function(index)
+ {
+ if(options.data[index] && options.data[index].story == story)
+ {
+ options.data[index].id = {html: '' + allTestcases + ' ' + '' + options.data[index].rowspan + ''};
+ options.data[index].rowspan = 1;
+ options.data[index].colspan = 12;
+ options.data[index].hidden = 1;
+ }
+ });
+ $(event.target).closest('a').find('span').removeClass('is-expanded').addClass('is-collapsed');
+ $('#groupCaseTable').zui('dtable').render();
+ }
+}
diff --git a/module/testtask/ui/cases.html.php b/module/testtask/ui/cases.html.php
index 3ce3a98c33..af46e2dabd 100644
--- a/module/testtask/ui/cases.html.php
+++ b/module/testtask/ui/cases.html.php
@@ -27,7 +27,7 @@ $canBatchAssign = hasPriv('testtask', 'batchAssign');
$canBatchRun = hasPriv('testtask', 'batchRun');
$canBatchAction = ($canBeChanged && ($canBatchEdit || $canBatchUnlink || $canBatchAssign || $canBatchRun));
-$closeLink = $browseType == 'bymodule' ? inlink('cases', "taskID=$task->id&browseType=$browseType¶m=0&orderBy=$orderBy&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}") : 'javascript:removeCookieByKey("taskCaseModule")';
+$closeLink = $browseType == 'bymodule' ? inlink('cases', "taskID={$task->id}&browseType={$browseType}¶m=0&orderBy={$orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}") : 'javascript:removeCookieByKey("taskCaseModule")';
sidebar
(
moduleMenu(set(array
@@ -40,7 +40,7 @@ sidebar
/* Process variables of sutie menu. */
$suiteItems = array();
-if(empty($suiteList))
+if(empty($suites))
{
if($canCreateSuite && (empty($productID) || common::canModify('product', $product)))
{
diff --git a/module/testtask/ui/groupcase.html.php b/module/testtask/ui/groupcase.html.php
index d9229b3f55..f7897b5aac 100644
--- a/module/testtask/ui/groupcase.html.php
+++ b/module/testtask/ui/groupcase.html.php
@@ -11,14 +11,95 @@ declare(strict_types=1);
namespace zin;
jsVar('allTestcases', $lang->testcase->allTestcases);
-$cases = initTableData(array_values($cases), $config->testcase->group->dtable->fieldList);
+$canCreateSuite = hasPriv('testsuite', 'create');
+$canLinkCase = hasPriv('testtask', 'linkCase');
+$canExport = hasPriv('testcase', 'export');
+$canReport = hasPriv('testtask', 'report');
+$canView = hasPriv('testtask', 'view');
+
+/* Process variables of sutie menu. */
+$suiteItems = array();
+if(empty($suites))
+{
+ if($canCreateSuite && (empty($productID) || common::canModify('product', $product)))
+ {
+ $suiteItems[] = array('text' => $lang->testsuite->create, 'url' => $this->createLink('testsuite', 'create', "productID=$productID"));
+ }
+}
+else
+{
+ foreach($suites as $id => $name)
+ {
+ $suiteItems[] = array('text' => $name, 'url' => inlink('cases', "taskID=$task->id&browseType=bySuite¶m=$id"), 'active' => $name == $suiteName);
+ }
+}
+
+featureBar
+(
+ li
+ (
+ set::class('nav-item'),
+ a
+ (
+ set::href(inlink('cases', "taskID=$task->id&browseType=all")),
+ set('data-app', $app->tab),
+ $lang->testtask->allCases
+ )
+ ),
+ li
+ (
+ set::class('nav-item'),
+ a
+ (
+ set::href(inlink('cases', "taskID=$task->id&browseType=assignedtome")),
+ set('data-app', $app->tab),
+ $lang->testtask->assignedToMe
+ )
+ )
+);
+
+$viewItems = array(array('text' => $lang->testcase->listView, 'url' => inlink('browse', "productID=$productID&branch=$branch&browseType=all"), 'active' => $rawMethod != 'groupcase' ? true : false));
+toolbar
+(
+ dropdown
+ (
+ btn
+ (
+ setClass('btn btn-link ghost square'),
+ set::icon('kanban')
+ ),
+ set::items($viewItems),
+ set::placement('bottom-end'),
+ ),
+ set::items
+ (
+ array
+ (
+ $canLinkCase ? array('class' => 'ghost', 'icon' => 'link', 'text' => $lang->testtask->linkCase, 'url' => inlink('linkCase', "taskID=$task->id")) : null,
+ $canExport ? array('class' => 'ghost', 'icon' => 'export', 'text' => $lang->export, 'url' => $this->createLink('testcase', 'export', "productID=$productID&orderBy=case_desc&taskID=$task->id"), 'data-toggle' => 'modal') : null,
+ $canReport ? array('class' => 'ghost', 'icon' => 'bar-chart', 'text' => $lang->testtask->report->common, 'url' => inlink('report', "productID=$productID&taskID=$task->id&browseType=$browseType&branchID=$task->branch&moduleID=" . (empty($moduleID) ? '' : $moduleID))) : null,
+ $canView ? array('class' => 'ghost', 'icon' => 'list-alt', 'text' => $lang->testtask->view, 'url' => inlink('view', "taskID=$task->id")) : null,
+ array('class' => 'ghost', 'icon' => 'back', 'text' => $lang->goback, 'url' => $this->session->testtaskList)
+ )
+ )
+);
+
+$config->testcase->actionList['runCase']['url'] = array('module' => 'testtask', 'method' => 'runCase', 'params' => 'runID={id}&caseID={case}&version={version}');
+$config->testcase->actionList['edit']['url'] = array('module' => 'testcase', 'method' => 'edit', 'params' => 'caseID={case}&comment=false&executionID=%executionID%');
+
+$config->testcase->group->dtable->fieldList['actions']['list'] = $config->testcase->actionList;
+$config->testcase->group->dtable->fieldList['id']['name'] = 'case';
+$config->testcase->group->dtable->fieldList['actions']['menu'] = array('runCase', 'edit', 'unlinkCase');
+$config->testcase->group->dtable->fieldList['title']['link']['params'] = 'caseID={case}';
+
+$runs = initTableData($runs, $config->testcase->group->dtable->fieldList);
dtable
(
set::id('groupCaseTable'),
set::userMap($users),
set::cols($config->testcase->group->dtable->fieldList),
- set::data($cases),
+ set::data($runs),
set::plugins(array('cellspan')),
set::onRenderCell(jsRaw('window.onRenderCell')),
set::getCellSpan(jsRaw('window.getCellSpan')),