* implement kanban pages with kanban component.

This commit is contained in:
Hao Sun
2021-09-07 08:27:02 +08:00
parent 75ac012b6c
commit d88a87d307
8 changed files with 412 additions and 582 deletions
+7 -13
View File
@@ -1159,31 +1159,25 @@ class product extends control
$kanbanGroup = $this->product->getStats4Kanban();
extract($kanbanGroup);
$products = array();
$myProducts = array();
$otherProducts = array();
foreach($productList as $productID => $product)
{
if($product->status == 'normal' and $product->PO == $this->app->user->account)
{
$myProducts[$product->program][$productID] = $productID;
}
elseif($product->status == 'normal' and !($product->PO == $this->app->user->account))
{
$otherProducts[$product->program][$productID] = $productID;
}
if($product->status != 'normal') continue;
if($product->PO == $this->app->user->account) $myProducts[$product->program][] = $productID;
else $otherProducts[$product->program][] = $productID;
}
$products['myProducts'] = $myProducts;
$products['otherProducts'] = $otherProducts;
$kanbanList = array();
if(!empty($myProducts)) $kanbanList['my'] = $myProducts;
if(!empty($otherProducts)) $kanbanList['other'] = $otherProducts;
$this->view->title = $this->lang->product->kanban;
$this->view->products = $products;
$this->view->kanbanList = $kanbanList;
$this->view->programList = array(0 => $this->lang->product->emptyProgram) + $programList;
$this->view->productList = $productList;
$this->view->planList = $planList;
$this->view->projectList = $projectList;
$this->view->executionList = $executionList;
$this->view->projectProduct = $projectProduct;
$this->view->latestExecutions = $projectLatestExecutions;
$this->view->hourList = $hourList;
-30
View File
@@ -1,30 +0,0 @@
#kanban {padding: 10px;}
.table caption {background: #fff; border-top-left-radius: 2px; border-top-right-radius: 2px; margin-bottom: 0; font-weight: 500; padding-top: 0;}
.table th {border: 2px solid #fff;}
.table td {border-top: 5px solid #fff; border-bottom: 5px solid #fff; border-left: 2px solid #fff; border-right: 2px solid #fff;}
.table tbody tr:last-child td {border-bottom: unset;}
td.plan, td.release, td.project, td.execution, td.classicProject {vertical-align: top !important;}
td.program {padding-top: 10px !important; padding-bottom: 10px !important;}
.main-table tbody>tr>td:first-child, .main-table thead>tr>th:first-child {padding: 0 4px 10px 4px;}
.main-table tbody>tr>td {padding: 0 10px 10px;}
.table .program {writing-mode: vertical-lr; padding-left: 4px !important; color: #fff;}
td.product {padding-top: 10px !important;}
td.project, td.execution {padding: 0 0 10px 0 !important;}
td > div:not(.board) > .board-item:first-child {margin-top: 10px;}
td > div.board:first-child {margin-top: 5px;}
.fix-table-copy-wrapper thead > tr > th:first-child {background: none!important;}
.board-item {border: 1px solid #ebebeb; padding: 3px 10px; cursor: default; border-radius: 2px; background-color: #fff; text-align: left;}
.board-item:hover {border-color: #ccc;}
.board {padding: 0 10px;}
.board + .board {margin-top: 5px;}
.board-item + .board-item {margin-top: 10px;}
.project .board, .execution .board {padding-top: 5px; border-top: 2px solid #fff;}
.project .board:first-child, .execution .board:first-child {padding-top: 5px; border-top: unset;}
.release .board-item {display: flex; flex-flow: row nowrap; justify-content: flex-start; align-items: center;}
.emptyBoard {padding: 3px 10px; cursor: default;}
.project .table-row > .table-col:last-child, .execution .table-row > .table-col:last-child, .classicProject .table-row > .table-col:last-child{width: 24px; padding-top: 2px;}
.scroll {overflow-x: hidden; overflow-y: auto;}
+108
View File
@@ -0,0 +1,108 @@
/**
* Process kanban data
* @param {string} key Kanban key, used as kanban id
* @param {Object} programsData Programs data
* @returns {Object} kanban data
*/
function processKanbanData(key, programsData)
{
var kanbanId = key;
/* Generate columns */
var columns = [];
$.each(kanbanColumns, function(_, column)
{
columns.push($.extend({}, column,
{
kanban: kanbanId,
id: kanbanId + '-' + column.type,
}));
});
/* Format lanes data */
var lanes = [];
$.each(programsData, function(programId, programProducts)
{
var programName = programList[programId];
var subLanes = [];
programProducts.forEach(function(productID)
{
var product = productList[productID];
var items = {};
/* unclosed products */
var productItem = {id: 'product-' + productID, _id: productID, name: product.name};
items.unclosedProduct = [productItem];
/* plans */
items.unexpiredPlan = [];
var plans = planList[productID];
if(plans)
{
$.each(plans, function(planID, plan)
{
items.unexpiredPlan.push($.extend({}, plan, {id: 'plan-' + planID, _id: planID}));
});
}
/* doing projects */
if(kanbanColumns.doingProject)
{
items.doingProject = [];
var productProjects = projectProduct[productID];
if(productProjects)
{
$.each(productProjects, function(projectID)
{
var project = projectList[projectID];
if(!project) return;
var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID});
items.doingProject.push(projectItem);
});
}
}
/* doing execution */
items.doingExecution = [];
var productProjects = projectProduct[productID];
if(productProjects)
{
$.each(productProjects, function(projectID)
{
var execution = latestExecutions[projectID];
if(!execution) return;
var executionItem = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id});
items.doingExecution.push(executionItem);
});
}
/* normal release */
items.normalRelease = [];
var releases = releaseList[productID];
if(releases)
{
$.each(releases, function(releaseID, release)
{
if(!release) return;
var releaseItem = $.extend({}, release, {id: 'release-' + releaseID, _id: releaseID});
items.normalRelease.push(releaseItem);
});
}
subLanes.push({id: kanbanId + '-' + programId + '-' + productID, items: items});
});
lanes.push({id: programId, kanban: kanbanId, name: programName, subLanes: subLanes});
});
return {id: kanbanId, columns: columns, lanes: lanes};
}
$(function()
{
/* Init all kanbans */
$.each(kanbanList, function(key, programsData)
{
$('#kanban-' + key).kanban({data: processKanbanData(key, programsData)});
});
});
+52 -216
View File
@@ -2,228 +2,64 @@
/**
* The html product kanban file of kanban method of product module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @copyright Copyright 2021-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Fangzhou Hu <hufangzhou@easycorp.ltd>
* @author Hao Sun <sunhao@easycorp.ltd>
* @package ZenTaoPMS
* @version $Id
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
<?php if(empty($productList)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->noData;?></span></p>
</div>
<?php else:?>
<?php foreach($products as $type => $programs):?>
<?php if(empty($programs)) continue;?>
<div class="cell">
<table class="table text-center" style="border-radius: 0; background: #efefef;">
<caption><?php echo $type == 'myProducts' ? $lang->product->myProduct : $lang->product->otherProduct;?></caption>
<thead>
<tr>
<?php
$rowspan = $config->systemMode == 'new' ? "rowspan='2'" : '';
$colspan = $config->systemMode == 'new' ? "colspan='2'" : '';
?>
<?php if($config->systemMode == 'new'):?>
<th <?php echo $rowspan;?> class="w-20px" style="border-right: 0; border-left: 0; background: #32C5FF;"></th>
<?php endif;?>
<th <?php echo $rowspan;?> style="border-left: 0px;"><?php echo $lang->product->unclosedProduct;?></th>
<th <?php echo $rowspan;?>><?php echo $lang->product->unexpiredPlan;?></th>
<th <?php echo $colspan;?>><?php echo $lang->product->doing;?></th>
<th <?php echo $rowspan;?>><?php echo $lang->product->normalRelease;?></th>
</tr>
<?php if($config->systemMode == 'new'):?>
<tr>
<th><?php echo $lang->product->doingProject;?></th>
<th><?php echo $lang->product->doingExecution;?></th>
</tr>
<?php endif;?>
</thead>
<tbody>
<?php $colorIndex = 0;?>
<?php foreach($programs as $programID => $programProduct):?>
<?php $i = 0;?>
<?php foreach($programProduct as $productID):?>
<?php
$scroll = '';
$doingCounts = isset($projectProduct[$productID]) ? count($projectProduct[$productID]) : 0;
$planCounts = isset($planList[$productID]) ? count($planList[$productID]) : 0;
$releaseCount = isset($releaseList[$productID]) ? count($releaseList[$productID]) : 0;
if(($doingCounts < $planCounts or $doingCounts < $releaseCount) and ($planCounts > 5 or $releaseCount > 5)) $scroll = 'scroll';
?>
<tr>
<?php if($i == 0 and $config->systemMode == 'new'):?>
<td rowspan="<?php echo count($programs[$programID])?>" class="program text-ellipsis" style="background: <?php echo $this->lang->product->kanbanColorList[$colorIndex];?>"><?php echo zget($programList, $programID);?></td>
<?php endif;?>
<td class="product">
<?php if(common::hasPriv('product', 'browse')):?>
<?php echo "<span>" . html::a($this->createLink('product', 'browse', "productID=$productID"), $productList[$productID]->name, '', "title={$productList[$productID]->name}") . '</span>';?>
<?php else:?>
<?php echo "<span title={$productList[$productID]->name}>{$productList[$productID]->name}</span>";?>
<?php endif;?>
</td>
<td class="plan">
<div class="<?php echo $scroll;?>">
<?php if(isset($planList[$productID])):?>
<?php foreach($planList[$productID] as $planID => $plan):?>
<div class="board-item text-ellipsis">
<?php if(common::hasPriv('productplan', 'view')):?>
<?php echo html::a($this->createLink('productplan', 'view', "planID=$planID"), $plan->title, '', "title={$plan->title}");?>
<?php else:?>
<?php echo "<span title={$plan->title}>{$plan->title}</span>"?>
<?php endif;?>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
<?php if($config->systemMode == 'new'):?>
<td class="project">
<?php if(isset($projectProduct[$productID])):?>
<?php foreach($projectProduct[$productID] as $projectID => $project):?>
<?php if(!isset($projectList[$projectID])) continue;?>
<div class="board">
<?php $borderStyle = isset($project->delay) ? 'border-left: 3px solid red' : 'border-left: 3px solid #0bd986';?>
<div class="board-item" style="<?php echo $borderStyle;?>">
<div class="table-row">
<div class="table-col text-ellipsis">
<?php if(common::hasPriv('project', 'index')):?>
<?php echo html::a($this->createLink('project', 'index', "projectID=$projectID"), $projectList[$projectID]->name, '', "title={$projectList[$projectID]->name}");?>
<?php else:?>
<?php echo "<span title={$projectList[$projectID]->name}>{$projectList[$projectID]->name}</span>"?>
<?php endif;?>
</div>
<div class="table-col">
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($projectList[$projectID]->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($projectList[$projectID]->hours->progress);?></div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<td class="execution">
<?php if(isset($projectProduct[$productID])):?>
<?php foreach($projectProduct[$productID] as $projectID => $project):?>
<div class="board">
<?php
$borderStyle = '';
if(isset($latestExecutions[$projectID]))
{
$delay = helper::diffDate(helper::today(), $latestExecutions[$projectID]->end);
$borderStyle = $delay > 0 ? 'border-left: 3px solid red' : 'border-left: 3px solid #0bd986';
}
?>
<?php $boardStyle = isset($latestExecutions[$projectID]) ? 'board-item' : 'emptyBoard';?>
<?php $executionID = isset($latestExecutions[$projectID]) ? $latestExecutions[$projectID]->id : 0;?>
<?php $executionName = isset($latestExecutions[$projectID]) ? $latestExecutions[$projectID]->name : ''?>
<div class="<?php echo $boardStyle;?> text-ellipsis" style="<?php echo $borderStyle;?>">
<div class="table-row">
<div class="table-col text-ellipsis">
<?php if(isset($latestExecutions[$projectID])):?>
<?php if(common::hasPriv('execution', 'task')):?>
<?php echo html::a($this->createLink('execution', 'task', "executionID=$executionID"), $executionName, '', "title={$executionName}");?>
<?php else:?>
<?php echo "<span title=$executionName>$executionName</span>";?>
<?php endif;?>
<?php endif;?>
</div>
<div class="table-col">
<?php if(isset($latestExecutions[$projectID])):?>
<div class="c-progress">
<?php $hourList[$executionID] = isset($hourList[$executionID]) ? $hourList[$executionID] : $emptyHour; ?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($hourList[$executionID]->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($hourList[$executionID]->progress);?></div>
</div>
</div>
<?php endif;?>
</div>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<?php endif;?>
<?php if($config->systemMode == 'classic'):?>
<td class="classicProject">
<div>
<?php if(isset($executionList[$productID])):?>
<?php foreach($executionList[$productID] as $executionID => $execution):?>
<?php
$borderStyle = '';
$delay = helper::diffDate(helper::today(), $execution->end);
$borderStyle = $delay > 0 ? 'border-left: 3px solid red' : 'border-left: 3px solid #0bd986';
?>
<div class="board-item" style="<?php echo $borderStyle;?>">
<div class="table-row">
<div class="table-col text-ellipsis">
<?php if(common::hasPriv('execution', 'task')):?>
<?php echo html::a($this->createLink('execution', 'task', "executionID=$executionID"), $execution->name, '', "title={$execution->name} class='text-ellipsis'");?>
<?php else:?>
<?php echo "<span title={$execution->name}>{$execution->name}</span>"?>
<?php endif;?>
</div>
<div class="table-col">
<div class="c-progress">
<?php $hourList[$executionID] = isset($hourList[$executionID]) ? $hourList[$executionID] : $emptyHour; ?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($hourList[$executionID]->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($hourList[$executionID]->progress);?></div>
</div>
</div>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
<?php endif;?>
<td class="release">
<div class="<?php echo $scroll;?>">
<?php if(isset($releaseList[$productID])):?>
<?php foreach($releaseList[$productID] as $releaseID => $release):?>
<div class="board-item">
<?php if(common::hasPriv('release', 'view')):?>
<?php echo html::a($this->createLink('release', 'view', "releaseID=$releaseID"), $release->name, '', "title={$release->name} class='text-ellipsis'");?>
<?php else:?>
<?php echo "<span title={$release->name}>{$release->name}</span>"?>
<?php endif;?>
<?php if($release->marker) echo "&nbsp;<span><i class='icon icon-flag red'></i></span>";?>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
<?php $i ++;?>
</tr>
<?php endforeach;?>
<?php $colorIndex = $colorIndex == 9 ? 0 : ++ $colorIndex;?>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php endforeach;?>
<?php endif;?>
<?php include '../../common/view/kanban.html.php';?>
<?php if(empty($kanbanList)):?>
<div class="table-empty-tip cell">
<p class="text-muted"><?php echo $lang->noData;?></p>
</div>
<script>
$(function()
<?php else:?>
<div id='kanbanList'>
<?php foreach($kanbanList as $type => $programs):?>
<div class='panel kanban-panel'>
<div class='panel-heading'>
<strong><?php echo $type == 'my' ? $lang->product->myProduct : $lang->product->otherProduct;?></strong>
</div>
<div class='panel-body'>
<div id='kanban-<?php echo $type;?>' class='kanban'></div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php
$kanbanColumns = array();
$kanbanColumns['unclosedProduct'] = array('name' => $lang->product->unclosedProduct, 'type' => 'unclosedProduct');
$kanbanColumns['unexpiredPlan'] = array('name' => $lang->product->unexpiredPlan, 'type' => 'unexpiredPlan');
if($config->systemMode == 'new')
{
$('.board').height($('.project .board').height());
$('.table div.scroll').each(function()
{
var count = $(this).parent().siblings('td.project').children('.board').length >= 5 ? $(this).parent().siblings('td.project').children('.board').length : 5;
var preHeight = $(this).parent().siblings('td.project').children('.board').length >= 5 ? $('.board').outerHeight(true) : $(this).find('.board-item').outerHeight(true);
$(this).css('max-height', preHeight * count);
})
})
</script>
$kanbanColumns['doingProject'] = array('name' => $lang->product->doingProject, 'type' => 'doingProject');
$kanbanColumns['doingExecution'] = array('name' => $lang->product->doingExecution, 'type' => 'doingExecution');
}
else
{
$kanbanColumns['doingExecution'] = array('name' => $lang->product->doing, 'type' => 'doingExecution');
}
$kanbanColumns['normalRelease'] = array('name' => $lang->product->normalRelease, 'type' => 'normalRelease');
$userPrivs = array();
$userPrivs['product'] = common::hasPriv('product', 'browse');
$userPrivs['productplan'] = common::hasPriv('productplan', 'view');
$userPrivs['project'] = common::hasPriv('project', 'index');
$userPrivs['execution'] = common::hasPriv('execution', 'task');
$userPrivs['release'] = common::hasPriv('release', 'view');
js::set('kanbanColumns', $kanbanColumns);
js::set('userPrivs', $userPrivs);
js::set('kanbanList', $kanbanList);
js::set('programList', $programList);
js::set('productList', $productList);
js::set('planList', $planList);
js::set('projectList', $projectList);
js::set('projectProduct', $projectProduct);
js::set('latestExecutions', $latestExecutions);
js::set('releaseList', $releaseList);
js::set('hourList', $hourList);
js::set('doingText', $lang->product->doing);
?>
<?php endif; ?>
<?php include '../../common/view/footer.html.php';?>
+109
View File
@@ -0,0 +1,109 @@
/**
* Process kanban data
* @param {string} key Kanban key, used as kanban id
* @param {Object} programsData Programs data
* @returns {Object} kanban data
*/
function processKanbanData(key, programsData)
{
var kanbanId = key;
/* Generate columns */
var columns = [];
$.each(kanbanColumns, function(_, column)
{
columns.push($.extend({}, column,
{
kanban: kanbanId,
id: kanbanId + '-' + column.type,
}));
});
/* Format lanes data */
var lanes = [];
$.each(programsData, function(programId, program)
{
var subLanes = [];
$.each(program.products, function(productID, product)
{
var items = {};
/* unclosed products */
var productItem = {id: 'product-' + productID, _id: productID, name: product.name};
items.unclosedProduct = [productItem];
/* plans */
items.unexpiredPlan = [];
var plans = product.plans;
if(plans)
{
$.each(plans, function(planID, plan)
{
items.unexpiredPlan.push($.extend({}, plan, {id: 'plan-' + planID, _id: planID}));
});
}
/* wait projects */
items.waitProject = [];
var waitProjects = product.projects && product.projects.wait;
if(waitProjects)
{
$.each(waitProjects, function(projectID, project)
{
var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID});
items.waitProject.push(projectItem);
});
}
/* doing projects and executions */
items.doingProject = [];
items.doingExecution = [];
var doingProjects = product.projects && product.projects.doing;
if(doingProjects)
{
$.each(doingProjects, function(projectID, project)
{
var projectItem = $.extend({}, project, {id: 'project-' + projectID, _id: projectID});
items.doingProject.push(projectItem);
var execution = project.execution;
if(!execution) return;
var executionItem = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id});
items.doingExecution.push(executionItem);
});
}
/* normal release */
items.normalRelease = [];
var releases = product.releases;
if(releases)
{
$.each(releases, function(releaseID, release)
{
if(!release) return;
var releaseItem = $.extend({}, release, {id: 'release-' + releaseID, _id: releaseID});
items.normalRelease.push(releaseItem);
});
}
subLanes.push({id: kanbanId + '-' + programId + '-' + productID, items: items});
});
var programItem = $.extend({}, program, {id: programId, kanban: kanbanId, subLanes: subLanes});
lanes.push(programItem);
});
return {id: kanbanId, columns: columns, lanes: lanes};
}
$(function()
{
/* Init all kanbans */
$.each(kanbanGroup, function(key, programsData)
{
var $kanban = $('#kanban-' + key);
if(!$kanban.length) return;
$kanban.kanban({data: processKanbanData(key, programsData)});
});
});
+39 -169
View File
@@ -1,182 +1,52 @@
<?php
/**
* The html template file of kanban method of program module of ZenTaoPMS.
* The html product kanban file of kanban method of product module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @copyright Copyright 2021-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Guangming Sun<sunguangming@cnezsoft.com>
* @author Hao Sun <sunhao@easycorp.ltd>
* @package ZenTaoPMS
* @version $Id
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
<?php foreach($kanbanGroup as $type => $programGroup):?>
<?php if(empty($programGroup)) continue;?>
<?php $colorIndex = 0;?>
<div class="cell">
<div class='detail'>
<div class='detail-title'><?php echo $lang->program->kanban->typeList[$type];?></div>
<div class='detail-content'>
<table class="table no-margin table-grouped text-center" style='background: #efefef;'>
<thead>
<tr>
<th rowspan='2' class='w-20px' style='background: #32C5FF; border: none'></th>
<th rowspan='2'><?php echo $lang->program->kanban->openProducts;?></th>
<th rowspan='2'><?php echo $lang->program->kanban->unexpiredPlans;?></th>
<th rowspan='2'><?php echo $lang->program->kanban->waitingProjects;?></th>
<th colspan='2'><?php echo $lang->program->statusList['doing'];?></th>
<th rowspan='2'><?php echo $lang->program->kanban->normalReleases;?></th>
</tr>
<tr>
<th><?php echo $lang->program->kanban->doingProjects;?></th>
<th><?php echo $lang->program->kanban->doingExecutions;?></th>
</tr>
</thead>
<tbody>
<?php foreach($programGroup as $programID => $program):?>
<tr>
<td class='lane-name text-ellipsis' style='background: <?php echo $lang->program->kanban->laneColorList[$colorIndex];?>; color: #fff; border: none;' rowspan='<?php echo $program->rowspan;?>' title=<?php echo $program->name;?>><?php echo $program->name;?></td>
<?php $i = 0;?>
<?php if(!empty($program->products)):?>
<?php foreach($program->products as $productID => $product):?>
<?php
$scroll = '';
$doingCounts = isset($product->projects['doing']) ? count($product->projects['doing']) : 0;
$planCounts = isset($product->plans) ? count($product->plans) : 0;
$releaseCount = isset($product->releases) ? count($product->releases) : 0;
if(($doingCounts < $planCounts || $doingCounts < $releaseCount) and ($planCounts > 5 or $releaseCount > 5)) $scroll = 'scroll';
?>
<?php if($i != 0) echo '<tr>';?>
<td title=<?php echo $product->name;?> rowspan='<?php echo $product->rowspan;?>'><?php echo $product->name;?></td>
<td class='normal-plan' rowspan='<?php echo $product->rowspan;?>'>
<div class="<?php echo $scroll;?>">
<?php foreach($product->plans as $planID => $plan):?>
<div class='board-item'>
<div class='table-row'>
<div class='table-col text-ellipsis text-left'>
<?php echo html::a($this->createLink('productplan', 'view', "planID=$plan->id"), $plan->title);?>
</div>
</div>
</div>
<?php endforeach;?>
</div>
</td>
<td class='wait-project' rowspan='<?php echo $product->rowspan?>'>
<div class="<?php echo $scroll;?>">
<?php if(isset($product->projects['wait'])):?>
<?php foreach($product->projects['wait'] as $projectID => $project):?>
<div class='board-item' style='border-left: 3px solid #0991FF'>
<div class='table-row'>
<div class='table-col text-ellipsis text-left'>
<?php echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name);?>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
<?php if(isset($product->projects['doing'])):?>
<?php $index = 0;?>
<?php foreach($product->projects['doing'] as $projectID => $project):?>
<?php if($index != 0) echo "<tr>";?>
<td class='doing-td project'>
<div class='board'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name);?>
</div>
<div class='table-col'>
<div class="c-progress">
<?php $projectProgress = isset($project->hours->progress) ? $project->hours->progress : 0;?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($projectProgress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($projectProgress);?></div>
</div>
</div>
</div>
</div>
</div>
</div>
</td>
<td class='doing-td execution'>
<?php if(!empty($project->execution)):?>
<div class='board'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->execution->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php echo html::a($this->createLink('execution', 'task', "executionID={$project->execution->id}"), $project->execution->name);?>
</div>
<div class='table-col'>
<div class="c-progress">
<?php $executionProgress = isset($project->execution->hours->progress) ? $project->execution->hours->progress : 0;?>
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($executionProgress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($executionProgress);?></div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif;?>
</td>
<?php if($index == 0):?>
<td class='normal-release' rowspan=<?php echo $product->rowspan;?>>
<div class="<?php echo $scroll;?>">
<?php foreach($product->releases as $releaseID => $release):?>
<div class='board-item'>
<div class='table-row'>
<div class='table-col'>
<?php $flag = $release->marker ? " <icon class='icon icon-flag red' title='{$lang->release->marker}'></icon> " : '';?>
<?php echo html::a($this->createLink('release', 'view', "releaseID=$release->id"), $release->name . $flag);?>
</div>
</div>
</div>
<?php endforeach;?>
</div>
</td>
<?php endif;?>
<?php if($index != 0) echo "</tr>";?>
<?php $index ++;?>
<?php endforeach;?>
<?php else:?>
<td></td>
<td></td>
<td></td>
<?php endif;?>
<?php if($i != 0) echo '</tr>';?>
<?php $i ++;?>
<?php endforeach;?>
<?php else:?>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<?php endif;?>
</tr>
<?php $colorIndex ++;?>
<?php if($colorIndex > 9) $colorIndex = 0;?>
<?php endforeach;?>
</tbody>
</table>
</div>
<?php include '../../common/view/kanban.html.php';?>
<?php if(empty($kanbanGroup)):?>
<div class="table-empty-tip cell">
<p class="text-muted"><?php echo $lang->noData;?></p>
</div>
<?php else:?>
<div id='kanbanList'>
<?php foreach($kanbanGroup as $type => $programs):?>
<?php if(empty($programs)) continue;?>
<div class='panel kanban-panel'>
<div class='panel-heading'>
<strong><?php echo $lang->program->kanban->typeList[$type];?></strong>
</div>
<div class='panel-body'>
<div id='kanban-<?php echo $type;?>' class='kanban'></div>
</div>
</div>
<?php endforeach;?>
<?php endforeach; ?>
</div>
<script>
$(function()
{
$('.table div.scroll').each(function()
{
var count = $(this).closest('tr').find('td:first').attr('rowspan') >= 5 ? $(this).closest('tr').find('td:first').attr('rowspan') : 5;
var projectTdHeight = $(this).closest('tr').find('td.project .board').outerHeight(true) + 20;
var maxHeight = count > 5 ? projectTdHeight * count : projectTdHeight * count - 40;
$(this).css('max-height', maxHeight);
})
})
</script>
<?php
$kanbanColumns = array();
$kanbanColumns['unclosedProduct'] = array('name' => $lang->program->kanban->openProducts, 'type' => 'unclosedProduct');
$kanbanColumns['unexpiredPlan'] = array('name' => $lang->program->kanban->unexpiredPlans, 'type' => 'unexpiredPlan');
$kanbanColumns['waitProject'] = array('name' => $lang->program->kanban->waitingProjects, 'type' => 'waitProject');
$kanbanColumns['doingProject'] = array('name' => $lang->program->kanban->doingProjects, 'type' => 'doingProject');
$kanbanColumns['doingExecution'] = array('name' => $lang->program->kanban->doingExecutions, 'type' => 'doingExecution');
$kanbanColumns['normalRelease'] = array('name' => $lang->program->kanban->normalReleases, 'type' => 'normalRelease');
$userPrivs = array();
$userPrivs['product'] = common::hasPriv('product', 'browse');
$userPrivs['productplan'] = common::hasPriv('productplan', 'view');
$userPrivs['project'] = common::hasPriv('project', 'index');
$userPrivs['execution'] = common::hasPriv('execution', 'task');
$userPrivs['release'] = common::hasPriv('release', 'view');
js::set('kanbanColumns', $kanbanColumns);
js::set('userPrivs', $userPrivs);
js::set('kanbanGroup', $kanbanGroup);
js::set('doingText', $lang->program->statusList['doing']);
?>
<?php endif; ?>
<?php include '../../common/view/footer.html.php';?>
+62 -23
View File
@@ -1,27 +1,66 @@
/**
* Process kanban data
* @param {string} key Kanban key, used as kanban id
* @param {Object} programGroup Group data
* @returns {Object} kanban data
*/
function processKanbanData(key, programGroup)
{
var kanbanId = key;
/* Generate columns */
var columns = [];
$.each(kanbanColumns, function(_, column)
{
columns.push($.extend({}, column,
{
kanban: kanbanId,
id: kanbanId + '-' + column.type,
}));
});
/* Format lanes data */
var lanes = [];
$.each(programGroup, function(programId, statusMap)
{
var programName = programPairs[programId];
var items = {doingExecution: []};
/* Projects and executions */
['wait', 'doing', 'closed'].forEach(function(status)
{
var itemsList = [];
var statusProjects = statusMap[status];
if(statusProjects)
{
$.each(statusProjects, function(projectID, project)
{
itemsList.push($.extend({}, project, {id: 'project-' + projectID, _id: projectID}));
if(status === 'doing')
{
var execution = latestExecutions[projectID];
if(execution)
{
var executionItem = $.extend({}, execution, {id: 'execution-' + execution.id, _id: execution.id});
items.doingExecution = [executionItem];
}
}
});
}
items[status + 'Project'] = itemsList;
});
lanes.push({id: programId, kanban: kanbanId, name: programName, items: items});
});
return {id: kanbanId, columns: columns, lanes: lanes};
}
$(function()
{
$("div[class^='board-doing-']").height($('.board-doing-project').height());
$('.board-program').each(function()
/* Init all kanbans */
$.each(kanbanGroup, function(key, programGroup)
{
var boardWaitCount = $(this).find('.board-wait .board-item').length;
var boardClosedCount = $(this).find('.board-closed .board-item').length;
var boardDoingCount = $(this).find('.board-doing-project .board-item').length;
if((boardWaitCount > 5 || boardClosedCount > 5) && (boardWaitCount > boardDoingCount || boardClosedCount > boardDoingCount))
{
var boardHeight = 0;
if(boardDoingCount >= 5)
{
boardHeight = $('.board-doing-project').outerHeight(true) * boardDoingCount;
}
else
{
var boardHeight = $(this).find('.board-project .board-item').outerHeight(true) * 5;
}
$(this).find('.board-project').css("height", boardHeight);
$(this).find('.board-project').css("overflow", 'auto');
}
$('#kanban-' + key).kanban({data: processKanbanData(key, programGroup)});
});
})
});
+35 -131
View File
@@ -1,144 +1,48 @@
<?php
/**
* The project kanban view file of project module of ZenTaoPMS.
* The html product kanban file of kanban method of product module of ZenTaoPMS.
*
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Qiyu Xie
* @package project
* @version $Id: kanban.html.php $
* @copyright Copyright 2021-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Hao Sun <sunhao@easycorp.ltd>
* @package ZenTaoPMS
* @version $Id
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php $colorIndex = 0;?>
<?php include '../../common/view/kanban.html.php';?>
<?php if(empty($kanbanGroup)):?>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->project->empty;?></span></p>
<div class="table-empty-tip cell">
<p class="text-muted"><?php echo $lang->project->empty;?></p>
</div>
<?php else:?>
<?php foreach($kanbanGroup as $type => $projectGroup):?>
<?php if(empty($projectGroup)) continue;?>
<div id="kanban" class="main-table fade auto-fade-in" data-ride="table" data-checkable="false" data-group="true">
<div class="cell">
<div class='detail'>
<div class='detail-title'><?php echo $lang->project->typeList[$type];?></div>
<div class='detail-content'>
<table class='table no-margin' style='background-color: #efefef;'>
<thead class='text-center'>
<tr>
<th rowspan='2' style="border-right: unset !important" class='w-20px'></th>
<th rowspan='2' style="border-left: unset !important"><?php echo $lang->project->waitProjects;?></th>
<th colspan='2'><?php echo $lang->project->statusList['doing'];?></th>
<th rowspan='2'><?php echo $lang->project->closedProjects;?></th>
</tr>
<tr>
<th><?php echo $lang->project->doingProjects;?></th>
<th><?php echo $lang->project->doingExecutions;?></th>
</tr>
</thead>
<tbody>
<?php foreach($projectGroup as $programID => $statusList):?>
<tr class='board-program'>
<td class='text-center' title='<?php echo zget($programPairs, $programID);?>' style='background: <?php echo $lang->project->laneColorList[$colorIndex];?>; color: #fff; padding-left: 2px; writing-mode: vertical-lr;'><?php echo zget($programPairs, $programID);?></td>
<?php foreach(array('wait','doing','closed') as $status):?>
<?php if($status == 'doing'):?>
<td class='board-doing'>
<?php if(isset($statusList[$status])):?>
<?php foreach($statusList[$status] as $project):?>
<div class='board-doing-project'>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($project->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php
if(common::hasPriv('project', 'index'))
{
echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name, '', "title='{$project->name}'");
}
else
{
echo "<span title='{$project->name}'>{$project->name}</span>";
}
?>
</div>
<div class='table-col'>
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($project->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($project->hours->progress);?></div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<td class='board-doing'>
<?php if(isset($statusList[$status])):?>
<?php foreach($statusList[$status] as $project):?>
<div class='board-doing-execution'>
<?php if(isset($latestExecutions[$project->id])):?>
<?php $execution = $latestExecutions[$project->id];?>
<div class='board-item' <?php echo "style='border-left: 3px solid " . (isset($execution->delay) ? 'red' : "#0BD986") . "'";?>>
<div class='table-row'>
<div class='table-col'>
<?php
if(common::hasPriv('execution', 'task'))
{
echo html::a($this->createLink('execution', 'task', "executionID=$execution->id"), $execution->name, '', "title='{$execution->name}'");
}
else
{
echo "<span title='{$execution->name}'>{$execution->name}</span>";
}
?>
</div>
<div class='table-col'>
<div class="c-progress">
<div class='progress-pie' data-doughnut-size='90' data-color='#3CB371' data-value='<?php echo round($execution->hours->progress);?>' data-width='24' data-height='24' data-back-color='#e8edf3'>
<div class='progress-info'><?php echo round($execution->hours->progress);?></div>
</div>
</div>
</div>
</div>
</div>
<?php endif;?>
</div>
<?php endforeach;?>
<?php endif;?>
</td>
<?php else:?>
<td class='board-<?php echo $status;?>'>
<div class='board-project'>
<?php if(isset($statusList[$status])):?>
<?php foreach($statusList[$status] as $project):?>
<div class='board-item' <?php echo "style='border-left: 3px solid " . $lang->execution->statusColorList[$status] . "'";?>>
<?php
if(common::hasPriv('project', 'index'))
{
echo html::a($this->createLink('project', 'index', "projectID=$project->id"), $project->name, '', "title='{$project->name}'");
}
else
{
echo "<span title='{$project->name}'>{$project->name}</span>";
}
?>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
<?php endif;?>
<?php endforeach;?>
</tr>
<?php $colorIndex ++;?>
<?php if($colorIndex > 9) $colorIndex = 0;?>
<?php endforeach;?>
</tbody>
</table>
</div>
<div id='kanbanList'>
<?php foreach($kanbanGroup as $type => $projectGroup):?>
<div class='panel kanban-panel'>
<div class='panel-heading'>
<strong><?php echo $lang->project->typeList[$type];?></strong>
</div>
<div class='panel-body'>
<div id='kanban-<?php echo $type;?>' class='kanban'></div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach;?>
<?php endif;?>
<?php
$kanbanColumns = array();
$kanbanColumns['waitProject'] = array('name' => $lang->project->waitProjects, 'type' => 'waitProject');
$kanbanColumns['doingProject'] = array('name' => $lang->project->doingProjects, 'type' => 'doingProject');
$kanbanColumns['doingExecution'] = array('name' => $lang->project->doingExecutions, 'type' => 'doingExecution');
$kanbanColumns['closedProject'] = array('name' => $lang->project->closedProjects, 'type' => 'closedProject');
$userPrivs = array();
$userPrivs['project'] = common::hasPriv('project', 'index');
$userPrivs['execution'] = common::hasPriv('execution', 'task');
js::set('kanbanColumns', $kanbanColumns);
js::set('userPrivs', $userPrivs);
js::set('kanbanGroup', $kanbanGroup);
js::set('latestExecutions', $latestExecutions);
js::set('programPairs', $programPairs);
js::set('doingText', $lang->project->statusList['doing']);
?>
<?php endif; ?>
<?php include '../../common/view/footer.html.php';?>