Merge branch 'sgm_kanbanmodel' into 'kanban'

Add kanban to execution.

See merge request easycorp/zentaopms!1339
This commit is contained in:
李玉春
2022-01-11 06:14:15 +00:00
15 changed files with 153 additions and 13 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
CREATE TABLE `zt_cardgroup` (
CREATE TABLE `zt_kanbancardgroup` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`kanban` mediumint(8) NOT NULL,
`objectType` char(30) NOT NULL,
+3 -1
View File
@@ -707,6 +707,8 @@ CREATE TABLE `zt_kanbancard` (
`kanban` mediumint(8) unsigned NOT NULL,
`region` mediumint(8) unsigned NOT NULL,
`group` mediumint(8) unsigned NOT NULL,
`lane` mediumint(8) unsigned NOT NULL,
`column` mediumint(8) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`pri` mediumint(8) unsigned NOT NULL,
`assignedTo` text NOT NULL,
@@ -731,7 +733,7 @@ CREATE TABLE `zt_kanbancard` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_kanbancardgroup`;
CREATE TABLE `zt_cardgroup` (
CREATE TABLE `zt_kanbancardgroup` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`kanban` mediumint(8) NOT NULL,
`objectType` char(30) NOT NULL,
+1 -1
View File
@@ -180,7 +180,7 @@ $lang->action->desc->diff2 = '修改了 <strong><i>%s</i></strong
$lang->action->desc->diff3 = '将文件名 %s 改为 %s 。' . "\n";
$lang->action->desc->linked2bug = '$date 由 <strong>$actor</strong> 关联到版本 <strong>$extra</strong>';
$lang->action->desc->linked2testtask = '$date 由 <strong>$actor</strong> 关联到测试单 <strong>$extra</strong>';
$lang->action->desc->unlinkedfromtesttask = '$date 由 $actor 从测试单 <strong>$extra</strong> 中移除';
$lang->action->desc->unlinkedfromtesttask = '$date 由 <strong>$actor</strong> 从测试单 <strong>$extra</strong> 中移除';
$lang->action->desc->resolved = '$date, 由 <strong>$actor</strong> 解决。' . "\n";
$lang->action->desc->managed = '$date, 由 <strong>$actor</strong> 维护。' . "\n";
$lang->action->desc->estimated = '$date, 由 <strong>$actor</strong> 估算。' . "\n";
+10
View File
@@ -381,6 +381,11 @@ class actionModel extends model
$name = $this->dao->select('name')->from(TABLE_RELEASE)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('release', 'view') ? html::a(helper::createLink('release', 'view', "releaseID=$action->extra&type={$action->objectType}"), $name) : $name;
}
elseif($actionName == 'linked2testtask')
{
$name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('testtask', 'view') ? html::a(helper::createLink('testtask', 'view', "taskID=$action->extra"), $name) : $name;
}
elseif($actionName == 'moved')
{
$name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name');
@@ -416,6 +421,11 @@ class actionModel extends model
$title = $this->dao->select('title')->from(TABLE_PRODUCTPLAN)->where('id')->eq($action->extra)->fetch('title');
if($title) $action->extra = common::hasPriv('productplan', 'view') ? html::a(helper::createLink('productplan', 'view', "planID=$action->extra"), "#$action->extra " . $title) : "#$action->extra " . $title;
}
elseif($actionName == 'unlinkedfromtesttask')
{
$name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('testtask', 'view') ? html::a(helper::createLink('testtask', 'view', "taskID=$action->extra"), $name) : $name;
}
elseif($actionName == 'tostory')
{
$title = $this->dao->select('title')->from(TABLE_STORY)->where('id')->eq($action->extra)->fetch('title');
+5
View File
@@ -289,6 +289,11 @@ $lang->waterfall->menu->design['subMenu']->dbds = array('link' => "{$lang->d
$lang->waterfall->menu->design['subMenu']->ads = array('link' => "{$lang->design->ADS}|design|browse|projectID=%s&productID=0&browseType=ADS");
$lang->waterfall->menu->design['subMenu']->bysearch = array('link' => '<a href="javascript:;" class="querybox-toggle"><i class="icon-search icon"></i> ' . $lang->searchAB . '</a>');
/* Kanban project menu. */
$lang->kanban->menu = new stdclass();
$lang->kanban->menuOrder = array();
$lang->kanban->dividerMenu = '';
/* Execution menu. */
$lang->execution->homeMenu = new stdclass();
$lang->execution->homeMenu->all = array('link' => "{$lang->execution->all}|execution|all|", 'alias' => 'batchedit');
+17
View File
@@ -2478,6 +2478,23 @@ class executionModel extends model
->fetchAll('account');
}
/**
* Get members by execution id list.
*
* @param array $executionIdList
* @access public
* @return void
*/
public function getMembersByIdList($executionIdList)
{
return $this->dao->select("t1.root, t1.account, t2.realname")->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account')
->where('t1.root')->in($executionIdList)
->andWhere('t1.type')->eq('execution')
->andWhere('t2.deleted')->eq('0')
->fetchGroup('root');
}
/**
* Get the skip members of the team.
*
+11 -1
View File
@@ -222,10 +222,11 @@ class project extends control
* Project index view.
*
* @param int $projectID
* @param string $browseType
* @access public
* @return void
*/
public function index($projectID = 0)
public function index($projectID = 0, $browseType = 'all')
{
$projectID = $this->project->saveState($projectID, $this->project->getPairsByProgram());
@@ -240,6 +241,15 @@ class project extends control
if(!$projectID) $this->locate($this->createLink('project', 'browse'));
setCookie("lastProject", $projectID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
if($project->model == 'kanban')
{
$kanbanList = $this->loadModel('execution')->getList($projectID, 'all', $browseType);
$this->view->kanbanList = $kanbanList;
$this->view->browseType = $browseType;
$this->view->memberGroup = $this->execution->getMembersByIdList(array_keys($kanbanList));
$this->view->usersAvatar = $this->loadModel('user')->getAvatarPairs();
}
$this->view->title = $this->lang->project->common . $this->lang->colon . $this->lang->project->index;
$this->view->position[] = $this->lang->project->index;
$this->view->project = $project;
+24
View File
@@ -0,0 +1,24 @@
#cards > .col {width: 25% !important;}
#cards .panel {margin: 0 0; border: 1px solid #DCDCDC; border-radius: 4px; box-shadow: none; cursor: pointer; height: 160px; margin-bottom: 5px;}
#cards .panel:hover {border-color: #006AF1; box-shadow: 0 0 10px 0 rgba(0,0,100,.25);}
#cards .panel-heading {padding: 4px 2px 10px 16px;}
#cards .kanban-name {overflow: hidden; white-space:nowrap; width: 88%; float: left; padding-top: 8px;}
#cards .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-1px !important;}
#cards .kanban-actions {float: right; visibility: hidden;}
#cards .kanban-actions .dropdown-menu.pull-right {top:31px; right: -84px; left: auto;}
#cards .kanban-actions .dropdown-menu.pull-left {top:31px; right:-1px; left: auto;}
#cards .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-6px;}
#cards .panel-body {padding: 0 16px 16px;}
#cards .kanban-desc {color: #838a9d; word-break:break-all; height: 70px; overflow: hidden; display: -webkit-box; float:left;}
#cards .kanban-footer {position: absolute; bottom: 10px; right: 10px; left: 15px;}
#cards .kanban-members {float: left; height: 24px; line-height: 24px;}
#cards .kanban-members > div {display: inline-block; height: 24px;}
#cards .kanban-members > div + div {margin-left: -5px;}
#cards .kanban-members > div > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 1px;}
#cards .kanban-members > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position: relative; border-radius: 50%; top: 3px; margin: 0 3px;}
#cards .kanban-members > span:before,
#cards .kanban-members > span:after {content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0; border-radius: 50%}
#cards .kanban-members > span:before {left: -4px;}
#cards .kanban-members > span:after {right: -4px;}
#cards .kanban-members-total {display: inline-block; margin-left: 6px; position: relative; top: 3px}
#cards .kanbanAcl {position: absolute; right: 0px; bottom: 2px; color: #838a9d;}
+1
View File
@@ -156,6 +156,7 @@ $lang->project->product = 'Product';
$lang->project->branch = 'Branch';
$lang->project->plan = 'Plan';
$lang->project->kanban = 'Kanban';
$lang->project->createKanban = 'Create Kanban';
/* Project Kanban. */
$lang->project->typeList = array();
+1
View File
@@ -155,6 +155,7 @@ $lang->project->etc = "等";
$lang->project->product = '所属产品';
$lang->project->branch = '所属分支';
$lang->project->plan = '所属计划';
$lang->project->createKanban = '创建看板';
$lang->project->kanban = '看板';
/* Project Kanban. */
+1 -1
View File
@@ -1776,7 +1776,7 @@ class projectModel extends model
$type = 'project';
if($this->config->systemMode == 'new')
{
if($project->type == 'sprint' or $project->type == 'stage') $type = 'execution';
if($project->type == 'sprint' or $project->type == 'stage' or $project->type == 'kanban') $type = 'execution';
}
$members = $this->dao->select("t1.account, if(t2.deleted='0', t2.realname, t1.account) as realname")->from(TABLE_TEAM)->alias('t1')
+6 -3
View File
@@ -72,9 +72,12 @@ foreach($projects as $programID => $programProjects)
foreach($programProjects as $index => $project)
{
$selected = $project->id == $projectID ? 'selected' : '';
$link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id);
$projectName = $project->model == 'scrum' ? '<i class="icon icon-sprint"></i> ' . $project->name : '<i class="icon icon-waterfall"></i> ' . $project->name;
$selected = $project->id == $projectID ? 'selected' : '';
$link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id);
$icon = '<i class="icon icon-sprint"></i> ';
if($project->model != 'scrum') $icon = "<i class='icon icon-{$project->model}'></i> ";
$projectName = $icon . $project->name;
if($project->status != 'done' and $project->status != 'closed' and $project->PM == $this->app->user->account)
{
+69
View File
@@ -10,5 +10,74 @@
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php if($project->model != 'kanban'):?>
<?php echo $this->fetch('block', 'dashboard', "module=project&type={$project->model}&projectID={$project->id}");?>
<?php else:?>
<div class='clearfix' id='mainMenu'>
<div class='btn-toolbar pull-left'>
<?php
foreach($lang->project->featureBar as $label => $labelName)
{
$active = $browseType == $label ? 'btn-active-text' : '';
echo html::a($this->createLink('project', 'index', "projectID=$project->id&browseType=" . $label), '<span class="text">' . $labelName . '</span> ' . ($browseType == $label ? "<span class='label label-light label-badge'>0</span>" : ''), '', "class='btn btn-link $active'");
}
?>
</div>
<div class='btn-toolbar pull-right'>
<?php echo html::a($this->createLink('execution', 'create', "projectID=$project->id"), "<i class='icon icon-sm icon-plus'></i> " . $lang->project->createKanban, '', "class='btn btn-primary create-execution-btn'");?>
</div>
</div>
<div id="mainContent">
<div class="row cell" id='cards'>
<?php if(empty($kanbanList)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->noData;?></span>
<?php common::printLink('execution', 'create', "projectID=$project->id", '<i class="icon icon-plus"></i> ' . $lang->project->createKanban, '', 'class="btn btn-info"');?>
</p>
</div>
<?php else:?>
<?php foreach ($kanbanList as $kanbanID => $kanban):?>
<div class='col' data-id='<?php echo $kanbanID?>'>
<div class='panel'>
<div class='panel-heading'>
<div class='kanban-name'>
<span class="label label-closed"><?php echo zget($lang->execution->statusList, $kanban->status);?></span>
<strong title='<?php echo $kanban->name;?>'><?php echo $kanban->name;?></strong>
</div>
</div>
<div class='panel-body'>
<div class='kanban-desc' title="<?php echo strip_tags(htmlspecialchars_decode($kanban->desc));?>"><?php echo strip_tags(htmlspecialchars_decode($kanban->desc));?></div>
<div class='kanban-footer'>
<div class="clearfix">
<?php $members = zget($memberGroup, $kanbanID, array());?>
<?php if(!empty($members)):?>
<div class='kanban-members pull-left'>
<?php $count = 0;?>
<?php foreach($members as $member):?>
<?php if($count > 2) break;?>
<?php $count ++;?>
<div title="<?php echo $member->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member->account], 'account' => $member->account)); ?>
</div>
<?php endforeach;?>
<?php if(count($members) > 4):?>
<?php echo '<span>…</span>';?>
<?php $lastMember = end($members);?>
<div title="<?php echo $lastMember->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$lastMember->account], 'account' => $lastMember->account)); ?>
</div>
<?php endif;?>
</div>
<?php endif;?>
</div>
</div>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</div>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>
+2 -3
View File
@@ -1086,15 +1086,14 @@ class testtask extends control
$response['result'] = 'success';
$response['message'] = '';
$testRun = $this->dao->select('task, `case`, t2.name')->from(TABLE_TESTRUN)->alias('t1')->LeftJoin(TABLE_TESTTASK)->alias('t2')->on('t1.task = t2.id')->where('t1.id')->eq((int)
$rowID)->fetch();
$testRun = $this->dao->select('task,`case`')->from(TABLE_TESTRUN)->where('id')->eq((int)$rowID)->fetch();
$this->dao->delete()->from(TABLE_TESTRUN)->where('id')->eq((int)$rowID)->exec();
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
}
$this->loadModel('action')->create('case' ,$testRun->case, 'unlinkedfromtesttask', '', $testRun->name);
$this->loadModel('action')->create('case' ,$testRun->case, 'unlinkedfromtesttask', '', $testRun->task);
return $this->send($response);
}
}
+1 -2
View File
@@ -893,7 +893,6 @@ class testtaskModel extends model
if($type == 'bybuild') $row->assignedTo = zget($assignedToPairs, $caseID, '');
$this->dao->replace(TABLE_TESTRUN)->data($row)->exec();
$task = $this->getById($taskID);
/* When the cases linked the testtask, the cases link to the project. */
if($this->app->tab != 'qa')
{
@@ -907,7 +906,7 @@ class testtaskModel extends model
$data->version = 1;
$data->order = ++ $lastOrder;
$this->dao->replace(TABLE_PROJECTCASE)->data($data)->exec();
$this->loadModel('action')->create('case', $caseID, 'linked2testtask', '', $task->name);
$this->loadModel('action')->create('case', $caseID, 'linked2testtask', '', $taskID);
}
}
}